From 1fe56fa5b4d5474c433ae7064356728d9a0dc185 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 27 Apr 2026 03:56:19 +0300 Subject: [PATCH 001/114] Provide access to `RootDatabase`'s `LineIndex` for the proc macro protocol Instead of recreating it. --- src/tools/rust-analyzer/crates/base-db/src/lib.rs | 3 +++ src/tools/rust-analyzer/crates/hir-def/src/test_db.rs | 4 ++++ src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs | 4 ++++ src/tools/rust-analyzer/crates/ide-db/src/lib.rs | 4 ++++ src/tools/rust-analyzer/crates/load-cargo/src/lib.rs | 8 +++----- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index a209a0e631e0b..0f4cc88b9d566 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -7,6 +7,7 @@ extern crate rustc_driver as _; pub use salsa; pub use salsa_macros; +use span::TextSize; // FIXME: Rename this crate, base db is non descriptive mod change; @@ -280,6 +281,8 @@ pub trait SourceDatabase: salsa::Database { fn crates_map(&self) -> Arc; fn nonce_and_revision(&self) -> (Nonce, salsa::Revision); + + fn line_column(&self, file: FileId, offset: TextSize) -> Result<(u32, u32), ()>; } static NEXT_NONCE: AtomicUsize = AtomicUsize::new(0); 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 b854d2aa218de..913b99223d9df 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 @@ -142,6 +142,10 @@ impl SourceDatabase for TestDB { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, _file: FileId, _offset: syntax::TextSize) -> Result<(u32, u32), ()> { + Err(()) + } } impl TestDB { 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 e19e26ebc4064..653fbe34f1581 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 @@ -132,6 +132,10 @@ impl SourceDatabase for TestDB { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, _file: FileId, _offset: syntax::TextSize) -> Result<(u32, u32), ()> { + Err(()) + } } #[salsa_macros::db] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 6b72a3033990b..6091be3eb9cd7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -180,6 +180,10 @@ impl SourceDatabase for RootDatabase { fn nonce_and_revision(&self) -> (Nonce, salsa::Revision) { (self.nonce, salsa::plumbing::ZalsaDatabase::zalsa(self).current_revision()) } + + fn line_column(&self, file: FileId, offset: syntax::TextSize) -> Result<(u32, u32), ()> { + line_index(self, file).try_line_col(offset).map(|lc| (lc.line, lc.col)).ok_or(()) + } } impl Default for RootDatabase { 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 839df181597ba..69ff36078cd63 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -609,11 +609,9 @@ impl ProcMacroExpander for Expander { SubRequest::LineColumn { file_id, ast_id, offset } => { let range = resolve_sub_span(db, file_id, ast_id, TextRange::empty(TextSize::from(offset))); - let source = db.file_text(range.file_id.file_id(db)).text(db); - let line_index = ide_db::line_index::LineIndex::new(source); - let (line, column) = line_index - .try_line_col(range.range.start()) - .map(|lc| (lc.line + 1, lc.col + 1)) + let (line, column) = db + .line_column(range.file_id.file_id(db), range.range.start()) + .map(|(line, col)| (line + 1, col + 1)) .unwrap_or((1, 1)); // proc_macro::Span line/column are 1-based Ok(SubResponse::LineColumnResult { line, column }) From 6fe00733b87906459864c7afaeab2b43b76e7220 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 10 May 2026 15:05:36 +0800 Subject: [PATCH 002/114] Add some Result methods to minicore --- .../crates/test-utils/src/minicore.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 8975fa56d7272..84395739809bf 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -1713,6 +1713,43 @@ pub mod result { #[lang = "Err"] Err(E), } + impl Result { + pub const fn or(self, res: Result) -> Result { + match self { + Ok(v) => Ok(v), + Err(_) => res, + } + } + + pub const fn unwrap_or(self, default: T) -> T { + match self { + Ok(t) => t, + Err(_) => default, + } + } + + // region:fn + pub const fn or_else(self, op: O) -> Result + where + O: FnOnce(E) -> Result, + { + match self { + Ok(t) => Ok(t), + Err(e) => op(e), + } + } + + pub const fn unwrap_or_else(self, op: F) -> T + where + F: FnOnce(E) -> T, + { + match self { + Ok(t) => t, + Err(e) => op(e), + } + } + // endregion:fn + } } // endregion:result From 35be753b6d6b15f9f43a34292e6caf8017404ed8 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 10 May 2026 15:05:56 +0800 Subject: [PATCH 003/114] fix: add param on result methods for replace_method_eager_lazy Example --- ```rust fn foo() { let foo = Ok(1); return foo.unwrap_$0or(2); } ``` **Before this PR** ```rust fn foo() { let foo = Ok(1); return foo.unwrap_or_else(|| 2); } ``` **After this PR** ```rust fn foo() { let foo = Ok(1); return foo.unwrap_or_else(|e| 2); } ``` --- .../src/handlers/replace_method_eager_lazy.rs | 46 ++++++++++++++++--- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 7aa9a82109c1e..39e15578cb65a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -1,5 +1,5 @@ use hir::Semantics; -use ide_db::{RootDatabase, assists::AssistId, defs::Definition}; +use ide_db::{RootDatabase, assists::AssistId, defs::Definition, famous_defs::FamousDefs}; use syntax::{ AstNode, ast::{self, Expr, HasArgList, make, syntax_factory::SyntaxFactory}, @@ -64,7 +64,20 @@ pub(crate) fn replace_with_lazy_method( format!("Replace {method_name} with {method_name_lazy}"), call.syntax().text_range(), |builder| { - let closured = into_closure(&last_arg, &method_name_lazy); + let param_name = match &*method_name_lazy { + "and_then" => "it", + "or_else" | "unwrap_or_else" => { + if let Some(result) = FamousDefs(&ctx.sema, scope.krate()).core_result_Result() + && result.ty(ctx.db()).could_unify_with(ctx.db(), &receiver_ty) + { + "e" + } else { + "" + } + } + _ => "", + }; + let closured = into_closure(&last_arg, param_name); builder.replace(method_name.syntax().text_range(), method_name_lazy); builder.replace_ast(last_arg, closured); }, @@ -83,7 +96,7 @@ fn lazy_method_name(name: &str) -> String { } } -fn into_closure(param: &Expr, name_lazy: &str) -> Expr { +fn into_closure(param: &Expr, param_name: &str) -> Expr { (|| { if let ast::Expr::CallExpr(call) = param { if call.arg_list()?.args().count() == 0 { Some(call.expr()?) } else { None } @@ -92,8 +105,9 @@ fn into_closure(param: &Expr, name_lazy: &str) -> Expr { } })() .unwrap_or_else(|| { - let pats = (name_lazy == "and_then") - .then(|| make::untyped_param(make::ext::simple_ident_pat(make::name("it")).into())); + let pats = (!param_name.is_empty()).then(|| { + make::untyped_param(make::ext::simple_ident_pat(make::name(param_name)).into()) + }); make::expr_closure(pats, param.clone()).into() }) } @@ -213,7 +227,7 @@ mod tests { check_assist( replace_with_lazy_method, r#" -//- minicore: option, fn +//- minicore: option, result, fn fn foo() { let foo = Some(1); return foo.unwrap_$0or(2); @@ -228,6 +242,26 @@ fn foo() { ) } + #[test] + fn replace_or_with_or_else_with_parameter() { + check_assist( + replace_with_lazy_method, + r#" +//- minicore: option, result, fn +fn foo() { + let foo = Ok(1); + return foo.unwrap_$0or(2); +} +"#, + r#" +fn foo() { + let foo = Ok(1); + return foo.unwrap_or_else(|e| 2); +} +"#, + ) + } + #[test] fn replace_or_with_or_else_call() { check_assist( From f16851bf7f6980e71ba9d8eb109da2d2ba220dcc Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 21 Apr 2026 11:22:37 +0200 Subject: [PATCH 004/114] Less conjuration magic --- .../crates/hir-ty/src/builtin_derive.rs | 2 +- .../crates/hir-ty/src/display.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 8 +-- .../crates/hir-ty/src/lower/path.rs | 4 ++ .../hir-ty/src/next_solver/infer/traits.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 1 + .../hir-ty/src/next_solver/predicate.rs | 15 ++--- src/tools/rust-analyzer/crates/hir/src/lib.rs | 56 +++++++++++-------- .../crates/hir/src/source_analyzer.rs | 9 +-- .../crates/hir/src/term_search.rs | 2 +- .../crates/hir/src/term_search/tactics.rs | 2 +- .../crates/ide-assists/src/utils.rs | 6 +- .../ide-completion/src/completions/dot.rs | 2 +- .../ide-completion/src/context/analysis.rs | 2 +- .../ide-db/src/imports/import_assets.rs | 4 +- .../src/handlers/type_mismatch.rs | 2 +- 17 files changed, 67 insertions(+), 58 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index fe60fbc5109f5..4e19fb80671f9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -193,7 +193,7 @@ pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPr else { // Malformed derive. return GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind( - Clauses::default().store(), + Clauses::empty(interner).store(), )); }; let duplicated_bounds = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index bc726b652fd4e..326f920b118e4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -780,7 +780,7 @@ fn render_const_scalar<'db>( memory_map: &MemoryMap<'db>, ty: Ty<'db>, ) -> Result { - let param_env = ParamEnv::empty(); + let param_env = ParamEnv::empty(f.interner); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_inner(f, b, memory_map, ty, param_env) @@ -1065,7 +1065,7 @@ fn render_const_scalar_from_valtree<'db>( ty: Ty<'db>, valtree: ValTree<'db>, ) -> Result { - let param_env = ParamEnv::empty(); + let param_env = ParamEnv::empty(f.interner); let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis); let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty); render_const_scalar_from_valtree_inner(f, ty, valtree, param_env) 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 39ffb91a8c5db..6a29b2cedba3f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -2438,8 +2438,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> { }; let args = path_ctx.substs_from_path_segment(it.into(), true, None, false, node.into()); + let interner = path_ctx.interner(); drop(ctx); - let interner = DbInterner::conjure(); let ty = self.db.ty(it.into()).instantiate(interner, args).skip_norm_wip(); let ty = self.insert_type_vars(ty); 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 5b0bcd2be8380..0cb1a2db26896 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -198,7 +198,7 @@ pub trait TyLoweringInferVarsCtx<'db> { pub struct TyLoweringContext<'db, 'a> { pub db: &'db dyn HirDatabase, - interner: DbInterner<'db>, + pub(crate) interner: DbInterner<'db>, types: &'db crate::next_solver::DefaultAny<'db>, lang_items: &'db LangItems, resolver: &'a Resolver<'db>, @@ -2072,12 +2072,12 @@ impl<'db> GenericPredicates { /// A cycle can occur from malformed code. fn generic_predicates_cycle_result( - _db: &dyn HirDatabase, + db: &dyn HirDatabase, _: salsa::Id, _def: GenericDefId, ) -> TyLoweringResult { TyLoweringResult::empty(GenericPredicates::from_explicit_own_predicates( - StoredEarlyBinder::bind(Clauses::default().store()), + StoredEarlyBinder::bind(Clauses::empty(DbInterner::new_no_crate(db)).store()), )) } @@ -2086,7 +2086,7 @@ impl GenericPredicates { pub fn empty() -> &'static GenericPredicates { static EMPTY: OnceLock = OnceLock::new(); EMPTY.get_or_init(|| GenericPredicates { - predicates: StoredEarlyBinder::bind(Clauses::default().store()), + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&[]).store()), has_trait_implied_predicate: false, parent_explicit_self_predicates_start: 0, own_predicates_start: 0, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index ff9718af111be..6633215679873 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -994,6 +994,10 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }) }) } + + pub(crate) fn interner(&self) -> DbInterner<'db> { + self.ctx.interner + } } /// A const that were parsed like a type. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 4584b35796456..362689ab5ce9e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -161,11 +161,11 @@ impl<'db> PredicateObligation<'db> { /// Flips the polarity of the inner predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. - pub fn flip_polarity(&self, _interner: DbInterner<'db>) -> Option> { + pub fn flip_polarity(&self, interner: DbInterner<'db>) -> Option> { Some(PredicateObligation { cause: self.cause, param_env: self.param_env, - predicate: self.predicate.flip_polarity()?, + predicate: self.predicate.flip_polarity(interner)?, recursion_depth: self.recursion_depth, }) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index b3d31dd40bf9e..9871042e61382 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -330,6 +330,7 @@ unsafe impl Sync for DbInterner<'_> {} impl<'db> DbInterner<'db> { // FIXME(next-solver): remove this method + #[doc(hidden)] pub fn conjure() -> DbInterner<'db> { // Here we can not reinit the cache since we do that when we attach the db. crate::with_attached_db(|db| DbInterner { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs index 30738dea5c958..cf492e65c3ff3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs @@ -229,7 +229,7 @@ impl<'db> Predicate<'db> { /// Flips the polarity of a Predicate. /// /// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`. - pub fn flip_polarity(self) -> Option> { + pub fn flip_polarity(self, interner: DbInterner<'db>) -> Option> { let kind = self .kind() .map_bound(|kind| match kind { @@ -245,7 +245,7 @@ impl<'db> Predicate<'db> { }) .transpose()?; - Some(Predicate::new(DbInterner::conjure(), kind)) + Some(Predicate::new(interner, kind)) } } @@ -355,13 +355,6 @@ impl<'db> rustc_type_ir::inherent::SliceLike for Clauses<'db> { } } -impl<'db> Default for Clauses<'db> { - #[inline] - fn default() -> Self { - Clauses::empty(DbInterner::conjure()) - } -} - impl<'db> rustc_type_ir::inherent::Clauses> for Clauses<'db> {} impl<'db> rustc_type_ir::TypeSuperFoldable> for Clauses<'db> { @@ -444,8 +437,8 @@ pub struct ParamEnv<'db> { } impl<'db> ParamEnv<'db> { - pub fn empty() -> Self { - ParamEnv { clauses: Clauses::empty(DbInterner::conjure()) } + pub fn empty(interner: DbInterner<'db>) -> Self { + ParamEnv { clauses: Clauses::empty(interner) } } pub fn clauses(self) -> Clauses<'db> { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 63b834a8d1d59..ac9adf592da34 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1665,9 +1665,11 @@ impl Enum { /// The type of the enum variant bodies. pub fn variant_body_ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - let interner = DbInterner::new_no_crate(db); + let krate = self.id.lookup(db).container.krate(db); + let interner = DbInterner::new_with(db, krate); Type::new_for_crate( - self.id.lookup(db).container.krate(db), + db, + krate, match EnumSignature::variant_body_type(db, self.id) { layout::IntegerType::Pointer(sign) => match sign { true => Ty::new_int(interner, rustc_type_ir::IntTy::Isize), @@ -2267,8 +2269,11 @@ impl DefWithBody { mir::MirSpan::Unknown => continue, }; acc.push( - MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.as_ref()), span } - .into(), + MovedOutOfRef { + ty: Type::new_for_crate(db, krate, moof.ty.as_ref()), + span, + } + .into(), ) } let mol = &borrowck_result.mutability_of_locals; @@ -3485,7 +3490,7 @@ impl BuiltinType { pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| all_crates(db)[0]); let interner = DbInterner::new_no_crate(db); - Type::new_for_crate(core, Ty::from_builtin_type(interner, self.inner)) + Type::new_for_crate(db, core, Ty::from_builtin_type(interner, self.inner)) } pub fn name(self) -> Name { @@ -5534,8 +5539,13 @@ impl<'db> Type<'db> { Type { env: environment, ty } } - pub(crate) fn new_for_crate(krate: base_db::Crate, ty: Ty<'db>) -> Self { - Type { env: empty_param_env(krate), ty } + pub(crate) fn new_for_crate( + db: &'db dyn HirDatabase, + krate: base_db::Crate, + ty: Ty<'db>, + ) -> Self { + let interner = DbInterner::new_with(db, krate); + Type { env: ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate }, ty } } fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty<'db>) -> Self { @@ -5588,15 +5598,18 @@ impl<'db> Type<'db> { Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) } - pub fn new_slice(ty: Self) -> Self { - let interner = DbInterner::conjure(); + pub fn new_slice(db: &'db dyn HirDatabase, ty: Self) -> Self { + let interner = DbInterner::new_no_crate(db); Type { env: ty.env, ty: Ty::new_slice(interner, ty.ty) } } - pub fn new_tuple(krate: base_db::Crate, tys: &[Self]) -> Self { + pub fn new_tuple(db: &'db dyn HirDatabase, krate: base_db::Crate, tys: &[Self]) -> Self { let tys = tys.iter().map(|it| it.ty); - let interner = DbInterner::conjure(); - Type { env: empty_param_env(krate), ty: Ty::new_tup_from_iter(interner, tys) } + let interner = DbInterner::new_with(db, krate); + Type { + env: ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate }, + ty: Ty::new_tup_from_iter(interner, tys), + } } pub fn is_unit(&self) -> bool { @@ -5708,8 +5721,8 @@ impl<'db> Type<'db> { Some((self.derived(ty), m)) } - pub fn add_reference(&self, mutability: Mutability) -> Self { - let interner = DbInterner::conjure(); + pub fn add_reference(&self, db: &'db dyn HirDatabase, mutability: Mutability) -> Self { + let interner = DbInterner::new_no_crate(db); let ty_mutability = match mutability { Mutability::Shared => hir_ty::next_solver::Mutability::Not, Mutability::Mut => hir_ty::next_solver::Mutability::Mut, @@ -6012,9 +6025,9 @@ impl<'db> Type<'db> { } } - pub fn fingerprint_for_trait_impl(&self) -> Option { + pub fn fingerprint_for_trait_impl(&self, db: &'db dyn HirDatabase) -> Option { fast_reject::simplify_type( - DbInterner::conjure(), + DbInterner::new_no_crate(db), self.ty, fast_reject::TreatParams::AsRigid, ) @@ -7430,9 +7443,10 @@ fn param_env_from_resolver<'db>( resolver: &Resolver<'_>, ) -> ParamEnvAndCrate<'db> { ParamEnvAndCrate { - param_env: resolver - .generic_def() - .map_or_else(ParamEnv::empty, |generic_def| db.trait_environment(generic_def.into())), + param_env: resolver.generic_def().map_or_else( + || ParamEnv::empty(DbInterner::new_no_crate(db)), + |generic_def| db.trait_environment(generic_def.into()), + ), krate: resolver.krate(), } } @@ -7451,10 +7465,6 @@ fn body_param_env_from_has_crate<'db>( ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.krate(db) } } -fn empty_param_env<'db>(krate: base_db::Crate) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { param_env: ParamEnv::empty(), krate } -} - // FIXME: We probably don't want to expose this. pub trait MacroCallIdExt { fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc; 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 06182620c86e2..ba62cc11c3c06 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -330,13 +330,14 @@ impl<'db> SourceAnalyzer<'db> { } fn trait_environment(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> { - self.param_and(self.body_or_sig.as_ref().map_or_else(ParamEnv::empty, |body_or_sig| { - match *body_or_sig { + self.param_and(self.body_or_sig.as_ref().map_or_else( + || ParamEnv::empty(DbInterner::new_no_crate(db)), + |body_or_sig| match *body_or_sig { BodyOrSig::Body { def, .. } => db.trait_environment(def.into()), BodyOrSig::VariantFields { def, .. } => db.trait_environment(def.into()), BodyOrSig::Sig { def, .. } => db.trait_environment(def.into()), - } - })) + }, + )) } pub(crate) fn expr_id(&self, expr: ast::Expr) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index af2371d49349d..1cc6766bfb3b4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -145,7 +145,7 @@ impl<'db> LookupTable<'db> { self.data .iter() .find(|(t, _)| { - t.add_reference(Mutability::Shared).could_unify_with_deeply(db, ty) + t.add_reference(db, Mutability::Shared).could_unify_with_deeply(db, ty) }) .map(|(t, it)| { it.exprs(t) diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 8700326e17b37..67b6fd64c1e6d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -774,7 +774,7 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( .filter(|_| should_continue()) .map(|params| { let tys: Vec> = params.iter().map(|it| it.ty(db)).collect(); - let tuple_ty = Type::new_tuple(module.krate(db).into(), &tys); + let tuple_ty = Type::new_tuple(db, module.krate(db).into(), &tys); let expr = Expr::Tuple { ty: tuple_ty.clone(), params }; lookup.insert(tuple_ty, iter::once(expr.clone())); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 096f6678a58b8..3ab4279b0addb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -841,8 +841,8 @@ pub(crate) fn convert_reference_type<'db>( } fn could_deref_to_target(ty: &hir::Type<'_>, target: &hir::Type<'_>, db: &dyn HirDatabase) -> bool { - let ty_ref = ty.add_reference(hir::Mutability::Shared); - let target_ref = target.add_reference(hir::Mutability::Shared); + let ty_ref = ty.add_reference(db, hir::Mutability::Shared); + let target_ref = target.add_reference(db, hir::Mutability::Shared); ty_ref.could_coerce_to(db, &target_ref) } @@ -870,7 +870,7 @@ fn handle_as_ref_slice( famous_defs: &FamousDefs<'_, '_>, ) -> Option<(ReferenceConversionType, bool)> { let type_argument = ty.type_arguments().next()?; - let slice_type = hir::Type::new_slice(type_argument); + let slice_type = hir::Type::new_slice(db, type_argument); ty.impls_trait(db, famous_defs.core_convert_AsRef()?, slice::from_ref(&slice_type)).then_some(( ReferenceConversionType::AsRefSlice, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 2cc2200df901b..c64bdf6bc5e15 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -93,7 +93,7 @@ pub(crate) fn complete_dot( // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid let iter = receiver_ty .autoderef(ctx.db) - .map(|ty| ty.strip_references().add_reference(hir::Mutability::Shared)) + .map(|ty| ty.strip_references().add_reference(ctx.db, hir::Mutability::Shared)) .find_map(|ty| ty.into_iterator_iter(ctx.db)) .map(|ty| (ty, SmolStr::new_static("iter()"))); // Does ::IntoIter` exist? 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 faeb97f93f7d1..6be9813619293 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 @@ -641,7 +641,7 @@ fn expected_type_and_name<'db>( } for _ in refs_level..0 { cov_mark::hit!(expected_type_fn_param_deref); - ty = ty.add_reference(hir::Mutability::Shared); + ty = ty.add_reference(sema.db, hir::Mutability::Shared); } ty } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs index e0501b5e44ea3..6edc550b331a1 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs @@ -814,13 +814,13 @@ fn trait_applicable_items<'db>( let mut deref_chain = trait_candidate.receiver_ty.autoderef(db).collect::>(); // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) if let Some((ty, _len)) = deref_chain.last().and_then(|ty| ty.as_array(db)) { - let slice = Type::new_slice(ty); + let slice = Type::new_slice(db, ty); deref_chain.push(slice); } deref_chain .into_iter() .flat_map(|ty| { - let fingerprint = ty.fingerprint_for_trait_impl()?; + let fingerprint = ty.fingerprint_for_trait_impl(db)?; let mut crates = vec![]; if let Some(adt) = ty.as_adt() { 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 250c692d16f80..08791fecbedbc 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 @@ -88,7 +88,7 @@ fn add_reference( let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into())); let (_, mutability) = d.expected.as_reference()?; - let actual_with_ref = d.actual.add_reference(mutability); + let actual_with_ref = d.actual.add_reference(ctx.db(), mutability); if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) { return None; } From e3f5ff4b2b9bc9bfadb96e74ea1db417579226d1 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 10 May 2026 03:27:22 +0300 Subject: [PATCH 005/114] Make interneds return their loc by ref --- .../rust-analyzer/crates/base-db/src/lib.rs | 1 + .../rust-analyzer/crates/hir-def/src/db.rs | 7 +-- .../crates/hir-def/src/nameres.rs | 2 +- .../crates/hir-def/src/nameres/assoc.rs | 4 +- .../rust-analyzer/crates/hir-expand/src/db.rs | 12 ++-- .../crates/hir-expand/src/lib.rs | 63 ++++++++++--------- .../crates/hir-ty/src/opaques.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 4 +- .../rust-analyzer/crates/hir/src/semantics.rs | 4 +- .../crates/ide/src/navigation_target.rs | 2 +- 10 files changed, 54 insertions(+), 47 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index a209a0e631e0b..fd47403ec49bf 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -49,6 +49,7 @@ macro_rules! impl_intern_key { #[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct $id { + #[returns(ref)] pub loc: $loc, } 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 11e5c54246440..6301eb7901664 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -7,8 +7,7 @@ use hir_expand::{ use triomphe::Arc; use crate::{ - AssocItemId, AttrDefId, Macro2Loc, MacroExpander, MacroId, MacroRulesLoc, MacroRulesLocFlags, - TraitId, + AssocItemId, AttrDefId, MacroExpander, MacroId, MacroRulesLocFlags, TraitId, attrs::AttrFlags, item_tree::{ItemTree, file_item_tree}, nameres::crate_def_map, @@ -81,7 +80,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { match id { MacroId::Macro2Id(it) => { - let loc: Macro2Loc = it.lookup(db); + let loc = it.lookup(db); MacroDefId { krate: loc.container.krate(db), @@ -92,7 +91,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { } } MacroId::MacroRulesId(it) => { - let loc: MacroRulesLoc = it.lookup(db); + let loc = it.lookup(db); MacroDefId { krate: loc.container.krate(db), 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 88e3408a33b14..ef7fb0888f3bd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -427,7 +427,7 @@ pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefM #[salsa_macros::tracked(returns(ref))] pub fn block_def_map(db: &dyn DefDatabase, block_id: BlockId) -> DefMap { - let BlockLoc { ast_id, module } = block_id.lookup(db); + let BlockLoc { ast_id, module } = *block_id.lookup(db); let visibility = Visibility::Module(module, VisibilityExplicitness::Implicit); let module_data = 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 b1d554738f71f..7b5b39cb08cbe 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 @@ -50,7 +50,7 @@ impl TraitItems { db: &dyn DefDatabase, tr: TraitId, ) -> (TraitItems, DefDiagnostics) { - let ItemLoc { container: module_id, id: ast_id } = tr.lookup(db); + let ItemLoc { container: module_id, id: ast_id } = *tr.lookup(db); let ast_id_map = db.ast_id_map(ast_id.file_id); let source = ast_id.with_value(ast_id_map.get(ast_id.value)).to_node(db); if source.eq_token().is_some() { @@ -115,7 +115,7 @@ impl ImplItems { #[salsa::tracked(returns(ref))] pub fn of(db: &dyn DefDatabase, id: ImplId) -> (ImplItems, DefDiagnostics) { let _p = tracing::info_span!("impl_items_with_diagnostics_query").entered(); - let ItemLoc { container: module_id, id: ast_id } = id.lookup(db); + let ItemLoc { container: module_id, id: ast_id } = *id.lookup(db); let collector = AssocItemCollector::new(db, module_id, ItemContainerId::ImplId(id), ast_id.file_id); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index beae6e843e493..b09f69a295915 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -149,8 +149,8 @@ fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> match file { HirFileId::FileId(_) => SyntaxContext::root(edition), HirFileId::MacroFile(m) => { - let kind = m.loc(db).kind; - db.macro_arg_considering_derives(m, &kind).2.ctx + let kind = &m.loc(db).kind; + db.macro_arg_considering_derives(m, kind).2.ctx } } } @@ -542,11 +542,11 @@ impl<'db> TokenExpander<'db> { } } -fn macro_expand( - db: &dyn ExpandDatabase, +fn macro_expand<'db>( + db: &'db dyn ExpandDatabase, macro_call_id: MacroCallId, - loc: MacroCallLoc, -) -> ExpandResult<(Cow<'_, tt::TopSubtree>, MatchedArmIndex)> { + loc: &MacroCallLoc, +) -> ExpandResult<(Cow<'db, tt::TopSubtree>, MatchedArmIndex)> { let _p = tracing::info_span!("macro_expand").entered(); let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 0850d6156d112..c98d072784d4e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -81,7 +81,7 @@ macro_rules! impl_intern_lookup { impl $crate::Lookup for $id { type Database = dyn $db; type Data = $loc; - fn lookup(&self, db: &Self::Database) -> Self::Data { + fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data { self.loc(db) } } @@ -98,7 +98,7 @@ pub trait Intern { pub trait Lookup { type Database: ?Sized; type Data; - fn lookup(&self, db: &Self::Database) -> Self::Data; + fn lookup<'db>(&self, db: &'db Self::Database) -> &'db Self::Data; } impl_intern_lookup!(ExpandDatabase, MacroCallId, MacroCallLoc); @@ -714,24 +714,27 @@ impl MacroCallKind { /// - fn_like! {}, it spans the path and token tree /// - #\[derive], it spans the `#[derive(...)]` attribute and the annotated item /// - #\[attr], it spans the `#[attr(...)]` attribute and the annotated item - pub fn original_call_range_with_input(self, db: &dyn ExpandDatabase) -> FileRange { - let mut kind = self; + pub fn original_call_range_with_input(&self, db: &dyn ExpandDatabase) -> FileRange { + let get_range = |kind: &_| match kind { + MacroCallKind::FnLike { ast_id, .. } => ast_id.erase(), + MacroCallKind::Derive { ast_id, .. } => ast_id.erase(), + MacroCallKind::Attr { ast_id, .. } => ast_id.erase(), + }; + + let mut ast_id = get_range(self); + let mut file_id = self.file_id(); let file_id = loop { - match kind.file_id() { + match file_id { HirFileId::MacroFile(file) => { - kind = file.loc(db).kind; + let kind = &file.loc(db).kind; + ast_id = get_range(kind); + file_id = kind.file_id(); } HirFileId::FileId(file_id) => break file_id, } }; - let range = match kind { - MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(), - MacroCallKind::Derive { ast_id, .. } => ast_id.to_ptr(db).text_range(), - MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).text_range(), - }; - - FileRange { range, file_id } + FileRange { range: ast_id.to_ptr(db).text_range(), file_id } } /// Returns the original file range that best describes the location of this macro call. @@ -739,18 +742,8 @@ impl MacroCallKind { /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the /// attribute's range, and derives get only the specific derive that is being referred to. - pub fn original_call_range(self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { - let mut kind = self; - let file_id = loop { - match kind.file_id() { - HirFileId::MacroFile(file) => { - kind = file.loc(db).kind; - } - HirFileId::FileId(file_id) => break file_id, - } - }; - - let range = match kind { + pub fn original_call_range(&self, db: &dyn ExpandDatabase, krate: Crate) -> FileRange { + let get_range = |kind: &_| match kind { MacroCallKind::FnLike { ast_id, .. } => { let node = ast_id.to_node(db); node.path() @@ -761,11 +754,24 @@ impl MacroCallKind { } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: should be the range of the macro name, not the whole derive - derive_attr_index.find_attr_range(db, krate, ast_id).1.syntax().text_range() + derive_attr_index.find_attr_range(db, krate, *ast_id).1.syntax().text_range() } // FIXME: handle `cfg_attr` MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { - attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).1.syntax().text_range() + attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range() + } + }; + + let mut range = get_range(self); + let mut file_id = self.file_id(); + let file_id = loop { + match file_id { + HirFileId::MacroFile(file) => { + let kind = &file.loc(db).kind; + range = get_range(kind); + file_id = kind.file_id(); + } + HirFileId::FileId(file_id) => break file_id, } }; @@ -797,7 +803,7 @@ pub struct ExpansionInfo<'db> { arg: InFile>, exp_map: &'db ExpansionSpanMap, arg_map: SpanMap<'db>, - loc: MacroCallLoc, + loc: &'db MacroCallLoc, } impl<'db> ExpansionInfo<'db> { @@ -1056,6 +1062,7 @@ intern::impl_internable!(ModPath); #[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[doc(alias = "MacroFileId")] pub struct MacroCallId { + #[returns(ref)] pub loc: MacroCallLoc, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs index 4244b1bac443b..79d2fa0c2d1c2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs @@ -132,7 +132,7 @@ pub(crate) fn tait_hidden_types( let param_env = db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(type_alias))); - let defining_bodies = tait_defining_bodies(db, &loc); + let defining_bodies = tait_defining_bodies(db, loc); let mut result = ArenaMap::with_capacity(taits_count); for defining_body in defining_bodies { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index ac9adf592da34..894bfe91b5b34 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -7467,11 +7467,11 @@ fn body_param_env_from_has_crate<'db>( // FIXME: We probably don't want to expose this. pub trait MacroCallIdExt { - fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc; + fn loc(self, db: &dyn HirDatabase) -> &hir_expand::MacroCallLoc; } impl MacroCallIdExt for span::MacroCallId { #[inline] - fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc { + fn loc(self, db: &dyn HirDatabase) -> &hir_expand::MacroCallLoc { hir_expand::MacroCallId::from(self).loc(db) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index a1bbe47188b46..d0202c1054c50 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1323,7 +1323,7 @@ impl<'db> SemanticsImpl<'db> { .map(|(call_id, item)| { let item_range = item.syntax().text_range(); let loc = call_id.loc(db); - let text_range = match loc.kind { + let text_range = match &loc.kind { hir_expand::MacroCallKind::Attr { censored_attr_ids: attr_ids, .. @@ -2436,7 +2436,7 @@ impl<'db> SemanticsImpl<'db> { AnyImplId::ImplId(id) => id, AnyImplId::BuiltinDeriveImplId(id) => return Some(id.loc(self.db).adt.into()), }; - let source = hir_def::src::HasSource::ast_ptr(&id.loc(self.db), self.db); + let source = hir_def::src::HasSource::ast_ptr(id.loc(self.db), self.db); let mut file_id = source.file_id; let adt_ast_id = loop { let macro_call = file_id.macro_file()?; diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index f70bb3353fd5d..b8c14dc09f9a2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -964,7 +964,7 @@ pub(crate) fn orig_range_with_focus_r( // *should* contain the name _ => { let call = call(); - let kind = call.kind; + let kind = &call.kind; let range = kind.clone().original_call_range_with_input(db); //If the focus range is in the attribute/derive body, we // need to point the call site to the entire body, if not, fall back From 59eeffeaf3e9feb2d718b28634f10e94033e1061 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 10 May 2026 03:55:17 +0300 Subject: [PATCH 006/114] Encode the name instead of index in `EnumVariantId` The most important reason is incrementality. While not a lot of things depend on the stability of `EnumVariantId`, it's still useful to have them stable. However it turns out that many things actually do want the name, more than those that want the index. --- .../crates/hir-def/src/find_path.rs | 4 +- .../crates/hir-def/src/lang_item.rs | 2 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 19 +++++++- .../crates/hir-def/src/nameres/collector.rs | 2 +- .../hir-def/src/nameres/path_resolution.rs | 11 ++--- .../crates/hir-def/src/signatures.rs | 45 ++++++++++--------- .../crates/hir-ty/src/builtin_derive.rs | 2 +- .../crates/hir-ty/src/consteval.rs | 6 +-- .../hir-ty/src/diagnostics/decl_check.rs | 6 +-- .../hir-ty/src/diagnostics/match_check.rs | 8 +--- .../diagnostics/match_check/pat_analysis.rs | 5 +-- .../crates/hir-ty/src/display.rs | 16 +------ .../rust-analyzer/crates/hir-ty/src/drop.rs | 4 +- .../closure/analysis/expr_use_visitor.rs | 2 +- .../crates/hir-ty/src/inhabitedness.rs | 2 +- .../crates/hir-ty/src/layout/adt.rs | 4 +- .../crates/hir-ty/src/mir/eval.rs | 5 +-- .../crates/hir-ty/src/mir/lower.rs | 5 +-- .../crates/hir-ty/src/mir/pretty.rs | 4 +- .../crates/hir-ty/src/next_solver/interner.rs | 4 +- .../crates/hir-ty/src/representability.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 4 +- .../crates/hir-ty/src/variance.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 14 +++--- .../hir/src/semantics/child_by_source.rs | 2 +- .../rust-analyzer/crates/hir/src/symbols.rs | 2 +- 26 files changed, 85 insertions(+), 99 deletions(-) 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 2a1b7f7cb0cf8..6aaf8a674eee9 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 @@ -142,9 +142,7 @@ fn find_path_inner(ctx: &FindPathCtx<'_>, item: ItemInNs, max_len: usize) -> Opt // - if the item is an enum variant, refer to it via the enum let loc = variant.lookup(ctx.db); if let Some(mut path) = find_path_inner(ctx, ItemInNs::Types(loc.parent.into()), max_len) { - path.push_segment( - loc.parent.enum_variants(ctx.db).variants[loc.index as usize].1.clone(), - ); + path.push_segment(loc.name.clone()); return Some(path); } // If this doesn't work, it seems we have no way of referring to the 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 c2cbb9eda7268..483c3f16c67fb 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 @@ -74,7 +74,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option { lang_items.collect_lang_item(db, e); - e.enum_variants(db).variants.iter().for_each(|&(id, _, _)| { + e.enum_variants(db).variants.values().for_each(|&(id, _)| { lang_items.collect_lang_item(db, id); }); } 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 3aaf89102fab9..bf265cd42e945 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -367,20 +367,35 @@ impl ExternBlockId { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct EnumVariantLoc { pub id: AstId, pub parent: EnumId, - pub index: u32, + pub name: Name, } impl_intern!(EnumVariantId, EnumVariantLoc); impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId); +impl EnumVariantLoc { + pub fn index(&self, db: &dyn DefDatabase) -> usize { + self.parent + .enum_variants(db) + .variants + .get_full(&self.name) + .expect("parent enum should include this variant") + .0 + } +} + impl EnumVariantId { pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields { VariantFields::of(db, self.into()) } + pub fn index(self, db: &dyn DefDatabase) -> usize { + self.loc(db).index(db) + } + pub fn fields_with_source_map( self, db: &dyn DefDatabase, 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 5ef51dbb2ca53..8e16c5ece608d 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 @@ -1027,7 +1027,7 @@ impl<'db> DefCollector<'db> { .enum_variants(self.db) .variants .iter() - .map(|&(variant, ref name, _)| { + .map(|(name, &(variant, _))| { let res = PerNs::both(variant.into(), variant.into(), vis, None); (Some(name.clone()), res) }) 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 cf33cecf5fba9..54fd30fe6ba3e 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 @@ -519,12 +519,8 @@ impl DefMap { // enum variant cov_mark::hit!(can_import_enum_variant); - let res = e - .enum_variants(db) - .variants - .iter() - .find(|(_, name, _)| name == segment) - .map(|&(variant, _, shape)| match shape { + let res = e.enum_variants(db).variants.get(segment).map(|&(variant, shape)| { + match shape { FieldsShape::Record => { PerNs::types(variant.into(), Visibility::Public, None) } @@ -534,7 +530,8 @@ impl DefMap { Visibility::Public, None, ), - }); + } + }); // FIXME: Need to filter visibility here and below? Not sure. return match res { Some(res) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index e58befae20c04..463fc9ceff82a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -19,8 +19,9 @@ use thin_vec::ThinVec; use triomphe::Arc; use crate::{ - ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, HasModule, ImplId, - ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId, + ConstId, EnumId, EnumVariantId, EnumVariantLoc, ExternBlockId, FunctionId, FxIndexMap, + HasModule, ImplId, ItemContainerId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, + UnionId, VariantId, attrs::AttrFlags, db::DefDatabase, expr_store::{ @@ -1055,7 +1056,7 @@ pub struct InactiveEnumVariantCode { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumVariants { - pub variants: Box<[(EnumVariantId, Name, FieldsShape)]>, + pub variants: FxIndexMap, } #[salsa::tracked] @@ -1071,23 +1072,24 @@ impl EnumVariants { let mut diagnostics = ThinVec::new(); let cfg_options = loc.container.krate(db).cfg_options(db); - let mut index = 0; let Some(variants) = source.value.variant_list() else { - return (EnumVariants { variants: Box::default() }, None); + return (EnumVariants { variants: FxIndexMap::default() }, None); }; - let variants = variants + let mut variants = variants .variants() .filter_map(|variant| { let ast_id = ast_id_map.ast_id(&variant); match AttrFlags::is_cfg_enabled_for(&variant, cfg_options) { Ok(()) => { - let enum_variant = - EnumVariantLoc { id: source.with_value(ast_id), parent: e, index } - .intern(db); - index += 1; let name = as_name_opt(variant.name()); + let enum_variant = EnumVariantLoc { + id: source.with_value(ast_id), + parent: e, + name: name.clone(), + } + .intern(db); let shape = adt_shape(variant.kind()); - Some((enum_variant, name, shape)) + Some((name, (enum_variant, shape))) } Err(cfg) => { diagnostics.push(InactiveEnumVariantCode { @@ -1099,7 +1101,8 @@ impl EnumVariants { } } }) - .collect(); + .collect::>(); + variants.shrink_to_fit(); (EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics)) } @@ -1107,26 +1110,28 @@ impl EnumVariants { impl EnumVariants { pub fn variant(&self, name: &Name) -> Option { - self.variants.iter().find_map(|(v, n, _)| if n == name { Some(*v) } else { None }) + self.variants.get(name).map(|&(id, _)| id) } pub fn variant_name_by_id(&self, variant_id: EnumVariantId) -> Option { self.variants .iter() - .find_map(|(id, name, _)| if *id == variant_id { Some(name.clone()) } else { None }) + .find_map(|(name, (id, _))| if *id == variant_id { Some(name.clone()) } else { None }) } // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448) pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool { - self.variants.iter().all(|&(v, _, _)| { + self.variants.values().all(|&(v, shape)| { // The condition check order is slightly modified from rustc // to improve performance by early returning with relatively fast checks - let variant = v.fields(db); - if !variant.fields().is_empty() { - return false; - } + // The outer if condition is whether this variant has const ctor or not - if !matches!(variant.shape, FieldsShape::Unit) { + if !matches!(shape, FieldsShape::Unit) { + let variant = v.fields(db); + if !variant.fields().is_empty() { + return false; + } + let body = Body::of(db, v.into()); // A variant with explicit discriminant if !matches!(body[body.root_expr()], crate::hir::Expr::Missing) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs index 4e19fb80671f9..928e3da6e8c9a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builtin_derive.rs @@ -305,7 +305,7 @@ fn simple_trait_predicates<'db>( loc.trait_, ), AdtId::EnumId(id) => { - for &(variant_id, _, _) in &id.enum_variants(interner.db).variants { + for &(variant_id, _) in id.enum_variants(interner.db).variants.values() { extend_assoc_type_bounds( interner, &mut assoc_type_bounds, 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 e1d6cec59421e..9a6e2a87f2f8e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -400,12 +400,10 @@ pub(crate) fn const_eval_discriminant_variant( let body = Body::of(db, def); let loc = variant_id.lookup(db); if matches!(body[body.root_expr()], Expr::Missing) { - let prev_idx = loc.index.checked_sub(1); + let prev_idx = loc.index(db).checked_sub(1); let value = match prev_idx { Some(prev_idx) => { - 1 + db.const_eval_discriminant( - loc.parent.enum_variants(db).variants[prev_idx as usize].0, - )? + 1 + db.const_eval_discriminant(loc.parent.enum_variants(db).variants[prev_idx].0)? } _ => 0, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 76fbb66b063f0..ace361633916c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -504,15 +504,15 @@ impl<'a> DeclValidator<'a> { fn validate_enum_variants(&mut self, enum_id: EnumId) { let data = enum_id.enum_variants(self.db); - for (variant_id, _, _) in data.variants.iter() { + for (variant_id, _) in data.variants.values() { self.validate_enum_variant_fields(*variant_id); } let edition = self.edition(enum_id); let mut enum_variants_replacements = data .variants - .iter() - .filter_map(|(_, name, _)| { + .keys() + .filter_map(|name| { to_camel_case(&name.display_no_db(edition).to_smolstr()).map(|new_name| { Replacement { current_name: name.clone(), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index 8356329d96ae3..14bff65220702 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -330,13 +330,7 @@ impl<'db> HirDisplay<'db> for Pat<'db> { match variant { VariantId::EnumVariantId(v) => { let loc = v.lookup(f.db); - write!( - f, - "{}", - loc.parent.enum_variants(f.db).variants[loc.index as usize] - .1 - .display(f.db, f.edition()) - )?; + write!(f, "{}", loc.name.display(f.db, f.edition()))?; } VariantId::StructId(s) => write!( f, 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 46959aaa5ac03..984ac1abfbb36 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 @@ -49,8 +49,7 @@ pub(crate) struct EnumVariantContiguousIndex(usize); impl EnumVariantContiguousIndex { fn from_enum_variant_id(db: &dyn HirDatabase, target_evid: EnumVariantId) -> Self { // Find the index of this variant in the list of variants. - use hir_def::Lookup; - let i = target_evid.lookup(db).index as usize; + let i = target_evid.index(db); EnumVariantContiguousIndex(i) } @@ -438,7 +437,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> { ConstructorSet::NoConstructors } else { let mut variants = IndexVec::with_capacity(enum_data.variants.len()); - for &(variant, _, _) in enum_data.variants.iter() { + for &(variant, _) in enum_data.variants.values() { let is_uninhabited = is_enum_variant_uninhabited_from( cx.infcx, variant, subst, cx.module, self.env, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 326f920b118e4..03d0e15fecc7e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -990,13 +990,7 @@ fn render_const_scalar_inner<'db>( return f.write_str(""); }; let loc = var_id.lookup(f.db); - write!( - f, - "{}", - loc.parent.enum_variants(f.db).variants[loc.index as usize] - .1 - .display(f.db, f.edition()) - )?; + write!(f, "{}", loc.name.display(f.db, f.edition()))?; let field_types = f.db.field_types(var_id.into()); render_variant_after_name( var_id.fields(f.db), @@ -1362,13 +1356,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } CallableDefId::EnumVariantId(e) => { let loc = e.lookup(db); - write!( - f, - "{}", - loc.parent.enum_variants(db).variants[loc.index as usize] - .1 - .display(db, f.edition()) - )? + write!(f, "{}", loc.name.display(db, f.edition()))? } }; f.end_location_link(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 61e6720a29186..726b862fe1e7d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -100,8 +100,8 @@ fn has_drop_glue_impl<'db>( AdtId::EnumId(id) => id .enum_variants(db) .variants - .iter() - .map(|&(variant, _, _)| { + .values() + .map(|&(variant, _)| { db.field_types(variant.into()) .iter() .map(|(_, field_ty)| { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index deafff6b43e06..e7e97df48e6f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -1473,7 +1473,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { fn variant_index_for_adt(&self, pat_id: PatId) -> Result<(u32, VariantId)> { let variant = self.cx.result.variant_resolution_for_pat(pat_id).ok_or(ErrorGuaranteed)?; let variant_idx = match variant { - VariantId::EnumVariantId(variant) => variant.loc(self.cx.db).index, + VariantId::EnumVariantId(variant) => variant.index(self.cx.db) as u32, VariantId::StructId(_) | VariantId::UnionId(_) => 0, }; Ok((variant_idx, variant)) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index 0070d14f373dc..26253fbabf1d4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -125,7 +125,7 @@ impl<'a, 'db> UninhabitedFrom<'a, 'db> { AdtId::EnumId(e) => { let enum_data = e.enum_variants(self.db()); - for &(variant, _, _) in enum_data.variants.iter() { + for &(variant, _) in enum_data.variants.values() { let variant_inhabitedness = self.visit_variant(variant.into(), subst); match variant_inhabitedness { Break(VisiblyUninhabited) => (), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index b7e1697059633..22dd53ca2dd01 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -60,8 +60,8 @@ pub fn layout_of_adt_query( let variants = e.enum_variants(db); let r = variants .variants - .iter() - .map(|&(v, _, _)| handle_variant(v.into(), v.fields(db))) + .values() + .map(|&(v, _)| handle_variant(v.into(), v.fields(db))) .collect::, _>>()?; (r, AttrFlags::repr(db, e.into()).unwrap_or_default(), false) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 3372f6ec2ed9b..e080d3d71334d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -844,7 +844,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { Variants::Multiple { variants, .. } => { &variants[match f.parent { hir_def::VariantId::EnumVariantId(it) => { - RustcEnumVariantIdx(it.lookup(self.db).index as usize) + RustcEnumVariantIdx(it.index(self.db)) } _ => { return Err(MirEvalError::InternalError( @@ -1837,8 +1837,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { _ => not_supported!("multi variant layout for non-enums"), }; let mut discriminant = self.const_eval_discriminant(enum_variant_id)?; - let lookup = enum_variant_id.lookup(self.db); - let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index as usize); + let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.index(self.db)); let variant_layout = variants[rustc_enum_variant_idx].clone(); let have_tag = match tag_encoding { TagEncoding::Direct => true, 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 3852db909e392..27efc5cfd5b5d 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 @@ -2309,10 +2309,7 @@ pub fn mir_body_query<'db>(db: &'db dyn HirDatabase, def: InferBodyId) -> Result .to_string(), InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(it)) => { let loc = it.lookup(db); - loc.parent.enum_variants(db).variants[loc.index as usize] - .1 - .display(db, edition) - .to_string() + loc.name.display(db, edition).to_string() } InferBodyId::AnonConstId(_) => "{const}".to_owned(), }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 777cf170bc262..a534e50997793 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -341,9 +341,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { w!( this, " as {}).{}", - loc.parent.enum_variants(this.db).variants[loc.index as usize] - .1 - .display(this.db, this.display_target.edition), + loc.name.display(this.db, this.display_target.edition), name.display(this.db, this.display_target.edition) ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 9871042e61382..5ca0e67d292e2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -608,8 +608,8 @@ impl<'db> inherent::AdtDef> for AdtDef { hir_def::AdtId::EnumId(id) => id .enum_variants(db) .variants - .iter() - .flat_map(|&(variant_id, _, _)| field_tys(variant_id.into())) + .values() + .flat_map(|&(variant_id, _)| field_tys(variant_id.into())) .collect(), }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs index 0b7dc4d30954f..0ea8003e5507d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/representability.rs @@ -29,7 +29,7 @@ pub(crate) fn representability(db: &dyn HirDatabase, id: AdtId) -> Representabil AdtId::StructId(id) => variant_representability(db, id.into()), AdtId::UnionId(id) => variant_representability(db, id.into()), AdtId::EnumId(id) => { - for &(variant, ..) in &id.enum_variants(db).variants { + for &(variant, ..) in id.enum_variants(db).variants.values() { rtry!(variant_representability(db, variant.into())); } Representability::Representable @@ -105,7 +105,7 @@ fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { AdtId::StructId(def_id) => handle_variant(def_id.into()), AdtId::UnionId(def_id) => handle_variant(def_id.into()), AdtId::EnumId(def_id) => { - for &(variant, ..) in &def_id.enum_variants(db).variants { + for &(variant, ..) in def_id.enum_variants(db).variants.values() { handle_variant(variant.into()); } } 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 d259ce7963f65..ce4eff701c90c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -460,7 +460,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { AdtId::EnumId(id) => variants.extend( id.enum_variants(&db) .variants - .iter() + .values() .map(|&(variant, ..)| (variant.into(), krate)), ), } @@ -600,7 +600,7 @@ pub(crate) fn visit_module( visit_body(db, body, cb); } ModuleDefId::AdtId(AdtId::EnumId(it)) => { - it.enum_variants(db).variants.iter().for_each(|&(it, _, _)| { + it.enum_variants(db).variants.values().for_each(|&(it, _)| { let body = Body::of(db, it.into()); cb(it.into()); visit_body(db, body, cb); 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 7eee78b8c4419..39d51dcee033b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -135,7 +135,7 @@ impl<'db> Context<'db> { AdtId::StructId(s) => add_constraints_from_variant(VariantId::StructId(s)), AdtId::UnionId(u) => add_constraints_from_variant(VariantId::UnionId(u)), AdtId::EnumId(e) => { - e.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| { + e.enum_variants(db).variants.values().for_each(|&(variant, _)| { add_constraints_from_variant(VariantId::EnumVariantId(variant)) }); } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 894bfe91b5b34..633fe1f86b691 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -796,7 +796,7 @@ impl Module { ); } } - for &(v, _, _) in &variants.variants { + for &(v, _) in variants.variants.values() { let source_map = &v.fields_with_source_map(db).1; push_ty_diagnostics( db, @@ -1644,7 +1644,7 @@ impl Enum { } pub fn variants(self, db: &dyn HirDatabase) -> Vec { - self.id.enum_variants(db).variants.iter().map(|&(id, _, _)| EnumVariant { id }).collect() + self.id.enum_variants(db).variants.values().map(|&(id, _)| EnumVariant { id }).collect() } pub fn num_variants(self, db: &dyn HirDatabase) -> usize { @@ -1763,9 +1763,7 @@ impl EnumVariant { } pub fn name(self, db: &dyn HirDatabase) -> Name { - let lookup = self.id.lookup(db); - let enum_ = lookup.parent; - enum_.enum_variants(db).variants[lookup.index as usize].1.clone() + self.id.lookup(db).name.clone() } pub fn fields(self, db: &dyn HirDatabase) -> Vec { @@ -1800,7 +1798,7 @@ impl EnumVariant { layout::Variants::Multiple { variants, .. } => Layout( { let lookup = self.id.lookup(db); - let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index as usize); + let rustc_enum_variant_idx = RustcEnumVariantIdx(lookup.index(db)); Arc::new(variants[rustc_enum_variant_idx].clone()) }, db.target_data_layout(parent_enum.krate(db).into()).unwrap(), @@ -5694,8 +5692,8 @@ impl<'db> Type<'db> { AdtId::EnumId(id) => id .enum_variants(self.interner.db()) .variants - .iter() - .map(|&(variant_id, _, _)| variant_id_to_fields(variant_id.into())) + .values() + .map(|&(variant_id, _)| variant_id_to_fields(variant_id.into())) .collect(), AdtId::UnionId(id) => { vec![variant_id_to_fields(id.into())] diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index babeb3591345c..97c5a451ab6b8 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -202,7 +202,7 @@ impl ChildBySource for EnumId { let ast_id_map = db.ast_id_map(loc.id.file_id); - self.enum_variants(db).variants.iter().for_each(|&(variant, _, _)| { + self.enum_variants(db).variants.values().for_each(|&(variant, _)| { res[keys::ENUM_VARIANT].insert(ast_id_map.get(variant.lookup(db).id.value), variant); }); let (_, source_map) = EnumSignature::with_source_map(db, *self); diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index ff56544d82e01..e9cfb63a1233c 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -190,7 +190,7 @@ impl<'a> SymbolCollector<'a> { let enum_name = Symbol::intern(EnumSignature::of(this.db, id).name.as_str()); this.with_container_name(Some(enum_name), |this| { let variants = id.enum_variants(this.db); - for (variant_id, variant_name, _) in &variants.variants { + for (variant_name, (variant_id, _)) in &variants.variants { this.push_decl(*variant_id, variant_name, true, None); } }); From 11a244390156c8178293ce6a48932db993cba856 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 10 May 2026 04:00:43 +0300 Subject: [PATCH 007/114] Remove `Option` from enum variants diagnostics `ThinVec` doesn't allocate when empty. --- .../rust-analyzer/crates/hir-def/src/lib.rs | 2 +- .../crates/hir-def/src/signatures.rs | 9 ++++--- src/tools/rust-analyzer/crates/hir/src/lib.rs | 26 +++++++++---------- 3 files changed, 18 insertions(+), 19 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 bf265cd42e945..7b7d046581155 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -298,7 +298,7 @@ impl EnumId { pub fn enum_variants_with_diagnostics( self, db: &dyn DefDatabase, - ) -> &(EnumVariants, Option>) { + ) -> &(EnumVariants, ThinVec) { EnumVariants::of(db, self) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index 463fc9ceff82a..e9307a125502a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -1,6 +1,6 @@ //! Item signature IR definitions -use std::{cell::LazyCell, ops::Not as _}; +use std::cell::LazyCell; use bitflags::bitflags; use cfg::{CfgExpr, CfgOptions}; @@ -1065,7 +1065,7 @@ impl EnumVariants { pub(crate) fn of( db: &dyn DefDatabase, e: EnumId, - ) -> (EnumVariants, Option>) { + ) -> (EnumVariants, ThinVec) { let loc = e.lookup(db); let source = loc.source(db); let ast_id_map = db.ast_id_map(source.file_id); @@ -1073,7 +1073,7 @@ impl EnumVariants { let mut diagnostics = ThinVec::new(); let cfg_options = loc.container.krate(db).cfg_options(db); let Some(variants) = source.value.variant_list() else { - return (EnumVariants { variants: FxIndexMap::default() }, None); + return (EnumVariants { variants: FxIndexMap::default() }, ThinVec::new()); }; let mut variants = variants .variants() @@ -1103,8 +1103,9 @@ impl EnumVariants { }) .collect::>(); variants.shrink_to_fit(); + diagnostics.shrink_to_fit(); - (EnumVariants { variants }, diagnostics.is_empty().not().then_some(diagnostics)) + (EnumVariants { variants }, diagnostics) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 633fe1f86b691..f9d28ea3b46a7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -781,20 +781,18 @@ impl Module { let (variants, diagnostics) = e.id.enum_variants_with_diagnostics(db); let file = e.id.lookup(db).id.file_id; let ast_id_map = db.ast_id_map(file); - if let Some(diagnostics) = &diagnostics { - for diag in diagnostics.iter() { - acc.push( - InactiveCode { - node: InFile::new( - file, - ast_id_map.get(diag.ast_id).syntax_node_ptr(), - ), - cfg: diag.cfg.clone(), - opts: diag.opts.clone(), - } - .into(), - ); - } + for diag in diagnostics { + acc.push( + InactiveCode { + node: InFile::new( + file, + ast_id_map.get(diag.ast_id).syntax_node_ptr(), + ), + cfg: diag.cfg.clone(), + opts: diag.opts.clone(), + } + .into(), + ); } for &(v, _) in variants.variants.values() { let source_map = &v.fields_with_source_map(db).1; From c0a4eb3497a19da2d88beee4c28d6204d2d7a7b3 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 8 May 2026 09:28:18 +0300 Subject: [PATCH 008/114] Provide better incrementality for modules Previously module ID reuse was based on order of creation solely. Now we store the parent module and name, and so Salsa will reuse the ID for modules that have the same name and parent. This is important as many interned IDs contain modules, so if the module is invalidated they too are. --- .../src/expr_store/tests/body/block.rs | 5 ++++ .../rust-analyzer/crates/hir-def/src/lib.rs | 23 ++++++++++++------- .../crates/hir-def/src/nameres.rs | 13 +++++++++-- .../crates/hir-def/src/nameres/collector.rs | 2 ++ 4 files changed, 33 insertions(+), 10 deletions(-) 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 71fcced2d85b9..906c4835c7959 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 @@ -199,6 +199,11 @@ fn f() { 4401, ), ), + containing_module_inside_def_map: None, + name_or_empty: Name { + symbol: "", + ctx: (), + }, }"#]], ); } 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 3aaf89102fab9..e6bf8a2a19aa0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -48,7 +48,7 @@ pub mod find_path; pub mod import_map; pub mod visibility; -use intern::Interned; +use intern::{Interned, sym}; use rustc_abi::ExternAbi; use thin_vec::ThinVec; @@ -457,6 +457,11 @@ pub struct ModuleIdLt<'db> { /// `BlockId` of that block expression. If `None`, this module is part of the crate-level /// `DefMap` of `krate`. pub block: Option, + /// The parent module of this module, or `None` if this is the root module inside the def + /// map (including for block def maps). + pub containing_module_inside_def_map: Option>, + /// The name of this module, or [`sym::__empty`] for the root module. + name_or_empty: Name, } pub type ModuleId = ModuleIdLt<'static>; @@ -502,21 +507,23 @@ impl ModuleId { } pub fn name(self, db: &dyn DefDatabase) -> Option { - let def_map = self.def_map(db); - let parent = def_map[self].parent?; - def_map[parent].children.iter().find_map(|(name, module_id)| { - if *module_id == self { Some(name.clone()) } else { None } - }) + let name = self.name_or_empty(db); + if *name.symbol() == sym::__empty { None } else { Some(name) } } /// Returns the module containing `self`, either the parent `mod`, or the module (or block) containing /// the block, if `self` corresponds to a block expression. pub fn containing_module(self, db: &dyn DefDatabase) -> Option { - self.def_map(db).containing_module(self) + self.containing_module_inside_def_map(db) + .or_else(|| self.block(db).map(|block| block.loc(db).module)) + .map(|module| { + // SAFETY: Not sure. + unsafe { module.to_static() } + }) } pub fn is_block_module(self, db: &dyn DefDatabase) -> bool { - self.block(db).is_some() && self.def_map(db).root_module_id() == self + self.block(db).is_some() && self.containing_module_inside_def_map(db).is_none() } } 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 88e3408a33b14..cf25e4cc7e07d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -66,7 +66,7 @@ use hir_expand::{ EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, mod_path::ModPath, name::Name, proc_macro::ProcMacroKind, }; -use intern::Symbol; +use intern::{Symbol, sym}; use itertools::Itertools; use rustc_hash::FxHashMap; use span::{Edition, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID}; @@ -465,7 +465,16 @@ impl DefMap { block: Option, ) -> DefMap { let mut modules = ModulesMap::new(); - let root = unsafe { ModuleIdLt::new(db, krate, block.map(|it| it.block)).to_static() }; + let root = unsafe { + ModuleIdLt::new( + db, + krate, + block.map(|it| it.block), + None, + Name::new_symbol_root(sym::__empty), + ) + .to_static() + }; modules.insert(root, module_data); DefMap { 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 5ef51dbb2ca53..1d40f022121a0 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 @@ -2428,6 +2428,8 @@ impl ModCollector<'_, '_> { self.def_collector.db, self.def_collector.def_map.krate, self.def_collector.def_map.block_id(), + Some(self.module_id), + name.clone(), ) .to_static() }; From 140fd7cddcf96c07f79319d7a8747f8f8083df2f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 13 Apr 2026 10:54:33 +0200 Subject: [PATCH 009/114] Implement `pattern_type` macro --- .../crates/hir-def/src/expr_store.rs | 6 +- .../crates/hir-def/src/expr_store/body.rs | 6 +- .../crates/hir-def/src/expr_store/lower.rs | 5 + .../crates/hir-def/src/expr_store/pretty.rs | 6 + .../rust-analyzer/crates/hir-def/src/hir.rs | 4 +- .../crates/hir-def/src/hir/type_ref.rs | 3 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 6 + .../hir-expand/src/builtin/derive_macro.rs | 3 + .../crates/hir-expand/src/builtin/fn_macro.rs | 13 ++ .../hir-ty/src/diagnostics/unsafe_check.rs | 5 +- .../crates/hir-ty/src/display.rs | 164 +++++++++++++----- .../closure/analysis/expr_use_visitor.rs | 2 + .../crates/hir-ty/src/infer/expr.rs | 1 + .../crates/hir-ty/src/infer/pat.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/layout.rs | 12 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 45 ++++- .../crates/hir-ty/src/mir/lower.rs | 15 +- .../hir-ty/src/mir/lower/pattern_matching.rs | 5 +- .../rust-analyzer/crates/hir/src/display.rs | 89 ++++++---- .../rust-analyzer/crates/hir/src/symbols.rs | 2 +- .../src/handlers/convert_let_else_to_match.rs | 1 + .../crates/ide-assists/src/utils.rs | 1 + .../crates/intern/src/symbol/symbols.rs | 1 + .../crates/parser/src/grammar/patterns.rs | 14 ++ .../crates/parser/src/grammar/types.rs | 26 +++ .../parser/src/syntax_kind/generated.rs | 22 +++ .../parser/test_data/generated/runner.rs | 4 + .../err/0032_match_arms_inner_attrs.rast | 83 +++++---- .../parser/inline/ok/not_null_pat.rast | 46 +++++ .../parser/inline/ok/not_null_pat.rs | 3 + .../parser/inline/ok/pattern_type.rast | 34 ++++ .../parser/inline/ok/pattern_type.rs | 1 + .../rust-analyzer/crates/syntax/rust.ungram | 8 + .../crates/syntax/src/ast/generated/nodes.rs | 122 +++++++++++++ .../crates/test-utils/src/minicore.rs | 30 +++- .../xtask/src/codegen/grammar/ast_src.rs | 3 + 36 files changed, 649 insertions(+), 146 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.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 4dc72672314cc..6d6e369cd6858 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 @@ -616,7 +616,7 @@ impl ExpressionStore { visitor.on_expr_opt(*end); } Pat::Lit(expr) | Pat::ConstBlock(expr) | Pat::Expr(expr) => visitor.on_expr(*expr), - Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest => {} + Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest | Pat::NotNull => {} &Pat::Bind { subpat, id: _ } => visitor.on_pat_opt(subpat), Pat::Or(args) | Pat::Tuple { args, ellipsis: _ } => visitor.on_pats(args), Pat::TupleStruct { args, ellipsis: _, path } => { @@ -855,6 +855,10 @@ impl ExpressionStore { pub fn visit_type_ref_children(&self, type_ref: TypeRefId, mut visitor: impl StoreVisitor) { match &self[type_ref] { TypeRef::Never | TypeRef::Placeholder | TypeRef::TypeParam(_) | TypeRef::Error => {} + &TypeRef::PatternType(ty, pat) => { + visitor.on_type(ty); + visitor.on_pat(pat) + } TypeRef::Tuple(types) => visitor.on_types(types), TypeRef::Path(path) => visitor.on_path(path), TypeRef::RawPtr(inner, _) | TypeRef::Slice(inner) => visitor.on_type(*inner), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs index 2fb47e59c546f..01423d5109d03 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs @@ -9,7 +9,7 @@ use syntax::ast; use triomphe::Arc; use crate::{ - DefWithBodyId, HasModule, + DefWithBodyId, ExpressionStoreOwnerId, HasModule, db::DefDatabase, expr_store::{ ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr, lower::lower_body, pretty, @@ -160,12 +160,12 @@ impl Body { pub fn pretty_print_pat( &self, db: &dyn DefDatabase, - owner: DefWithBodyId, + owner: ExpressionStoreOwnerId, pat: PatId, oneline: bool, edition: Edition, ) -> String { - pretty::print_pat_hir(db, self, owner.into(), pat, oneline, edition) + pretty::print_pat_hir(db, self, owner, pat, oneline, edition) } } 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 8818096500242..76503ac97c1ad 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 @@ -719,6 +719,10 @@ impl<'db> ExprCollector<'db> { ast::Type::DynTraitType(inner) => TypeRef::DynTrait( self.type_bounds_from_ast(inner.type_bound_list(), impl_trait_lower_fn), ), + ast::Type::PatternType(inner) => TypeRef::PatternType( + self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn), + self.collect_pat_top(inner.pat()), + ), ast::Type::MacroType(mt) => match mt.macro_call() { Some(mcall) => { let macro_ptr = AstPtr::new(&mcall); @@ -2782,6 +2786,7 @@ impl<'db> ExprCollector<'db> { let inner = self.collect_pat_opt(inner.pat(), binding_list); Pat::Deref { inner } } + ast::Pat::NotNull(_) => Pat::NotNull, ast::Pat::ConstBlockPat(const_block_pat) => { if let Some(block) = const_block_pat.block_expr() { let expr_id = self.with_label_rib(RibKind::Constant, |this| { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 34cedbd728f9a..4dc7ebebbb1cb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -906,6 +906,7 @@ impl Printer<'_> { Pat::Missing => w!(self, "�"), Pat::Rest => w!(self, ".."), Pat::Wild => w!(self, "_"), + Pat::NotNull => w!(self, "!null"), Pat::Tuple { args, ellipsis } => { w!(self, "("); for (i, pat) in args.iter().enumerate() { @@ -1346,6 +1347,11 @@ impl Printer<'_> { w!(self, "dyn "); self.print_type_bounds(bounds); } + TypeRef::PatternType(ty, pat) => { + self.print_type_ref(*ty); + w!(self, " is "); + self.print_pat(*pat); + } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 6eba85926403b..4c8d835ad7fc1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -717,6 +717,7 @@ pub enum Pat { Deref { inner: PatId, }, + NotNull, ConstBlock(ExprId), /// An expression inside a pattern. That can only occur inside assignments. /// @@ -734,7 +735,8 @@ impl Pat { | Pat::Wild | Pat::Missing | Pat::Rest - | Pat::Expr(_) => {} + | Pat::Expr(_) + | Pat::NotNull => {} Pat::Bind { subpat, .. } => { subpat.iter().copied().for_each(f); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs index 43e29c1f5ffcf..6cd8377b5fc6f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs @@ -9,7 +9,7 @@ use thin_vec::ThinVec; use crate::{ LifetimeParamId, TypeParamId, expr_store::{ExpressionStore, path::Path}, - hir::ExprId, + hir::{ExprId, PatId}, }; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -139,6 +139,7 @@ pub enum TypeRef { ImplTrait(ThinVec), DynTrait(ThinVec), TypeParam(TypeParamId), + PatternType(TypeRefId, PatId), Error, } 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 3aaf89102fab9..f9d5fe12e8a2b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -837,6 +837,12 @@ impl From for ExpressionStoreOwnerId { } } +impl From for ExpressionStoreOwnerId { + fn from(id: ImplId) -> Self { + ExpressionStoreOwnerId::Signature(id.into()) + } +} + impl GenericDefId { pub fn file_id_and_params_of( self, 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 8b031e364775c..e8321cd8da72c 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 @@ -1494,6 +1494,9 @@ fn coerce_pointee_expand( ast::Type::TupleType(ty) => any_long(ty.fields(), |ty| { substitute_type_in_bound(editor, ty, param_name, replacement) }), + ast::Type::PatternType(ty) => ty + .ty() + .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)), ast::Type::InferType(_) | ast::Type::MacroType(_) | ast::Type::NeverType(_) => false, }; 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 eb7175c686a08..9181ad88b6cc5 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 @@ -137,6 +137,7 @@ register_builtin! { (const_format_args, ConstFormatArgs) => format_args_expand, (format_args_nl, FormatArgsNl) => format_args_nl_expand, (quote, Quote) => quote_expand, + (pattern_type, PatternType) => pattern_type_expand, EagerExpander: (compile_error, CompileError) => compile_error_expand, @@ -994,3 +995,15 @@ fn unescape_str(s: &str) -> Cow<'_, str> { Cow::Borrowed(s) } } + +fn pattern_type_expand( + _db: &dyn ExpandDatabase, + _arg_id: MacroCallId, + tt: &tt::TopSubtree, + call_site: Span, +) -> ExpandResult { + let mut tt = tt.clone(); + tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible); + let pound = mk_pound(call_site); + ExpandResult::ok(quote! {call_site => builtin #pound pattern_type ( #tt ) }) +} 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 c37a194d47572..3c69c6a44b127 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 @@ -255,9 +255,8 @@ impl<'db> UnsafeVisitor<'db> { | Pat::Box { .. } | Pat::Deref { .. } | Pat::Expr(..) - | Pat::ConstBlock(..) => { - self.on_unsafe_op(current.into(), UnsafetyReason::UnionField) - } + | Pat::ConstBlock(..) + | Pat::NotNull => self.on_unsafe_op(current.into(), UnsafetyReason::UnionField), // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read. Pat::Missing | Pat::Rest | Pat::Wild | Pat::Or(_) => {} } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 326f920b118e4..c93e4a265787e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -15,7 +15,7 @@ use hir_def::{ expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, hir::{ - ClosureKind as HirClosureKind, CoroutineKind, + ClosureKind as HirClosureKind, CoroutineKind, PatId, generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, }, item_scope::ItemInNs, @@ -2362,39 +2362,58 @@ pub fn write_visibility<'db>( } pub trait HirDisplayWithExpressionStore<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result; + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result; } impl<'db, T: ?Sized + HirDisplayWithExpressionStore<'db>> HirDisplayWithExpressionStore<'db> for &'_ T { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { - T::hir_fmt(&**self, f, store) + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { + T::hir_fmt(&**self, f, owner, store) } } pub fn hir_display_with_store<'a, 'db, T: HirDisplayWithExpressionStore<'db> + 'a>( value: T, + owner: ExpressionStoreOwnerId, store: &'a ExpressionStore, ) -> impl HirDisplay<'db> + 'a { - ExpressionStoreAdapter(value, store) + ExpressionStoreAdapter(value, owner, store) } -struct ExpressionStoreAdapter<'a, T>(T, &'a ExpressionStore); +struct ExpressionStoreAdapter<'a, T>(T, ExpressionStoreOwnerId, &'a ExpressionStore); impl<'a, T> ExpressionStoreAdapter<'a, T> { - fn wrap(store: &'a ExpressionStore) -> impl Fn(T) -> ExpressionStoreAdapter<'a, T> { - move |value| ExpressionStoreAdapter(value, store) + fn wrap( + owner: ExpressionStoreOwnerId, + store: &'a ExpressionStore, + ) -> impl Fn(T) -> ExpressionStoreAdapter<'a, T> { + move |value| ExpressionStoreAdapter(value, owner, store) } } impl<'db, T: HirDisplayWithExpressionStore<'db>> HirDisplay<'db> for ExpressionStoreAdapter<'_, T> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - T::hir_fmt(&self.0, f, self.1) + T::hir_fmt(&self.0, f, self.1, self.2) } } impl<'db> HirDisplayWithExpressionStore<'db> for LifetimeRefId { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + _owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match &store[*self] { LifetimeRef::Named(name) => write!(f, "{}", name.display(f.db, f.edition())), LifetimeRef::Static => write!(f, "'static"), @@ -2413,7 +2432,12 @@ impl<'db> HirDisplayWithExpressionStore<'db> for LifetimeRefId { } impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match &store[*self] { TypeRef::Never => write!(f, "!")?, TypeRef::TypeParam(param) => { @@ -2438,7 +2462,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { } _ => None, }) - .map(ExpressionStoreAdapter::wrap(store)), + .map(ExpressionStoreAdapter::wrap(owner, store)), " + ", )?; } @@ -2447,20 +2471,20 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { TypeRef::Placeholder => write!(f, "_")?, TypeRef::Tuple(elems) => { write!(f, "(")?; - f.write_joined(elems.iter().map(ExpressionStoreAdapter::wrap(store)), ", ")?; + f.write_joined(elems.iter().map(ExpressionStoreAdapter::wrap(owner, store)), ", ")?; if elems.len() == 1 { write!(f, ",")?; } write!(f, ")")?; } - TypeRef::Path(path) => path.hir_fmt(f, store)?, + TypeRef::Path(path) => path.hir_fmt(f, owner, store)?, TypeRef::RawPtr(inner, mutability) => { let mutability = match mutability { hir_def::type_ref::Mutability::Shared => "*const ", hir_def::type_ref::Mutability::Mut => "*mut ", }; write!(f, "{mutability}")?; - inner.hir_fmt(f, store)?; + inner.hir_fmt(f, owner, store)?; } TypeRef::Reference(ref_) => { let mutability = match ref_.mutability { @@ -2469,22 +2493,22 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { }; write!(f, "&")?; if let Some(lifetime) = &ref_.lifetime { - lifetime.hir_fmt(f, store)?; + lifetime.hir_fmt(f, owner, store)?; write!(f, " ")?; } write!(f, "{mutability}")?; - ref_.ty.hir_fmt(f, store)?; + ref_.ty.hir_fmt(f, owner, store)?; } TypeRef::Array(array) => { write!(f, "[")?; - array.ty.hir_fmt(f, store)?; + array.ty.hir_fmt(f, owner, store)?; write!(f, "; ")?; - array.len.hir_fmt(f, store)?; + array.len.hir_fmt(f, owner, store)?; write!(f, "]")?; } TypeRef::Slice(inner) => { write!(f, "[")?; - inner.hir_fmt(f, store)?; + inner.hir_fmt(f, owner, store)?; write!(f, "]")?; } TypeRef::Fn(fn_) => { @@ -2504,7 +2528,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { write!(f, "{}: ", name.display(f.db, f.edition()))?; } - param_type.hir_fmt(f, store)?; + param_type.hir_fmt(f, owner, store)?; if index != function_parameters.len() - 1 { write!(f, ", ")?; @@ -2518,18 +2542,29 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { TypeRef::Tuple(tup) if tup.is_empty() => {} _ => { write!(f, " -> ")?; - return_type.hir_fmt(f, store)?; + return_type.hir_fmt(f, owner, store)?; } } } } TypeRef::ImplTrait(bounds) => { write!(f, "impl ")?; - f.write_joined(bounds.iter().map(ExpressionStoreAdapter::wrap(store)), " + ")?; + f.write_joined( + bounds.iter().map(ExpressionStoreAdapter::wrap(owner, store)), + " + ", + )?; } TypeRef::DynTrait(bounds) => { write!(f, "dyn ")?; - f.write_joined(bounds.iter().map(ExpressionStoreAdapter::wrap(store)), " + ")?; + f.write_joined( + bounds.iter().map(ExpressionStoreAdapter::wrap(owner, store)), + " + ", + )?; + } + TypeRef::PatternType(ty, pat) => { + ty.hir_fmt(f, owner, store)?; + write!(f, " is ")?; + pat.hir_fmt(f, owner, store)?; } TypeRef::Error => write!(f, "{{error}}")?, } @@ -2538,7 +2573,12 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId { } impl<'db> HirDisplayWithExpressionStore<'db> for ConstRef { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, _store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + _owner: ExpressionStoreOwnerId, + _store: &ExpressionStore, + ) -> Result { // FIXME write!(f, "{{const}}")?; @@ -2546,17 +2586,45 @@ impl<'db> HirDisplayWithExpressionStore<'db> for ConstRef { } } +impl<'db> HirDisplayWithExpressionStore<'db> for PatId { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { + write!( + f, + "{}", + hir_def::expr_store::pretty::print_pat_hir( + f.db, + store, + owner, + *self, + false, + f.edition() + ) + )?; + Ok(()) + } +} + impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match self { &TypeBound::Path(path, modifier) => { match modifier { TraitBoundModifier::None => (), TraitBoundModifier::Maybe => write!(f, "?")?, } - store[path].hir_fmt(f, store) + store[path].hir_fmt(f, owner, store) } - TypeBound::Lifetime(lifetime) => lifetime.hir_fmt(f, store), + TypeBound::Lifetime(lifetime) => lifetime.hir_fmt(f, owner, store), TypeBound::ForLifetime(lifetimes, path) => { let edition = f.edition(); write!( @@ -2564,7 +2632,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { "for<{}> ", lifetimes.iter().map(|it| it.display(f.db, edition)).format(", ") )?; - store[*path].hir_fmt(f, store) + store[*path].hir_fmt(f, owner, store) } TypeBound::Use(args) => { write!(f, "use<")?; @@ -2572,7 +2640,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { let last = args.len().saturating_sub(1); for (idx, arg) in args.iter().enumerate() { match arg { - UseArgRef::Lifetime(lt) => lt.hir_fmt(f, store)?, + UseArgRef::Lifetime(lt) => lt.hir_fmt(f, owner, store)?, UseArgRef::Name(n) => write!(f, "{}", n.display(f.db, edition))?, } if idx != last { @@ -2587,11 +2655,16 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeBound { } impl<'db> HirDisplayWithExpressionStore<'db> for Path { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match (self.type_anchor(), self.kind()) { (Some(anchor), _) => { write!(f, "<")?; - anchor.hir_fmt(f, store)?; + anchor.hir_fmt(f, owner, store)?; write!(f, ">")?; } (_, PathKind::Plain) => {} @@ -2634,7 +2707,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { }); if let Some(ty) = trait_self_ty { write!(f, "<")?; - ty.hir_fmt(f, store)?; + ty.hir_fmt(f, owner, store)?; write!(f, " as ")?; // Now format the path of the trait... } @@ -2664,17 +2737,17 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { if let Some(v) = tuple { if v.len() == 1 { write!(f, "(")?; - v[0].hir_fmt(f, store)?; + v[0].hir_fmt(f, owner, store)?; write!(f, ")")?; } else { - generic_args.args[0].hir_fmt(f, store)?; + generic_args.args[0].hir_fmt(f, owner, store)?; } } if let Some(ret) = generic_args.bindings[0].type_ref && !matches!(&store[ret], TypeRef::Tuple(v) if v.is_empty()) { write!(f, " -> ")?; - ret.hir_fmt(f, store)?; + ret.hir_fmt(f, owner, store)?; } } hir_def::expr_store::path::GenericArgsParentheses::No => { @@ -2687,7 +2760,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { } else { write!(f, ", ")?; } - arg.hir_fmt(f, store)?; + arg.hir_fmt(f, owner, store)?; } for binding in generic_args.bindings.iter() { if first { @@ -2700,7 +2773,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { match &binding.type_ref { Some(ty) => { write!(f, " = ")?; - ty.hir_fmt(f, store)? + ty.hir_fmt(f, owner, store)? } None => { write!(f, ": ")?; @@ -2708,7 +2781,7 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { binding .bounds .iter() - .map(ExpressionStoreAdapter::wrap(store)), + .map(ExpressionStoreAdapter::wrap(owner, store)), " + ", )?; } @@ -2735,14 +2808,21 @@ impl<'db> HirDisplayWithExpressionStore<'db> for Path { } impl<'db> HirDisplayWithExpressionStore<'db> for hir_def::expr_store::path::GenericArg { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>, store: &ExpressionStore) -> Result { + fn hir_fmt( + &self, + f: &mut HirFormatter<'_, 'db>, + owner: ExpressionStoreOwnerId, + store: &ExpressionStore, + ) -> Result { match self { - hir_def::expr_store::path::GenericArg::Type(ty) => ty.hir_fmt(f, store), + hir_def::expr_store::path::GenericArg::Type(ty) => ty.hir_fmt(f, owner, store), hir_def::expr_store::path::GenericArg::Const(_c) => { // write!(f, "{}", c.display(f.db, f.edition())) write!(f, "") } - hir_def::expr_store::path::GenericArg::Lifetime(lifetime) => lifetime.hir_fmt(f, store), + hir_def::expr_store::path::GenericArg::Lifetime(lifetime) => { + lifetime.hir_fmt(f, owner, store) + } } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index deafff6b43e06..322f8d128f758 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -1023,6 +1023,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { | Pat::Tuple { .. } | Pat::Wild | Pat::Missing + | Pat::NotNull | Pat::Rest => { // If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses // are made later as these patterns contains subpatterns. @@ -1696,6 +1697,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { | Pat::Range { .. } | Pat::Missing | Pat::Rest + | Pat::NotNull | Pat::Wild => { // always ok } 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 0675b5e8578fc..90ecfbd7e93d0 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 @@ -201,6 +201,7 @@ impl<'db> InferenceContext<'_, 'db> { | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Record { .. } + | Pat::NotNull | Pat::Missing => true, Pat::Expr(_) => unreachable!( "we don't call pat_guaranteed_to_constitute_read_for_never() with assignments" diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index f21438647c14f..60957f4942bec 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -448,7 +448,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { ) } Pat::Missing => self.types.types.error, - Pat::Wild | Pat::Rest => expected, + Pat::Wild | Pat::Rest | Pat::NotNull => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. // Pat::Never => expected, Pat::Path(_) => { @@ -662,6 +662,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { Pat::Ref { .. } // No need to do anything on a missing pattern. | Pat::Missing + // No need to do anything on a `NotNull` pattern, they are only allowed in type contexts. + | Pat::NotNull // A `_`/`..` pattern works with any expected type, so there's no need to do anything. | Pat::Wild | Pat::Rest // Bindings also work with whatever the expected type is, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 3e569076ad9ec..78df93311ef40 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -37,6 +37,9 @@ pub use self::{adt::layout_of_adt_query, target::target_data_layout_query}; pub(crate) mod adt; pub(crate) mod target; +#[cfg(test)] +mod tests; + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub usize); @@ -341,12 +344,14 @@ pub fn layout_of_ty_query( return db .layout_of_ty(args.as_coroutine_closure().tupled_upvars_ty().store(), trait_env); } - TyKind::CoroutineWitness(_, _) => { return Err(LayoutError::NotImplemented); } - TyKind::Pat(_, _) | TyKind::UnsafeBinder(_) => { + TyKind::Pat(_ty, _pat) => { + return Err(LayoutError::NotImplemented); + } + TyKind::UnsafeBinder(_) => { return Err(LayoutError::NotImplemented); } @@ -411,6 +416,3 @@ fn field_ty<'a>( fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) } } - -#[cfg(test)] -mod tests; 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 0cb1a2db26896..ec42b8a3493bb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -18,9 +18,12 @@ use hir_def::{ TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId, builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, - hir::generics::{ - GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, - TypeParamProvenance, WherePredicate, + hir::{ + ExprId, + generics::{ + GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, + TypeParamProvenance, WherePredicate, + }, }, item_tree::FieldsShape, lang_item::LangItems, @@ -60,10 +63,10 @@ use crate::{ next_solver::{ AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, ConstKind, DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind, - FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Predicate, Region, - StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, - StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, - util::BottomUpFolder, + FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, Pattern, PolyFnSig, Predicate, + Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, + StoredPolyFnSig, StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, + abi::Safety, util::BottomUpFolder, }, }; @@ -357,6 +360,14 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> { + self.lower_expr_as_const(const_ref.expr, const_type) + } + + pub(crate) fn lower_expr_as_const( + &mut self, + expr_id: ExprId, + const_type: Ty<'db>, + ) -> Const<'db> { #[expect(clippy::manual_map, reason = "a `map()` here generates a borrowck error")] let create_var = match &mut self.infer_vars { Some(infer_vars) => Some( @@ -368,7 +379,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.interner, self.def, self.store, - const_ref.expr, + expr_id, self.resolver, const_type, &|| self.generics.get_or_init(|| generics(self.db, self.generic_def)), @@ -528,6 +539,24 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } } } + &TypeRef::PatternType(ty, pat) => { + let ty = self.lower_ty(ty); + // FIXME: Properly do the lowering here + let pat_kind = match self.store[pat] { + hir_def::hir::Pat::Range { + start: Some(start), + end: Some(end), + range_type: _, + } => rustc_type_ir::PatternKind::Range { + start: self.lower_expr_as_const(start, ty), + end: self.lower_expr_as_const(end, ty), + }, + hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull, + _ => rustc_type_ir::PatternKind::NotNull, + }; + let pat = Pattern::new(self.interner, pat_kind); + Ty::new_pat(self.interner, ty, pat) + } TypeRef::Error => self.types.types.error, }; (ty, res) 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 3852db909e392..95939d458b26a 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 @@ -276,10 +276,11 @@ impl MirLowerError { db: &dyn HirDatabase, p: &Path, display_target: DisplayTarget, + owner: ExpressionStoreOwnerId, store: &ExpressionStore, ) -> Self { Self::UnresolvedName( - hir_display_with_store(p, store).display(db, display_target).to_string(), + hir_display_with_store(p, owner, store).display(db, display_target).to_string(), ) } } @@ -517,6 +518,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.db, p, DisplayTarget::from_crate(self.db, self.krate()), + self.owner.expression_store_owner(self.db), self.store, ) })?; @@ -884,10 +886,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let variant_id = self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| { - MirLowerError::UnresolvedName( - hir_display_with_store(path, self.store) - .display(self.db, self.display_target()) - .to_string(), + MirLowerError::unresolved_path( + self.db, + path, + self.display_target(), + self.owner.expression_store_owner(self.db), + self.store, ) })?; let subst = match self.expr_ty_without_adjust(expr_id).kind() { @@ -1432,6 +1436,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.db, c, DisplayTarget::from_crate(db, owner.krate(db)), + self.owner.expression_store_owner(self.db), self.store, ) }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index c924c5bdf0fdf..2cb2143229ee5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -132,7 +132,9 @@ impl<'db> MirLowerCtx<'_, 'db> { .into(), ); Ok(match &self.store[pattern] { - Pat::Missing | Pat::Rest => return Err(MirLowerError::IncompletePattern), + Pat::Missing | Pat::Rest | Pat::NotNull => { + return Err(MirLowerError::IncompletePattern); + } Pat::Wild => (current, current_else), Pat::Tuple { args, ellipsis } => { let subst = match self.infer.pat_ty(pattern).kind() { @@ -376,6 +378,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.db, p, self.display_target(), + self.owner.expression_store_owner(self.db), self.store, ) }; diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index a71851ea8ceff..c3af5fa7cef8a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -2,7 +2,8 @@ use either::Either; use hir_def::{ - AdtId, BuiltinDeriveImplId, FunctionId, GenericDefId, ImplId, ItemContainerId, + AdtId, BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, FunctionId, GenericDefId, + ImplId, ItemContainerId, builtin_derive::BuiltinDeriveImplMethod, expr_store::{Body, ExpressionStore}, hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate}, @@ -102,8 +103,11 @@ impl<'db> HirDisplay<'db> for Function { if f.show_container_bounds() && !params.is_empty() { write_trait_header(trait_.into(), f)?; f.write_char('\n')?; - has_disaplayable_predicates(f.db, params, params_store) - .then_some((params, params_store)) + has_disaplayable_predicates(f.db, params, params_store).then_some(( + params, + trait_.into(), + params_store, + )) } else { None } @@ -113,8 +117,11 @@ impl<'db> HirDisplay<'db> for Function { if f.show_container_bounds() && !params.is_empty() { write_impl_header(impl_, f)?; f.write_char('\n')?; - has_disaplayable_predicates(f.db, params, params_store) - .then_some((params, params_store)) + has_disaplayable_predicates(f.db, params, params_store).then_some(( + params, + impl_.into(), + params_store, + )) } else { None } @@ -125,7 +132,7 @@ impl<'db> HirDisplay<'db> for Function { // Write signature of the function let has_written_where = write_function(f, id)?; - if let Some((container_params, container_params_store)) = container_params { + if let Some((container_params, owner, container_params_store)) = container_params { if !has_written_where { f.write_str("\nwhere")?; } @@ -135,7 +142,12 @@ impl<'db> HirDisplay<'db> for Function { _ => unreachable!(), }; write!(f, "\n // Bounds from {container_name}:",)?; - write_where_predicates(container_params, container_params_store, f)?; + write_where_predicates( + container_params, + ExpressionStoreOwnerId::Signature(owner), + container_params_store, + f, + )?; } Ok(()) } @@ -197,6 +209,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re let comma = if too_long_param { ",\n " } else { ", " }; // FIXME: Use resolved `param.ty` once we no longer discard lifetimes let body = Body::of(db, func_id.into()); + let owner = DefWithBodyId::FunctionId(func_id).into(); for (type_ref, param) in data.params.iter().zip(func.assoc_fn_params(db)).skip(skip_self) { if !first { f.write_str(comma)?; @@ -205,11 +218,11 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re } let pat_id = body.params[param.idx - body.self_param().is_some() as usize]; - let pat_str = body.pretty_print_pat(db, func_id.into(), pat_id, true, f.edition()); + let pat_str = body.pretty_print_pat(db, owner, pat_id, true, f.edition()); f.write_str(&pat_str)?; f.write_str(": ")?; - type_ref.hir_fmt(f, &data.store)?; + type_ref.hir_fmt(f, owner, &data.store)?; } if data.is_varargs() { @@ -258,7 +271,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re TypeRef::Tuple(tup) if tup.is_empty() => {} _ => { f.write_str(" -> ")?; - ret_type.hir_fmt(f, &data.store)?; + ret_type.hir_fmt(f, owner, &data.store)?; } } } @@ -278,7 +291,8 @@ fn write_impl_header<'db>(impl_: ImplId, f: &mut HirFormatter<'_, 'db>) -> Resul let impl_data = ImplSignature::of(db, impl_); if let Some(target_trait) = &impl_data.target_trait { f.write_char(' ')?; - hir_display_with_store(&impl_data.store[target_trait.path], &impl_data.store).hir_fmt(f)?; + hir_display_with_store(&impl_data.store[target_trait.path], impl_.into(), &impl_data.store) + .hir_fmt(f)?; f.write_str(" for")?; } @@ -306,13 +320,14 @@ impl<'db> HirDisplay<'db> for SelfParam { }; let data = FunctionSignature::of(f.db, func); let param = *data.params.first().unwrap(); + let owner = ExpressionStoreOwnerId::Body(func.into()); match &data.store[param] { TypeRef::Path(p) if p.is_self_type() => f.write_str("self"), TypeRef::Reference(ref_) if matches!(&data.store[ref_.ty], TypeRef::Path(p) if p.is_self_type()) => { f.write_char('&')?; if let Some(lifetime) = &ref_.lifetime { - lifetime.hir_fmt(f, &data.store)?; + lifetime.hir_fmt(f, owner, &data.store)?; f.write_char(' ')?; } if let hir_def::type_ref::Mutability::Mut = ref_.mutability { @@ -322,7 +337,7 @@ impl<'db> HirDisplay<'db> for SelfParam { } _ => { f.write_str("self: ")?; - param.hir_fmt(f, &data.store) + param.hir_fmt(f, owner, &data.store) } } } @@ -517,7 +532,11 @@ impl<'db> HirDisplay<'db> for EnumVariant { f.write_str(", ")?; } // Enum variant fields must be pub. - field.type_ref.hir_fmt(f, &data.store)?; + field.type_ref.hir_fmt( + f, + ExpressionStoreOwnerId::VariantFields(self.id.into()), + &data.store, + )?; } f.write_char(')')?; } @@ -666,6 +685,7 @@ fn write_generic_params_or_args<'db>( include_defaults: bool, ) -> Result { let (params, store) = GenericParams::with_store(f.db, def); + let owner = def.into(); if params.iter_lt().next().is_none() && params.iter_type_or_consts().all(|it| it.1.const_param().is_none()) && params @@ -701,17 +721,17 @@ fn write_generic_params_or_args<'db>( write!(f, "{}", name.display(f.db, f.edition()))?; if include_defaults && let Some(default) = &ty.default { f.write_str(" = ")?; - default.hir_fmt(f, store)?; + default.hir_fmt(f, owner, store)?; } } TypeOrConstParamData::ConstParamData(c) => { delim(f)?; write!(f, "const {}: ", name.display(f.db, f.edition()))?; - c.ty.hir_fmt(f, store)?; + c.ty.hir_fmt(f, owner, store)?; if include_defaults && let Some(default) = &c.default { f.write_str(" = ")?; - default.hir_fmt(f, store)?; + default.hir_fmt(f, owner, store)?; } } } @@ -729,7 +749,7 @@ fn write_where_clause<'db>(def: GenericDefId, f: &mut HirFormatter<'_, 'db>) -> } f.write_str("\nwhere")?; - write_where_predicates(params, store, f)?; + write_where_predicates(params, def.into(), store, f)?; Ok(true) } @@ -752,6 +772,7 @@ fn has_disaplayable_predicates( fn write_where_predicates<'db>( params: &GenericParams, + owner: ExpressionStoreOwnerId, store: &ExpressionStore, f: &mut HirFormatter<'_, 'db>, ) -> Result { @@ -783,29 +804,31 @@ fn write_where_predicates<'db>( f.write_str("\n ")?; match pred { TypeBound { target, bound } => { - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; f.write_str(": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } Lifetime { target, bound } => { - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; write!(f, ": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } ForLifetime { lifetimes, target, bound } => { let lifetimes = lifetimes.iter().map(|it| it.display(f.db, f.edition())).join(", "); write!(f, "for<{lifetimes}> ")?; - target.hir_fmt(f, store)?; + target.hir_fmt(f, owner, store)?; f.write_str(": ")?; - bound.hir_fmt(f, store)?; + bound.hir_fmt(f, owner, store)?; } } while let Some(nxt) = iter.next_if(|nxt| check_same_target(pred, nxt)) { f.write_str(" + ")?; match nxt { - TypeBound { bound, .. } | ForLifetime { bound, .. } => bound.hir_fmt(f, store)?, - Lifetime { bound, .. } => bound.hir_fmt(f, store)?, + TypeBound { bound, .. } | ForLifetime { bound, .. } => { + bound.hir_fmt(f, owner, store)? + } + Lifetime { bound, .. } => bound.hir_fmt(f, owner, store)?, } } f.write_str(",")?; @@ -830,7 +853,7 @@ impl<'db> HirDisplay<'db> for Const { Some(name) => write!(f, "{}: ", name.display(f.db, f.edition()))?, None => f.write_str("_: ")?, } - data.type_ref.hir_fmt(f, &data.store)?; + data.type_ref.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; Ok(()) } } @@ -844,7 +867,7 @@ impl<'db> HirDisplay<'db> for Static { f.write_str("mut ")?; } write!(f, "{}: ", data.name.display(f.db, f.edition()))?; - data.type_ref.hir_fmt(f, &data.store)?; + data.type_ref.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; Ok(()) } } @@ -925,13 +948,19 @@ impl<'db> HirDisplay<'db> for TypeAlias { if !data.bounds.is_empty() { f.write_str(": ")?; f.write_joined( - data.bounds.iter().map(|bound| hir_display_with_store(bound, &data.store)), + data.bounds.iter().map(|bound| { + hir_display_with_store( + bound, + ExpressionStoreOwnerId::Signature(self.id.into()), + &data.store, + ) + }), " + ", )?; } if let Some(ty) = data.ty { f.write_str(" = ")?; - ty.hir_fmt(f, &data.store)?; + ty.hir_fmt(f, ExpressionStoreOwnerId::Signature(self.id.into()), &data.store)?; } write_where_clause(def_id, f)?; Ok(()) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index ff56544d82e01..4841de700068b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -401,7 +401,7 @@ impl<'a> SymbolCollector<'a> { fn collect_from_impl(&mut self, impl_id: ImplId) { let impl_data = ImplSignature::of(self.db, impl_id); let impl_name = Some( - hir_display_with_store(impl_data.self_ty, &impl_data.store) + hir_display_with_store(impl_data.self_ty, impl_id.into(), &impl_data.store) .display( self.db, crate::Impl::from(impl_id).krate(self.db).to_display_target(self.db), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 07e12f0320be7..65c1c7cb70e78 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -229,6 +229,7 @@ fn remove_mut_and_collect_idents( | ast::Pat::LiteralPat(_) | ast::Pat::PathPat(_) | ast::Pat::WildcardPat(_) + | ast::Pat::NotNull(_) | ast::Pat::ConstBlockPat(_) => pat.clone(), // don't support macro pat yet ast::Pat::MacroPat(_) => return None, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 3ab4279b0addb..33e0f476da5bf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -417,6 +417,7 @@ fn check_pat_variant_nested_or_literal_with_depth( | ast::Pat::PathPat(_) | ast::Pat::BoxPat(_) | ast::Pat::DerefPat(_) + | ast::Pat::NotNull(_) | ast::Pat::ConstBlockPat(_) => true, ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| { diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index ac6daaf00681f..6ad8006d18446 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -475,6 +475,7 @@ define_symbols! { PartialOrd, CoercePointee, path, + pattern_type, Pending, phantom_data, pieces, diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs index 5726f085a03e9..29fa11720aca7 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs @@ -12,6 +12,7 @@ pub(super) const PATTERN_FIRST: TokenSet = T![_], T![-], T![.], + T![!], ])); const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]])); @@ -256,6 +257,7 @@ fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option ref_pat(p), T!['('] => tuple_pat(p), T!['['] => slice_pat(p), + T![!] => not_null_pat(p), _ => { p.err_recover("expected pattern", recovery_set); @@ -435,6 +437,18 @@ fn ref_pat(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, REF_PAT) } +// test not_null_pat +// fn main() { +// let (!a | !&0) = (); +// } +fn not_null_pat(p: &mut Parser<'_>) -> CompletedMarker { + assert!(p.at(T![!])); + let m = p.start(); + p.bump(T![!]); + pattern_single(p); + m.complete(p, NOT_NULL) +} + // test tuple_pat // fn main() { // let (a, b, ..) = (); diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs index 667bb68c649c5..f92afde60de15 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs @@ -1,3 +1,5 @@ +use crate::grammar::entry::prefix::pat; + use super::*; pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[ @@ -341,6 +343,10 @@ fn bare_dyn_trait_type(p: &mut Parser<'_>) { // type B = crate::foo!(); fn path_or_macro_type(p: &mut Parser<'_>, allow_bounds: bool) { assert!(paths::is_path_start(p)); + if p.at_contextual_kw(T![builtin]) && p.nth_at(1, T![#]) { + pattern_type(p); + return; + } let r = p.start(); let m = p.start(); @@ -411,3 +417,23 @@ pub(super) fn opt_type_bounds_as_dyn_trait_type( // Finally precede everything with DYN_TRAIT_TYPE m.precede(p).complete(p, DYN_TRAIT_TYPE) } + +// test pattern_type +// type T = builtin#pattern_type (u8 is 0..10); +fn pattern_type(p: &mut Parser<'_>) { + let m = p.start(); + p.bump_remap(T![builtin]); + p.bump(T![#]); + if p.eat_contextual_kw(T![pattern_type]) { + p.expect(T!['(']); + type_(p); + if !p.eat_contextual_kw(T![is]) { + p.error("expected `is`") + } + pat(p); + p.expect(T![')']); + m.complete(p, PATTERN_TYPE); + } else { + m.abandon(p); + } +} diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index b1867275cebfa..dd675e0834051 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -127,6 +127,7 @@ pub enum SyntaxKind { GLOBAL_ASM_KW, INLATEOUT_KW, INOUT_KW, + IS_KW, LABEL_KW, LATEOUT_KW, MACRO_RULES_KW, @@ -135,9 +136,11 @@ pub enum SyntaxKind { NOMEM_KW, NORETURN_KW, NOSTACK_KW, + NULL_KW, OFFSET_OF_KW, OPTIONS_KW, OUT_KW, + PATTERN_TYPE_KW, PRESERVES_FLAGS_KW, PURE_KW, RAW_KW, @@ -254,6 +257,7 @@ pub enum SyntaxKind { NAME, NAME_REF, NEVER_TYPE, + NOT_NULL, OFFSET_OF_EXPR, OR_PAT, PARAM, @@ -268,6 +272,7 @@ pub enum SyntaxKind { PATH_PAT, PATH_SEGMENT, PATH_TYPE, + PATTERN_TYPE, PREFIX_EXPR, PTR_TYPE, RANGE_EXPR, @@ -439,6 +444,7 @@ impl SyntaxKind { | NAME | NAME_REF | NEVER_TYPE + | NOT_NULL | OFFSET_OF_EXPR | OR_PAT | PARAM @@ -453,6 +459,7 @@ impl SyntaxKind { | PATH_PAT | PATH_SEGMENT | PATH_TYPE + | PATTERN_TYPE | PREFIX_EXPR | PTR_TYPE | RANGE_EXPR @@ -636,6 +643,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => "global_asm", INLATEOUT_KW => "inlateout", INOUT_KW => "inout", + IS_KW => "is", LABEL_KW => "label", LATEOUT_KW => "lateout", MACRO_RULES_KW => "macro_rules", @@ -644,9 +652,11 @@ impl SyntaxKind { NOMEM_KW => "nomem", NORETURN_KW => "noreturn", NOSTACK_KW => "nostack", + NULL_KW => "null", OFFSET_OF_KW => "offset_of", OPTIONS_KW => "options", OUT_KW => "out", + PATTERN_TYPE_KW => "pattern_type", PRESERVES_FLAGS_KW => "preserves_flags", PURE_KW => "pure", RAW_KW => "raw", @@ -742,6 +752,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => true, INLATEOUT_KW => true, INOUT_KW => true, + IS_KW => true, LABEL_KW => true, LATEOUT_KW => true, MACRO_RULES_KW => true, @@ -750,9 +761,11 @@ impl SyntaxKind { NOMEM_KW => true, NORETURN_KW => true, NOSTACK_KW => true, + NULL_KW => true, OFFSET_OF_KW => true, OPTIONS_KW => true, OUT_KW => true, + PATTERN_TYPE_KW => true, PRESERVES_FLAGS_KW => true, PURE_KW => true, RAW_KW => true, @@ -836,6 +849,7 @@ impl SyntaxKind { GLOBAL_ASM_KW => true, INLATEOUT_KW => true, INOUT_KW => true, + IS_KW => true, LABEL_KW => true, LATEOUT_KW => true, MACRO_RULES_KW => true, @@ -844,9 +858,11 @@ impl SyntaxKind { NOMEM_KW => true, NORETURN_KW => true, NOSTACK_KW => true, + NULL_KW => true, OFFSET_OF_KW => true, OPTIONS_KW => true, OUT_KW => true, + PATTERN_TYPE_KW => true, PRESERVES_FLAGS_KW => true, PURE_KW => true, RAW_KW => true, @@ -993,6 +1009,7 @@ impl SyntaxKind { "global_asm" => GLOBAL_ASM_KW, "inlateout" => INLATEOUT_KW, "inout" => INOUT_KW, + "is" => IS_KW, "label" => LABEL_KW, "lateout" => LATEOUT_KW, "macro_rules" => MACRO_RULES_KW, @@ -1001,9 +1018,11 @@ impl SyntaxKind { "nomem" => NOMEM_KW, "noreturn" => NORETURN_KW, "nostack" => NOSTACK_KW, + "null" => NULL_KW, "offset_of" => OFFSET_OF_KW, "options" => OPTIONS_KW, "out" => OUT_KW, + "pattern_type" => PATTERN_TYPE_KW, "preserves_flags" => PRESERVES_FLAGS_KW, "pure" => PURE_KW, "raw" => RAW_KW, @@ -1168,6 +1187,7 @@ macro_rules ! T_ { [global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW }; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW }; [inout] => { $ crate :: SyntaxKind :: INOUT_KW }; + [is] => { $ crate :: SyntaxKind :: IS_KW }; [label] => { $ crate :: SyntaxKind :: LABEL_KW }; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW }; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW }; @@ -1176,9 +1196,11 @@ macro_rules ! T_ { [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW }; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW }; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW }; + [null] => { $ crate :: SyntaxKind :: NULL_KW }; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW }; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW }; [out] => { $ crate :: SyntaxKind :: OUT_KW }; + [pattern_type] => { $ crate :: SyntaxKind :: PATTERN_TYPE_KW }; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW }; [pure] => { $ crate :: SyntaxKind :: PURE_KW }; [raw] => { $ crate :: SyntaxKind :: RAW_KW }; 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 7aaf270a77bf7..ccf8b89be74a2 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 @@ -474,6 +474,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/nocontentexpr_after_item.rs"); } #[test] + fn not_null_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/not_null_pat.rs"); } + #[test] fn offset_of_parens() { run_and_expect_no_errors("test_data/parser/inline/ok/offset_of_parens.rs"); } @@ -506,6 +508,8 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/path_type_with_bounds.rs"); } #[test] + fn pattern_type() { run_and_expect_no_errors("test_data/parser/inline/ok/pattern_type.rs"); } + #[test] fn placeholder_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/placeholder_pat.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast index 2334b730e4cc8..234070bcb1355 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast @@ -37,27 +37,26 @@ SOURCE_FILE MATCH_ARM ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Not allowed here\"" + LITERAL_PAT + LITERAL + STRING "\"Not allowed here\"" R_PAREN ")" - R_BRACK "]" - WHITESPACE "\n " - MATCH_ARM - WILDCARD_PAT + R_BRACK "]" + WHITESPACE "\n " + UNDERSCORE_EXPR UNDERSCORE "_" - WHITESPACE " " + WHITESPACE " " + MATCH_ARM FAT_ARROW "=>" WHITESPACE " " TUPLE_EXPR @@ -103,22 +102,21 @@ SOURCE_FILE MATCH_ARM ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Nor here\"" + LITERAL_PAT + LITERAL + STRING "\"Nor here\"" R_PAREN ")" - R_BRACK "]" + R_BRACK "]" WHITESPACE "\n " R_CURLY "}" WHITESPACE "\n\n " @@ -146,27 +144,26 @@ SOURCE_FILE WHITESPACE "\n " ATTR POUND "#" - ERROR + NOT_NULL BANG "!" - ARRAY_EXPR - L_BRACK "[" - CALL_EXPR - PATH_EXPR + SLICE_PAT + L_BRACK "[" + TUPLE_STRUCT_PAT PATH PATH_SEGMENT NAME_REF IDENT "doc" - ARG_LIST L_PAREN "(" - LITERAL - STRING "\"Nor here\"" + LITERAL_PAT + LITERAL + STRING "\"Nor here\"" R_PAREN ")" - R_BRACK "]" - WHITESPACE "\n " - MATCH_ARM - WILDCARD_PAT + R_BRACK "]" + WHITESPACE "\n " + UNDERSCORE_EXPR UNDERSCORE "_" - WHITESPACE " " + WHITESPACE " " + MATCH_ARM FAT_ARROW "=>" WHITESPACE " " TUPLE_EXPR @@ -190,13 +187,13 @@ SOURCE_FILE R_CURLY "}" WHITESPACE "\n" error 52: expected L_BRACK -error 52: expected pattern -error 53: expected FAT_ARROW -error 78: expected `,` +error 78: expected FAT_ARROW +error 88: expected `,` +error 89: expected pattern error 161: expected L_BRACK -error 161: expected pattern -error 162: expected FAT_ARROW +error 179: expected FAT_ARROW +error 179: expected expression error 232: expected L_BRACK -error 232: expected pattern -error 233: expected FAT_ARROW -error 250: expected `,` +error 250: expected FAT_ARROW +error 260: expected `,` +error 261: expected pattern diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast new file mode 100644 index 0000000000000..7a2e655201b67 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rast @@ -0,0 +1,46 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "main" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + PAREN_PAT + L_PAREN "(" + OR_PAT + NOT_NULL + BANG "!" + IDENT_PAT + NAME + IDENT "a" + WHITESPACE " " + PIPE "|" + WHITESPACE " " + NOT_NULL + BANG "!" + REF_PAT + AMP "&" + LITERAL_PAT + LITERAL + INT_NUMBER "0" + R_PAREN ")" + WHITESPACE " " + EQ "=" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs new file mode 100644 index 0000000000000..f44fae5a77347 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/not_null_pat.rs @@ -0,0 +1,3 @@ +fn main() { + let (!a | !&0) = (); +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast new file mode 100644 index 0000000000000..c9caeb1f9b210 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rast @@ -0,0 +1,34 @@ +SOURCE_FILE + TYPE_ALIAS + TYPE_KW "type" + WHITESPACE " " + NAME + IDENT "T" + WHITESPACE " " + EQ "=" + WHITESPACE " " + PATTERN_TYPE + BUILTIN_KW "builtin" + POUND "#" + PATTERN_TYPE_KW "pattern_type" + WHITESPACE " " + L_PAREN "(" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "u8" + WHITESPACE " " + IS_KW "is" + WHITESPACE " " + RANGE_PAT + LITERAL_PAT + LITERAL + INT_NUMBER "0" + DOT2 ".." + LITERAL_PAT + LITERAL + INT_NUMBER "10" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rs new file mode 100644 index 0000000000000..c909c7b708053 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/pattern_type.rs @@ -0,0 +1 @@ +type T = builtin#pattern_type (u8 is 0..10); diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 6bcf8ba743bc8..408f2f4b32c75 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -665,6 +665,7 @@ Type = | NeverType | ParenType | PathType +| PatternType | PtrType | RefType | SliceType @@ -706,6 +707,9 @@ FnPtrType = ForType = ForBinder Type +PatternType = + 'builtin' '#' 'pattern_type' '(' Type 'is' Pat ')' + ImplTraitType = 'impl' TypeBoundList @@ -749,6 +753,10 @@ Pat = | TupleStructPat | ConstBlockPat | DerefPat +| NotNull + +NotNull = + '!' 'null' DerefPat = 'builtin' '#' 'deref' '(' Pat ')' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index e3e5c499d4ea0..c18311bfa3ad1 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1186,6 +1186,15 @@ impl NeverType { #[inline] pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } } +pub struct NotNull { + pub(crate) syntax: SyntaxNode, +} +impl NotNull { + #[inline] + pub fn excl_token(&self) -> Option { support::token(&self.syntax, T![!]) } + #[inline] + pub fn null_token(&self) -> Option { support::token(&self.syntax, T![null]) } +} pub struct OffsetOfExpr { pub(crate) syntax: SyntaxNode, } @@ -1357,6 +1366,29 @@ impl PathType { #[inline] pub fn path(&self) -> Option { support::child(&self.syntax) } } +pub struct PatternType { + pub(crate) syntax: SyntaxNode, +} +impl PatternType { + #[inline] + pub fn pat(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn ty(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } + #[inline] + pub fn is_token(&self) -> Option { support::token(&self.syntax, T![is]) } + #[inline] + pub fn pattern_type_token(&self) -> Option { + support::token(&self.syntax, T![pattern_type]) + } +} pub struct PrefixExpr { pub(crate) syntax: SyntaxNode, } @@ -2275,6 +2307,7 @@ pub enum Pat { IdentPat(IdentPat), LiteralPat(LiteralPat), MacroPat(MacroPat), + NotNull(NotNull), OrPat(OrPat), ParenPat(ParenPat), PathPat(PathPat), @@ -2307,6 +2340,7 @@ pub enum Type { NeverType(NeverType), ParenType(ParenType), PathType(PathType), + PatternType(PatternType), PtrType(PtrType), RefType(RefType), SliceType(SliceType), @@ -5363,6 +5397,38 @@ impl fmt::Debug for NeverType { f.debug_struct("NeverType").field("syntax", &self.syntax).finish() } } +impl AstNode for NotNull { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + NOT_NULL + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == NOT_NULL } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for NotNull { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for NotNull {} +impl PartialEq for NotNull { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for NotNull { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for NotNull { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("NotNull").field("syntax", &self.syntax).finish() + } +} impl AstNode for OffsetOfExpr { #[inline] fn kind() -> SyntaxKind @@ -5811,6 +5877,38 @@ impl fmt::Debug for PathType { f.debug_struct("PathType").field("syntax", &self.syntax).finish() } } +impl AstNode for PatternType { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + PATTERN_TYPE + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == PATTERN_TYPE } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for PatternType { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for PatternType {} +impl PartialEq for PatternType { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for PatternType { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for PatternType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PatternType").field("syntax", &self.syntax).finish() + } +} impl AstNode for PrefixExpr { #[inline] fn kind() -> SyntaxKind @@ -8581,6 +8679,10 @@ impl From for Pat { #[inline] fn from(node: MacroPat) -> Pat { Pat::MacroPat(node) } } +impl From for Pat { + #[inline] + fn from(node: NotNull) -> Pat { Pat::NotNull(node) } +} impl From for Pat { #[inline] fn from(node: OrPat) -> Pat { Pat::OrPat(node) } @@ -8636,6 +8738,7 @@ impl AstNode for Pat { | IDENT_PAT | LITERAL_PAT | MACRO_PAT + | NOT_NULL | OR_PAT | PAREN_PAT | PATH_PAT @@ -8658,6 +8761,7 @@ impl AstNode for Pat { IDENT_PAT => Pat::IdentPat(IdentPat { syntax }), LITERAL_PAT => Pat::LiteralPat(LiteralPat { syntax }), MACRO_PAT => Pat::MacroPat(MacroPat { syntax }), + NOT_NULL => Pat::NotNull(NotNull { syntax }), OR_PAT => Pat::OrPat(OrPat { syntax }), PAREN_PAT => Pat::ParenPat(ParenPat { syntax }), PATH_PAT => Pat::PathPat(PathPat { syntax }), @@ -8682,6 +8786,7 @@ impl AstNode for Pat { Pat::IdentPat(it) => &it.syntax, Pat::LiteralPat(it) => &it.syntax, Pat::MacroPat(it) => &it.syntax, + Pat::NotNull(it) => &it.syntax, Pat::OrPat(it) => &it.syntax, Pat::ParenPat(it) => &it.syntax, Pat::PathPat(it) => &it.syntax, @@ -8748,6 +8853,10 @@ impl From for Type { #[inline] fn from(node: PathType) -> Type { Type::PathType(node) } } +impl From for Type { + #[inline] + fn from(node: PatternType) -> Type { Type::PatternType(node) } +} impl From for Type { #[inline] fn from(node: PtrType) -> Type { Type::PtrType(node) } @@ -8779,6 +8888,7 @@ impl AstNode for Type { | NEVER_TYPE | PAREN_TYPE | PATH_TYPE + | PATTERN_TYPE | PTR_TYPE | REF_TYPE | SLICE_TYPE @@ -8798,6 +8908,7 @@ impl AstNode for Type { NEVER_TYPE => Type::NeverType(NeverType { syntax }), PAREN_TYPE => Type::ParenType(ParenType { syntax }), PATH_TYPE => Type::PathType(PathType { syntax }), + PATTERN_TYPE => Type::PatternType(PatternType { syntax }), PTR_TYPE => Type::PtrType(PtrType { syntax }), REF_TYPE => Type::RefType(RefType { syntax }), SLICE_TYPE => Type::SliceType(SliceType { syntax }), @@ -8819,6 +8930,7 @@ impl AstNode for Type { Type::NeverType(it) => &it.syntax, Type::ParenType(it) => &it.syntax, Type::PathType(it) => &it.syntax, + Type::PatternType(it) => &it.syntax, Type::PtrType(it) => &it.syntax, Type::RefType(it) => &it.syntax, Type::SliceType(it) => &it.syntax, @@ -10453,6 +10565,11 @@ impl std::fmt::Display for NeverType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for NotNull { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for OffsetOfExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -10523,6 +10640,11 @@ impl std::fmt::Display for PathType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for PatternType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for PrefixExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 8975fa56d7272..ff531af638a9e 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -55,10 +55,11 @@ //! manually_drop: drop //! matches: //! non_null: -//! non_zero: +//! non_zero: transmute, option //! option: panic //! ord: eq, option //! panic: fmt +//! pat: //! phantom_data: //! pin: //! pointee: copy, send, sync, ord, hash, unpin, phantom_data @@ -2278,6 +2279,33 @@ mod macros { // endregion:deref_pat } +// region:pat +pub mod pat { + #[macro_export] + #[rustc_builtin_macro(pattern_type)] + macro_rules! pattern_type { + ($($arg:tt)*) => { + /* compiler built-in */ + }; + } + + pub const trait RangePattern { + /// Trait version of the inherent `MIN` assoc const. + #[lang = "RangeMin"] + const MIN: Self; + + /// Trait version of the inherent `MIN` assoc const. + #[lang = "RangeMax"] + const MAX: Self; + + /// A compile-time helper to subtract 1 for exclusive ranges. + #[lang = "RangeSub"] + #[track_caller] + fn sub_one(self) -> Self; + } +} +// endregion:pat + // region:non_zero pub mod num { #[repr(transparent)] diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs index 43462d1c6e044..49a625bdbd400 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs @@ -123,6 +123,7 @@ const CONTEXTUAL_KEYWORDS: &[&str] = &[ "bikeshed", "cfg_attr", "cfg", + "null", ]; // keywords we use for special macro expansions const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ @@ -151,6 +152,8 @@ const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[ "readonly", "sym", "deref", + "pattern_type", + "is", ]; // keywords that are keywords depending on the edition From ea35b1564ba48d815da304a6f4a543886ca0f5d3 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 12 May 2026 09:50:31 +0800 Subject: [PATCH 010/114] fix: handle usages in macro for extract_function Example --- ```rust struct Foo(&'static str); impl Foo { fn text(&self) -> &str { self.0 } } fn main() { let s = Foo(""); $0print!("{}{}", s, s);$0 let _ = s.text() == ""; } ``` **Before this PR** ```rust fn $0fun_name(s: &Foo) { *print!("{}{}", s, s); } ``` **After this PR** ```rust fn $0fun_name(s: &Foo) { print!("{}{}", *s, *s); } ``` --- ```rust macro_rules! refmut { ($e:expr) => { &mut $e }; } fn foo() { let mut n = 1; $0let v = refmut!(n); *v += 1;$0 let k = n; } ``` **Before this PR** ```rust fn $0fun_name(n: &mut i32) { let v = refmut!(n); *v += 1; } ``` **After this PR** ```rust fn $0fun_name(n: &mut i32) { let v = refmut!(*n); *v += 1; } ``` --- .../src/handlers/extract_function.rs | 219 +++++++++++------- 1 file changed, 134 insertions(+), 85 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 44123dc20dee0..67bb7fc03ee55 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -21,7 +21,7 @@ use itertools::Itertools; use syntax::{ Edition, SyntaxElement, SyntaxKind::{self, COMMENT}, - SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent, + SyntaxNode, T, TextRange, WalkEvent, ast::{ self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, edit::{AstNodeEdit, IndentLevel}, @@ -103,7 +103,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - let ret_ty = body.return_ty(ctx)?; let control_flow = body.external_control_flow(ctx, &container_info)?; - let ret_values = body.ret_values(ctx, node.parent().as_ref().unwrap_or(&node)); + let ret_values = body.ret_values(ctx); let target_range = body.text_range(); @@ -968,13 +968,11 @@ impl FunctionBody { fn ret_values<'a>( &self, ctx: &'a AssistContext<'_, '_>, - parent: &SyntaxNode, ) -> impl Iterator + 'a { - let parent = parent.clone(); let range = self.text_range(); locals_defined_in_body(&ctx.sema, self) .into_iter() - .filter_map(move |local| local_outlives_body(ctx, range, local, &parent)) + .filter_map(move |local| local_outlives_body(ctx, range, local)) } /// Analyses the function body for external control flow. @@ -1174,15 +1172,11 @@ fn has_exclusive_usages( usages .iter() .filter(|reference| body.contains_range(reference.range)) - .any(|reference| reference_is_exclusive(reference, body, ctx)) + .any(|reference| reference_is_exclusive(reference, ctx)) } /// checks if this reference requires `&mut` access inside node -fn reference_is_exclusive( - reference: &FileReference, - node: &dyn HasTokenAtOffset, - ctx: &AssistContext<'_, '_>, -) -> bool { +fn reference_is_exclusive(reference: &FileReference, ctx: &AssistContext<'_, '_>) -> bool { // FIXME: this quite an incorrect way to go about doing this :-) // `FileReference` is an IDE-type --- it encapsulates data communicated to the human, // but doesn't necessary fully reflect all the intricacies of the underlying language semantics @@ -1195,7 +1189,7 @@ fn reference_is_exclusive( } // we take `&mut` reference to variable: `&mut v` - let path = match path_element_of_reference(node, reference.range) { + let path = match path_element_of(reference) { Some(path) => path, None => return false, }; @@ -1205,11 +1199,6 @@ fn reference_is_exclusive( /// checks if this expr requires `&mut` access, recurses on field access fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option { - if let ast::Expr::MacroExpr(_) = expr { - // FIXME: expand macro and check output for mutable usages of the variable? - return None; - } - let parent = expr.syntax().parent()?; if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) { @@ -1238,66 +1227,19 @@ fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) Some(false) } -trait HasTokenAtOffset { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset; -} - -impl HasTokenAtOffset for SyntaxNode { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset { - SyntaxNode::token_at_offset(self, offset) - } -} - -impl HasTokenAtOffset for FunctionBody { - fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset { - match self { - FunctionBody::Expr(expr) => expr.syntax().token_at_offset(offset), - FunctionBody::Span { parent, text_range, .. } => { - match parent.syntax().token_at_offset(offset) { - TokenAtOffset::None => TokenAtOffset::None, - TokenAtOffset::Single(t) => { - if text_range.contains_range(t.text_range()) { - TokenAtOffset::Single(t) - } else { - TokenAtOffset::None - } - } - TokenAtOffset::Between(a, b) => { - match ( - text_range.contains_range(a.text_range()), - text_range.contains_range(b.text_range()), - ) { - (true, true) => TokenAtOffset::Between(a, b), - (true, false) => TokenAtOffset::Single(a), - (false, true) => TokenAtOffset::Single(b), - (false, false) => TokenAtOffset::None, - } - } - } - } - } - } -} - /// find relevant `ast::Expr` for reference /// /// # Preconditions /// /// `node` must cover `reference`, that is `node.text_range().contains_range(reference.range)` -fn path_element_of_reference( - node: &dyn HasTokenAtOffset, - reference_range: TextRange, -) -> Option { - let token = node.token_at_offset(reference_range.start()).right_biased().or_else(|| { - stdx::never!(false, "cannot find token at variable usage: {:?}", reference_range); - None - })?; - let path = token.parent_ancestors().find_map(ast::Expr::cast).or_else(|| { - stdx::never!(false, "cannot find path parent of variable usage: {:?}", token); +fn path_element_of(reference: &FileReference) -> Option { + let path = reference.name.syntax().ancestors().find_map(ast::Expr::cast).or_else(|| { + stdx::never!(false, "cannot find path parent of variable usage: {:?}", reference.name); None })?; stdx::always!( - matches!(path, ast::Expr::PathExpr(_) | ast::Expr::MacroExpr(_)), + matches!(path, ast::Expr::PathExpr(_)) + || path.syntax().parent().and_then(ast::FormatArgsExpr::cast).is_some(), "unexpected expression type for variable usage: {:?}", path ); @@ -1327,14 +1269,13 @@ fn local_outlives_body( ctx: &AssistContext<'_, '_>, body_range: TextRange, local: Local, - parent: &SyntaxNode, ) -> Option { let usages = LocalUsages::find_local_usages(ctx, local); let mut has_mut_usages = false; let mut any_outlives = false; for usage in usages.iter() { if body_range.end() <= usage.range.start() { - has_mut_usages |= reference_is_exclusive(usage, parent, ctx); + has_mut_usages |= reference_is_exclusive(usage, ctx); any_outlives |= true; if has_mut_usages { break; // no need to check more elements we have all the info we wanted @@ -2093,12 +2034,11 @@ fn fix_param_usages( let mut usages_for_param: Vec<(&Param<'_>, Vec)> = Vec::new(); let mut usages_for_self_param: Vec = Vec::new(); let source_range = source_syntax.text_range(); - let source_start = source_range.start(); + let syntax_offset = source_range.start() - syntax.text_range().start(); let reference_filter = |reference: &FileReference| { source_range.contains_range(reference.range).then_some(())?; - let local_range = reference.range - source_start; - path_element_of_reference(syntax, local_range) + path_element_of(reference) }; if let Some(self_param) = to_this_param { @@ -2119,10 +2059,20 @@ fn fix_param_usages( } let make = editor.make(); + let to_original = |old: &SyntaxNode| { + ctx.sema + .original_range_opt(old) + .map(|orig| crate::utils::cover_edit_range(syntax, orig.range - syntax_offset)) + }; + let replace = |range, new: &SyntaxNode| { + editor.replace_all(range, vec![new.clone().into()]); + }; for self_usage in usages_for_self_param { - let this_expr = make.expr_path(make.ident_path("this")); - editor.replace(self_usage.syntax(), this_expr.syntax()); + if let Some(original) = to_original(self_usage.syntax()) { + let this_expr = make.expr_path(make.ident_path("this")); + replace(original, this_expr.syntax()) + } } for (param, usages) in usages_for_param { for usage in usages { @@ -2135,24 +2085,33 @@ fn fix_param_usages( // do nothing } Some(ast::Expr::RefExpr(node)) - if param.kind() == ParamKind::MutRef && node.mut_token().is_some() => + if param.kind() == ParamKind::MutRef + && node.mut_token().is_some() + && let Some(original) = to_original(node.syntax()) => { - editor.replace( - node.syntax(), + replace( + original, node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); } Some(ast::Expr::RefExpr(node)) - if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() => + if param.kind() == ParamKind::SharedRef + && node.mut_token().is_none() + && let Some(original) = to_original(node.syntax()) => { - editor.replace( - node.syntax(), + replace( + original, node.expr().expect("RefExpr::expr() cannot be None").syntax(), ); } Some(_) | None => { - let p = make.expr_prefix(T![*], usage.clone()); - editor.replace(usage.syntax(), p.syntax()) + if let Some(original) = to_original(usage.syntax()) + // ignore variable in format string + && !matches!(usage, ast::Expr::Literal(_)) + { + let p = make.expr_prefix(T![*], usage.clone()); + replace(original, p.syntax()) + } } } } @@ -3519,6 +3478,61 @@ fn $0fun_name(n: &mut i32) { ); } + #[test] + fn mut_param_because_of_mut_ref_in_macro() { + check_assist( + extract_function, + r#" +macro_rules! refmut { ($e:expr) => { &mut $e }; } +fn foo() { + let mut n = 1; + $0let v = refmut!(n); + *v += 1;$0 + let k = n; +} +"#, + r#" +macro_rules! refmut { ($e:expr) => { &mut $e }; } +fn foo() { + let mut n = 1; + fun_name(&mut n); + let k = n; +} + +fn $0fun_name(n: &mut i32) { + let v = refmut!(*n); + *v += 1; +} +"#, + ); + + check_assist( + extract_function, + r#" +macro_rules! id { ($e:expr) => { $e }; } +fn foo() { + let mut n = 1; + $0let v = id!(&mut n); + *v += 1;$0 + let k = n; +} +"#, + r#" +macro_rules! id { ($e:expr) => { $e }; } +fn foo() { + let mut n = 1; + fun_name(&mut n); + let k = n; +} + +fn $0fun_name(n: &mut i32) { + let v = id!(n); + *v += 1; +} +"#, + ); + } + #[test] fn mut_param_by_value_because_of_mut_ref() { check_assist( @@ -6396,7 +6410,42 @@ fn main() { } fn $0fun_name(s: &Foo) { - *print!("{}{}", s, s); + print!("{}{}", *s, *s); +}"#, + ); + + check_assist( + extract_function, + r#" +//- minicore: fmt +#[derive(Debug)] +struct Foo(&'static str); + +impl Foo { + fn text(&self) -> &str { self.0 } +} + +fn main() { + let s = Foo(""); + $0print!("{s}{s}");$0 + let _ = s.text() == ""; +}"#, + r#" +#[derive(Debug)] +struct Foo(&'static str); + +impl Foo { + fn text(&self) -> &str { self.0 } +} + +fn main() { + let s = Foo(""); + fun_name(&s); + let _ = s.text() == ""; +} + +fn $0fun_name(s: &Foo) { + print!("{s}{s}"); }"#, ); } From a3443c6c7631c129d4b63992de4ad4e01d390faa Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Tue, 12 May 2026 16:28:13 +0800 Subject: [PATCH 011/114] feat: add diagnostic for E0029 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 ++ .../crates/hir-ty/src/infer/pat.rs | 2 +- .../crates/hir/src/diagnostics.rs | 10 ++++ .../src/handlers/invalid_range_pat_type.rs | 55 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs 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 6a29b2cedba3f..8f17776d02c46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -302,6 +302,10 @@ pub enum InferenceDiagnostic { pat: PatId, found: StoredTy, }, + InvalidRangePatType { + #[type_visitable(ignore)] + pat: PatId, + }, DuplicateField { #[type_visitable(ignore)] field: ExprOrPatId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index f21438647c14f..8e87d22a45a04 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -843,7 +843,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { // There exists a side that didn't meet our criteria that the end-point // be of a numeric or char type, as checked in `calc_side` above. - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::InvalidRangePatType { pat }); return self.types.types.error; } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index a044f24587bae..77a17f1c76a56 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -115,6 +115,7 @@ diagnostics![AnyDiagnostic<'db> -> InvalidCast<'db>, InvalidDeriveTarget, InvalidLhsOfAssignment, + InvalidRangePatType, MacroDefError, MacroError, MacroExpansionParseError, @@ -303,6 +304,11 @@ pub struct ExpectedArrayOrSlicePat<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct InvalidRangePatType { + pub pat: InFile, +} + #[derive(Debug)] pub struct ExpectedFunction<'db> { pub call: InFile, @@ -788,6 +794,10 @@ impl<'db> AnyDiagnostic<'db> { let pat = pat_syntax(*pat)?.map(Into::into); ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into() } + &InferenceDiagnostic::InvalidRangePatType { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + InvalidRangePatType { pat }.into() + } &InferenceDiagnostic::DuplicateField { field: expr, variant } => { let expr_or_pat = match expr { ExprOrPatId::ExprId(expr) => { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs new file mode 100644 index 0000000000000..465f59559d294 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_range_pat_type.rs @@ -0,0 +1,55 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: invalid-range-pat-type +// +// This diagnostic is triggered when a range pattern is used with a type that +// is neither `char` nor numeric. +pub(crate) fn invalid_range_pat_type( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::InvalidRangePatType, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0029"), + "only `char` and numeric types are allowed in range patterns", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn bool_range_pattern() { + check_diagnostics( + r#" +fn f(x: bool) { + match x { + false..=true => {} + //^^^^^^^^^^^^ error: only `char` and numeric types are allowed in range patterns + } +} +"#, + ); + } + + #[test] + fn numeric_and_char_range_patterns() { + check_diagnostics( + r#" +fn f(x: u8, c: char) { + match x { + 0..=9 => {} + _ => {} + } + match c { + 'a'..='z' => {} + _ => {} + } +} +"#, + ); + } +} 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 49b3234a11d8e..967b965f51a06 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -47,6 +47,7 @@ mod handlers { pub(crate) mod invalid_cast; pub(crate) mod invalid_derive_target; pub(crate) mod invalid_lhs_of_assignment; + pub(crate) mod invalid_range_pat_type; pub(crate) mod macro_error; pub(crate) mod malformed_derive; pub(crate) mod mismatched_arg_count; @@ -519,6 +520,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d), AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), + AnyDiagnostic::InvalidRangePatType(d) => handlers::invalid_range_pat_type::invalid_range_pat_type(&ctx, &d), AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), From 1bae2d43ff7ca618fccbc1d31ef72eb35916674b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 12 May 2026 18:29:23 +0300 Subject: [PATCH 012/114] Replace cfg-if with std::cfg_select --- src/tools/rust-analyzer/Cargo.lock | 1 - src/tools/rust-analyzer/crates/profile/Cargo.toml | 1 - .../crates/profile/src/memory_usage.rs | 15 ++++++++------- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index cbbeef00309e3..0606ad81ba34d 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1939,7 +1939,6 @@ dependencies = [ name = "profile" version = "0.0.0" dependencies = [ - "cfg-if", "libc", "perf-event", "tikv-jemalloc-ctl", diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml index 048d3776ca33f..2cffb269fd677 100644 --- a/src/tools/rust-analyzer/crates/profile/Cargo.toml +++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml @@ -13,7 +13,6 @@ rust-version.workspace = true doctest = false [dependencies] -cfg-if = "1.0.1" jemalloc-ctl = { version = "0.5.4", package = "tikv-jemalloc-ctl", optional = true } [target.'cfg(all(target_os = "linux", not(target_env = "ohos"), any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))'.dependencies] diff --git a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs index 1462259d627b3..a8a409bd4686d 100644 --- a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs +++ b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs @@ -3,8 +3,6 @@ //! Measures the total size of all currently allocated objects. use std::fmt; -use cfg_if::cfg_if; - #[derive(Copy, Clone)] pub struct MemoryUsage { pub allocated: Bytes, @@ -25,15 +23,17 @@ impl std::ops::Sub for MemoryUsage { impl MemoryUsage { pub fn now() -> MemoryUsage { - cfg_if! { - if #[cfg(all(feature = "jemalloc", not(target_env = "msvc")))] { + cfg_select! { + all(feature = "jemalloc", not(target_env = "msvc")) => { jemalloc_ctl::epoch::advance().unwrap(); MemoryUsage { allocated: Bytes(jemalloc_ctl::stats::allocated::read().unwrap() as isize), } - } else if #[cfg(all(target_os = "linux", target_env = "gnu"))] { + } + all(target_os = "linux", target_env = "gnu") => { memusage_linux() - } else if #[cfg(windows)] { + } + windows => { // There doesn't seem to be an API for determining heap usage, so we try to // approximate that by using the Commit Charge value. @@ -48,7 +48,8 @@ impl MemoryUsage { let usage = unsafe { mem_counters.assume_init().PagefileUsage }; MemoryUsage { allocated: Bytes(usage as isize) } - } else { + } + _ => { MemoryUsage { allocated: Bytes(0) } } } From 465c5773ecdb22234876ffc8ce95b548b756ef93 Mon Sep 17 00:00:00 2001 From: Kao-Wei Yeh Date: Thu, 14 May 2026 00:37:53 +0800 Subject: [PATCH 013/114] Fix assit `qualify_path` error when number of path segments greater than 2 --- .../ide-assists/src/handlers/qualify_path.rs | 82 +++++++++++++++++-- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index cb48554083ab4..5e07d85db9cf2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -52,8 +52,8 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Op ImportCandidate::Path(candidate) if !candidate.qualifier.is_empty() => { cov_mark::hit!(qualify_path_qualifier_start); let path = ast::Path::cast(syntax_under_caret.clone())?; - let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); - QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) + let first_seg_generics = path.segments().next()?.generic_arg_list(); + QualifyCandidate::QualifierStart(path, first_seg_generics) } ImportCandidate::Path(_) => { cov_mark::hit!(qualify_path_unqualified_name); @@ -113,7 +113,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Op Some(()) } pub(crate) enum QualifyCandidate<'db> { - QualifierStart(ast::PathSegment, Option), + QualifierStart(ast::Path, Option), UnqualifiedName(Option), TraitAssocItem(ast::Path, ast::PathSegment), TraitMethod(&'db RootDatabase, ast::MethodCallExpr), @@ -131,9 +131,11 @@ impl QualifyCandidate<'_> { ) { let import = mod_path_to_ast_with_factory(editor.make(), import, edition); match self { - QualifyCandidate::QualifierStart(segment, generics) => { + QualifyCandidate::QualifierStart(path, generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); - replacer(format!("{import}{generics}::{segment}")); + let suffix = + path.segments().skip(1).map(|s| s.to_string()).collect::>().join("::"); + replacer(format!("{import}{generics}::{suffix}")); } QualifyCandidate::UnqualifiedName(generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); @@ -284,6 +286,76 @@ fmt::Formatter ); } + #[test] + fn applicable_when_multiple_segments() { + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b::c::foo$0 +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b::c$0::foo +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + check_assist( + qualify_path, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +b$0::c::foo +"#, + r#" + mod a { pub mod b { pub mod c { pub fn foo() {} } } } +a::b::c::foo +"#, + ); + } + + #[test] + fn applicable_when_multiple_segments_with_generics() { + check_assist( + qualify_path, + r#" +mod a { + pub mod b { + pub struct TestStruct(T); + impl TestStruct { + pub const TEST_CONST: u8 = 42; + } + } +} +fn main() { + b::TestStruct::<()>::TEST_CONST$0; +} +"#, + r#" +mod a { + pub mod b { + pub struct TestStruct(T); + impl TestStruct { + pub const TEST_CONST: u8 = 42; + } + } +} +fn main() { + a::b::TestStruct::<()>::TEST_CONST; +} +"#, + ); + } + #[test] fn applicable_when_found_an_import() { check_assist( From 7ef1b16290cab5cd3b7d2abb4a386f6a7912f338 Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Thu, 14 May 2026 00:57:18 +0800 Subject: [PATCH 014/114] feat: add diagnostic for E0638 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 6 ++ .../crates/hir-ty/src/infer/pat.rs | 4 +- .../crates/hir/src/diagnostics.rs | 11 +++ .../src/handlers/non_exhaustive_record_pat.rs | 79 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 4 + 5 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs 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 8f17776d02c46..6aa647694e108 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -365,6 +365,12 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] expr: ExprId, }, + NonExhaustiveRecordPat { + #[type_visitable(ignore)] + pat: PatId, + #[type_visitable(ignore)] + variant: VariantId, + }, FunctionalRecordUpdateOnNonStruct { #[type_visitable(ignore)] base_expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 8e87d22a45a04..a42d00786e8d1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -1155,7 +1155,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; fn check_record_pat_fields( &mut self, adt_ty: Ty<'db>, - _pat: PatId, + pat: PatId, variant: VariantId, fields: &[RecordFieldPat], has_rest_pat: bool, @@ -1233,7 +1233,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // Require `..` if struct has non_exhaustive attribute. let non_exhaustive = self.has_applicable_non_exhaustive(variant.into()); if non_exhaustive && !has_rest_pat { - // FIXME: Emit an error. + self.push_diagnostic(InferenceDiagnostic::NonExhaustiveRecordPat { pat, variant }); } // Report an error if an incorrect number of fields was specified. diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 77a17f1c76a56..9e4b929dd5fed 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -129,6 +129,7 @@ diagnostics![AnyDiagnostic<'db> -> NeedMut, NonExhaustiveLet, NonExhaustiveRecordExpr, + NonExhaustiveRecordPat, NoSuchField, MismatchedArrayPatLen, DuplicateField, @@ -406,6 +407,12 @@ pub struct NonExhaustiveRecordExpr { pub expr: InFile, } +#[derive(Debug)] +pub struct NonExhaustiveRecordPat { + pub pat: InFile, + pub variant: Variant, +} + #[derive(Debug)] pub struct TypeMismatch<'db> { pub expr_or_pat: InFile, @@ -882,6 +889,10 @@ impl<'db> AnyDiagnostic<'db> { &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => { NonExhaustiveRecordExpr { expr: expr_syntax(expr)? }.into() } + &InferenceDiagnostic::NonExhaustiveRecordPat { pat, variant } => { + let pat = pat_syntax(pat)?.map(Into::into); + NonExhaustiveRecordPat { pat, variant: variant.into() }.into() + } &InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr } => { FunctionalRecordUpdateOnNonStruct { base_expr: expr_syntax(base_expr)? }.into() } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs new file mode 100644 index 0000000000000..ea587e6037db1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_record_pat.rs @@ -0,0 +1,79 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: non-exhaustive-record-pat +// +// This diagnostic is triggered if a record pattern destructures a `#[non_exhaustive]` +// struct or enum variant from another crate without `..`. +pub(crate) fn non_exhaustive_record_pat( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::NonExhaustiveRecordPat, +) -> Diagnostic { + let item = match d.variant { + hir::Variant::Struct(_) => "struct", + hir::Variant::Union(_) => "union", + hir::Variant::EnumVariant(_) => "variant", + }; + + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0638"), + format!("`..` required with {item} marked as non-exhaustive"), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn reports_external_non_exhaustive_struct_pattern_without_rest() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +#[non_exhaustive] +pub struct S { + pub field: u32, +} + +fn local_ok(s: S) { + let S { field } = s; + let _ = field; +} + +//- /main.rs crate:main deps:lib +fn main(s: lib::S) { + let lib::S { field } = s; + //^^^^^^^^^^^^^^^^ error: `..` required with struct marked as non-exhaustive + let _ = field; +} +"#, + ); + } + + #[test] + fn reports_external_non_exhaustive_variant_pattern_without_rest() { + check_diagnostics( + r#" +//- /lib.rs crate:lib +pub enum E { + #[non_exhaustive] + V { field: u32 }, +} + +fn local_ok(e: E) { + let E::V { field } = e; + let _ = field; +} + +//- /main.rs crate:main deps:lib +fn main(e: lib::E) { + let lib::E::V { field } = e; + //^^^^^^^^^^^^^^^^^^^ error: `..` required with variant marked as non-exhaustive + let _ = field; +} +"#, + ); + } +} 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 967b965f51a06..0809054a2e3aa 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -61,6 +61,7 @@ mod handlers { pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; pub(crate) mod non_exhaustive_record_expr; + pub(crate) mod non_exhaustive_record_pat; pub(crate) mod parenthesized_generic_args_without_fn_trait; pub(crate) mod pattern_arg_in_extern_fn; pub(crate) mod private_assoc_item; @@ -467,6 +468,9 @@ pub fn semantic_diagnostics( AnyDiagnostic::NonExhaustiveRecordExpr(d) => { handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d) } + AnyDiagnostic::NonExhaustiveRecordPat(d) => { + handlers::non_exhaustive_record_pat::non_exhaustive_record_pat(&ctx, &d) + } AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d), AnyDiagnostic::DuplicateField(d) => handlers::duplicate_field::duplicate_field(&ctx, &d), AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d), From 0374c681b164e3e5be5e6012900c0eb0ac6c5207 Mon Sep 17 00:00:00 2001 From: Kai Tanaka <275430420+quyentonndbs@users.noreply.github.com> Date: Thu, 14 May 2026 05:34:24 +0000 Subject: [PATCH 015/114] fix: duplicated words in stdx/assert.rs and ide/inject.rs doc-comments Signed-off-by: Kai Tanaka <275430420+quyentonndbs@users.noreply.github.com> --- .../crates/ide/src/syntax_highlighting/inject.rs | 2 +- src/tools/rust-analyzer/crates/stdx/src/assert.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 76bb06328b7cf..9af6d67b59803 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 @@ -177,7 +177,7 @@ pub(super) fn doc_comment( match sema.first_crate(vfs_file_id) { Some(krate) => krate.base().data(sema.db).proc_macro_cwd.clone(), None => { - // Arbitrarily pick /, since from_single_file treats this file as as /main.rs anyway. + // Arbitrarily pick /, since from_single_file treats this file as /main.rs anyway. Arc::new(ide_db::base_db::AbsPathBuf::try_from("/").unwrap()) } } diff --git a/src/tools/rust-analyzer/crates/stdx/src/assert.rs b/src/tools/rust-analyzer/crates/stdx/src/assert.rs index 91c279798c266..6032395a4dd85 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/assert.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/assert.rs @@ -43,7 +43,7 @@ /// Asserts that the condition is always true and returns its actual value. /// -/// If the condition is true does nothing and and evaluates to true. +/// If the condition is true does nothing and evaluates to true. /// /// If the condition is false: /// * panics if `force` feature or `debug_assertions` are enabled, @@ -71,7 +71,7 @@ macro_rules! always { /// Asserts that the condition is never true and returns its actual value. /// -/// If the condition is false does nothing and and evaluates to false. +/// If the condition is false does nothing and evaluates to false. /// /// If the condition is true: /// * panics if `force` feature or `debug_assertions` are enabled, From a16343f2a72aae6c57a367f3d64319fd831580d4 Mon Sep 17 00:00:00 2001 From: Onyeka Obi Date: Thu, 14 May 2026 00:27:37 -0700 Subject: [PATCH 016/114] fix: show Run lens for fn main in bench targets `request.rs` was passing `binary_target = true` for TargetKind::Bin, Example, and Test, but not for Bench, so a `fn main()` in `benches/foo.rs` (typical with `harness = false`) was suppressed by `should_skip_runnable` in the annotations layer. Mirror `target_spec.rs:255` by including Bench in both the code-lens config check and `should_skip_target`. Closes rust-lang/rust-analyzer#21948. Signed-off-by: Onyeka Obi --- .../crates/rust-analyzer/src/handlers/request.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index ca6a2e70b09f5..5bc0f5f0a72aa 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1662,7 +1662,10 @@ pub(crate) fn handle_code_lens( .map(|spec| { matches!( spec.target_kind(), - TargetKind::Bin | TargetKind::Example | TargetKind::Test + TargetKind::Bin + | TargetKind::Example + | TargetKind::Test + | TargetKind::Bench ) }) .unwrap_or(false), @@ -2345,7 +2348,7 @@ fn should_skip_target(runnable: &Runnable, cargo_spec: Option<&TargetSpec>) -> b match &cargo_spec { Some(spec) => !matches!( spec.target_kind(), - TargetKind::Bin | TargetKind::Example | TargetKind::Test + TargetKind::Bin | TargetKind::Example | TargetKind::Test | TargetKind::Bench ), None => true, } From f908c16761b274a11c3f5dad2ca008ad948d2b35 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 14 May 2026 10:46:15 +0200 Subject: [PATCH 017/114] Show `const` in the signature help if applicable --- .../crates/ide/src/signature_help.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index edcf0dc22b231..dbecbafdd7c03 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -174,6 +174,9 @@ fn signature_help_for_call( match callable.kind() { hir::CallableKind::Function(func) => { res.doc = func.docs(db).map(Documentation::into_owned); + if func.is_const(db) { + format_to!(res.signature, "const "); + } if func.is_async(db) { format_to!(res.signature, "async "); } @@ -2664,4 +2667,22 @@ fn main() { "#]], ); } + + #[test] + fn test_const_function() { + check( + r#" +//- minicore: sized, fn +pub const fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + foo($0) +} + "#, + expect![[r#" + const fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } } From b775d2bd66366e61c230ee4dbaa846ac0a32c507 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 12 May 2026 11:50:58 +0800 Subject: [PATCH 018/114] minor: no add newline when `.lete` not in tail_expr Previously, adding newline between statements was often redundant Example --- ```rust fn main() { let bar = Some(true); bar.$0 other(); } ``` **Before this PR** ```rust fn main() { let bar = Some(true); let Some(${1:bar}) = bar else { $2 }; $0 other(); } ``` **After this PR** ```rust fn main() { let bar = Some(true); let Some(${1:bar}) = bar else { $2 };$0 other(); } ``` --- .../ide-completion/src/completions/postfix.rs | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 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 0cb39dd10885b..540089cf91052 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 @@ -259,7 +259,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "lete", "let Ok else {}", - format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0"), + format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};$0"), ) .add_to(acc, ctx.db); @@ -281,9 +281,7 @@ pub(crate) fn complete_postfix( postfix_snippet( "lete", "let Some else {}", - format!( - "let Some({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0" - ), + format!("let Some({placeholder}) = {receiver_text} else {{\n $2\n}};$0"), ) .add_to(acc, ctx.db); @@ -1101,8 +1099,28 @@ fn main() { let bar = Some(true); let Some(${1:bar}) = bar else { $2 -}; -$0 +};$0 +} +"#, + ); + + check_edit( + "lete", + r#" +//- minicore: option +fn main() { + let bar = Some(true); + bar.$0 + other(); +} +"#, + r#" +fn main() { + let bar = Some(true); + let Some(${1:bar}) = bar else { + $2 +};$0 + other(); } "#, ); From 8e6a614ae28f157dfe1fec4e459156ac7903ec2d Mon Sep 17 00:00:00 2001 From: mu001999 Date: Fri, 15 May 2026 09:23:33 +0800 Subject: [PATCH 019/114] Remove unused FixupError --- .../hir-ty/src/next_solver/infer/mod.rs | 27 ------------------- 1 file changed, 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 839bdf17e7589..6b6dd549b34e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -1,7 +1,6 @@ //! Infer context the next-trait-solver. use std::cell::{Cell, RefCell}; -use std::fmt; use std::ops::Range; use std::sync::Arc; @@ -308,32 +307,6 @@ pub enum BoundRegionConversionTime { AssocTypeProjection(SolverDefId), } -#[derive(Copy, Clone, Debug)] -pub struct FixupError { - unresolved: TyOrConstInferVar, -} - -impl fmt::Display for FixupError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use TyOrConstInferVar::*; - - match self.unresolved { - TyInt(_) => write!( - f, - "cannot determine the type of this integer; \ - add a suffix to specify the type explicitly" - ), - TyFloat(_) => write!( - f, - "cannot determine the type of this number; \ - add a suffix to specify the type explicitly" - ), - Ty(_) => write!(f, "unconstrained type"), - Const(_) => write!(f, "unconstrained const value"), - } - } -} - /// See the `region_obligations` field for more information. #[derive(Clone, Debug)] pub struct TypeOutlivesConstraint<'db> { From 68ef6edbf8559c690953b3c0144b0a9eddfb2942 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 15 May 2026 02:19:51 +0800 Subject: [PATCH 020/114] fix: no lint unsized adt self_ty missing bounded assoc Example --- ```rust trait Trait { fn item() where Self: Sized; } impl Trait for Adt {} struct Adt([i32]); ``` **Before this PR** ``` error: not all trait items implemented, missing: `fn item` ``` **After this PR** no lint --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 53 ++++++++++++++++--- .../handlers/trait_impl_missing_assoc_item.rs | 8 +++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 0423a8fc4f202..53df9f21dddb9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -946,14 +946,17 @@ impl Module { .collect(); if !missing.is_empty() { + let env = ParamEnvAndCrate { + param_env: db.trait_environment(GenericDefId::from(impl_id).into()), + krate: self.id.krate(db), + }; let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); - let self_ty = structurally_normalize_ty( - &infcx, - self_ty, - db.trait_environment(GenericDefId::from(impl_id).into()), - ); + let self_ty = structurally_normalize_ty(&infcx, self_ty, env.param_env); + let tail_ty = struct_tail_raw(db, interner, self_ty, |ty| { + structurally_normalize_ty(&infcx, ty, env.param_env) + }); let self_ty_is_guaranteed_unsized = matches!( - self_ty.kind(), + tail_ty.kind(), TyKind::Dynamic(..) | TyKind::Slice(..) | TyKind::Str ); if self_ty_is_guaranteed_unsized { @@ -7455,5 +7458,43 @@ fn empty_param_env<'db>(krate: base_db::Crate) -> ParamEnvAndCrate<'db> { ParamEnvAndCrate { param_env: ParamEnv::empty(), krate } } +// Like https://github.com/rust-lang/rust/blob/7c3c88f42ad444f4688b865591d84660be4ece2f/compiler/rustc_middle/src/ty/util.rs#L254-L310 +pub fn struct_tail_raw<'db>( + db: &'db dyn HirDatabase, + interner: DbInterner<'db>, + mut ty: Ty<'db>, + mut normalize: impl FnMut(Ty<'db>) -> Ty<'db>, +) -> Ty<'db> { + let recursion_limit = 16; + for iteration in 0.. { + if iteration >= recursion_limit { + return Ty::new_error(interner, ErrorGuaranteed); + } + match ty.kind() { + TyKind::Adt(def, args) => { + let AdtId::StructId(def_id) = def.def_id() else { break }; + let last_field = db.field_types(def_id.into()).iter().next_back(); + match last_field { + Some((_, field)) => { + ty = normalize(field.get().instantiate(interner, args).skip_norm_wip()) + } + None => break, + } + } + TyKind::Tuple(tys) if let Some((&last_ty, _)) = tys.split_last() => { + ty = last_ty; + } + TyKind::Tuple(_) => break, + TyKind::Pat(inner, _) => { + ty = inner; + } + _ => { + break; + } + } + } + ty +} + pub use hir_ty::next_solver; pub use hir_ty::setup_tracing; 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 5f5e155bd79ea..1ffef25b50605 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 @@ -149,10 +149,18 @@ impl Trait for () { type Item = (); fn item() {} } +impl Trait for Adt {} + //^^^^^ error: not all trait items implemented, missing: `type Item`, `fn item` // Items with Self: Sized bound not required to be implemented for unsized types. impl Trait for str {} impl Trait for dyn OtherTrait {} +impl Trait for Adt<[i32]> {} +impl Trait for Slice {} +impl Trait for (str,) {} + +struct Adt(i32, T); +struct Slice([T]); "#, ) } From 862da81c00c6a0b2af035aeed2d07728f5954c02 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 15 May 2026 10:45:30 +0800 Subject: [PATCH 021/114] Migrate replace_method_eager_lazy to SyntaxEditor --- .../src/handlers/replace_method_eager_lazy.rs | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 39e15578cb65a..bbbbc3bf7bea0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -2,7 +2,7 @@ use hir::Semantics; use ide_db::{RootDatabase, assists::AssistId, defs::Definition, famous_defs::FamousDefs}; use syntax::{ AstNode, - ast::{self, Expr, HasArgList, make, syntax_factory::SyntaxFactory}, + ast::{self, Expr, HasArgList, syntax_factory::SyntaxFactory}, }; use crate::{AssistContext, Assists, utils::wrap_paren_in_call}; @@ -64,6 +64,7 @@ pub(crate) fn replace_with_lazy_method( format!("Replace {method_name} with {method_name_lazy}"), call.syntax().text_range(), |builder| { + let editor = builder.make_editor(call.syntax()); let param_name = match &*method_name_lazy { "and_then" => "it", "or_else" | "unwrap_or_else" => { @@ -77,9 +78,10 @@ pub(crate) fn replace_with_lazy_method( } _ => "", }; - let closured = into_closure(&last_arg, param_name); - builder.replace(method_name.syntax().text_range(), method_name_lazy); - builder.replace_ast(last_arg, closured); + let closured = into_closure(&last_arg, param_name, editor.make()); + editor.replace(method_name.syntax(), editor.make().name(&method_name_lazy).syntax()); + editor.replace(last_arg.syntax(), closured.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -96,7 +98,7 @@ fn lazy_method_name(name: &str) -> String { } } -fn into_closure(param: &Expr, param_name: &str) -> Expr { +fn into_closure(param: &Expr, param_name: &str, make: &SyntaxFactory) -> Expr { (|| { if let ast::Expr::CallExpr(call) = param { if call.arg_list()?.args().count() == 0 { Some(call.expr()?) } else { None } @@ -105,10 +107,9 @@ fn into_closure(param: &Expr, param_name: &str) -> Expr { } })() .unwrap_or_else(|| { - let pats = (!param_name.is_empty()).then(|| { - make::untyped_param(make::ext::simple_ident_pat(make::name(param_name)).into()) - }); - make::expr_closure(pats, param.clone()).into() + let pats = (!param_name.is_empty()) + .then(|| make.untyped_param(make.simple_ident_pat(make.name(param_name)).into())); + make.expr_closure(pats, param.clone()).into() }) } @@ -170,14 +171,16 @@ pub(crate) fn replace_with_eager_method( format!("Replace {method_name} with {method_name_eager}"), call.syntax().text_range(), |builder| { - builder.replace(method_name.syntax().text_range(), method_name_eager); - let called = into_call(&last_arg, &ctx.sema); - builder.replace_ast(last_arg, called); + let editor = builder.make_editor(call.syntax()); + let called = into_call(&last_arg, &ctx.sema, editor.make()); + editor.replace(method_name.syntax(), editor.make().name(method_name_eager).syntax()); + editor.replace(last_arg.syntax(), called.syntax()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } -fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr { +fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>, make: &SyntaxFactory) -> Expr { (|| { if let ast::Expr::ClosureExpr(closure) = param { let mut params = closure.param_list()?.params(); @@ -197,8 +200,8 @@ fn into_call(param: &Expr, sema: &Semantics<'_, RootDatabase>) -> Expr { } })() .unwrap_or_else(|| { - let callable = wrap_paren_in_call(param.clone(), &SyntaxFactory::without_mappings()); - make::expr_call(callable, make::arg_list(Vec::new())).into() + let callable = wrap_paren_in_call(param.clone(), make); + make.expr_call(callable, make.arg_list(Vec::new())).into() }) } From 06cb94134a3a92e9bfef9934b7dd37261068c0be Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 15 May 2026 10:56:40 +0800 Subject: [PATCH 022/114] Added param uses placeholder --- .../src/handlers/replace_method_eager_lazy.rs | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index bbbbc3bf7bea0..22b8861e5f543 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -65,22 +65,24 @@ pub(crate) fn replace_with_lazy_method( call.syntax().text_range(), |builder| { let editor = builder.make_editor(call.syntax()); - let param_name = match &*method_name_lazy { - "and_then" => "it", + let add_param = match &*method_name_lazy { + "and_then" => true, "or_else" | "unwrap_or_else" => { - if let Some(result) = FamousDefs(&ctx.sema, scope.krate()).core_result_Result() - && result.ty(ctx.db()).could_unify_with(ctx.db(), &receiver_ty) - { - "e" - } else { - "" - } + FamousDefs(&ctx.sema, scope.krate()).core_result_Result().is_some_and( + |result| result.ty(ctx.db()).could_unify_with(ctx.db(), &receiver_ty), + ) } - _ => "", + _ => false, }; - let closured = into_closure(&last_arg, param_name, editor.make()); + let closured = into_closure(&last_arg, add_param, editor.make()); editor.replace(method_name.syntax(), editor.make().name(&method_name_lazy).syntax()); editor.replace(last_arg.syntax(), closured.syntax()); + if let Some(cap) = ctx.config.snippet_cap + && let ast::Expr::ClosureExpr(closured) = closured + && let Some(param) = closured.param_list().and_then(|it| it.params().next()) + { + editor.add_annotation(param.syntax(), builder.make_placeholder_snippet(cap)); + } builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) @@ -98,7 +100,7 @@ fn lazy_method_name(name: &str) -> String { } } -fn into_closure(param: &Expr, param_name: &str, make: &SyntaxFactory) -> Expr { +fn into_closure(param: &Expr, add_param: bool, make: &SyntaxFactory) -> Expr { (|| { if let ast::Expr::CallExpr(call) = param { if call.arg_list()?.args().count() == 0 { Some(call.expr()?) } else { None } @@ -107,8 +109,7 @@ fn into_closure(param: &Expr, param_name: &str, make: &SyntaxFactory) -> Expr { } })() .unwrap_or_else(|| { - let pats = (!param_name.is_empty()) - .then(|| make.untyped_param(make.simple_ident_pat(make.name(param_name)).into())); + let pats = add_param.then(|| make.untyped_param(make.wildcard_pat().into())); make.expr_closure(pats, param.clone()).into() }) } @@ -259,7 +260,7 @@ fn foo() { r#" fn foo() { let foo = Ok(1); - return foo.unwrap_or_else(|e| 2); + return foo.unwrap_or_else(|${0:_}| 2); } "#, ) @@ -395,7 +396,7 @@ fn foo() { r#" fn foo() { let foo = Some("foo"); - return foo.and_then(|it| Some("bar")); + return foo.and_then(|${0:_}| Some("bar")); } "#, ) From 7395257da6f735ee02b6dae1a34d4643e9f4e66b Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 15 May 2026 07:13:48 +0300 Subject: [PATCH 023/114] Lower OR pattern types --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) 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 ec42b8a3493bb..64f3cbe06a954 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -19,7 +19,7 @@ use hir_def::{ builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, hir::{ - ExprId, + ExprId, PatId, generics::{ GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamProvenance, WherePredicate, @@ -63,8 +63,8 @@ use crate::{ next_solver::{ AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, ConstKind, DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind, - FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, Pattern, PolyFnSig, Predicate, - Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, + FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PatList, Pattern, PolyFnSig, + Predicate, Region, StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety, util::BottomUpFolder, }, @@ -541,20 +541,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } &TypeRef::PatternType(ty, pat) => { let ty = self.lower_ty(ty); - // FIXME: Properly do the lowering here - let pat_kind = match self.store[pat] { - hir_def::hir::Pat::Range { - start: Some(start), - end: Some(end), - range_type: _, - } => rustc_type_ir::PatternKind::Range { - start: self.lower_expr_as_const(start, ty), - end: self.lower_expr_as_const(end, ty), - }, - hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull, - _ => rustc_type_ir::PatternKind::NotNull, + let Some(pat) = self.lower_pattern_type(pat, ty) else { + // FIXME: Report an error. + return (self.types.types.error, res); }; - let pat = Pattern::new(self.interner, pat_kind); Ty::new_pat(self.interner, ty, pat) } TypeRef::Error => self.types.types.error, @@ -562,6 +552,27 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { (ty, res) } + fn lower_pattern_type(&mut self, pat: PatId, ty: Ty<'db>) -> Option> { + let pat_kind = match self.store[pat] { + hir_def::hir::Pat::Range { start: Some(start), end: Some(end), range_type: _ } => { + rustc_type_ir::PatternKind::Range { + start: self.lower_expr_as_const(start, ty), + end: self.lower_expr_as_const(end, ty), + } + } + hir_def::hir::Pat::NotNull => rustc_type_ir::PatternKind::NotNull, + hir_def::hir::Pat::Or(ref pats) => rustc_type_ir::PatternKind::Or( + PatList::new_from_iter( + self.interner, + pats.iter().map(|&pat| self.lower_pattern_type(pat, ty).ok_or(())), + ) + .ok()?, + ), + _ => return None, + }; + Some(Pattern::new(self.interner, pat_kind)) + } + fn lower_fn_ptr(&mut self, fn_: &FnType) -> Ty<'db> { let interner = self.interner; let (params, ret_ty) = fn_.split_params_and_ret(); From 94b5aa9b169020c8eb1156784091df148eb182b1 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 15 May 2026 14:13:08 +0800 Subject: [PATCH 024/114] fix: only ref match non-unknown value items Example --- ```rust struct S(T); impl core::ops::Deref for S { type Target = T; } fn foo(s: &u32) {} fn main() { let ssss = S(); foo($0); } ``` **Before this PR** ```text lc ssss S<{unknown}> [local] lc &ssss [type+local] st S S<{unknown}> [] st &S [type] ... ``` **After this PR** ```text lc ssss S<{unknown}> [local] st S S<{unknown}> [] ... ``` --- .../crates/ide-completion/src/render.rs | 45 ++++++++++++++----- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index fbbdffefe324b..ce6c792a89bb7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -492,7 +492,13 @@ fn render_resolution_path( ..CompletionRelevance::default() }); - path_ref_match(completion, path_ctx, &ty, &mut item); + match resolution { + ScopeDef::Local(_) + | ScopeDef::ModuleDef(ModuleDef::Const(_) | ModuleDef::Static(_)) => { + path_ref_match(completion, path_ctx, &ty, &mut item) + } + _ => (), + } }; match resolution { @@ -720,7 +726,7 @@ fn compute_ref_match( || completion_ty.could_unify_with(ctx.db, expected_without_ref)) && completion_ty .autoderef(ctx.db) - .any(|ty| ty.could_unify_with(ctx.db, expected_without_ref)) + .any(|ty| !ty.is_unknown() && ty.could_unify_with(ctx.db, expected_without_ref)) { cov_mark::hit!(suggest_ref); let mutability = if expected_type.is_mutable_reference() { @@ -2639,7 +2645,6 @@ fn go(world: &WorldSnapshot) { go(w$0) } st WorldSnapshot {…} WorldSnapshot { _f: () } [] st &WorldSnapshot {…} [type] st WorldSnapshot WorldSnapshot [] - st &WorldSnapshot [type] fn go(…) fn(&WorldSnapshot) [] "#]], ); @@ -2865,7 +2870,6 @@ fn main() { st S S [] st &mut S [type] st S S [] - st &mut S [type] fn foo(…) fn(&mut S) [] fn main() fn() [] "#]], @@ -2879,6 +2883,7 @@ fn main() { foo(&mut $0); } "#, + // FIXME: There are many `S` here expect![[r#" lc s S [type+name+local] st S S [type] @@ -2941,11 +2946,34 @@ fn main() { lc ssss S [local] lc &mut ssss [type+local] st S S<{unknown}> [] - st &mut S [type] fn foo(…) fn(&mut S) [] fn main() fn() [] "#]], ); + // Regression test https://github.com/rust-lang/rust-analyzer/issues/22324 + check_relevance( + r#" +//- minicore: deref +struct S(T); +impl core::ops::Deref for S { + type Target = T; +} +fn foo(s: &u32) {} +fn main() { + let ssss = S(); + foo($0); +} + "#, + // FIXME: term_search exclude ssss.0 (field.ty().is_unknown()) + expect![[r#" + ex ssss.0 [type_could_unify] + lc ssss S<{unknown}> [local] + st S S<{unknown}> [] + md core:: [] + fn foo(…) fn(&u32) [] + fn main() fn() [] + "#]], + ); } #[test] @@ -3016,9 +3044,7 @@ fn main() { lc t T [local] lc &t [type+local] st S S [] - st &S [type] st T T [] - st &T [type] md core:: [] fn foo(…) fn(&S) [] fn main() fn() [] @@ -3065,9 +3091,7 @@ fn main() { lc t T [local] lc &mut t [type+local] st S S [] - st &mut S [type] st T T [] - st &mut T [type] md core:: [] fn foo(…) fn(&mut S) [] fn main() fn() [] @@ -3131,7 +3155,6 @@ fn bar(t: &Foo) {} ev Foo::B Foo::B [] ev &Foo::B [type] en Foo Foo [] - en &Foo [type] fn bar(…) fn(&Foo) [] fn foo() fn() [] "#]], @@ -3166,9 +3189,7 @@ fn main() { st &S [type] ex core::ops::Deref::deref(&bar()) [type_could_unify] st S S [] - st &S [type] st T T [] - st &T [type] fn bar() fn() -> T [] fn &bar() [type] md core:: [] From 221bb5426049fda4c3e10a15f51399b816e26514 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 15 May 2026 10:57:16 +0300 Subject: [PATCH 025/114] Lower half-open, open and exclusive ranges in pattern types correctly --- .../crates/hir-def/src/expr_store/lower.rs | 93 ++++++++++++++++++- .../crates/hir-def/src/expr_store/pretty.rs | 1 + .../crates/hir-def/src/lang_item.rs | 14 ++- .../crates/hir-def/src/resolver.rs | 7 +- .../crates/hir-ty/src/consteval.rs | 10 +- .../crates/hir-ty/src/infer/path.rs | 25 ++++- .../crates/intern/src/symbol/symbols.rs | 3 + .../crates/test-utils/src/minicore.rs | 48 +++++++--- 8 files changed, 176 insertions(+), 25 deletions(-) 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 76503ac97c1ad..33161c503e6e1 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 @@ -35,8 +35,8 @@ use thin_vec::ThinVec; use tt::TextRange; use crate::{ - AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, ItemContainerId, - MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, + AdtId, BlockId, BlockLoc, ConstId, DefWithBodyId, FunctionId, GenericDefId, ImplId, + ItemContainerId, MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, attrs::AttrFlags, db::DefDatabase, expr_store::{ @@ -721,7 +721,7 @@ impl<'db> ExprCollector<'db> { ), ast::Type::PatternType(inner) => TypeRef::PatternType( self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn), - self.collect_pat_top(inner.pat()), + self.collect_ty_pat_opt(inner.pat()), ), ast::Type::MacroType(mt) => match mt.macro_call() { Some(mcall) => { @@ -2912,6 +2912,93 @@ impl<'db> ExprCollector<'db> { } } + fn collect_ty_pat_opt(&mut self, pat: Option) -> PatId { + match pat { + Some(pat) => self.collect_ty_pat(pat), + None => self.missing_pat(), + } + } + + fn collect_ty_pat(&mut self, pat: ast::Pat) -> PatId { + let ptr = AstPtr::new(&pat); + match pat { + ast::Pat::NotNull(_) => self.alloc_pat(Pat::NotNull, ptr), + ast::Pat::OrPat(pat) => { + let pat = pat.pats().map(|pat| self.collect_ty_pat(pat)).collect(); + self.alloc_pat(Pat::Or(pat), ptr) + } + ast::Pat::RangePat(range_pat) => { + let start = range_pat + .start() + .map(|pat| { + self.with_fresh_binding_expr_root(|this| this.lower_ty_pat_range_side(pat)) + }) + .unwrap_or_else(|| self.lower_ty_pat_range_end(self.lang_items().RangeMin)); + let end = range_pat + .end() + .map(|pat| match range_pat.op_kind() { + Some(ast::RangeOp::Inclusive) | None => self + .with_fresh_binding_expr_root(|this| this.lower_ty_pat_range_side(pat)), + Some(ast::RangeOp::Exclusive) => self.lower_excluded_range_end(pat), + }) + .unwrap_or_else(|| self.lower_ty_pat_range_end(self.lang_items().RangeMax)); + self.alloc_pat( + Pat::Range { + start: Some(start), + end: Some(end), + range_type: ast::RangeOp::Inclusive, + }, + ptr, + ) + } + ast::Pat::MacroPat(pat) => { + let Some(call) = pat.macro_call() else { return self.missing_pat() }; + let ptr = AstPtr::new(&call); + self.collect_macro_call(call, ptr, true, |this, pat| this.collect_ty_pat_opt(pat)) + } + _ => { + // FIXME: Emit an error. + self.alloc_pat(Pat::Missing, ptr) + } + } + } + + fn lower_ty_pat_range_side(&mut self, pat: ast::Pat) -> ExprId { + match &pat { + ast::Pat::LiteralPat(it) => { + let Some((literal, _)) = pat_literal_to_hir(it) else { return self.missing_expr() }; + self.alloc_expr_from_pat(Expr::Literal(literal), AstPtr::new(&pat)) + } + _ => self.missing_expr(), + } + } + + /// When a range has no end specified (`1..` or `1..=`) or no start specified (`..5` or `..=5`), + /// we instead use a constant of the MAX/MIN of the type. + /// This way the type system does not have to handle the lack of a start/end. + fn lower_ty_pat_range_end(&mut self, lang_item: Option) -> ExprId { + self.with_fresh_binding_expr_root(|this| { + this.alloc_expr_desugared( + this.lang_path(lang_item).map(Expr::Path).unwrap_or(Expr::Missing), + ) + }) + } + + /// Lowers the range end of an exclusive range (`2..5`) to an inclusive range 2..=(5 - 1). + /// This way the type system doesn't have to handle the distinction between inclusive/exclusive ranges. + fn lower_excluded_range_end(&mut self, pat: ast::Pat) -> ExprId { + self.with_fresh_binding_expr_root(|this| { + let excluded_end = this.lower_ty_pat_range_side(pat); + let range_sub_path = + this.lang_path(this.lang_items().RangeSub).map(Expr::Path).unwrap_or(Expr::Missing); + let range_sub_path = this.alloc_expr_desugared(range_sub_path); + this.alloc_expr_desugared(Expr::Call { + callee: range_sub_path, + args: Box::new([excluded_end]), + }) + }) + } + // endregion: patterns /// Returns `false` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `true` when diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 4dc7ebebbb1cb..35e3fc44c3836 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -1135,6 +1135,7 @@ impl Printer<'_> { LangItemTarget::TypeAliasId(it) => write_name!(it), LangItemTarget::TraitId(it) => write_name!(it), LangItemTarget::EnumVariantId(it) => write_name!(it), + LangItemTarget::ConstId(it) => write_name!(it), } if let Some(s) = s { 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 483c3f16c67fb..f9d5a843bd8c9 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 @@ -7,7 +7,7 @@ use intern::{Symbol, sym}; use stdx::impl_from; use crate::{ - AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, + AdtId, AssocItemId, AttrDefId, ConstId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ItemContainerId, MacroId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, attrs::AttrFlags, db::DefDatabase, @@ -25,10 +25,11 @@ pub enum LangItemTarget { TypeAliasId(TypeAliasId), TraitId(TraitId), EnumVariantId(EnumVariantId), + ConstId(ConstId), } impl_from!( - EnumId, FunctionId, ImplId, StaticId, StructId, UnionId, TypeAliasId, TraitId, EnumVariantId for LangItemTarget + EnumId, FunctionId, ImplId, StaticId, StructId, UnionId, TypeAliasId, TraitId, EnumVariantId, ConstId for LangItemTarget ); /// Salsa query. This will look for lang items in a specific crate. @@ -51,7 +52,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option lang_items.collect_lang_item(db, f), AssocItemId::TypeAliasId(t) => lang_items.collect_lang_item(db, t), - AssocItemId::ConstId(_) => (), + AssocItemId::ConstId(c) => lang_items.collect_lang_item(db, c), } } } @@ -68,7 +69,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option { lang_items.collect_lang_item(db, alias) } - AssocItemId::ConstId(_) => {} + AssocItemId::ConstId(c) => lang_items.collect_lang_item(db, c), } }); } @@ -93,6 +94,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option { lang_items.collect_lang_item(db, t); } + ModuleDefId::ConstId(c) => lang_items.collect_lang_item(db, c), _ => {} } } @@ -646,6 +648,10 @@ language_item_table! { LangItems => FieldBase, sym::field_base, TypeAliasId; FieldType, sym::field_type, TypeAliasId; + RangeMin, sym::RangeMin, ConstId; + RangeMax, sym::RangeMax, ConstId; + RangeSub, sym::RangeSub, FunctionId; + @non_lang_core_traits: core::default, Default; core::fmt, Debug; 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 8320221ffc8ca..0062e6c170785 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -194,7 +194,8 @@ impl<'db> Resolver<'db> { LangItemTarget::TraitId(it) => TypeNs::TraitId(it), LangItemTarget::FunctionId(_) | LangItemTarget::ImplId(_) - | LangItemTarget::StaticId(_) => return None, + | LangItemTarget::StaticId(_) + | LangItemTarget::ConstId(_) => return None, }; return Some(( type_ns, @@ -337,6 +338,7 @@ impl<'db> Resolver<'db> { LangItemTarget::StaticId(it) => ValueNs::StaticId(it), LangItemTarget::StructId(it) => ValueNs::StructId(it), LangItemTarget::EnumVariantId(it) => ValueNs::EnumVariantId(it), + LangItemTarget::ConstId(it) => ValueNs::ConstId(it), LangItemTarget::UnionId(_) | LangItemTarget::ImplId(_) | LangItemTarget::TypeAliasId(_) @@ -356,7 +358,8 @@ impl<'db> Resolver<'db> { LangItemTarget::TraitId(it) => TypeNs::TraitId(it), LangItemTarget::FunctionId(_) | LangItemTarget::ImplId(_) - | LangItemTarget::StaticId(_) => return None, + | LangItemTarget::StaticId(_) + | LangItemTarget::ConstId(_) => return None, }; // Remaining segments start from 0 because lang paths have no segments other than the remaining. return Some(( 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 9a6e2a87f2f8e..fb52813c52ffa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -303,6 +303,7 @@ pub(crate) enum CreateConstError<'db> { UsedForbiddenParam, ResolveToNonConst, DoesNotResolve, + ConstHasGenerics, UnderscoreExpr, TypeMismatch { #[expect(unused, reason = "will need this for diagnostics")] @@ -321,9 +322,11 @@ pub(crate) fn path_to_const<'a, 'db>( let resolution = resolver .resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) .ok_or(CreateConstError::DoesNotResolve)?; + let no_generics = |def| crate::generics::generics(db, def).has_no_params(); let konst = match resolution { - ValueNs::ConstId(id) => GeneralConstId::ConstId(id), + ValueNs::ConstId(id) if no_generics(id.into()) => GeneralConstId::ConstId(id), ValueNs::StaticId(id) => GeneralConstId::StaticId(id), + ValueNs::ConstId(_) => return Err(CreateConstError::ConstHasGenerics), ValueNs::GenericParam(param) => { let index = generics().type_or_const_param_idx(param.into()); if forbid_params_after.is_some_and(|forbid_after| index >= forbid_after) { @@ -363,7 +366,10 @@ pub(crate) fn create_anon_const<'a, 'db>( Expr::Path(path) if let konst = path_to_const(interner.db, resolver, generics, forbid_params_after, path) - && !matches!(konst, Err(CreateConstError::DoesNotResolve)) => + && !matches!( + konst, + Err(CreateConstError::DoesNotResolve | CreateConstError::ConstHasGenerics) + ) => { konst } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 704f15cc86cc6..1c3d93ae6e93c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -183,7 +183,30 @@ impl<'db> InferenceContext<'_, 'db> { match value_or_partial { ResolveValueResult::ValueNs(it) => { drop_ctx(ctx, no_diagnostics); - (it, None) + + let args = if let Path::LangItem(..) = path { + let def_and_container = match it { + ValueNs::ConstId(it) => Some((it.into(), it.loc(self.db).container)), + ValueNs::FunctionId(it) => Some((it.into(), it.loc(self.db).container)), + _ => None, + }; + let def_and_container = + def_and_container.and_then(|(def, container)| match container { + ItemContainerId::ImplId(it) => Some((def, it.into())), + ItemContainerId::TraitId(it) => Some((def, it.into())), + ItemContainerId::ExternBlockId(_) + | ItemContainerId::ModuleId(_) => None, + }); + def_and_container.map(|(def, container)| { + let args = self.infcx().fresh_args_for_item(id.into(), container); + self.write_assoc_resolution(id, def, args); + args + }) + } else { + None + }; + + (it, args) } ResolveValueResult::Partial(def, remaining_index) => { // there may be more intermediate segments between the resolved one and diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 6ad8006d18446..f9ba0582ab809 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -671,4 +671,7 @@ define_symbols! { deref_patterns, mut_ref, type_changing_struct_update, + RangeMin, + RangeMax, + RangeSub, } diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 190c59170fe8a..bd15dca609b12 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -54,12 +54,12 @@ //! iterators: iterator, fn //! manually_drop: drop //! matches: -//! non_null: -//! non_zero: transmute, option +//! non_null: pat +//! non_zero: pat, transmute, option //! option: panic //! ord: eq, option //! panic: fmt -//! pat: +//! pat: panic //! phantom_data: //! pin: //! pointee: copy, send, sync, ord, hash, unpin, phantom_data @@ -539,10 +539,10 @@ pub mod ptr { // endregion:pointee // region:non_null - #[rustc_layout_scalar_valid_range_start(1)] #[rustc_nonnull_optimization_guaranteed] + #[repr(transparent)] pub struct NonNull { - pointer: *const T, + pointer: crate::pattern_type!(*const T is !null), } // region:coerce_unsized impl @@ -2327,28 +2327,50 @@ pub mod pat { } pub const trait RangePattern { - /// Trait version of the inherent `MIN` assoc const. #[lang = "RangeMin"] const MIN: Self; - /// Trait version of the inherent `MIN` assoc const. #[lang = "RangeMax"] const MAX: Self; - /// A compile-time helper to subtract 1 for exclusive ranges. #[lang = "RangeSub"] - #[track_caller] fn sub_one(self) -> Self; } + + impl const RangePattern for u8 { + const MIN: u8 = 0; + const MAX: u8 = 0xFF; + fn sub_one(self) -> Self { + if self == Self::MIN { + panic!("exclusive range end at minimum value of type") + } else { + self - 1 + } + } + } + + // region:coerce_unsized + impl + crate::ops::CoerceUnsized for pattern_type!(*const T is !null) + where + T: crate::marker::Unsize, + { + } + // endregion:coerce_unsized + + // region:dispatch_from_dyn + impl, U> + crate::ops::DispatchFromDyn for pattern_type!(T is !null) + { + } + // endregion:dispatch_from_dyn } // endregion:pat // region:non_zero pub mod num { #[repr(transparent)] - #[rustc_layout_scalar_valid_range_start(1)] - #[rustc_nonnull_optimization_guaranteed] - pub struct NonZeroU8(u8); + pub struct NonZeroU8(crate::pattern_type!(u8 is 1..)); } // endregion:non_zero @@ -2457,6 +2479,7 @@ pub mod prelude { hash::derive::Hash, // :hash, derive iter::{FromIterator, IntoIterator, Iterator}, // :iterator macros::builtin::{derive, derive_const}, // :derive + macros::deref, // :deref_pat marker::Copy, // :copy marker::Send, // :send marker::Sized, // :sized @@ -2470,7 +2493,6 @@ pub mod prelude { panic, // :panic result::Result::{self, Err, Ok}, // :result str::FromStr, // :str - macros::deref, // :deref_pat }; } From e54bd4803bccbdb78f3d843a36327495763683fb Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 15 May 2026 10:57:28 +0300 Subject: [PATCH 026/114] Compute layout for pattern types --- .../rust-analyzer/crates/hir-ty/src/layout.rs | 169 +++++++++++++++++- .../crates/hir-ty/src/layout/tests.rs | 1 - .../rust-analyzer/crates/hir-ty/src/lower.rs | 7 +- .../crates/hir-ty/src/mir/eval.rs | 20 ++- .../hir-ty/src/next_solver/consts/valtree.rs | 29 +++ .../crates/hir-ty/src/variance.rs | 26 ++- 6 files changed, 231 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 78df93311ef40..2b0a93e61d598 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -10,12 +10,12 @@ use hir_def::{ use la_arena::{Idx, RawIdx}; use rustc_abi::{ - AddressSpace, Float, Integer, LayoutCalculator, Primitive, ReprOptions, Scalar, StructKind, - TargetDataLayout, WrappingRange, + AddressSpace, BackendRepr, FieldsShape, Float, Integer, LayoutCalculator, Niche, Primitive, + ReprOptions, Scalar, Size, StructKind, TargetDataLayout, WrappingRange, }; use rustc_index::IndexVec; use rustc_type_ir::{ - FloatTy, IntTy, UintTy, + FloatTy, IntTy, TypeVisitableExt as _, UintTy, inherent::{GenericArgs as _, IntoKind}, }; use triomphe::Arc; @@ -25,7 +25,8 @@ use crate::{ consteval::try_const_usize, db::HirDatabase, next_solver::{ - DbInterner, GenericArgs, StoredTy, Ty, TyKind, TypingMode, + Const, ConstKind, DbInterner, GenericArgs, PatternKind, StoredTy, Ty, TyKind, TypingMode, + ValueConst, infer::{DbInternerInferExt, traits::ObligationCause}, }, traits::StoredParamEnvAndCrate, @@ -348,8 +349,145 @@ pub fn layout_of_ty_query( return Err(LayoutError::NotImplemented); } - TyKind::Pat(_ty, _pat) => { - return Err(LayoutError::NotImplemented); + TyKind::Pat(ty, pat) => { + let mut layout = (*db.layout_of_ty(ty.store(), trait_env.clone())?).clone(); + match pat.kind() { + PatternKind::Range { start, end } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + scalar.valid_range_mut().start = extract_const_value(start)? + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?; + + scalar.valid_range_mut().end = extract_const_value(end)? + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?; + + // FIXME(pattern_types): create implied bounds from pattern types in signatures + // that require that the range end is >= the range start so that we can't hit + // this error anymore without first having hit a trait solver error. + // Very fuzzy on the details here, but pattern types are an internal impl detail, + // so we can just go with this for now + if scalar.is_signed() { + let range = scalar.valid_range_mut(); + let start = layout.size.sign_extend(range.start); + let end = layout.size.sign_extend(range.end); + if end < start { + return Err(LayoutError::HasErrorType); + } + } else { + let range = scalar.valid_range_mut(); + if range.end < range.start { + return Err(LayoutError::HasErrorType); + } + }; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!("pattern type with range but not scalar layout: {ty:?}, {layout:?}") + } + } + PatternKind::NotNull => { + if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = + &mut layout.backend_repr + { + scalar.valid_range_mut().start = 1; + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!( + "pattern type with `!null` pattern but not scalar/pair layout: {ty:?}, {layout:?}" + ) + } + } + + PatternKind::Or(variants) => match variants[0].kind() { + PatternKind::Range { .. } => { + if let BackendRepr::Scalar(scalar) = &mut layout.backend_repr { + let variants: Result, _> = variants + .iter() + .map(|pat| match pat.kind() { + PatternKind::Range { start, end } => Ok::<_, LayoutError>(( + extract_const_value(start) + .unwrap() + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?, + extract_const_value(end) + .unwrap() + .try_to_bits(db, trait_env.as_ref()) + .ok_or(LayoutError::Unknown)?, + )), + PatternKind::NotNull | PatternKind::Or(_) => { + unreachable!("mixed or patterns are not allowed") + } + }) + .collect(); + let mut variants = variants?; + if !scalar.is_signed() { + return Err(LayoutError::HasErrorType); + } + variants.sort(); + if variants.len() != 2 { + return Err(LayoutError::HasErrorType); + } + + // first is the one starting at the signed in range min + let mut first = variants[0]; + let mut second = variants[1]; + if second.0 + == layout.size.truncate(layout.size.signed_int_min() as u128) + { + (second, first) = (first, second); + } + + if layout.size.sign_extend(first.1) >= layout.size.sign_extend(second.0) + { + return Err(LayoutError::HasErrorType); + } + if layout.size.signed_int_max() as u128 != second.1 { + return Err(LayoutError::HasErrorType); + } + + // Now generate a wrapping range (which aren't allowed in surface syntax). + scalar.valid_range_mut().start = second.0; + scalar.valid_range_mut().end = first.1; + + let niche = Niche { + offset: Size::ZERO, + value: scalar.primitive(), + valid_range: scalar.valid_range(target), + }; + + layout.largest_niche = Some(niche); + } else { + panic!( + "pattern type with range but not scalar layout: {ty:?}, {layout:?}" + ) + } + } + PatternKind::NotNull => panic!("or patterns can't contain `!null` patterns"), + PatternKind::Or(..) => panic!("patterns cannot have nested or patterns"), + }, + } + // Pattern types contain their base as their sole field. + // This allows the rest of the compiler to process pattern types just like + // single field transparent Adts, and only the parts of the compiler that + // specifically care about pattern types will have to handle it. + layout.fields = FieldsShape::Arbitrary { + offsets: [Size::ZERO].into_iter().collect(), + in_memory_order: [RustcFieldIdx::new(0)].into_iter().collect(), + }; + layout } TyKind::UnsafeBinder(_) => { return Err(LayoutError::NotImplemented); @@ -376,6 +514,25 @@ pub(crate) fn layout_of_ty_cycle_result( Err(LayoutError::RecursiveTypeWithoutIndirection) } +fn extract_const_value<'db>(ct: Const<'db>) -> Result, LayoutError> { + match ct.kind() { + ConstKind::Value(cv) => Ok(cv), + ConstKind::Param(_) + | ConstKind::Expr(_) + | ConstKind::Unevaluated(_) + | ConstKind::Infer(_) + | ConstKind::Bound(..) + | ConstKind::Placeholder(_) => { + if ct.has_param() { + Err(LayoutError::HasPlaceholder) + } else { + Err(LayoutError::Unknown) + } + } + ConstKind::Error(_) => Err(LayoutError::HasErrorConst), + } +} + fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> { match pointee.kind() { TyKind::Adt(def, args) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index bc18f05790f02..482945f5f0aaa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -529,7 +529,6 @@ fn tuple_ptr_with_dst_tail() { } #[test] -#[ignore = "FIXME: We need to have proper pattern types"] fn non_zero_and_non_null() { size_and_align! { minicore: non_zero, non_null, option; 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 64f3cbe06a954..8beaa481b5280 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -542,7 +542,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { &TypeRef::PatternType(ty, pat) => { let ty = self.lower_ty(ty); let Some(pat) = self.lower_pattern_type(pat, ty) else { - // FIXME: Report an error. return (self.types.types.error, res); }; Ty::new_pat(self.interner, ty, pat) @@ -568,7 +567,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ) .ok()?, ), - _ => return None, + hir_def::hir::Pat::Missing => return None, + _ => { + never!("pattern type can only be Range, NotNull or Or"); + return None; + } }; Some(Pattern::new(self.interner, pat_kind)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index e080d3d71334d..a8879521eba67 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -1709,15 +1709,19 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { if let Some(it) = goal(kind) { return Ok(it); } - if let TyKind::Adt(adt_ef, subst) = kind - && let AdtId::StructId(struct_id) = adt_ef.def_id() - { - let field_types = self.db.field_types(struct_id.into()); - if let Some(ty) = - field_types.iter().last().map(|it| it.1.get().instantiate(self.interner(), subst)) - { - return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); + match kind { + TyKind::Adt(adt_ef, subst) if let AdtId::StructId(struct_id) = adt_ef.def_id() => { + let field_types = self.db.field_types(struct_id.into()); + if let Some(ty) = field_types + .iter() + .last() + .map(|it| it.1.get().instantiate(self.interner(), subst)) + { + return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal); + } } + TyKind::Pat(ty, _) => return self.coerce_unsized_look_through_fields(ty, goal), + _ => (), } Err(MirEvalError::CoerceUnsizedError(ty.store())) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs index b856ee5a85a76..bef2386706814 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts/valtree.rs @@ -8,6 +8,7 @@ use stdx::never; use crate::{ MemoryMap, ParamEnvAndCrate, consteval, + db::HirDatabase, mir::pad16, next_solver::{Const, Consts, TyKind, WorldExposer}, }; @@ -32,6 +33,34 @@ impl<'db> ValueConst<'db> { let value = ValTree::new(kind); ValueConst { ty, value } } + + /// Attempts to convert to a `ValTreeKind::Leaf` value. + pub fn try_to_leaf(self) -> Option { + match self.value.inner() { + ValTreeKind::Leaf(s) => Some(*s), + ValTreeKind::Branch(_) => None, + } + } + + /// Attempts to extract the raw bits from the constant. + /// + /// Fails if the value can't be represented as bits (e.g. because it is a reference + /// or an aggregate). + #[inline] + pub fn try_to_bits( + self, + db: &'db dyn HirDatabase, + param_env: ParamEnvAndCrate<'db>, + ) -> Option { + let (TyKind::Bool | TyKind::Char | TyKind::Uint(_) | TyKind::Int(_) | TyKind::Float(_)) = + self.ty.kind() + else { + return None; + }; + let scalar = self.try_to_leaf()?; + let size = db.layout_of_ty(self.ty.store(), param_env.store()).ok()?.size; + Some(scalar.to_bits(size)) + } } pub(super) fn allocation_to_const<'db>( 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 39d51dcee033b..49dacc16eb2c4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -25,8 +25,8 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, next_solver::{ - Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Region, - RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, + Const, ConstKind, DbInterner, ExistentialPredicate, GenericArgKind, GenericArgs, Pattern, + PatternKind, Region, RegionKind, StoredVariancesOf, TermKind, Ty, TyKind, VariancesOf, }, }; @@ -249,17 +249,35 @@ impl<'db> Context<'db> { // we encounter this when walking the trait references for object // types, where we use Error as the Self type } + TyKind::Pat(typ, pat) => { + self.add_constraints_from_pat(pat); + self.add_constraints_from_ty(typ, variance); + } TyKind::Bound(..) => {} TyKind::CoroutineWitness(..) | TyKind::Placeholder(..) | TyKind::Infer(..) - | TyKind::UnsafeBinder(..) - | TyKind::Pat(..) => { + | TyKind::UnsafeBinder(..) => { never!("unexpected type encountered in variance inference: {:?}", ty) } } } + fn add_constraints_from_pat(&mut self, pat: Pattern<'db>) { + match pat.kind() { + PatternKind::Range { start, end } => { + self.add_constraints_from_const(start); + self.add_constraints_from_const(end); + } + PatternKind::NotNull => {} + PatternKind::Or(patterns) => { + for pat in patterns { + self.add_constraints_from_pat(pat) + } + } + } + } + fn add_constraints_from_invariant_args(&mut self, args: GenericArgs<'db>) { for k in args.iter() { match k.kind() { From e240d8e5aad1a1f58c81d1ef7f05a738f724cf63 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 15 May 2026 16:27:27 +0800 Subject: [PATCH 027/114] fix: no use sad pattern on happy arm with guard For replace_if_let_with_match assist Example --- ```rust fn foo(x: Option) { $0if let Some(x) = x && x != 4 { println!("{}", x) } else { println!("none") } } ``` **Before this PR** ```rust fn foo(x: Option) { match x { Some(x) if x != 4 => println!("{}", x), None => println!("none"), } } ``` **After this PR** ```rust fn foo(x: Option) { match x { Some(x) if x != 4 => println!("{}", x), _ => println!("none"), } } ``` --- .../src/handlers/replace_if_let_with_match.rs | 52 ++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index aa3f917f124df..ff2d0544b2a18 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -171,7 +171,7 @@ fn make_else_arm( let (pattern, expr) = if let Some(else_expr) = else_expr { let pattern = match conditionals { [(None, Some(_), _)] => make.literal_pat("false").into(), - [(Some(pat), _, _)] => match ctx + [(Some(pat), None, _)] => match ctx .sema .type_of_pat(pat) .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty.adjusted())) @@ -955,6 +955,31 @@ fn foo(x: Option) { ); } + #[test] + fn special_case_option_with_guard() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: option +fn foo(x: Option) { + $0if let Some(x) = x && x != 4 { + println!("{}", x) + } else { + println!("none") + } +} +"#, + r#" +fn foo(x: Option) { + match x { + Some(x) if x != 4 => println!("{}", x), + _ => println!("none"), + } +} +"#, + ); + } + #[test] fn special_case_option_ref() { check_assist( @@ -1005,6 +1030,31 @@ fn foo(x: Option) { ); } + #[test] + fn special_case_inverted_option_with_guard() { + check_assist( + replace_if_let_with_match, + r#" +//- minicore: option +fn foo(x: Option) { + $0if let None = x && other_cond { + println!("none") + } else { + println!("some") + } +} +"#, + r#" +fn foo(x: Option) { + match x { + None if other_cond => println!("none"), + _ => println!("some"), + } +} +"#, + ); + } + #[test] fn special_case_result() { check_assist( From df37dde1b4c9bbb01baf0e35bf056d11eb08285d Mon Sep 17 00:00:00 2001 From: Onyeka Obi Date: Fri, 15 May 2026 05:07:47 -0700 Subject: [PATCH 028/114] method-resolution: emit error for method calls with illegal Sized bound Resolves a FIXME at hir-ty/src/method_resolution.rs:151 in lookup_method_including_private that previously allowed calls like `x.cant_call()` on a `&dyn Foo` receiver to type-check without diagnostic when `cant_call(&self) where Self: Sized`, even though the `Self: Sized` predicate cannot be satisfied by the trait object. The existing predicates_require_illegal_sized_bound check in confirm.rs already correctly identifies this case (it iterates elaborated predicates and looks for `Self: Sized` clauses with a `TyKind::Dynamic` self type). Adds an InferenceDiagnostic::MethodCallIllegalSizedBound variant, plumbs the cooked diagnostic through hir, and renders it as RustcHardError E0277. Tests: trait-object call with Sized-bounded method errors; trait-object call to dyn-safe method does not; concrete-type call to a Sized-bounded method does not. Part of rust-lang/rust-analyzer#22140. Signed-off-by: Onyeka Obi --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 + .../crates/hir-ty/src/method_resolution.rs | 4 +- .../crates/hir/src/diagnostics.rs | 9 +++ .../method_call_illegal_sized_bound.rs | 78 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs 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 6aa647694e108..3f90a78cc24fd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -438,6 +438,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] def: GenericDefId, }, + MethodCallIllegalSizedBound { + #[type_visitable(ignore)] + call_expr: ExprId, + }, MethodCallIncorrectGenericsOrder { #[type_visitable(ignore)] expr: ExprId, 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 5e90e371fcd9c..9e0188fd2604e 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 @@ -35,7 +35,7 @@ use stdx::impl_from; use triomphe::Arc; use crate::{ - Span, all_super_traits, + InferenceDiagnostic, Span, all_super_traits, db::HirDatabase, infer::{InferenceContext, unify::InferenceTable}, lower::GenericPredicates, @@ -148,7 +148,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { debug!("result = {:?}", result); if result.illegal_sized_bound { - // FIXME: Report an error. + self.push_diagnostic(InferenceDiagnostic::MethodCallIllegalSizedBound { call_expr }); } self.write_expr_adj(receiver, result.adjustments); diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 9e4b929dd5fed..7e4853539d81b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -120,6 +120,7 @@ diagnostics![AnyDiagnostic<'db> -> MacroError, MacroExpansionParseError, MalformedDerive, + MethodCallIllegalSizedBound, MismatchedArgCount, MismatchedTupleStructPatArgCount, MissingFields, @@ -595,6 +596,11 @@ pub struct InvalidLhsOfAssignment { pub lhs: InFile>>, } +#[derive(Debug)] +pub struct MethodCallIllegalSizedBound { + pub call_expr: InFile, +} + #[derive(Debug)] pub struct PatternArgInExternFn { pub node: InFile>, @@ -984,6 +990,9 @@ impl<'db> AnyDiagnostic<'db> { let lhs = expr_syntax(lhs)?; InvalidLhsOfAssignment { lhs }.into() } + &InferenceDiagnostic::MethodCallIllegalSizedBound { call_expr } => { + MethodCallIllegalSizedBound { call_expr: expr_syntax(call_expr)? }.into() + } &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => { let at_point = span_syntax(at_point)?; let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs new file mode 100644 index 0000000000000..0a0ac8627c3d1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/method_call_illegal_sized_bound.rs @@ -0,0 +1,78 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: method-call-illegal-sized-bound +// +// This diagnostic is triggered when a method is called on a trait-object +// receiver but the method's predicates require `Self: Sized`, which the +// trait object cannot satisfy. +pub(crate) fn method_call_illegal_sized_bound( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MethodCallIllegalSizedBound, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0277"), + "the method cannot be invoked on a trait object because its `Self: Sized` bound is not satisfied", + d.call_expr.map(|it| it.into()), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn sized_bound_method_on_trait_object_errors() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn cant_call(&self) where Self: Sized; +} + +fn f(x: &dyn Foo) { + x.cant_call(); + //^^^^^^^^^^^^^ error: the method cannot be invoked on a trait object because its `Self: Sized` bound is not satisfied +} +"#, + ); + } + + #[test] + fn method_without_sized_bound_on_trait_object_does_not_error() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn dyn_safe(&self); +} + +fn f(x: &dyn Foo) { + x.dyn_safe(); +} +"#, + ); + } + + #[test] + fn sized_bound_method_on_concrete_type_does_not_error() { + check_diagnostics( + r#" +//- minicore: sized +trait Foo { + fn cant_dispatch(&self) where Self: Sized; +} + +struct S; +impl Foo for S { + fn cant_dispatch(&self) {} +} + +fn f(s: &S) { + s.cant_dispatch(); +} +"#, + ); + } +} 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 0809054a2e3aa..0880002cba8d5 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -50,6 +50,7 @@ mod handlers { pub(crate) mod invalid_range_pat_type; pub(crate) mod macro_error; pub(crate) mod malformed_derive; + pub(crate) mod method_call_illegal_sized_bound; pub(crate) mod mismatched_arg_count; pub(crate) mod mismatched_array_pat_len; pub(crate) mod missing_fields; @@ -454,6 +455,7 @@ pub fn semantic_diagnostics( continue; }, AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d), + AnyDiagnostic::MethodCallIllegalSizedBound(d) => handlers::method_call_illegal_sized_bound::method_call_illegal_sized_bound(&ctx, &d), AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d), AnyDiagnostic::MismatchedArrayPatLen(d) => handlers::mismatched_array_pat_len::mismatched_array_pat_len(&ctx, &d), AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), From cb0bb009f87d6b0eb7ccfc677f8b6682c74bfd57 Mon Sep 17 00:00:00 2001 From: Lukas Matta Date: Fri, 15 May 2026 19:28:14 +0200 Subject: [PATCH 029/114] Migrate inline_call assist to SyntaxFactory Part of rust-lang/rust-analyzer#18285. --- .../ide-assists/src/handlers/inline_call.rs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index af048c6ae0415..8897e59355e45 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -7,7 +7,7 @@ use hir::{ sym, }; use ide_db::{ - EditionedFileId, FileId, FxHashMap, RootDatabase, + EditionedFileId, FxHashMap, RootDatabase, base_db::Crate, defs::Definition, imports::insert_use::remove_use_tree_if_simple, @@ -21,7 +21,7 @@ use syntax::{ ast::{ self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::{AstNodeEdit, IndentLevel}, - make, + syntax_factory::SyntaxFactory, }, syntax_editor::SyntaxEditor, }; @@ -79,7 +79,11 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> let function = ctx.sema.to_def(&ast_func)?; - let params = get_fn_params(ctx.sema.db, function, ¶m_list)?; + let def_file_editor = SyntaxEditor::new(ast_func.syntax().ancestors().last().unwrap()).0; + let params = get_fn_params(ctx.sema.db, function, ¶m_list, def_file_editor.make())?; + + let mut file_editors = FxHashMap::default(); + file_editors.insert(vfs_def_file, def_file_editor); let usages = Definition::Function(function).usages(&ctx.sema); if !usages.at_least_one() { @@ -106,7 +110,6 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> let mut usages = usages.all(); let current_file_usage = usages.references.remove(&def_file); - let mut file_editors: FxHashMap = FxHashMap::default(); let mut remove_def = true; let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec| { let file_id = file_id.file_id(ctx.db()); @@ -170,10 +173,10 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_> None => builder.edit_file(vfs_def_file), } if remove_def { - let editor = file_editors + file_editors .entry(vfs_def_file) - .or_insert_with(|| builder.make_editor(ast_func.syntax())); - editor.delete(ast_func.syntax()); + .or_insert_with(|| builder.make_editor(ast_func.syntax())) + .delete(ast_func.syntax()); } for (file_id, editor) in file_editors { builder.add_file_edits(file_id, editor); @@ -251,7 +254,9 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt cov_mark::hit!(inline_call_recursive); return None; } - let params = get_fn_params(ctx.sema.db, function, ¶m_list)?; + let syntax = call_info.node.syntax().clone(); + let editor = SyntaxEditor::new(syntax.ancestors().last().unwrap()).0; + let params = get_fn_params(ctx.sema.db, function, ¶m_list, editor.make())?; if call_info.arguments.len() != params.len() { // Can't inline the function because they've passed the wrong number of @@ -260,9 +265,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Opt return None; } - let syntax = call_info.node.syntax().clone(); acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| { - let editor = builder.make_editor(call_info.node.syntax()); let replacement = inline(&ctx.sema, file_id, function, &fn_body, ¶ms, &call_info, &editor); editor.replace(call_info.node.syntax(), replacement.syntax()); @@ -311,6 +314,7 @@ fn get_fn_params<'db>( db: &'db dyn HirDatabase, function: hir::Function, param_list: &ast::ParamList, + make: &SyntaxFactory, ) -> Option, hir::Param<'db>)>> { let mut assoc_fn_params = function.assoc_fn_params(db).into_iter(); @@ -318,10 +322,10 @@ fn get_fn_params<'db>( if let Some(self_param) = param_list.self_param() { // Keep `ref` and `mut` and transform them into `&` and `mut` later params.push(( - make::ident_pat( + make.ident_pat( self_param.amp_token().is_some(), self_param.mut_token().is_some(), - make::name("this"), + make.name("this"), ) .into(), None, From 1b81d40f8cef9aea7b4112341085840963491cf1 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 16 May 2026 11:11:56 +0800 Subject: [PATCH 030/114] fix: not complete same name inherent deref methods Example --- ```rust fn test(a: A) { a.$0 } impl core::ops::Deref for A { type Target = B; fn deref(&self) -> &Self::Target { loop {} } } trait Foo { fn foo(&self) -> u32 {} } impl Foo for A {} impl Foo for B {} impl A { fn foo(&self) -> u8 {} } impl B { fn foo(&self) -> u16 {} } struct A {} struct B {} ``` **Before this PR** ```text me foo() fn(&self) -> u8 me foo() fn(&self) -> u16 me foo() (as Foo) fn(&self) -> u32 ``` **After this PR** ```text me foo() fn(&self) -> u8 me foo() (as Foo) fn(&self) -> u32 ``` --- .../ide-completion/src/completions/dot.rs | 88 +++++++++++++++++-- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index c64bdf6bc5e15..cc6df718ce0a5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1,9 +1,9 @@ //! Completes references after dot (fields and method calls). -use std::ops::ControlFlow; +use std::{collections::hash_map, ops::ControlFlow}; -use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback}; -use ide_db::FxHashSet; +use hir::{Complete, Function, HasContainer, ItemContainer, MethodCandidateCallback, Name}; +use ide_db::{FxHashMap, FxHashSet}; use itertools::Either; use syntax::SmolStr; @@ -239,6 +239,9 @@ fn complete_methods( // duplicated, trait methods can. And it is still useful to show all of them (even when there // is also an inherent method, especially considering that it may be private, and filtered later). seen_methods: FxHashSet, + // However, duplicate inherent methods is usually meaningless + // https://github.com/rust-lang/rust-analyzer/issues/20773#issuecomment-4302781553 + seen_inherent_methods: FxHashMap, } impl MethodCandidateCallback for Callback<'_, '_, F> @@ -249,7 +252,21 @@ fn complete_methods( // `where` clauses or `dyn Trait`. fn on_inherent_method(&mut self, func: hir::Function) -> ControlFlow<()> { if func.self_param(self.ctx.db).is_some() && self.seen_methods.insert(func) { - (self.f)(func); + let same_name = self.seen_inherent_methods.entry(func.name(self.ctx.db)); + let do_complete = match &same_name { + hash_map::Entry::Vacant(_) => true, + hash_map::Entry::Occupied(same_func) => { + match self.ctx.is_visible(same_func.get()) { + crate::context::Visible::Yes => false, + crate::context::Visible::Editable => true, + crate::context::Visible::No => true, + } + } + }; + same_name.insert_entry(func); + if do_complete { + (self.f)(func); + } } ControlFlow::Continue(()) } @@ -277,7 +294,12 @@ fn complete_methods( &ctx.scope, traits_in_scope, None, - Callback { ctx, f, seen_methods: FxHashSet::default() }, + Callback { + ctx, + f, + seen_methods: FxHashSet::default(), + seen_inherent_methods: FxHashMap::default(), + }, ); } @@ -869,6 +891,62 @@ fn test(a: A) { ); } + #[test] + fn test_inherent_method_no_same_name() { + check_no_kw( + r#" +//- minicore: deref +struct A {} +struct B {} +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +trait Foo { fn foo(&self) -> u32 {} } +impl Foo for A {} +impl Foo for B {} +impl A { fn foo(&self) -> u8 {} } +impl B { fn foo(&self) -> u16 {} } +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + me deref() (use core::ops::Deref) fn(&self) -> &::Target + me foo() fn(&self) -> u8 + me foo() (as Foo) fn(&self) -> u32 + "#]], + ); + + check_no_kw( + r#" +//- minicore: deref +//- /dep.rs crate:dep +pub struct A {} +pub struct B {} +impl core::ops::Deref for A { + type Target = B; + fn deref(&self) -> &Self::Target { loop {} } +} +pub trait Foo { fn foo(&self) -> u32 {} } +impl Foo for A {} +impl Foo for B {} +impl A { fn foo(&self) -> u8 {} } +impl B { pub fn foo(&self) -> u16 {} } +//- /main.rs crate:main deps:dep +use dep::*; +fn test(a: A) { + a.$0 +} +"#, + expect![[r#" + me deref() (use core::ops::Deref) fn(&self) -> &::Target + me foo() fn(&self) -> u16 + me foo() (as Foo) fn(&self) -> u32 + "#]], + ); + } + #[test] fn test_completion_works_in_consts() { check_no_kw( From 3819dbf77c5904bc5405aeb020c9ed8de3823854 Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Sun, 17 May 2026 00:07:10 +0800 Subject: [PATCH 031/114] feat: add diagnostic for E0614 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 5 ++ .../crates/hir-ty/src/infer/expr.rs | 5 +- .../crates/hir-ty/src/infer/unify.rs | 3 +- .../crates/hir/src/diagnostics.rs | 11 +++ .../src/handlers/cannot_be_dereferenced.rs | 74 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 6 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs 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 3f90a78cc24fd..51e9435c804d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -396,6 +396,11 @@ pub enum InferenceDiagnostic { call_expr: ExprId, found: StoredTy, }, + CannotBeDereferenced { + #[type_visitable(ignore)] + expr: ExprId, + found: StoredTy, + }, TypedHole { #[type_visitable(ignore)] expr: ExprId, 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 90ecfbd7e93d0..3cff0ea89693a 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 @@ -1295,7 +1295,10 @@ impl<'db> InferenceContext<'_, 'db> { if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { oprnd_t = ty; } else { - // FIXME: Report an error. + self.push_diagnostic(InferenceDiagnostic::CannotBeDereferenced { + expr, + found: oprnd_t.store(), + }); oprnd_t = self.types.types.error; } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index f9ad76b0c12c0..6500eb213c209 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -577,7 +577,8 @@ pub(super) mod resolve_completely { diagnostics.retain_mut(|diagnostic| { self.resolve_completely(diagnostic); - if let InferenceDiagnostic::ExpectedFunction { found: ty, .. } + if let InferenceDiagnostic::CannotBeDereferenced { found: ty, .. } + | InferenceDiagnostic::ExpectedFunction { found: ty, .. } | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 7e4853539d81b..7c128d6bce832 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -102,6 +102,7 @@ macro_rules! diagnostics { diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, BreakOutsideOfLoop, + CannotBeDereferenced<'db>, CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, @@ -317,6 +318,12 @@ pub struct ExpectedFunction<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct CannotBeDereferenced<'db> { + pub expr: InFile, + pub found: Type<'db>, +} + #[derive(Debug)] pub struct FunctionalRecordUpdateOnNonStruct { pub base_expr: InFile, @@ -923,6 +930,10 @@ impl<'db> AnyDiagnostic<'db> { let cast_ty = Type::new(db, def, cast_ty.as_ref()); InvalidCast { expr, error: *error, expr_ty, cast_ty }.into() } + InferenceDiagnostic::CannotBeDereferenced { expr, found } => { + let expr = expr_syntax(*expr)?; + CannotBeDereferenced { expr, found: Type::new(db, def, found.as_ref()) }.into() + } InferenceDiagnostic::TyDiagnostic { source, diag } => { let source_map = match source { InferenceTyDiagnosticSource::Body => source_map, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs new file mode 100644 index 0000000000000..6c9726f8af2ee --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_be_dereferenced.rs @@ -0,0 +1,74 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-be-dereferenced +// +// This diagnostic is triggered if the operand of a dereference expression +// cannot be dereferenced. +pub(crate) fn cannot_be_dereferenced( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotBeDereferenced<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0614"), + format!( + "type `{}` cannot be dereferenced", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn cannot_be_dereferenced() { + check_diagnostics( + r#" +fn f() { + let x = 1i32; + let _ = *x; + //^^ error: type `i32` cannot be dereferenced +} +"#, + ); + } + + #[test] + fn allows_reference_deref() { + check_diagnostics( + r#" +fn f(x: &i32) { + let _ = *x; +} +"#, + ); + } + + #[test] + fn allows_overloaded_deref() { + check_diagnostics( + r#" +//- minicore: deref +struct Wrapper(i32); + +impl core::ops::Deref for Wrapper { + type Target = i32; + + fn deref(&self) -> &i32 { + &self.0 + } +} + +fn f(x: Wrapper) { + let _ = *x; +} +"#, + ); + } +} 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 0880002cba8d5..459728592f6b1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -32,6 +32,7 @@ mod handlers { pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; + pub(crate) mod cannot_be_dereferenced; pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_array_or_slice_pat; @@ -429,6 +430,7 @@ pub fn semantic_diagnostics( for diag in diags { let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), + AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), From 459d1eddd3e3d7d4066e61d92164d3a1b84cef0e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Sat, 16 May 2026 13:19:53 +0200 Subject: [PATCH 032/114] Revert "decide not to resolve a FIXME" This reverts commit 3efe801fe27ae68eff3cfe9cf84fb52b7e427ae3. --- .../rust-analyzer/crates/hir-def/src/expr_store/lower.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 76503ac97c1ad..df8b4cd93533a 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 @@ -1894,9 +1894,7 @@ impl<'db> ExprCollector<'db> { }; let record_field_list = e.record_expr_field_list()?; let ellipsis = record_field_list.dotdot_token().is_some(); - // We wanted to emit an error here if `record_field_list.spread().is_some()`, - // but that's already a syntax error in rustc, so we decided not to. - // See https://github.com/rust-lang/rust-analyzer/pull/22206#discussion_r3156097370 + // FIXME: Report an error here if `record_field_list.spread().is_some()`. let args = record_field_list .fields() .filter_map(|f| { From c53984de36584cc644ac7ba3c71b5f309108b449 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Tue, 28 Apr 2026 16:05:29 +0200 Subject: [PATCH 033/114] feat(ide-diagnostics): add handler for functional record update in destructuring assignment --- .../crates/hir-def/src/expr_store.rs | 1 + .../crates/hir-def/src/expr_store/lower.rs | 8 ++- .../crates/hir/src/diagnostics.rs | 6 ++ src/tools/rust-analyzer/crates/hir/src/lib.rs | 3 + .../fru_in_destructuring_assignment.rs | 67 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 6 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.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 6d6e369cd6858..fa33a00a80e12 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 @@ -332,6 +332,7 @@ pub enum ExpressionStoreDiagnostics { AwaitOutsideOfAsync { node: InFile>, location: String }, UndeclaredLabel { node: InFile>, name: Name }, PatternArgInExternFn { node: InFile> }, + FruInDestructuringAssignment { node: InFile> }, } impl ExpressionStoreBuilder { 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 df8b4cd93533a..fd884c38b7a9e 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 @@ -1894,7 +1894,13 @@ impl<'db> ExprCollector<'db> { }; let record_field_list = e.record_expr_field_list()?; let ellipsis = record_field_list.dotdot_token().is_some(); - // FIXME: Report an error here if `record_field_list.spread().is_some()`. + if let Some(spread) = record_field_list.spread() { + self.store.diagnostics.push( + ExpressionStoreDiagnostics::FruInDestructuringAssignment { + node: self.expander.in_file(AstPtr::new(&spread)), + }, + ); + } let args = record_field_list .fields() .filter_map(|f| { diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 7c128d6bce832..f4a29a4bcc08b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -106,6 +106,7 @@ diagnostics![AnyDiagnostic<'db> -> CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, + FruInDestructuringAssignment, FunctionalRecordUpdateOnNonStruct, GenericDefaultRefersToSelf, InactiveCode, @@ -324,6 +325,11 @@ pub struct CannotBeDereferenced<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct FruInDestructuringAssignment { + pub node: InFile>, +} + #[derive(Debug)] pub struct FunctionalRecordUpdateOnNonStruct { pub base_expr: InFile, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index b31fac3cd21ce..df7743fdba354 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2424,6 +2424,9 @@ fn expr_store_diagnostics<'db>( ExpressionStoreDiagnostics::PatternArgInExternFn { node } => { PatternArgInExternFn { node: *node }.into() } + ExpressionStoreDiagnostics::FruInDestructuringAssignment { node } => { + FruInDestructuringAssignment { node: *node }.into() + } }); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs new file mode 100644 index 0000000000000..f8d3d80d62f6b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs @@ -0,0 +1,67 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: fru-in-destructuring-assignment +// +// This diagnostic is triggered when a destructuring assignment contains functional record update +pub(crate) fn fru_in_destructuring_assignment( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::FruInDestructuringAssignment, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::SyntaxError, + "functional record updates are not allowed in destructuring assignments", + d.node.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_diagnostics_with_disabled}; + + #[test] + fn spread_variable() { + check_diagnostics_with_disabled( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo, g: Foo, mut bar: u32, mut baz: u32) { + Foo { ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments + Foo { bar, ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments + Foo { bar, baz, ..g } = f; + // ^ error: functional record updates are not allowed in destructuring assignments +} + "#, + // We don't end up using neither `bar` nor `baz` + &["unused_variables"], + ); + } + + #[test] + fn spread_default() { + check_diagnostics( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo) { + Foo { ..Default::default() } = f; + // ^^^^^^^^^^^^^^^^^^ error: functional record updates are not allowed in destructuring assignments +} + "#, + ); + } + + #[test] + fn spread_struct() { + check_diagnostics( + r#" +struct Foo { bar: u32, baz: u32 } +fn test(f: Foo) { + Foo { ..Foo { bar: 0, baz: 0 } } = f; + // ^^^^^^^^^^^^^^^^^^^^^^ error: functional record updates are not allowed in destructuring assignments +} + "#, + ); + } +} 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 459728592f6b1..d38780ede2343 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -37,6 +37,7 @@ mod handlers { pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_array_or_slice_pat; pub(crate) mod expected_function; + pub(crate) mod fru_in_destructuring_assignment; pub(crate) mod functional_record_update_on_non_struct; pub(crate) mod generic_args_prohibited; pub(crate) mod generic_default_refers_to_self; @@ -533,6 +534,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d), AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d), + AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d), }; res.push(d) } From 61d77ca59bc32b0be4571959753b67254566761b Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 16 May 2026 22:00:54 +0300 Subject: [PATCH 034/114] Show `unsafe` in the signature help if applicable --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 7 +++ .../crates/ide/src/signature_help.rs | 57 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index df7743fdba354..9f94243062b38 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2760,6 +2760,13 @@ impl Function { } } + pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { + match self.id { + AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_unsafe(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => false, + } + } + pub fn is_varargs(self, db: &dyn HirDatabase) -> bool { match self.id { AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_varargs(), diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index dbecbafdd7c03..7854a14187b3f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -180,6 +180,9 @@ fn signature_help_for_call( if func.is_async(db) { format_to!(res.signature, "async "); } + if func.is_unsafe(db) { + format_to!(res.signature, "unsafe "); + } format_to!(res.signature, "fn {}", func.name(db).display(db, edition)); let generic_params = GenericDef::Function(func) @@ -2685,4 +2688,58 @@ fn main() { "#]], ); } + + #[test] + fn test_unsafe_function() { + check( + r#" +//- minicore: sized, fn +pub unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } + + #[test] + fn test_const_unsafe_function() { + check( + r#" +//- minicore: sized, fn +pub const unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + const unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } + + #[test] + fn test_async_unsafe_function() { + check( + r#" +//- minicore: sized, fn, future +pub async unsafe fn foo(x: u32, y: u32) -> u32 { x + y } + +fn main() { + unsafe { foo($0) } +} + "#, + expect![[r#" + async unsafe fn foo(x: u32, y: u32) -> u32 + ^^^^^^ ------ + "#]], + ); + } } From cf94374e58dff90f4531cbdfbacd60d0b7301859 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 30 Apr 2026 11:39:10 +0200 Subject: [PATCH 035/114] misc improvements --- .../rust-analyzer/crates/hir-expand/src/db.rs | 2 +- .../src/handlers/no_such_field.rs | 15 +++++----- .../src/handlers/unresolved_method.rs | 30 +++++++------------ 3 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index b09f69a295915..878fae88ad65d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -396,7 +396,7 @@ pub(crate) fn parse_with_map( /// This resolves the [MacroCallId] to check if it is a derive macro if so get the [macro_arg] for the derive. /// Other wise return the [macro_arg] for the macro_call_id. /// -/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is +/// This is not connected to the database so it does not cache the result. However, the inner [macro_arg] query is #[allow(deprecated)] // we are macro_arg_considering_derives fn macro_arg_considering_derives<'db>( db: &'db dyn ExpandDatabase, 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 7959fddc757f4..3dd6744b05bdb 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 @@ -116,14 +116,13 @@ fn missing_record_expr_field_fixes( let mut new_field = new_field.to_string(); // FIXME: check submodule instead of FileId - if usage_file_id != def_file_id && !matches!(def_id, hir::Variant::EnumVariant(_)) { - new_field = format!("pub(crate) {new_field}"); - } - new_field = format!("\n{indent}{new_field}{postfix}"); - - if needs_comma { - new_field = format!(",{new_field}"); - } + let vis = if usage_file_id != def_file_id && !matches!(def_id, hir::Variant::EnumVariant(_)) { + "pub(crate) " + } else { + "" + }; + let comma = if needs_comma { "," } else { "" }; + new_field = format!("{comma}\n{indent}{vis}{new_field}{postfix}"); let source_change = SourceChange::from_text_edit( def_file_id.file_id(sema.db), 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 93caf281f035f..01929a5144719 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 @@ -129,25 +129,17 @@ fn assoc_func_fix( let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original; let assoc_fn_params = f.assoc_fn_params(db); - let need_to_take_receiver_as_first_arg = if assoc_fn_params.is_empty() { - false - } else { - assoc_fn_params - .first() - .map(|first_arg| { - // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example, - // type of `b` is `Self`, which is `Box`, containing unspecified generics. - // However, type of `receiver` is specified, it could be `Box` or something like that, - // so `first_arg.ty() == receiver_type` evaluate to `false` here. - // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard, - // apply `.as_adt()` over `Box` or `Box` gets `Box`, so we get `true` here. - - // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver` - first_arg.ty() == receiver_type - || first_arg.ty().as_adt() == receiver_type.as_adt() - }) - .unwrap_or(false) - }; + let need_to_take_receiver_as_first_arg = assoc_fn_params.first().is_some_and(|first_arg| { + // For generic type, say `Box`, take `Box::into_raw(b: Self)` as example, + // type of `b` is `Self`, which is `Box`, containing unspecified generics. + // However, type of `receiver` is specified, it could be `Box` or something like that, + // so `first_arg.ty() == receiver_type` evaluate to `false` here. + // Here add `first_arg.ty().as_adt() == receiver_type.as_adt()` as guard, + // apply `.as_adt()` over `Box` or `Box` gets `Box`, so we get `true` here. + + // FIXME: it fails when type of `b` is `Box` with other generic param different from `receiver` + first_arg.ty() == receiver_type || first_arg.ty().as_adt() == receiver_type.as_adt() + }); let mut receiver_type_adt_name = receiver_type.as_adt()?.name(db).display_no_db(ctx.edition).to_smolstr(); From 0db85538726534e6ab28321a04d43ef6081e84ff Mon Sep 17 00:00:00 2001 From: "workflows-rust-analyzer[bot]" <223433972+workflows-rust-analyzer[bot]@users.noreply.github.com> Date: Sun, 17 May 2026 00:08:54 +0000 Subject: [PATCH 036/114] internal: update generated lints --- .../crates/ide-db/src/generated/lints.rs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 264bb4fa814dc..9d8175d5e930f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -2451,6 +2451,22 @@ The tracking issue for this feature is: [#111889] [#111889]: https://github.com/rust-lang/rust/issues/111889 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "abi_swift", + description: r##"# `abi_swift` + +Allows `extern "Swift" fn()`. + +The tracking issue for this feature is: [#156481] + +[#156481]: https://github.com/rust-lang/rust/issues/156481 + ------------------------ "##, default_severity: Severity::Allow, @@ -11160,6 +11176,22 @@ The tracking issue for this feature is: [#147456] [#147456]: https://github.com/rust-lang/rust/issues/147456 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "move_expr", + description: r##"# `move_expr` + +Allows `move(expr)` in closures. + +The tracking issue for this feature is: [#155050] + +[#155050]: https://github.com/rust-lang/rust/issues/155050 + ------------------------ "##, default_severity: Severity::Allow, @@ -13460,6 +13492,22 @@ The tracking issue for this feature is: [#138099] [#138099]: https://github.com/rust-lang/rust/issues/138099 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "return_address", + description: r##"# `return_address` + + + +The tracking issue for this feature is: [#154966] + +[#154966]: https://github.com/rust-lang/rust/issues/154966 + ------------------------ "##, default_severity: Severity::Allow, @@ -16299,6 +16347,22 @@ The tracking issue for this feature is: [#89517] [#89517]: https://github.com/rust-lang/rust/issues/89517 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "unix_kill_process_group", + description: r##"# `unix_kill_process_group` + + + +The tracking issue for this feature is: [#156537] + +[#156537]: https://github.com/rust-lang/rust/issues/156537 + ------------------------ "##, default_severity: Severity::Allow, From cc9e6e7fe2b59cf47d03e2f1d7270271787313d8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 17 May 2026 04:01:29 +0300 Subject: [PATCH 037/114] Have a specific error for unimplemented builtin macros Instead of failing to resolve them, which can be confusing. --- .../rust-analyzer/crates/hir-def/src/db.rs | 1 + .../rust-analyzer/crates/hir-def/src/lib.rs | 1 + .../crates/hir-def/src/nameres.rs | 1 + .../crates/hir-def/src/nameres/collector.rs | 4 ++-- .../rust-analyzer/crates/hir-expand/src/db.rs | 13 ++++++++++++ .../crates/hir-expand/src/eager.rs | 3 ++- .../crates/hir-expand/src/lib.rs | 21 ++++++++++++------- src/tools/rust-analyzer/crates/hir/src/lib.rs | 2 ++ .../src/handlers/macro_error.rs | 17 +++++++++++++++ 9 files changed, 53 insertions(+), 10 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 6301eb7901664..1776b7c84b4be 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -75,6 +75,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId { MacroExpander::BuiltInAttr(it) => MacroDefKind::BuiltInAttr(in_file, it), MacroExpander::BuiltInDerive(it) => MacroDefKind::BuiltInDerive(in_file, it), MacroExpander::BuiltInEager(it) => MacroDefKind::BuiltInEager(in_file, it), + MacroExpander::UnimplementedBuiltIn => MacroDefKind::UnimplementedBuiltIn(in_file), } }; 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 73cdc4a9d0ed1..ac2c03280eee5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -442,6 +442,7 @@ pub enum MacroExpander { BuiltInAttr(BuiltinAttrExpander), BuiltInDerive(BuiltinDeriveExpander), BuiltInEager(EagerExpander), + UnimplementedBuiltIn, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 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 ef7fb0888f3bd..2bc3cf38ebcb9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -841,6 +841,7 @@ pub(crate) fn macro_styles_from_id(db: &dyn DefDatabase, macro_id: MacroId) -> M MacroExpander::BuiltIn(_) | MacroExpander::BuiltInEager(_) => MacroCallStyles::FN_LIKE, MacroExpander::BuiltInAttr(_) => MacroCallStyles::ATTR, MacroExpander::BuiltInDerive(_) => MacroCallStyles::DERIVE, + MacroExpander::UnimplementedBuiltIn => MacroCallStyles::all(), // Unknown. } } 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 8e16c5ece608d..7a69e512d80a6 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 @@ -2560,7 +2560,7 @@ impl ModCollector<'_, '_> { .def_map .diagnostics .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, f_ast_id)); - return; + MacroExpander::UnimplementedBuiltIn } } } else { @@ -2639,7 +2639,7 @@ impl ModCollector<'_, '_> { .def_map .diagnostics .push(DefDiagnostic::unimplemented_builtin_macro(self.module_id, f_ast_id)); - return; + MacroExpander::UnimplementedBuiltIn } } else { // Case 2: normal `macro` diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index b09f69a295915..d9344618e3087 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -43,6 +43,7 @@ pub enum TokenExpander<'db> { BuiltInAttr(BuiltinAttrExpander), /// `derive(Copy)` and such. BuiltInDerive(BuiltinDeriveExpander), + UnimplementedBuiltIn, /// The thing we love the most here in rust-analyzer -- procedural macros. ProcMacro(CustomProcMacroExpander), } @@ -311,6 +312,7 @@ pub fn expand_speculative( it.expand(db, actual_macro_call, &tt, span).map_err(Into::into) } MacroDefKind::BuiltInAttr(_, it) => it.expand(db, actual_macro_call, &tt, span), + MacroDefKind::UnimplementedBuiltIn(_) => expand_unimplemented_builtin_macro(span), }; let expand_to = loc.expand_to(); @@ -335,6 +337,13 @@ pub fn expand_speculative( Some((node.syntax_node(), token)) } +fn expand_unimplemented_builtin_macro(span: Span) -> ExpandResult { + ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "this built-in macro is not implemented"), + ) +} + #[salsa::tracked(lru = 1024, returns(ref))] fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> AstIdMap { AstIdMap::from_source(&db.parse_or_expand(file_id)) @@ -538,6 +547,7 @@ impl<'db> TokenExpander<'db> { MacroDefKind::BuiltInDerive(_, expander) => TokenExpander::BuiltInDerive(expander), MacroDefKind::BuiltInEager(_, expander) => TokenExpander::BuiltInEager(expander), MacroDefKind::ProcMacro(_, expander, _) => TokenExpander::ProcMacro(expander), + MacroDefKind::UnimplementedBuiltIn(_) => TokenExpander::UnimplementedBuiltIn, } } } @@ -572,6 +582,9 @@ fn macro_expand<'db>( MacroDefKind::BuiltInDerive(_, it) => { it.expand(db, macro_call_id, arg, span).map_err(Into::into).zip_val(None) } + MacroDefKind::UnimplementedBuiltIn(_) => { + expand_unimplemented_builtin_macro(span).zip_val(None) + } MacroDefKind::BuiltInEager(_, it) => { // This might look a bit odd, but we do not expand the inputs to eager macros here. // Eager macros inputs are expanded, well, eagerly when we collect the macro calls. diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs index a19f58709b8f9..f8a560834adb3 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs @@ -246,7 +246,8 @@ fn eager_macro_recur( | MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInAttr(..) | MacroDefKind::BuiltInDerive(..) - | MacroDefKind::ProcMacro(..) => { + | MacroDefKind::ProcMacro(..) + | MacroDefKind::UnimplementedBuiltIn(..) => { let ExpandResult { value: (parse, tm), err } = lazy_expand( db, &def, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index c98d072784d4e..9c5714be2de7d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -249,6 +249,7 @@ pub enum MacroDefKind { BuiltInAttr(AstId, BuiltinAttrExpander), BuiltInDerive(AstId, BuiltinDeriveExpander), BuiltInEager(AstId, EagerExpander), + UnimplementedBuiltIn(AstId), ProcMacro(AstId, CustomProcMacroExpander, ProcMacroKind), } @@ -265,7 +266,8 @@ impl MacroDefKind { | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) | MacroDefKind::BuiltInEager(id, _) - | MacroDefKind::Declarative(id, ..) => id.erase(), + | MacroDefKind::Declarative(id, ..) + | MacroDefKind::UnimplementedBuiltIn(id) => id.erase(), } } } @@ -500,6 +502,7 @@ impl MacroCallId { MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => MacroKind::Attr, MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) => MacroKind::ProcMacro, MacroDefKind::BuiltInAttr(..) => MacroKind::AttrBuiltIn, + MacroDefKind::UnimplementedBuiltIn(..) => MacroKind::Declarative, } } @@ -551,7 +554,8 @@ impl MacroDefId { | MacroDefKind::BuiltIn(id, _) | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) - | MacroDefKind::BuiltInEager(id, _) => { + | MacroDefKind::BuiltInEager(id, _) + | MacroDefKind::UnimplementedBuiltIn(id) => { id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range()) } MacroDefKind::ProcMacro(id, _, _) => { @@ -567,7 +571,8 @@ impl MacroDefId { | MacroDefKind::BuiltIn(id, _) | MacroDefKind::BuiltInAttr(id, _) | MacroDefKind::BuiltInDerive(id, _) - | MacroDefKind::BuiltInEager(id, _) => Either::Left(id), + | MacroDefKind::BuiltInEager(id, _) + | MacroDefKind::UnimplementedBuiltIn(id) => Either::Left(id), } } @@ -577,9 +582,9 @@ impl MacroDefId { pub fn is_attribute(&self) -> bool { match self.kind { - MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) => { - true - } + MacroDefKind::BuiltInAttr(..) + | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Attr) + | MacroDefKind::UnimplementedBuiltIn(_) => true, MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::ATTR), _ => false, } @@ -588,7 +593,8 @@ impl MacroDefId { pub fn is_derive(&self) -> bool { match self.kind { MacroDefKind::BuiltInDerive(..) - | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) => true, + | MacroDefKind::ProcMacro(_, _, ProcMacroKind::CustomDerive) + | MacroDefKind::UnimplementedBuiltIn(_) => true, MacroDefKind::Declarative(_, styles) => styles.contains(MacroCallStyles::DERIVE), _ => false, } @@ -601,6 +607,7 @@ impl MacroDefId { | MacroDefKind::ProcMacro(_, _, ProcMacroKind::Bang) | MacroDefKind::BuiltInEager(..) | MacroDefKind::Declarative(..) + | MacroDefKind::UnimplementedBuiltIn(_) ) } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9f94243062b38..768ffd569789e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3612,6 +3612,7 @@ impl Macro { } MacroExpander::BuiltInAttr(_) => MacroKind::AttrBuiltIn, MacroExpander::BuiltInDerive(_) => MacroKind::DeriveBuiltIn, + MacroExpander::UnimplementedBuiltIn => MacroKind::Declarative, }, MacroId::MacroRulesId(it) => match it.lookup(db).expander { MacroExpander::Declarative { .. } => MacroKind::Declarative, @@ -3620,6 +3621,7 @@ impl Macro { } MacroExpander::BuiltInAttr(_) => MacroKind::AttrBuiltIn, MacroExpander::BuiltInDerive(_) => MacroKind::DeriveBuiltIn, + MacroExpander::UnimplementedBuiltIn => MacroKind::Declarative, }, MacroId::ProcMacroId(it) => match it.lookup(db).kind { ProcMacroKind::CustomDerive => MacroKind::Derive, 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 b6571e02efbe6..7acbcad22c5a3 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 @@ -327,4 +327,21 @@ fn it_works() { "#, ); } + + #[test] + fn unimplemented_builtin_macro() { + check_diagnostics( + r#" +#[rustc_builtin_macro] +macro_rules! unimplemented_builtin_macro { + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: unimplemented built-in macro + () => {}; +} + + #[unimplemented_builtin_macro] +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this built-in macro is not implemented +struct Foo; + "#, + ); + } } From 41b1abebca7cc08def8357669d366892d5b026f7 Mon Sep 17 00:00:00 2001 From: DaniPopes <57450786+DaniPopes@users.noreply.github.com> Date: Sun, 17 May 2026 06:18:06 +0200 Subject: [PATCH 038/114] Handle TyKind::{Pat,UnsafeBinder} in has_drop_glue `TyKind::Pat` is hit on stable through `core::num::NonZero*` types which use it internally. `TyKind::UnsafeBinder` isn't hit yet, but added for completeness, since has_drop_glue ends up being called on hover. --- .../rust-analyzer/crates/hir-ty/src/drop.rs | 7 ++--- .../crates/ide/src/hover/tests.rs | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs index 726b862fe1e7d..e1fbc85960899 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/drop.rs @@ -6,7 +6,6 @@ use hir_def::{ }; use rustc_hash::FxHashSet; use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind}; -use stdx::never; use crate::{ consteval, @@ -177,9 +176,7 @@ fn has_drop_glue_impl<'db>( } } TyKind::Infer(..) => unreachable!("inference vars shouldn't exist out of inference"), - TyKind::Pat(..) | TyKind::UnsafeBinder(..) => { - never!("we do not handle pattern and unsafe binder types"); - DropGlue::None - } + TyKind::Pat(ty, _) => has_drop_glue_impl(infcx, ty, env, visited), + TyKind::UnsafeBinder(ty) => has_drop_glue_impl(infcx, ty.skip_binder(), env, visited), } } 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 bf5e0be37420d..30644fe2db898 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -3152,6 +3152,35 @@ fn test_hover_layout_of_enum() { ); } +#[test] +fn test_hover_layout_nonzero_type_alias() { + check( + r#"//- minicore: non_zero +use core::num; +trait Trait { type Inner; } +impl Trait for u8 { type Inner = num::NonZeroU8; } +#[repr(transparent)] +struct NonZero(T::Inner); +type NonZeroU8$0 = NonZero; +"#, + expect![[r#" + *NonZeroU8* + + ```rust + ra_test_fixture + ``` + + ```rust + type NonZeroU8 = NonZero + ``` + + --- + + size = 1, align = 1, niches = 1, no Drop + "#]], + ); +} + #[test] fn test_hover_layout_padding_info() { check( From 4e1b8c98c7fd9d044e7c7f5bafcf7cb249813a44 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 17 May 2026 07:40:32 +0300 Subject: [PATCH 039/114] Do not mark enum variants as assoc items in symbol table Since they can be referenced without the enum, and this prevents autoimport of them. --- .../rust-analyzer/crates/hir/src/symbols.rs | 2 +- .../ide-assists/src/handlers/auto_import.rs | 31 +++++++++++++++++++ .../ide-completion/src/tests/expression.rs | 1 + .../test_symbol_index_collection.txt | 4 +-- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index a00c3219b229e..c4040c1c0099b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -191,7 +191,7 @@ impl<'a> SymbolCollector<'a> { this.with_container_name(Some(enum_name), |this| { let variants = id.enum_variants(this.db); for (variant_name, (variant_id, _)) in &variants.variants { - this.push_decl(*variant_id, variant_name, true, None); + this.push_decl(*variant_id, variant_name, false, None); } }); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index ac0bae7cd9b1a..5c5261c8983b1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -2037,4 +2037,35 @@ fn baz() { "Import `foo::Ext` without `as _`", ); } + + #[test] + fn local_enum_variant() { + check_assist( + auto_import, + r#" +mod foo { + pub enum ControlFlow { + Continue, + } +} + +fn main() { + Continue$0; +} + "#, + r#" +use foo::ControlFlow::Continue; + +mod foo { + pub enum ControlFlow { + Continue, + } +} + +fn main() { + Continue; +} + "#, + ); + } } 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 c1205f9e189eb..a1ae60bfc7fda 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 @@ -1055,6 +1055,7 @@ fn brr() { fn brr() fn() st YoloVariant YoloVariant st YoloVariant {…} YoloVariant { f: usize } + ev Yolo(…) (use HH::Yolo) Yolo(YoloVariant) bt u32 u32 kw const kw crate:: diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 1b20a574bd1b1..a84f75cb4e053 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -38,7 +38,7 @@ "Enum", ), is_alias: false, - is_assoc: true, + is_assoc: false, is_import: false, do_not_complete: Yes, _marker: PhantomData<&()>, @@ -110,7 +110,7 @@ "Enum", ), is_alias: false, - is_assoc: true, + is_assoc: false, is_import: false, do_not_complete: Yes, _marker: PhantomData<&()>, From d1568f0499fc2cb4d570471b4bfa6163f33f65cc Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 17 May 2026 07:52:22 +0300 Subject: [PATCH 040/114] Include enum variants in the import map So they can be autoimported for external crates. --- .../crates/hir-def/src/import_map.rs | 36 +++++++++++++++++-- .../ide-assists/src/handlers/auto_import.rs | 25 +++++++++++++ .../crates/ide-completion/src/render.rs | 2 ++ 3 files changed, 60 insertions(+), 3 deletions(-) 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 ba077b1b2ef5d..f8be211bf92a4 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 @@ -12,7 +12,7 @@ use span::Edition; use stdx::format_to; use crate::{ - AssocItemId, AttrDefId, Complete, FxIndexMap, ModuleDefId, ModuleId, TraitId, + AdtId, AssocItemId, AttrDefId, Complete, EnumId, FxIndexMap, ModuleDefId, ModuleId, TraitId, attrs::AttrFlags, db::DefDatabase, item_scope::{ImportOrExternCrate, ItemInNs}, @@ -208,7 +208,8 @@ impl ImportMap { complete: do_not_complete, }; - if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { + let module_def = item.as_module_def_id(); + if let Some(ModuleDefId::TraitId(tr)) = module_def { Self::collect_trait_assoc_items( db, &mut map, @@ -216,6 +217,8 @@ impl ImportMap { matches!(item, ItemInNs::Types(_)), &import_info, ); + } else if let Some(ModuleDefId::AdtId(AdtId::EnumId(enum_))) = module_def { + Self::collect_enum_variants(db, &mut map, enum_, &import_info); } let (infos, _) = @@ -224,7 +227,7 @@ impl ImportMap { infos.push(import_info); // If we've just added a module, descend into it. - if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { + if let Some(ModuleDefId::ModuleId(mod_id)) = module_def { worklist.push(mod_id); } } @@ -234,6 +237,33 @@ impl ImportMap { map } + fn collect_enum_variants( + db: &dyn DefDatabase, + map: &mut ImportMapIndex, + enum_: EnumId, + enum_import_info: &ImportInfo, + ) { + let _p = tracing::info_span!("collect_enum_variants").entered(); + for (variant_name, &(variant, _)) in &enum_.enum_variants(db).variants { + let attr_id = variant.into(); + let attrs = AttrFlags::query(db, attr_id); + let do_not_complete = Complete::extract(false, attrs); + let variant_info = ImportInfo { + container: enum_import_info.container, + name: variant_name.clone(), + is_doc_hidden: attrs.contains(AttrFlags::IS_DOC_HIDDEN), + is_unstable: attrs.contains(AttrFlags::IS_UNSTABLE), + complete: do_not_complete, + }; + + let (infos, _) = map + .entry(ItemInNs::Types(variant.into())) + .or_insert_with(|| (SmallVec::new(), IsTraitAssocItem::No)); + infos.reserve_exact(1); + infos.push(variant_info); + } + } + fn collect_trait_assoc_items( db: &dyn DefDatabase, map: &mut ImportMapIndex, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index 5c5261c8983b1..d6e459d04409c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -2062,6 +2062,31 @@ mod foo { } } +fn main() { + Continue; +} + "#, + ); + } + + #[test] + fn foreign_enum_variant() { + check_assist( + auto_import, + r#" +//- /foo.rs crate:foo +pub enum ControlFlow { + Continue, +} + +//- /main.rs crate:main deps:foo +fn main() { + Continue$0; +} + "#, + r#" +use foo::ControlFlow::Continue; + fn main() { Continue; } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index fbbdffefe324b..fe7a9a9d4adde 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -1059,6 +1059,8 @@ fn main() { "#, expect![[r#" ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify] + ev Variant Variant [type_could_unify+requires_import] + ev Variant Variant [requires_import] ex dep::test_mod_b::Enum::Variant [type_could_unify] md dep:: [] fn main() fn() [] From aebaad5c5fa3eb28c8f63b7fb2cf4170844782ca Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 17 May 2026 13:39:04 +0800 Subject: [PATCH 041/114] fix: no complete module colons before exists colons Example --- ```rust mod module {} fn foo() { $0::bar } ``` **Before this PR** ```rust mod module {} fn foo() { module::::bar } ``` **After this PR** ```rust mod module {} fn foo() { module::bar } ``` --- .../crates/ide-completion/src/render.rs | 4 ++- .../ide-completion/src/tests/expression.rs | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index fbbdffefe324b..9c1cf61622340 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -471,7 +471,9 @@ fn render_resolution_path( .insert_snippet(cap, ""); // set is snippet } } - let allow_module_path = matches!(path_ctx.kind, PathKind::Use) || !config.add_colons_to_module; + let allow_module_path = matches!(path_ctx.kind, PathKind::Use) + || completion.token.next_token().is_some_and(|it| it.kind() == syntax::T![::]) + || !config.add_colons_to_module; if !allow_module_path && matches!(resolution, ScopeDef::ModuleDef(Module(_))) { insert_text = format_smolstr!("{insert_text}::"); item.lookup_by(name.clone()).label(insert_text.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 c1205f9e189eb..c26c4c2ff846d 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 @@ -1157,6 +1157,12 @@ fn complete_module_colons() { r#"mod module {} fn foo() { module:: }"#, ); + check_edit( + "module", + r#"mod module {} fn foo() { $0foo::bar }"#, + r#"mod module {} fn foo() { module::foo::bar }"#, + ); + check_edit_with_config( CompletionConfig { add_colons_to_module: false, ..TEST_CONFIG }, "module", @@ -1165,6 +1171,27 @@ fn complete_module_colons() { ); } +#[test] +fn complete_module_exists_colons() { + check_edit( + "module", + r#"mod module {} fn foo() { $0::bar }"#, + r#"mod module {} fn foo() { module::bar }"#, + ); + + check_edit( + "module", + r#" +macro_rules! i { ($i:ident) => { $i::bar } } +mod module {} +fn foo() { i!($0) }"#, + r#" +macro_rules! i { ($i:ident) => { $i::bar } } +mod module {} +fn foo() { i!(module) }"#, + ); +} + #[test] fn else_completion_after_if() { check( From d1b5f6657b0dcd24c6652b504ff55b166e6bd0be Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 17 May 2026 12:38:39 +0800 Subject: [PATCH 042/114] Fix deref iterate order and multiple private in derefs Co-authored-by: Chayim Refael Friedman --- .../hir-ty/src/method_resolution/probe.rs | 5 ++-- .../ide-completion/src/completions/dot.rs | 26 ++++++++++++++++++- .../ide-completion/src/tests/flyimport.rs | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 4b2f0cfd7066b..60344490d550a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -3,6 +3,7 @@ use std::{cell::RefCell, convert::Infallible, ops::ControlFlow}; +use base_db::FxIndexMap; use hir_def::{ AssocItemId, FunctionId, GenericParamId, ImplId, ItemContainerId, TraitId, hir::generics::GenericParams, @@ -10,7 +11,7 @@ use hir_def::{ }; use hir_expand::name::Name; use rustc_ast_ir::Mutability; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashSet; use rustc_type_ir::{ InferTy, TypeVisitableExt, Upcast, Variance, elaborate::{self, supertrait_def_ids}, @@ -719,7 +720,7 @@ impl<'db> ProbeChoice<'db> for ProbeForNameChoice<'db> { #[derive(Debug)] struct ProbeAllChoice<'db> { - candidates: RefCell>>, + candidates: RefCell>>, considering_visible_candidates: bool, } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index cc6df718ce0a5..59c6c55c22b95 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -263,8 +263,8 @@ fn complete_methods( } } }; - same_name.insert_entry(func); if do_complete { + same_name.insert_entry(func); (self.f)(func); } } @@ -924,15 +924,39 @@ fn test(a: A) { //- /dep.rs crate:dep pub struct A {} pub struct B {} +pub struct C {} +pub struct D {} +pub struct E {} +pub struct F {} impl core::ops::Deref for A { type Target = B; fn deref(&self) -> &Self::Target { loop {} } } +impl core::ops::Deref for B { + type Target = C; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for C { + type Target = D; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for D { + type Target = E; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::Deref for E { + type Target = F; + fn deref(&self) -> &Self::Target { loop {} } +} pub trait Foo { fn foo(&self) -> u32 {} } impl Foo for A {} impl Foo for B {} impl A { fn foo(&self) -> u8 {} } impl B { pub fn foo(&self) -> u16 {} } +impl C { fn foo(&self) -> i8 {} } +impl D { fn foo(&self) -> i16 {} } +impl E { pub fn foo(&self) -> i32 {} } +impl F { pub fn foo(&self) -> f32 {} } //- /main.rs crate:main deps:dep use dep::*; fn test(a: A) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 231623a42fc65..45db8ecfc6a5f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -781,9 +781,9 @@ fn main() { } "#, expect![[r#" - me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED + me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED "#]], ); } From 102b1a899a148b238cca3cc3795860e53d999992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 17 May 2026 12:30:29 +0300 Subject: [PATCH 043/114] Prepare for merging from rust-lang/rust This updates the rust-version file to ba0949ab745985a442e274ba52e8fb348cb0c662. --- 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 1e8579c04511c..4b1ba970b7c4b 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -8afb6a8b1b32fce2f8aa7520517833338dc36c5e +ba0949ab745985a442e274ba52e8fb348cb0c662 From 4053e2406f980f180791a594540360ecbf0d28b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 17 May 2026 15:02:26 +0300 Subject: [PATCH 044/114] Ignore workspace/diagnostic/refresh in slow tests --- .../crates/rust-analyzer/tests/slow-tests/flycheck.rs | 2 -- .../crates/rust-analyzer/tests/slow-tests/support.rs | 10 +++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs index 386edb82f4e93..c6f1f81139d28 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/flycheck.rs @@ -76,7 +76,6 @@ fn main() { } #[test] -#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostic_with_override_command() { if skip_slow_tests() { return; @@ -113,7 +112,6 @@ fn main() {} } #[test] -#[ignore = "this test tends to stuck, FIXME: investigate that"] fn test_flycheck_diagnostics_with_override_command_cleared_after_fix() { if skip_slow_tests() { return; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index 73904036730b9..e2304c3d716c9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -372,9 +372,13 @@ impl Server { Message::Request(req) => { if req.method == "client/registerCapability" { let params = req.params.to_string(); - if ["workspace/didChangeWatchedFiles", "textDocument/didSave"] - .into_iter() - .any(|it| params.contains(it)) + if [ + "workspace/diagnostic/refresh", + "workspace/didChangeWatchedFiles", + "textDocument/didSave", + ] + .into_iter() + .any(|it| params.contains(it)) { continue; } From 71565e6a6cfa18ed8b521166dab7d55447f25390 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 30 Apr 2026 11:39:10 +0200 Subject: [PATCH 045/114] feat(diagnostics): add handler for E0040 Co-authored-by: WaterWhisperer --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 10 + .../crates/hir-ty/src/infer/path.rs | 10 +- .../rust-analyzer/crates/hir-ty/src/lib.rs | 7 +- .../hir-ty/src/method_resolution/confirm.rs | 6 +- .../crates/hir/src/diagnostics.rs | 30 +- .../src/handlers/explicit_drop_method_use.rs | 426 ++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 7 files changed, 483 insertions(+), 8 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs 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 51e9435c804d9..86a4f613b3188 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -478,6 +478,16 @@ pub enum InferenceDiagnostic { found: StoredTy, }, SolverDiagnostic(SolverDiagnostic), + ExplicitDropMethodUse { + #[type_visitable(ignore)] + kind: ExplicitDropMethodUseKind, + }, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ExplicitDropMethodUseKind { + MethodCall(ExprId), + Path(ExprOrPatId), } /// Represents coercing a value to a different type of value. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 1c3d93ae6e93c..0ec72edc3d59c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -11,7 +11,7 @@ use rustc_type_ir::inherent::{SliceLike, Ty as _}; use stdx::never; use crate::{ - InferenceDiagnostic, Span, ValueTyDefId, + ExplicitDropMethodUseKind, InferenceDiagnostic, Span, ValueTyDefId, infer::{ InferenceTyLoweringVarsCtx, diagnostics::InferenceTyLoweringContext as TyLoweringContext, }, @@ -33,6 +33,14 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Option<(ValueNs, Ty<'db>)> { let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; + if let ValueNs::FunctionId(f) = value + && self.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == f) + { + self.push_diagnostic(InferenceDiagnostic::ExplicitDropMethodUse { + kind: ExplicitDropMethodUseKind::Path(id), + }); + } + let (value_def, generic_def, substs) = match self.resolve_value_path(path, id, value, self_subst)? { ValuePathResolution::GenericDef(value_def, generic_def, substs) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 91e3b85aa1311..1900a41f7f175 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -104,9 +104,10 @@ use crate::{ pub use autoderef::autoderef; pub use infer::{ - Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, InferenceDiagnostic, InferenceResult, - InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, cast::CastError, could_coerce, - could_unify, could_unify_deeply, infer_query_with_inspect, + Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, ExplicitDropMethodUseKind, + InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, + PointerCast, cast::CastError, could_coerce, could_unify, could_unify_deeply, + infer_query_with_inspect, }; pub use lower::{ GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, LifetimeElisionKind, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index c425e69dc59dd..d960a6547d462 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -18,7 +18,7 @@ use crate::{ Adjust, Adjustment, AutoBorrow, IncorrectGenericsLenKind, InferenceDiagnostic, LifetimeElisionKind, PointerCast, Span, db::HirDatabase, - infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext}, + infer::{AllowTwoPhase, AutoBorrowMutability, ExplicitDropMethodUseKind, InferenceContext}, lower::{ GenericPredicates, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, @@ -582,7 +582,9 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> { fn check_for_illegal_method_calls(&self) { // Disallow calls to the method `drop` defined in the `Drop` trait. if self.ctx.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == self.candidate) { - // FIXME: Report an error. + self.ctx.push_diagnostic(InferenceDiagnostic::ExplicitDropMethodUse { + kind: ExplicitDropMethodUseKind::MethodCall(self.call_expr), + }); } } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index f4a29a4bcc08b..a399df8276661 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -15,8 +15,9 @@ use hir_def::{ }; use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name}; use hir_ty::{ - CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, ParamEnvAndCrate, - PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, + CastError, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceTyDiagnosticSource, + ParamEnvAndCrate, PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, + TyLoweringDiagnosticKind, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, display::{DisplayTarget, HirDisplay}, @@ -106,6 +107,7 @@ diagnostics![AnyDiagnostic<'db> -> CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, + ExplicitDropMethodUse, FruInDestructuringAssignment, FunctionalRecordUpdateOnNonStruct, GenericDefaultRefersToSelf, @@ -325,6 +327,11 @@ pub struct CannotBeDereferenced<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct ExplicitDropMethodUse { + pub expr_or_path: Either>, InFile>>, +} + #[derive(Debug)] pub struct FruInDestructuringAssignment { pub node: InFile>, @@ -1044,6 +1051,25 @@ impl<'db> AnyDiagnostic<'db> { let span = span_syntax(d.span)?; Self::solver_diagnostic(db, &d.kind, span, env)? } + InferenceDiagnostic::ExplicitDropMethodUse { kind } => { + let expr_or_path = match kind { + ExplicitDropMethodUseKind::MethodCall(expr) => { + let expr = expr_syntax(*expr)?; + let expr = expr.with_value(expr.value.cast::()?); + Either::Left(expr) + } + ExplicitDropMethodUseKind::Path(path_expr_id) => { + let syntax = expr_or_pat_syntax(*path_expr_id)?; + let file_id = syntax.file_id; + let syntax = + syntax.with_value(syntax.value.cast::()?).to_node(db); + let path = syntax.path()?; + let path = InFile::new(file_id, AstPtr::new(&path)); + Either::Right(path) + } + }; + ExplicitDropMethodUse { expr_or_path }.into() + } }) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs new file mode 100644 index 0000000000000..b02bccf5a533c --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs @@ -0,0 +1,426 @@ +use either::Either; +use hir::InFile; +use ide_db::assists::Assist; +use ide_db::source_change::{SourceChange, SourceChangeBuilder}; +use ide_db::text_edit::TextEdit; +use itertools::Itertools; +use syntax::{ + AstNode, AstPtr, + ast::{self, HasArgList}, +}; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_range, fix}; + +// Diagnostic: explicit-drop-method-use +// +// This diagnostic is triggered when the `Drop::drop` method is called (or named) explicitly. +pub(crate) fn explicit_drop_method_use( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ExplicitDropMethodUse, +) -> Diagnostic { + match d.expr_or_path { + Either::Left(expr) => { + let display_range = adjusted_display_range(ctx, expr, &|node| { + Some(node.name_ref()?.syntax().text_range()) + }); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0040"), + "explicit use of destructor method", + display_range, + ) + .stable() + .with_main_node(expr.map(Into::into)) + .with_fixes(fix_method_call(ctx, expr)) + } + Either::Right(path) => Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0040"), + "explicit use of destructor method", + path.map(Into::into), + ) + .stable() + .with_fixes(fix_path(ctx, path)), + } +} + +fn fix_method_call( + ctx: &DiagnosticsContext<'_, '_>, + mcall_ptr: InFile>, +) -> Option> { + if mcall_ptr.file_id.is_macro() { + // TODO: handle macro calls. Rough plan: + // 1. upmap the range of the receiver and the range of the whole call + // 2. delete everything outside the receiver and replace it with `drop(...)`, using range edits only. + return None; + } + + let db = ctx.db(); + + let file_id = mcall_ptr.file_id; + let mcall = mcall_ptr.to_node(db); + let range = mcall.syntax().text_range(); + + // `mcall` is `foo.drop()` -- extract the receiver, and wrap it in `drop()` + // NOTE: it could theoretically be `(&mut foo).drop()` instead, in which case the fix + // below would be incorrect, as it'd result in `drop((&mut foo))` instead of `drop(foo)` + // -- but we don't bother to deal with that case. + let recv = mcall.receiver()?; + + let mut builder = SourceChangeBuilder::new(file_id.original_file(db).file_id(db)); + let editor = builder.make_editor(mcall.syntax()); + let make = editor.make(); + let new_call = + make.expr_call(make.expr_path(make.path_from_text("drop")), make.arg_list([recv])); + builder.replace_ast(ast::Expr::MethodCallExpr(mcall), ast::Expr::CallExpr(new_call)); + let source_change = builder.finish(); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range)]) +} + +fn fix_path( + ctx: &DiagnosticsContext<'_, '_>, + path_ptr: InFile>, +) -> Option> { + let db = ctx.db(); + + let file_id = path_ptr.file_id; + let path = path_ptr.to_node(db); + + if let Some(call) = + path.syntax().parent().and_then(|it| it.parent()).and_then(ast::CallExpr::cast) + { + if file_id.is_macro() { + // TODO: make this work in macros? Might not be worth it, as this is a niche way to trigger this + // already niche error + return None; + } + + // `call` is `Drop::drop(&mut foo)` -- extract the arg, and wrap it in `drop()` + let arg_list = call.arg_list()?; + let ref_recv = arg_list.args().exactly_one().ok()?; + let ast::Expr::RefExpr(ref_recv) = ref_recv else { + return None; + }; + let recv = ref_recv.expr()?; + + let range = call.syntax().text_range(); + + let mut builder = SourceChangeBuilder::new(file_id.original_file(db).file_id(db)); + let editor = builder.make_editor(call.syntax()); + let make = editor.make(); + let new_call = + make.expr_call(make.expr_path(make.path_from_text("drop")), make.arg_list([recv])); + builder.replace_ast(call, new_call); + let source_change = builder.finish(); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range)]) + } else { + // `path` could be the `Foo::drop` in `let d = Foo::drop;` + // -- replace the path with `drop` + + let range = InFile::new(file_id, path.syntax().text_range()) + .original_node_file_range_rooted_opt(db)?; + + let edit = TextEdit::replace(range.range, "drop".to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id.file_id(db), edit); + Some(vec![fix("use-drop-function", "Use `drop` function", source_change, range.range)]) + } +} + +#[cfg(test)] +mod tests { + use crate::tests::{ + check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled, + }; + + #[test] + fn method_call_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); + // ^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn method_call_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop$0(); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ); + } + + #[test] + fn qualified_call_1_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + A::drop(&mut a); + // ^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn qualified_call_1_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + A::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn qualified_call_2_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + Drop::drop(&mut a); + // ^^^^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn qualified_call_2_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + Drop::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn fully_qualified_call_diagnostic() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + ::drop(&mut a); + // ^^^^^^^^^^^^^^^^^ 💡 error: explicit use of destructor method +} +"#, + ); + } + + #[test] + fn fully_qualified_call_fix() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + ::drop(&mut a$0); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + drop(a); +} +"#, + ) + } + + #[test] + fn path_diagnostic() { + check_diagnostics_with_disabled( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = A::drop; + // ^^^^^^^ 💡 error: explicit use of destructor method + d(&mut a); +} +"#, + // Because of the error, the code isn't analyzed further (?), and so `d` is warned on as unused. + // Arguably a bug in r-a (rustc doesn't emit a warning in this case) + // FIXME: remove this once r-a no longer warns + &["unused_variables"], + ); + } + + #[test] + // NOTE: Here, the fix is not completely correct, as it doesn't replace `d(&mut a)` with `d(a)`. + // Oh well, rustc doesn't either + fn path_fix() { + check_fix_with_disabled( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = A::drop$0; + d(&mut a); +} +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + let d = drop; + d(&mut a); +} +"#, + // Because of the error, the code isn't analyzed further (?), and so `d` is warned on as unused. + // Arguably a bug in r-a (rustc doesn't emit a warning in this case) + // FIXME: remove this once r-a no longer warns + &["unused_variables"], + ); + } + + #[test] + // NOTE: Here, the fix is not completely correct, as it doesn't replace `d(&mut a)` with `d(a)`. + // Oh well, rustc doesn't either + fn path_fix_in_macro() { + check_fix( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +macro_rules! main { + ($e:expr) => { + fn main() { $e } + } +} + +main!{{ + let mut a = A; + let d = A::drop$0; + d(&mut a); +}}; +"#, + r#" +struct A; +impl Drop for A { fn drop(&mut self) {} } + +macro_rules! main { + ($e:expr) => { + fn main() { $e } + } +} + +main!{{ + let mut a = A; + let d = drop; + d(&mut a); +}}; +"#, + ); + } + + #[test] + fn std_mem_drop() { + check_diagnostics( + r#" +//- minicore: drop +struct A; +impl Drop for A { fn drop(&mut self) {} } + +fn main(a: A) { + drop(a); +} +"#, + ); + } + + #[test] + fn inherent_drop_method() { + check_diagnostics( + r#" +struct A; +impl A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); +} +"#, + ); + } + + #[test] + fn custom_trait_drop_method() { + check_diagnostics( + r#" +struct A; +trait MyDrop { fn drop(&mut self); } +impl MyDrop for A { fn drop(&mut self) {} } + +fn main(mut a: A) { + a.drop(); +} +"#, + ); + } +} 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 d38780ede2343..c8972316f96b9 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -37,6 +37,7 @@ mod handlers { pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_array_or_slice_pat; pub(crate) mod expected_function; + pub(crate) mod explicit_drop_method_use; pub(crate) mod fru_in_destructuring_assignment; pub(crate) mod functional_record_update_on_non_struct; pub(crate) mod generic_args_prohibited; @@ -535,6 +536,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d), AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d), AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d), + AnyDiagnostic::ExplicitDropMethodUse(d) => handlers::explicit_drop_method_use::explicit_drop_method_use(&ctx, &d), }; res.push(d) } From b2c2e6fa90bf4dc8ad6702f19a2b29bfad30f3db Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 18 May 2026 02:02:27 +0300 Subject: [PATCH 046/114] Support named consts in range pattern types --- .../crates/hir-def/src/expr_store/lower.rs | 25 +++++++++++++++++-- .../crates/hir-ty/src/layout/tests.rs | 10 ++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) 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 48ccc1c0aa7fc..7fd635c89add1 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 @@ -2968,12 +2968,33 @@ impl<'db> ExprCollector<'db> { } fn lower_ty_pat_range_side(&mut self, pat: ast::Pat) -> ExprId { + let ptr = AstPtr::new(&pat); match &pat { ast::Pat::LiteralPat(it) => { let Some((literal, _)) = pat_literal_to_hir(it) else { return self.missing_expr() }; - self.alloc_expr_from_pat(Expr::Literal(literal), AstPtr::new(&pat)) + self.alloc_expr_from_pat(Expr::Literal(literal), ptr) + } + ast::Pat::ConstBlockPat(it) => { + if let Some(block) = it.block_expr() { + let expr_id = self.with_label_rib(RibKind::Constant, |this| { + this.with_binding_owner(|this| this.collect_block(block)) + }); + self.alloc_expr_from_pat(Expr::Const(expr_id), ptr) + } else { + self.missing_expr() + } + } + ast::Pat::PathPat(it) => { + let path = it + .path() + .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator)); + self.alloc_expr_from_pat(path.map(Expr::Path).unwrap_or(Expr::Missing), ptr) + } + ast::Pat::IdentPat(it) if it.is_simple_ident() => { + let name = it.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing); + self.alloc_expr_from_pat(Expr::Path(name.into()), ptr) } - _ => self.missing_expr(), + _ => self.missing_expr(), // FIXME: Emit an error. } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index 482945f5f0aaa..b42ac54f130a6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -182,6 +182,7 @@ fn check_fail(#[rust_analyzer::rust_fixture] ra_fixture: &str, e: LayoutError) { assert_eq!(r, Err(e)); } +#[rust_analyzer::macro_style(braces)] macro_rules! size_and_align { (minicore: $($x:tt),*;$($t:tt)*) => { { @@ -535,6 +536,15 @@ fn non_zero_and_non_null() { use core::{num::NonZeroU8, ptr::NonNull}; struct Goal(Option, Option>); } + check_size_and_align( + r#" +const END: usize = 10; +struct Goal(core::pattern_type!(usize is 0..=END)); + "#, + "//- minicore: pat\n", + 8, + 8, + ); } #[test] From 42e5f53e7cd19cd5ce3d9966b53bac90f72f59c2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 18 May 2026 04:00:53 +0300 Subject: [PATCH 047/114] Do not consider the path of the macro in a macro call to be inside a macro call --- .../rust-analyzer/crates/hir/src/semantics.rs | 7 +++- .../ide-completion/src/tests/expression.rs | 33 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index d0202c1054c50..562c78809a6df 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1088,7 +1088,12 @@ impl<'db> SemanticsImpl<'db> { /// That is, we strictly check if it lies inside the input of a macro call. pub fn is_inside_macro_call(&self, token @ InFile { value, .. }: InFile<&SyntaxToken>) -> bool { value.parent_ancestors().any(|ancestor| { - if ast::MacroCall::can_cast(ancestor.kind()) { + if let Some(macro_call) = ast::MacroCall::cast(ancestor.clone()) + // If this is the *path* of a macro, it's not inside the call. + && macro_call.path().is_none_or(|path| { + !path.syntax().text_range().contains_range(value.text_range()) + }) + { return true; } 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 c26c4c2ff846d..e2baf42848b80 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 @@ -3928,3 +3928,36 @@ fn tryme(param: impl SubTrait) { "#]], ); } + +#[test] +fn can_complete_macro_path_inside_expansion() { + check( + r#" +macro_rules! bar { () => (); } +macro_rules! foo { ($i:ident) => { $i!() }; } +fn main() { + foo!(ba$0); +} + "#, + expect![[r#" + fn main() fn() + ma bar macro_rules! bar + ma foo macro_rules! foo + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); +} From f5dc299af6183945c7a49378acbfc75ca6dd7f6f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 18 May 2026 07:33:33 +0800 Subject: [PATCH 048/114] fix: complete ref_match in macro Example --- ```rust macro_rules! id { ($($t:tt)*) => ($($t)*); } fn foo(data: &i32) {} fn main() { let indent = 2i32; id!(foo(i$0)) } ``` **Before this PR** ``` indent ``` **After this PR** ``` &indent indent ``` --- .../crates/ide-completion/src/render.rs | 208 +++++++++++++++++- .../ide-completion/src/render/function.rs | 4 +- 2 files changed, 204 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index e2bfb7909e664..575c869093b06 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -181,9 +181,9 @@ pub(crate) fn render_field( if !expected_fn_type && let Some(receiver) = &dot_access.receiver - && let Some(receiver) = ctx.completion.sema.original_ast_node(receiver.clone()) + && let Some(receiver) = ctx.completion.sema.original_range_opt(receiver.syntax()) { - builder.insert(receiver.syntax().text_range().start(), "(".to_owned()); + builder.insert(receiver.range.start(), "(".to_owned()); builder.insert(ctx.source_range().end(), ")".to_owned()); let is_parens_needed = !matches!(dot_access.kind, DotAccessKind::Method); @@ -198,10 +198,10 @@ pub(crate) fn render_field( item.insert_text(field_with_receiver(receiver.as_deref(), &escaped_name)); } if let Some(receiver) = &dot_access.receiver - && let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) + && let Some(original) = ctx.completion.sema.original_range_opt(receiver.syntax()) && let Some(ref_mode) = compute_ref_match(ctx.completion, ty) { - item.ref_match(ref_mode, original.syntax().text_range().start()); + item.ref_match(ref_mode, original.range.start()); } item.doc_aliases(ctx.doc_aliases); item.build(db) @@ -758,10 +758,10 @@ fn path_ref_match( ) { if let Some(original_path) = &path_ctx.original_path { // At least one char was typed by the user already, in that case look for the original path - if let Some(original_path) = completion.sema.original_ast_node(original_path.clone()) + if let Some(original_path) = completion.sema.original_range_opt(original_path.syntax()) && let Some(ref_mode) = compute_ref_match(completion, ty) { - item.ref_match(ref_mode, original_path.syntax().text_range().start()); + item.ref_match(ref_mode, original_path.range.start()); } } else { // completion requested on an empty identifier, there is no path here yet. @@ -2711,6 +2711,202 @@ fn main() { ); } + #[test] + fn complete_ref_match_in_macro() { + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +fn main() { + let indent = 2i32; + id!(foo(i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Local)], + expect![[r#" + [ + CompletionItem { + label: "indent", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 114..115, + delete: 114..115, + insert: "indent", + kind: SymbolKind( + Local, + ), + detail: "i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: true, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: None, + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: false, + }, + ref_match: "&@114", + }, + ] + "#]], + ); + + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +fn indent() -> i32 { i32 } +fn main() { + id!(foo(i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Function)], + expect![[r#" + [ + CompletionItem { + label: "foo(…)", + detail_left: None, + detail_right: Some( + "fn(&i32)", + ), + source_range: 118..119, + delete: 118..119, + insert: "foo(${1:data})$0", + kind: SymbolKind( + Function, + ), + lookup: "foo", + detail: "fn(&i32)", + trigger_call_info: true, + }, + CompletionItem { + label: "indent()", + detail_left: None, + detail_right: Some( + "fn() -> i32", + ), + source_range: 118..119, + delete: 118..119, + insert: "indent()$0", + kind: SymbolKind( + Function, + ), + lookup: "indent", + detail: "fn() -> i32", + ref_match: "&@118", + }, + CompletionItem { + label: "main()", + detail_left: None, + detail_right: Some( + "fn()", + ), + source_range: 118..119, + delete: 118..119, + insert: "main()$0", + kind: SymbolKind( + Function, + ), + lookup: "main", + detail: "fn()", + }, + ] + "#]], + ); + + // FIXME: It is best to test `S.in` if speculative execution is implemented + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +struct S; +impl S {fn indent(&self) -> i32 { i32 }} +fn main() { + id!(foo(S.i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Method)], + expect![[r#" + [ + CompletionItem { + label: "indent()", + detail_left: None, + detail_right: Some( + "fn(&self) -> i32", + ), + source_range: 144..145, + delete: 144..145, + insert: "indent()$0", + kind: SymbolKind( + Method, + ), + lookup: "indent", + detail: "fn(&self) -> i32", + relevance: CompletionRelevance { + exact_name_match: false, + type_match: None, + is_local: false, + trait_: None, + is_name_already_imported: false, + requires_import: false, + is_private_editable: false, + postfix_match: None, + function: Some( + CompletionRelevanceFn { + has_params: true, + has_self_param: true, + return_type: Other, + }, + ), + is_skipping_completion: false, + has_local_inherent_impl: false, + is_deprecated: false, + }, + ref_match: "&@142", + }, + ] + "#]], + ); + + check_kinds( + r#" +macro_rules! id { ($($t:tt)*) => ($($t)*); } +fn foo(data: &i32) {} +struct S { indent: i32 } +fn main(s: S) { + id!(foo(s.i$0)) +} +"#, + &[CompletionItemKind::SymbolKind(SymbolKind::Field)], + expect![[r#" + [ + CompletionItem { + label: "indent", + detail_left: None, + detail_right: Some( + "i32", + ), + source_range: 122..123, + delete: 122..123, + insert: "indent", + kind: SymbolKind( + Field, + ), + detail: "i32", + ref_match: "&@120", + }, + ] + "#]], + ); + } + #[test] fn too_many_arguments() { cov_mark::check!(too_many_arguments); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 97d5a25f493d5..c690ccfdc5066 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -132,10 +132,10 @@ fn render( super::path_ref_match(completion, path_ctx, &ret_type, &mut item); } FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => { - if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) + if let Some(original_expr) = completion.sema.original_range_opt(receiver.syntax()) && let Some(ref_mode) = compute_ref_match(completion, &ret_type) { - item.ref_match(ref_mode, original_expr.syntax().text_range().start()); + item.ref_match(ref_mode, original_expr.range.start()); } } _ => (), From a1e3a4676a676495b7ab45c8458dc05a83147e3c Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Tue, 19 May 2026 22:39:15 +0800 Subject: [PATCH 049/114] feat: add diagnostic for E0608 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 5 ++ .../crates/hir-ty/src/infer/expr.rs | 12 ++- .../crates/hir-ty/src/infer/unify.rs | 1 + .../crates/hir/src/diagnostics.rs | 11 +++ .../src/handlers/cannot_index_into.rs | 77 +++++++++++++++++++ .../src/handlers/mutability_errors.rs | 2 +- .../crates/ide-diagnostics/src/lib.rs | 2 + 7 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs 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 86a4f613b3188..db437788c0e71 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -401,6 +401,11 @@ pub enum InferenceDiagnostic { expr: ExprId, found: StoredTy, }, + CannotIndexInto { + #[type_visitable(ignore)] + expr: ExprId, + found: StoredTy, + }, TypedHole { #[type_visitable(ignore)] expr: ExprId, 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 3cff0ea89693a..0a1bfb41d8b23 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 @@ -729,8 +729,16 @@ impl<'db> InferenceContext<'_, 'db> { self.table.select_obligations_where_possible(); trait_element_ty } - // FIXME: Report an error. - None => self.types.types.error, + None => { + if self.lang_items.Index.is_some() && self.lang_items.Index_index.is_some() + { + self.push_diagnostic(InferenceDiagnostic::CannotIndexInto { + expr: tgt_expr, + found: base_t.store(), + }); + } + self.types.types.error + } } } Expr::Tuple { exprs, .. } => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 6500eb213c209..393b404cde5dc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -578,6 +578,7 @@ pub(super) mod resolve_completely { self.resolve_completely(diagnostic); if let InferenceDiagnostic::CannotBeDereferenced { found: ty, .. } + | InferenceDiagnostic::CannotIndexInto { found: ty, .. } | InferenceDiagnostic::ExpectedFunction { found: ty, .. } | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } | InferenceDiagnostic::UnresolvedField { receiver: ty, .. } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index a399df8276661..6b07834231438 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -104,6 +104,7 @@ diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, BreakOutsideOfLoop, CannotBeDereferenced<'db>, + CannotIndexInto<'db>, CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, ExpectedFunction<'db>, @@ -327,6 +328,12 @@ pub struct CannotBeDereferenced<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct CannotIndexInto<'db> { + pub expr: InFile, + pub found: Type<'db>, +} + #[derive(Debug)] pub struct ExplicitDropMethodUse { pub expr_or_path: Either>, InFile>>, @@ -947,6 +954,10 @@ impl<'db> AnyDiagnostic<'db> { let expr = expr_syntax(*expr)?; CannotBeDereferenced { expr, found: Type::new(db, def, found.as_ref()) }.into() } + InferenceDiagnostic::CannotIndexInto { expr, found } => { + let expr = expr_syntax(*expr)?; + CannotIndexInto { expr, found: Type::new(db, def, found.as_ref()) }.into() + } InferenceDiagnostic::TyDiagnostic { source, diag } => { let source_map = match source { InferenceTyDiagnosticSource::Body => source_map, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs new file mode 100644 index 0000000000000..1e42313b4a887 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_index_into.rs @@ -0,0 +1,77 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-index-into +// +// This diagnostic is triggered if indexing is used on a type that cannot be +// indexed. +pub(crate) fn cannot_index_into( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotIndexInto<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0608"), + format!( + "cannot index into a value of type `{}`", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.expr.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn cannot_index_into() { + check_diagnostics( + r#" +//- minicore: index +fn f() { + let x = 1i32; + let _ = x[0]; + //^^^^ error: cannot index into a value of type `i32` +} +"#, + ); + } + + #[test] + fn allows_array_indexing() { + check_diagnostics( + r#" +//- minicore: index, slice +fn f() { + let x = [1i32, 2]; + let _ = x[0]; +} +"#, + ); + } + + #[test] + fn allows_overloaded_indexing() { + check_diagnostics( + r#" +//- minicore: index +struct Bag(i32); + +impl core::ops::Index for Bag { + type Output = i32; + + fn index(&self, _: usize) -> &Self::Output { + &self.0 + } +} + +fn f(x: Bag) { + let _ = x[0]; +} +"#, + ); + } +} 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 31becd1d74960..49aa6d7bd9469 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 @@ -1313,7 +1313,7 @@ fn main() { fn regression_20662() { check_diagnostics( r#" -//- minicore: index +//- minicore: index, slice pub trait A: core::ops::IndexMut { type T: A; } 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 c8972316f96b9..11ee669af0b4a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -33,6 +33,7 @@ mod handlers { pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; pub(crate) mod cannot_be_dereferenced; + pub(crate) mod cannot_index_into; pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_array_or_slice_pat; @@ -433,6 +434,7 @@ pub fn semantic_diagnostics( let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), + AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), From ce96d6ef3a41b4fb15924fa51851ee2447ab5af3 Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Tue, 19 May 2026 23:24:15 +0800 Subject: [PATCH 050/114] fix: remove lang-item guard and update test --- .../rust-analyzer/crates/hir-ty/src/infer/expr.rs | 11 ++++------- .../ide-diagnostics/src/handlers/missing_unsafe.rs | 7 +++++-- 2 files changed, 9 insertions(+), 9 deletions(-) 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 0a1bfb41d8b23..8df6d9bee4c57 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 @@ -730,13 +730,10 @@ impl<'db> InferenceContext<'_, 'db> { trait_element_ty } None => { - if self.lang_items.Index.is_some() && self.lang_items.Index_index.is_some() - { - self.push_diagnostic(InferenceDiagnostic::CannotIndexInto { - expr: tgt_expr, - found: base_t.store(), - }); - } + self.push_diagnostic(InferenceDiagnostic::CannotIndexInto { + expr: tgt_expr, + found: base_t.store(), + }); self.types.types.error } } 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 b4ddb239c8cc4..d279e9b4e8da0 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 @@ -425,17 +425,20 @@ fn main() { fn raw_deref_on_union_field() { check_diagnostics( r#" +//- minicore: index, slice +#![allow(unused_variables)] + fn main() { union U { a: u8 } - let x = U { a: 3 }; + let mut x = U { a: 3 }; let a = &raw mut x.a; union U1 { - a: u8 + a: usize } let x = U1 { a: 3 }; From 0d1fe7ea12c4f5674bea46a2d5e1adff5cd65fb1 Mon Sep 17 00:00:00 2001 From: mason hall Date: Tue, 19 May 2026 12:45:48 -0400 Subject: [PATCH 051/114] resolve merge conflicts --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 ++ .../crates/hir-ty/src/infer/pat.rs | 4 +- .../crates/hir/src/diagnostics.rs | 10 ++++ .../src/handlers/mutable_ref.rs | 60 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 5 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs 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 86a4f613b3188..4abf52f306c8b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -482,6 +482,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] kind: ExplicitDropMethodUseKind, }, + MutableRefBinding { + #[type_visitable(ignore)] + pat: PatId, + }, } #[derive(Debug, PartialEq, Eq, Clone)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 300c0cf214e87..8703b85e33632 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -893,14 +893,14 @@ impl<'a, 'db> InferenceContext<'a, 'db> { let user_bind_annot = BindingMode::from_annotation(binding_data.mode); let bm = match user_bind_annot { BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_) = def_br => { - // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and + // Only mention the experimental `mut_ref` feature if we're in edition 2024 and // using other experimental matching features compatible with it. if self.edition.at_least_2024() && (self.features.ref_pat_eat_one_layer_2024 || self.features.ref_pat_eat_one_layer_2024_structural) { if !self.features.mut_ref { - // FIXME: Emit an error: binding cannot be both mutable and by-reference. + self.push_diagnostic(InferenceDiagnostic::MutableRefBinding { pat }); } BindingMode(def_br, Mutability::Mut) diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index a399df8276661..62957b23a21af 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -131,6 +131,7 @@ diagnostics![AnyDiagnostic<'db> -> MissingMatchArms, MissingUnsafe, MovedOutOfRef<'db>, + MutableRefBinding, NeedMut, NonExhaustiveLet, NonExhaustiveRecordExpr, @@ -633,6 +634,11 @@ pub struct UnimplementedTrait<'db> { pub root_trait_predicate: Option>, } +#[derive(Debug)] +pub struct MutableRefBinding { + pub pat: InFile, +} + impl<'db> AnyDiagnostic<'db> { pub(crate) fn body_validation_diagnostic( db: &'db dyn HirDatabase, @@ -1070,6 +1076,10 @@ impl<'db> AnyDiagnostic<'db> { }; ExplicitDropMethodUse { expr_or_path }.into() } + InferenceDiagnostic::MutableRefBinding { pat } => { + let pat = pat_syntax(*pat)?.map(Into::into); + MutableRefBinding { pat }.into() + } }) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs new file mode 100644 index 0000000000000..9423f2a9aac2a --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs @@ -0,0 +1,60 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: mutable-ref +// +// This diagnostic is triggered when binding is taken that is both mutable and by-reference. +pub(crate) fn mutable_ref_binding( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::MutableRefBinding, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0658"), + "`mut` bindings cannot also be `ref` by default in 2024 edition", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn mutable_ref_binding_missing_feature() { + check_diagnostics( + r#" +//- minicore: option +//- /main.rs +#![feature(ref_pat_eat_one_layer_2024)] +fn main() { + let opt_ref = &Some(42); + + if let Some(mut x) = opt_ref { + //^^^^^ error: `mut` bindings cannot also be `ref` by default in 2024 edition + x = &5; + } +} +"#, + ); + } + + #[test] + fn mutable_ref_binding_with_feature() { + check_diagnostics( + r#" +//- minicore: option +//- /main.rs +#![feature(ref_pat_eat_one_layer_2024)] +#![feature(mut_ref)] +fn main() { + let opt_ref = &Some(42); + + if let Some(mut x) = opt_ref { + x = &5; + } +} +"#, + ); + } +} 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 c8972316f96b9..1fdcbfe5d7291 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -62,6 +62,7 @@ mod handlers { pub(crate) mod missing_unsafe; pub(crate) mod moved_out_of_ref; pub(crate) mod mutability_errors; + pub(crate) mod mutable_ref; pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; pub(crate) mod non_exhaustive_record_expr; @@ -466,6 +467,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d), + AnyDiagnostic::MutableRefBinding(d) => handlers::mutable_ref::mutable_ref_binding(&ctx, &d), AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) { Some(it) => it, None => continue, From 766bc0d8899f88e9026a10c2ef6e7d834fc6c484 Mon Sep 17 00:00:00 2001 From: mason hall Date: Tue, 19 May 2026 12:39:18 -0400 Subject: [PATCH 052/114] remove unneeded test feature --- .../crates/ide-diagnostics/src/handlers/mutable_ref.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs index 9423f2a9aac2a..14734564b798f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs @@ -25,7 +25,6 @@ mod tests { check_diagnostics( r#" //- minicore: option -//- /main.rs #![feature(ref_pat_eat_one_layer_2024)] fn main() { let opt_ref = &Some(42); @@ -44,7 +43,6 @@ fn main() { check_diagnostics( r#" //- minicore: option -//- /main.rs #![feature(ref_pat_eat_one_layer_2024)] #![feature(mut_ref)] fn main() { From 4a116649257d93785a4bddde7c98f6e8f732eb01 Mon Sep 17 00:00:00 2001 From: mason hall Date: Tue, 19 May 2026 12:54:43 -0400 Subject: [PATCH 053/114] clearer diagnostic message, fix formatting --- .../crates/hir/src/diagnostics.rs | 2 +- .../src/handlers/mutable_ref.rs | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 62957b23a21af..497aa2ab97c69 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -131,7 +131,7 @@ diagnostics![AnyDiagnostic<'db> -> MissingMatchArms, MissingUnsafe, MovedOutOfRef<'db>, - MutableRefBinding, + MutableRefBinding, NeedMut, NonExhaustiveLet, NonExhaustiveRecordExpr, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs index 14734564b798f..b56619612d25b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutable_ref.rs @@ -10,7 +10,7 @@ pub(crate) fn mutable_ref_binding( Diagnostic::new_with_syntax_node_ptr( ctx, DiagnosticCode::RustcHardError("E0658"), - "`mut` bindings cannot also be `ref` by default in 2024 edition", + "bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality", d.pat.map(Into::into), ) .stable() @@ -26,12 +26,15 @@ mod tests { r#" //- minicore: option #![feature(ref_pat_eat_one_layer_2024)] +struct TestStruct { + val: i32 +} fn main() { - let opt_ref = &Some(42); + let opt_ref = &Some(TestStruct {val: 1}); if let Some(mut x) = opt_ref { - //^^^^^ error: `mut` bindings cannot also be `ref` by default in 2024 edition - x = &5; + //^^^^^ error: bindings cannot be both mutable and by-reference by default in 2024 edition. add experimental #![feature(mut_ref)] for this functionality + x = &TestStruct{val: 5}; } } "#, @@ -45,11 +48,14 @@ fn main() { //- minicore: option #![feature(ref_pat_eat_one_layer_2024)] #![feature(mut_ref)] +struct TestStruct { + val: i32 +} fn main() { - let opt_ref = &Some(42); + let opt_ref = &Some(TestStruct{val: 1}); if let Some(mut x) = opt_ref { - x = &5; + x = &TestStruct{val: 5}; } } "#, From 8cebbb6309c197d101a61d696f2fb94218f67cd2 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 20 May 2026 16:40:14 +0800 Subject: [PATCH 054/114] fix: do not complete semicolon in match-expr place Example --- ```rust fn foo() {} fn bar() { match fo$0 {} } ``` **Before this PR** ```rust fn foo() {} fn bar() { match foo();$0 {} } ``` **After this PR** ```rust fn foo() {} fn bar() { match foo()$0 {} } ``` --- .../crates/ide-completion/src/context.rs | 8 +++++++- .../ide-completion/src/render/function.rs | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index f7fced3f062e9..e4d599742dbe8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -858,7 +858,13 @@ impl<'a, 'db> CompletionContext<'a, 'db> { sema.token_ancestors_with_macros(token.clone()).find(|node| { matches!( node.kind(), - BLOCK_EXPR | MATCH_ARM | CLOSURE_EXPR | ARG_LIST | PAREN_EXPR | ARRAY_EXPR + BLOCK_EXPR + | MATCH_ARM + | CLOSURE_EXPR + | ARG_LIST + | PAREN_EXPR + | ARRAY_EXPR + | MATCH_EXPR ) }) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 97d5a25f493d5..c7f382033a414 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -949,6 +949,25 @@ fn foo() {} fn bar() { let _ = [foo()$0]; } +"#, + ); + } + + #[test] + fn no_semicolon_in_match() { + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + match fo$0 {} +} +"#, + r#" +fn foo() {} +fn bar() { + match foo()$0 {} +} "#, ); } From f61dcdd4bf02633ff3cee5ef1532d311570d25ff Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 17 May 2026 15:31:29 +0300 Subject: [PATCH 055/114] Do not autoref in method probe in path mode --- .../crates/hir-ty/src/method_resolution/probe.rs | 9 +++++++++ .../crates/hir-ty/src/tests/method_resolution.rs | 4 ++-- .../crates/hir-ty/src/tests/traits.rs | 10 +++++----- .../crates/ide-completion/src/tests/expression.rs | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 60344490d550a..84edb510237d6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -1295,6 +1295,15 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { return ControlFlow::Break(by_value_pick); } + if self.mode == Mode::Path { + // Don't autoref in path mode. + // rustc doesn't do that and it's not a big deal as non-autorefd methods take priority + // and if an autorefd one is selected, we'll register the `NonAutorefdT: Trait` obligation + // (which will fail) anyway. But it does have an impact when probing for all methods, + // which is something we need to stay accurate. + return ControlFlow::Continue(()); + } + let autoref_pick = self.pick_autorefd_method( step, self_ty, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 4291c9ba18dc7..2084c01853a18 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1715,8 +1715,8 @@ fn f() { 95..103 'u32::foo': fn foo() -> u8 109..115 'S::foo': fn foo() -> u8 121..127 'T::foo': fn foo() -> u8 - 133..139 'U::foo': fn foo() -> u8 - 145..157 '<[u32]>::foo': fn foo<[u32]>() -> u8 + 133..139 'U::foo': {unknown} + 145..157 '<[u32]>::foo': {unknown} "#]], ); } 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 ea978cde58c19..4eab1d6314280 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 @@ -3027,13 +3027,13 @@ fn test() { 140..146 'IsCopy': IsCopy 140..153 'IsCopy.test()': bool 159..166 'NotCopy': NotCopy - 159..173 'NotCopy.test()': bool + 159..173 'NotCopy.test()': {unknown} 179..195 '(IsCop...sCopy)': (IsCopy, IsCopy) 179..202 '(IsCop...test()': bool 180..186 'IsCopy': IsCopy 188..194 'IsCopy': IsCopy 208..225 '(IsCop...tCopy)': (IsCopy, NotCopy) - 208..232 '(IsCop...test()': bool + 208..232 '(IsCop...test()': {unknown} 209..215 'IsCopy': IsCopy 217..224 'NotCopy': NotCopy "#]], @@ -3126,7 +3126,7 @@ fn test() { 79..194 '{ ...ized }': () 85..88 '1u8': u8 85..95 '1u8.test()': bool - 101..116 '(*"foo").test()': bool + 101..116 '(*"foo").test()': {unknown} 102..108 '*"foo"': str 103..108 '"foo"': &'static str 135..145 '(1u8, 1u8)': (u8, u8) @@ -3134,7 +3134,7 @@ fn test() { 136..139 '1u8': u8 141..144 '1u8': u8 158..171 '(1u8, *"foo")': (u8, str) - 158..178 '(1u8, ...test()': bool + 158..178 '(1u8, ...test()': {unknown} 159..162 '1u8': u8 164..170 '*"foo"': str 165..170 '"foo"': &'static str @@ -4069,7 +4069,7 @@ fn f() { 212..295 '{ ...ZED; }': () 218..239 'F::Exp..._SIZED': Yes 245..266 'F::Imp..._SIZED': Yes - 272..292 'F::Rel..._SIZED': Yes + 272..292 'F::Rel..._SIZED': {unknown} "#]], ); } 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 b5465ee87a6c5..4a4b09c6585a1 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 @@ -3962,3 +3962,18 @@ fn main() { "#]], ); } + +#[test] +fn no_completion_for_autorefd_traits_in_path_mode() { + check( + r#" +//- minicore: clone +trait Test1 {} + +fn test(test: H) { + H::$0 +} + "#, + expect![""], + ); +} From ee7196da62e3052940cdfe15b35cc63d51386962 Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Wed, 20 May 2026 21:03:40 -0700 Subject: [PATCH 056/114] allow wildcard params in foreign fn declarations --- .../crates/hir-def/src/expr_store/lower.rs | 7 ++++--- .../src/handlers/pattern_arg_in_extern_fn.rs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) 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 48ccc1c0aa7fc..205c5e6dc9302 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 @@ -2570,9 +2570,9 @@ impl<'db> ExprCollector<'db> { } fn collect_extern_fn_param(&mut self, pat: Option) -> PatId { - // `extern` functions cannot have pattern-matched parameters, and furthermore, the identifiers - // in their parameters are always interpreted as bindings, even if in a normal function they - // won't be, because they would refer to a path pattern. + // parameters of functions in `extern` blocks can only be simple identifiers and wildcards. + // Furthermore, the identifiers in their parameters are always interpreted as bindings, even + // if in a normal function they won't be, because they would refer to a path pattern. let Some(pat) = pat else { return self.missing_pat() }; match &pat { @@ -2588,6 +2588,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(binding, pat); pat } + ast::Pat::WildcardPat(_) => self.collect_pat_top(Some(pat)), _ => { self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn { node: self.expander.in_file(AstPtr::new(&pat)), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs index 459ec175b158f..8a54834361a60 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs @@ -20,6 +20,24 @@ pub(crate) fn pattern_arg_in_extern_fn( mod tests { use crate::tests::check_diagnostics; + #[test] + fn ident_pattern_allowed() { + check_diagnostics( + r#" +unsafe extern { fn foo(a: i32); } + "#, + ); + } + + #[test] + fn wildcard_pattern_allowed() { + check_diagnostics( + r#" +unsafe extern { fn foo(_: i32); } + "#, + ); + } + #[test] fn tuple_pattern() { check_diagnostics( From c70dad753aba932990f547ebe0fc44377d877fb8 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 21 May 2026 03:34:25 +0800 Subject: [PATCH 057/114] feat: completions imports exclude supports sub items Example --- **Before this PR** ```json { "rust-analyzer.completion.autoimport.exclude": [ {"path":"std::intrinsics::AtomicOrdering","type":"always"}, {"path":"std::intrinsics::ConstParamTy","type":"always"}, // ... 223 items ] } ``` **After this PR** ```json { "rust-analyzer.completion.autoimport.exclude": [ {"path":"std::intrinsics","type":"subItems"}, ] } ``` --- .../src/completions/flyimport.rs | 2 +- .../crates/ide-completion/src/config.rs | 1 + .../crates/ide-completion/src/context.rs | 15 ++++ .../ide-completion/src/tests/expression.rs | 77 +++++++++++++++++++ .../crates/rust-analyzer/src/config.rs | 12 ++- .../docs/book/src/configuration_generated.md | 3 + .../rust-analyzer/editors/code/package.json | 8 +- 7 files changed, 112 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index b350647b9a2bd..77f9dda7779ec 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -389,7 +389,7 @@ fn filter_excluded_flyimport(ctx: &CompletionContext<'_, '_>, import: &LocatedIm } let method_imported = import.item_to_import != import.original_item; if method_imported - && (is_exclude_flyimport.is_some() + && (is_exclude_flyimport == Some(AutoImportExclusionType::Methods) || ctx.exclude_flyimport.contains_key(&import.original_item.into_module_def())) { // If this is a method, exclude it either if it was excluded itself (which may not be caught above, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 21f624be2cb7d..ac52c816e52a9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -44,6 +44,7 @@ pub struct CompletionConfig<'a> { pub enum AutoImportExclusionType { Always, Methods, + SubItems, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index f7fced3f062e9..8235d34a83e67 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -848,8 +848,23 @@ impl<'a, 'db> CompletionContext<'a, 'db> { .map(|it| (it.into_module_def(), *kind)) }) .collect(); + let exclude_subitems = exclude_flyimport + .iter() + .flat_map(|it| match it { + (ModuleDef::Module(module), AutoImportExclusionType::SubItems) => { + module.scope(db, None) + } + _ => vec![], + }) + .filter_map(|(_, def)| match def { + ScopeDef::ModuleDef(module_def) => Some(module_def), + _ => None, + }) + .collect::>(); exclude_flyimport .extend(exclude_traits.iter().map(|&t| (t.into(), AutoImportExclusionType::Always))); + exclude_flyimport + .extend(exclude_subitems.into_iter().map(|it| (it, AutoImportExclusionType::Always))); // FIXME: This should be part of `CompletionAnalysis` / `expand_and_analyze` let complete_semicolon = if !config.add_semicolon_to_unit { 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 e2baf42848b80..1bed2f0dcc879 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 @@ -2962,6 +2962,83 @@ fn foo() { ); } +#[test] +fn flyimport_excluded_mod_items_from_flyimport() { + check_with_config( + CompletionConfig { + exclude_flyimport: vec![( + "ra_test_fixture::pack::module2".to_owned(), + AutoImportExclusionType::SubItems, + )], + ..TEST_CONFIG + }, + r#" +mod pack { + mod module1 { + pub struct XOther; + } + pub mod module2 { + pub use super::module1::*; + pub struct XStruct; + pub fn xfn() {} + } +} + +fn foo() { + x$0 +} + "#, + expect![[r#" + ct CONST Unit + en Enum Enum + fn foo() fn() + fn function() fn() + ma makro!(…) macro_rules! makro + md module:: + md pack:: + sc STATIC Unit + st Record Record + st Tuple Tuple + st Unit Unit + un Union Union + ev TupleV(…) TupleV(u32) + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} + #[test] fn excluded_trait_method_is_excluded_from_path_completion() { check_with_config( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index f4c3f24ce6306..6d57c5535b9c8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -662,6 +662,9 @@ config_data! { /// For traits the type "methods" can be used to only exclude the methods but not the trait /// itself. /// + /// For modules the type "subItems" can be used to only exclude the sub items but not the module + /// itself. + /// /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`. completion_autoimport_exclude: Vec = vec![ AutoImportExclusion::Verbose { path: "core::borrow::Borrow".to_owned(), r#type: AutoImportExclusionType::Methods }, @@ -1938,6 +1941,9 @@ impl Config { AutoImportExclusionType::Methods => { ide_completion::AutoImportExclusionType::Methods } + AutoImportExclusionType::SubItems => { + ide_completion::AutoImportExclusionType::SubItems + } }, ), }) @@ -2997,6 +3003,7 @@ pub enum AutoImportExclusion { pub enum AutoImportExclusionType { Always, Methods, + SubItems, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -4128,10 +4135,11 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json }, "type": { "type": "string", - "enum": ["always", "methods"], + "enum": ["always", "methods", "subItems"], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions." + "Do not show this traits methods in auto-import completions.", + "Do not show this modules sub items in auto-import completions." ], }, } diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 069c8211dbb6a..87d21b7eaa0ea 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -446,6 +446,9 @@ verbose form `{ "path": "path::to::item", type: "always" }`. For traits the type "methods" can be used to only exclude the methods but not the trait itself. +For modules the type "subItems" can be used to only exclude the sub items but not the module +itself. + This setting also inherits `#rust-analyzer.completion.excludeTraits#`. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 14369e6f33e43..8c1a91836c97c 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1328,7 +1328,7 @@ "title": "Completion", "properties": { "rust-analyzer.completion.autoimport.exclude": { - "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", + "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nFor modules the type \"subItems\" can be used to only exclude the sub items but not the module\nitself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", "default": [ { "path": "core::borrow::Borrow", @@ -1355,11 +1355,13 @@ "type": "string", "enum": [ "always", - "methods" + "methods", + "subItems" ], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions." + "Do not show this traits methods in auto-import completions.", + "Do not show this modules sub items in auto-import completions." ] } } From db47597a823ebb18e09bde680933042f8094d90b Mon Sep 17 00:00:00 2001 From: Spencer Young Date: Thu, 21 May 2026 12:54:01 -0700 Subject: [PATCH 058/114] inline collect_pat_top --- src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 205c5e6dc9302..a016bfd009517 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 @@ -2588,7 +2588,7 @@ impl<'db> ExprCollector<'db> { self.add_definition_to_binding(binding, pat); pat } - ast::Pat::WildcardPat(_) => self.collect_pat_top(Some(pat)), + ast::Pat::WildcardPat(_) => self.alloc_pat(Pat::Wild, AstPtr::new(&pat)), _ => { self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn { node: self.expander.in_file(AstPtr::new(&pat)), From 2800da73145037bb34f07406d4394aeb62c83ee9 Mon Sep 17 00:00:00 2001 From: itsjunetime Date: Thu, 21 May 2026 17:59:07 -0500 Subject: [PATCH 059/114] Add format! call to print repo in error --- src/tools/rust-analyzer/xtask/src/pgo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/xtask/src/pgo.rs b/src/tools/rust-analyzer/xtask/src/pgo.rs index 0c6f499811a24..ca6dace940b54 100644 --- a/src/tools/rust-analyzer/xtask/src/pgo.rs +++ b/src/tools/rust-analyzer/xtask/src/pgo.rs @@ -91,7 +91,7 @@ fn download_crate_for_training(sh: &Shell, pgo_dir: &Path, repo: &str) -> anyhow let target_path = pgo_dir.join(normalized_path); cmd!(sh, "git clone --depth 1 https://github.com/{repo} {revision...} {target_path}") .run() - .with_context(|| "cannot download PGO training crate from {repo}")?; + .with_context(|| format!("cannot download PGO training crate from {repo}"))?; Ok(target_path) } From ded1572c36486dfca70f132cc5f12fe45ec256f3 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 22 May 2026 09:54:00 +0800 Subject: [PATCH 060/114] fix: use grouped annotation for add_label_to_loop Example --- ```rust fn main() { $0loop { break; continue; } } ``` **Before this PR** ```rust fn main() { ${1:'l}: loop { break ${2:'l}; continue ${0:'l}; } } ``` **After this PR** ```rust fn main() { ${0:'l}: loop { break ${0:'l}; continue ${0:'l}; } } ``` --- .../src/handlers/add_label_to_loop.rs | 50 +++++++++---------- .../crates/ide-assists/src/tests/generated.rs | 4 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs index b2194ab3dcd71..3c845ace6975d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -1,10 +1,8 @@ -use ide_db::{ - source_change::SourceChangeBuilder, syntax_helpers::node_ext::for_each_break_and_continue_expr, -}; +use ide_db::syntax_helpers::node_ext::for_each_break_and_continue_expr; use syntax::{ SyntaxToken, T, ast::{self, AstNode, HasLoopBody}, - syntax_editor::{Position, SyntaxEditor}, + syntax_editor::{Position, SyntaxAnnotation, SyntaxEditor}, }; use crate::{AssistContext, AssistId, Assists}; @@ -24,8 +22,8 @@ use crate::{AssistContext, AssistId, Assists}; // -> // ``` // fn main() { -// ${1:'l}: loop { -// break ${2:'l}; +// ${0:'l}: loop { +// break ${0:'l}; // continue ${0:'l}; // } // } @@ -53,8 +51,11 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ]; editor.insert_all(Position::before(&loop_kw), elements); - if let Some(cap) = ctx.config.snippet_cap { - editor.add_annotation(label.syntax(), builder.make_placeholder_snippet(cap)); + let annotation = + ctx.config.snippet_cap.map(|cap| builder.make_placeholder_snippet(cap)); + + if let Some(annotation) = annotation { + editor.add_annotation(label.syntax(), annotation); } let loop_body = loop_expr.loop_body().and_then(|it| it.stmt_list()); @@ -64,8 +65,10 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ast::Expr::ContinueExpr(continue_expr) => continue_expr.continue_token(), _ => return, }; - if let Some(token) = token { - insert_label_after_token(&editor, &token, ctx, builder); + if let Some(token) = token + && let Some(annotation) = annotation + { + insert_label_after_token(&editor, &token, annotation); } }); @@ -86,17 +89,14 @@ fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option { fn insert_label_after_token( editor: &SyntaxEditor, token: &SyntaxToken, - ctx: &AssistContext<'_, '_>, - builder: &mut SourceChangeBuilder, + annotation: SyntaxAnnotation, ) { let make = editor.make(); let label = make.lifetime("'l"); let elements = vec![make.whitespace(" ").into(), label.syntax().clone().into()]; editor.insert_all(Position::after(token), elements); - if let Some(cap) = ctx.config.snippet_cap { - editor.add_annotation(label.syntax(), builder.make_placeholder_snippet(cap)); - } + editor.add_annotation(label.syntax(), annotation); } #[cfg(test)] @@ -118,8 +118,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -139,8 +139,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: while true { - break ${2:'l}; + ${0:'l}: while true { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -160,8 +160,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: for _ in 0..5 { - break ${2:'l}; + ${0:'l}: for _ in 0..5 { + break ${0:'l}; continue ${0:'l}; } }"#, @@ -185,8 +185,8 @@ fn main() { }"#, r#" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; loop { break; @@ -217,8 +217,8 @@ fn main() { loop { break; continue; - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } } 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 d3ee35aa86940..15572e55291d6 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 @@ -204,8 +204,8 @@ fn main() { "#####, r#####" fn main() { - ${1:'l}: loop { - break ${2:'l}; + ${0:'l}: loop { + break ${0:'l}; continue ${0:'l}; } } From d1a1ffd54fee299f6a2db58fda646abdef602927 Mon Sep 17 00:00:00 2001 From: abusalah0 <109g83@gmail.com> Date: Fri, 22 May 2026 19:44:41 +0300 Subject: [PATCH 061/114] Emit diagnostic for rest array patterns without fixed-length arrays Add a new inference diagnostic for rest array patterns on arrays whose length is not known to be fixed, plumb it through hir and ide-diagnostics, and add handler tests. AI-assisted: Changes were prepared with GitHub Copilot (GPT-5.3-Codex), then reviewed and validated with local tests. --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 4 ++ .../crates/hir-ty/src/infer/pat.rs | 2 +- .../crates/hir/src/diagnostics.rs | 10 ++++ .../array_pattern_without_fixed_length.rs | 46 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 6 +++ 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs 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 bbb8c99d85592..c063d0211f156 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -297,6 +297,10 @@ pub enum InferenceDiagnostic { #[type_visitable(ignore)] has_rest: bool, }, + ArrayPatternWithoutFixedLength { + #[type_visitable(ignore)] + pat: PatId, + }, ExpectedArrayOrSlicePat { #[type_visitable(ignore)] pat: PatId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 8703b85e33632..f1af8a0b73a5b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -1701,7 +1701,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; // We have a variable-length pattern and don't know the array length. // This happens if we have e.g., // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. - // FIXME: Emit an error: cannot pattern-match on an array without a fixed length. + self.push_diagnostic(InferenceDiagnostic::ArrayPatternWithoutFixedLength { pat }); }; // If we get here, we must have emitted an error. diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index d482135812a98..2d2883eb60e14 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -101,6 +101,7 @@ macro_rules! diagnostics { } diagnostics![AnyDiagnostic<'db> -> + ArrayPatternWithoutFixedLength, AwaitOutsideOfAsync, BreakOutsideOfLoop, CannotBeDereferenced<'db>, @@ -306,6 +307,11 @@ pub struct MismatchedArrayPatLen { pub has_rest: bool, } +#[derive(Debug)] +pub struct ArrayPatternWithoutFixedLength { + pub pat: InFile, +} + #[derive(Debug)] pub struct ExpectedArrayOrSlicePat<'db> { pub pat: InFile, @@ -836,6 +842,10 @@ impl<'db> AnyDiagnostic<'db> { let pat = pat_syntax(pat)?.map(Into::into); MismatchedArrayPatLen { pat, expected, found, has_rest }.into() } + &InferenceDiagnostic::ArrayPatternWithoutFixedLength { pat } => { + let pat = pat_syntax(pat)?.map(Into::into); + ArrayPatternWithoutFixedLength { pat }.into() + } InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => { let pat = pat_syntax(*pat)?.map(Into::into); ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs new file mode 100644 index 0000000000000..e7d0868350f31 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/array_pattern_without_fixed_length.rs @@ -0,0 +1,46 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: array-pattern-without-fixed-length +// +// This diagnostic is triggered when a rest array pattern is matched against an +// array with a non-constant length. +pub(crate) fn array_pattern_without_fixed_length( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::ArrayPatternWithoutFixedLength, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0730"), + "cannot pattern-match on an array without a fixed length", + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn array_pattern_without_fixed_length() { + check_diagnostics( + r#" +fn f(arr: [u8; N]) { + let [_head, _tail @ ..] = arr; + //^^^^^^^^^^^^^^^^^^^ error: cannot pattern-match on an array without a fixed length +} +"#, + ); + } + + #[test] + fn fixed_length_array_pattern_is_ok() { + check_diagnostics( + r#" +fn f(arr: [u8; 3]) { + let [_head, _tail @ ..] = arr; +} +"#, + ); + } +} 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 9639772094c13..18251bc8a2dda 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -29,6 +29,7 @@ extern crate rustc_driver as _; mod handlers { + pub(crate) mod array_pattern_without_fixed_length; pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; @@ -437,6 +438,11 @@ pub fn semantic_diagnostics( AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), + AnyDiagnostic::ArrayPatternWithoutFixedLength(d) => { + handlers::array_pattern_without_fixed_length::array_pattern_without_fixed_length( + &ctx, &d, + ) + } AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::FunctionalRecordUpdateOnNonStruct(d) => handlers::functional_record_update_on_non_struct::functional_record_update_on_non_struct(&ctx, &d), From c30939598706342118dd467fc5240681a7ad7cc4 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 23 May 2026 13:44:06 +0800 Subject: [PATCH 062/114] Remove redundant rename --- .../crates/ide-assists/src/handlers/add_label_to_loop.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs index 3c845ace6975d..e7a2b1345241c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_label_to_loop.rs @@ -73,7 +73,6 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) }); builder.add_file_edits(ctx.vfs_file_id(), editor); - builder.rename(); }, ) } From 57aa06092728e95f0c201cc6245217837f9b05c6 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 23 May 2026 14:46:59 +0800 Subject: [PATCH 063/114] fix: normalize expected tuple struct pat field Example --- ```rust struct Foo(T); fn foo(x: Foo>) -> Foo { match x { Foo($0) => () } } ``` **Before this PR** ```rust ty: T, name: ? ``` **After this PR** ```rust ty: Option, name: ? ``` --- .../crates/ide-completion/src/context/analysis.rs | 8 ++------ .../crates/ide-completion/src/context/tests.rs | 11 +++++++++++ 2 files changed, 13 insertions(+), 6 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 6be9813619293..4fb8248e780b3 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 @@ -791,13 +791,9 @@ fn expected_type_and_name<'db>( (ty, None) }, ast::TupleStructPat(it) => { - let fields = it.path().and_then(|path| match sema.resolve_path(&path)? { - hir::PathResolution::Def(hir::ModuleDef::Adt(adt)) => Some(adt.as_struct()?.fields(sema.db)), - hir::PathResolution::Def(hir::ModuleDef::EnumVariant(variant)) => Some(variant.fields(sema.db)), - _ => None, - }); + let fields = sema.type_of_pat(&it.clone().into()).map(|ty| ty.original.fields(sema.db)); let nr = it.fields().take_while(|it| it.syntax().text_range().end() <= token.text_range().start()).count(); - let ty = fields.and_then(|fields| Some(fields.get(nr)?.ty(sema.db).to_type(sema.db))); + let ty = fields.and_then(|fields| Some(fields.get(nr)?.1.clone())); (ty, None) }, ast::Fn(it) => { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 94d904932ac56..706e8ea3c0c1f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -329,6 +329,17 @@ fn foo(x: Foo) -> Foo { "#, expect![[r#"ty: u32, name: ?"#]], ); + + check_expected_type_and_name( + r#" +//- minicore: option +struct Foo(T); +fn foo(x: Foo>) -> Foo { + match x { Foo($0) => () } +} +"#, + expect![[r#"ty: Option, name: ?"#]], + ); } #[test] From bb63b77f2bd037da7a3951e30c800d5738810eb5 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Sat, 23 May 2026 15:50:23 +0800 Subject: [PATCH 064/114] fix(cfg): correct separator index in CfgDiff disable loop --- src/tools/rust-analyzer/crates/cfg/src/lib.rs | 2 +- src/tools/rust-analyzer/crates/cfg/src/tests.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs index 3e3d67cb4aaf3..5923849d62c22 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs @@ -222,7 +222,7 @@ impl fmt::Display for CfgDiff { for (i, atom) in self.disable.iter().enumerate() { let sep = match i { 0 => "", - _ if i == self.enable.len() - 1 => " and ", + _ if i == self.disable.len() - 1 => " and ", _ => ", ", }; f.write_str(sep)?; diff --git a/src/tools/rust-analyzer/crates/cfg/src/tests.rs b/src/tools/rust-analyzer/crates/cfg/src/tests.rs index bfc9220a05d9b..45cba042b33be 100644 --- a/src/tools/rust-analyzer/crates/cfg/src/tests.rs +++ b/src/tools/rust-analyzer/crates/cfg/src/tests.rs @@ -194,6 +194,10 @@ fn hints() { check_enable_hints("#![cfg(test)]", &opts, &[]); check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]); + + opts.insert_atom(Symbol::intern("a")); + opts.insert_atom(Symbol::intern("b")); + check_enable_hints("#![cfg(all(not(a), not(b)))]", &opts, &["disable a and b"]); } /// Tests that we don't suggest hints for cfgs that express an inconsistent formula. From e61601c9317321d5a34a5ed30ca46d87e6e964f3 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Sat, 23 May 2026 16:03:29 +0800 Subject: [PATCH 065/114] fix(test-utils): drain inactive_regions by inactive_line_region --- src/tools/rust-analyzer/crates/test-utils/src/fixture.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index 1f6262c897c7f..730e9aeb52946 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -507,7 +507,7 @@ impl MiniCore { active_regions.drain(active_regions.len() - active_line_region..); } if inactive_line_region > 0 { - inactive_regions.drain(inactive_regions.len() - active_line_region..); + inactive_regions.drain(inactive_regions.len() - inactive_line_region..); } } From e8ae147c827132a16d0f0fb8dd390aa8221346d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sat, 23 May 2026 12:39:02 +0300 Subject: [PATCH 066/114] Actually ignore workspace/diagnostic/refresh --- .../rust-analyzer/tests/slow-tests/support.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs index e2304c3d716c9..2c4b978a4d732 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs @@ -372,18 +372,15 @@ impl Server { Message::Request(req) => { if req.method == "client/registerCapability" { let params = req.params.to_string(); - if [ - "workspace/diagnostic/refresh", - "workspace/didChangeWatchedFiles", - "textDocument/didSave", - ] - .into_iter() - .any(|it| params.contains(it)) + if ["workspace/didChangeWatchedFiles", "textDocument/didSave"] + .into_iter() + .any(|it| params.contains(it)) { continue; } + } else if req.method != "workspace/diagnostic/refresh" { + panic!("unexpected request: {req:?}") } - panic!("unexpected request: {req:?}") } Message::Notification(_) => (), Message::Response(res) => { From 5f790401e80175fd330e228cee4b0a606dc93889 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Sat, 23 May 2026 17:42:38 +0800 Subject: [PATCH 067/114] fix(hir-ty): saturate float-to-uint cast in const eval --- src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs | 3 +++ src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index f15866106928b..5421b97db2386 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -287,6 +287,9 @@ fn floating_point_casts() { check_number(r#"const GOAL: i8 = (0./0.) as i8"#, 0); check_number(r#"const GOAL: i8 = (1./0.) as i8"#, 127); check_number(r#"const GOAL: i8 = (-1./0.) as i8"#, -128); + check_number(r#"const GOAL: u8 = (1./0.) as u8"#, 255); + check_number(r#"const GOAL: u8 = 256.0f32 as u8"#, 255); + check_number(r#"const GOAL: u16 = 1e10f32 as u16"#, 65535); check_number(r#"const GOAL: i64 = 1e18f64 as f32 as i64"#, 999999984306749440); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index a8879521eba67..97aeb7d41260a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -1594,7 +1594,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let max = 1i128 << (dest_bits - 1); (max - 1, -max) } else { - (1i128 << dest_bits, 0) + ((1i128 << dest_bits) - 1, 0) }; let value = (value as i128).min(max).max(min); let result = value.to_le_bytes(); From b9dae9ff887f21e2f7d536cd95a26cb2e0ca2adf Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Sat, 23 May 2026 09:40:19 -0400 Subject: [PATCH 068/114] Filter package-scoped features --- .../crates/rust-analyzer/src/flycheck.rs | 38 ++++++++++++++++--- .../crates/rust-analyzer/src/test_runner.rs | 2 +- 2 files changed, 34 insertions(+), 6 deletions(-) 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 aad8bece95546..3c9a8c147d925 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -63,7 +63,12 @@ pub(crate) enum Target { } impl CargoOptions { - pub(crate) fn apply_on_command(&self, cmd: &mut Command, ws_target_dir: Option<&Utf8Path>) { + pub(crate) fn apply_on_command( + &self, + cmd: &mut Command, + ws_target_dir: Option<&Utf8Path>, + package_repr: Option<&str>, + ) { for target in &self.target_tuples { cmd.args(["--target", target.as_str()]); } @@ -83,8 +88,24 @@ impl CargoOptions { cmd.arg("--no-default-features"); } if !self.features.is_empty() { + // If we are scoped to a particular package, filter any features of the form + // `crate/feature` which target other packages. + let features = if let Some(name) = package_repr { + let filtered = self + .features + .iter() + .filter(|f| match f.split_once('/') { + Some((c, _)) => c == name, + None => true, + }) + .map(|s| s.as_str()) + .collect::>(); + filtered.join(" ") + } else { + self.features.join(" ") + }; cmd.arg("--features"); - cmd.arg(self.features.join(" ")); + cmd.arg(features); } } if let Some(target_dir) = self.target_dir_config.target_dir(ws_target_dir) { @@ -890,12 +911,18 @@ impl FlycheckActor { cmd.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); cmd.arg(&cargo_options.subcommand); - match scope { - FlycheckScope::Workspace => cmd.arg("--workspace"), + let package_repr = match scope { + FlycheckScope::Workspace => { + cmd.arg("--workspace"); + None + } FlycheckScope::Package { package: PackageSpecifier::Cargo { package_id }, .. - } => cmd.arg("-p").arg(&package_id.repr), + } => { + cmd.arg("-p").arg(&package_id.repr); + Some(package_id.repr.as_str()) + } FlycheckScope::Package { package: PackageSpecifier::BuildInfo { .. }, .. } => { @@ -935,6 +962,7 @@ impl FlycheckActor { cargo_options.apply_on_command( &mut cmd, self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), + package_repr, ); cmd.args(&cargo_options.extra_args); Some((cmd, FlycheckCommandOrigin::Cargo)) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 0d9c8310d8587..31f35df5c796d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -124,7 +124,7 @@ impl CargoTestHandle { cmd.arg("--no-fail-fast"); cmd.arg("--manifest-path"); cmd.arg(root.join("Cargo.toml")); - options.apply_on_command(&mut cmd, ws_target_dir); + options.apply_on_command(&mut cmd, ws_target_dir, Some(&test_target.package)); cmd.arg("--"); if let Some(path) = path { cmd.arg(path); From 720536432bde5dc44a223eb098096185cdef6e35 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 1 May 2026 12:57:23 +0300 Subject: [PATCH 069/114] Make `trait_environment()` take `GenericDefId` and not `ExpressionStoreOwnedId` A `ParamEnv` is fundamentally associated with generic arguments and not with a body. It feels wrong to attach it to `ExpressionStoreOwnedId`. There is one exception: a defaulted trait method with RPITIT has a different ParamEnv inside its body than for its callers, since inside the body the RPITIT is interpreted as an RPIT equating the internal assoc type. We don't yet handle RPITIT, but even when we'll do I don't feel it's justifies treating *any* body as different. --- .../crates/hir-ty/src/consteval.rs | 27 ++++++++----------- .../rust-analyzer/crates/hir-ty/src/db.rs | 2 +- .../crates/hir-ty/src/diagnostics/expr.rs | 2 +- .../crates/hir-ty/src/display.rs | 8 ++---- .../rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- .../crates/hir-ty/src/layout/tests.rs | 16 +++++------ .../rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 7 +---- .../crates/hir-ty/src/mir/borrowck.rs | 2 +- .../crates/hir-ty/src/mir/eval.rs | 2 +- .../crates/hir-ty/src/mir/eval/tests.rs | 4 +-- .../crates/hir-ty/src/mir/lower.rs | 4 +-- .../crates/hir-ty/src/opaques.rs | 7 +++-- .../crates/hir-ty/src/specialization.rs | 9 ++----- src/tools/rust-analyzer/crates/hir/src/lib.rs | 11 +++++--- .../crates/hir/src/source_analyzer.rs | 19 ++++++++----- .../rust-analyzer/src/cli/analysis_stats.rs | 2 +- 17 files changed, 56 insertions(+), 70 deletions(-) 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 fb52813c52ffa..2c43feeb3b1a0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -5,7 +5,7 @@ mod tests; use base_db::Crate; use hir_def::{ - ConstId, EnumVariantId, ExpressionStoreOwnerId, GenericDefId, HasModule, StaticId, + ConstId, EnumVariantId, ExpressionStoreOwnerId, HasModule, StaticId, attrs::AttrFlags, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{Expr, ExprId, Literal}, @@ -422,8 +422,11 @@ pub(crate) fn const_eval_discriminant_variant( let mir_body = db.monomorphized_mir_body( def.into(), GenericArgs::empty(interner).store(), - ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } - .store(), + ParamEnvAndCrate { + param_env: db.trait_environment(def.generic_def(db)), + krate: def.krate(db), + } + .store(), )?; let c = interpret_mir(db, mir_body, false, None)?.0?; let c = if is_signed { allocation_as_isize(c) } else { allocation_as_usize(c) as i128 }; @@ -459,12 +462,8 @@ pub(crate) fn const_eval<'db>( let body = db.monomorphized_mir_body( def.into(), subst, - ParamEnvAndCrate { - param_env: db - .trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(def))), - krate: def.krate(db), - } - .store(), + ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } + .store(), )?; let c = interpret_mir(db, body, false, trait_env.as_ref().map(|env| env.as_ref()))?.0?; Ok(c.store()) @@ -503,7 +502,7 @@ pub(crate) fn anon_const_eval<'db>( def.into(), subst, ParamEnvAndCrate { - param_env: db.trait_environment(def.loc(db).owner), + param_env: db.trait_environment(def.loc(db).owner.generic_def(db)), krate: def.krate(db), } .store(), @@ -541,12 +540,8 @@ pub(crate) fn const_eval_static<'db>( let body = db.monomorphized_mir_body( def.into(), GenericArgs::empty(interner).store(), - ParamEnvAndCrate { - param_env: db - .trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(def))), - krate: def.krate(db), - } - .store(), + ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) } + .store(), )?; let c = interpret_mir(db, body, false, None)?.0?; Ok(c.store()) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index c24a5b943db08..511ab856107f8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -247,7 +247,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::lower::trait_environment)] #[salsa::transparent] - fn trait_environment<'db>(&'db self, def: ExpressionStoreOwnerId) -> ParamEnv<'db>; + fn trait_environment<'db>(&'db self, def: GenericDefId) -> ParamEnv<'db>; #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics)] #[salsa::transparent] 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 760ebd27e0573..be4de11ceb36b 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 @@ -83,7 +83,7 @@ impl<'db> BodyValidationDiagnostic<'db> { let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered(); let infer = InferenceResult::of(db, owner); let body = Body::of(db, owner); - let env = db.trait_environment(owner.into()); + let env = db.trait_environment(owner.generic_def(db)); let interner = DbInterner::new_with(db, owner.krate(db)); let infcx = interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, owner.into())); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 0acf6e63d782f..8edb178cd74f7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -968,9 +968,7 @@ fn render_const_scalar_inner<'db>( s.fields(f.db), f, field_types, - f.db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - def, - ))), + f.db.trait_environment(def.into()), &layout, args, b, @@ -996,9 +994,7 @@ fn render_const_scalar_inner<'db>( var_id.fields(f.db), f, field_types, - f.db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - def, - ))), + f.db.trait_environment(def.into()), var_layout, args, b, 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 c063d0211f156..7e5fdfdd81c6b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1331,7 +1331,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { resolver: Resolver<'db>, allow_using_generic_params: bool, ) -> Self { - let trait_env = db.trait_environment(store_owner); + let trait_env = db.trait_environment(generic_def); let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner); let types = crate::next_solver::default_types(db); InferenceContext { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index b42ac54f130a6..c12fbeccdb19b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -1,7 +1,7 @@ use base_db::target::TargetData; use either::Either; use hir_def::{ - DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, HasModule, + DefWithBodyId, HasModule, expr_store::Body, signatures::{ EnumSignature, FunctionSignature, StructSignature, TypeAliasSignature, UnionSignature, @@ -92,13 +92,10 @@ fn eval_goal( ), Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity().skip_norm_wip(), }; - let param_env = db.trait_environment( - match adt_or_type_alias_id { - Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), - Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), - } - .into(), - ); + let param_env = db.trait_environment(match adt_or_type_alias_id { + Either::Left(adt) => hir_def::GenericDefId::AdtId(adt), + Either::Right(ty) => hir_def::GenericDefId::TypeAliasId(ty), + }); let krate = match adt_or_type_alias_id { Either::Left(it) => it.krate(&db), Either::Right(it) => it.krate(&db), @@ -145,8 +142,7 @@ fn eval_expr( .0; let infer = InferenceResult::of(&db, DefWithBodyId::from(function_id)); let goal_ty = infer.type_of_binding[b].clone(); - let param_env = - db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(function_id))); + let param_env = db.trait_environment(function_id.into()); let krate = function_id.krate(&db); db.layout_of_ty(goal_ty, ParamEnvAndCrate { param_env, krate }.store()) }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 1900a41f7f175..f612bdc266970 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -385,7 +385,7 @@ pub fn associated_type_shorthand_candidates( let mut dedup_map = FxHashSet::default(); let param_ty = Ty::new_param(interner, param, type_or_const_param_idx(db, param.into())); // We use the ParamEnv and not the predicates because the ParamEnv elaborates bounds. - let param_env = db.trait_environment(ExpressionStoreOwnerId::from(def)); + let param_env = db.trait_environment(def); for clause in param_env.clauses { let ClauseKind::Trait(trait_clause) = clause.kind().skip_binder() else { continue }; if trait_clause.self_ty() != param_ty { 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 8beaa481b5280..df83b2abb870f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -2240,12 +2240,7 @@ pub(crate) fn param_env_from_predicates<'db>( ParamEnv { clauses } } -pub(crate) fn trait_environment<'db>( - db: &'db dyn HirDatabase, - def: ExpressionStoreOwnerId, -) -> ParamEnv<'db> { - let def = def.generic_def(db); - +pub(crate) fn trait_environment<'db>(db: &'db dyn HirDatabase, def: GenericDefId) -> ParamEnv<'db> { return ParamEnv { clauses: trait_environment_query(db, def).as_ref() }; #[salsa::tracked(returns(ref))] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 940bc572595e5..bc158ac884e9d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -141,7 +141,7 @@ pub fn borrowck_query( let _p = tracing::info_span!("borrowck_query").entered(); let module = def.module(db); let interner = DbInterner::new_with(db, module.krate(db)); - let env = db.trait_environment(def.expression_store_owner(db)); + let env = db.trait_environment(def.generic_def(db)); // This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`. let typing_mode = TypingMode::borrowck(interner, def.into()); let res = all_mir_bodies( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 97aeb7d41260a..1ee18e09ba899 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -685,7 +685,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { db, random_state: oorandom::Rand64::new(0), param_env: trait_env.unwrap_or_else(|| ParamEnvAndCrate { - param_env: db.trait_environment(owner.expression_store_owner(db)), + param_env: db.trait_environment(owner.generic_def(db)), krate: crate_id, }), crate_id, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 6bf966c3ef1d3..0e94a5b92dd1c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -1,4 +1,4 @@ -use hir_def::{GenericDefId, HasModule, signatures::FunctionSignature}; +use hir_def::{HasModule, signatures::FunctionSignature}; use hir_expand::EditionedFileId; use span::Edition; use syntax::{TextRange, TextSize}; @@ -41,7 +41,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), func_id.into(), GenericArgs::empty(interner).store(), crate::ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(func_id).into()), + param_env: db.trait_environment(func_id.into()), krate: func_id.krate(db), } .store(), 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 025aff9307e15..68612c2ce2fec 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 @@ -314,8 +314,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { closures: vec![], }; let store_owner = owner.expression_store_owner(db); - let resolver = store_owner.resolver(db); - let env = db.trait_environment(store_owner); + let resolver = owner.resolver(db); + let env = db.trait_environment(owner.generic_def(db)); let interner = DbInterner::new_with(db, resolver.krate()); // FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body? let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs index 79d2fa0c2d1c2..dfd1fd96c6564 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/opaques.rs @@ -1,8 +1,8 @@ //! Handling of opaque types, detection of defining scope and hidden type. use hir_def::{ - AssocItemId, AssocItemLoc, DefWithBodyId, ExpressionStoreOwnerId, FunctionId, GenericDefId, - HasModule, ItemContainerId, TypeAliasId, signatures::ImplSignature, + AssocItemId, AssocItemLoc, DefWithBodyId, FunctionId, HasModule, ItemContainerId, TypeAliasId, + signatures::ImplSignature, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -129,8 +129,7 @@ pub(crate) fn tait_hidden_types( let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); let mut ocx = ObligationCtxt::new(&infcx); let cause = ObligationCause::dummy(); - let param_env = - db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from(type_alias))); + let param_env = db.trait_environment(type_alias.into()); let defining_bodies = tait_defining_bodies(db, loc); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs index 467b598447d6e..2d206fe380023 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/specialization.rs @@ -1,9 +1,6 @@ //! Impl specialization related things -use hir_def::{ - ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, signatures::ImplSignature, - unstable_features::UnstableFeatures, -}; +use hir_def::{HasModule, ImplId, signatures::ImplSignature, unstable_features::UnstableFeatures}; use tracing::debug; use crate::{ @@ -48,9 +45,7 @@ fn specializes_query( specializing_impl_def_id: ImplId, parent_impl_def_id: ImplId, ) -> bool { - let trait_env = db.trait_environment(ExpressionStoreOwnerId::from(GenericDefId::from( - specializing_impl_def_id, - ))); + let trait_env = db.trait_environment(specializing_impl_def_id.into()); let interner = DbInterner::new_with(db, specializing_impl_def_id.krate(db)); let specializing_impl_signature = ImplSignature::of(db, specializing_impl_def_id); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 768ffd569789e..02800fda98367 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2927,7 +2927,7 @@ impl Function { id.into(), GenericArgs::empty(interner).store(), ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(id).into()), + param_env: db.trait_environment(id.into()), krate: id.module(db).krate(db), } .store(), @@ -7456,7 +7456,7 @@ fn param_env_from_resolver<'db>( ParamEnvAndCrate { param_env: resolver.generic_def().map_or_else( || ParamEnv::empty(DbInterner::new_no_crate(db)), - |generic_def| db.trait_environment(generic_def.into()), + |generic_def| db.trait_environment(generic_def), ), krate: resolver.krate(), } @@ -7466,14 +7466,17 @@ fn param_env_from_has_crate<'db>( db: &'db dyn HirDatabase, id: impl hir_def::HasModule + Into + Copy, ) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { param_env: db.trait_environment(id.into().into()), krate: id.krate(db) } + ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.krate(db) } } fn body_param_env_from_has_crate<'db>( db: &'db dyn HirDatabase, id: impl hir_def::HasModule + Into + Copy, ) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.krate(db) } + ParamEnvAndCrate { + param_env: db.trait_environment(id.into().generic_def(db)), + krate: id.krate(db), + } } // FIXME: We probably don't want to expose this. 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 ba62cc11c3c06..3afa98365e18b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -332,10 +332,17 @@ impl<'db> SourceAnalyzer<'db> { fn trait_environment(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> { self.param_and(self.body_or_sig.as_ref().map_or_else( || ParamEnv::empty(DbInterner::new_no_crate(db)), - |body_or_sig| match *body_or_sig { - BodyOrSig::Body { def, .. } => db.trait_environment(def.into()), - BodyOrSig::VariantFields { def, .. } => db.trait_environment(def.into()), - BodyOrSig::Sig { def, .. } => db.trait_environment(def.into()), + |body_or_sig| { + let def = match *body_or_sig { + BodyOrSig::Body { def, .. } => def.generic_def(db), + BodyOrSig::VariantFields { def, .. } => match def { + VariantId::EnumVariantId(def) => def.loc(db).parent.into(), + VariantId::StructId(def) => def.into(), + VariantId::UnionId(def) => def.into(), + }, + BodyOrSig::Sig { def, .. } => def, + }; + db.trait_environment(def) }, )) } @@ -1590,7 +1597,7 @@ impl<'db> SourceAnalyzer<'db> { func: FunctionId, substs: GenericArgs<'db>, ) -> (Function, GenericArgs<'db>) { - let owner = match self.resolver.expression_store_owner() { + let owner = match self.resolver.generic_def() { Some(it) => it, None => return (func.into(), substs), }; @@ -1610,7 +1617,7 @@ impl<'db> SourceAnalyzer<'db> { const_id: ConstId, subs: GenericArgs<'db>, ) -> (ConstId, GenericArgs<'db>) { - let owner = match self.resolver.expression_store_owner() { + let owner = match self.resolver.generic_def() { Some(it) => it, None => return (const_id, subs), }; 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 61e0b1e611cfa..ec5503fe39f5b 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 @@ -415,7 +415,7 @@ impl flags::AnalysisStats { hir_def::AdtId::from(a), GenericArgs::empty(interner).store(), hir_ty::ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(a).into()), + param_env: db.trait_environment(a.into()), krate: a.krate(db).into(), } .store(), From 1775ddc5b5414007c2419493103967c00b7654a5 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 3 May 2026 04:54:23 +0300 Subject: [PATCH 070/114] Refactor handling of generic params in `hir::Type` The essence of the new handling is that `hir::Type` now remembers its owner (and not `ParamEnv`; we could remember both but that is unnecessary) and refuse (panics) to work with types not from the same owner. It would be best if we could enforce this statically, but unfortunately we can't. --- .../rust-analyzer/crates/hir-ty/src/traits.rs | 6 +- .../rust-analyzer/crates/hir/src/attrs.rs | 15 +- .../crates/hir/src/diagnostics.rs | 61 +- .../rust-analyzer/crates/hir/src/display.rs | 12 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 1353 ++++++++--------- .../rust-analyzer/crates/hir/src/semantics.rs | 10 +- .../crates/hir/src/source_analyzer.rs | 120 +- .../crates/hir/src/term_search/expr.rs | 17 +- .../crates/hir/src/term_search/tactics.rs | 87 +- .../src/handlers/add_missing_match_arms.rs | 29 +- .../ide-assists/src/handlers/auto_import.rs | 2 +- .../src/handlers/expand_rest_pattern.rs | 8 +- .../handlers/generate_from_impl_for_enum.rs | 7 +- .../generate_single_field_struct_from.rs | 7 +- .../src/handlers/replace_method_eager_lazy.rs | 13 +- .../src/handlers/wrap_return_type.rs | 4 +- .../crates/ide-completion/src/completions.rs | 6 +- .../ide-completion/src/completions/expr.rs | 3 + .../ide-completion/src/completions/record.rs | 12 +- .../crates/ide-completion/src/context.rs | 7 + .../ide-completion/src/context/analysis.rs | 16 +- .../crates/ide-completion/src/render.rs | 65 +- .../ide-completion/src/render/function.rs | 7 +- .../ide-completion/src/render/literal.rs | 2 +- .../ide-completion/src/render/pattern.rs | 7 +- .../ide-completion/src/render/variant.rs | 2 +- .../ide-completion/src/tests/expression.rs | 4 +- .../ide-completion/src/tests/predicate.rs | 4 +- .../ide-completion/src/tests/type_pos.rs | 14 +- .../ide-db/src/syntax_helpers/suggest_name.rs | 4 +- .../src/handlers/type_mismatch.rs | 4 +- .../crates/ide/src/goto_type_definition.rs | 2 +- .../rust-analyzer/crates/ide/src/hover.rs | 4 +- .../crates/ide/src/hover/render.rs | 16 +- .../rust-analyzer/crates/ide/src/rename.rs | 4 +- .../crates/ide/src/signature_help.rs | 2 +- .../crates/ide/src/view_memory_layout.rs | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 6 +- 38 files changed, 950 insertions(+), 994 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 9582f2ceba831..f6b5adfb6fffc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -121,7 +121,7 @@ pub fn implements_trait_unique<'db>( env: ParamEnvAndCrate<'db>, trait_: TraitId, ) -> bool { - implements_trait_unique_impl(db, env, trait_, &mut |infcx| { + implements_trait_unique_with_infcx(db, env, trait_, &mut |infcx| { infcx.fill_rest_fresh_args(Span::Dummy, trait_.into(), [ty.into()]) }) } @@ -133,10 +133,10 @@ pub fn implements_trait_unique_with_args<'db>( trait_: TraitId, args: GenericArgs<'db>, ) -> bool { - implements_trait_unique_impl(db, env, trait_, &mut |_| args) + implements_trait_unique_with_infcx(db, env, trait_, &mut |_| args) } -fn implements_trait_unique_impl<'db>( +pub fn implements_trait_unique_with_infcx<'db>( db: &'db dyn HirDatabase, env: ParamEnvAndCrate<'db>, trait_: TraitId, diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs index f9cf05e73a103..9a61885ccb701 100644 --- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs @@ -26,8 +26,8 @@ use stdx::never; use crate::{ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, EnumVariant, - ExternCrateDecl, Field, Function, GenericParam, HasCrate, Impl, LangItem, LifetimeParam, Macro, - Module, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, + ExternCrateDecl, Field, Function, GenericParam, Impl, LangItem, LifetimeParam, Macro, Module, + ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, }; #[derive(Debug, Clone, Copy)] @@ -487,27 +487,28 @@ fn resolve_impl_trait_item<'db>( ns: Option, ) -> Option { let krate = ty.krate(db); - let environment = crate::param_env_from_resolver(db, &resolver); + let param_env = ty.param_env(db); let traits_in_scope = resolver.traits_in_scope(db); // `ty.iterate_path_candidates()` require a scope, which is not available when resolving // attributes here. Use path resolution directly instead. // // FIXME: resolve type aliases (which are not yielded by iterate_path_candidates) - let interner = DbInterner::new_with(db, environment.krate); + let interner = DbInterner::new_with(db, param_env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); let features = resolver.top_level_def_map().features(); let ctx = MethodResolutionContext { infcx: &infcx, resolver: &resolver, - param_env: environment.param_env, + param_env: param_env.param_env, traits_in_scope: &traits_in_scope, - edition: krate.edition(db), + edition: krate.data(db).edition, features, call_span: hir_ty::Span::Dummy, receiver_span: hir_ty::Span::Dummy, }; - let resolution = ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty); + let resolution = + ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty.skip_binder()); let resolution = match resolution { Ok(resolution) => resolution.item, Err(MethodError::PrivateMatch(resolution)) => resolution.item, diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 2d2883eb60e14..f5b1463f6ffb6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -16,12 +16,11 @@ use hir_def::{ use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name}; use hir_ty::{ CastError, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceTyDiagnosticSource, - ParamEnvAndCrate, PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, - TyLoweringDiagnosticKind, + PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind, db::HirDatabase, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, display::{DisplayTarget, HirDisplay}, - next_solver::DbInterner, + next_solver::{DbInterner, EarlyBinder}, solver_errors::SolverDiagnosticKind, }; use stdx::{impl_from, never}; @@ -32,7 +31,7 @@ use syntax::{ }; use triomphe::Arc; -use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, Variant}; +use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, TypeOwnerId, Variant}; pub use hir_def::VariantId; pub use hir_ty::{ @@ -791,7 +790,7 @@ impl<'db> AnyDiagnostic<'db> { d: &'db InferenceDiagnostic, source_map: &hir_def::expr_store::BodySourceMap, sig_map: &hir_def::expr_store::ExpressionStoreSourceMap, - env: ParamEnvAndCrate<'db>, + type_owner: TypeOwnerId, ) -> Option> { let expr_syntax = |expr| { source_map @@ -815,6 +814,7 @@ impl<'db> AnyDiagnostic<'db> { ExprOrPatId::ExprId(expr) => expr_syntax(expr), ExprOrPatId::PatId(pat) => pat_syntax(pat), }; + let new_ty = |ty| Type { owner: type_owner, ty: EarlyBinder::bind(ty) }; let span_syntax = |span| match span { hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()), hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()), @@ -848,7 +848,11 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => { let pat = pat_syntax(*pat)?.map(Into::into); - ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into() + ExpectedArrayOrSlicePat { + pat, + found: Type { owner: type_owner, ty: EarlyBinder::bind(found.as_ref()) }, + } + .into() } &InferenceDiagnostic::InvalidRangePatType { pat } => { let pat = pat_syntax(pat)?.map(Into::into); @@ -878,8 +882,7 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::ExpectedFunction { call_expr, found } => { let call_expr = expr_syntax(*call_expr)?; - ExpectedFunction { call: call_expr, found: Type::new(db, def, found.as_ref()) } - .into() + ExpectedFunction { call: call_expr, found: new_ty(found.as_ref()) }.into() } InferenceDiagnostic::UnresolvedField { expr, @@ -891,7 +894,7 @@ impl<'db> AnyDiagnostic<'db> { UnresolvedField { expr, name: name.clone(), - receiver: Type::new(db, def, receiver.as_ref()), + receiver: new_ty(receiver.as_ref()), method_with_same_name_exists: *method_with_same_name_exists, } .into() @@ -907,10 +910,10 @@ impl<'db> AnyDiagnostic<'db> { UnresolvedMethodCall { expr, name: name.clone(), - receiver: Type::new(db, def, receiver.as_ref()), + receiver: new_ty(receiver.as_ref()), field_with_same_name: field_with_same_name .as_ref() - .map(|ty| Type::new(db, def, ty.as_ref())), + .map(|ty| new_ty(ty.as_ref())), assoc_func_with_same_name: assoc_func_with_same_name.map(Into::into), } .into() @@ -947,7 +950,7 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::TypedHole { expr, expected } => { let expr = expr_syntax(*expr)?; - TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into() + TypedHole { expr, expected: new_ty(expected.as_ref()) }.into() } &InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => { let InFile { file_id, value } = pat_syntax(pat)?; @@ -958,21 +961,21 @@ impl<'db> AnyDiagnostic<'db> { } InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { let expr = expr_syntax(*expr)?; - CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.as_ref()) }.into() + CastToUnsized { expr, cast_ty: new_ty(cast_ty.as_ref()) }.into() } InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => { let expr = expr_syntax(*expr)?; - let expr_ty = Type::new(db, def, expr_ty.as_ref()); - let cast_ty = Type::new(db, def, cast_ty.as_ref()); + let expr_ty = new_ty(expr_ty.as_ref()); + let cast_ty = new_ty(cast_ty.as_ref()); InvalidCast { expr, error: *error, expr_ty, cast_ty }.into() } InferenceDiagnostic::CannotBeDereferenced { expr, found } => { let expr = expr_syntax(*expr)?; - CannotBeDereferenced { expr, found: Type::new(db, def, found.as_ref()) }.into() + CannotBeDereferenced { expr, found: new_ty(found.as_ref()) }.into() } InferenceDiagnostic::CannotIndexInto { expr, found } => { let expr = expr_syntax(*expr)?; - CannotIndexInto { expr, found: Type::new(db, def, found.as_ref()) }.into() + CannotIndexInto { expr, found: new_ty(found.as_ref()) }.into() } InferenceDiagnostic::TyDiagnostic { source, diag } => { let source_map = match source { @@ -1047,10 +1050,7 @@ impl<'db> AnyDiagnostic<'db> { &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => { let at_point = span_syntax(at_point)?; let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() { - rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type { - ty, - env: crate::body_param_env_from_has_crate(db, def), - }), + rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(new_ty(ty)), // FIXME: Printing the const to string is definitely not the correct thing to do here. rustc_type_ir::GenericArgKind::Const(konst) => Either::Right( konst.display(db, DisplayTarget::from_crate(db, def.krate(db))).to_string(), @@ -1069,14 +1069,14 @@ impl<'db> AnyDiagnostic<'db> { let expr_or_pat = expr_or_pat_syntax(*node)?; TypeMismatch { expr_or_pat, - expected: Type { env, ty: expected.as_ref() }, - actual: Type { env, ty: found.as_ref() }, + expected: Type { owner: type_owner, ty: EarlyBinder::bind(expected.as_ref()) }, + actual: Type { owner: type_owner, ty: EarlyBinder::bind(found.as_ref()) }, } .into() } InferenceDiagnostic::SolverDiagnostic(d) => { let span = span_syntax(d.span)?; - Self::solver_diagnostic(db, &d.kind, span, env)? + Self::solver_diagnostic(db, &d.kind, span, type_owner)? } InferenceDiagnostic::ExplicitDropMethodUse { kind } => { let expr_or_path = match kind { @@ -1108,16 +1108,21 @@ impl<'db> AnyDiagnostic<'db> { db: &'db dyn HirDatabase, d: &'db SolverDiagnosticKind, span: SpanSyntax, - env: ParamEnvAndCrate<'db>, + type_owner: TypeOwnerId, ) -> Option> { let interner = DbInterner::new_no_crate(db); Some(match d { SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate } => { - let trait_predicate = - crate::TraitPredicate { inner: trait_predicate.get(interner), env }; + let trait_predicate = crate::TraitPredicate { + inner: trait_predicate.get(interner), + owner: type_owner, + }; let root_trait_predicate = root_trait_predicate.as_ref().map(|root_trait_predicate| { - crate::TraitPredicate { inner: root_trait_predicate.get(interner), env } + crate::TraitPredicate { + inner: root_trait_predicate.get(interner), + owner: type_owner, + } }); UnimplementedTrait { span, trait_predicate, root_trait_predicate }.into() } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index c3af5fa7cef8a..ed18482bf3807 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -33,7 +33,7 @@ use crate::{ Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum, EnumVariant, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl, LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitPredicate, - TraitRef, TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union, + TraitRef, TupleField, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, }; fn write_builtin_derive_impl_method<'db>( @@ -552,13 +552,7 @@ impl<'db> HirDisplay<'db> for EnumVariant { impl<'db> HirDisplay<'db> for Type<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - self.ty.hir_fmt(f) - } -} - -impl<'db> HirDisplay<'db> for TypeNs<'db> { - fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result { - self.ty.hir_fmt(f) + self.ty.skip_binder().hir_fmt(f) } } @@ -598,7 +592,7 @@ impl<'db> HirDisplay<'db> for TypeParam { let params = GenericParams::of(f.db, self.id.parent()); let param_data = ¶ms[self.id.local_id()]; let krate = self.id.parent().krate(f.db).id; - let ty = self.ty(f.db).ty; + let ty = self.ty(f.db).ty.skip_binder(); let predicates = GenericPredicates::query_all(f.db, self.id.parent()); let predicates = predicates .iter_identity() diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 02800fda98367..d187763151a22 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -39,7 +39,8 @@ mod display; pub use hir_def::ModuleId; use std::{ - fmt, + borrow::Borrow, + fmt, iter, mem::discriminant, ops::{ControlFlow, Not}, }; @@ -93,19 +94,17 @@ use hir_ty::{ method_resolution::{self, InherentImpls, MethodResolutionContext}, mir::interpret_mir, next_solver::{ - AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion, - ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Region, - RegionKind, SolverDefId, Ty, TyKind, TypingMode, + AliasTy, AnyImplId, ClauseKind, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, + GenericArgs, ParamEnv, PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, traits::{self, is_inherent_impl_coherent, structurally_normalize_ty}, }; use itertools::Itertools; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - AliasTyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, - TypeVisitor, fast_reject, - inherent::{GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, + AliasTyKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, fast_reject, + inherent::{AdtDef as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _}, }; use span::{AstIdNode, Edition, FileId}; use stdx::{format_to, impl_from, never}; @@ -183,6 +182,7 @@ pub use { mir::{MirEvalError, MirLowerError}, next_solver::abi::Safety, next_solver::{clear_tls_solver_cache, collect_ty_garbage}, + setup_tracing, }, // FIXME: These are needed for import assets, properly encapsulate them. hir_ty::{method_resolution::TraitImpls, next_solver::SimplifiedType}, @@ -198,6 +198,7 @@ use { name::AsName, span_map::{ExpansionSpanMap, RealSpanMap, SpanMap}, }, + hir_ty::next_solver, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -945,7 +946,7 @@ impl Module { if !missing.is_empty() { let env = ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(impl_id).into()), + param_env: db.trait_environment(GenericDefId::from(impl_id)), krate: self.id.krate(db), }; let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); @@ -1333,24 +1334,6 @@ pub struct Field { pub(crate) id: LocalFieldId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedField<'db> { - pub(crate) inner: Field, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedField<'db> { - /// Returns the type as in the signature of the struct. - pub fn ty(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let var_id = self.inner.parent.into(); - let field = db.field_types(var_id)[self.inner.id].get(); - let ty = field.instantiate(interner, self.args).skip_norm_wip(); - TypeNs::new(db, var_id, ty) - } -} - #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] pub struct TupleField { pub owner: InferBodyId, @@ -1371,7 +1354,7 @@ impl TupleField { .get(self.index as usize) .copied() .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)); - Type { env: body_param_env_from_has_crate(db, self.owner.expression_store_owner(db)), ty } + Type::new_body(db, self.owner.expression_store_owner(db), ty) } } @@ -1421,46 +1404,14 @@ impl Field { /// Returns the type as in the signature of the struct. Only use this in the /// context of the field definition. - pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let var_id = self.parent.into(); - let ty = db.field_types(var_id)[self.id].get().skip_binder(); - TypeNs::new(db, var_id, ty) - } - - // FIXME: Find better API to also handle const generics - pub fn ty_with_args<'db>( - &self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Type<'db> { + pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { let var_id = self.parent.into(); - let def_id: AdtId = match self.parent { - Variant::Struct(it) => it.id.into(), - Variant::Union(it) => it.id.into(), - Variant::EnumVariant(it) => it.parent_enum(db).id.into(), - }; - let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys(interner, def_id.into(), generics.map(|ty| ty.ty)); - let ty = db.field_types(var_id)[self.id].get().instantiate(interner, args).skip_norm_wip(); - Type::new(db, var_id, ty) + let ty = db.field_types(var_id)[self.id].get().instantiate_identity().skip_norm_wip(); + Type::new(var_id.adt_id(db).into(), ty) } pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result, LayoutError> { - db.layout_of_ty( - self.ty(db).ty.store(), - param_env_from_has_crate( - db, - match hir_def::VariantId::from(self.parent) { - hir_def::VariantId::EnumVariantId(id) => { - GenericDefId::AdtId(id.lookup(db).parent.into()) - } - hir_def::VariantId::StructId(id) => GenericDefId::AdtId(id.into()), - hir_def::VariantId::UnionId(id) => GenericDefId::AdtId(id.into()), - }, - ) - .store(), - ) - .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap())) + self.ty(db).layout(db) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> Variant { @@ -1505,10 +1456,6 @@ impl Struct { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> { Type::from_value_def(db, self.id) } @@ -1532,11 +1479,6 @@ impl Struct { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } - - pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> { - let args = infer_ctxt.fresh_args_for_item(hir_ty::Span::Dummy, self.id.into()); - InstantiatedStruct { inner: self, args } - } } impl HasVisibility for Struct { @@ -1547,34 +1489,6 @@ impl HasVisibility for Struct { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedStruct<'db> { - pub(crate) inner: Struct, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedStruct<'db> { - pub fn fields(self, db: &dyn HirDatabase) -> Vec> { - self.inner - .id - .fields(db) - .fields() - .iter() - .map(|(id, _)| InstantiatedField { - inner: Field { parent: self.inner.into(), id }, - args: self.args, - }) - .collect() - } - - pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let ty = db.ty(self.inner.id.into()); - TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip()) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Union { pub(crate) id: UnionId, @@ -1593,10 +1507,6 @@ impl Union { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type<'_> { Type::from_value_def(db, self.id) } @@ -1660,17 +1570,11 @@ impl Enum { Type::from_def(db, self.id) } - pub fn ty_params<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - Type::from_def_params(db, self.id) - } - /// The type of the enum variant bodies. pub fn variant_body_ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - let krate = self.id.lookup(db).container.krate(db); - let interner = DbInterner::new_with(db, krate); - Type::new_for_crate( - db, - krate, + let interner = DbInterner::new_no_crate(db); + Type::no_params( + Type::builtin_type_crate(db), match EnumSignature::variant_body_type(db, self.id) { layout::IntegerType::Pointer(sign) => match sign { true => Ty::new_int(interner, rustc_type_ir::IntTy::Isize), @@ -1724,27 +1628,6 @@ impl HasVisibility for Enum { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedEnum<'db> { - pub(crate) inner: Enum, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedEnum<'db> { - pub fn ty(self, db: &'db dyn HirDatabase) -> TypeNs<'db> { - let interner = DbInterner::new_no_crate(db); - - let ty = db.ty(self.inner.id.into()); - TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip()) - } -} - -impl From<&EnumVariant> for DefWithBodyId { - fn from(&v: &EnumVariant) -> Self { - DefWithBodyId::VariantId(v.into()) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EnumVariant { pub(crate) id: EnumVariantId, @@ -1811,40 +1694,6 @@ impl EnumVariant { pub fn is_unstable(self, db: &dyn HirDatabase) -> bool { AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_UNSTABLE) } - - pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> { - let args = infer_ctxt.fresh_args_for_item( - hir_ty::Span::Dummy, - self.parent_enum(infer_ctxt.interner.db()).id.into(), - ); - InstantiatedVariant { inner: self, args } - } -} - -// FIXME: Rename to `EnumVariant` -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InstantiatedVariant<'db> { - pub(crate) inner: EnumVariant, - pub(crate) args: GenericArgs<'db>, -} - -impl<'db> InstantiatedVariant<'db> { - pub fn parent_enum(self, db: &dyn HirDatabase) -> InstantiatedEnum<'db> { - InstantiatedEnum { inner: self.inner.id.lookup(db).parent.into(), args: self.args } - } - - pub fn fields(self, db: &dyn HirDatabase) -> Vec> { - self.inner - .id - .fields(db) - .fields() - .iter() - .map(|(id, _)| InstantiatedField { - inner: Field { parent: self.inner.into(), id }, - args: self.args, - }) - .collect() - } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -1893,23 +1742,6 @@ impl Adt { Type::from_def(db, id) } - /// Turns this ADT into a type with the given type parameters. This isn't - /// the greatest API, FIXME find a better one. - pub fn ty_with_args<'db>( - self, - db: &'db dyn HirDatabase, - args: impl IntoIterator>, - ) -> Type<'db> { - let id = AdtId::from(self); - let interner = DbInterner::new_no_crate(db); - let ty = Ty::new_adt( - interner, - id, - generic_args_from_tys(interner, id.into(), args.into_iter().map(|ty| ty.ty)), - ); - Type::new(db, id, ty) - } - pub fn module(self, db: &dyn HirDatabase) -> Module { match self { Adt::Struct(s) => s.module(db), @@ -2017,8 +1849,7 @@ impl AnonConst { pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { let loc = self.id.loc(db); - let env = body_param_env_from_has_crate(db, loc.owner); - Type { env, ty: loc.ty.get().instantiate_identity().skip_norm_wip() } + Type { owner: self.id.into(), ty: loc.ty.get() } } pub fn eval(self, db: &dyn HirDatabase) -> Result, ConstEvalError> { @@ -2128,7 +1959,7 @@ impl DefWithBody { }, DefWithBody::Static(it) => it.id.into(), DefWithBody::Const(it) => it.id.into(), - DefWithBody::EnumVariant(it) => it.into(), + DefWithBody::EnumVariant(it) => it.id.into(), }) } @@ -2168,8 +1999,6 @@ impl DefWithBody { let Ok(id) = self.try_into() else { return; }; - let krate = self.module(db).id.krate(db); - let env = body_param_env_from_has_crate(db, id); let (body, source_map) = Body::with_source_map(db, id); let sig_source_map = match self { @@ -2192,6 +2021,7 @@ impl DefWithBody { expr_store_diagnostics(db, acc, source_map); let infer = InferenceResult::of(db, id); + let type_owner = id.generic_def(db).into(); for d in infer.diagnostics() { acc.extend(AnyDiagnostic::inference_diagnostic( db, @@ -2199,7 +2029,7 @@ impl DefWithBody { d, source_map, sig_source_map, - env, + type_owner, )); } @@ -2269,7 +2099,7 @@ impl DefWithBody { }; acc.push( MovedOutOfRef { - ty: Type::new_for_crate(db, krate, moof.ty.as_ref()), + ty: Type { owner: type_owner, ty: EarlyBinder::bind(moof.ty.as_ref()) }, span, } .into(), @@ -2369,9 +2199,9 @@ impl DefWithBody { ) -> impl Iterator> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.expression_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.expression_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } @@ -2379,9 +2209,9 @@ impl DefWithBody { pub fn pattern_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.pattern_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.pattern_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } @@ -2389,9 +2219,9 @@ impl DefWithBody { pub fn binding_types<'db>(self, db: &'db dyn HirDatabase) -> impl Iterator> { self.id().into_iter().flat_map(move |def_id| { let infer = InferenceResult::of(db, def_id); - let resolver = def_id.resolver(db); + let def_id = def_id.generic_def(db); - infer.binding_types().map(move |(_, ty)| Type::new_with_resolver(db, &resolver, ty)) + infer.binding_types().map(move |(_, ty)| Type::new(def_id, ty)) }) } } @@ -2475,12 +2305,9 @@ impl Function { AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { // Get the type for the trait function, as we can't get the type for the impl function // because it has not `CallableDefId`. - let krate = impl_.module(db).krate(db); - let interner = DbInterner::new_with(db, krate); - let param_env = hir_ty::builtin_derive::param_env(interner, impl_); - let env = ParamEnvAndCrate { param_env, krate }; + // FIXME: This does not account for replacing `Self`. Do we really need that? let Some(trait_method) = method.trait_method(db, impl_) else { - return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) }; + return Type::unknown(); }; Function::from(trait_method).ty(db) } @@ -2490,160 +2317,36 @@ impl Function { pub fn fn_ptr_type(self, db: &dyn HirDatabase) -> Type<'_> { match self.id { AnyFunctionId::FunctionId(id) => { - let resolver = id.resolver(db); let interner = DbInterner::new_no_crate(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let callable_sig = db.callable_item_signature(id.into()).instantiate_identity().skip_norm_wip(); let ty = Ty::new_fn_ptr(interner, callable_sig); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(id.into(), ty) } AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { - struct ParamsShifter<'db> { - interner: DbInterner<'db>, - shift_by: i32, - } - - impl<'db> TypeFolder> for ParamsShifter<'db> { - fn cx(&self) -> DbInterner<'db> { - self.interner - } - - fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> { - if let TyKind::Param(param) = ty.kind() { - Ty::new_param( - self.interner, - param.id, - param.index.checked_add_signed(self.shift_by).unwrap(), - ) - } else { - ty.super_fold_with(self) - } - } - - fn fold_const( - &mut self, - ct: hir_ty::next_solver::Const<'db>, - ) -> hir_ty::next_solver::Const<'db> { - if let ConstKind::Param(param) = ct.kind() { - hir_ty::next_solver::Const::new_param( - self.interner, - ParamConst { - id: param.id, - index: param.index.checked_add_signed(self.shift_by).unwrap(), - }, - ) - } else { - ct.super_fold_with(self) - } - } - - fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { - if let RegionKind::ReEarlyParam(param) = r.kind() { - Region::new_early_param( - self.interner, - EarlyParamRegion { - id: param.id, - index: param.index.checked_add_signed(self.shift_by).unwrap(), - }, - ) - } else { - r - } - } - } - // Get the type for the trait function, as we can't get the type for the impl function // because it has not `CallableDefId`. - let krate = impl_.module(db).krate(db); - let interner = DbInterner::new_with(db, krate); - let param_env = hir_ty::builtin_derive::param_env(interner, impl_); - let env = ParamEnvAndCrate { param_env, krate }; + // FIXME: This does not account for replacing `Self`. Do we really need that? let Some(trait_method) = method.trait_method(db, impl_) else { - return Type { env, ty: Ty::new_error(interner, ErrorGuaranteed) }; + return Type::unknown(); }; - // The procedure works as follows: the method may have additional generic parameters (e.g. `Hash::hash()`), - // and we want them to be params of the impl method as well. So we start with the trait method identity - // args and extract from them the trait method own args. In parallel, we retrieve the impl trait ref. - // Now we can put our args as [...impl_trait_ref.args, ...trait_method_own_args], but we have one problem: - // the args in `trait_method_own_args` use indices appropriate for the trait method, which are not necessarily - // good for the impl method. So we shift them by `impl_generics_len - trait_generics_len`, which is essentially - // `impl_generics_len - impl_trait_ref.args.len()`. - let trait_method_fn_ptr = Ty::new_fn_ptr( - interner, - db.callable_item_signature(trait_method.into()) - .instantiate_identity() - .skip_norm_wip(), - ); - let impl_trait_ref = hir_ty::builtin_derive::impl_trait(interner, impl_) - .instantiate_identity() - .skip_norm_wip(); - let trait_method_args = - GenericArgs::identity_for_item(interner, trait_method.into()); - let trait_method_own_args = GenericArgs::new_from_iter( - interner, - trait_method_args.iter().skip(impl_trait_ref.args.len()), - ); - let impl_params_count = hir_ty::builtin_derive::generic_params_count(db, impl_); - let shift_args_by = impl_params_count as i32 - impl_trait_ref.args.len() as i32; - let shifted_trait_method_own_args = trait_method_own_args - .fold_with(&mut ParamsShifter { interner, shift_by: shift_args_by }); - let impl_method_args = GenericArgs::new_from_iter( - interner, - impl_trait_ref.args.iter().chain(shifted_trait_method_own_args), - ); - let impl_method_fn_ptr = EarlyBinder::bind(trait_method_fn_ptr) - .instantiate(interner, impl_method_args) - .skip_norm_wip(); - Type { env, ty: impl_method_fn_ptr } + Function::from(trait_method).fn_ptr_type(db) } } } - fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (ParamEnvAndCrate<'db>, PolyFnSig<'db>) { + fn fn_sig<'db>(self, db: &'db dyn HirDatabase) -> (TypeOwnerId, PolyFnSig<'db>) { let fn_ptr = self.fn_ptr_type(db); - let TyKind::FnPtr(sig_tys, hdr) = fn_ptr.ty.kind() else { + let TyKind::FnPtr(sig_tys, hdr) = fn_ptr.ty.skip_binder().kind() else { unreachable!(); }; - (fn_ptr.env, sig_tys.with(hdr)) + (fn_ptr.owner, sig_tys.with(hdr)) } - // FIXME: Find a better API to express all combinations here, perhaps we should have `PreInstantiationType`? - /// Get this function's return type pub fn ret_type(self, db: &dyn HirDatabase) -> Type<'_> { - let (env, sig) = self.fn_sig(db); - Type { env, ty: sig.skip_binder().output() } - } - - // FIXME: Find better API to also handle const generics - pub fn ret_type_with_args<'db>( - self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Type<'db> { - let ret_type = self.ret_type(db); - let interner = DbInterner::new_no_crate(db); - let args = self.adapt_generic_args(interner, generics); - ret_type.derived(EarlyBinder::bind(ret_type.ty).instantiate(interner, args).skip_norm_wip()) - } - - fn adapt_generic_args<'db>( - self, - interner: DbInterner<'db>, - generics: impl Iterator>, - ) -> GenericArgs<'db> { - let generics = generics.map(|ty| ty.ty); - match self.id { - AnyFunctionId::FunctionId(id) => generic_args_from_tys(interner, id.into(), generics), - AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => { - let impl_args = GenericArgs::identity_for_item(interner, impl_.into()); - GenericArgs::new_from_iter( - interner, - impl_args.iter().chain(generics.map(Into::into)), - ) - } - } + let (owner, sig) = self.fn_sig(db); + Type { owner, ty: EarlyBinder::bind(sig.skip_binder().output()) } } pub fn async_ret_type<'db>(self, db: &'db dyn HirDatabase) -> Option> { @@ -2653,15 +2356,13 @@ impl Function { if !self.is_async(db) { return None; } - let resolver = id.resolver(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let ret_ty = db.callable_item_signature(id.into()).instantiate_identity().skip_binder().output(); for pred in ret_ty.impl_trait_bounds(db).into_iter().flatten() { if let ClauseKind::Projection(projection) = pred.kind().skip_binder() && let Some(output_ty) = projection.term.as_type() { - return Type::new_with_resolver_inner(db, &resolver, output_ty).into(); + return Some(Type::new(id.into(), output_ty)); } } None @@ -2687,7 +2388,7 @@ impl Function { } pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec> { - let (env, sig) = self.fn_sig(db); + let (owner, sig) = self.fn_sig(db); let func = match self.id { AnyFunctionId::FunctionId(id) => Callee::Def(CallableDefId::FunctionId(id)), AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => { @@ -2698,7 +2399,11 @@ impl Function { .inputs() .iter() .enumerate() - .map(|(idx, &ty)| Param { func: func.clone(), ty: Type { env, ty }, idx }) + .map(|(idx, &ty)| Param { + func: func.clone(), + ty: Type { owner, ty: EarlyBinder::bind(ty) }, + idx, + }) .collect() } @@ -2724,28 +2429,6 @@ impl Function { params } - // FIXME: Find better API to also handle const generics - pub fn params_without_self_with_args<'db>( - self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Vec> { - let interner = DbInterner::new_no_crate(db); - let args = self.adapt_generic_args(interner, generics); - let params = self.params_without_self(db); - params - .into_iter() - .map(|param| Param { - func: param.func, - idx: param.idx, - ty: Type { - env: param.ty.env, - ty: EarlyBinder::bind(param.ty.ty).instantiate(interner, args).skip_norm_wip(), - }, - }) - .collect() - } - pub fn is_const(self, db: &dyn HirDatabase) -> bool { match self.id { AnyFunctionId::FunctionId(id) => FunctionSignature::of(db, id).is_const(), @@ -3099,20 +2782,8 @@ impl SelfParam { } pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { - let (env, sig) = self.func.fn_sig(db); - Type { env, ty: sig.skip_binder().inputs()[0] } - } - - // FIXME: Find better API to also handle const generics - pub fn ty_with_args<'db>( - &self, - db: &'db dyn HirDatabase, - generics: impl Iterator>, - ) -> Type<'db> { - let interner = DbInterner::new_no_crate(db); - let args = self.func.adapt_generic_args(interner, generics); - let Type { env, ty } = self.ty(db); - Type { env, ty: EarlyBinder::bind(ty).instantiate(interner, args).skip_norm_wip() } + let (owner, sig) = self.func.fn_sig(db); + Type { owner, ty: EarlyBinder::bind(sig.skip_binder().inputs()[0]) } } } @@ -3444,10 +3115,6 @@ impl TypeAlias { Type::from_def(db, self.id) } - pub fn ty_params(self, db: &dyn HirDatabase) -> Type<'_> { - Type::from_def_params(db, self.id) - } - pub fn name(self, db: &dyn HirDatabase) -> Name { TypeAliasSignature::of(db, self.id).name.clone() } @@ -3496,10 +3163,13 @@ impl BuiltinType { } } + pub fn bool() -> BuiltinType { + BuiltinType { inner: hir_def::builtin_type::BuiltinType::Bool } + } + pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> { - let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| all_crates(db)[0]); let interner = DbInterner::new_no_crate(db); - Type::new_for_crate(db, core, Ty::from_builtin_type(interner, self.inner)) + Type::no_params(Type::builtin_type_crate(db), Ty::from_builtin_type(interner, self.inner)) } pub fn name(self) -> Name { @@ -4294,23 +3964,19 @@ impl GenericDef { // We cannot call this `Substitution` unfortunately... #[derive(Debug)] pub struct GenericSubstitution<'db> { + owner: TypeOwnerId, def: GenericDefId, subst: GenericArgs<'db>, - env: ParamEnvAndCrate<'db>, } impl<'db> GenericSubstitution<'db> { - fn new(def: GenericDefId, subst: GenericArgs<'db>, env: ParamEnvAndCrate<'db>) -> Self { - Self { def, subst, env } + fn new(def: GenericDefId, subst: GenericArgs<'db>, owner: TypeOwnerId) -> Self { + Self { owner, def, subst } } - fn new_from_fn( - def: Function, - subst: GenericArgs<'db>, - env: ParamEnvAndCrate<'db>, - ) -> Option { + fn new_from_fn(def: Function, subst: GenericArgs<'db>, owner: TypeOwnerId) -> Option { match def.id { - AnyFunctionId::FunctionId(def) => Some(Self::new(def.into(), subst, env)), + AnyFunctionId::FunctionId(def) => Some(Self::new(def.into(), subst, owner)), AnyFunctionId::BuiltinDeriveImplMethod { .. } => None, } } @@ -4357,7 +4023,12 @@ impl<'db> GenericSubstitution<'db> { .zip(type_params); container_params .chain(self_params) - .filter_map(|(ty, name)| Some((name?.symbol().clone(), Type { ty, env: self.env }))) + .filter_map(|(ty, name)| { + Some(( + name?.symbol().clone(), + Type { ty: EarlyBinder::bind(ty), owner: self.owner }, + )) + }) .collect() } } @@ -4470,7 +4141,7 @@ impl Local { let def = self.parent; let infer = InferenceResult::of(db, self.parent_infer); let ty = infer.binding_ty(self.binding_id); - Type::new(db, def, ty) + Type::new_body(db, def, ty) } /// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = it;` @@ -4770,18 +4441,17 @@ impl TypeParam { } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - let resolver = self.id.parent().resolver(db); let interner = DbInterner::new_no_crate(db); let index = hir_ty::type_or_const_param_idx(db, self.id.into()); let ty = Ty::new_param(interner, self.id, index); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(self.id.parent(), ty) } /// FIXME: this only lists trait bounds from the item defining the type /// parameter, not additional bounds that might be added e.g. by a method if /// the parameter comes from an impl! pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec { - let self_ty = self.ty(db).ty; + let self_ty = self.ty(db).ty.instantiate_identity().skip_norm_wip(); GenericPredicates::query_explicit(db, self.id.parent()) .iter_identity() .filter_map(|pred| match &pred.kind().skip_binder() { @@ -4795,10 +4465,9 @@ impl TypeParam { pub fn default(self, db: &dyn HirDatabase) -> Option> { let ty = generic_arg_from_param(db, self.id.into())?; - let resolver = self.id.parent().resolver(db); match ty.kind() { rustc_type_ir::GenericArgKind::Type(it) if !it.is_ty_error() => { - Some(Type::new_with_resolver_inner(db, &resolver, it)) + Some(Type::new(self.id.parent(), it)) } _ => None, } @@ -4859,7 +4528,7 @@ impl ConstParam { } pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> { - Type::new(db, self.id.parent(), db.const_param_ty(self.id)) + Type::new(self.id.parent(), db.const_param_ty(self.id)) } pub fn default(self, db: &dyn HirDatabase, display_target: DisplayTarget) -> Option { @@ -4981,21 +4650,26 @@ impl Impl { /// blanket impls, and only does a shallow type constructor check. In fact, this should've probably been on `Adt` /// etc., and not on `Type`. If you would want to create a precise list of all impls applying to a type, /// you would need to include blanket impls, and try to prove to predicates for each candidate. - pub fn all_for_type<'db>(db: &'db dyn HirDatabase, Type { ty, env }: Type<'db>) -> Vec { + pub fn all_for_type<'db>(db: &'db dyn HirDatabase, ty: Type<'db>) -> Vec { let mut result = Vec::new(); let interner = DbInterner::new_no_crate(db); - let Some(simplified_ty) = - fast_reject::simplify_type(interner, ty, fast_reject::TreatParams::AsRigid) - else { + let Some(simplified_ty) = fast_reject::simplify_type( + interner, + ty.ty.skip_binder(), + fast_reject::TreatParams::AsRigid, + ) else { return Vec::new(); }; let mut extend_with_impls = |impls: Either<&[ImplId], &[BuiltinDeriveImplId]>| match impls { Either::Left(impls) => result.extend(impls.iter().copied().map(Impl::from)), Either::Right(impls) => result.extend(impls.iter().copied().map(Impl::from)), }; - method_resolution::with_incoherent_inherent_impls(db, env.krate, &simplified_ty, |impls| { - extend_with_impls(Either::Left(impls)) - }); + method_resolution::with_incoherent_inherent_impls( + db, + ty.krate(db), + &simplified_ty, + |impls| extend_with_impls(Either::Left(impls)), + ); if let Some(module) = method_resolution::simplified_type_module(db, &simplified_ty) { InherentImpls::for_each_crate_and_block( db, @@ -5003,7 +4677,7 @@ impl Impl { module.block(db), &mut |impls| extend_with_impls(Either::Left(impls.for_self_ty(&simplified_ty))), ); - std::iter::successors(module.block(db), |block| block.loc(db).module.block(db)) + iter::successors(module.block(db), |block| block.loc(db).module.block(db)) .filter_map(|block| TraitImpls::for_block(db, block).as_deref()) .for_each(|impls| impls.for_self_ty(&simplified_ty, &mut extend_with_impls)); for &krate in &*all_crates(db) { @@ -5058,21 +4732,16 @@ impl Impl { match self.id { AnyImplId::ImplId(id) => { let trait_ref = db.impl_trait(id)?.instantiate_identity().skip_norm_wip(); - let resolver = id.resolver(db); - Some(TraitRef::new_with_resolver(db, &resolver, trait_ref)) + Some(TraitRef::new(id.into(), trait_ref)) } AnyImplId::BuiltinDeriveImplId(id) => { let loc = id.loc(db); let krate = loc.module(db).krate(db); let interner = DbInterner::new_with(db, krate); - let env = ParamEnvAndCrate { - param_env: hir_ty::builtin_derive::param_env(interner, id), - krate, - }; let trait_ref = hir_ty::builtin_derive::impl_trait(interner, id) .instantiate_identity() .skip_norm_wip(); - Some(TraitRef { env, trait_ref }) + Some(TraitRef { owner: TypeOwnerId::BuiltinDeriveImplId(id), trait_ref }) } } } @@ -5080,24 +4749,16 @@ impl Impl { pub fn self_ty(self, db: &dyn HirDatabase) -> Type<'_> { match self.id { AnyImplId::ImplId(id) => { - let resolver = id.resolver(db); - // FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s. let ty = db.impl_self_ty(id).instantiate_identity().skip_norm_wip(); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::new(id.into(), ty) } AnyImplId::BuiltinDeriveImplId(id) => { let loc = id.loc(db); let krate = loc.module(db).krate(db); let interner = DbInterner::new_with(db, krate); - let env = ParamEnvAndCrate { - param_env: hir_ty::builtin_derive::param_env(interner, id), - krate, - }; - let ty = hir_ty::builtin_derive::impl_trait(interner, id) - .instantiate_identity() - .skip_norm_wip() - .self_ty(); - Type { env, ty } + let ty = + hir_ty::builtin_derive::impl_trait(interner, id).map_bound(|it| it.self_ty()); + Type { owner: TypeOwnerId::BuiltinDeriveImplId(id), ty } } } } @@ -5159,38 +4820,33 @@ impl Impl { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TraitRef<'db> { - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, trait_ref: hir_ty::next_solver::TraitRef<'db>, } impl<'db> TraitRef<'db> { - pub(crate) fn new_with_resolver( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - trait_ref: hir_ty::next_solver::TraitRef<'db>, - ) -> Self { - let env = param_env_from_resolver(db, resolver); - TraitRef { env, trait_ref } + fn new(owner: GenericDefId, trait_ref: hir_ty::next_solver::TraitRef<'db>) -> Self { + Self { owner: TypeOwnerId::GenericDefId(owner), trait_ref } } pub fn trait_(&self) -> Trait { Trait { id: self.trait_ref.def_id.0 } } - pub fn self_ty(&self) -> TypeNs<'_> { + pub fn self_ty(&self) -> Type<'_> { let ty = self.trait_ref.self_ty(); - TypeNs { env: self.env, ty } + Type { owner: self.owner, ty: EarlyBinder::bind(ty) } } /// Returns `idx`-th argument of this trait reference if it is a type argument. Note that the /// first argument is the `Self` type. - pub fn get_type_argument(&self, idx: usize) -> Option> { + pub fn get_type_argument(&self, idx: usize) -> Option> { self.trait_ref .args .as_slice() .get(idx) .and_then(|arg| arg.ty()) - .map(|ty| TypeNs { env: self.env, ty }) + .map(|ty| Type { owner: self.owner, ty: EarlyBinder::bind(ty) }) } } @@ -5202,6 +4858,7 @@ enum AnyClosureId { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Closure<'db> { + owner: TypeOwnerId, id: AnyClosureId, subst: GenericArgs<'db>, } @@ -5239,12 +4896,11 @@ impl<'db> Closure<'db> { let InternedClosure { owner: infer_owner, expr: closure, .. } = closure; let infer = InferenceResult::of(db, infer_owner); let owner = infer_owner.expression_store_owner(db); - let param_env = body_param_env_from_has_crate(db, owner); infer.closures_data[&closure] .min_captures .values() .flatten() - .map(|capture| ClosureCapture { owner, infer_owner, closure, capture, param_env }) + .map(|capture| ClosureCapture { owner, infer_owner, closure, capture }) .collect() } @@ -5335,7 +4991,6 @@ pub struct ClosureCapture<'db> { infer_owner: InferBodyId, closure: ExprId, capture: &'db hir_ty::closure_analysis::CapturedPlace, - param_env: ParamEnvAndCrate<'db>, } impl<'db> ClosureCapture<'db> { @@ -5443,14 +5098,14 @@ impl<'db> ClosureCapture<'db> { result } - pub fn ty(&self, _db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.param_env, ty: self.capture.place.ty() } + pub fn ty(&self, db: &'db dyn HirDatabase) -> Type<'db> { + Type::new_body(db, self.owner, self.capture.place.ty()) } /// The type that is stored in the closure, which is different from [`Self::ty()`], representing /// the place's type, when the capture is by ref. pub fn captured_ty(&self, db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.param_env, ty: self.capture.captured_ty(db) } + Type::new_body(db, self.owner, self.capture.captured_ty(db)) } } @@ -5526,74 +5181,143 @@ impl CaptureUsageSource { } } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] +enum TypeOwnerId { + GenericDefId(GenericDefId), + BuiltinDeriveImplId(BuiltinDeriveImplId), + AnonConstId(AnonConstId), + // FIXME: What do when we unify two different crates? Currently we just randomly keep one. + NoParams(base_db::Crate), +} +impl_from!(GenericDefId, BuiltinDeriveImplId, AnonConstId for TypeOwnerId); + +impl TypeOwnerId { + fn unify(self, other: Self) -> Option { + match (self, other) { + (TypeOwnerId::NoParams(_), owner) => Some(owner), + (owner, TypeOwnerId::NoParams(_)) => Some(owner), + (_, _) => { + if self == other { + Some(self) + } else { + None + } + } + } + } + + #[track_caller] + fn must_unify(self, other: Self) -> Self { + self.unify(other).expect("failed to unify type owners") + } + + fn can_rebase_into( + self, + db: &dyn HirDatabase, + rebase_into: Self, + self_ty: EarlyBinder<'_, Ty<'_>>, + ) -> bool { + if self == rebase_into || !self_ty.skip_binder().has_param() { + return true; + } + let self_def = match self { + TypeOwnerId::GenericDefId(def) => def, + TypeOwnerId::BuiltinDeriveImplId(_) | TypeOwnerId::AnonConstId(_) => return false, + TypeOwnerId::NoParams(_) => return true, + }; + let self_def = match self_def { + GenericDefId::ImplId(def) => ItemContainerId::ImplId(def), + GenericDefId::TraitId(def) => ItemContainerId::TraitId(def), + GenericDefId::AdtId(_) + | GenericDefId::ConstId(_) + | GenericDefId::FunctionId(_) + | GenericDefId::StaticId(_) + | GenericDefId::TypeAliasId(_) => return false, + }; + let rebase_into_def = match rebase_into { + TypeOwnerId::GenericDefId(def) => def, + TypeOwnerId::BuiltinDeriveImplId(_) + | TypeOwnerId::AnonConstId(_) + | TypeOwnerId::NoParams(_) => return false, + }; + let rebase_into_parent = match rebase_into_def { + GenericDefId::ConstId(def) => def.loc(db).container, + GenericDefId::FunctionId(def) => def.loc(db).container, + GenericDefId::TypeAliasId(def) => def.loc(db).container, + GenericDefId::AdtId(_) + | GenericDefId::ImplId(_) + | GenericDefId::StaticId(_) + | GenericDefId::TraitId(_) => return false, + }; + self_def == rebase_into_parent + } +} + +/// Note: A [`Type`] remembers its origin. Trying to do anything (except comparing) +/// with types of different origins will cause errors or panics. Instead, use the `instantiate` methods. +#[derive(Clone, Debug)] pub struct Type<'db> { - env: ParamEnvAndCrate<'db>, - ty: Ty<'db>, + owner: TypeOwnerId, + ty: EarlyBinder<'db, Ty<'db>>, } -impl<'db> Type<'db> { - pub(crate) fn new_with_resolver( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - ty: Ty<'db>, - ) -> Self { - Type::new_with_resolver_inner(db, resolver, ty) +impl<'db> std::hash::Hash for Type<'db> { + fn hash(&self, state: &mut H) { + // Do not hash the owner as different owners can compare the same. + // self.owner.hash(state); + self.ty.hash(state); } +} - pub(crate) fn new_with_resolver_inner( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, - ty: Ty<'db>, - ) -> Self { - let environment = param_env_from_resolver(db, resolver); - Type { env: environment, ty } +impl<'db> PartialEq for Type<'db> { + fn eq(&self, other: &Self) -> bool { + if self.ty != other.ty { + return false; + } + hir_ty::with_attached_db(|db| { + self.owner.can_rebase_into(db, other.owner, self.ty) + || other.owner.can_rebase_into(db, self.owner, other.ty) + }) } +} - pub(crate) fn new_for_crate( - db: &'db dyn HirDatabase, - krate: base_db::Crate, - ty: Ty<'db>, - ) -> Self { - let interner = DbInterner::new_with(db, krate); - Type { env: ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate }, ty } +impl<'db> Eq for Type<'db> {} + +impl<'db> Type<'db> { + fn new(owner: GenericDefId, ty: Ty<'db>) -> Self { + Type { owner: TypeOwnerId::GenericDefId(owner), ty: EarlyBinder::bind(ty) } } - fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty<'db>) -> Self { - let resolver = lexical_env.resolver(db); - let environment = param_env_from_resolver(db, &resolver); - Type { env: environment, ty } + fn new_body(db: &dyn HirDatabase, owner: ExpressionStoreOwnerId, ty: Ty<'db>) -> Self { + Self::new(owner.generic_def(db), ty) } - fn from_def(db: &'db dyn HirDatabase, def: impl Into + HasResolver) -> Self { - let interner = DbInterner::new_no_crate(db); - let ty = db.ty(def.into()); - let def = match def.into() { - TyDefId::AdtId(it) => GenericDefId::AdtId(it), - TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), - TyDefId::BuiltinType(_) => { - return Type::new(db, def, ty.skip_binder()); - } - }; - let args = GenericArgs::error_for_item(interner, def.into()); - Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) + fn no_params(krate: base_db::Crate, ty: Ty<'db>) -> Self { + Type { owner: TypeOwnerId::NoParams(krate), ty: EarlyBinder::bind(ty) } } - // FIXME: We shouldn't leak `TyKind::Param`s. - fn from_def_params(db: &'db dyn HirDatabase, def: impl Into + HasResolver) -> Self { - let ty = db.ty(def.into()); - Type::new(db, def, ty.instantiate_identity().skip_norm_wip()) + fn builtin_type_crate(db: &'db dyn HirDatabase) -> base_db::Crate { + // It doesn't really matter. + all_crates(db)[0] } - fn from_value_def( - db: &'db dyn HirDatabase, - def: impl Into + HasResolver, - ) -> Self { - let interner = DbInterner::new_no_crate(db); - let Some(ty) = db.value_ty(def.into()) else { - return Type::new(db, def, Ty::new_error(interner, ErrorGuaranteed)); + fn from_def(db: &'db dyn HirDatabase, def: impl Into) -> Self { + let def = def.into(); + let ty = db.ty(def); + let owner = match def { + TyDefId::AdtId(it) => TypeOwnerId::GenericDefId(GenericDefId::AdtId(it)), + TyDefId::TypeAliasId(it) => TypeOwnerId::GenericDefId(GenericDefId::TypeAliasId(it)), + TyDefId::BuiltinType(_) => TypeOwnerId::NoParams(Self::builtin_type_crate(db)), + }; + Type { owner, ty } + } + + fn from_value_def(db: &'db dyn HirDatabase, def: impl Into) -> Self { + let def = def.into(); + let Some(ty) = db.value_ty(def) else { + return Type::unknown(); }; - let def = match def.into() { + let def = match def { ValueTyDefId::ConstId(it) => GenericDefId::ConstId(it), ValueTyDefId::FunctionId(it) => GenericDefId::FunctionId(it), ValueTyDefId::StructId(it) => GenericDefId::AdtId(AdtId::StructId(it)), @@ -5601,55 +5325,192 @@ impl<'db> Type<'db> { ValueTyDefId::EnumVariantId(it) => { GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent)) } - ValueTyDefId::StaticId(_) => { - return Type::new(db, def, ty.skip_binder()); + ValueTyDefId::StaticId(it) => { + return Type::no_params(hir_def::HasModule::krate(&it, db), ty.skip_binder()); + } + }; + Type::new(def, ty.instantiate_identity().skip_norm_wip()) + } + + /// Replace any generic parameters with error types. + pub fn instantiate_with_errors(&self) -> Self { + let interner = DbInterner::conjure(); + let krate = self.krate(interner.db()); + let args = match self.owner { + TypeOwnerId::GenericDefId(def) => GenericArgs::error_for_item(interner, def.into()), + TypeOwnerId::BuiltinDeriveImplId(def) => { + GenericArgs::error_for_item(interner, def.into()) } + TypeOwnerId::AnonConstId(def) => GenericArgs::error_for_item(interner, def.into()), + TypeOwnerId::NoParams(_) => GenericArgs::empty(interner), }; - let args = GenericArgs::error_for_item(interner, def.into()); - Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip()) + Type::no_params(krate, self.ty.instantiate(interner, args).skip_norm_wip()) + } + + // FIXME: Find some way with const params, maybe even lifetimes? + pub fn instantiate(&self, args: impl IntoIterator>>) -> Type<'db> { + let interner = DbInterner::conjure(); + let (args, owner) = match self.owner { + TypeOwnerId::GenericDefId(def) => generic_args_from_tys(interner, def.into(), args), + TypeOwnerId::BuiltinDeriveImplId(def) => { + generic_args_from_tys(interner, def.into(), args) + } + TypeOwnerId::AnonConstId(def) => generic_args_from_tys(interner, def.into(), args), + TypeOwnerId::NoParams(krate) => { + (GenericArgs::empty(interner), TypeOwnerId::NoParams(krate)) + } + }; + Type { owner, ty: EarlyBinder::bind(self.ty.instantiate(interner, args).skip_norm_wip()) } + } + + /// Instantiates multiple types with infer vars, keeping the same infer vars for the same owners. + fn instantiate_many_with_infer( + tys: impl IntoIterator>>, + infcx: &InferCtxt<'db>, + ) -> impl Iterator> { + let mut var_for_param = FxHashMap::default(); + tys.into_iter().map(move |ty| { + let ty = ty.borrow(); + let owner = match ty.owner { + TypeOwnerId::GenericDefId(def) => def.into(), + TypeOwnerId::BuiltinDeriveImplId(def) => def.into(), + TypeOwnerId::AnonConstId(def) => def.into(), + TypeOwnerId::NoParams(_) => return ty.ty.skip_binder(), + }; + let args = GenericArgs::for_item(infcx.interner, owner, |_, param, _| { + *var_for_param + .entry(param) + .or_insert_with(|| infcx.var_for_def(param, hir_ty::Span::Dummy)) + }); + + ty.ty.instantiate(infcx.interner, args).skip_norm_wip() + }) + } + + /// Tries to put this type as-is in the context of `rebase_into`. This will return `Some(_)` if: + /// + /// - The type does not reference generic parameters, or + /// - `rebase_into` is in the context of a child of our context (for example, a function in an impl). + pub fn try_rebase_into( + &self, + db: &'db dyn HirDatabase, + rebase_into: &Type<'db>, + ) -> Option { + if self.owner.can_rebase_into(db, rebase_into.owner, self.ty) { + Some(Type { owner: rebase_into.owner, ty: self.ty }) + } else { + None + } + } + + /// If `self` can be rebased into `rebase_into`, returns that. Otherwise, instantiates `self` with errors + /// and returns that. + pub fn rebase_into_or_error( + &self, + db: &'db dyn HirDatabase, + rebase_into: &Type<'db>, + ) -> Type<'db> { + self.try_rebase_into(db, rebase_into).unwrap_or_else(|| self.instantiate_with_errors()) + } + + pub fn try_rebase_into_owner( + &self, + db: &'db dyn HirDatabase, + new_owner: GenericDef, + ) -> Option { + let new_owner = new_owner.id()?.into(); + if self.owner.can_rebase_into(db, new_owner, self.ty) { + Some(Type { owner: new_owner, ty: self.ty }) + } else { + None + } + } + + pub fn rebase_into_owner_or_error( + &self, + db: &'db dyn HirDatabase, + new_owner: GenericDef, + ) -> Self { + self.try_rebase_into_owner(db, new_owner).unwrap_or_else(|| self.instantiate_with_errors()) + } + + pub fn unknown() -> Self { + let interner = DbInterner::conjure(); + Type::no_params( + Self::builtin_type_crate(interner.db()), + Ty::new_error(interner, ErrorGuaranteed), + ) } pub fn new_slice(db: &'db dyn HirDatabase, ty: Self) -> Self { let interner = DbInterner::new_no_crate(db); - Type { env: ty.env, ty: Ty::new_slice(interner, ty.ty) } + Type { owner: ty.owner, ty: ty.ty.map_bound(|ty| Ty::new_slice(interner, ty)) } } - pub fn new_tuple(db: &'db dyn HirDatabase, krate: base_db::Crate, tys: &[Self]) -> Self { - let tys = tys.iter().map(|it| it.ty); - let interner = DbInterner::new_with(db, krate); - Type { - env: ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate }, - ty: Ty::new_tup_from_iter(interner, tys), - } + pub fn new_tuple( + db: &'db dyn HirDatabase, + tys: impl IntoIterator>>, + ) -> Self { + let interner = DbInterner::new_no_crate(db); + let mut owner = None::; + let ty = EarlyBinder::bind(Ty::new_tup_from_iter( + interner, + tys.into_iter().map(|ty| { + let ty = ty.borrow(); + + match &mut owner { + Some(owner) => *owner = owner.must_unify(ty.owner), + None => owner = Some(ty.owner), + } + + ty.ty.skip_binder() + }), + )); + let owner = + owner.unwrap_or_else(|| TypeOwnerId::NoParams(Self::builtin_type_crate(interner.db()))); + Type { owner, ty } + } + + pub fn new_unit() -> Self { + let interner = DbInterner::conjure(); + Type::no_params(Self::builtin_type_crate(interner.db()), Ty::new_unit(interner)) } pub fn is_unit(&self) -> bool { - self.ty.is_unit() + self.ty.skip_binder().is_unit() } pub fn is_bool(&self) -> bool { - matches!(self.ty.kind(), TyKind::Bool) + matches!(self.ty.skip_binder().kind(), TyKind::Bool) } pub fn is_str(&self) -> bool { - matches!(self.ty.kind(), TyKind::Str) + matches!(self.ty.skip_binder().kind(), TyKind::Str) } pub fn is_never(&self) -> bool { - matches!(self.ty.kind(), TyKind::Never) + matches!(self.ty.skip_binder().kind(), TyKind::Never) } pub fn is_mutable_reference(&self) -> bool { - matches!(self.ty.kind(), TyKind::Ref(.., hir_ty::next_solver::Mutability::Mut)) + matches!( + self.ty.skip_binder().kind(), + TyKind::Ref(.., hir_ty::next_solver::Mutability::Mut) + ) } pub fn is_reference(&self) -> bool { - matches!(self.ty.kind(), TyKind::Ref(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Ref(..)) } pub fn contains_reference(&self, db: &'db dyn HirDatabase) -> bool { let interner = DbInterner::new_no_crate(db); - return self.ty.visit_with(&mut Visitor { interner }).is_break(); + return self + .ty + .instantiate_identity() + .skip_norm_wip() + .visit_with(&mut Visitor { interner }) + .is_break(); fn is_phantom_data(db: &dyn HirDatabase, adt_id: AdtId) -> bool { match adt_id { @@ -5727,89 +5588,129 @@ impl<'db> Type<'db> { } pub fn as_reference(&self) -> Option<(Type<'db>, Mutability)> { - let TyKind::Ref(_lt, ty, m) = self.ty.kind() else { return None }; + let TyKind::Ref(_lt, ty, m) = self.ty.skip_binder().kind() else { return None }; let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut)); Some((self.derived(ty), m)) } + pub fn as_reference_inner(&self) -> Option> { + self.as_reference().map(|(inner, _)| inner) + } + pub fn add_reference(&self, db: &'db dyn HirDatabase, mutability: Mutability) -> Self { let interner = DbInterner::new_no_crate(db); let ty_mutability = match mutability { Mutability::Shared => hir_ty::next_solver::Mutability::Not, Mutability::Mut => hir_ty::next_solver::Mutability::Mut, }; - self.derived(Ty::new_ref(interner, Region::error(interner), self.ty, ty_mutability)) + self.derived(Ty::new_ref( + interner, + Region::error(interner), + self.ty.skip_binder(), + ty_mutability, + )) } pub fn is_slice(&self) -> bool { - matches!(self.ty.kind(), TyKind::Slice(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Slice(..)) } pub fn is_usize(&self) -> bool { - matches!(self.ty.kind(), TyKind::Uint(rustc_type_ir::UintTy::Usize)) + matches!(self.ty.skip_binder().kind(), TyKind::Uint(rustc_type_ir::UintTy::Usize)) } pub fn is_float(&self) -> bool { - matches!(self.ty.kind(), TyKind::Float(_)) + matches!(self.ty.skip_binder().kind(), TyKind::Float(_)) } pub fn is_char(&self) -> bool { - matches!(self.ty.kind(), TyKind::Char) + matches!(self.ty.skip_binder().kind(), TyKind::Char) } pub fn is_int_or_uint(&self) -> bool { - matches!(self.ty.kind(), TyKind::Int(_) | TyKind::Uint(_)) + matches!(self.ty.skip_binder().kind(), TyKind::Int(_) | TyKind::Uint(_)) } pub fn is_scalar(&self) -> bool { matches!( - self.ty.kind(), + self.ty.skip_binder().kind(), TyKind::Bool | TyKind::Char | TyKind::Int(_) | TyKind::Uint(_) | TyKind::Float(_) ) } pub fn is_tuple(&self) -> bool { - matches!(self.ty.kind(), TyKind::Tuple(..)) - } - - pub fn remove_ref(&self) -> Option> { - match self.ty.kind() { - TyKind::Ref(_, ty, _) => Some(self.derived(ty)), - _ => None, - } + matches!(self.ty.skip_binder().kind(), TyKind::Tuple(..)) } pub fn as_slice(&self) -> Option> { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Slice(ty) => Some(self.derived(ty)), _ => None, } } pub fn strip_references(&self) -> Self { - self.derived(self.ty.strip_references()) + self.derived(self.ty.skip_binder().strip_references()) } // FIXME: This is the same as `remove_ref()`, remove one of these methods. pub fn strip_reference(&self) -> Self { - self.derived(self.ty.strip_reference()) + self.derived(self.ty.skip_binder().strip_reference()) } pub fn is_unknown(&self) -> bool { - self.ty.is_ty_error() + self.ty.skip_binder().is_ty_error() + } + + fn krate(&self, db: &'db dyn HirDatabase) -> base_db::Crate { + match self.owner { + TypeOwnerId::GenericDefId(def) => hir_def::HasModule::krate(&def, db), + TypeOwnerId::BuiltinDeriveImplId(def) => { + hir_def::HasModule::krate(&def.loc(db).adt, db) + } + TypeOwnerId::AnonConstId(def) => hir_def::HasModule::krate(&def, db), + TypeOwnerId::NoParams(krate) => krate, + } + } + + fn param_env(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> { + let interner = DbInterner::new_no_crate(db); + let krate = self.krate(db); + match self.owner { + TypeOwnerId::GenericDefId(def) => { + ParamEnvAndCrate { param_env: db.trait_environment(def), krate } + } + TypeOwnerId::BuiltinDeriveImplId(def) => ParamEnvAndCrate { + param_env: hir_ty::builtin_derive::param_env(interner, def), + krate, + }, + TypeOwnerId::AnonConstId(def) => ParamEnvAndCrate { + param_env: db.trait_environment(def.loc(db).owner.generic_def(db)), + krate, + }, + TypeOwnerId::NoParams(_) => { + ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate } + } + } } /// Checks that particular type `ty` implements `std::future::IntoFuture` or /// `std::future::Future` and returns the `Output` associated type. /// This function is used in `.await` syntax completion. pub fn into_future_output(&self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let (trait_, output_assoc_type) = lang_items .IntoFuture .zip(lang_items.IntoFutureOutput) .or(lang_items.Future.zip(lang_items.FutureOutput))?; - if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { + if !traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + trait_, + ) { return None; } @@ -5818,32 +5719,46 @@ impl<'db> Type<'db> { /// This does **not** resolve `IntoFuture`, only `Future`. pub fn future_output(self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let krate = self.krate(db); + let lang_items = hir_def::lang_item::lang_items(db, krate); let future_output = lang_items.FutureOutput?; self.normalize_trait_assoc_type(db, &[], future_output.into()) } /// This does **not** resolve `IntoIterator`, only `Iterator`. pub fn iterator_item(self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let krate = self.krate(db); + let lang_items = hir_def::lang_item::lang_items(db, krate); let iterator_item = lang_items.IteratorItem?; self.normalize_trait_assoc_type(db, &[], iterator_item.into()) } pub fn impls_iterator(self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let Some(iterator_trait) = lang_items.Iterator else { return false; }; - traits::implements_trait_unique(self.ty, db, self.env, iterator_trait) + traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + iterator_trait, + ) } /// Resolves the projection `::IntoIter` and returns the resulting type pub fn into_iterator_iter(self, db: &'db dyn HirDatabase) -> Option> { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let trait_ = lang_items.IntoIterator?; - if !traits::implements_trait_unique(self.ty, db, self.env, trait_) { + if !traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + trait_, + ) { return None; } @@ -5856,24 +5771,63 @@ impl<'db> Type<'db> { /// This function can be used to check if a particular type is callable, since FnOnce is a /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. pub fn impls_fnonce(&self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let fnonce_trait = match lang_items.FnOnce { Some(it) => it, None => return false, }; - traits::implements_trait_unique(self.ty, db, self.env, fnonce_trait) + traits::implements_trait_unique( + self.ty.instantiate_identity().skip_norm_wip(), + db, + env, + fnonce_trait, + ) } // FIXME: Find better API that also handles const generics pub fn impls_trait(&self, db: &'db dyn HirDatabase, trait_: Trait, args: &[Type<'db>]) -> bool { + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let args = generic_args_from_tys( - interner, - trait_.id.into(), - std::iter::once(self.ty).chain(args.iter().map(|ty| ty.ty)), - ); - traits::implements_trait_unique_with_args(db, self.env, trait_.id, args) + let (args, _owner) = + generic_args_from_tys(interner, trait_.id.into(), iter::once(self).chain(args)); + traits::implements_trait_unique_with_args(db, env, trait_.id, args) + } + + /// Unlike [`Type::impls_trait()`], which checks whether the type always implements the trait, + /// this check whether there are any generic args substitution for `args`` that will cause the + /// trait to be implemented. + /// + /// For example, suppose we're there's `struct Foo` and we're checking `Foo: Trait`. + /// `impls_trait()` will return true only if there is `impl Trait for Foo`, while this + /// method will also return true if there is only `impl Trait for Foo`. + /// + /// Note that you can of course instantiate `Foo` with `` and then the checks will + /// be the same, but this check for *any* substitution. + /// + /// Unlike almost anything that takes more than one type, you *can* pass types from different origins + /// to this function. + pub fn has_any_impl( + &self, + db: &'db dyn HirDatabase, + trait_: Trait, + args: &[Type<'db>], + ) -> bool { + let interner = DbInterner::new_no_crate(db); + let env = ParamEnvAndCrate { param_env: ParamEnv::empty(interner), krate: self.krate(db) }; + traits::implements_trait_unique_with_infcx(db, env, trait_.id, &mut |infcx| { + let mut args = Self::instantiate_many_with_infer(iter::once(self).chain(args), infcx); + GenericArgs::for_item(infcx.interner, trait_.id.into(), |_, param, _| { + if let GenericParamId::TypeParamId(_) = param + && let Some(arg) = args.next() + { + arg.into() + } else { + infcx.var_for_def(param, hir_ty::Span::Dummy) + } + }) + }) } pub fn normalize_trait_assoc_type( @@ -5882,12 +5836,10 @@ impl<'db> Type<'db> { args: &[Type<'db>], alias: TypeAlias, ) -> Option> { - let interner = DbInterner::new_with(db, self.env.krate); - let args = generic_args_from_tys( - interner, - alias.id.into(), - std::iter::once(self.ty).chain(args.iter().map(|ty| ty.ty)), - ); + let env = self.param_env(db); + let interner = DbInterner::new_with(db, env.krate); + let (args, owner) = + generic_args_from_tys(interner, alias.id.into(), iter::once(self).chain(args)); // FIXME: We don't handle GATs yet. let projection = Ty::new_alias( interner, @@ -5899,12 +5851,13 @@ impl<'db> Type<'db> { ); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let ty = structurally_normalize_ty(&infcx, projection, self.env.param_env); - if ty.is_ty_error() { None } else { Some(self.derived(ty)) } + let ty = structurally_normalize_ty(&infcx, projection, env.param_env); + if ty.is_ty_error() { None } else { Some(Type { owner, ty: EarlyBinder::bind(ty) }) } } pub fn is_copy(&self, db: &'db dyn HirDatabase) -> bool { - let lang_items = hir_def::lang_item::lang_items(db, self.env.krate); + let env = self.param_env(db); + let lang_items = hir_def::lang_item::lang_items(db, env.krate); let Some(copy_trait) = lang_items.Copy else { return false; }; @@ -5913,7 +5866,7 @@ impl<'db> Type<'db> { pub fn as_callable(&self, db: &'db dyn HirDatabase) -> Option> { let interner = DbInterner::new_no_crate(db); - let callee = match self.ty.kind() { + let callee = match self.ty.skip_binder().kind() { TyKind::Closure(id, subst) => Callee::Closure(id.0, subst), TyKind::CoroutineClosure(id, subst) => Callee::CoroutineClosure(id.0, subst), TyKind::FnPtr(..) => Callee::FnPtr, @@ -5921,7 +5874,9 @@ impl<'db> Type<'db> { // This will happen when it implements fn or fn mut, since we add an autoborrow adjustment TyKind::Ref(_, inner_ty, _) => return self.derived(inner_ty).as_callable(db), _ => { - let (fn_trait, sig) = hir_ty::callable_sig_from_fn_trait(self.ty, self.env, db)?; + let env = self.param_env(db); + let (fn_trait, sig) = + hir_ty::callable_sig_from_fn_trait(self.ty.skip_binder(), env, db)?; return Some(Callable { ty: self.clone(), sig, @@ -5931,74 +5886,77 @@ impl<'db> Type<'db> { } }; - let sig = self.ty.callable_sig(interner)?; + let sig = self.ty.skip_binder().callable_sig(interner)?; Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false }) } pub fn is_closure(&self) -> bool { - matches!(self.ty.kind(), TyKind::Closure { .. }) + matches!(self.ty.skip_binder().kind(), TyKind::Closure { .. }) } pub fn as_closure(&self) -> Option> { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Closure(id, subst) => { - Some(Closure { id: AnyClosureId::ClosureId(id.0), subst }) - } - TyKind::CoroutineClosure(id, subst) => { - Some(Closure { id: AnyClosureId::CoroutineClosureId(id.0), subst }) + Some(Closure { id: AnyClosureId::ClosureId(id.0), subst, owner: self.owner }) } + TyKind::CoroutineClosure(id, subst) => Some(Closure { + id: AnyClosureId::CoroutineClosureId(id.0), + subst, + owner: self.owner, + }), _ => None, } } pub fn is_fn(&self) -> bool { - matches!(self.ty.kind(), TyKind::FnDef(..) | TyKind::FnPtr { .. }) + matches!(self.ty.skip_binder().kind(), TyKind::FnDef(..) | TyKind::FnPtr { .. }) } pub fn is_array(&self) -> bool { - matches!(self.ty.kind(), TyKind::Array(..)) + matches!(self.ty.skip_binder().kind(), TyKind::Array(..)) } - pub fn is_packed(&self, db: &'db dyn HirDatabase) -> bool { - let adt_id = match self.ty.kind() { - TyKind::Adt(adt_def, ..) => adt_def.def_id(), - _ => return false, - }; - - let adt = adt_id.into(); - match adt { - Adt::Struct(s) => s.repr(db).unwrap_or_default().pack.is_some(), + pub fn is_packed(&self, _db: &'db dyn HirDatabase) -> bool { + match self.ty.skip_binder().kind() { + TyKind::Adt(adt_def, ..) => adt_def.is_packed(), _ => false, } } pub fn is_raw_ptr(&self) -> bool { - matches!(self.ty.kind(), TyKind::RawPtr(..)) + matches!(self.ty.skip_binder().kind(), TyKind::RawPtr(..)) } pub fn is_mutable_raw_ptr(&self) -> bool { // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers). - matches!(self.ty.kind(), TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut)) + matches!( + self.ty.skip_binder().kind(), + TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut) + ) } pub fn as_raw_ptr(&self) -> Option<(Type<'db>, Mutability)> { // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers). - let TyKind::RawPtr(ty, m) = self.ty.kind() else { return None }; + let TyKind::RawPtr(ty, m) = self.ty.skip_binder().kind() else { return None }; let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut)); Some((self.derived(ty), m)) } pub fn remove_raw_ptr(&self) -> Option> { - if let TyKind::RawPtr(ty, _) = self.ty.kind() { Some(self.derived(ty)) } else { None } + if let TyKind::RawPtr(ty, _) = self.ty.skip_binder().kind() { + Some(self.derived(ty)) + } else { + None + } } pub fn contains_unknown(&self) -> bool { - self.ty.references_non_lt_error() + self.ty.skip_binder().references_non_lt_error() } pub fn fields(&self, db: &'db dyn HirDatabase) -> Vec<(Field, Self)> { let interner = DbInterner::new_no_crate(db); - let (variant_id, substs) = match self.ty.kind() { + let (variant_id, substs) = match self.ty.skip_binder().kind() { TyKind::Adt(adt_def, substs) => { let id = match adt_def.def_id() { AdtId::StructId(id) => id.into(), @@ -6021,7 +5979,7 @@ impl<'db> Type<'db> { } pub fn tuple_fields(&self, _db: &'db dyn HirDatabase) -> Vec { - if let TyKind::Tuple(substs) = self.ty.kind() { + if let TyKind::Tuple(substs) = self.ty.skip_binder().kind() { substs.iter().map(|ty| self.derived(ty)).collect() } else { Vec::new() @@ -6029,17 +5987,18 @@ impl<'db> Type<'db> { } pub fn as_array(&self, db: &'db dyn HirDatabase) -> Option<(Self, usize)> { - if let TyKind::Array(ty, len) = self.ty.kind() { + if let TyKind::Array(ty, len) = self.ty.skip_binder().kind() { try_const_usize(db, len).map(|it| (self.derived(ty), it as usize)) } else { None } } + // FIXME: We should probably remove this. pub fn fingerprint_for_trait_impl(&self, db: &'db dyn HirDatabase) -> Option { fast_reject::simplify_type( DbInterner::new_no_crate(db), - self.ty, + self.ty.skip_binder(), fast_reject::TreatParams::AsRigid, ) } @@ -6055,9 +6014,10 @@ impl<'db> Type<'db> { fn autoderef_(&self, db: &'db dyn HirDatabase) -> impl Iterator> { let interner = DbInterner::new_no_crate(db); + let env = self.param_env(db); // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(interner, &self.ty); - autoderef(db, self.env, canonical) + let canonical = hir_ty::replace_errors_with_variables(interner, &self.ty.skip_binder()); + autoderef(db, env, canonical) } // This would be nicer if it just returned an iterator, but that runs into @@ -6089,17 +6049,20 @@ impl<'db> Type<'db> { } } }; + let krate = self.krate(db); let interner = DbInterner::new_no_crate(db); - let Some(simplified_type) = - fast_reject::simplify_type(interner, self.ty, fast_reject::TreatParams::AsRigid) - else { + let Some(simplified_type) = fast_reject::simplify_type( + interner, + self.ty.skip_binder(), + fast_reject::TreatParams::AsRigid, + ) else { return; }; method_resolution::with_incoherent_inherent_impls( db, - self.env.krate, + krate, &simplified_type, &mut handle_impls, ); @@ -6135,12 +6098,12 @@ impl<'db> Type<'db> { /// - "U" /// ``` pub fn type_arguments(&self) -> impl Iterator> + '_ { - match self.ty.strip_references().kind() { + match self.ty.skip_binder().strip_references().kind() { TyKind::Adt(_, substs) => Either::Left(substs.types().map(move |ty| self.derived(ty))), TyKind::Tuple(substs) => { Either::Right(Either::Left(substs.iter().map(move |ty| self.derived(ty)))) } - _ => Either::Right(Either::Right(std::iter::empty())), + _ => Either::Right(Either::Right(iter::empty())), } } @@ -6168,6 +6131,7 @@ impl<'db> Type<'db> { display_target: DisplayTarget, ) -> impl Iterator + 'a { self.ty + .skip_binder() .strip_references() .as_adt() .into_iter() @@ -6258,7 +6222,7 @@ impl<'db> Type<'db> { }; let infcx = interner.infer_ctxt().build(typing_mode); let features = resolver.top_level_def_map().features(); - let environment = param_env_from_resolver(db, resolver); + let environment = self.param_env(db); let ctx = MethodResolutionContext { infcx: &infcx, resolver, @@ -6293,7 +6257,8 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); + let canonical = + hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty.skip_binder()); let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { @@ -6401,7 +6366,8 @@ impl<'db> Type<'db> { self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| { // There should be no inference vars in types passed here - let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty); + let canonical = + hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty.skip_binder()); let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical); match name { @@ -6450,23 +6416,23 @@ impl<'db> Type<'db> { } pub fn as_adt(&self) -> Option { - let (adt, _subst) = self.ty.as_adt()?; + let (adt, _subst) = self.ty.skip_binder().as_adt()?; Some(adt.into()) } /// Holes in the args can come from lifetime/const params. pub fn as_adt_with_args(&self) -> Option<(Adt, Vec>>)> { - let (adt, args) = self.ty.as_adt()?; + let (adt, args) = self.ty.skip_binder().as_adt()?; let args = args.iter().map(|arg| Some(self.derived(arg.ty()?))).collect(); Some((adt.into(), args)) } pub fn as_builtin(&self) -> Option { - self.ty.as_builtin().map(|inner| BuiltinType { inner }) + self.ty.skip_binder().as_builtin().map(|inner| BuiltinType { inner }) } pub fn as_dyn_trait(&self) -> Option { - self.ty.dyn_trait().map(Into::into) + self.ty.skip_binder().dyn_trait().map(Into::into) } /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type, @@ -6485,11 +6451,11 @@ impl<'db> Type<'db> { pub fn env_traits(&self, db: &'db dyn HirDatabase) -> impl Iterator { let _p = tracing::info_span!("env_traits").entered(); + let env = self.param_env(db); self.autoderef_(db) .filter(|ty| matches!(ty.kind(), TyKind::Param(_))) - .flat_map(|ty| { - self.env - .param_env + .flat_map(move |ty| { + env.param_env .clauses() .iter() .filter_map(move |pred| match pred.kind().skip_binder() { @@ -6503,7 +6469,7 @@ impl<'db> Type<'db> { } pub fn as_impl_traits(&self, db: &'db dyn HirDatabase) -> Option> { - self.ty.impl_trait_bounds(db).map(|it| { + self.ty.skip_binder().impl_trait_bounds(db).map(|it| { it.into_iter().filter_map(|pred| match pred.kind().skip_binder() { ClauseKind::Trait(trait_ref) => Some(Trait::from(trait_ref.def_id().0)), _ => None, @@ -6513,7 +6479,7 @@ impl<'db> Type<'db> { pub fn as_associated_type_parent_trait(&self, db: &'db dyn HirDatabase) -> Option { let TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { def_id }, .. }) = - self.ty.kind() + self.ty.skip_binder().kind() else { return None; }; @@ -6524,7 +6490,7 @@ impl<'db> Type<'db> { } fn derived(&self, ty: Ty<'db>) -> Self { - Type { env: self.env, ty } + Type { owner: self.owner, ty: EarlyBinder::bind(ty) } } /// Visits every type, including generic arguments, in this type. `callback` is called with type @@ -6532,7 +6498,7 @@ impl<'db> Type<'db> { pub fn walk(&self, db: &'db dyn HirDatabase, callback: impl FnMut(Type<'db>)) { struct Visitor<'db, F> { db: &'db dyn HirDatabase, - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, callback: F, visited: FxHashSet>, } @@ -6547,7 +6513,7 @@ impl<'db> Type<'db> { return; } - (self.callback)(Type { env: self.env, ty }); + (self.callback)(Type { owner: self.owner, ty: EarlyBinder::bind(ty) }); if let Some(bounds) = ty.impl_trait_bounds(self.db) { bounds.visit_with(self); @@ -6557,17 +6523,23 @@ impl<'db> Type<'db> { } } - let mut visitor = Visitor { db, env: self.env, callback, visited: FxHashSet::default() }; - self.ty.visit_with(&mut visitor); + let mut visitor = + Visitor { db, owner: self.owner, callback, visited: FxHashSet::default() }; + self.ty.skip_binder().visit_with(&mut visitor); } /// Check if type unifies with another type. /// /// Note that we consider placeholder types to unify with everything. /// For example `Option` and `Option` unify although there is unresolved goal `T = U`. pub fn could_unify_with(&self, db: &'db dyn HirDatabase, other: &Type<'db>) -> bool { + self.owner.must_unify(other.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, other.ty)); - hir_ty::could_unify(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), other.ty.skip_binder()), + ); + hir_ty::could_unify(db, env, &tys) } /// Check if type unifies with another type eagerly making sure there are no unresolved goals. @@ -6575,19 +6547,29 @@ impl<'db> Type<'db> { /// This means that placeholder types are not considered to unify if there are any bounds set on /// them. For example `Option` and `Option` do not unify as we cannot show that `T = U` pub fn could_unify_with_deeply(&self, db: &'db dyn HirDatabase, other: &Type<'db>) -> bool { + self.owner.must_unify(other.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, other.ty)); - hir_ty::could_unify_deeply(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), other.ty.skip_binder()), + ); + hir_ty::could_unify_deeply(db, env, &tys) } pub fn could_coerce_to(&self, db: &'db dyn HirDatabase, to: &Type<'db>) -> bool { + self.owner.must_unify(to.owner); + let env = self.param_env(db); let interner = DbInterner::new_no_crate(db); - let tys = hir_ty::replace_errors_with_variables(interner, &(self.ty, to.ty)); - hir_ty::could_coerce(db, self.env, &tys) + let tys = hir_ty::replace_errors_with_variables( + interner, + &(self.ty.skip_binder(), to.ty.skip_binder()), + ); + hir_ty::could_coerce(db, env, &tys) } pub fn as_type_param(&self, _db: &'db dyn HirDatabase) -> Option { - match self.ty.kind() { + match self.ty.skip_binder().kind() { TyKind::Param(param) => Some(TypeParam { id: param.id }), _ => None, } @@ -6595,60 +6577,23 @@ impl<'db> Type<'db> { /// Returns unique `GenericParam`s contained in this type. pub fn generic_params(&self, db: &'db dyn HirDatabase) -> FxHashSet { - hir_ty::collect_params(&self.ty) + hir_ty::collect_params(&self.ty.skip_binder()) .into_iter() .map(|id| TypeOrConstParam { id }.split(db).either_into()) .collect() } pub fn layout(&self, db: &'db dyn HirDatabase) -> Result, LayoutError> { - db.layout_of_ty(self.ty.store(), self.env.store()) - .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) + let env = self.param_env(db); + db.layout_of_ty(self.ty.skip_binder().store(), env.store()) + .map(|layout| Layout(layout, db.target_data_layout(env.krate).unwrap())) } pub fn drop_glue(&self, db: &'db dyn HirDatabase) -> DropGlue { - let interner = DbInterner::new_with(db, self.env.krate); + let env = self.param_env(db); + let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - hir_ty::drop::has_drop_glue(&infcx, self.ty, self.env.param_env) - } -} - -#[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct TypeNs<'db> { - env: ParamEnvAndCrate<'db>, - ty: Ty<'db>, -} - -impl<'db> TypeNs<'db> { - fn new(db: &'db dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty<'db>) -> Self { - let resolver = lexical_env.resolver(db); - let environment = param_env_from_resolver(db, &resolver); - TypeNs { env: environment, ty } - } - - pub fn to_type(&self, _db: &'db dyn HirDatabase) -> Type<'db> { - Type { env: self.env, ty: self.ty } - } - - // FIXME: Find better API that also handles const generics - pub fn impls_trait(&self, infcx: InferCtxt<'db>, trait_: Trait, args: &[TypeNs<'db>]) -> bool { - let args = GenericArgs::new_from_iter( - infcx.interner, - [self.ty].into_iter().chain(args.iter().map(|t| t.ty)).map(GenericArg::from), - ); - let trait_ref = - hir_ty::next_solver::TraitRef::new_from_args(infcx.interner, trait_.id.into(), args); - let obligation = hir_ty::next_solver::infer::traits::Obligation::new( - infcx.interner, - hir_ty::next_solver::infer::traits::ObligationCause::dummy(), - self.env.param_env, - trait_ref, - ); - infcx.predicate_must_hold_modulo_regions(&obligation) - } - - pub fn is_bool(&self) -> bool { - matches!(self.ty.kind(), rustc_type_ir::TyKind::Bool) + hir_ty::drop::has_drop_glue(&infcx, self.ty.skip_binder(), env.param_env) } } @@ -6713,12 +6658,16 @@ impl<'db> Callable<'db> { Callee::Def(CallableDefId::EnumVariantId(it)) => { CallableKind::TupleEnumVariant(it.into()) } - Callee::Closure(id, subst) => { - CallableKind::Closure(Closure { id: AnyClosureId::ClosureId(id), subst }) - } - Callee::CoroutineClosure(id, subst) => { - CallableKind::Closure(Closure { id: AnyClosureId::CoroutineClosureId(id), subst }) - } + Callee::Closure(id, subst) => CallableKind::Closure(Closure { + id: AnyClosureId::ClosureId(id), + subst, + owner: self.ty.owner, + }), + Callee::CoroutineClosure(id, subst) => CallableKind::Closure(Closure { + id: AnyClosureId::CoroutineClosureId(id), + subst, + owner: self.ty.owner, + }), Callee::FnPtr => CallableKind::FnPtr, Callee::FnImpl(fn_) => CallableKind::FnImpl(fn_.into()), } @@ -6859,7 +6808,7 @@ impl<'db> Layout<'db> { reverse_index .into_iter() .flatten() - .chain(std::iter::once((0, self.0.size.bytes()))) + .chain(iter::once((0, self.0.size.bytes()))) .tuple_windows() .filter_map(|((i, start), (_, end))| { let size = field_size(i)?; @@ -7017,7 +6966,7 @@ pub enum PredicatePolarity { #[derive(Debug, Clone, PartialEq, Eq)] pub struct TraitPredicate<'db> { inner: hir_ty::next_solver::TraitPredicate<'db>, - env: ParamEnvAndCrate<'db>, + owner: TypeOwnerId, } impl<'db> TraitPredicate<'db> { @@ -7029,7 +6978,7 @@ impl<'db> TraitPredicate<'db> { } pub fn trait_ref(&self) -> TraitRef<'db> { - TraitRef { env: self.env, trait_ref: self.inner.trait_ref } + TraitRef { owner: self.owner, trait_ref: self.inner.trait_ref } } } @@ -7099,8 +7048,8 @@ impl HasCrate for TypeAlias { } impl HasCrate for Type<'_> { - fn krate(&self, _db: &dyn HirDatabase) -> Crate { - self.env.krate.into() + fn krate(&self, db: &dyn HirDatabase) -> Crate { + self.krate(db).into() } } @@ -7419,21 +7368,33 @@ fn as_name_opt(name: Option) -> Name { name.map_or_else(Name::missing, |name| name.as_name()) } +#[track_caller] fn generic_args_from_tys<'db>( interner: DbInterner<'db>, def_id: SolverDefId, - args: impl IntoIterator>, -) -> GenericArgs<'db> { + args: impl IntoIterator>>, +) -> (GenericArgs<'db>, TypeOwnerId) { + let mut owner = None::; let mut args = args.into_iter(); - GenericArgs::for_item(interner, def_id, |_, id, _| { + let args = GenericArgs::for_item(interner, def_id, |_, id, _| { if matches!(id, GenericParamId::TypeParamId(_)) && let Some(arg) = args.next() { - arg.into() + let arg = arg.borrow(); + + match &mut owner { + Some(owner) => *owner = owner.must_unify(arg.owner), + None => owner = Some(arg.owner), + } + + arg.ty.skip_binder().into() } else { next_solver::GenericArg::error_from_id(interner, id) } - }) + }); + let owner = + owner.unwrap_or_else(|| TypeOwnerId::NoParams(Type::builtin_type_crate(interner.db()))); + (args, owner) } fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) -> bool { @@ -7449,19 +7410,6 @@ fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId) }) } -fn param_env_from_resolver<'db>( - db: &'db dyn HirDatabase, - resolver: &Resolver<'_>, -) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { - param_env: resolver.generic_def().map_or_else( - || ParamEnv::empty(DbInterner::new_no_crate(db)), - |generic_def| db.trait_environment(generic_def), - ), - krate: resolver.krate(), - } -} - fn param_env_from_has_crate<'db>( db: &'db dyn HirDatabase, id: impl hir_def::HasModule + Into + Copy, @@ -7469,16 +7417,6 @@ fn param_env_from_has_crate<'db>( ParamEnvAndCrate { param_env: db.trait_environment(id.into()), krate: id.krate(db) } } -fn body_param_env_from_has_crate<'db>( - db: &'db dyn HirDatabase, - id: impl hir_def::HasModule + Into + Copy, -) -> ParamEnvAndCrate<'db> { - ParamEnvAndCrate { - param_env: db.trait_environment(id.into().generic_def(db)), - krate: id.krate(db), - } -} - // FIXME: We probably don't want to expose this. pub trait MacroCallIdExt { fn loc(self, db: &dyn HirDatabase) -> &hir_expand::MacroCallLoc; @@ -7527,6 +7465,3 @@ pub fn struct_tail_raw<'db>( } ty } - -pub use hir_ty::next_solver; -pub use hir_ty::setup_tracing; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 562c78809a6df..a23d045c6fc0a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1745,11 +1745,7 @@ impl<'db> SemanticsImpl<'db> { analyzer.expr_adjustments(expr).map(|it| { it.iter() .map(|adjust| { - let target = Type::new_with_resolver( - self.db, - &analyzer.resolver, - adjust.target.as_ref(), - ); + let target = analyzer.ty(adjust.target.as_ref()); let kind = match adjust.kind { hir_ty::Adjust::NeverToAny => Adjust::NeverToAny, hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => { @@ -1840,10 +1836,10 @@ impl<'db> SemanticsImpl<'db> { let substs = hir_ty::next_solver::GenericArgs::for_item(interner, trait_.id.into(), |_, id, _| { assert!(matches!(id, hir_def::GenericParamId::TypeParamId(_)), "expected a type"); - subst.next().expect("too few subst").ty.into() + subst.next().expect("too few subst").ty.skip_binder().into() }); assert!(subst.next().is_none(), "too many subst"); - Some(match self.db.lookup_impl_method(env.env, func, substs).0 { + Some(match self.db.lookup_impl_method(env.param_env(self.db), func, substs).0 { Either::Left(it) => it.into(), Either::Right((impl_, method)) => { Function { id: AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } } 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 3afa98365e18b..f24ec420b5dfb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -41,8 +41,8 @@ use hir_ty::{ lang_items::lang_items_for_bin_op, method_resolution::{self, CandidateId}, next_solver::{ - AliasTy, DbInterner, DefaultAny, ErrorGuaranteed, GenericArgs, ParamEnv, Region, Ty, - TyKind, TypingMode, infer::DbInternerInferExt, + AliasTy, DbInterner, DefaultAny, EarlyBinder, ErrorGuaranteed, GenericArgs, ParamEnv, + Region, Ty, TyKind, TypingMode, infer::DbInternerInferExt, }, traits::structurally_normalize_ty, }; @@ -63,7 +63,7 @@ use syntax::{ use crate::{ Adt, AnyFunctionId, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const, DeriveHelper, EnumVariant, Field, Function, GenericSubstitution, Local, Macro, ModuleDef, - SemanticsImpl, Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias, + SemanticsImpl, Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias, TypeOwnerId, db::HirDatabase, semantics::{PathResolution, PathResolutionPerNs}, }; @@ -75,6 +75,7 @@ pub(crate) struct SourceAnalyzer<'db> { pub(crate) file_id: HirFileId, pub(crate) resolver: Resolver<'db>, pub(crate) body_or_sig: Option>, + pub(crate) type_owner: TypeOwnerId, pub(crate) infer_body: Option, } @@ -148,6 +149,7 @@ impl<'db> SourceAnalyzer<'db> { resolver, body_or_sig: Some(BodyOrSig::Body { def, body, source_map, infer }), file_id, + type_owner: def.generic_def(db).into(), infer_body: Some(def.into()), } } @@ -212,6 +214,7 @@ impl<'db> SourceAnalyzer<'db> { resolver, body_or_sig: Some(BodyOrSig::Sig { def, store, source_map, generics, infer }), file_id, + type_owner: def.into(), infer_body, } } @@ -261,6 +264,7 @@ impl<'db> SourceAnalyzer<'db> { infer, }), file_id, + type_owner: GenericDefId::from(def.adt_id(db)).into(), infer_body, } } @@ -269,7 +273,16 @@ impl<'db> SourceAnalyzer<'db> { resolver: Resolver<'db>, node: InFile<&SyntaxNode>, ) -> SourceAnalyzer<'db> { - SourceAnalyzer { resolver, body_or_sig: None, file_id: node.file_id, infer_body: None } + SourceAnalyzer { + type_owner: resolver + .generic_def() + .map(Into::into) + .unwrap_or_else(|| TypeOwnerId::NoParams(resolver.krate())), + resolver, + body_or_sig: None, + file_id: node.file_id, + infer_body: None, + } } fn owner(&self) -> Option { @@ -288,6 +301,10 @@ impl<'db> SourceAnalyzer<'db> { }) } + pub(crate) fn ty(&self, ty: Ty<'db>) -> Type<'db> { + Type { owner: self.type_owner, ty: EarlyBinder::bind(ty) } + } + pub(crate) fn def( &self, ) -> Option<( @@ -426,12 +443,12 @@ impl<'db> SourceAnalyzer<'db> { } } - Some(Type::new_with_resolver(db, &self.resolver, ty)) + Some(self.ty(ty)) } pub(crate) fn type_of_expr( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, expr: &ast::Expr, ) -> Option<(Type<'db>, Option>)> { let expr_id = self.expr_id(expr.clone())?; @@ -441,13 +458,13 @@ impl<'db> SourceAnalyzer<'db> { .and_then(|expr_id| infer.expr_adjustment(expr_id)) .and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.as_ref())); let ty = infer.expr_or_pat_ty(expr_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some((mk_ty(ty), coerced.map(mk_ty))) } pub(crate) fn type_of_pat( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option<(Type<'db>, Option>)> { let expr_or_pat_id = self.pat_id(pat)?; @@ -464,25 +481,25 @@ impl<'db> SourceAnalyzer<'db> { }; let ty = infer.expr_or_pat_ty(expr_or_pat_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some((mk_ty(ty), coerced.map(mk_ty))) } pub(crate) fn type_of_binding_in_pat( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::IdentPat, ) -> Option> { let binding_id = self.binding_id_of_pat(pat)?; let infer = self.infer()?; let ty = infer.binding_ty(binding_id); - let mk_ty = |ty: Ty<'db>| Type::new_with_resolver(db, &self.resolver, ty); + let mk_ty = |ty: Ty<'db>| self.ty(ty); Some(mk_ty(ty)) } pub(crate) fn type_of_self( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, _param: &ast::SelfParam, ) -> Option> { let binding = match self.body_or_sig.as_ref()? { @@ -490,7 +507,7 @@ impl<'db> SourceAnalyzer<'db> { BodyOrSig::Body { body, .. } => body.self_param()?, }; let ty = self.infer()?.binding_ty(binding); - Some(Type::new_with_resolver(db, &self.resolver, ty)) + Some(self.ty(ty)) } pub(crate) fn binding_mode_of_pat( @@ -512,7 +529,7 @@ impl<'db> SourceAnalyzer<'db> { } pub(crate) fn pattern_adjustments( &self, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, pat: &ast::Pat, ) -> Option; 1]>> { let pat_id = self.pat_id(pat)?; @@ -521,7 +538,7 @@ impl<'db> SourceAnalyzer<'db> { infer .pat_adjustment(pat_id.as_pat()?)? .iter() - .map(|adjust| Type::new_with_resolver(db, &self.resolver, adjust.source.as_ref())) + .map(|adjust| self.ty(adjust.source.as_ref())) .collect(), ) } @@ -535,7 +552,7 @@ impl<'db> SourceAnalyzer<'db> { let (func, args) = self.infer()?.method_resolution(expr_id)?; let interner = DbInterner::new_no_crate(db); let ty = db.value_ty(func.into())?.instantiate(interner, args).skip_norm_wip(); - let ty = Type::new_with_resolver(db, &self.resolver, ty); + let ty = self.ty(ty); let mut res = ty.as_callable(db)?; res.is_bound_method = true; Some(res) @@ -565,7 +582,7 @@ impl<'db> SourceAnalyzer<'db> { self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs); Some(( Either::Left(fn_), - GenericSubstitution::new_from_fn(fn_, subst, self.trait_environment(db)), + GenericSubstitution::new_from_fn(fn_, subst, self.type_owner), )) } None => { @@ -600,12 +617,12 @@ impl<'db> SourceAnalyzer<'db> { &self, field_expr: ExprId, infer: &InferenceResult, - db: &'db dyn HirDatabase, + _db: &'db dyn HirDatabase, ) -> Option> { let body = self.store()?; if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] { let (adt, subst) = infer.type_of_expr_with_adjust(object_expr)?.as_adt()?; - return Some(GenericSubstitution::new(adt.into(), subst, self.trait_environment(db))); + return Some(GenericSubstitution::new(adt.into(), subst, self.type_owner)); } None } @@ -636,10 +653,7 @@ impl<'db> SourceAnalyzer<'db> { }, None => inference_result.method_resolution(expr_id).map(|(f, substs)| { let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs); - ( - Either::Right(f), - GenericSubstitution::new_from_fn(f, subst, self.trait_environment(db)), - ) + (Either::Right(f), GenericSubstitution::new_from_fn(f, subst, self.type_owner)) }), } } @@ -729,7 +743,7 @@ impl<'db> SourceAnalyzer<'db> { .map(Trait::from); if let Some(into_future_trait) = into_future_trait { - let type_ = Type::new_with_resolver(db, &self.resolver, ty); + let type_ = self.ty(ty); if type_.impls_trait(db, into_future_trait, &[]) { let items = into_future_trait.items(db); let into_future_type = items.into_iter().find_map(|item| match item { @@ -741,7 +755,7 @@ impl<'db> SourceAnalyzer<'db> { _ => None, })?; let future_trait = type_.normalize_trait_assoc_type(db, &[], into_future_type)?; - ty = future_trait.ty; + ty = future_trait.ty.skip_binder(); } } @@ -890,8 +904,8 @@ impl<'db> SourceAnalyzer<'db> { Some(( field.into(), local, - Type::new_with_resolver(db, &self.resolver, field_ty), - GenericSubstitution::new(adt.into(), subst, self.trait_environment(db)), + self.ty(field_ty), + GenericSubstitution::new(adt.into(), subst, self.type_owner), )) } @@ -914,8 +928,8 @@ impl<'db> SourceAnalyzer<'db> { .skip_norm_wip(); Some(( field.into(), - Type::new_with_resolver(db, &self.resolver, field_ty), - GenericSubstitution::new(adt.into(), subst, self.trait_environment(db)), + self.ty(field_ty), + GenericSubstitution::new(adt.into(), subst, self.type_owner), )) } @@ -970,15 +984,15 @@ impl<'db> SourceAnalyzer<'db> { let container = offset_of_expr.ty()?; let container = self.type_of_type(db, &container)?; - let trait_env = container.env; + let env = self.trait_environment(db); - let interner = DbInterner::new_with(db, trait_env.krate); + let interner = DbInterner::new_with(db, env.krate); let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); - let mut container = Either::Right(container.ty); + let mut container = Either::Right(container.ty.skip_binder()); for field_name in offset_of_expr.fields() { if let Either::Right(container) = &mut container { - *container = structurally_normalize_ty(&infcx, *container, trait_env.param_env); + *container = structurally_normalize_ty(&infcx, *container, env.param_env); } let handle_variants = |variant: VariantId, subst: GenericArgs<'db>, container: &mut _| { @@ -1025,7 +1039,10 @@ impl<'db> SourceAnalyzer<'db> { }; if field_name.syntax().text_range() == name_ref.syntax().text_range() { - return Some((field_def, GenericSubstitution::new(generic_def, subst, trait_env))); + return Some(( + field_def, + GenericSubstitution::new(generic_def, subst, self.type_owner), + )); } } never!("the `NameRef` is a child of the `OffsetOfExpr`, we should've visited it"); @@ -1053,7 +1070,7 @@ impl<'db> SourceAnalyzer<'db> { let subst = GenericSubstitution::new( f_in_trait.into(), subs, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(f_in_trait.into()), Some(subst)) } @@ -1066,14 +1083,14 @@ impl<'db> SourceAnalyzer<'db> { let subst = GenericSubstitution::new_from_fn( fn_, subst, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(fn_), subst) } else { let subst = GenericSubstitution::new( f_in_trait.into(), subs, - self.trait_environment(db), + self.type_owner, ); (AssocItem::Function(f_in_trait.into()), Some(subst)) } @@ -1083,11 +1100,8 @@ impl<'db> SourceAnalyzer<'db> { CandidateId::ConstId(const_id) => { let (konst, subst) = self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); - let subst = GenericSubstitution::new( - konst.into(), - subst, - self.trait_environment(db), - ); + let subst = + GenericSubstitution::new(konst.into(), subst, self.type_owner); (AssocItem::Const(konst.into()), Some(subst)) } }; @@ -1111,20 +1125,13 @@ impl<'db> SourceAnalyzer<'db> { CandidateId::ConstId(const_id) => { let (konst, subst) = self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); - let subst = GenericSubstitution::new( - konst.into(), - subst, - self.trait_environment(db), - ); + let subst = + GenericSubstitution::new(konst.into(), subst, self.type_owner); (AssocItemId::from(konst), subst) } CandidateId::FunctionId(function_id) => ( function_id.into(), - GenericSubstitution::new( - function_id.into(), - subs, - self.trait_environment(db), - ), + GenericSubstitution::new(function_id.into(), subs, self.type_owner), ), }; return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst))); @@ -1328,12 +1335,11 @@ impl<'db> SourceAnalyzer<'db> { } else { return None; }; - let env = self.trait_environment(db); let (subst, expected_resolution) = match ty.kind() { TyKind::Adt(adt_def, subst) => { let adt_id = adt_def.def_id(); ( - GenericSubstitution::new(adt_id.into(), subst, env), + GenericSubstitution::new(adt_id.into(), subst, self.type_owner), PathResolution::Def(ModuleDef::Adt(adt_id.into())), ) } @@ -1344,7 +1350,7 @@ impl<'db> SourceAnalyzer<'db> { }) => { let assoc_id = def_id.0; ( - GenericSubstitution::new(assoc_id.into(), args, env), + GenericSubstitution::new(assoc_id.into(), args, self.type_owner), PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), ) } @@ -1355,7 +1361,7 @@ impl<'db> SourceAnalyzer<'db> { CallableDefId::EnumVariantId(_) => return None, }; ( - GenericSubstitution::new(generic_def_id, subst, env), + GenericSubstitution::new(generic_def_id, subst, self.type_owner), PathResolution::Def(ModuleDefId::from(fn_id.0).into()), ) } @@ -1474,7 +1480,7 @@ impl<'db> SourceAnalyzer<'db> { .map(|local_id| { let field = FieldId { parent: variant, local_id }; let ty = field_types[local_id].get().instantiate(interner, substs).skip_norm_wip(); - (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) + (field.into(), self.ty(ty)) }) .collect() } diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs index e3d0121e49123..a824c8bdca5d9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/expr.rs @@ -310,21 +310,18 @@ impl<'db> Expr<'db> { Expr::Local(it) => it.ty(db), Expr::ConstParam(it) => it.ty(db), Expr::FamousType { ty, .. } => ty.clone(), - Expr::Function { func, generics, .. } => { - func.ret_type_with_args(db, generics.iter().cloned()) - } - Expr::Method { func, generics, target, .. } => func.ret_type_with_args( - db, - target.ty(db).type_arguments().chain(generics.iter().cloned()), - ), + Expr::Function { func, generics, .. } => func.ret_type(db).instantiate(generics), + Expr::Method { func, generics, target, .. } => func + .ret_type(db) + .instantiate(target.ty(db).type_arguments().chain(generics.iter().cloned())), Expr::Variant { variant, generics, .. } => { - Adt::from(variant.parent_enum(db)).ty_with_args(db, generics.iter().cloned()) + Adt::from(variant.parent_enum(db)).ty(db).instantiate(generics) } Expr::Struct { strukt, generics, .. } => { - Adt::from(*strukt).ty_with_args(db, generics.iter().cloned()) + Adt::from(*strukt).ty(db).instantiate(generics) } Expr::Tuple { ty, .. } => ty.clone(), - Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()), + Expr::Field { expr, field } => field.ty(db).instantiate(expr.ty(db).type_arguments()), Expr::Reference(it) => it.ty(db), Expr::Many(ty) => ty.clone(), } diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 67b6fd64c1e6d..2b7f7da3bf0d3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -10,18 +10,13 @@ use std::iter; -use hir_ty::{ - db::HirDatabase, - mir::BorrowKind, - next_solver::{DbInterner, Ty}, -}; +use hir_ty::{db::HirDatabase, mir::BorrowKind}; use itertools::Itertools; use rustc_hash::FxHashSet; -use rustc_type_ir::inherent::Ty as _; use crate::{ - Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef, - Type, TypeParam, term_search::Expr, + Adt, AssocItem, BuiltinType, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, + ModuleDef, ScopeDef, Type, TypeParam, term_search::Expr, }; use super::{LookupTable, NewTypesKey, TermSearchCtx}; @@ -87,7 +82,7 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>( return None; } - ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr) + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr) }) } @@ -137,7 +132,7 @@ pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>( lookup.insert(ty.clone(), std::iter::once(expr.clone())); - ty.could_unify_with_deeply(db, &ctx.goal).then_some(expr) + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr) }) } @@ -202,7 +197,9 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = fields .into_iter() - .map(|field| lookup.find(db, &field.ty_with_args(db, generics.iter().cloned()))) + .map(|field| { + lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned())) + }) .collect::>()?; // Note that we need special case for 0 param constructors because of multi cartesian @@ -252,7 +249,7 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( .fields(db) .into_iter() .map(|field| { - lookup.find(db, &field.ty_with_args(db, generics.iter().cloned())) + lookup.find(db, &field.ty(db).instantiate(generics.iter().cloned())) }) .collect::>()?; @@ -285,7 +282,9 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>( } Adt::Union(_) => None, }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -364,7 +363,7 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( }) .collect::>()?; - let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); + let ret_ty = it.ret_type(db).instantiate(generics.iter().cloned()); // Filter out private and unsafe functions if !it.is_visible_from(db, module) || it.is_unsafe_to_call( @@ -381,10 +380,10 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = it - .params_without_self_with_args(db, generics.iter().cloned()) + .params_without_self(db) .into_iter() .map(|field| { - let ty = field.ty(); + let ty = &field.ty().instantiate(&generics); match ty.is_mutable_reference() { true => None, false => lookup.find_autoref(db, ty), @@ -418,7 +417,9 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>( _ => None, }) .flatten() - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -493,7 +494,7 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( return None; } - let ret_ty = it.ret_type_with_args(db, ty.type_arguments()); + let ret_ty = it.ret_type(db).instantiate(ty.type_arguments()); // Filter out functions that return references if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { @@ -501,12 +502,12 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( } // Ignore functions that do not change the type - if ty.could_unify_with_deeply(db, &ret_ty) { + if ty.instantiate_with_errors().could_unify_with_deeply(db, &ret_ty) { return None; } let self_ty = - it.self_param(db).expect("No self param").ty_with_args(db, ty.type_arguments()); + it.self_param(db).expect("No self param").ty(db).instantiate(ty.type_arguments()); // Ignore functions that have different self type if !self_ty.autoderef(db).any(|s_ty| ty == s_ty) { @@ -517,9 +518,9 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = it - .params_without_self_with_args(db, ty.type_arguments()) + .params_without_self(db) .into_iter() - .map(|field| lookup.find_autoref(db, field.ty())) + .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments()))) .collect::>()?; let generics: Vec<_> = ty.type_arguments().collect(); @@ -540,7 +541,9 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>( Some((ret_ty, fn_exprs)) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -581,7 +584,9 @@ pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>( Some((filed_ty, exprs)) }) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -604,20 +609,18 @@ pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>( lookup: &'lt mut LookupTable<'db>, ) -> impl Iterator> + use<'a, 'db, 'lt, DB> { let db = ctx.sema.db; - let module = ctx.scope.module(); - let interner = DbInterner::new_no_crate(db); - let bool_ty = Ty::new_bool(interner); - let unit_ty = Ty::new_unit(interner); + let bool_ty = BuiltinType::bool().ty(db); + let unit_ty = Type::new_unit(); [ - Expr::FamousType { ty: Type::new(db, module.id, bool_ty), value: "true" }, - Expr::FamousType { ty: Type::new(db, module.id, bool_ty), value: "false" }, - Expr::FamousType { ty: Type::new(db, module.id, unit_ty), value: "()" }, + Expr::FamousType { ty: bool_ty.clone(), value: "true" }, + Expr::FamousType { ty: bool_ty, value: "false" }, + Expr::FamousType { ty: unit_ty, value: "()" }, ] .into_iter() .inspect(|exprs| { lookup.insert(exprs.ty(db), std::iter::once(exprs.clone())); }) - .filter(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal)) + .filter(|expr| expr.ty(db).instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal)) } /// # Impl static method (without self type) tactic @@ -691,7 +694,7 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( return None; } - let ret_ty = it.ret_type_with_args(db, ty.type_arguments()); + let ret_ty = it.ret_type(db).instantiate(ty.type_arguments()); // Filter out functions that return references if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { @@ -700,9 +703,9 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( // Early exit if some param cannot be filled from lookup let param_exprs: Vec>> = it - .params_without_self_with_args(db, ty.type_arguments()) + .params_without_self(db) .into_iter() - .map(|field| lookup.find_autoref(db, field.ty())) + .map(|field| lookup.find_autoref(db, &field.ty().instantiate(ty.type_arguments()))) .collect::>()?; // Note that we need special case for 0 param constructors because of multi cartesian @@ -722,7 +725,9 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>( Some((ret_ty, fn_exprs)) }) - .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs)) + .filter_map(|(ty, exprs)| { + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(exprs) + }) .flatten() } @@ -745,7 +750,6 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( should_continue: &'a dyn std::ops::Fn() -> bool, ) -> impl Iterator> + use<'a, 'db, 'lt, DB> { let db = ctx.sema.db; - let module = ctx.scope.module(); lookup .types_wishlist() @@ -774,7 +778,7 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( .filter(|_| should_continue()) .map(|params| { let tys: Vec> = params.iter().map(|it| it.ty(db)).collect(); - let tuple_ty = Type::new_tuple(db, module.krate(db).into(), &tys); + let tuple_ty = Type::new_tuple(db, &tys); let expr = Expr::Tuple { ty: tuple_ty.clone(), params }; lookup.insert(tuple_ty, iter::once(expr.clone())); @@ -785,5 +789,10 @@ pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>( Some(exprs) }) .flatten() - .filter_map(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal).then_some(expr)) + .filter_map(|expr| { + expr.ty(db) + .instantiate_with_errors() + .could_unify_with_deeply(db, &ctx.goal) + .then_some(expr) + }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 667a1d7813c5c..632fe0d72cfa5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -80,16 +80,25 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, let module = scope.module(); let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(scope.krate())); let self_ty = if ctx.config.prefer_self_ty { - scope.expression_store_owner().and_then(|def| { - match def { - hir::ExpressionStoreOwner::Body(def_with_body) => { - def_with_body.as_assoc_item(ctx.db()) + scope + .expression_store_owner() + .and_then(|def| { + match def { + hir::ExpressionStoreOwner::Body(def_with_body) => { + def_with_body.as_assoc_item(ctx.db()) + } + hir::ExpressionStoreOwner::Signature(def) => def.as_assoc_item(ctx.db()), + hir::ExpressionStoreOwner::VariantFields(_) => None, + }? + .implementing_ty(ctx.db()) + }) + .map(|self_ty| { + if let Some(owner) = scope.generic_def() { + self_ty.try_rebase_into_owner(ctx.db(), owner).unwrap() + } else { + self_ty } - hir::ExpressionStoreOwner::Signature(def) => def.as_assoc_item(ctx.db()), - hir::ExpressionStoreOwner::VariantFields(_) => None, - }? - .implementing_ty(ctx.db()) - }) + }) } else { None }; @@ -613,7 +622,7 @@ fn build_pat( hir::StructKind::Tuple => { let mut name_generator = suggest_name::NameGenerator::default(); let pats = fields.into_iter().map(|f| { - let name = name_generator.for_type(&f.ty(db).to_type(db), db, edition); + let name = name_generator.for_type(&f.ty(db), db, edition); match name { Some(name) => make.ident_pat(false, false, make.name(&name)).into(), None => make.wildcard_pat().into(), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs index d6e459d04409c..dd082476d2d62 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs @@ -293,7 +293,7 @@ pub(crate) fn relevance_score( if let Some(ty) = ty { if ty == *expected { score = 100000; - } else if ty.could_unify_with(ctx.db(), expected) { + } else if ty.could_unify_with(ctx.db(), &expected.instantiate_with_errors()) { score = 10000; } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index 4aa11b4e03c85..8ae322c020038 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -137,13 +137,7 @@ fn expand_tuple_struct_rest_pattern( pat.fields() .take(prefix_count) .chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| { - gen_unnamed_pat( - ctx, - make, - &mut name_gen, - &f.ty(ctx.db()).to_type(ctx.sema.db), - f.index(), - ) + gen_unnamed_pat(ctx, make, &mut name_gen, &f.ty(ctx.db()), f.index()) })) .chain(pat.fields().skip(prefix_count + 1)), ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs index 52df6182ac56f..1617016172d02 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs @@ -1,4 +1,3 @@ -use hir::next_solver::{DbInterner, TypingMode}; use ide_db::{RootDatabase, famous_defs::FamousDefs}; use syntax::ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory}; use syntax::syntax_editor::Position; @@ -159,16 +158,12 @@ fn existing_from_impl( let variant = sema.to_def(variant)?; let krate = variant.module(db).krate(db); let from_trait = FamousDefs(sema, krate).core_convert_From()?; - let interner = DbInterner::new_with(db, krate.base()); - use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let variant = variant.instantiate_infer(&infcx); let enum_ = variant.parent_enum(sema.db); let field_ty = variant.fields(sema.db).first()?.ty(sema.db); let enum_ty = enum_.ty(sema.db); tracing::debug!(?enum_, ?field_ty, ?enum_ty); - enum_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) + enum_ty.has_any_impl(db, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index 4348dfa212c70..d5629e2e7e073 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs @@ -1,4 +1,3 @@ -use hir::next_solver::{DbInterner, TypingMode}; use hir::{HasCrate, ModuleDef, Semantics}; use ide_db::use_trivial_constructor::use_trivial_constructor_with_factory; use ide_db::{ @@ -233,15 +232,11 @@ fn from_impl_exists( let strukt = sema.to_def(strukt)?; let krate = strukt.krate(db); let from_trait = FamousDefs(sema, krate).core_convert_From()?; - let interner = DbInterner::new_with(db, krate.base()); - use hir::next_solver::infer::DbInternerInferExt; - let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - let strukt = strukt.instantiate_infer(&infcx); let field_ty = strukt.fields(db).get(main_field_i)?.ty(db); let struct_ty = strukt.ty(db); tracing::debug!(?strukt, ?field_ty, ?struct_ty); - struct_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(()) + struct_ty.has_any_impl(db, from_trait, &[field_ty]).then_some(()) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 22b8861e5f543..17ee8597c1020 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs @@ -67,11 +67,14 @@ pub(crate) fn replace_with_lazy_method( let editor = builder.make_editor(call.syntax()); let add_param = match &*method_name_lazy { "and_then" => true, - "or_else" | "unwrap_or_else" => { - FamousDefs(&ctx.sema, scope.krate()).core_result_Result().is_some_and( - |result| result.ty(ctx.db()).could_unify_with(ctx.db(), &receiver_ty), - ) - } + "or_else" | "unwrap_or_else" => FamousDefs(&ctx.sema, scope.krate()) + .core_result_Result() + .is_some_and(|result| { + result + .ty(ctx.db()) + .instantiate_with_errors() + .could_unify_with(ctx.db(), &receiver_ty) + }), _ => false, }; let closured = into_closure(&last_arg, add_param, editor.make()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs index f9c103aab8f1f..032cc28858c29 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type.rs @@ -92,7 +92,7 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - }; let semantic_ty = ty_constructor .map(|ty_constructor| { - hir::Adt::from(ty_constructor).ty_with_args(ctx.db(), [ty.clone()]) + hir::Adt::from(ty_constructor).ty(ctx.db()).instantiate([ty.clone()]) }) .unwrap_or_else(|| ty.clone()); (ast_ty, semantic_ty) @@ -256,7 +256,7 @@ fn wrapper_alias<'db>( ); let new_ty = - hir::Adt::from(enum_ty).ty_with_args(ctx.db(), [semantic_ret_type.clone()]); + hir::Adt::from(enum_ty).ty(ctx.db()).instantiate([semantic_ret_type.clone()]); Some((make.ty_path(path), new_ty)) }) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index f3190bbbc82e6..20048ea97b2c8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -199,10 +199,10 @@ impl Completions { item.add_to(self, ctx.db); } - pub(crate) fn add_expr( + pub(crate) fn add_expr<'db>( &mut self, - ctx: &CompletionContext<'_, '_>, - expr: &hir::term_search::Expr<'_>, + ctx: &CompletionContext<'_, 'db>, + expr: &hir::term_search::Expr<'db>, ) { if let Some(item) = render_expr(ctx, expr) { item.add_to(self, ctx.db) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index a2a4cbac2161b..506662ca72376 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -182,6 +182,9 @@ pub(crate) fn complete_expr_path( } _ => return, }; + // Note: this is not *required* here, we do it to also find methods that require + // the type to be instantiated with specific types. + let ty = ty.instantiate_with_errors(); if let Some(hir::Adt::Enum(e)) = ty.as_adt() { cov_mark::hit!(completes_variant_through_alias); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 1238a91dad871..48a51690271b1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -28,11 +28,7 @@ pub(crate) fn complete_record_pattern_fields( record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some(); match were_fields_specified { - false => un - .fields(ctx.db) - .into_iter() - .map(|f| (f, f.ty(ctx.db).to_type(ctx.db))) - .collect(), + false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), true => return, } } @@ -60,11 +56,7 @@ pub(crate) fn complete_record_expr_fields( record_expr.record_expr_field_list().and_then(|fl| fl.fields().next()).is_some(); match were_fields_specified { - false => un - .fields(ctx.db) - .into_iter() - .map(|f| (f, f.ty(ctx.db).to_type(ctx.db))) - .collect(), + false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), true => return, } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index e4d599742dbe8..df62829269367 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -729,6 +729,13 @@ impl<'db> CompletionContext<'_, 'db> { vec![] } } + + pub(crate) fn rebase_ty(&self, ty: &hir::Type<'db>) -> hir::Type<'db> { + self.scope + .generic_def() + .and_then(|def| ty.try_rebase_into_owner(self.db, def)) + .unwrap_or_else(|| ty.instantiate_with_errors()) + } } // CompletionContext construction 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 4fb8248e780b3..7aec6f7e8f9e6 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 @@ -648,6 +648,16 @@ fn expected_type_and_name<'db>( _ => ty, }; + let mut generic_def = None; + let mut rebase_ty = { + let node = node.clone(); + move |ty: hir::Type<'db>| { + let def = *generic_def + .get_or_insert_with(|| sema.scope(&node).and_then(|scope| scope.generic_def())); + def.and_then(|def| ty.try_rebase_into_owner(sema.db, def)) + .unwrap_or_else(|| ty.instantiate_with_errors()) + } + }; let (ty, name) = loop { break match_ast! { match node { @@ -793,20 +803,20 @@ fn expected_type_and_name<'db>( ast::TupleStructPat(it) => { let fields = sema.type_of_pat(&it.clone().into()).map(|ty| ty.original.fields(sema.db)); let nr = it.fields().take_while(|it| it.syntax().text_range().end() <= token.text_range().start()).count(); - let ty = fields.and_then(|fields| Some(fields.get(nr)?.1.clone())); + let ty = fields.and_then(|fields| Some(rebase_ty(fields.get(nr)?.1.clone()))); (ty, None) }, ast::Fn(it) => { cov_mark::hit!(expected_type_fn_ret_with_leading_char); cov_mark::hit!(expected_type_fn_ret_without_leading_char); let def = sema.to_def(&it); - (def.map(|def| def.ret_type(sema.db)), None) + (def.map(|def| rebase_ty(def.ret_type(sema.db))), None) }, ast::ReturnExpr(it) => { let fn_ = sema.ancestors_with_macros(it.syntax().clone()) .find_map(Either::::cast); let ty = fn_.and_then(|f| match f { - Either::Left(f) => Some(sema.to_def(&f)?.ret_type(sema.db)), + Either::Left(f) => Some(rebase_ty(sema.to_def(&f)?.ret_type(sema.db))), Either::Right(f) => { let ty = sema.type_of_expr(&f.into())?.original.as_callable(sema.db)?; Some(ty.return_type()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index bc71c1da2b8bf..f559aae789e24 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -71,7 +71,7 @@ impl<'a, 'db> RenderContext<'a, 'db> { self.completion.config.snippet_cap } - fn db(&self) -> &'a RootDatabase { + fn db(&self) -> &'db RootDatabase { self.completion.db } @@ -294,9 +294,9 @@ pub(crate) fn render_resolution_with_import_pat( Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution)) } -pub(crate) fn render_expr( - ctx: &CompletionContext<'_, '_>, - expr: &hir::term_search::Expr<'_>, +pub(crate) fn render_expr<'db>( + ctx: &CompletionContext<'_, 'db>, + expr: &hir::term_search::Expr<'db>, ) -> Option { let mut i = 1; let mut snippet_formatter = |ty: &hir::Type<'_>| { @@ -341,7 +341,7 @@ pub(crate) fn render_expr( "Autogenerated expression by term search", ))); item.set_relevance(crate::CompletionRelevance { - type_match: compute_type_match(ctx, &expr.ty(ctx.db)), + type_match: compute_type_match(ctx, &ctx.rebase_ty(&expr.ty(ctx.db))), ..Default::default() }); for trait_ in expr.traits_used(ctx.db) { @@ -405,8 +405,8 @@ fn render_resolution_pat( } } -fn render_resolution_path( - ctx: RenderContext<'_, '_>, +fn render_resolution_path<'db>( + ctx: RenderContext<'_, 'db>, path_ctx: &PathCompletionCtx<'_>, local_name: hir::Name, import_to_add: Option, @@ -480,11 +480,12 @@ fn render_resolution_path( } adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into()); - let mut set_item_relevance = |ty: Type<'_>| { + let mut set_item_relevance = |ty: Type<'db>| { if !ty.is_unknown() { item.detail(ty.display(db, krate).to_string()); } + let ty = completion.rebase_ty(&ty); item.set_relevance(CompletionRelevance { type_match: compute_type_match(completion, &ty), exact_name_match: compute_exact_name_match(completion, &name), @@ -688,8 +689,8 @@ fn compute_type_match( // &mut ty -> &ty if completion_ty.is_mutable_reference() - && let Some(expected_type) = expected_type.remove_ref() - && let Some(completion_ty) = completion_ty.remove_ref() + && let Some((expected_type, _)) = expected_type.as_reference() + && let Some((completion_ty, _)) = completion_ty.as_reference() { return match_types(ctx, &expected_type, &completion_ty); } @@ -718,12 +719,12 @@ fn compute_ref_match( completion_ty: &hir::Type<'_>, ) -> Option { let expected_type = ctx.expected_type.as_ref()?; - let expected_without_ref = expected_type.remove_ref(); - let completion_without_ref = completion_ty.remove_ref(); + let expected_without_ref = expected_type.as_reference(); + let completion_without_ref = completion_ty.as_reference(); if expected_type.could_unify_with(ctx.db, completion_ty) { return None; } - if let Some(expected_without_ref) = &expected_without_ref + if let Some((expected_without_ref, _)) = &expected_without_ref && (completion_without_ref.is_none() || completion_ty.could_unify_with(ctx.db, expected_without_ref)) && completion_ty @@ -739,7 +740,7 @@ fn compute_ref_match( return Some(CompletionItemRefMode::Reference(mutability)); } - if let Some(completion_without_ref) = completion_without_ref + if let Some((completion_without_ref, _)) = completion_without_ref && completion_without_ref == *expected_type && completion_without_ref.is_copy(ctx.db) { @@ -952,9 +953,9 @@ fn main() { } "#, expect![[r#" - st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type_could_unify] - ex dep::test_mod_b::Struct { } [type_could_unify] - st Struct Struct [type_could_unify+requires_import] + st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type] + ex dep::test_mod_b::Struct { } [type] + st Struct Struct [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Struct) [] @@ -992,7 +993,7 @@ fn main() { } "#, expect![[r#" - un Union Union [type_could_unify+requires_import] + un Union Union [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Union) [] @@ -1028,9 +1029,9 @@ fn main() { } "#, expect![[r#" - ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type_could_unify] - ex dep::test_mod_b::Enum::variant [type_could_unify] - en Enum Enum [type_could_unify+requires_import] + ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type] + ex dep::test_mod_b::Enum::variant [type] + en Enum Enum [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] @@ -1066,10 +1067,10 @@ fn main() { } "#, expect![[r#" - ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify] - ev Variant Variant [type_could_unify+requires_import] + ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type] + ev Variant Variant [type+requires_import] + ex dep::test_mod_b::Enum::Variant [type] ev Variant Variant [requires_import] - ex dep::test_mod_b::Enum::Variant [type_could_unify] md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] @@ -1132,7 +1133,7 @@ fn main() { } "#, expect![[r#" - ct CONST i32 [type_could_unify+requires_import] + ct CONST i32 [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(i32) [] @@ -1164,7 +1165,7 @@ fn main() { } "#, expect![[r#" - sc STATIC i32 [type_could_unify+requires_import] + sc STATIC i32 [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(i32) [] @@ -3145,7 +3146,7 @@ fn main() { st &mut S(…) [type] lc ssss S [local] lc &mut ssss [type+local] - st S S<{unknown}> [] + st S S [] fn foo(…) fn(&mut S) [] fn main() fn() [] "#]], @@ -3168,7 +3169,7 @@ fn main() { expect![[r#" ex ssss.0 [type_could_unify] lc ssss S<{unknown}> [local] - st S S<{unknown}> [] + st S S [] md core:: [] fn foo(…) fn(&u32) [] fn main() fn() [] @@ -3916,9 +3917,9 @@ fn foo() { lc foo Foo [type+local] ex Foo::B [type] ex foo [type] - en Foo Foo<{unknown}> [type_could_unify] + en Foo Foo [type_could_unify] + fn baz() fn() -> Foo [type_could_unify] fn bar() fn() -> Foo [] - fn baz() fn() -> Foo [] fn foo() fn() [] "#]], ); @@ -3948,6 +3949,7 @@ fn main() { &[CompletionItemKind::Snippet, CompletionItemKind::SymbolKind(SymbolKind::Method)], expect![[r#" sn not !expr [snippet] + me not() fn(self) -> ::Output [type_could_unify+requires_import] sn box Box::new(expr) [] sn call function(expr) [] sn const const {} [] @@ -3961,7 +3963,6 @@ fn main() { sn return return expr [] sn unsafe unsafe {} [] sn while while expr {} [] - me not() fn(self) -> ::Output [requires_import] "#]], ); } @@ -4012,7 +4013,7 @@ enum Foo { en Foo Foo [] st Other Other [] sp Self Foo [] - st Vec<…> Vec<{unknown}> [] + st Vec<…> Vec [] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index ea228fdd60e50..4f70a90affbdf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -76,7 +76,7 @@ fn render( completion.edition, ); - let ret_type = func.ret_type(db); + let ret_type = ctx.completion.rebase_ty(&func.ret_type(db)); let assoc_item = func.as_assoc_item(db); let trait_info = @@ -107,6 +107,7 @@ fn render( let function = assoc_item .and_then(|assoc_item| assoc_item.implementing_ty(db)) + .map(|self_type| ctx.completion.rebase_ty(&self_type)) .map(|self_type| compute_return_type_match(db, &ctx, self_type, &ret_type)) .map(|return_type| CompletionRelevanceFn { has_params: has_self_param || func.num_params(db) > 0, @@ -118,7 +119,7 @@ fn render( type_match: if has_call_parens || complete_call_parens.is_some() { compute_type_match(completion, &ret_type) } else { - compute_type_match(completion, &func.ty(db)) + compute_type_match(completion, &ctx.completion.rebase_ty(&func.ty(db))) }, exact_name_match: compute_exact_name_match(completion, &call), function, @@ -287,7 +288,7 @@ pub(super) fn add_call_parens<'b>( } fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { - if let Some(derefed_ty) = ty.remove_ref() { + if let Some(derefed_ty) = ty.as_reference_inner() { for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { return if local.ty(ctx.db) == derefed_ty { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index 9e0cec62e6418..943ff58219693 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -127,7 +127,7 @@ fn render( item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx)); - let ty = thing.ty(db); + let ty = ctx.completion.rebase_ty(&thing.ty(db)); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, &ty), // function is a misnomer here, this is more about constructor information diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 392ecbc302ae5..7e70aa13d4229 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -103,17 +103,18 @@ pub(crate) fn render_variant_pat( )) } -fn build_completion( - ctx: RenderContext<'_, '_>, +fn build_completion<'db>( + ctx: RenderContext<'_, 'db>, label: SmolStr, lookup: SmolStr, pat: String, def: impl HasDocs, - adt_ty: hir::Type<'_>, + adt_ty: hir::Type<'db>, // Missing in context of match statement completions is_variant_missing: bool, ) -> CompletionItem { let mut relevance = ctx.completion_relevance(); + let adt_ty = ctx.completion.rebase_ty(&adt_ty); if is_variant_missing { relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs index f86af6cdcb7d8..8d1b88596982b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs @@ -32,7 +32,7 @@ pub(crate) fn render_record_lit( if let Some(local) = ctx.locals.get(&field_name) && local .ty(ctx.db) - .could_unify_with_deeply(ctx.db, &field.ty(ctx.db).to_type(ctx.db)) + .could_unify_with_deeply(ctx.db, &ctx.rebase_ty(&field.ty(ctx.db))) { f(&format_args!("{}{tab}", field_name.display(ctx.db, ctx.edition))) } else { 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 4a4b09c6585a1..595c864ae3c27 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 @@ -3120,8 +3120,8 @@ fn bar() { } "#, expect![[r#" - en Option Option<{unknown}> - en Result Result<{unknown}, {unknown}> + en Option Option + en Result Result fn bar() fn() lc i i32 ma const_format_args!(…) macro_rules! const_format_args diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs index 9826a8ed7b33a..3f3a6f4cf574d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs @@ -14,7 +14,7 @@ struct Foo<'lt, T, const C: usize> where $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -91,7 +91,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {} en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<'_, {unknown}, _> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 24080334ae9b9..ad058901c0473 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -15,8 +15,8 @@ struct Foo<'lt, T, const C: usize> { en Enum Enum ma makro!(…) macro_rules! makro md module:: - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'lt, T, C> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -44,8 +44,8 @@ struct Foo<'lt, T, const C: usize>(f$0); en Enum Enum ma makro!(…) macro_rules! makro md module:: - sp Self Foo<'_, {unknown}, _> - st Foo<…> Foo<'_, {unknown}, _> + sp Self Foo<'lt, T, C> + st Foo<…> Foo<'lt, T, C> st Record Record st Tuple Tuple st Unit Unit @@ -409,7 +409,7 @@ const FOO: $0 = Foo(2); en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo st Record Record st Tuple Tuple st Unit Unit @@ -438,7 +438,7 @@ static FOO: $0 = Foo(2); en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo st Record Record st Tuple Tuple st Unit Unit @@ -629,7 +629,7 @@ fn foo<'lt, T, const C: usize>() { en Enum Enum ma makro!(…) macro_rules! makro md module:: - st Foo<…> Foo<{unknown}> + st Foo<…> Foo st Record Record st Tuple Tuple st Unit Unit diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 76fea5c2623ca..15920595a8271 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -412,7 +412,7 @@ fn from_type( edition: Edition, ) -> Option { let ty = sema.type_of_expr(expr)?.adjusted(); - let ty = ty.remove_ref().unwrap_or(ty); + let ty = ty.strip_reference(); name_of_type(&ty, sema.db, edition) } @@ -445,7 +445,7 @@ fn name_of_type<'db>( return None; } name - } else if let Some(inner_ty) = ty.remove_ref() { + } else if let Some((inner_ty, _)) = ty.as_reference() { return name_of_type(&inner_ty, db, edition); } else if let Some(inner_ty) = ty.as_slice() { return Some(sequence_name(Some(&inner_ty), db, edition)); 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 08791fecbedbc..678e45e145dc6 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 @@ -127,7 +127,7 @@ fn add_missing_ok_or_some( let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" }; let wrapped_actual_ty = - expected_adt.ty_with_args(ctx.sema.db, std::iter::once(d.actual.clone())); + expected_adt.ty(ctx.sema.db).instantiate(std::iter::once(d.actual.clone())); if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) { return None; @@ -225,7 +225,7 @@ fn remove_unnecessary_wrapper( return None; } - let inner_type = variant.fields(db).first()?.ty_with_args(db, d.actual.type_arguments()); + let inner_type = variant.fields(db).first()?.ty(db).instantiate(d.actual.type_arguments()); if !d.expected.could_unify_with(db, &inner_type) { return None; } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index ae208fe1b5615..ffd144a827e34 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -88,7 +88,7 @@ pub(crate) fn goto_type_definition( ast::Pat(it) => sema.type_of_pat(&it)?.original, ast::SelfParam(it) => sema.type_of_self(&it)?, ast::Type(it) => sema.resolve_type(&it)?, - ast::RecordField(it) => sema.to_def(&it)?.ty(db).to_type(db), + ast::RecordField(it) => sema.to_def(&it)?.ty(db), // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise ast::NameRef(it) => { if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index df1fcecc991fe..c3a8e0362fee8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -469,7 +469,7 @@ pub(crate) fn hover_for_definition( Definition::Local(it) => Some(it.ty(db)), Definition::GenericParam(hir::GenericParam::ConstParam(it)) => Some(it.ty(db)), Definition::GenericParam(hir::GenericParam::TypeParam(it)) => Some(it.ty(db)), - Definition::Field(field) => Some(field.ty(db).to_type(db)), + Definition::Field(field) => Some(field.ty(db)), Definition::TupleField(it) => Some(it.ty(db)), Definition::Function(it) => Some(it.ty(db)), Definition::Adt(it) => Some(it.ty(db)), @@ -630,7 +630,7 @@ fn goto_type_action_for_def( let ty = match def { Definition::Local(it) => Some(it.ty(db)), - Definition::Field(field) => Some(field.ty(db).to_type(db)), + Definition::Field(field) => Some(field.ty(db)), Definition::TupleField(field) => Some(field.ty(db)), Definition::Const(it) => Some(it.ty(db)), Definition::Static(it) => Some(it.ty(db)), 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 e08bbc5c21b65..da4f185d75641 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -664,14 +664,14 @@ pub(super) fn definition( } let drop_info = match def { Definition::Field(field) => { - DropInfo { drop_glue: field.ty(db).to_type(db).drop_glue(db), has_dtor: None } + DropInfo { drop_glue: field.ty(db).drop_glue(db), has_dtor: None } } Definition::Adt(Adt::Struct(strukt)) => { - let struct_drop_glue = strukt.ty_params(db).drop_glue(db); + let struct_drop_glue = strukt.ty(db).drop_glue(db); let mut fields_drop_glue = strukt .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None); let has_dtor = match (fields_drop_glue, struct_drop_glue) { @@ -688,10 +688,10 @@ pub(super) fn definition( // Unions cannot have fields with drop glue. Definition::Adt(Adt::Union(union)) => DropInfo { drop_glue: DropGlue::None, - has_dtor: Some(union.ty_params(db).drop_glue(db) != DropGlue::None), + has_dtor: Some(union.ty(db).drop_glue(db) != DropGlue::None), }, Definition::Adt(Adt::Enum(enum_)) => { - let enum_drop_glue = enum_.ty_params(db).drop_glue(db); + let enum_drop_glue = enum_.ty(db).drop_glue(db); let fields_drop_glue = enum_ .variants(db) .iter() @@ -699,7 +699,7 @@ pub(super) fn definition( variant .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None) }) @@ -714,13 +714,13 @@ pub(super) fn definition( let fields_drop_glue = variant .fields(db) .iter() - .map(|field| field.ty(db).to_type(db).drop_glue(db)) + .map(|field| field.ty(db).drop_glue(db)) .max() .unwrap_or(DropGlue::None); DropInfo { drop_glue: fields_drop_glue, has_dtor: None } } Definition::TypeAlias(type_alias) => { - DropInfo { drop_glue: type_alias.ty_params(db).drop_glue(db), has_dtor: None } + DropInfo { drop_glue: type_alias.ty(db).drop_glue(db), has_dtor: None } } Definition::Local(local) => { DropInfo { drop_glue: local.ty(db).drop_glue(db), has_dtor: None } diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index b664187932efa..e220cbdce8d32 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -522,11 +522,11 @@ fn rename_to_self( }; let first_param_ty = first_param.ty(); let impl_ty = impl_.self_ty(sema.db); - let (ty, self_param) = if impl_ty.remove_ref().is_some() { + let (ty, self_param) = if impl_ty.is_reference() { // if the impl is a ref to the type we can just match the `&T` with self directly (first_param_ty.clone(), "self") } else { - first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| { + first_param_ty.as_reference_inner().map_or((first_param_ty.clone(), "self"), |ty| { (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" }) }) }; diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index 7854a14187b3f..0022c1148a14c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -535,7 +535,7 @@ fn signature_help_for_tuple_struct_pat( pat.syntax(), token, pat.fields(), - fields.into_iter().map(|it| it.ty(db).to_type(db)), + fields.into_iter().map(|it| it.ty(db)), display_target, )) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index 1b9df9722b07e..03bde6f3e545b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -98,7 +98,7 @@ pub(crate) fn view_memory_layout( Definition::BuiltinType(it) => it.ty(db), Definition::SelfType(it) => it.self_ty(db), Definition::Local(it) => it.ty(db), - Definition::Field(it) => it.ty(db).to_type(db), + Definition::Field(it) => it.ty(db), Definition::Const(it) => it.ty(db), Definition::Static(it) => it.ty(db), _ => return None, 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 ec5503fe39f5b..1a036c3b99195 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 @@ -14,14 +14,16 @@ use hir::{ Adt, AssocItem, Crate, DefWithBody, FindPathConfig, GenericDef, HasCrate, HasSource, HirDisplay, ModuleDef, Name, Variant, crate_lang_items, db::{DefDatabase, ExpandDatabase, HirDatabase}, - next_solver::{DbInterner, GenericArgs}, }; use hir_def::{ DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, SyntheticSyntax, expr_store::{Body, BodySourceMap, ExpressionStore}, hir::{ExprId, PatId, generics::GenericParams}, }; -use hir_ty::InferenceResult; +use hir_ty::{ + InferenceResult, + next_solver::{DbInterner, GenericArgs}, +}; use ide::{ Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve, InlayHintsConfig, LineCol, RaFixtureConfig, RootDatabase, From d607c56a0179b29d6e53b413225a8aed5a382445 Mon Sep 17 00:00:00 2001 From: "workflows-rust-analyzer[bot]" <223433972+workflows-rust-analyzer[bot]@users.noreply.github.com> Date: Sun, 24 May 2026 00:10:34 +0000 Subject: [PATCH 071/114] internal: update generated lints --- .../crates/ide-db/src/generated/lints.rs | 69 ++++--------------- 1 file changed, 14 insertions(+), 55 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 9d8175d5e930f..310023ea2ad31 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -572,13 +572,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[ warn_since: None, deny_since: None, }, - Lint { - label: "inline_always_mismatching_target_features", - description: r##"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"##, - default_severity: Severity::Warning, - warn_since: None, - deny_since: None, - }, Lint { label: "inline_no_sanitize", description: r##"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"##, @@ -4654,22 +4647,6 @@ The tracking issue for this feature is: [#94039] [#94039]: https://github.com/rust-lang/rust/issues/94039 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "cfg_target_has_atomic_equal_alignment", - description: r##"# `cfg_target_has_atomic_equal_alignment` - -Allows `cfg(target_has_atomic_equal_alignment = "...")`. - -The tracking issue for this feature is: [#93822] - -[#93822]: https://github.com/rust-lang/rust/issues/93822 - ------------------------ "##, default_severity: Severity::Allow, @@ -12094,6 +12071,20 @@ The tracking issue for this feature is: [#118485] [#118485]: https://github.com/rust-lang/rust/issues/118485 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "os_str_split_at", + description: r##"# `os_str_split_at` + + + +This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. + ------------------------ "##, default_severity: Severity::Allow, @@ -14968,22 +14959,6 @@ The tracking issue for this feature is: [#95439] [#95439]: https://github.com/rust-lang/rust/issues/95439 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "target_feature_inline_always", - description: r##"# `target_feature_inline_always` - -Allows the use of target_feature when a function is marked inline(always). - -The tracking issue for this feature is: [#145574] - -[#145574]: https://github.com/rust-lang/rust/issues/145574 - ------------------------ "##, default_severity: Severity::Allow, @@ -15724,22 +15699,6 @@ This feature has no tracking issue, and is therefore likely internal to the comp -The tracking issue for this feature is: [#37572] - -[#37572]: https://github.com/rust-lang/rust/issues/37572 - ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "trusted_len_next_unchecked", - description: r##"# `trusted_len_next_unchecked` - - - The tracking issue for this feature is: [#37572] [#37572]: https://github.com/rust-lang/rust/issues/37572 From 0f40f8baf0df70c7ff17ab81b961c03a51ea68cb Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 21 May 2026 15:38:37 +0800 Subject: [PATCH 072/114] Revert is_exclude_flyimport condition changes --- .../crates/ide-completion/src/completions/flyimport.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 77f9dda7779ec..b350647b9a2bd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -389,7 +389,7 @@ fn filter_excluded_flyimport(ctx: &CompletionContext<'_, '_>, import: &LocatedIm } let method_imported = import.item_to_import != import.original_item; if method_imported - && (is_exclude_flyimport == Some(AutoImportExclusionType::Methods) + && (is_exclude_flyimport.is_some() || ctx.exclude_flyimport.contains_key(&import.original_item.into_module_def())) { // If this is a method, exclude it either if it was excluded itself (which may not be caught above, From 403e1ff5b1ea4092247aadc1ae70fc563bc25c42 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 24 May 2026 09:27:27 +0800 Subject: [PATCH 073/114] Add nested modules doc and fix typo --- .../rust-analyzer/crates/rust-analyzer/src/config.rs | 8 ++++---- .../docs/book/src/configuration_generated.md | 4 ++-- src/tools/rust-analyzer/editors/code/package.json | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 6d57c5535b9c8..6f532e4224885 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -662,8 +662,8 @@ config_data! { /// For traits the type "methods" can be used to only exclude the methods but not the trait /// itself. /// - /// For modules the type "subItems" can be used to only exclude the sub items but not the module - /// itself. + /// For modules the type "subItems" can be used to only exclude the all items in it but not the module + /// itself. This does not include items defined in nested modules. /// /// This setting also inherits `#rust-analyzer.completion.excludeTraits#`. completion_autoimport_exclude: Vec = vec![ @@ -4138,8 +4138,8 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "enum": ["always", "methods", "subItems"], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions.", - "Do not show this modules sub items in auto-import completions." + "Do not show this trait's methods in auto-import completions.", + "Do not show this module's all items in it in auto-import completions." ], }, } diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 87d21b7eaa0ea..7bbb9e0258169 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -446,8 +446,8 @@ verbose form `{ "path": "path::to::item", type: "always" }`. For traits the type "methods" can be used to only exclude the methods but not the trait itself. -For modules the type "subItems" can be used to only exclude the sub items but not the module -itself. +For modules the type "subItems" can be used to only exclude the all items in it but not the module +itself. This does not include items defined in nested modules. This setting also inherits `#rust-analyzer.completion.excludeTraits#`. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 8c1a91836c97c..8df606d4c4ca8 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1328,7 +1328,7 @@ "title": "Completion", "properties": { "rust-analyzer.completion.autoimport.exclude": { - "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nFor modules the type \"subItems\" can be used to only exclude the sub items but not the module\nitself.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", + "markdownDescription": "A list of full paths to items to exclude from auto-importing completions.\n\nTraits in this list won't have their methods suggested in completions unless the trait\nis in scope.\n\nYou can either specify a string path which defaults to type \"always\" or use the more\nverbose form `{ \"path\": \"path::to::item\", type: \"always\" }`.\n\nFor traits the type \"methods\" can be used to only exclude the methods but not the trait\nitself.\n\nFor modules the type \"subItems\" can be used to only exclude the all items in it but not the module\nitself. This does not include items defined in nested modules.\n\nThis setting also inherits `#rust-analyzer.completion.excludeTraits#`.", "default": [ { "path": "core::borrow::Borrow", @@ -1360,8 +1360,8 @@ ], "enumDescriptions": [ "Do not show this item or its methods (if it is a trait) in auto-import completions.", - "Do not show this traits methods in auto-import completions.", - "Do not show this modules sub items in auto-import completions." + "Do not show this trait's methods in auto-import completions.", + "Do not show this module's all items in it in auto-import completions." ] } } From 456a1ac2d85d5aaa4e93a69bb508fe91a31673dc Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 20 May 2026 17:12:32 +0800 Subject: [PATCH 074/114] fix: no suggest ref match when expected generic ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example --- ```rust fn foo(s: &T) {} fn main() { let ssss = &mut 2i32; foo($0); } ``` **Before this PR** ```text lc ssss &mut i32 [type_could_unify+local] lc &ssss [type+local] md core:: [] fn foo(…) fn(&T) [] fn &foo(…) [type] fn main() fn() [] fn &main() [type] ``` **After this PR** ```text lc ssss &mut i32 [type_could_unify+local] md core:: [] fn foo(…) fn(&T) [] fn &foo(…) [type] fn main() fn() [] fn &main() [type] ``` --- .../crates/ide-completion/src/render.rs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index f559aae789e24..fcc81c3611a78 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -718,12 +718,13 @@ fn compute_ref_match( ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option { + if compute_type_match(ctx, completion_ty).is_some() { + return None; + } let expected_type = ctx.expected_type.as_ref()?; let expected_without_ref = expected_type.as_reference(); let completion_without_ref = completion_ty.as_reference(); - if expected_type.could_unify_with(ctx.db, completion_ty) { - return None; - } + if let Some((expected_without_ref, _)) = &expected_without_ref && (completion_without_ref.is_none() || completion_ty.could_unify_with(ctx.db, expected_without_ref)) @@ -3175,6 +3176,24 @@ fn main() { fn main() fn() [] "#]], ); + check_relevance( + r#" +//- minicore: deref +fn foo(s: &T) {} +fn main() { + let ssss = &mut 2i32; + foo($0); +} + "#, + expect![[r#" + lc ssss &mut i32 [type_could_unify+local] + md core:: [] + fn foo(…) fn(&T) [] + fn &foo(…) [type] + fn main() fn() [] + fn &main() [type] + "#]], + ); } #[test] From c7877533797ce1f8112cc4add89fcddc034c3c52 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 24 May 2026 13:40:36 +0800 Subject: [PATCH 075/114] Rename test highlight not exclude module it self --- .../ide-completion/src/tests/expression.rs | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) 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 1bed2f0dcc879..64ba10642c348 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 @@ -2967,18 +2967,18 @@ fn flyimport_excluded_mod_items_from_flyimport() { check_with_config( CompletionConfig { exclude_flyimport: vec![( - "ra_test_fixture::pack::module2".to_owned(), + "ra_test_fixture::xpack::xmodule2".to_owned(), AutoImportExclusionType::SubItems, )], ..TEST_CONFIG }, r#" -mod pack { - mod module1 { +mod xpack { + mod xmodule1 { pub struct XOther; } - pub mod module2 { - pub use super::module1::*; + pub mod xmodule2 { + pub use super::xmodule1::*; pub struct XStruct; pub fn xfn() {} } @@ -2989,20 +2989,21 @@ fn foo() { } "#, expect![[r#" - ct CONST Unit - en Enum Enum - fn foo() fn() - fn function() fn() - ma makro!(…) macro_rules! makro + ct CONST Unit + en Enum Enum + fn foo() fn() + fn function() fn() + ma makro!(…) macro_rules! makro md module:: - md pack:: - sc STATIC Unit - st Record Record - st Tuple Tuple - st Unit Unit - un Union Union - ev TupleV(…) TupleV(u32) - bt u32 u32 + md xmodule2:: (use xpack::xmodule2) + md xpack:: + sc STATIC Unit + st Record Record + st Tuple Tuple + st Unit Unit + un Union Union + ev TupleV(…) TupleV(u32) + bt u32 u32 kw async kw const kw crate:: From 322217126d9a708ec11fe3cf6a19b426617ece3f Mon Sep 17 00:00:00 2001 From: albab-hasan Date: Sun, 24 May 2026 11:42:32 +0600 Subject: [PATCH 076/114] fix: add import for function-like macros in extract_module assist Fixes https://github.com/rust-lang/rust-analyzer/issues/20209 was missing a arm, causing it to always return for macros. This made false, so the import was never added to the extracted module. --- .../src/handlers/extract_module.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index 9e06a17337d8e..40eaed0080a0a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -733,6 +733,7 @@ fn check_def_in_mod_and_out_sel( Definition::Static(x) => check_item!(x), Definition::Trait(x) => check_item!(x), Definition::TypeAlias(x) => check_item!(x), + Definition::Macro(x) => check_item!(x), _ => {} } @@ -1813,4 +1814,33 @@ mod foo { "#, ) } + + #[test] + fn test_extract_module_macro_call_import() { + check_assist( + extract_module, + r" +macro_rules! my_macro { + () => {}; +} + +$0fn bar() { + my_macro!(); +}$0 + ", + r" +macro_rules! my_macro { + () => {}; +} + +mod modname { + use super::my_macro; + + pub(crate) fn bar() { + my_macro!(); + } +} + ", + ) + } } From 697ea43c8919bb4b2e068d0c64aeb2f706314013 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 22 May 2026 10:27:02 +0200 Subject: [PATCH 077/114] Cleanup bidirection proc-macro-srv protocol a bit --- .../crates/load-cargo/src/lib.rs | 2 + .../src/bidirectional_protocol.rs | 27 ++---- .../src/bidirectional_protocol/msg.rs | 89 ++++++++++++++++--- .../crates/proc-macro-api/src/process.rs | 5 +- .../crates/proc-macro-srv-cli/src/lib.rs | 22 ++--- .../proc-macro-srv-cli/src/main_loop.rs | 67 +++++++++++++- .../tests/bidirectional_postcard.rs | 35 +++++--- .../crates/proc-macro-srv/src/dylib.rs | 18 ++-- .../proc-macro-srv/src/dylib/proc_macros.rs | 15 ++-- .../crates/proc-macro-srv/src/lib.rs | 51 ++++++----- .../src/server_impl/rust_analyzer_span.rs | 33 +++---- .../src/server_impl/token_id.rs | 18 +--- .../crates/proc-macro-srv/src/tests/utils.rs | 40 ++++++++- 13 files changed, 290 insertions(+), 132 deletions(-) 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 d0c34580c497d..fd90bc404aca4 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -724,6 +724,8 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::SpanParentResult { parent_span: None }) } + // FIXME: implement this + SubRequest::SpanJoin { .. } => Ok(SubResponse::SpanJoinResult { span: None }), }; match self.0.expand( subtree.view(), diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs index ba59cb219b9a4..75c3bf8d35bb3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -12,8 +12,8 @@ use span::Span; use crate::{ ProcMacro, ProcMacroKind, ServerError, bidirectional_protocol::msg::{ - BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response, - SubRequest, SubResponse, + ApiVersionCheck, BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, + ListMacros, Request, Response, SubRequest, SubResponse, }, legacy_protocol::{ SpanMode, @@ -98,7 +98,7 @@ pub(crate) fn version_check( srv: &ProcMacroServerProcess, callback: SubCallback<'_>, ) -> Result { - let request = BidirectionalMessage::Request(Request::ApiVersionCheck {}); + let request = BidirectionalMessage::Request(Request::ApiVersionCheck(ApiVersionCheck {})); let response_payload = run_request(srv, request, callback)?; @@ -135,9 +135,9 @@ pub(crate) fn find_proc_macros( dylib_path: &AbsPath, callback: SubCallback<'_>, ) -> Result, String>, ServerError> { - let request = BidirectionalMessage::Request(Request::ListMacros { + let request = BidirectionalMessage::Request(Request::ListMacros(ListMacros { dylib_path: dylib_path.to_path_buf().into(), - }); + })); let response_payload = run_request(srv, request, callback)?; @@ -186,25 +186,12 @@ pub(crate) fn expand( match response_payload { BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it - .map(|tree| { - let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table); - if proc_macro.needs_fixup_change() { - proc_macro.change_fixup_to_match_old_server(&mut expanded); - } - expanded - }) - .map_err(|msg| msg.0)), - BidirectionalMessage::Response(Response::ExpandMacroExtended(it)) => Ok(it .map(|resp| { - let mut expanded = FlatTree::to_subtree_resolved( + FlatTree::to_subtree_resolved( resp.tree, version, &deserialize_span_data_index_map(&resp.span_data_table), - ); - if proc_macro.needs_fixup_change() { - proc_macro.change_fixup_to_match_old_server(&mut expanded); - } - expanded + ) }) .map_err(|msg| msg.0)), _ => Err(ServerError { message: "unexpected response".to_owned(), io: None }), diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index ab4bed81e6118..e516297f06196 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -1,6 +1,8 @@ //! Bidirectional protocol messages +#![expect(clippy::disallowed_types)] use std::{ + collections::{HashMap, HashSet}, io::{self, BufRead, Write}, ops::Range, }; @@ -16,13 +18,54 @@ use crate::{ #[derive(Debug, Serialize, Deserialize)] pub enum SubRequest { - FilePath { file_id: u32 }, - SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 }, - LocalFilePath { file_id: u32 }, - LineColumn { file_id: u32, ast_id: u32, offset: u32 }, - ByteRange { file_id: u32, ast_id: u32, start: u32, end: u32 }, - SpanSource { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 }, - SpanParent { file_id: u32, ast_id: u32, start: u32, end: u32, ctx: u32 }, + FilePath { + file_id: u32, + }, + SourceText { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + }, + LocalFilePath { + file_id: u32, + }, + LineColumn { + file_id: u32, + ast_id: u32, + offset: u32, + }, + ByteRange { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + }, + SpanSource { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + ctx: u32, + }, + SpanParent { + file_id: u32, + ast_id: u32, + start: u32, + end: u32, + ctx: u32, + }, + SpanJoin { + file_id: u32, + ast_id_first: u32, + start_first: u32, + end_first: u32, + ctx_first: u32, + ast_id_second: u32, + start_second: u32, + end_second: u32, + ctx_second: u32, + }, } #[derive(Debug, Serialize, Deserialize)] @@ -54,6 +97,9 @@ pub enum SubResponse { SpanParentResult { parent_span: Option, }, + SpanJoinResult { + span: Option, + }, Cancel { reason: String, }, @@ -68,6 +114,15 @@ pub struct ParentSpan { pub ctx: u32, } +#[derive(Debug, Serialize, Deserialize)] +pub struct SpanJoin { + pub ast_id: u32, + pub start: u32, + pub end: u32, + pub ctx: u32, +} + +#[expect(clippy::large_enum_variant)] #[derive(Debug, Serialize, Deserialize)] pub enum BidirectionalMessage { Request(Request), @@ -78,21 +133,29 @@ pub enum BidirectionalMessage { #[derive(Debug, Serialize, Deserialize)] pub enum Request { - ListMacros { dylib_path: Utf8PathBuf }, + ListMacros(ListMacros), ExpandMacro(Box), - ApiVersionCheck {}, + ApiVersionCheck(ApiVersionCheck), SetConfig(ServerConfig), } +#[expect(clippy::large_enum_variant)] #[derive(Debug, Serialize, Deserialize)] pub enum Response { ListMacros(Result, String>), - ExpandMacro(Result), ApiVersionCheck(u32), SetConfig(ServerConfig), - ExpandMacroExtended(Result), + ExpandMacro(Result), } +#[derive(Debug, Serialize, Deserialize)] +pub struct ListMacros { + pub dylib_path: Utf8PathBuf, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ApiVersionCheck {} + #[derive(Debug, Serialize, Deserialize)] pub struct ExpandMacro { pub lib: Utf8PathBuf, @@ -102,9 +165,11 @@ pub struct ExpandMacro { } #[derive(Debug, Serialize, Deserialize)] -pub struct ExpandMacroExtended { +pub struct ExpandMacroResponse { pub tree: FlatTree, pub span_data_table: Vec, + pub tracked_env_vars: HashMap, Option>>, + pub tracked_paths: HashSet>, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs index 80e4ed05c36d8..035c12669c8f6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs @@ -240,8 +240,9 @@ impl ProcMacroServerProcess { /// Enable support for rust-analyzer span mode if the server supports it. pub(crate) fn rust_analyzer_spans(&self) -> bool { match self.protocol { - Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer, - Protocol::BidirectionalPostcardPrototype { mode } => mode == SpanMode::RustAnalyzer, + Protocol::LegacyJson { mode } | Protocol::BidirectionalPostcardPrototype { mode } => { + mode == SpanMode::RustAnalyzer + } } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs index 8475c05ae8a1e..3d0e2027b70f3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs @@ -1,11 +1,11 @@ -//! Library interface for `proc-macro-srv-cli`. -//! -//! This module exposes the server main loop and protocol format for integration testing. - -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] - -#[cfg(feature = "in-rust-tree")] -extern crate rustc_driver as _; - -#[cfg(feature = "sysroot-abi")] -pub mod main_loop; +//! Library interface for `proc-macro-srv-cli`. +//! +//! This module exposes the server main loop and protocol format for integration testing. + +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_driver as _; + +#[cfg(feature = "sysroot-abi")] +pub mod main_loop; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index c525ed848b2fc..6697b6380dd20 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,4 +1,5 @@ //! The main loop of the proc-macro server. +use proc_macro_api::bidirectional_protocol::msg::{ApiVersionCheck, ListMacros}; use proc_macro_api::{ ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, @@ -72,7 +73,7 @@ fn run_new( match req { bidirectional::BidirectionalMessage::Request(request) => match request { - bidirectional::Request::ListMacros { dylib_path } => { + bidirectional::Request::ListMacros(ListMacros { dylib_path }) => { let res = srv.list_macros(&dylib_path).map(|macros| { macros .into_iter() @@ -83,7 +84,7 @@ fn run_new( send_response(stdout, bidirectional::Response::ListMacros(res))?; } - bidirectional::Request::ApiVersionCheck {} => { + bidirectional::Request::ApiVersionCheck(ApiVersionCheck {}) => { send_response( stdout, bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION), @@ -142,6 +143,7 @@ fn handle_expand_id( let attributes = attributes .map(|it| it.to_tokenstream_unresolved::(CURRENT_API_VERSION, |_, b| b)); + let mut tracked_env = Default::default(); let res = srv .expand( lib, @@ -153,11 +155,18 @@ fn handle_expand_id( def_site, call_site, mixed_site, + &mut tracked_env, None, ) .map(|it| { legacy::FlatTree::from_tokenstream_raw::(it, call_site, CURRENT_API_VERSION) }) + .map(|tree| bidirectional::ExpandMacroResponse { + tree, + span_data_table: vec![], + tracked_env_vars: tracked_env.env_vars, + tracked_paths: tracked_env.paths, + }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); send_response(stdout, bidirectional::Response::ExpandMacro(res)) @@ -343,6 +352,46 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { other => handle_failure(other), } } + + fn span_join( + &mut self, + first: proc_macro_srv::span::Span, + second: proc_macro_srv::span::Span, + ) -> Option { + assert_eq!(first.anchor.file_id, second.anchor.file_id); + let response = self.roundtrip(bidirectional::SubRequest::SpanJoin { + file_id: first.anchor.file_id.as_u32(), + ast_id_first: first.anchor.ast_id.into_raw(), + start_first: first.range.start().into(), + end_first: first.range.end().into(), + ctx_first: first.ctx.into_u32(), + ast_id_second: second.anchor.ast_id.into_raw(), + start_second: second.range.start().into(), + end_second: second.range.end().into(), + ctx_second: second.ctx.into_u32(), + }); + + match response { + Ok(bidirectional::SubResponse::SpanJoinResult { span }) => { + span.map(|bidirectional::SpanJoin { ast_id, start, end, ctx }| { + proc_macro_srv::span::Span { + range: proc_macro_srv::span::TextRange::new( + proc_macro_srv::span::TextSize::new(start), + proc_macro_srv::span::TextSize::new(end), + ), + anchor: proc_macro_srv::span::SpanAnchor { + file_id: first.anchor.file_id, + ast_id: proc_macro_srv::span::ErasedFileAstId::from_raw(ast_id), + }, + // SAFETY: spans originate from the server. If the protocol is violated, + // undefined behavior is the caller’s responsibility. + ctx: unsafe { proc_macro_srv::span::SyntaxContext::from_u32(ctx) }, + } + }) + } + other => handle_failure(other), + } + } } fn handle_expand_ra( @@ -383,6 +432,8 @@ fn handle_expand_ra( }) }); + let mut tracked_env = Default::default(); + let res = srv .expand( lib, @@ -394,6 +445,7 @@ fn handle_expand_ra( def_site, call_site, mixed_site, + &mut tracked_env, Some(&mut ProcMacroClientHandle { stdin, stdout, buf }), ) .map(|it| { @@ -407,10 +459,15 @@ fn handle_expand_ra( legacy::serialize_span_data_index_map(&span_data_table), ) }) - .map(|(tree, span_data_table)| bidirectional::ExpandMacroExtended { tree, span_data_table }) + .map(|(tree, span_data_table)| bidirectional::ExpandMacroResponse { + tree, + span_data_table, + tracked_env_vars: tracked_env.env_vars, + tracked_paths: tracked_env.paths, + }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response(stdout, bidirectional::Response::ExpandMacroExtended(res)) + send_response(stdout, bidirectional::Response::ExpandMacro(res)) } fn run_old( @@ -480,6 +537,7 @@ fn run_old( def_site, call_site, mixed_site, + &mut Default::default(), None, ) .map(|it| { @@ -522,6 +580,7 @@ fn run_old( def_site, call_site, mixed_site, + &mut Default::default(), None, ) .map(|it| { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs index ba9657a9bb45e..456b9fd70bc9d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -15,7 +15,10 @@ use expect_test::expect; use proc_macro_api::{ ProtocolFormat::BidirectionalPostcardPrototype, bidirectional_protocol::{ - msg::{ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response}, + msg::{ + ApiVersionCheck, ExpandMacro, ExpandMacroData, ExpnGlobals, ListMacros, Request, + Response, + }, reject_subrequests, }, legacy_protocol::msg::{PanicMessage, ServerConfig, SpanDataIndexMap, SpanMode}, @@ -25,8 +28,12 @@ use proc_macro_api::{ #[test] fn test_bidi_version_check_bidirectional() { with_server(BidirectionalPostcardPrototype, |writer, reader| { - let response = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let response = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); match response { Response::ApiVersionCheck(version) => { @@ -44,7 +51,7 @@ fn test_bidi_list_macros() { let response = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path }, + Request::ListMacros(ListMacros { dylib_path }), &reject_subrequests, ); @@ -84,7 +91,7 @@ fn test_bidi_list_macros_invalid_path() { let response = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, + Request::ListMacros(ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }), reject_subrequests, ); @@ -168,8 +175,12 @@ fn test_bidi_basic_call_flow() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let response1 = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let response1 = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); assert!(matches!(response1, Response::ApiVersionCheck(_))); let response2 = request_bidirectional( @@ -183,7 +194,7 @@ fn test_bidi_basic_call_flow() { let response3 = request_bidirectional( writer, reader, - Request::ListMacros { dylib_path: dylib_path.clone() }, + Request::ListMacros(ListMacros { dylib_path: dylib_path.clone() }), reject_subrequests, ); assert!(matches!(response3, Response::ListMacros(Ok(_)))); @@ -195,8 +206,12 @@ fn test_bidi_expand_nonexistent_macro() { with_server(BidirectionalPostcardPrototype, |writer, reader| { let dylib_path = proc_macro_test_dylib_path(); - let version_response = - request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests); + let version_response = request_bidirectional( + writer, + reader, + Request::ApiVersionCheck(ApiVersionCheck {}), + reject_subrequests, + ); let Response::ApiVersionCheck(version) = version_response else { panic!("expected version check response"); }; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 9a65538675fe9..96daa2c4625cd 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -12,7 +12,7 @@ use object::Object; use paths::{Utf8Path, Utf8PathBuf}; use crate::{ - PanicMessage, ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, + PanicMessage, ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv, dylib::proc_macros::ProcMacros, token_stream::TokenStream, }; @@ -45,14 +45,22 @@ impl Expander { def_site: S, call_site: S, mixed_site: S, - callback: Option>, + tracked_env: &'a mut TrackedEnv, + callback: Option>, ) -> Result, PanicMessage> where as bridge::server::Server>::TokenStream: Default, { - self.inner - .proc_macros - .expand(macro_name, macro_body, attribute, def_site, call_site, mixed_site, callback) + self.inner.proc_macros.expand( + macro_name, + macro_body, + attribute, + def_site, + call_site, + mixed_site, + tracked_env, + callback, + ) } pub(crate) fn list_macros(&self) -> impl Iterator { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs index cf00be0327cf2..4ed32f8e6ca1b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib/proc_macros.rs @@ -1,5 +1,7 @@ //! Proc macro ABI -use crate::{ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, token_stream::TokenStream}; +use crate::{ + ProcMacroClientHandle, ProcMacroKind, ProcMacroSrvSpan, TrackedEnv, token_stream::TokenStream, +}; use rustc_proc_macro::bridge; #[repr(transparent)] @@ -12,7 +14,7 @@ impl From for crate::PanicMessage { } impl ProcMacros { - pub(crate) fn expand( + pub(crate) fn expand<'a, S: ProcMacroSrvSpan>( &self, macro_name: &str, macro_body: TokenStream, @@ -20,7 +22,8 @@ impl ProcMacros { def_site: S, call_site: S, mixed_site: S, - callback: Option>, + tracked_env: &'a mut TrackedEnv, + callback: Option>, ) -> Result, crate::PanicMessage> { let parsed_attributes = attribute.unwrap_or_default(); @@ -31,7 +34,7 @@ impl ProcMacros { { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), macro_body, cfg!(debug_assertions), ); @@ -40,7 +43,7 @@ impl ProcMacros { bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), macro_body, cfg!(debug_assertions), ); @@ -49,7 +52,7 @@ impl ProcMacros { bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { let res = client.run( &bridge::server::SAME_THREAD, - S::make_server(call_site, def_site, mixed_site, callback), + S::make_server(call_site, def_site, mixed_site, tracked_env, callback), parsed_attributes, macro_body, cfg!(debug_assertions), diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 0bdc379cb6264..a71323d89d299 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -13,14 +13,8 @@ #![cfg(feature = "sysroot-abi")] #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] -#![allow( - unreachable_pub, - internal_features, - clippy::disallowed_types, - clippy::print_stderr, - unused_crate_dependencies, - unused_features -)] +#![expect(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)] +#![allow(unused_features, unused_crate_dependencies)] #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)] #[cfg(not(feature = "in-rust-tree"))] @@ -41,7 +35,7 @@ mod server_impl; mod token_stream; use std::{ - collections::{HashMap, hash_map::Entry}, + collections::{HashMap, HashSet, hash_map::Entry}, env, ffi::OsString, fs, @@ -52,7 +46,7 @@ use std::{ }; use paths::{Utf8Path, Utf8PathBuf}; -use span::Span; +use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span}; use temp_dir::TempDir; pub use crate::server_impl::token_id::SpanId; @@ -123,6 +117,7 @@ pub trait ProcMacroClientInterface { fn byte_range(&mut self, span: Span) -> Range; fn span_source(&mut self, span: Span) -> Span; fn span_parent(&mut self, span: Span) -> Option; + fn span_join(&mut self, first: Span, second: Span) -> Option; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; @@ -144,7 +139,7 @@ impl ExpandError { } impl ProcMacroSrv<'_> { - pub fn expand( + pub fn expand<'a, S: ProcMacroSrvSpan + 'a>( &self, lib: impl AsRef, env: &[(String, String)], @@ -155,7 +150,8 @@ impl ProcMacroSrv<'_> { def_site: S, call_site: S, mixed_site: S, - callback: Option>, + tracked_env: &'a mut TrackedEnv, + callback: Option>, ) -> Result, ExpandError> { let snapped_env = self.env; let expander = self.expander(lib.as_ref()).map_err(|err| ExpandError::Internal { @@ -172,13 +168,18 @@ impl ProcMacroSrv<'_> { .name(macro_name.to_owned()) .spawn_scoped(s, move || { expander.expand( - macro_name, macro_body, attribute, def_site, call_site, mixed_site, + macro_name, + macro_body, + attribute, + def_site, + call_site, + mixed_site, + tracked_env, callback, ) }); match thread.unwrap().join() { Ok(res) => res.map_err(ExpandError::Panic), - Err(payload) => { if let Some(marker) = payload.downcast_ref::() { return match marker { @@ -235,6 +236,12 @@ impl ProcMacroSrv<'_> { } } +#[derive(Default)] +pub struct TrackedEnv { + pub env_vars: HashMap, Option>>, + pub paths: HashSet>, +} + pub trait ProcMacroSrvSpan: Copy + Send + Sync { type Server<'a>: rustc_proc_macro::bridge::server::Server< TokenStream = crate::token_stream::TokenStream, @@ -243,6 +250,7 @@ pub trait ProcMacroSrvSpan: Copy + Send + Sync { call_site: Self, def_site: Self, mixed_site: Self, + tracked_env: &'a mut TrackedEnv, callback: Option>, ) -> Self::Server<'a>; } @@ -254,16 +262,10 @@ impl ProcMacroSrvSpan for SpanId { call_site: Self, def_site: Self, mixed_site: Self, + _: &'a mut TrackedEnv, callback: Option>, ) -> Self::Server<'a> { - Self::Server { - call_site, - def_site, - mixed_site, - callback, - tracked_env_vars: Default::default(), - tracked_paths: Default::default(), - } + Self::Server { call_site, def_site, mixed_site, callback } } } @@ -273,6 +275,7 @@ impl ProcMacroSrvSpan for Span { call_site: Self, def_site: Self, mixed_site: Self, + tracked_env: &'a mut TrackedEnv, callback: Option>, ) -> Self::Server<'a> { Self::Server { @@ -280,8 +283,8 @@ impl ProcMacroSrvSpan for Span { def_site, mixed_site, callback, - tracked_env_vars: Default::default(), - tracked_paths: Default::default(), + tracked_env, + fixup_id: FIXUP_ERASED_FILE_AST_ID_MARKER, } } } 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 6b6bfcc934888..61eb4243f80ac 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 @@ -4,30 +4,25 @@ //! It is an unfortunate result of how the proc-macro API works that we need to look into the //! concrete representation of the spans, and as such, RustRover cannot make use of this unless they //! change their representation to be compatible with rust-analyzer's. -use std::{ - collections::{HashMap, HashSet}, - ops::{Bound, Range}, -}; +use std::ops::{Bound, Range}; use intern::Symbol; use rustc_proc_macro::bridge::server; -use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span, TextRange, TextSize}; +use span::{ErasedFileAstId, Span, TextRange, TextSize}; use crate::{ - ProcMacroClientHandle, + ProcMacroClientHandle, TrackedEnv, bridge::{Diagnostic, ExpnGlobals, Literal, TokenTree}, server_impl::literal_from_str, }; pub struct RaSpanServer<'a> { - // FIXME: Report this back to the caller to track as dependencies - pub tracked_env_vars: HashMap, Option>>, - // FIXME: Report this back to the caller to track as dependencies - pub tracked_paths: HashSet>, + pub tracked_env: &'a mut TrackedEnv, pub call_site: Span, pub def_site: Span, pub mixed_site: Span, pub callback: Option>, + pub fixup_id: ErasedFileAstId, } impl server::Server for RaSpanServer<'_> { @@ -56,10 +51,10 @@ impl server::Server for RaSpanServer<'_> { } fn track_env_var(&mut self, var: &str, value: Option<&str>) { - self.tracked_env_vars.insert(var.into(), value.map(Into::into)); + self.tracked_env.env_vars.insert(var.into(), value.map(Into::into)); } fn track_path(&mut self, path: &str) { - self.tracked_paths.insert(path.into()); + self.tracked_env.paths.insert(path.into()); } fn literal_from_str(&mut self, s: &str) -> Result, String> { @@ -185,16 +180,14 @@ impl server::Server for RaSpanServer<'_> { fn span_join(&mut self, first: Self::Span, second: Self::Span) -> Option { // We can't modify the span range for fixup spans, those are meaningful to fixup, so just // prefer the non-fixup span. - if first.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if first.anchor.ast_id == self.fixup_id { return Some(second); } - if second.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if second.anchor.ast_id == self.fixup_id { return Some(first); } - // FIXME: Once we can talk back to the client, implement a "long join" request for anchors - // that differ in [AstId]s as joining those spans requires resolving the AstIds. if first.anchor != second.anchor { - return None; + return self.callback.as_mut()?.span_join(first, second); } // Differing context, we can't merge these so prefer the one that's root if first.ctx != second.ctx { @@ -217,7 +210,7 @@ impl server::Server for RaSpanServer<'_> { end: Bound, ) -> Option { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return Some(span); } let length = span.range.len().into(); @@ -260,7 +253,7 @@ impl server::Server for RaSpanServer<'_> { fn span_end(&mut self, span: Self::Span) -> Self::Span { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return span; } Span { range: TextRange::empty(span.range.end()), ..span } @@ -268,7 +261,7 @@ impl server::Server for RaSpanServer<'_> { fn span_start(&mut self, span: Self::Span) -> Self::Span { // We can't modify the span range for fixup spans, those are meaningful to fixup. - if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER { + if span.anchor.ast_id == self.fixup_id { return span; } Span { range: TextRange::empty(span.range.start()), ..span } 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 e1c96095c8fc1..6c393b8befb15 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 @@ -1,9 +1,6 @@ //! proc-macro server backend based on [`proc_macro_api::msg::SpanId`] as the backing span. //! This backend is rather inflexible, used by RustRover and older rust-analyzer versions. -use std::{ - collections::{HashMap, HashSet}, - ops::{Bound, Range}, -}; +use std::ops::{Bound, Range}; use intern::Symbol; use rustc_proc_macro::bridge::server; @@ -26,10 +23,6 @@ impl std::fmt::Debug for SpanId { type Span = SpanId; pub struct SpanIdServer<'a> { - // FIXME: Report this back to the caller to track as dependencies - pub tracked_env_vars: HashMap, Option>>, - // FIXME: Report this back to the caller to track as dependencies - pub tracked_paths: HashSet>, pub call_site: Span, pub def_site: Span, pub mixed_site: Span, @@ -60,12 +53,9 @@ impl server::Server for SpanIdServer<'_> { fn injected_env_var(&mut self, _: &str) -> Option { None } - fn track_env_var(&mut self, var: &str, value: Option<&str>) { - self.tracked_env_vars.insert(var.into(), value.map(Into::into)); - } - fn track_path(&mut self, path: &str) { - self.tracked_paths.insert(path.into()); - } + fn track_env_var(&mut self, _: &str, _: Option<&str>) {} + + fn track_path(&mut self, _: &str) {} fn literal_from_str(&mut self, s: &str) -> Result, String> { literal_from_str(s, self.call_site) 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 31beca20d61ee..9da4d90d65768 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 @@ -62,7 +62,16 @@ fn assert_expand_impl( let attr_ts_string = attr_ts.as_ref().map(|it| format!("{it:?}")); let res = expander - .expand(macro_name, input_ts, attr_ts, def_site, call_site, mixed_site, None) + .expand( + macro_name, + input_ts, + attr_ts, + def_site, + call_site, + mixed_site, + &mut Default::default(), + None, + ) .unwrap(); expect.assert_eq(&format!( "{input_ts_string}{}{}{}", @@ -94,8 +103,18 @@ fn assert_expand_impl( let fixture_string = format!("{fixture:?}"); let attr_string = attr.as_ref().map(|it| format!("{it:?}")); - let res = - expander.expand(macro_name, fixture, attr, def_site, call_site, mixed_site, None).unwrap(); + let res = expander + .expand( + macro_name, + fixture, + attr, + def_site, + call_site, + mixed_site, + &mut Default::default(), + None, + ) + .unwrap(); expect_spanned.assert_eq(&format!( "{fixture_string}{}{}{}", if attr_string.is_some() { "\n\n" } else { "" }, @@ -150,6 +169,10 @@ impl ProcMacroClientInterface for MockCallback<'_> { fn span_parent(&mut self, _span: Span) -> Option { None } + + fn span_join(&mut self, _: Span, _: Span) -> Option { + None + } } pub fn assert_expand_with_callback( @@ -182,7 +205,16 @@ pub fn assert_expand_with_callback( let mut callback = MockCallback { text: ra_fixture }; let res = expander - .expand(macro_name, fixture, None, def_site, call_site, mixed_site, Some(&mut callback)) + .expand( + macro_name, + fixture, + None, + def_site, + call_site, + mixed_site, + &mut Default::default(), + Some(&mut callback), + ) .unwrap(); expect_spanned.assert_eq(&format!("{res:?}")); } From 8719de90d7ed161aa3ad8d4b7b9fa53d9c05651e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 2 Feb 2026 11:33:04 +0100 Subject: [PATCH 078/114] fix: Fix `SyntaxContext::root`s technically overlapping valid interneds --- .../crates/proc-macro-api/Cargo.toml | 3 + .../src/server_impl/rust_analyzer_span.rs | 8 +- .../rust-analyzer/crates/span/src/hygiene.rs | 140 +++++++++++++----- .../rust-analyzer/crates/span/src/lib.rs | 9 +- .../rust-analyzer/crates/span/src/map.rs | 7 +- src/tools/rust-analyzer/crates/tt/src/lib.rs | 28 ++-- 6 files changed, 137 insertions(+), 58 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index a135a469e87e4..5542c5da8fc54 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -33,6 +33,9 @@ postcard.workspace = true semver.workspace = true rayon.workspace = true +[dev-dependencies] +span.workspace = true + [features] sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] default = [] 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 61eb4243f80ac..bdac9c920c433 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 @@ -189,13 +189,9 @@ impl server::Server for RaSpanServer<'_> { if first.anchor != second.anchor { return self.callback.as_mut()?.span_join(first, second); } - // Differing context, we can't merge these so prefer the one that's root + // Differing context, we can't merge these if first.ctx != second.ctx { - if first.ctx.is_root() { - return Some(second); - } else if second.ctx.is_root() { - return Some(first); - } + return Some(first); } Some(Span { range: first.range.cover(second.range), diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index f475de93e0582..70b0447569bf4 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -19,7 +19,9 @@ //! # The Call-site Hierarchy //! //! `ExpnData::call_site` in rustc, `MacroCallLoc::call_site` in rust-analyzer. +#[cfg(feature = "salsa")] use crate::Edition; + use std::fmt; /// A syntax context describes a hierarchy tracking order of macro definitions. @@ -282,7 +284,7 @@ const _: () = { let fields = SyntaxContext::ingredient(zalsa).data(zalsa, id); fields.edition } - None => Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()), + None => Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()), } } @@ -332,32 +334,9 @@ const _: () = { } }; -impl SyntaxContext { - #[inline] - pub fn is_root(self) -> bool { - (SyntaxContext::MAX_ID - Edition::LATEST as u32) <= self.into_u32() - && self.into_u32() <= (SyntaxContext::MAX_ID - Edition::Edition2015 as u32) - } - - #[inline] - pub fn remove_root_edition(&mut self) { - if self.is_root() { - *self = Self::root(Edition::Edition2015); - } - } - - /// The root context, which is the parent of all other contexts. All `FileId`s have this context. - #[inline] - pub const fn root(edition: Edition) -> Self { - let edition = edition as u32; - // SAFETY: Roots are valid `SyntaxContext`s - unsafe { SyntaxContext::from_u32(SyntaxContext::MAX_ID - edition) } - } -} - #[cfg(feature = "salsa")] impl<'db> SyntaxContext { - const MAX_ID: u32 = salsa::Id::MAX_U32 - 1; + const MAX_ROOT_ID: u32 = salsa::Id::MAX_U32 + Edition::LATEST as u32; #[inline] pub const fn into_u32(self) -> u32 { @@ -378,7 +357,8 @@ impl<'db> SyntaxContext { if self.is_root() { None } else { - // SAFETY: By our invariant, this is either a root (which we verified it's not) or a valid `salsa::Id`. + // SAFETY: By our invariant, this is either a root (which we verified it's not) or a + // valid `salsa::Id` index. unsafe { Some(salsa::Id::from_index(self.0)) } } } @@ -389,6 +369,27 @@ impl<'db> SyntaxContext { unsafe { Self::from_u32(id.index()) } } + #[inline] + pub fn is_root(self) -> bool { + (SyntaxContext::MAX_ROOT_ID - Edition::LATEST as u32) <= self.into_u32() + && self.into_u32() <= (SyntaxContext::MAX_ROOT_ID - Edition::Edition2015 as u32) + } + + #[inline] + pub fn remove_root_edition(&mut self) { + if self.is_root() { + *self = Self::root(Edition::Edition2015); + } + } + + /// The root context, which is the parent of all other contexts. All `FileId`s have this context. + #[inline] + pub const fn root(edition: Edition) -> Self { + let edition = edition as u32; + // SAFETY: Roots are valid `SyntaxContext`s + unsafe { SyntaxContext::from_u32(SyntaxContext::MAX_ROOT_ID - edition) } + } + #[inline] pub fn outer_mark( self, @@ -447,15 +448,8 @@ impl<'db> SyntaxContext { #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] pub struct SyntaxContext(u32); -#[allow(dead_code)] -const SALSA_MAX_ID_MIRROR: u32 = u32::MAX - 0xFF; -#[cfg(feature = "salsa")] -const _: () = assert!(salsa::Id::MAX_U32 == SALSA_MAX_ID_MIRROR); - #[cfg(not(feature = "salsa"))] impl SyntaxContext { - const MAX_ID: u32 = SALSA_MAX_ID_MIRROR - 1; - pub const fn into_u32(self) -> u32 { self.0 } @@ -496,16 +490,28 @@ impl Transparency { } } +#[cfg(feature = "salsa")] impl fmt::Display for SyntaxContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_root() { - write!(f, "ROOT{}", Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()).number()) + write!( + f, + "ROOT{}", + Edition::from_u32(SyntaxContext::MAX_ROOT_ID - self.into_u32()).number() + ) } else { write!(f, "{}", self.into_u32()) } } } +#[cfg(not(feature = "salsa"))] +impl fmt::Display for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.into_u32()) + } +} + impl std::fmt::Debug for SyntaxContext { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if f.alternate() { @@ -515,3 +521,69 @@ impl std::fmt::Debug for SyntaxContext { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_root_edition_is_root() { + for edition in Edition::iter() { + let ctx = SyntaxContext::root(edition); + assert!(ctx.is_root(), "{edition} root should be identified as root"); + } + } + + #[test] + fn test_root_edition_editions() { + let db = salsa::DatabaseImpl::new(); + for edition in Edition::iter() { + let ctx = SyntaxContext::root(edition); + assert_eq!(edition, ctx.edition(&db), "{edition} root should have edition {edition}"); + } + } + + #[test] + fn test_roots_do_not_overlap_with_salsa_ids() { + for edition in Edition::iter() { + let root = SyntaxContext::root(edition); + let root_u32 = root.into_u32(); + assert!( + root_u32 >= salsa::Id::MAX_U32, + "Root context for {:?} (value {}) must be >= salsa::Id::MAX_U32 ({}) to avoid collision", + edition, + root_u32, + salsa::Id::MAX_U32 + ); + } + } + + #[test] + fn test_non_root_value_is_not_root() { + for edition in Edition::iter() { + // SAFETY: This is just for testing purposes + let ctx = unsafe { SyntaxContext::from_u32(edition as u32 + 1) }; + assert!(!ctx.is_root(), "{edition} root should be identified as root"); + } + } + + #[test] + fn test_interned_context_round_trips_through_u32() { + let db = salsa::DatabaseImpl::new(); + let root = SyntaxContext::root(Edition::Edition2015); + let ctx = SyntaxContext::new( + &db, + None, + Transparency::Opaque, + Edition::Edition2021, + root, + |_| root, + |_| root, + ); + + // SAFETY: The value was produced by `SyntaxContext::into_u32` above. + let round_tripped = unsafe { SyntaxContext::from_u32(ctx.into_u32()) }; + assert_eq!(round_tripped.edition(&db), Edition::Edition2021); + assert_eq!(round_tripped.parent(&db), root); + } +} diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index bfe7b2620d56c..8274a94edb4ce 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -51,13 +51,20 @@ impl Span { } // Differing context, we can't merge these so prefer the one that's root if self.ctx != other.ctx { + #[cfg(feature = "salsa")] if self.ctx.is_root() { return Some(other); } else if other.ctx.is_root() { return Some(self); } + None + } else { + Some(Span { + range: self.range.cover(other.range), + anchor: other.anchor, + ctx: other.ctx, + }) } - Some(Span { range: self.range.cover(other.range), anchor: other.anchor, ctx: other.ctx }) } pub fn eq_ignoring_ctx(self, other: Self) -> bool { diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs index dc7d471aa03ac..d8309b04d4fc0 100644 --- a/src/tools/rust-analyzer/crates/span/src/map.rs +++ b/src/tools/rust-analyzer/crates/span/src/map.rs @@ -6,8 +6,8 @@ use std::{fmt, hash::Hash}; use stdx::{always, itertools::Itertools}; use crate::{ - EditionedFileId, ErasedFileAstId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, - TextRange, TextSize, + EditionedFileId, ErasedFileAstId, ROOT_ERASED_FILE_AST_ID, Span, SyntaxContext, TextRange, + TextSize, }; /// Maps absolute text ranges for the corresponding file to the relevant span data. @@ -220,6 +220,7 @@ impl RealSpanMap { Self { file_id, pairs, end } } + #[cfg(feature = "salsa")] pub fn span_for_range(&self, range: TextRange) -> Span { assert!( range.end() <= self.end, @@ -234,7 +235,7 @@ impl RealSpanMap { let (offset, ast_id) = self.pairs[idx - 1]; Span { range: range - offset, - anchor: SpanAnchor { file_id: self.file_id, ast_id }, + anchor: crate::SpanAnchor { file_id: self.file_id, ast_id }, ctx: SyntaxContext::root(self.file_id.edition()), } } diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs index 72b0d762ef62d..7b46c33596441 100644 --- a/src/tools/rust-analyzer/crates/tt/src/lib.rs +++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs @@ -765,27 +765,24 @@ impl Subtree { pub fn pretty(tkns: TokenTreesView<'_>) -> String { return dispatch_ref! { - match tkns.repr => tt => pretty_impl(tt) + match tkns.repr => tt => pretty_impl(tkns, tt) }; use crate::storage::TokenTree; - fn tokentree_to_text(tkn: &TokenTree, tkns: &mut &[TokenTree]) -> String { + fn tokentree_to_text( + tkns_view: TokenTreesView<'_>, + tkn: &TokenTree, + tkns: &mut &[TokenTree], + ) -> String { match tkn { TokenTree::Ident { sym, is_raw, .. } => format!("{}{}", is_raw.as_str(), sym), - &TokenTree::Literal { ref text_and_suffix, kind, suffix_len, span: _ } => { + &TokenTree::Literal { ref text_and_suffix, kind, suffix_len, span } => { format!( "{}", Literal { text_and_suffix: text_and_suffix.clone(), - span: Span { - range: TextRange::empty(TextSize::new(0)), - anchor: span::SpanAnchor { - file_id: span::EditionedFileId::from_raw(0), - ast_id: span::FIXUP_ERASED_FILE_AST_ID_MARKER - }, - ctx: span::SyntaxContext::root(span::Edition::Edition2015) - }, + span: span.span(tkns_view.span_parts), kind, suffix_len } @@ -794,7 +791,7 @@ pub fn pretty(tkns: TokenTreesView<'_>) -> String { TokenTree::Punct { char, .. } => format!("{}", char), TokenTree::Subtree { len, delim_kind, .. } => { let (subtree_content, rest) = tkns.split_at(*len as usize); - let content = pretty_impl(subtree_content); + let content = pretty_impl(tkns_view, subtree_content); *tkns = rest; let (open, close) = match *delim_kind { DelimiterKind::Brace => ("{", "}"), @@ -807,13 +804,16 @@ pub fn pretty(tkns: TokenTreesView<'_>) -> String { } } - fn pretty_impl(mut tkns: &[TokenTree]) -> String { + fn pretty_impl( + tkns_view: TokenTreesView<'_>, + mut tkns: &[TokenTree], + ) -> String { let mut last = String::new(); let mut last_to_joint = true; while let Some((tkn, rest)) = tkns.split_first() { tkns = rest; - last = [last, tokentree_to_text(tkn, &mut tkns)].join(if last_to_joint { + last = [last, tokentree_to_text(tkns_view, tkn, &mut tkns)].join(if last_to_joint { "" } else { " " From 2267c11edd280a72b869447e3574aff3e7598d31 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 25 May 2026 20:31:57 +0800 Subject: [PATCH 079/114] fix: Fix extract variable in token tree replace range `SyntaxEditor::replace` only replacing range with the same parent Example --- ```rust fn main() { let maybe_str = Some("world"); write!((), "Hello, {}!", $0maybe_str.unwrap()$0); } ``` **Before this PR** ```rust fn main() { let maybe_str = Some("world"); let $0var_name = maybe_str.unwrap(); write!((), "Hello, {}!", var_namemaybe_str.unwrap()); } ``` **After this PR** ```rust fn main() { let maybe_str = Some("world"); let $0var_name = maybe_str.unwrap(); write!((), "Hello, {}!", var_name); } ``` --- .../src/handlers/extract_variable.rs | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index d4a0490f16a41..e1b91c9e89d5a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -13,7 +13,10 @@ use syntax::{ syntax_editor::{Element, Position}, }; -use crate::{AssistContext, AssistId, Assists, utils::is_body_const}; +use crate::{ + AssistContext, AssistId, Assists, + utils::{cover_edit_range, is_body_const}, +}; // Assist: extract_variable // @@ -107,9 +110,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - .skip_while(|it| !it.text_range().contains_range(range)) .find_map(valid_target_expr(ctx))?; let original_range = ctx.sema.original_range(expr.syntax()); - let (first, last) = extract_token_range_of(&node, original_range.range)?; - let to_extract = first.syntax_element()..=last.syntax_element(); - (to_extract, expr) + (cover_edit_range(&node, original_range.range), expr) } else { let expr = node .descendants() @@ -3000,6 +3001,28 @@ fn main() { let $0var_name = 2+3; let x = foo!(= Foo { x: var_name + 4 }); } +"#, + "Extract into variable", + ); + } + + #[test] + fn regression_22441() { + check_assist_by_label( + extract_variable, + r#" +//- minicore: option, write +fn main() { + let maybe_str = Some("world"); + write!((), "Hello, {}!", $0maybe_str.unwrap()$0); +} +"#, + r#" +fn main() { + let maybe_str = Some("world"); + let $0var_name = maybe_str.unwrap(); + write!((), "Hello, {}!", var_name); +} "#, "Extract into variable", ); From 10821c2d6c4df2a74c55a22b237e4f6af3346147 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 25 May 2026 21:45:05 +0800 Subject: [PATCH 080/114] fix: analysis expected ty in enum variant Example --- ```rust enum Foo { Var(T) }; fn foo(x: Foo>) -> Foo { match x { Foo::Var($0) => () } } ``` **Before this PR** ```rust ty: ?, name: ? ``` **After this PR** ```rust ty: Option, name: ? ``` --- .../rust-analyzer/crates/hir/src/semantics.rs | 8 +++++++ .../crates/hir/src/source_analyzer.rs | 22 +++++++++++++++++++ .../ide-completion/src/context/analysis.rs | 2 +- .../ide-completion/src/context/tests.rs | 10 +++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index a23d045c6fc0a..8a761c2fc04b0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1947,6 +1947,14 @@ impl<'db> SemanticsImpl<'db> { self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field) } + pub fn resolve_tuple_struct_pat_fields( + &self, + tuple_struct_pat: &ast::TupleStructPat, + ) -> Option)>> { + self.analyze(tuple_struct_pat.syntax())? + .resolve_tuple_struct_pat_fields(self.db, tuple_struct_pat) + } + // FIXME: Replace this with `resolve_macro_call2` pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option { let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call); 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 f24ec420b5dfb..32e6acb6c58da 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -933,6 +933,28 @@ impl<'db> SourceAnalyzer<'db> { )) } + pub(crate) fn resolve_tuple_struct_pat_fields( + &self, + db: &'db dyn HirDatabase, + tuple_struct_pat: &ast::TupleStructPat, + ) -> Option)>> { + let interner = DbInterner::new_no_crate(db); + let pat_id = self.pat_id(&tuple_struct_pat.clone().into())?; + let variant_id = self.infer()?.variant_resolution_for_pat(pat_id.as_pat()?)?; + let (_adt, substs) = self.infer()?.pat_ty(pat_id.as_pat()?).as_adt()?; + + Some( + db.field_types(variant_id) + .iter() + .map(|(local_id, ty)| { + let def = Field { parent: variant_id.into(), id: local_id }; + let ty = ty.get().instantiate(interner, substs).skip_norm_wip(); + (def, self.ty(ty)) + }) + .collect(), + ) + } + pub(crate) fn resolve_bind_pat_to_const( &self, db: &'db dyn HirDatabase, 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 7aec6f7e8f9e6..b2ee94d49cbfe 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 @@ -801,7 +801,7 @@ fn expected_type_and_name<'db>( (ty, None) }, ast::TupleStructPat(it) => { - let fields = sema.type_of_pat(&it.clone().into()).map(|ty| ty.original.fields(sema.db)); + let fields = sema.resolve_tuple_struct_pat_fields(&it); let nr = it.fields().take_while(|it| it.syntax().text_range().end() <= token.text_range().start()).count(); let ty = fields.and_then(|fields| Some(rebase_ty(fields.get(nr)?.1.clone()))); (ty, None) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs index 706e8ea3c0c1f..1d1a55c6fe144 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/tests.rs @@ -337,6 +337,16 @@ struct Foo(T); fn foo(x: Foo>) -> Foo { match x { Foo($0) => () } } +"#, + expect![[r#"ty: Option, name: ?"#]], + ); + check_expected_type_and_name( + r#" +//- minicore: option +enum Foo { Var(T) }; +fn foo(x: Foo>) -> Foo { + match x { Foo::Var($0) => () } +} "#, expect![[r#"ty: Option, name: ?"#]], ); From 2e1fd86e0cbefed6f55bc64aaaf3f5ce4482a6a5 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 25 May 2026 23:29:22 +0800 Subject: [PATCH 081/114] fix: flip coerce_never type_mismatch tys Example --- **Before this PR** ```rust fn f() { if 1 {} //^ error: expected i32, found bool match () { _ if 1 => (), _ => () } //^ error: expected i32, found bool } ``` **After this PR** ```rust fn f() { if 1 {} //^ error: expected bool, found i32 match () { _ if 1 => (), _ => () } //^ error: expected bool, found i32 } ``` --- .../rust-analyzer/crates/hir-ty/src/infer/expr.rs | 2 +- .../ide-diagnostics/src/handlers/type_mismatch.rs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) 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 8df6d9bee4c57..c7562567efec4 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 @@ -306,7 +306,7 @@ impl<'db> InferenceContext<'_, 'db> { } } else { if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - _ = self.demand_eqtype(expr.into(), ty, expected_ty); + _ = self.demand_eqtype(expr.into(), expected_ty, ty); } ty } 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 678e45e145dc6..f1b597b44ff85 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 @@ -1206,6 +1206,20 @@ fn f() { ); } + #[test] + fn type_mismatch_in_condition() { + check_diagnostics( + r#" +fn f() { + if 1 {} + //^ error: expected bool, found i32 + match () { _ if 1 => (), _ => () } + //^ error: expected bool, found i32 +} +"#, + ); + } + #[test] fn regression_14768() { check_diagnostics( From d4a36436f25fb88eafe6d1afc622b63e5cdeaac9 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 26 May 2026 03:35:29 +0800 Subject: [PATCH 082/114] minor: no suggest ref match for unit type --- src/tools/rust-analyzer/crates/ide-completion/src/render.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index fcc81c3611a78..c44e0597df857 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -718,7 +718,7 @@ fn compute_ref_match( ctx: &CompletionContext<'_, '_>, completion_ty: &hir::Type<'_>, ) -> Option { - if compute_type_match(ctx, completion_ty).is_some() { + if compute_type_match(ctx, completion_ty).is_some() || completion_ty.is_unit() { return None; } let expected_type = ctx.expected_type.as_ref()?; @@ -3189,9 +3189,7 @@ fn main() { lc ssss &mut i32 [type_could_unify+local] md core:: [] fn foo(…) fn(&T) [] - fn &foo(…) [type] fn main() fn() [] - fn &main() [type] "#]], ); } From a2a3be2e37764b6d964b47847a08de9967b765d7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 26 May 2026 04:03:03 +0800 Subject: [PATCH 083/114] Add a fixme comment --- src/tools/rust-analyzer/crates/hir/src/semantics.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 8a761c2fc04b0..0d6f9accf89c1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1947,6 +1947,7 @@ impl<'db> SemanticsImpl<'db> { self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field) } + // FIXME: Remove this from https://github.com/rust-lang/rust-analyzer/pull/22449#discussion_r3299763452 pub fn resolve_tuple_struct_pat_fields( &self, tuple_struct_pat: &ast::TupleStructPat, From 4f1c8b329a6caba462e05047eab09165dadf5919 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 26 May 2026 01:47:54 +0800 Subject: [PATCH 084/114] fix: Add type_match score for struct_pat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example --- ```rust struct Foo(Bar); struct Bar { field: i32 } fn go(Foo($0): Foo) {} ``` **Before this PR** ```text st Bar [] bn Bar {…} Bar { field$1 }$0 [] st Foo [] bn Foo(…) Foo($1)$0 [] ``` **After this PR** ```text bn Bar {…} Bar { field$1 }$0 [type] st Bar [] st Foo [] bn Foo(…) Foo($1)$0 [] ``` --- .../crates/ide-completion/src/item.rs | 7 +++ .../crates/ide-completion/src/render.rs | 62 +++++++++++++++++++ .../ide-completion/src/render/pattern.rs | 5 +- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index cfadec6287947..f54c8dd8b0fc0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -181,6 +181,9 @@ pub struct CompletionRelevance { /// } /// ``` pub is_local: bool, + /// This is missing variant in the patterns. + /// Maybe this can also be used for struct fields. + pub is_missing: bool, /// Populated when the completion item comes from a trait (impl). pub trait_: Option, /// This is set when an import is suggested in a use item whose name is already imported. @@ -286,6 +289,7 @@ impl CompletionRelevance { exact_name_match, type_match, is_local, + is_missing, is_name_already_imported, requires_import, is_private_editable, @@ -306,6 +310,9 @@ impl CompletionRelevance { if is_local { score += 1; } + if is_missing { + score += 1; + } // lower rank private things if !is_private_editable { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index f559aae789e24..f213951ddf107 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -874,6 +874,7 @@ mod tests { exact_name_match, type_match, is_local, + is_missing, trait_, is_name_already_imported: _, requires_import, @@ -889,6 +890,7 @@ mod tests { (type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), "type_could_unify"), (exact_name_match, "name"), (is_local, "local"), + (is_missing, "missing"), (postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet"), (trait_.is_some_and(|it| it.is_op_method), "op_method"), (requires_import, "requires_import"), @@ -1292,6 +1294,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1344,6 +1347,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1489,6 +1493,7 @@ fn main() { Foo::Fo$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1575,6 +1580,7 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1613,6 +1619,7 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1664,6 +1671,7 @@ fn main() { som$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1723,6 +1731,7 @@ fn main() { som$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1766,6 +1775,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1809,6 +1819,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1854,6 +1865,7 @@ fn main() { A::$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1890,6 +1902,7 @@ fn main() { A::$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1940,6 +1953,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -1983,6 +1997,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2023,6 +2038,7 @@ impl A$0 exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2063,6 +2079,7 @@ fn main() { A$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2107,6 +2124,7 @@ fn main() { a$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2151,6 +2169,7 @@ fn main() { A { the$0 } } CouldUnify, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2206,6 +2225,7 @@ impl S { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2299,6 +2319,7 @@ use self::E::*; exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2371,6 +2392,7 @@ fn foo(s: S) { s.$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2591,6 +2613,7 @@ fn f() -> i32 { Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2697,6 +2720,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: true, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2745,6 +2769,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: true, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -2856,6 +2881,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3030,6 +3056,37 @@ mod b { ); } + #[test] + fn score_patterns() { + check_relevance( + r#" +struct Foo(Bar); +struct Bar { field: i32 } +fn go(Foo($0): Foo) {} +"#, + expect![[r#" + bn Bar {…} Bar { field$1 }$0 [type] + st Bar [] + st Foo [] + bn Foo(…) Foo($1)$0 [] + "#]], + ); + + check_relevance( + r#" +struct Foo(Bar); +enum Bar { Variant { field: i32 } } +fn go(foo: Foo) { match foo { Foo($0) } } +"#, + expect![[r#" + bn Bar::Variant {…} Bar::Variant { field$1 }$0 [type] + en Bar [] + st Foo [] + bn Foo(…) Foo($1)$0 [] + "#]], + ); + } + #[test] fn test_avoid_redundant_suggestion() { check_relevance( @@ -3721,6 +3778,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3816,6 +3874,7 @@ fn foo() { Exact, ), is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -3870,6 +3929,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: None, is_name_already_imported: false, requires_import: false, @@ -4362,6 +4422,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: Some( CompletionRelevanceTraitInfo { notable_trait: true, @@ -4398,6 +4459,7 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, + is_missing: false, trait_: Some( CompletionRelevanceTraitInfo { notable_trait: true, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 7e70aa13d4229..13a83eb216324 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -116,9 +116,8 @@ fn build_completion<'db>( let mut relevance = ctx.completion_relevance(); let adt_ty = ctx.completion.rebase_ty(&adt_ty); - if is_variant_missing { - relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); - } + relevance.type_match = super::compute_type_match(ctx.completion, &adt_ty); + relevance.is_missing = is_variant_missing; let mut item = CompletionItem::new( CompletionItemKind::Binding, From b5d6c335b3f32b6582e554babe31f3350140e0e8 Mon Sep 17 00:00:00 2001 From: WaterWhisperer Date: Wed, 20 May 2026 22:01:31 +0800 Subject: [PATCH 085/114] feat: add diagnostic for E0033 --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 5 ++ .../crates/hir-ty/src/infer/pat.rs | 25 ++++----- .../crates/hir-ty/src/infer/unify.rs | 1 + .../crates/hir/src/diagnostics.rs | 11 ++++ .../cannot_implicitly_deref_trait_object.rs | 53 +++++++++++++++++++ .../crates/ide-diagnostics/src/lib.rs | 2 + 6 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs 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 7e5fdfdd81c6b..2df2789a2eee9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -405,6 +405,11 @@ pub enum InferenceDiagnostic { expr: ExprId, found: StoredTy, }, + CannotImplicitlyDerefTraitObject { + #[type_visitable(ignore)] + pat: PatId, + found: StoredTy, + }, CannotIndexInto { #[type_visitable(ignore)] expr: ExprId, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index f1af8a0b73a5b..c36c29d6c7294 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -959,22 +959,23 @@ impl<'a, 'db> InferenceContext<'a, 'db> { local_ty } - fn check_dereferenceable(&self, expected: Ty<'db>, inner: PatId) -> Result<(), ()> { + fn check_dereferenceable( + &mut self, + expected: Ty<'db>, + pat: PatId, + inner: PatId, + ) -> Result<(), ()> { if let Pat::Bind { .. } = self.store[inner] && let Some(pointee_ty) = self.shallow_resolve(expected).builtin_deref(true) && let TyKind::Dynamic(..) = pointee_ty.kind() { // This is "x = dyn SomeTrait" being reduced from // "let &x = &dyn SomeTrait" or "let box x = Box", an error. - // FIXME: Emit an error. rustc emits this message: - const _CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ -This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ -pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ -this type has no compile-time size. Therefore, all accesses to trait types must be through \ -pointers. If you encounter this error you should try to avoid dereferencing the pointer. - -You can read more about trait objects in the Trait Objects section of the Reference: \ -https://doc.rust-lang.org/reference/types.html#trait-objects"; + self.push_diagnostic(InferenceDiagnostic::CannotImplicitlyDerefTraitObject { + pat, + found: expected.store(), + }); + return Err(()); } Ok(()) } @@ -1260,7 +1261,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; ) -> Ty<'db> { let interner = self.interner(); let (box_ty, inner_ty) = self - .check_dereferenceable(expected, inner) + .check_dereferenceable(expected, pat, inner) .map(|()| { // Here, `demand::subtype` is good enough, but I don't // think any errors can be introduced by using `demand::eqtype`. @@ -1473,7 +1474,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; } } - let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, inner) { + let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, pat, inner) { Ok(()) => { // `demand::subtype` would be good enough, but using `eqtype` turns // out to be equally general. See (note_1) for details. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 393b404cde5dc..4c808714d9506 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -578,6 +578,7 @@ pub(super) mod resolve_completely { self.resolve_completely(diagnostic); if let InferenceDiagnostic::CannotBeDereferenced { found: ty, .. } + | InferenceDiagnostic::CannotImplicitlyDerefTraitObject { found: ty, .. } | InferenceDiagnostic::CannotIndexInto { found: ty, .. } | InferenceDiagnostic::ExpectedFunction { found: ty, .. } | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index f5b1463f6ffb6..f3188c9aada55 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -104,6 +104,7 @@ diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, BreakOutsideOfLoop, CannotBeDereferenced<'db>, + CannotImplicitlyDerefTraitObject<'db>, CannotIndexInto<'db>, CastToUnsized<'db>, ExpectedArrayOrSlicePat<'db>, @@ -334,6 +335,12 @@ pub struct CannotBeDereferenced<'db> { pub found: Type<'db>, } +#[derive(Debug)] +pub struct CannotImplicitlyDerefTraitObject<'db> { + pub pat: InFile, + pub found: Type<'db>, +} + #[derive(Debug)] pub struct CannotIndexInto<'db> { pub expr: InFile, @@ -973,6 +980,10 @@ impl<'db> AnyDiagnostic<'db> { let expr = expr_syntax(*expr)?; CannotBeDereferenced { expr, found: new_ty(found.as_ref()) }.into() } + InferenceDiagnostic::CannotImplicitlyDerefTraitObject { pat, found } => { + let pat = pat_syntax(*pat)?.map(Into::into); + CannotImplicitlyDerefTraitObject { pat, found: new_ty(found.as_ref()) }.into() + } InferenceDiagnostic::CannotIndexInto { expr, found } => { let expr = expr_syntax(*expr)?; CannotIndexInto { expr, found: new_ty(found.as_ref()) }.into() diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs new file mode 100644 index 0000000000000..eca14144dd1c5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/cannot_implicitly_deref_trait_object.rs @@ -0,0 +1,53 @@ +use hir::HirDisplay; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: cannot-implicitly-deref-trait-object +// +// This diagnostic is triggered when a pointer to a trait object is implicitly +// dereferenced by a pattern. +pub(crate) fn cannot_implicitly_deref_trait_object( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::CannotImplicitlyDerefTraitObject<'_>, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0033"), + format!( + "type `{}` cannot be dereferenced", + d.found.display(ctx.sema.db, ctx.display_target) + ), + d.pat.map(Into::into), + ) + .stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn trait_object_pattern_deref() { + check_diagnostics( + r#" +trait Trait {} + +fn f(x: &dyn Trait) { + let &ref _y = x; + //^^^^^^^ error: type `&(dyn Trait + 'static)` cannot be dereferenced +} +"#, + ); + } + + #[test] + fn allows_sized_ref_pattern_deref() { + check_diagnostics( + r#" +fn f(x: &i32) { + let &ref _y = x; +} +"#, + ); + } +} 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 18251bc8a2dda..aec68b55c78dc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -34,6 +34,7 @@ mod handlers { pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; pub(crate) mod cannot_be_dereferenced; + pub(crate) mod cannot_implicitly_deref_trait_object; pub(crate) mod cannot_index_into; pub(crate) mod duplicate_field; pub(crate) mod elided_lifetimes_in_path; @@ -436,6 +437,7 @@ pub fn semantic_diagnostics( let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), AnyDiagnostic::CannotBeDereferenced(d) => handlers::cannot_be_dereferenced::cannot_be_dereferenced(&ctx, &d), + AnyDiagnostic::CannotImplicitlyDerefTraitObject(d) => handlers::cannot_implicitly_deref_trait_object::cannot_implicitly_deref_trait_object(&ctx, &d), AnyDiagnostic::CannotIndexInto(d) => handlers::cannot_index_into::cannot_index_into(&ctx, &d), AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), AnyDiagnostic::ArrayPatternWithoutFixedLength(d) => { From cf8235c50612500b7f0dfca6454896bdb8ad737e Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 30 Apr 2026 21:14:54 +0200 Subject: [PATCH 086/114] misc improvements - use `ctx.db()` - move `add_mutable_reference_to_let_stmt` closer to `add_reference_to_let_stmt` --- .../src/handlers/type_mismatch.rs | 65 +++++++++---------- 1 file changed, 32 insertions(+), 33 deletions(-) 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 f1b597b44ff85..f984c4d3df782 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 @@ -51,10 +51,10 @@ pub(crate) fn type_mismatch( format!( "expected {}, found {}", d.expected - .display(ctx.sema.db, ctx.display_target) + .display(ctx.db(), ctx.display_target) .with_closure_style(ClosureStyle::ClosureWithId), d.actual - .display(ctx.sema.db, ctx.display_target) + .display(ctx.db(), ctx.display_target) .with_closure_style(ClosureStyle::ClosureWithId), ), display_range, @@ -89,7 +89,7 @@ fn add_reference( let (_, mutability) = d.expected.as_reference()?; let actual_with_ref = d.actual.add_reference(ctx.db(), mutability); - if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) { + if !actual_with_ref.could_coerce_to(ctx.db(), &d.expected) { return None; } @@ -107,7 +107,7 @@ fn add_missing_ok_or_some( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range: expr_range } = ctx.sema.original_range_opt(expr.syntax())?; @@ -126,14 +126,13 @@ fn add_missing_ok_or_some( let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" }; - let wrapped_actual_ty = - expected_adt.ty(ctx.sema.db).instantiate(std::iter::once(d.actual.clone())); + let wrapped_actual_ty = expected_adt.ty(ctx.db()).instantiate([d.actual.clone()]); - if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) { + if !d.expected.could_unify_with(ctx.db(), &wrapped_actual_ty) { return None; } - let file_id = file_id.file_id(ctx.sema.db); + let file_id = file_id.file_id(ctx.db()); if d.actual.is_unit() { if let Expr::BlockExpr(block) = &expr { @@ -202,7 +201,7 @@ fn remove_unnecessary_wrapper( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let db = ctx.sema.db; + let db = ctx.db(); let root = db.parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); // FIXME: support inside MacroCall? @@ -233,7 +232,7 @@ fn remove_unnecessary_wrapper( let inner_arg = call_expr.arg_list()?.args().next()?; let file_id = expr_ptr.file_id.original_file(db); - let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.sema.db)); + let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.db())); let editor; match inner_arg { // We're returning `()` @@ -267,7 +266,7 @@ fn remove_unnecessary_wrapper( } } - builder.add_file_edits(file_id.file_id(ctx.sema.db), editor); + builder.add_file_edits(file_id.file_id(ctx.db()), editor); let name = format!("Remove unnecessary {}() wrapper", variant.name(db).as_str()); acc.push(fix( "remove_unnecessary_wrapper", @@ -284,7 +283,7 @@ fn remove_semicolon( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); if !d.actual.is_unit() { return None; @@ -294,14 +293,14 @@ fn remove_semicolon( let expr_before_semi = block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?; let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original(); - if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) { + if !type_before_semi.could_coerce_to(ctx.db(), &d.expected) { return None; } let semicolon_range = expr_before_semi.semicolon_token()?.text_range(); let edit = TextEdit::delete(semicolon_range); let source_change = SourceChange::from_text_edit( - expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db), + expr_ptr.file_id.original_file(ctx.db()).file_id(ctx.db()), edit, ); @@ -315,21 +314,21 @@ fn str_ref_to_owned( expr_ptr: &InFile>, acc: &mut Vec, ) -> Option<()> { - let expected = d.expected.display(ctx.sema.db, ctx.display_target); + let expected = d.expected.display(ctx.db(), ctx.display_target); // FIXME do this properly let is_applicable = d.actual.strip_reference().is_str() && expected.to_string() == "String"; if !is_applicable { return None; } - let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id); + let root = ctx.db().parse_or_expand(expr_ptr.file_id); let expr = expr_ptr.value.to_node(&root); let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(expr.syntax())?; let to_owned = ".to_owned()".to_owned(); let edit = TextEdit::insert(range.end(), to_owned); - let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit); + let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.db()), edit); acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, range)); Some(()) @@ -483,6 +482,22 @@ fn main() { ); } + #[test] + fn add_mutable_reference_to_let_stmt() { + check_fix( + r#" +fn main() { + let _test: &mut i32 = $0123; +} + "#, + r#" +fn main() { + let _test: &mut i32 = &mut 123; +} + "#, + ); + } + #[test] fn add_reference_to_macro_call() { check_fix( @@ -511,22 +526,6 @@ fn main() { ); } - #[test] - fn add_mutable_reference_to_let_stmt() { - check_fix( - r#" -fn main() { - let _test: &mut i32 = $0123; -} - "#, - r#" -fn main() { - let _test: &mut i32 = &mut 123; -} - "#, - ); - } - #[test] fn const_generic_type_mismatch() { check_diagnostics( From eb465102dd1f42da7b0d883349bbb4fc3a73cd9d Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Thu, 9 Apr 2026 16:04:29 +0200 Subject: [PATCH 087/114] fix(assists/add_reference_here): _modify_ the reference type when dealing with &T->&mut T ..instead of adding another layer of reference, i.e. &T -> &mut &T Co-authored-by: Ana Hobden --- .../src/handlers/type_mismatch.rs | 255 +++++++++++++++++- 1 file changed, 249 insertions(+), 6 deletions(-) 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 f984c4d3df782..afd6130bb3e9f 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 @@ -8,7 +8,7 @@ use ide_db::{ use syntax::{ AstNode, AstPtr, TextSize, ast::{ - self, BlockExpr, Expr, ExprStmt, HasArgList, + self, BlockExpr, Expr, ExprStmt, HasArgList, RefExpr, edit::{AstNodeEdit, IndentLevel}, }, }; @@ -69,7 +69,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option< if let Some(expr_ptr) = d.expr_or_pat.value.cast::() { let expr_ptr = &InFile { file_id: d.expr_or_pat.file_id, value: expr_ptr }; - add_reference(ctx, d, expr_ptr, &mut fixes); + add_or_fix_reference(ctx, d, expr_ptr, &mut fixes); add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes); remove_unnecessary_wrapper(ctx, d, expr_ptr, &mut fixes); remove_semicolon(ctx, d, expr_ptr, &mut fixes); @@ -79,7 +79,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option< if fixes.is_empty() { None } else { Some(fixes) } } -fn add_reference( +fn add_or_fix_reference( ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>, expr_ptr: &InFile>, @@ -87,13 +87,44 @@ fn add_reference( ) -> Option<()> { let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into())); - let (_, mutability) = d.expected.as_reference()?; - let actual_with_ref = d.actual.add_reference(ctx.db(), mutability); + let (expected_with_ref_removed, expected_mutability) = d.expected.as_reference()?; + + if let Some((actual_with_ref_removed, hir::Mutability::Shared)) = d.actual.as_reference() + && expected_mutability == hir::Mutability::Mut + && actual_with_ref_removed.could_coerce_to(ctx.db(), &expected_with_ref_removed) + { + // The actual type is `&T`, and the expected type is `&mut T`, (or `U` that `T` can be coerced to). + // It's likely that, instead of adding a reference, we should just change the mutability of + // the existing one. + + let expr = expr_ptr.to_node(ctx.db()); + // If the node comes from a macro expansion, then we shouldn't assist, + // as the suggestion would overwrite the macro _definition_ position + let expr = ctx.sema.original_ast_node(expr)?; + let expr_without_ref = RefExpr::cast(expr.syntax().clone())?.expr()?; + + let file_id = expr_ptr.file_id.original_file(ctx.db()); + let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.db())); + let editor = builder.make_editor(expr.syntax()); + let make = editor.make(); + let new_expr = make.expr_ref(expr_without_ref, true); + builder.replace_ast(expr, new_expr); + let source_change = builder.finish(); + acc.push(fix( + "make_reference_mutable", + "Make reference mutable", + source_change, + range.range, + )); + return Some(()); + } + + let actual_with_ref = d.actual.add_reference(ctx.db(), expected_mutability); if !actual_with_ref.could_coerce_to(ctx.db(), &d.expected) { return None; } - let ampersands = format!("&{}", mutability.as_keyword_for_ref()); + let ampersands = format!("&{}", expected_mutability.as_keyword_for_ref()); let edit = TextEdit::insert(range.range.start(), ampersands); let source_change = SourceChange::from_text_edit(range.file_id, edit); @@ -390,6 +421,96 @@ fn test(_arg: &mut i32) {} ); } + #[test] + fn fix_reference_to_int() { + check_fix( + r#" +fn main() { + test($0&123); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test(&mut 123); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn add_reference_to_parenthesized_int() { + check_fix( + r#" +fn main() { + test(($0123)); +} +fn test(_arg: &i32) {} + "#, + r#" +fn main() { + test((&123)); +} +fn test(_arg: &i32) {} + "#, + ); + } + + #[test] + fn add_mutable_reference_to_parenthesized_int() { + check_fix( + r#" +fn main() { + test(($0123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test((&mut 123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn fix_reference_to_parenthesized_int_paren_inside_ref() { + check_fix( + r#" +fn main() { + test(&$0(123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test(&mut (123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + + #[test] + fn fix_reference_to_parenthesized_int_ref_inside_paren() { + check_fix( + r#" +fn main() { + test(($0&123)); +} +fn test(_arg: &mut i32) {} + "#, + r#" +fn main() { + test((&mut 123)); +} +fn test(_arg: &mut i32) {} + "#, + ); + } + #[test] fn add_reference_to_array() { check_fix( @@ -409,6 +530,19 @@ fn test(_arg: &[i32]) {} ); } + #[test] + fn fix_reference_to_array() { + check_no_fix( + r#" +//- minicore: coerce_unsized +fn main() { + test($0&[1, 2, 3]); +} +fn test(_arg: &mut [i32]) {} + "#, + ); + } + #[test] fn add_reference_with_autoderef() { check_fix( @@ -442,6 +576,49 @@ fn test(_arg: &Bar) {} ); } + #[test] + // FIXME: this should suggest making the reference mutable instead: `&Foo -> &mut Foo`. + // Currently it doesn't, as the logic for that assist strips away references, and thus checks + // whether `Foo` can be coerced to `Bar` (which it can't), instead of checking `&mut Foo` to + // `&mut Bar` (which it can) + fn fix_reference_with_autoderef() { + check_fix( + r#" +//- minicore: coerce_unsized, deref_mut +struct Foo; +struct Bar; +impl core::ops::Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { loop {} } +} + +fn main() { + test($0&Foo); +} +fn test(_arg: &mut Bar) {} + "#, + r#" +struct Foo; +struct Bar; +impl core::ops::Deref for Foo { + type Target = Bar; + fn deref(&self) -> &Self::Target { loop {} } +} +impl core::ops::DerefMut for Foo { + fn deref_mut(&mut self) -> &mut Self::Target { loop {} } +} + +fn main() { + test(&mut &Foo); +} +fn test(_arg: &mut Bar) {} + "#, + ); + } + #[test] fn add_reference_to_method_call() { check_fix( @@ -498,6 +675,22 @@ fn main() { ); } + #[test] + fn fix_reference_to_let_stmt() { + check_fix( + r#" +fn main() { + let _test: &mut i32 = $0&123; +} + "#, + r#" +fn main() { + let _test: &mut i32 = &mut 123; +} + "#, + ); + } + #[test] fn add_reference_to_macro_call() { check_fix( @@ -526,6 +719,56 @@ fn main() { ); } + #[test] + fn fix_reference_to_macro_call() { + check_fix( + r#" +macro_rules! thousand { + () => { + 1000_u64 + }; +} + +fn test(_foo: &mut u64) {} +fn main() { + test($0&thousand!()); +} + "#, + r#" +macro_rules! thousand { + () => { + 1000_u64 + }; +} + +fn test(_foo: &mut u64) {} +fn main() { + test(&mut thousand!()); +} + "#, + ); + } + + #[test] + // If the immutable reference comes from a macro expansion, + // we can't do anything to change it to a mutable one. + fn dont_fix_reference_inside_macro_call() { + check_no_fix( + r#" +macro_rules! thousand { + () => { + &1000_u64 + }; +} + +fn test(_foo: &mut u64) {} +fn main() { + test($0thousand!()); +} + "#, + ); + } + #[test] fn const_generic_type_mismatch() { check_diagnostics( From 268293bb13a3144ad755ec951ebfa3cfad3de8a8 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Mon, 11 May 2026 20:27:57 +0200 Subject: [PATCH 088/114] switch to text-based edit --- .../ide-diagnostics/src/handlers/type_mismatch.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) 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 afd6130bb3e9f..f3cf823efe681 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 @@ -103,13 +103,9 @@ fn add_or_fix_reference( let expr = ctx.sema.original_ast_node(expr)?; let expr_without_ref = RefExpr::cast(expr.syntax().clone())?.expr()?; - let file_id = expr_ptr.file_id.original_file(ctx.db()); - let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.db())); - let editor = builder.make_editor(expr.syntax()); - let make = editor.make(); - let new_expr = make.expr_ref(expr_without_ref, true); - builder.replace_ast(expr, new_expr); - let source_change = builder.finish(); + let pos = expr_without_ref.syntax().text_range().start(); + let edit = TextEdit::insert(pos, expected_mutability.as_keyword_for_ref().to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id, edit); acc.push(fix( "make_reference_mutable", "Make reference mutable", From 1f0c9a055d56dd64130b5c08202454ac5b688690 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 27 May 2026 06:34:26 +0200 Subject: [PATCH 089/114] internal: Improve syntax mapping in for loop desugaring --- .../crates/hir-def/src/expr_store/lower.rs | 39 +++++++++++-------- .../crates/hir-ty/src/tests/macros.rs | 8 ++-- .../crates/hir-ty/src/tests/never_type.rs | 12 +++--- .../crates/hir-ty/src/tests/patterns.rs | 4 +- .../crates/hir-ty/src/tests/regression.rs | 8 ++-- .../src/handlers/unimplemented_trait.rs | 16 ++++++++ .../crates/test-utils/src/lib.rs | 2 +- 7 files changed, 56 insertions(+), 33 deletions(-) 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 ef88188572c8d..21b5b199867f0 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 @@ -2159,16 +2159,21 @@ impl<'db> ExprCollector<'db> { ) else { return self.missing_expr(); }; - let head = self.collect_expr_opt(e.iterable()); - let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr); - let iterator = self.alloc_expr( + let iterable = e.iterable(); + let syntax_ptr_iterable = iterable.as_ref().map_or(syntax_ptr, AstPtr::new); + let loop_body = e.loop_body().map(|it| it.into()); + let head = self.collect_expr_opt(iterable); + let into_iter_fn_expr = + self.alloc_expr_desugared_with_ptr(Expr::Path(into_iter_fn), syntax_ptr_iterable); + let iterator = self.alloc_expr_desugared_with_ptr( Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) }, - syntax_ptr, + syntax_ptr_iterable, ); let none_arm = MatchArm { pat: self.alloc_pat_desugared(Pat::Path(option_none)), guard: None, - expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr), + expr: self + .alloc_expr_desugared_with_ptr(Expr::Break { expr: None, label: None }, syntax_ptr), }; let some_pat = Pat::TupleStruct { path: option_some, @@ -2181,26 +2186,26 @@ impl<'db> ExprCollector<'db> { let some_arm = MatchArm { pat: self.alloc_pat_desugared(some_pat), guard: None, - expr: self.with_opt_labeled_rib(label, |this| { - this.collect_expr_opt(e.loop_body().map(|it| it.into())) - }), + expr: self.with_opt_labeled_rib(label, |this| this.collect_expr_opt(loop_body)), }; let iter_name = self.generate_new_name(); - let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); - let iter_expr_mut = self.alloc_expr( + let iter_expr = self + .alloc_expr_desugared_with_ptr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); + let iter_expr_mut = self.alloc_expr_desugared_with_ptr( Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, syntax_ptr, ); - let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr); - let iter_next_expr = self.alloc_expr( + let iter_next_fn_expr = + self.alloc_expr_desugared_with_ptr(Expr::Path(iter_next_fn), syntax_ptr); + let iter_next_expr = self.alloc_expr_desugared_with_ptr( Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) }, syntax_ptr, ); - let loop_inner = self.alloc_expr( + let loop_inner = self.alloc_expr_desugared_with_ptr( Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, syntax_ptr, ); - let loop_inner = self.alloc_expr( + let loop_inner = self.alloc_expr_desugared_with_ptr( Expr::Block { id: None, statements: Box::default(), @@ -2209,8 +2214,10 @@ impl<'db> ExprCollector<'db> { }, syntax_ptr, ); - let loop_outer = self - .alloc_expr(Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, syntax_ptr); + let loop_outer = self.alloc_expr_desugared_with_ptr( + Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, + syntax_ptr, + ); let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable, HygieneId::ROOT); let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); 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 28a688d4a39ba..f81028c174c43 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 @@ -196,8 +196,6 @@ fn expr_macro_def_expanded_in_various_places() { !0..6 '1isize': isize 39..442 '{ ...!(); }': {unknown} 73..94 'spam!(...am!())': {unknown} - 100..119 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 100..119 'for _ ...!() {}': ::IntoIter 100..119 'for _ ...!() {}': ! 100..119 'for _ ...!() {}': {unknown} 100..119 'for _ ...!() {}': &'? mut {unknown} @@ -208,6 +206,8 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 104..105 '_': {unknown} + 109..116 'spam!()': fn into_iter(isize) -> ::IntoIter + 109..116 'spam!()': ::IntoIter 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize 140..156 'while ...!() {}': ! @@ -290,8 +290,6 @@ fn expr_macro_rules_expanded_in_various_places() { !0..6 '1isize': isize 53..456 '{ ...!(); }': {unknown} 87..108 'spam!(...am!())': {unknown} - 114..133 'for _ ...!() {}': fn into_iter(isize) -> ::IntoIter - 114..133 'for _ ...!() {}': ::IntoIter 114..133 'for _ ...!() {}': ! 114..133 'for _ ...!() {}': {unknown} 114..133 'for _ ...!() {}': &'? mut {unknown} @@ -302,6 +300,8 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 118..119 '_': {unknown} + 123..130 'spam!()': fn into_iter(isize) -> ::IntoIter + 123..130 'spam!()': ::IntoIter 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize 154..170 'while ...!() {}': ! diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 91273cd177e8f..19a6a6b486cfe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -361,8 +361,6 @@ fn diverging_expression_3_break() { 97..343 '{ ...; }; }': () 140..141 'x': u32 149..175 '{ for ...; }; }': u32 - 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 151..172 'for a ...eak; }': <{unknown} as IntoIterator>::IntoIter 151..172 'for a ...eak; }': ! 151..172 'for a ...eak; }': {unknown} 151..172 'for a ...eak; }': &'? mut {unknown} @@ -374,12 +372,12 @@ fn diverging_expression_3_break() { 151..172 'for a ...eak; }': () 155..156 'a': {unknown} 160..161 'b': {unknown} + 160..161 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 160..161 'b': <{unknown} as IntoIterator>::IntoIter 162..172 '{ break; }': () 164..169 'break': ! 226..227 'x': u32 235..253 '{ for ... {}; }': u32 - 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 237..250 'for a in b {}': <{unknown} as IntoIterator>::IntoIter 237..250 'for a in b {}': ! 237..250 'for a in b {}': {unknown} 237..250 'for a in b {}': &'? mut {unknown} @@ -391,11 +389,11 @@ fn diverging_expression_3_break() { 237..250 'for a in b {}': () 241..242 'a': {unknown} 246..247 'b': {unknown} + 246..247 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 246..247 'b': <{unknown} as IntoIterator>::IntoIter 248..250 '{}': () 304..305 'x': u32 313..340 '{ for ...; }; }': u32 - 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 315..337 'for a ...urn; }': <{unknown} as IntoIterator>::IntoIter 315..337 'for a ...urn; }': ! 315..337 'for a ...urn; }': {unknown} 315..337 'for a ...urn; }': &'? mut {unknown} @@ -407,6 +405,8 @@ fn diverging_expression_3_break() { 315..337 'for a ...urn; }': () 319..320 'a': {unknown} 324..325 'b': {unknown} + 324..325 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 324..325 'b': <{unknown} as IntoIterator>::IntoIter 326..337 '{ return; }': () 328..334 'return': ! 149..175: expected u32, got () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index e719f43e74b7b..c48112ee81cf6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -47,8 +47,6 @@ fn infer_pattern() { 82..94 '(1, "hello")': (i32, &'? str) 83..84 '1': i32 86..93 '"hello"': &'static str - 101..150 'for (e... }': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter - 101..150 'for (e... }': IntoIter<(i32, i32), 1> 101..150 'for (e... }': ! 101..150 'for (e... }': IntoIter<(i32, i32), 1> 101..150 'for (e... }': &'? mut IntoIter<(i32, i32), 1> @@ -62,6 +60,8 @@ fn infer_pattern() { 106..107 'e': i32 109..110 'f': i32 115..123 '[(0, 1)]': [(i32, i32); 1] + 115..123 '[(0, 1)]': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter + 115..123 '[(0, 1)]': IntoIter<(i32, i32), 1> 116..122 '(0, 1)': (i32, i32) 117..118 '0': i32 120..121 '1': i32 diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 5a90e700acac9..c1c714f8d49a6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -270,8 +270,6 @@ fn infer_std_crash_5() { "#, expect![[r#" 26..322 '{ ... } }': () - 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter - 32..320 'for co... }': <{unknown} as IntoIterator>::IntoIter 32..320 'for co... }': ! 32..320 'for co... }': {unknown} 32..320 'for co... }': &'? mut {unknown} @@ -283,6 +281,8 @@ fn infer_std_crash_5() { 32..320 'for co... }': () 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 47..60 'doesnt_matter': {unknown} + 47..60 'doesnt_matter': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter + 47..60 'doesnt_matter': <{unknown} as IntoIterator>::IntoIter 61..320 '{ ... }': () 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} @@ -1261,8 +1261,6 @@ fn test() { "#, expect![[r#" 10..68 '{ ... } }': () - 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter - 16..66 'for _ ... }': <() as IntoIterator>::IntoIter 16..66 'for _ ... }': ! 16..66 'for _ ... }': {unknown} 16..66 'for _ ... }': &'? mut {unknown} @@ -1274,6 +1272,8 @@ fn test() { 16..66 'for _ ... }': () 20..21 '_': {unknown} 25..39 '{ let x = 0; }': () + 25..39 '{ let x = 0; }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter + 25..39 '{ let x = 0; }': <() as IntoIterator>::IntoIter 31..32 'x': i32 35..36 '0': i32 40..66 '{ ... }': () diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs index d94ceef642f77..4a253bc8310c0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs @@ -69,4 +69,20 @@ fn foo() { "#, ); } + + #[test] + fn for_iterable() { + check_diagnostics( + r#" +//- minicore: iterator +fn foo() { + for _ in () {} + // ^^ error: the trait bound `(): Iterator` is not satisfied + // ^^ error: the trait bound `(): Iterator` is not satisfied + // | required by the bound `(): IntoIterator` +} + + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs index 62867fd5b55b6..4eab7f4b18424 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs @@ -260,7 +260,7 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { let &(_, idx) = prev_line_annotations .iter() .find(|&&(off, _idx)| off == offset) - .unwrap(); + .expect("annotation continuation not found"); res[idx].1.push('\n'); res[idx].1.push_str(&content); res[idx].1.push('\n'); From a4bff5f2858a06b8a74be88875fb37ffd5952ea1 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 28 May 2026 16:40:34 +0800 Subject: [PATCH 090/114] fix: no complete functional update in non last Example --- ```rust #[derive(Default)] struct Foo { foo1: u32, foo2: u32 } fn main() { let thing = 1; let foo = Foo { foo1: 0, foo2: 0 }; let foo2 = Foo { $0 thing } } ``` **Before this PR** ``` fd ..Default::default() fd foo1 u32 fd foo2 u32 ``` **After this PR** ``` fd foo1 u32 fd foo2 u32 ``` --- .../ide-completion/src/completions/record.rs | 7 ++- .../crates/ide-completion/src/tests/record.rs | 45 ++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs index 48a51690271b1..16e89bd0de5a7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs @@ -1,7 +1,8 @@ //! Complete fields in record literals and patterns. use ide_db::SymbolKind; use syntax::{ - SmolStr, + SmolStr, T, + algo::next_non_trivia_token, ast::{self, Expr}, }; @@ -97,7 +98,9 @@ pub(crate) fn add_default_update( let impls_default_trait = default_trait .zip(ty) .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); - if impls_default_trait { + let ends_of_record_list = + next_non_trivia_token(ctx.token.clone()).is_none_or(|it| it.kind() == T!['}']); + if impls_default_trait && ends_of_record_list { // FIXME: This should make use of scope_def like completions so we get all the other goodies // that is we should handle this like actually completing the default function let completion_text = "..Default::default()"; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs index ddb9294469007..aecf8eb651e1a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs @@ -157,6 +157,8 @@ fn foo(f: Struct) { fn in_functional_update() { cov_mark::check!(functional_update); + // FIXME: This should filter out all completions that do not have the type `Foo` + // I think maybe ranking by type match is enough check( r#" //- minicore:default @@ -210,7 +212,6 @@ fn main() { #[test] fn functional_update_no_dot() { cov_mark::check!(functional_update_field); - // FIXME: This should filter out all completions that do not have the type `Foo` check( r#" //- minicore:default @@ -280,6 +281,48 @@ fn main() { ); } +#[test] +fn functional_update_non_last() { + check( + r#" +//- minicore:default +struct Foo { foo1: u32, foo2: u32 } +impl Default for Foo { + fn default() -> Self { loop {} } +} + +fn main() { + let thing = 1; + let foo = Foo { foo1: 0, foo2: 0 }; + let foo2 = Foo { $0 thing } +} +"#, + expect![[r#" + fd foo1 u32 + fd foo2 u32 + "#]], + ); + check( + r#" +//- minicore:default +struct Foo { foo1: u32, foo2: u32 } +impl Default for Foo { + fn default() -> Self { loop {} } +} + +fn main() { + let thing = 1; + let foo = Foo { foo1: 0, foo2: 0 }; + let foo2 = Foo { $0thing } +} +"#, + expect![[r#" + fd foo1 u32 + fd foo2 u32 + "#]], + ); +} + #[test] fn functional_update_fields_completion() { // Complete fields before functional update `..` From 2e41d530223c20d198cd5aa74e17b845449d2095 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 28 May 2026 20:15:43 +0530 Subject: [PATCH 091/114] use editor make in derive_macro --- .../crates/hir-expand/src/builtin/derive_macro.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 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 e8321cd8da72c..c4da558fdab24 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 @@ -22,7 +22,7 @@ use crate::{ use syntax::{ ast::{ self, AstNode, FieldList, HasAttrs, HasGenericArgs, HasGenericParams, HasModuleItem, - HasName, HasTypeBounds, make, + HasName, HasTypeBounds, }, syntax_editor::{GetOrCreateWhereClause, SyntaxEditor}, }; @@ -1435,6 +1435,7 @@ fn coerce_pointee_expand( param_name: &str, replacement: &str, ) -> bool { + let make = editor.make(); return match ty { ast::Type::ArrayType(ty) => ty .ty() @@ -1462,8 +1463,8 @@ fn coerce_pointee_expand( if path.as_single_name_ref().is_some_and(|name| name.text() == param_name) { editor.replace( path.syntax(), - make::path_from_segments( - [make::path_segment(make::name_ref(replacement))], + make.path_from_segments( + [make.path_segment(make.name_ref(replacement))], false, ) .syntax(), From fa6c05d862e2911d4cfd63f8e17271572d3d5c5a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 28 May 2026 20:15:54 +0530 Subject: [PATCH 092/114] use editor make in edits --- .../crates/syntax/src/syntax_editor/edits.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index 0338d976b0d58..f2b979eb9d1ae 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -3,7 +3,7 @@ use crate::{ AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, algo::neighbor, - ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel, make}, + ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel}, syntax_editor::{Position, SyntaxEditor}, }; @@ -207,6 +207,7 @@ impl ast::AssocItemList { /// Attention! This function does align the first line of `item` with respect to `self`, /// but it does _not_ change indentation of other lines (if any). pub fn add_items(&self, editor: &SyntaxEditor, items: Vec) { + let make = editor.make(); let (indent, position, whitespace) = match self.assoc_items().last() { Some(last_item) => ( IndentLevel::from_node(last_item.syntax()), @@ -228,7 +229,7 @@ impl ast::AssocItemList { .flat_map(|(i, item)| { let whitespace = if i != 0 { "\n\n" } else { whitespace }; vec![ - make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), + make.whitespace(&format!("{whitespace}{indent}")).into(), item.syntax().clone().into(), ] }) @@ -390,10 +391,11 @@ impl ast::VariantList { impl ast::Fn { pub fn replace_or_insert_body(&self, editor: &SyntaxEditor, body: ast::BlockExpr) { + let make = editor.make(); if let Some(old_body) = self.body() { editor.replace(old_body.syntax(), body.syntax()); } else { - let single_space = make::tokens::single_space(); + let single_space = make.whitespace(" "); let elements = vec![single_space.into(), body.syntax().clone().into()]; if let Some(semicolon) = self.semicolon_token() { From cec1e064da7ac1b14a67c19862b1dc56a096d6aa Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 28 May 2026 23:23:15 +0300 Subject: [PATCH 093/114] Fix Clippy 1.96.0 --- src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs | 1 + .../src/handlers/extract_struct_from_enum_variant.rs | 4 ---- .../crates/ide-completion/src/completions/expr.rs | 7 +------ .../crates/ide-db/src/syntax_helpers/node_ext.rs | 7 +------ 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index c12fbeccdb19b..afbafb3fea7dc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -441,6 +441,7 @@ fn return_position_impl_trait() { // but rustc actually runs this code. let pinned = pin!(inp); struct EmptyWaker; + #[expect(clippy::manual_noop_waker, reason = "we don't have access to std here")] impl Wake for EmptyWaker { fn wake(self: Arc) { } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 50ce8d9b4d5ac..5e6e74bc94429 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -249,10 +249,6 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b } } param if matches!(token.kind(), T![ident]) => { - #[expect( - clippy::collapsible_match, - reason = "it won't compile since in the guard, `param` is immutable" - )] if match param { ast::GenericParam::ConstParam(konst) => konst .name() diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index 506662ca72376..8d906064c0244 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -320,12 +320,7 @@ pub(crate) fn complete_expr_path( } // synthetic names currently leak out as we lack synthetic hygiene, so filter them // out here - ScopeDef::Local(_) => - { - #[expect( - clippy::collapsible_match, - reason = "this changes meaning, causing the next arm to be selected" - )] + ScopeDef::Local(_) => { if !name.as_str().starts_with('<') { acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs index 11ba815dab2ec..e30b21c139fad 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -216,12 +216,7 @@ pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type) -> bool) { preorder.skip_subtree(); cb(ty); } - Some(ty) => - { - #[expect( - clippy::collapsible_match, - reason = "it won't compile due to exhaustiveness" - )] + Some(ty) => { if cb(ty) { preorder.skip_subtree(); } From e0cf072897ecbb2436c6d390d2843796bc43fd51 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 7 May 2026 18:09:37 +0300 Subject: [PATCH 094/114] Port block and loop inference from rustc This fixes one diagnostic on self. --- .../crates/hir-def/src/expr_store.rs | 2 +- .../crates/hir-def/src/expr_store/lower.rs | 19 ++- .../crates/hir-def/src/expr_store/pretty.rs | 2 +- .../crates/hir-def/src/expr_store/scope.rs | 2 +- .../rust-analyzer/crates/hir-def/src/hir.rs | 12 ++ .../crates/hir-ty/src/infer/expr.rs | 113 ++++++++++-------- .../crates/hir-ty/src/infer/mutability.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../crates/hir-ty/src/tests/macros.rs | 6 +- .../crates/hir-ty/src/tests/never_type.rs | 38 +++--- .../crates/hir-ty/src/tests/patterns.rs | 2 +- .../crates/hir-ty/src/tests/regression.rs | 36 +++--- .../hir-ty/src/tests/regression/new_solver.rs | 2 +- .../crates/hir-ty/src/tests/simple.rs | 56 ++++++--- .../crates/hir-ty/src/tests/traits.rs | 10 +- .../src/handlers/break_outside_of_loop.rs | 3 +- 16 files changed, 181 insertions(+), 126 deletions(-) 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 fa33a00a80e12..8768413ce554f 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 @@ -720,7 +720,7 @@ impl ExpressionStore { } visitor.on_expr_opt(*tail); } - Expr::Loop { body, label: _ } => visitor.on_expr(*body), + Expr::Loop { body, label: _, source: _ } => visitor.on_expr(*body), Expr::Call { callee, args } => { visitor.on_expr(*callee); visitor.on_exprs(args); 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 21b5b199867f0..242a0b0b4ff92 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 @@ -49,9 +49,9 @@ use crate::{ }, hir::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, - Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement, - generics::GenericParams, + CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, LoopSource, + MatchArm, Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, + Statement, generics::GenericParams, }, item_scope::BuiltinShadowMode, item_tree::FieldsShape, @@ -1351,7 +1351,7 @@ impl<'db> ExprCollector<'db> { (self.hygiene_id_for(label.syntax().text_range()), self.collect_label(label)) }); let body = self.collect_labelled_block_opt(label, e.loop_body()); - self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1) }, syntax_ptr) + self.alloc_expr(Expr::Loop { body, label: label.map(|it| it.1), source: LoopSource::Loop }, syntax_ptr) } ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e), ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e), @@ -2133,7 +2133,10 @@ impl<'db> ExprCollector<'db> { Expr::If { condition, then_branch: body, else_branch: Some(break_expr) }, syntax_ptr, ); - self.alloc_expr(Expr::Loop { body: if_expr, label: label.map(|it| it.1) }, syntax_ptr) + self.alloc_expr( + Expr::Loop { body: if_expr, label: label.map(|it| it.1), source: LoopSource::While }, + syntax_ptr, + ) } /// Desugar `ast::ForExpr` from: `[opt_ident]: for in ` into: @@ -2215,7 +2218,11 @@ impl<'db> ExprCollector<'db> { syntax_ptr, ); let loop_outer = self.alloc_expr_desugared_with_ptr( - Expr::Loop { body: loop_inner, label: label.map(|it| it.1) }, + Expr::Loop { + body: loop_inner, + label: label.map(|it| it.1), + source: LoopSource::ForLoop, + }, syntax_ptr, ); let iter_binding = diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 35e3fc44c3836..293adfc9bd45e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -567,7 +567,7 @@ impl Printer<'_> { w!(self, " = "); self.print_expr_in(prec, *expr); } - Expr::Loop { body, label } => { + Expr::Loop { body, label, source: _ } => { if let Some(lbl) = label { w!(self, "{}: ", self.store[*lbl].name.display(self.db, self.edition)); } 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 ddb828513775c..7a2c8dc3ffc7f 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 @@ -350,7 +350,7 @@ fn compute_expr_scopes( Expr::Unsafe { id, statements, tail } => { handle_block(*id, statements, *tail, None, scopes, scope, const_scope); } - Expr::Loop { body: body_expr, label } => { + Expr::Loop { body: body_expr, label, source: _ } => { let mut scope = scopes.new_labeled_scope(*scope, make_label(*label)); compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 4c8d835ad7fc1..2f7724c72e229 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -224,6 +224,7 @@ pub enum Expr { Loop { body: ExprId, label: Option, + source: LoopSource, }, Call { callee: ExprId, @@ -389,6 +390,17 @@ impl Expr { } } +/// The loop type that yielded an `Expr::Loop`. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LoopSource { + /// A `loop { .. }` loop. + Loop, + /// A `while _ { .. }` loop. + While, + /// A `for _ in _ { .. }` loop. + ForLoop, +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct OffsetOf { pub container: TypeRefId, 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 c7562567efec4..75e64403341c8 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 @@ -8,7 +8,8 @@ use hir_def::{ expr_store::path::{GenericArgs as HirGenericArgs, Path}, hir::{ Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId, - InlineAsmKind, LabelId, Pat, PatId, RecordLitField, RecordSpread, Statement, UnaryOp, + InlineAsmKind, LabelId, LoopSource, Pat, PatId, RecordLitField, RecordSpread, Statement, + UnaryOp, }, resolver::ValueNs, signatures::VariantFields, @@ -401,24 +402,29 @@ impl<'db> InferenceContext<'_, 'db> { }) .1 } - &Expr::Loop { body, label } => { - let ty = expected.coercion_target_type(&mut self.table, tgt_expr.into()); + &Expr::Loop { body, label, source } => { + let coerce = match source { + // you can only use break with a value from a normal `loop { }` + LoopSource::Loop => { + Some(expected.coercion_target_type(&mut self.table, body.into())) + } + LoopSource::While | LoopSource::ForLoop => None, + }; let (breaks, ()) = - self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { - this.infer_expr( + self.with_breakable_ctx(BreakableKind::Loop, coerce, label, |this| { + this.infer_expr_suptype_coerce_never( body, &Expectation::HasType(this.types.types.unit), ExprIsRead::Yes, ); }); - match breaks { - Some(breaks) => { - self.diverges = Diverges::Maybe; - breaks - } - None => self.types.types.never, + if breaks.may_break { + self.diverges = Diverges::Maybe; + } else { + self.diverges = Diverges::Always; } + breaks.coerce.map(|c| c.complete(self)).unwrap_or(self.types.types.unit) } Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => self .infer_closure( @@ -1499,10 +1505,11 @@ impl<'db> InferenceContext<'_, 'db> { label: Option, expected: &Expectation<'db>, ) -> Ty<'db> { + let prev_diverges = self.diverges; let coerce_ty = expected.coercion_target_type(&mut self.table, expr.into()); let g = self.resolver.update_to_inner_scope(self.db, self.store_owner, expr); - let (break_ty, ty) = + let (ctxt, tail_expr_ty) = self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty), label, |this| { for stmt in statements { match stmt { @@ -1570,42 +1577,54 @@ impl<'db> InferenceContext<'_, 'db> { } } - // FIXME: This should make use of the breakable CoerceMany - if let Some(expr) = tail { - this.infer_expr_coerce(expr, expected, ExprIsRead::Yes) - } else { - // Citing rustc: if there is no explicit tail expression, - // that is typically equivalent to a tail expression - // of `()` -- except if the block diverges. In that - // case, there is no value supplied from the tail - // expression (assuming there are no other breaks, - // this implies that the type of the block will be - // `!`). - if this.diverges.is_always() { - // we don't even make an attempt at coercion - this.table.new_maybe_never_var(expr.into()) - } else if let Some(t) = expected.only_has_type(&mut this.table) { - if this - .coerce( - expr, - this.types.types.unit, - t, - AllowTwoPhase::No, - ExprIsRead::Yes, - ) - .is_err() - { - this.emit_type_mismatch(expr.into(), t, this.types.types.unit); - } - t - } else { - this.types.types.unit - } - } + // check the tail expression **without** holding the + // `enclosing_breakables` lock below. + tail.map(|expr| (expr, this.infer_expr_inner(expr, expected, ExprIsRead::Yes))) }); + + let mut coerce = ctxt.coerce.unwrap(); + if let Some((tail_expr, tail_expr_ty)) = tail_expr_ty { + let cause = ObligationCause::new(tail_expr); + coerce.coerce_inner( + self, + &cause, + tail_expr, + tail_expr_ty, + false, + false, + ExprIsRead::Yes, + ); + } else { + // Subtle: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + // + // #41425 -- label the implicit `()` as being the + // "found type" here, rather than the "expected type". + if !self.diverges.is_always() { + coerce.coerce_forced_unit( + self, + expr, + &ObligationCause::new(expr), + false, + ExprIsRead::Yes, + ); + } + } + + if ctxt.may_break { + // If we can break from the block, then the block's exit is always reachable + // (... as long as the entry is reachable) - regardless of the tail of the block. + self.diverges = prev_diverges; + } + self.resolver.reset_to_guard(g); - break_ty.unwrap_or(ty) + coerce.complete(self) } fn lookup_field( @@ -2178,13 +2197,13 @@ impl<'db> InferenceContext<'_, 'db> { ty: Option>, label: Option, cb: impl FnOnce(&mut Self) -> T, - ) -> (Option>, T) { + ) -> (BreakableContext<'db>, T) { self.breakables.push({ BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label } }); let res = cb(self); let ctx = self.breakables.pop().expect("breakable stack broken"); - (if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res) + (ctx, res) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index c3b532638f9d3..483f54a2270a2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -160,7 +160,7 @@ impl<'db> InferenceContext<'_, 'db> { | Expr::Range { rhs: Some(expr), lhs: None, range_type: _ } | Expr::Await { expr } | Expr::Box { expr } - | Expr::Loop { body: expr, label: _ } + | Expr::Loop { body: expr, label: _, source: _ } | Expr::Cast { expr, type_ref: _ } => { self.infer_mut_expr(*expr, Mutability::Not); } 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 68612c2ce2fec..394cac8065d7d 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 @@ -669,7 +669,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) } } - Expr::Loop { body, label } => { + Expr::Loop { body, label, source: _ } => { self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { let scope = this.push_drop_scope(); if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { 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 f81028c174c43..c0da6cfd307df 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 @@ -194,7 +194,7 @@ fn expr_macro_def_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 39..442 '{ ...!(); }': {unknown} + 39..442 '{ ...!(); }': ! 73..94 'spam!(...am!())': {unknown} 100..119 'for _ ...!() {}': ! 100..119 'for _ ...!() {}': {unknown} @@ -288,7 +288,7 @@ fn expr_macro_rules_expanded_in_various_places() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 53..456 '{ ...!(); }': {unknown} + 53..456 '{ ...!(); }': ! 87..108 'spam!(...am!())': {unknown} 114..133 'for _ ...!() {}': ! 114..133 'for _ ...!() {}': {unknown} @@ -1493,7 +1493,7 @@ fn main() { !0..136 'builti...tack))': () !0..449 'builti...urn),)': ! 10..1236 '{ ... } }': () - 16..1234 'unsafe... }': () + 16..1234 'unsafe... }': ! 37..40 'foo': i32 43..44 '1': i32 58..63 'mut o': i32 diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 19a6a6b486cfe..fd21286d50835 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -262,42 +262,42 @@ fn diverging_expression_1() { let x: u32 = { let y: u32 = { loop {}; }; }; } ", - expect![[r" - 11..39 '{ ...urn; }': () + expect![[r#" + 11..39 '{ ...urn; }': ! 21..22 'x': u32 30..36 'return': ! - 51..84 '{ ...; }; }': () + 51..84 '{ ...; }; }': ! 61..62 'x': u32 - 70..81 '{ return; }': u32 + 70..81 '{ return; }': ! 72..78 'return': ! - 96..125 '{ ... {}; }': () + 96..125 '{ ... {}; }': ! 106..107 'x': u32 115..122 'loop {}': ! 120..122 '{}': () - 137..170 '{ ...} }; }': () + 137..170 '{ ...} }; }': ! 147..148 'x': u32 156..167 '{ loop {} }': u32 158..165 'loop {}': ! 163..165 '{}': () - 182..246 '{ ...} }; }': () + 182..246 '{ ...} }; }': ! 192..193 'x': u32 201..243 '{ if t...}; } }': u32 203..241 'if tru... {}; }': u32 206..210 'true': bool - 211..223 '{ loop {}; }': u32 + 211..223 '{ loop {}; }': ! 213..220 'loop {}': ! 218..220 '{}': () - 229..241 '{ loop {}; }': u32 + 229..241 '{ loop {}; }': ! 231..238 'loop {}': ! 236..238 '{}': () - 258..310 '{ ...; }; }': () + 258..310 '{ ...; }; }': ! 268..269 'x': u32 - 277..307 '{ let ...; }; }': u32 + 277..307 '{ let ...; }; }': ! 283..284 'y': u32 - 292..304 '{ loop {}; }': u32 + 292..304 '{ loop {}; }': ! 294..301 'loop {}': ! 299..301 '{}': () - "]], + "#]], ); } @@ -312,7 +312,7 @@ fn diverging_expression_2() { } "#, expect![[r#" - 11..84 '{ ..." }; }': () + 11..84 '{ ..." }; }': ! 54..55 'x': u32 63..81 '{ loop...foo" }': u32 65..72 'loop {}': ! @@ -355,7 +355,7 @@ fn diverging_expression_3_break() { 54..55 'x': u32 63..82 '{ loop...k; } }': u32 65..80 'loop { break; }': u32 - 70..80 '{ break; }': () + 70..80 '{ break; }': ! 72..77 'break': ! 72..77: expected u32, got () 97..343 '{ ...; }; }': () @@ -374,7 +374,7 @@ fn diverging_expression_3_break() { 160..161 'b': {unknown} 160..161 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter 160..161 'b': <{unknown} as IntoIterator>::IntoIter - 162..172 '{ break; }': () + 162..172 '{ break; }': ! 164..169 'break': ! 226..227 'x': u32 235..253 '{ for ... {}; }': u32 @@ -407,7 +407,7 @@ fn diverging_expression_3_break() { 324..325 'b': {unknown} 324..325 'b': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter 324..325 'b': <{unknown} as IntoIterator>::IntoIter - 326..337 '{ return; }': () + 326..337 '{ return; }': ! 328..334 'return': ! 149..175: expected u32, got () 235..253: expected u32, got () @@ -419,7 +419,7 @@ fn diverging_expression_3_break() { 409..430 'while ...eak; }': () 409..430 'while ...eak; }': () 415..419 'true': bool - 420..430 '{ break; }': () + 420..430 '{ break; }': ! 422..427 'break': ! 537..538 'x': u32 546..564 '{ whil... {}; }': u32 @@ -434,7 +434,7 @@ fn diverging_expression_3_break() { 626..648 'while ...urn; }': () 626..648 'while ...urn; }': () 632..636 'true': bool - 637..648 '{ return; }': () + 637..648 '{ return; }': ! 639..645 'return': ! 407..433: expected u32, got () 546..564: expected u32, got () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index c48112ee81cf6..a6e864916f40f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -606,7 +606,7 @@ fn enum_variant_through_self_in_pattern() { } "#, expect![[r#" - 75..217 '{ ... }': () + 75..217 '{ ... }': ! 85..210 'match ... }': () 92..99 'loop {}': ! 97..99 '{}': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index c1c714f8d49a6..22404087bffdc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -279,29 +279,29 @@ fn infer_std_crash_5() { 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () - 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 36..43 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 47..60 'doesnt_matter': {unknown} 47..60 'doesnt_matter': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter 47..60 'doesnt_matter': <{unknown} as IntoIterator>::IntoIter 61..320 '{ ... }': () - 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 75..79 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 82..166 'if doe... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 85..98 'doesnt_matter': bool 99..128 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 113..118 'first': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 134..166 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 148..156 '&content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 191..313 'if ICE... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 134..166 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 148..156 '&content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 149..156 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 181..188 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 191..313 'if ICE... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} 194..231 'ICE_RE..._VALUE': {unknown} 194..247 'ICE_RE...&name)': bool - 241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 248..276 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 262..266 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 282..313 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} - 296..303 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 241..246 '&name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 242..246 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 248..276 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 262..266 'name': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 282..313 '{ ... }': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} + 296..303 'content': &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? &'? {unknown} "#]], ); } @@ -416,7 +416,7 @@ fn issue_2669() { 120..215 '{ ... }': () 130..133 'end': fn end<{unknown}>() 130..135 'end()': () - 164..209 '{ ... }': () + 164..209 '{ ... }': ! 182..184 '_x': ! 191..198 'loop {}': ! 196..198 '{}': () @@ -631,7 +631,7 @@ fn issue_4053_diesel_where_clauses() { 65..69 'self': Self 267..271 'self': Self 466..470 'self': SelectStatement - 488..522 '{ ... }': {unknown} + 488..522 '{ ... }': () 498..502 'self': SelectStatement 498..508 'self.order': O 498..515 'self.o...into()': dyn QueryFragment + 'static @@ -1059,7 +1059,7 @@ fn cfg_tail() { 216..227 '{ "third" }': () 218..225 '"third"': &'static str 293..357 '{ ...] 15 }': () - 299..311 '{ "fourth" }': &'static str + 299..311 '{ "fourth" }': &'? str 301..309 '"fourth"': &'static str "#]], ) @@ -2238,7 +2238,7 @@ type Bar = impl Foo; async fn f() -> Bar {} "#, expect![[r#" - 64..66 '{}': impl Foo + ?Sized + 64..66 '{}': () "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index 33a12fcd1ee1e..c77b20f4b54c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -357,7 +357,7 @@ where "#, expect![[r#" 182..183 't': T - 230..280 '{ ... {}; }': () + 230..280 '{ ... {}; }': ! 240..241 't': >::Output 270..277 'loop {}': ! 275..277 '{}': () 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 c0b8d93b47e32..b54ed080315e0 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 @@ -135,7 +135,7 @@ fn test(a: u32, b: isize, c: !, d: &str) { 16..17 'b': isize 26..27 'c': ! 32..33 'd': &'? str - 41..120 '{ ...f32; }': () + 41..120 '{ ...f32; }': ! 47..48 'a': u32 54..55 'b': isize 61..62 'c': ! @@ -1018,14 +1018,14 @@ fn foo() { 28..32 'true': bool 33..50 '{ ... }': i32 43..44 '1': i32 - 56..79 '{ ... }': i32 + 56..79 '{ ... }': ! 66..72 'return': ! 89..92 '_x2': i32 95..148 'if tru... }': i32 98..102 'true': bool 103..120 '{ ... }': i32 113..114 '2': i32 - 126..148 '{ ... }': ! + 126..148 '{ ... }': i32 136..142 'return': ! 158..161 '_x3': i32 164..246 'match ... }': i32 @@ -1034,7 +1034,7 @@ fn foo() { 185..189 'true': bool 193..194 '3': i32 204..205 '_': bool - 209..240 '{ ... }': i32 + 209..240 '{ ... }': ! 223..229 'return': ! 256..259 '_x4': i32 262..319 'match ... }': i32 @@ -1939,7 +1939,7 @@ fn closure_return() { 16..58 '{ ...; }; }': u32 26..27 'x': impl Fn() -> usize 30..55 '|| -> ...n 1; }': impl Fn() -> usize - 42..55 '{ return 1; }': usize + 42..55 '{ return 1; }': ! 44..52 'return 1': ! 51..52 '1': usize "#]], @@ -1958,7 +1958,7 @@ fn closure_return_unit() { 16..47 '{ ...; }; }': u32 26..27 'x': impl Fn() 30..44 '|| { return; }': impl Fn() - 33..44 '{ return; }': () + 33..44 '{ return; }': ! 35..41 'return': ! "#]], ); @@ -2434,10 +2434,10 @@ fn infer_loop_break_with_val() { 59..168 '{ ... }; }': () 69..70 'x': Option 73..165 'loop {... }': Option - 78..165 '{ ... }': () + 78..165 '{ ... }': ! 88..132 'if fal... }': () 91..96 'false': bool - 97..132 '{ ... }': () + 97..132 '{ ... }': ! 111..121 'break None': ! 117..121 'None': Option 142..158 'break ...(true)': ! @@ -2470,7 +2470,7 @@ fn infer_loop_break_without_val() { 78..133 '{ ... }': () 88..127 'if fal... }': () 91..96 'false': bool - 97..127 '{ ... }': () + 97..127 '{ ... }': ! 111..116 'break': ! "#]], ); @@ -2500,24 +2500,24 @@ fn infer_labelled_break_with_val() { 19..21 '_x': impl Fn() -> bool 24..332 '|| 'ou... }': impl Fn() -> bool 27..332 ''outer... }': bool - 40..332 '{ ... }': () + 40..332 '{ ... }': ! 54..59 'inner': i8 62..300 ''inner... }': i8 - 75..300 '{ ... }': () + 75..300 '{ ... }': ! 93..94 'i': bool 97..113 'Defaul...efault': {unknown} 97..115 'Defaul...ault()': bool 129..269 'if (br... }': () 133..147 'break 'outer i': ! 146..147 'i': bool - 149..208 '{ ... }': () + 149..208 '{ ... }': ! 167..193 'loop {...5i8; }': ! - 172..193 '{ brea...5i8; }': () + 172..193 '{ brea...5i8; }': ! 174..190 'break ...er 5i8': ! 187..190 '5i8': i8 214..269 'if tru... }': () 217..221 'true': bool - 222..269 '{ ... }': () + 222..269 '{ ... }': ! 240..254 'break 'inner 6': ! 253..254 '6': i8 282..289 'break 7': ! @@ -2566,12 +2566,12 @@ fn foo() { 140..270 'if (br... }': () 144..158 'break 'outer i': ! 157..158 'i': bool - 160..209 '{ ... }': () + 160..209 '{ ... }': ! 178..194 'break ...er 5i8': ! 191..194 '5i8': i8 215..270 'if tru... }': () 218..222 'true': bool - 223..270 '{ ... }': () + 223..270 '{ ... }': ! 241..255 'break 'inner 6': ! 254..255 '6': i8 283..313 'break ... { 0 }': ! @@ -2666,7 +2666,7 @@ fn generic_default_in_struct_literal() { } "#, expect![[r#" - 99..319 '{ ...32); }': () + 99..319 '{ ...32); }': ! 109..110 'x': Thing 113..133 'Thing ...p {} }': Thing 124..131 'loop {}': ! @@ -3254,9 +3254,9 @@ fn main() { expect![[r#" 104..108 'self': &'? Box 188..192 'self': &'a Box> - 218..220 '{}': &'a T + 218..220 '{}': &'? T 242..246 'self': &'a Box> - 275..277 '{}': &'a Foo + 275..277 '{}': &'? Foo 297..301 'self': Box> 322..324 '{}': Foo 338..559 '{ ...r(); }': () @@ -4305,3 +4305,21 @@ enum Enum { "#]], ); } + +#[test] +fn labelled_block_break() { + check_types( + r#" +//- minicore: option +fn foo() { + 'a: { + if false { + break 'a Some(1); + } + None + // ^^^^ Option + }; +} + "#, + ); +} 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 4eab1d6314280..85c93abcf9349 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 @@ -1490,7 +1490,7 @@ fn test(x: Box>, y: &dyn Trait) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 206..208 '{}': Box + 'static> + 206..208 '{}': Box + '?> 218..219 'x': Box + 'static> 242..243 'y': &'? (dyn Trait + 'static) 262..379 '{ ...2(); }': () @@ -1571,7 +1571,7 @@ fn test(x: Trait, y: &Trait) -> u64 { }"#, expect![[r#" 26..30 'self': &'? Self - 60..62 '{}': dyn Trait + 'static + 60..62 '{}': dyn Trait + '? 72..73 'x': dyn Trait + 'static 82..83 'y': &'? (dyn Trait + 'static) 100..175 '{ ...o(); }': u64 @@ -1712,7 +1712,7 @@ fn test>(x: T, y: impl Trait) { }"#, expect![[r#" 81..82 't': T - 109..111 '{}': ::Type + 109..111 '{}': () 143..144 't': T 154..156 '{}': U 186..187 't': T @@ -5018,7 +5018,7 @@ where "#, expect![[r#" 84..86 'de': D - 135..138 '{ }': >::Error + 135..138 '{ }': () "#]], ); } @@ -5083,7 +5083,7 @@ fn main() { let _ = iter.into_iter(); }"#, expect![[r#" - 10..313 '{ ...r(); }': () + 10..313 '{ ...r(); }': ! 223..227 'iter': Box + 'static> 273..280 'loop {}': ! 278..280 '{}': () 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 b7265c47b6fb2..43eb4ab8e03a2 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 @@ -126,14 +126,13 @@ fn foo() { #[test] fn value_break_in_for_loop() { - // FIXME: the error is correct, but the message is terrible check_diagnostics( r#" //- minicore: iterator fn test() { for _ in [()] { break 3; - // ^ error: expected (), found i32 + // ^^^^^^^ error: can't break with a value in this position } } "#, From fa6ab771ca045ebb29a4e6b9fed67f1169dd8025 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 27 May 2026 22:27:36 +0300 Subject: [PATCH 095/114] Improve detection of diverging expressions I.e., consider a block diverging if its tail expr is diverging, even if it doesn't have type never. This became important now that blocks coerce the tail expression's type to the expected type instead of coercing themselves, but in fact the logic was always incorrect - a statement *in the middle of* a block can be diverging without the block having never type. This situation is still not handled correctly, which is why I also left a comment for maybe having a side map for this. --- .../rust-analyzer/crates/hir/src/semantics.rs | 4 +++ .../crates/hir/src/source_analyzer.rs | 29 +++++++++++++++++++ .../src/handlers/convert_match_to_let_else.rs | 2 +- .../crates/ide-assists/src/utils.rs | 10 +------ 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 0d6f9accf89c1..f633bb063fdd2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1772,6 +1772,10 @@ impl<'db> SemanticsImpl<'db> { }) } + pub fn expr_is_diverging(&self, expr: &ast::Expr) -> bool { + (|| self.analyze(expr.syntax())?.expr_is_diverging(self.db, expr))().unwrap_or(false) + } + pub fn type_of_expr(&self, expr: &ast::Expr) -> Option> { self.analyze(expr.syntax())? .type_of_expr(self.db, expr) 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 32e6acb6c58da..1f9520d780f03 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -446,6 +446,35 @@ impl<'db> SourceAnalyzer<'db> { Some(self.ty(ty)) } + pub(crate) fn expr_is_diverging( + &self, + _db: &'db dyn HirDatabase, + expr: &ast::Expr, + ) -> Option { + let expr_id = self.expr_id(expr.clone())?; + let store = self.store()?; + let infer = self.infer()?; + Some(self.expr_id_is_diverging(store, infer, expr_id)) + } + + fn expr_id_is_diverging( + &self, + store: &ExpressionStore, + infer: &InferenceResult, + expr_id: ExprOrPatId, + ) -> bool { + // FIXME: This is an approximation, perhaps we need to store a set of diverging exprs in inference? + if infer.type_of_expr_or_pat(expr_id).is_some_and(|ty| ty.is_never()) { + true + } else if let ExprOrPatId::ExprId(expr_id) = expr_id + && let Expr::Block { tail: Some(tail), .. } = store[expr_id] + { + self.expr_id_is_diverging(store, infer, tail.into()) + } else { + false + } + } + pub(crate) fn type_of_expr( &self, _db: &'db dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index a93ab138e5f0e..9dffdf3f367c4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -93,7 +93,7 @@ fn find_arms( let mut extracting = None; let mut diverging = None; for arm in arms { - if ctx.sema.type_of_expr(&arm.expr()?)?.original().is_never() { + if ctx.sema.expr_is_diverging(&arm.expr()?) { diverging = Some(arm); } else { extracting = Some(arm); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 33e0f476da5bf..1b6c9a579aba0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -1182,13 +1182,5 @@ pub(crate) fn is_never_block( sema: &Semantics<'_, RootDatabase>, block_expr: &ast::BlockExpr, ) -> bool { - if let Some(tail_expr) = block_expr.tail_expr() { - sema.type_of_expr(&tail_expr).is_some_and(|ty| ty.original.is_never()) - } else if let Some(ast::Stmt::ExprStmt(expr_stmt)) = block_expr.statements().last() - && let Some(expr) = expr_stmt.expr() - { - sema.type_of_expr(&expr).is_some_and(|ty| ty.original.is_never()) - } else { - false - } + sema.expr_is_diverging(&block_expr.clone().into()) } From 95c5de078a2ca9c8718a93637832ebde2074727b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 29 May 2026 07:55:47 +0530 Subject: [PATCH 096/114] Add lifetime_param to syntaxFactory --- .../syntax/src/ast/syntax_factory/constructors.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 1070af65e7598..2f7eab2423ce5 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -479,6 +479,18 @@ impl SyntaxFactory { ast } + pub fn lifetime_param(&self, lifetime: ast::Lifetime) -> ast::LifetimeParam { + let ast = make::lifetime_param(lifetime.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(lifetime.syntax().clone(), ast.lifetime().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn generic_param_list( &self, params: impl IntoIterator, From d7dc411c8b00bcbc2017ea64e0d27c167930e468 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 29 May 2026 07:56:17 +0530 Subject: [PATCH 097/114] remove make constructor from rename --- src/tools/rust-analyzer/crates/ide/src/rename.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index e220cbdce8d32..26c6776107846 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -16,7 +16,7 @@ use std::fmt::Write; use stdx::{always, format_to, never}; use syntax::{ AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, - ast::{self, HasArgList, make, prec::ExprPrecedence}, + ast::{self, HasArgList, prec::ExprPrecedence}, }; use ide_db::text_edit::TextEdit; @@ -818,11 +818,12 @@ fn rename_elided_lifetime( let mut builder = SourceChangeBuilder::new(position.file_id); let editor = builder.make_editor(&root); + let make = editor.make(); - editor.replace(lifetime_token, make::lifetime(new_name).syntax().clone()); + editor.replace(lifetime_token, make.lifetime(new_name).syntax().clone()); if let Some(has_generic_params) = parent.ancestors().find_map(ast::AnyHasGenericParams::cast) { - let lifetime_param = make::lifetime_param(make::lifetime(new_name)); + let lifetime_param = make.lifetime_param(make.lifetime(new_name)); editor.add_generic_param(&has_generic_params, lifetime_param.into()); } From e452c62c984ae6d0a0422911e7b159623a692a6b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 29 May 2026 07:58:32 +0530 Subject: [PATCH 098/114] remove make from prettify_macro_expansion and add factory to dollar_crate_replacement closure --- .../src/prettify_macro_expansion_.rs | 18 ++++++------------ .../rust-analyzer/crates/mbe/src/tests.rs | 2 +- .../src/prettify_macro_expansion.rs | 7 ++++--- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs index 79e6f0f5b7aa4..687bd1a71400f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -2,8 +2,7 @@ use base_db::Crate; use rustc_hash::FxHashMap; -use syntax::NodeOrToken; -use syntax::{SyntaxNode, ast::make}; +use syntax::SyntaxNode; use crate::{db::ExpandDatabase, span_map::ExpansionSpanMap}; @@ -22,7 +21,7 @@ pub fn prettify_macro_expansion( let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default(); syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( syn, - &mut |dollar_crate| { + &mut |dollar_crate, make| { let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx; let replacement = syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| { @@ -38,13 +37,13 @@ pub fn prettify_macro_expansion( // is inserted, and also understandable to the user. // Lastly, if nothing else found, resort to leaving `$crate`. if target_crate_id == macro_def_crate { - make::tokens::crate_kw() + make.token(syntax::SyntaxKind::CRATE_KW) } else if let Some(dep) = target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate) { - make::tokens::ident(dep.name.as_str()) + make.ident(dep.name.as_str()) } else if let Some(crate_name) = ¯o_def_crate.extra_data(db).display_name { - make::tokens::ident(crate_name.crate_name().as_str()) + make.ident(crate_name.crate_name().as_str()) } else { dollar_crate.clone() } @@ -53,12 +52,7 @@ pub fn prettify_macro_expansion( // The parent may have many children, and looking for the token may yield incorrect results. return None; } - // We need to `clone_subtree()` but rowan doesn't provide such operation for tokens. - let parent = replacement.parent().unwrap().clone_subtree().clone_for_update(); - parent - .children_with_tokens() - .filter_map(NodeOrToken::into_token) - .find(|it| it.kind() == replacement.kind()) + Some(replacement.clone()) }, |_| (), ) diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs index 271dfc877b475..f3ebb663d92a9 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/tests.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -74,7 +74,7 @@ fn check_( "{}", syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( node.syntax_node(), - &mut |_| None, + &mut |_, _| None, |_| () ) ); diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index 648119ed7013c..001c920c9b824 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -3,6 +3,7 @@ use syntax::{ NodeOrToken, SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, T, WalkEvent, + ast::syntax_factory::SyntaxFactory, syntax_editor::{Position, SyntaxEditor}, }; @@ -21,7 +22,7 @@ pub enum PrettifyWsKind { #[deprecated = "use `hir_expand::prettify_macro_expansion()` instead"] pub fn prettify_macro_expansion( syn: SyntaxNode, - dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken) -> Option, + dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken, &SyntaxFactory) -> Option, inspect_mods: impl FnOnce(&[(Position, PrettifyWsKind)]), ) -> SyntaxNode { let mut indent = 0; @@ -66,7 +67,7 @@ pub fn prettify_macro_expansion( }; if token.kind() == SyntaxKind::IDENT && token.text() == "$crate" - && let Some(replacement) = dollar_crate_replacement(&token) + && let Some(replacement) = dollar_crate_replacement(&token, editor.make()) { dollar_crate_replacements.push((token.clone(), replacement)); } @@ -191,7 +192,7 @@ mod tests { let source_file = syntax::ast::SourceFile::parse(&ra_fixture, span::Edition::CURRENT); let syn = remove_whitespaces(&source_file.syntax_node()); - let pretty = prettify_macro_expansion(syn, &mut |_| None, |_| ()); + let pretty = prettify_macro_expansion(syn, &mut |_, _| None, |_| ()); let mut pretty = pretty.to_string(); if pretty.contains('\n') { pretty.push('\n'); From e0c85c27c692344c8e284fbda83793ac4b829745 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Tue, 2 Dec 2025 11:32:53 +0100 Subject: [PATCH 099/114] Intern MIR projections using the new ty interning infrastructure This allows creating projections from other queries, paving the way for further MIR analyses. conflict fixes some cleanups clean up into_place --- .../rust-analyzer/crates/hir-ty/src/mir.rs | 190 +++++++++--------- .../crates/hir-ty/src/mir/borrowck.rs | 20 +- .../crates/hir-ty/src/mir/eval.rs | 38 ++-- .../crates/hir-ty/src/mir/lower.rs | 182 +++++++++-------- .../crates/hir-ty/src/mir/lower/as_place.rs | 64 +++--- .../hir-ty/src/mir/lower/pattern_matching.rs | 125 ++++++------ .../crates/hir-ty/src/mir/pretty.rs | 2 +- .../crates/hir-ty/src/next_solver.rs | 8 + .../crates/hir-ty/src/next_solver/interner.rs | 2 + .../ide/src/inlay_hints/implicit_drop.rs | 2 +- 10 files changed, 330 insertions(+), 303 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 5f61b1defb8c0..e82038907c449 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -1,6 +1,6 @@ //! MIR definitions and implementation -use std::{collections::hash_map::Entry, fmt::Display, iter}; +use std::{fmt::Display, iter}; use base_db::Crate; use either::Either; @@ -8,10 +8,14 @@ use hir_def::{ FieldId, StaticId, TupleFieldId, UnionId, VariantId, hir::{BindingId, Expr, ExprId, Ordering, PatId}, }; +use intern::{InternedSlice, InternedSliceRef, impl_slice_internable}; use la_arena::{Arena, ArenaMap, Idx, RawIdx}; use rustc_ast_ir::Mutability; use rustc_hash::FxHashMap; -use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, Ty as _}; +use rustc_type_ir::{ + CollectAndApply, GenericTypeVisitable, + inherent::{GenericArgs as _, IntoKind, Ty as _}, +}; use smallvec::{SmallVec, smallvec}; use stdx::{impl_from, never}; @@ -24,6 +28,7 @@ use crate::{ next_solver::{ Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind, + impl_stored_interned_slice, infer::{InferCtxt, traits::ObligationCause}, obligation_ctxt::ObligationCtxt, }, @@ -145,7 +150,7 @@ impl<'db> Operand { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ProjectionElem { Deref, Field(Either), @@ -155,7 +160,7 @@ pub enum ProjectionElem { ConstantIndex { offset: u64, from_end: bool }, Subslice { from: u64, to: u64 }, //Downcast(Option, VariantIdx), - OpaqueCast(StoredTy), + OpaqueCast(std::convert::Infallible), } impl ProjectionElem { @@ -260,97 +265,114 @@ impl ProjectionElem { type PlaceElem = ProjectionElem; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ProjectionId(u32); +impl GenericTypeVisitable for PlaceElem { + fn generic_visit_with(&self, _: &mut W) {} +} + +impl_slice_internable!(gc; ProjectionStorage, (), PlaceElem); +impl_stored_interned_slice!(ProjectionStorage, Projection, StoredProjection); -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ProjectionStore { - id_to_proj: FxHashMap>, - proj_to_id: FxHashMap, ProjectionId>, +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct Projection<'db> { + interned: InternedSliceRef<'db, ProjectionStorage>, } -impl Default for ProjectionStore { - fn default() -> Self { - let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() }; - // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty` - this.intern(Box::new([])); - this +impl<'db> std::fmt::Debug for Projection<'db> { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (*self).as_slice().fmt(fmt) } } -impl ProjectionStore { - pub fn shrink_to_fit(&mut self) { - self.id_to_proj.shrink_to_fit(); - self.proj_to_id.shrink_to_fit(); +impl<'db> Projection<'db> { + pub fn new_from_iter(args: I) -> T::Output + where + I: IntoIterator, + T: CollectAndApply, + { + CollectAndApply::collect_and_apply(args.into_iter(), Self::new_from_slice) } - pub fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option { - self.proj_to_id.get(projection).copied() + #[inline] + pub fn new_from_slice(slice: &[PlaceElem]) -> Self { + Self { interned: InternedSlice::from_header_and_slice((), slice) } } - pub fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { - let new_id = ProjectionId(self.proj_to_id.len() as u32); - match self.proj_to_id.entry(projection) { - Entry::Occupied(id) => *id.get(), - Entry::Vacant(e) => { - let key_clone = e.key().clone(); - e.insert(new_id); - self.id_to_proj.insert(new_id, key_clone); - new_id - } - } + #[inline] + pub fn as_slice(self) -> &'db [PlaceElem] { + &self.interned.get().slice + } + + pub fn project(self, projection: PlaceElem) -> Projection<'db> { + Projection::new_from_iter(self.as_slice().iter().copied().chain([projection])) } } -impl ProjectionId { - pub const EMPTY: ProjectionId = ProjectionId(0); +impl<'db> std::ops::Deref for Projection<'db> { + type Target = [PlaceElem]; - pub fn is_empty(self) -> bool { - self == ProjectionId::EMPTY + fn deref(&self) -> &Self::Target { + self.as_slice() } +} - pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { - store.id_to_proj.get(&self).unwrap() +impl StoredProjection { + // FIXME: rename to as_slice + pub fn lookup(&self) -> &[PlaceElem] { + self.as_ref().as_slice() } - pub fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { - let mut current = self.lookup(store).to_vec(); - current.push(projection); - store.intern(current.into()) + pub fn is_empty(&self) -> bool { + self.lookup().is_empty() } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +// FIXME: would be nicer to rename PlaceRef -> Place, Place -> StoredPlace, but I didn't want to blow up the diff +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct PlaceRef<'db> { + pub local: LocalId, + pub projection: Projection<'db>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, - pub projection: ProjectionId, + pub projection: StoredProjection, } impl Place { - fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool { + pub fn as_ref<'db>(&self) -> PlaceRef<'db> { + PlaceRef { local: self.local, projection: self.projection.as_ref() } + } +} + +impl<'db> PlaceRef<'db> { + fn is_parent(&self, child: PlaceRef<'db>) -> bool { self.local == child.local - && child.projection.lookup(store).starts_with(self.projection.lookup(store)) + && child.projection.as_slice().starts_with(self.projection.as_slice()) } /// The place itself is not included - fn iterate_over_parents<'a>( - &'a self, - store: &'a ProjectionStore, - ) -> impl Iterator + 'a { - let projection = self.projection.lookup(store); - (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| { - Some(Place { local: self.local, projection: store.intern_if_exist(x)? }) + fn iterate_over_parents<'a>(&'a self) -> impl Iterator> + 'a { + let projection = self.projection.as_slice(); + (0..projection.len()).map(move |x| PlaceRef { + local: self.local, + projection: Projection::new_from_slice(&projection[0..x]), }) } - fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place { - Place { local: self.local, projection: self.projection.project(projection, store) } + fn project(&self, projection: PlaceElem) -> PlaceRef<'db> { + PlaceRef { local: self.local, projection: self.projection.project(projection) } + } + + pub fn store(&self) -> Place { + Place { local: self.local, projection: self.projection.store() } } } -impl From for Place { +impl<'db> From for PlaceRef<'db> { fn from(local: LocalId) -> Self { - Self { local, projection: ProjectionId::EMPTY } + let empty: &[PlaceElem] = &[]; + PlaceRef { local, projection: Projection::new_from_slice(empty) } } } @@ -1081,7 +1103,6 @@ pub struct BasicBlock { #[derive(Debug, Clone, PartialEq, Eq)] pub struct MirBody { - pub projection_store: ProjectionStore, pub basic_blocks: Arena, pub locals: Arena, pub start_block: BasicBlockId, @@ -1099,15 +1120,11 @@ impl MirBody { self.binding_locals.iter().map(|(it, y)| (*y, it)).collect() } - fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) { - fn for_operand( - op: &mut Operand, - f: &mut impl FnMut(&mut Place, &mut ProjectionStore), - store: &mut ProjectionStore, - ) { + fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) { + fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) { match &mut op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - f(p, store); + f(p); } OperandKind::Constant { .. } | OperandKind::Static(_) @@ -1118,25 +1135,25 @@ impl MirBody { for statement in &mut block.statements { match &mut statement.kind { StatementKind::Assign(p, r) => { - f(p, &mut self.projection_store); + f(p); match r { Rvalue::ShallowInitBoxWithAlloc(_) => (), Rvalue::ShallowInitBox(o, _) | Rvalue::UnaryOp(_, o) | Rvalue::Cast(_, o, _) | Rvalue::Repeat(o, _) - | Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store), + | Rvalue::Use(o) => for_operand(o, &mut f), Rvalue::CopyForDeref(p) | Rvalue::Discriminant(p) | Rvalue::Len(p) - | Rvalue::Ref(_, p) => f(p, &mut self.projection_store), + | Rvalue::Ref(_, p) => f(p), Rvalue::CheckedBinaryOp(_, o1, o2) => { - for_operand(o1, &mut f, &mut self.projection_store); - for_operand(o2, &mut f, &mut self.projection_store); + for_operand(o1, &mut f); + for_operand(o2, &mut f); } Rvalue::Aggregate(_, ops) => { for op in ops.iter_mut() { - for_operand(op, &mut f, &mut self.projection_store); + for_operand(op, &mut f); } } Rvalue::ThreadLocalRef(n) @@ -1145,9 +1162,7 @@ impl MirBody { | Rvalue::NullaryOp(n) => match *n {}, } } - StatementKind::FakeRead(p) | StatementKind::Deinit(p) => { - f(p, &mut self.projection_store) - } + StatementKind::FakeRead(p) | StatementKind::Deinit(p) => f(p), StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), @@ -1155,9 +1170,7 @@ impl MirBody { } match &mut block.terminator { Some(x) => match &mut x.kind { - TerminatorKind::SwitchInt { discr, .. } => { - for_operand(discr, &mut f, &mut self.projection_store) - } + TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f), TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } @@ -1167,24 +1180,23 @@ impl MirBody { | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Drop { place, .. } => { - f(place, &mut self.projection_store); + f(place); } TerminatorKind::DropAndReplace { place, value, .. } => { - f(place, &mut self.projection_store); - for_operand(value, &mut f, &mut self.projection_store); + f(place); + for_operand(value, &mut f); } TerminatorKind::Call { func, args, destination, .. } => { - for_operand(func, &mut f, &mut self.projection_store); - args.iter_mut() - .for_each(|x| for_operand(x, &mut f, &mut self.projection_store)); - f(destination, &mut self.projection_store); + for_operand(func, &mut f); + args.iter_mut().for_each(|x| for_operand(x, &mut f)); + f(destination); } TerminatorKind::Assert { cond, .. } => { - for_operand(cond, &mut f, &mut self.projection_store); + for_operand(cond, &mut f); } TerminatorKind::Yield { value, resume_arg, .. } => { - for_operand(value, &mut f, &mut self.projection_store); - f(resume_arg, &mut self.projection_store); + for_operand(value, &mut f); + f(resume_arg); } }, None => (), @@ -1202,9 +1214,7 @@ impl MirBody { upvar_locals, param_locals, closures, - projection_store, } = self; - projection_store.shrink_to_fit(); basic_blocks.shrink_to_fit(); locals.shrink_to_fit(); binding_locals.shrink_to_fit(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index bc158ac884e9d..ff963fc121205 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -195,11 +195,11 @@ fn moved_out_of_ref<'db>( ) -> Vec { let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref(); let mut is_dereference_of_ref = false; - for proj in p.projection.lookup(&body.projection_store) { + for proj in p.projection.lookup() { if *proj == ProjectionElem::Deref && ty.as_reference().is_some() { is_dereference_of_ref = true; } @@ -290,10 +290,10 @@ fn partially_moved<'db>( ) -> Vec { let db = infcx.interner.db; let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match op.kind { + let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref(); - for proj in p.projection.lookup(&body.projection_store) { + for proj in p.projection.lookup() { ty = proj.projected_ty(infcx, env, ty, body.owner.module(db).krate(db)); } if !infcx.type_is_copy_modulo_regions(env, ty) && !ty.references_non_lt_error() { @@ -430,7 +430,7 @@ fn place_case<'db>( let db = infcx.interner.db; let mut is_part_of = false; let mut ty = body.locals[lvalue.local].ty.as_ref(); - for proj in lvalue.projection.lookup(&body.projection_store).iter() { + for proj in lvalue.projection.lookup().iter() { match proj { ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw ProjectionElem::Deref // It's direct in case of `Box` @@ -470,7 +470,7 @@ fn ever_initialized_map( for statement in &block.statements { match &statement.kind { StatementKind::Assign(p, _) => { - if p.projection.lookup(&body.projection_store).is_empty() && p.local == l { + if p.projection.is_empty() && p.local == l { is_ever_initialized = true; } } @@ -508,9 +508,7 @@ fn ever_initialized_map( | TerminatorKind::Return | TerminatorKind::Unreachable => (), TerminatorKind::Call { target, cleanup, destination, .. } => { - if destination.projection.lookup(&body.projection_store).is_empty() - && destination.local == l - { + if destination.projection.is_empty() && destination.local == l { is_ever_initialized = true; } target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized)); @@ -566,7 +564,7 @@ fn record_usage(local: LocalId, result: &mut ArenaMap } fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap) { - if let OperandKind::Copy(p) | OperandKind::Move(p) = arg.kind { + if let OperandKind::Copy(p) | OperandKind::Move(p) = &arg.kind { record_usage(p.local, result); } } @@ -674,7 +672,7 @@ fn mutability_of_locals<'db>( for arg in args.iter() { record_usage_for_operand(arg, &mut result); } - if destination.projection.lookup(&body.projection_store).is_empty() { + if destination.projection.is_empty() { if ever_init_map.get(destination.local).copied().unwrap_or_default() { push_mut_span(destination.local, terminator.span, &mut result); } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 1ee18e09ba899..78b70edeeef78 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -56,7 +56,7 @@ use crate::{ use super::{ AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, - Operand, OperandKind, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind, + Operand, OperandKind, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, return_slot, }; @@ -567,26 +567,26 @@ impl std::fmt::Debug for MirEvalError { type Result<'db, T> = std::result::Result; #[derive(Debug, Default)] -struct DropFlags { - need_drop: FxHashSet, +struct DropFlags<'db> { + need_drop: FxHashSet>, } -impl DropFlags { - fn add_place(&mut self, p: Place, store: &ProjectionStore) { - if p.iterate_over_parents(store).any(|it| self.need_drop.contains(&it)) { +impl<'db> DropFlags<'db> { + fn add_place(&mut self, p: PlaceRef<'db>) { + if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) { return; } - self.need_drop.retain(|it| !p.is_parent(it, store)); + self.need_drop.retain(|it| !p.is_parent(*it)); self.need_drop.insert(p); } - fn remove_place(&mut self, p: &Place, store: &ProjectionStore) -> bool { + fn remove_place(&mut self, p: PlaceRef<'db>) -> bool { // FIXME: replace parents with parts - if let Some(parent) = p.iterate_over_parents(store).find(|it| self.need_drop.contains(it)) { + if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(it)) { self.need_drop.remove(&parent); return true; } - self.need_drop.remove(p) + self.need_drop.remove(&p) } fn clear(&mut self) { @@ -598,7 +598,7 @@ impl DropFlags { struct Locals<'a> { ptr: ArenaMap, body: &'a MirBody, - drop_flags: DropFlags, + drop_flags: DropFlags<'a>, } pub struct MirOutput { @@ -757,9 +757,9 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let mut addr = locals.ptr[p.local].addr; let mut ty: Ty<'db> = locals.body.locals[p.local].ty.as_ref(); let mut metadata: Option = None; // locals are always sized - for proj in p.projection.lookup(&locals.body.projection_store) { + for proj in p.projection.lookup() { let prev_ty = ty; - ty = self.projected_ty(ty, proj.clone()); + ty = self.projected_ty(ty, *proj); match proj { ProjectionElem::Deref => { metadata = if self.size_align_of(ty, locals)?.is_none() { @@ -955,7 +955,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { let addr = self.place_addr(l, locals)?; let result = self.eval_rvalue(r, locals)?; self.copy_from_interval_or_owned(addr, result)?; - locals.drop_flags.add_place(*l, &locals.body.projection_store); + locals.drop_flags.add_place(l.as_ref()); } StatementKind::Deinit(_) => not_supported!("de-init statement"), StatementKind::StorageLive(_) @@ -1008,9 +1008,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { )?, it => not_supported!("unknown function type {it:?}"), }; - locals - .drop_flags - .add_place(*destination, &locals.body.projection_store); + locals.drop_flags.add_place(destination.as_ref()); if let Some(stack_frame) = stack_frame { self.code_stack.push(my_stack_frame); current_block_idx = stack_frame.locals.body.start_block; @@ -1091,7 +1089,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { ) -> Result<'db, ()> { let mut remain_args = body.param_locals.len(); for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) { - locals.drop_flags.add_place(l.into(), &locals.body.projection_store); + locals.drop_flags.add_place(l.into()); match value { IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?, IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?, @@ -1915,7 +1913,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { fn eval_operand(&mut self, it: &Operand, locals: &mut Locals<'a>) -> Result<'db, Interval> { Ok(match &it.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { - locals.drop_flags.remove_place(p, &locals.body.projection_store); + locals.drop_flags.remove_place(p.as_ref()); self.eval_place(p, locals)? } OperandKind::Static(st) => { @@ -3036,7 +3034,7 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> { span: MirSpan, ) -> Result<'db, ()> { let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; - if !locals.drop_flags.remove_place(place, &locals.body.projection_store) { + if !locals.drop_flags.remove_place(place.as_ref()) { return Ok(()); } let metadata = match metadata { 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 394cac8065d7d..4e52c1f7c305a 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 @@ -45,9 +45,9 @@ use crate::{ mir::{ AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Either, Expr, FieldId, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan, - Mutability, Operand, Place, PlaceElem, PointerCast, ProjectionElem, ProjectionStore, - RawIdx, Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, - TupleFieldId, Ty, UnOp, VariantId, return_slot, + Mutability, Operand, Place, PlaceElem, PointerCast, Projection, ProjectionElem, RawIdx, + Rvalue, Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, TupleFieldId, + Ty, UnOp, VariantId, return_slot, }, next_solver::{ Const, DbInterner, ParamConst, ParamEnv, Region, StoredGenericArgs, StoredTy, TyKind, @@ -57,7 +57,7 @@ use crate::{ }, }; -use super::OperandKind; +use super::{OperandKind, PlaceRef}; mod as_place; mod pattern_matching; @@ -303,7 +303,6 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let locals = Arena::new(); let binding_locals: ArenaMap = ArenaMap::new(); let mir = MirBody { - projection_store: ProjectionStore::default(), basic_blocks, locals, start_block, @@ -371,13 +370,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let Some((p, current)) = self.lower_expr_as_place(current, expr_id, true)? else { return Ok(None); }; - Ok(Some((Operand { kind: OperandKind::Copy(p), span: Some(expr_id.into()) }, current))) + Ok(Some(( + Operand { kind: OperandKind::Copy(p.store()), span: Some(expr_id.into()) }, + current, + ))) } fn lower_expr_to_place_with_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, adjustments: &[Adjustment], ) -> Result<'db, Option> { @@ -396,7 +398,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(p), span: None }.into(), + Operand { kind: OperandKind::Copy(p.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -422,7 +424,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { place, Rvalue::Cast( CastKind::PointerCoercion(*cast), - Operand { kind: OperandKind::Copy(p), span: None }, + Operand { kind: OperandKind::Copy(p.store()), span: None }, last.target.clone(), ), expr_id.into(), @@ -437,7 +439,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_with_borrow_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, rest: &[Adjustment], m: Mutability, @@ -448,14 +450,14 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { return Ok(None); }; let bk = BorrowKind::from_rustc_mutability(m); - self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into()); Ok(Some(current)) } fn lower_expr_to_place( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, prev_block: BasicBlockId, ) -> Result<'db, Option> { if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) { @@ -467,7 +469,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_without_adjust( &mut self, expr_id: ExprId, - place: Place, + place: PlaceRef<'db>, mut current: BasicBlockId, ) -> Result<'db, Option> { match &self.store[expr_id] { @@ -535,7 +537,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(temp), span: None }.into(), + Operand { kind: OperandKind::Copy(temp.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -831,7 +833,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { .as_ref() .ok_or(MirLowerError::BreakWithoutLoop)?, }; - let Some(c) = self.lower_expr_to_place(expr, loop_data.place, current)? else { + let Some(c) = + self.lower_expr_to_place(expr, loop_data.place.as_ref(), current)? + else { return Ok(None); }; current = c; @@ -921,16 +925,18 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { .map(|(i, it)| match it { Some(it) => it, None => { - let p = sp.project( - ProjectionElem::Field(Either::Left(FieldId { + let p = sp.project(ProjectionElem::Field( + Either::Left(FieldId { parent: variant_id, local_id: LocalFieldId::from_raw(RawIdx::from( i as u32, )), - })), - &mut self.result.projection_store, - ); - Operand { kind: OperandKind::Copy(p), span: None } + }), + )); + Operand { + kind: OperandKind::Copy(p.store()), + span: None, + } } }) .collect(), @@ -948,13 +954,10 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let local_id = variant_fields.field(name).ok_or(MirLowerError::UnresolvedField)?; - let place = place.project( - PlaceElem::Field(Either::Left(FieldId { - parent: union_id.into(), - local_id, - })), - &mut self.result.projection_store, - ); + let place = place.project(PlaceElem::Field(Either::Left(FieldId { + parent: union_id.into(), + local_id, + }))); self.lower_expr_to_place(*expr, place, current) } } @@ -1001,7 +1004,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { return Ok(None); }; let bk = BorrowKind::from_hir_mutability(*mutability); - self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into()); + self.push_assignment(current, place, Rvalue::Ref(bk, p.store()), expr_id.into()); Ok(Some(current)) } Expr::Box { expr } => { @@ -1016,7 +1019,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { else { return Ok(None); }; - let p = place.project(ProjectionElem::Deref, &mut self.result.projection_store); + let p = place.project(ProjectionElem::Deref); self.push_assignment(current, p, operand.into(), expr_id.into()); Ok(Some(current)) } @@ -1031,7 +1034,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.push_assignment( current, place, - Operand { kind: OperandKind::Copy(p), span: None }.into(), + Operand { kind: OperandKind::Copy(p.store()), span: None }.into(), expr_id.into(), ); Ok(Some(current)) @@ -1124,7 +1127,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; let r_value = Rvalue::CheckedBinaryOp( op.into(), - Operand { kind: OperandKind::Copy(lhs_place), span: None }, + Operand { kind: OperandKind::Copy(lhs_place.store()), span: None }, rhs_op, ); self.push_assignment(current, lhs_place, r_value, expr_id.into()); @@ -1273,16 +1276,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { }; Ok(Place { local: this.binding_local(local)?, - projection: this - .result - .projection_store - .intern(convert_closure_capture_projections(self.db, place).collect()), + projection: Projection::new_from_iter(convert_closure_capture_projections( + self.db, place, + )) + .store(), }) }; for (place, _, sources) in &closure_data.fake_reads { let p = convert_place(self, place)?; - self.push_fake_read(current, p, span(sources)); + self.push_fake_read(current, p.as_ref(), span(sources)); } let captures = closure_data.min_captures.values().flatten(); @@ -1294,14 +1297,15 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let tmp_ty = capture.captured_ty(self.db); // FIXME: Handle more than one span. let capture_span = span(&capture.info.sources); - let tmp: Place = self.temp(tmp_ty, current, capture_span)?.into(); + let tmp = self.temp(tmp_ty, current, capture_span)?.into(); self.push_assignment( current, tmp, Rvalue::Ref(BorrowKind::from_hir(bk), p), capture_span, ); - operands.push(Operand { kind: OperandKind::Move(tmp), span: None }); + operands + .push(Operand { kind: OperandKind::Move(tmp.store()), span: None }); } UpvarCapture::ByValue => { operands.push(Operand { kind: OperandKind::Move(p), span: None }) @@ -1396,24 +1400,24 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } } - fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<'db, ()> { + fn push_field_projection( + &mut self, + place: &mut PlaceRef<'db>, + expr_id: ExprId, + ) -> Result<'db, ()> { if let Expr::Field { expr, name } = &self.store[expr_id] { if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind() { let index = name.as_tuple_index().ok_or(MirLowerError::TypeError("named field on tuple"))? as u32; - *place = place.project( - ProjectionElem::Field(Either::Right(TupleFieldId { - tuple: TupleId(!0), // dummy as its unused - index, - })), - &mut self.result.projection_store, - ) + *place = place.project(ProjectionElem::Field(Either::Right(TupleFieldId { + tuple: TupleId(!0), // dummy as its unused + index, + }))) } else { let field = self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?; - *place = - place.project(ProjectionElem::Field(field), &mut self.result.projection_store); + *place = place.project(ProjectionElem::Field(field)); } } else { not_supported!("") @@ -1528,7 +1532,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, const_id: GeneralConstId, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, subst: GenericArgs<'db>, span: MirSpan, ) -> Result<'db, ()> { @@ -1561,7 +1565,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn write_bytes_to_place( &mut self, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, cv: Box<[u8]>, ty: Ty<'db>, span: MirSpan, @@ -1574,7 +1578,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, variant_id: EnumVariantId, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, ty: Ty<'db>, fields: Box<[Operand]>, span: MirSpan, @@ -1596,7 +1600,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, func: Operand, args: impl Iterator, - place: Place, + place: PlaceRef<'db>, mut current: BasicBlockId, is_uninhabited: bool, span: MirSpan, @@ -1621,7 +1625,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, func: Operand, args: Box<[Operand]>, - place: Place, + place: PlaceRef<'db>, current: BasicBlockId, is_uninhabited: bool, span: MirSpan, @@ -1632,7 +1636,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { TerminatorKind::Call { func, args, - destination: place, + destination: place.store(), target: b, cleanup: None, from_hir_call: true, @@ -1672,31 +1676,31 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.result.basic_blocks[block].statements.push(statement); } - fn push_fake_read(&mut self, block: BasicBlockId, p: Place, span: MirSpan) { - self.push_statement(block, StatementKind::FakeRead(p).with_span(span)); + fn push_fake_read(&mut self, block: BasicBlockId, p: PlaceRef<'db>, span: MirSpan) { + self.push_statement(block, StatementKind::FakeRead(p.store()).with_span(span)); } fn push_assignment( &mut self, block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, rvalue: Rvalue, span: MirSpan, ) { - self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); + self.push_statement(block, StatementKind::Assign(place.store(), rvalue).with_span(span)); } - fn discr_temp_place(&mut self, current: BasicBlockId) -> Place { + fn discr_temp_place(&mut self, current: BasicBlockId) -> PlaceRef<'db> { match &self.discr_temp { - Some(it) => *it, + Some(it) => it.as_ref(), None => { // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); - let tmp: Place = self + let tmp: PlaceRef<'_> = self .temp(discr_ty, current, MirSpan::Unknown) .expect("discr_ty is never unsized") .into(); - self.discr_temp = Some(tmp); + self.discr_temp = Some(tmp.store()); tmp } } @@ -1705,7 +1709,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_loop( &mut self, prev_block: BasicBlockId, - place: Place, + place: PlaceRef<'db>, label: Option, span: MirSpan, f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId) -> Result<'db, ()>, @@ -1714,7 +1718,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let prev = self.current_loop_blocks.replace(LoopBlocks { begin, end: None, - place, + place: place.store(), drop_scope_index: self.drop_scopes.len(), }); let prev_label = if let Some(label) = label { @@ -1816,7 +1820,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { statements: &[hir_def::hir::Statement], mut current: BasicBlockId, tail: Option, - place: Place, + place: PlaceRef<'db>, span: MirSpan, ) -> Result<'db, Option>> { let scope = self.push_drop_scope(); @@ -2068,7 +2072,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let prev = std::mem::replace(current, self.new_basic_block()); self.set_terminator( prev, - TerminatorKind::Drop { place: l.into(), target: *current, unwind: None }, + TerminatorKind::Drop { + place: PlaceRef::from(l).store(), + target: *current, + unwind: None, + }, span, ); } @@ -2202,15 +2210,18 @@ pub fn mir_body_for_closure_query<'db>( projections.push(ProjectionElem::ClosureField(capture_idx)); let capture_param_place = Place { local: closure_local, - projection: ctx.result.projection_store.intern(projections.into_boxed_slice()), - }; - let capture_local_place = Place { - local: capture_local, - projection: ctx.result.projection_store.intern(Box::new([])), + projection: Projection::new_from_slice(&projections).store(), }; + let capture_local_place = + Place { local: capture_local, projection: Projection::new_from_slice(&[]).store() }; let capture_local_rvalue = Rvalue::Use(Operand { kind: OperandKind::Move(capture_param_place), span: None }); - ctx.push_assignment(current, capture_local_place, capture_local_rvalue, MirSpan::Unknown); + ctx.push_assignment( + current, + capture_local_place.as_ref(), + capture_local_rvalue, + MirSpan::Unknown, + ); let local = capture.captured_local(); let local = ctx.binding_local(local)?; @@ -2230,8 +2241,8 @@ pub fn mir_body_for_closure_query<'db>( } let mut err = None; - ctx.result.walk_places(|mir_place, store| { - let mir_projections = mir_place.projection.lookup(store); + ctx.result.walk_places(|mir_place| { + let mir_projections = mir_place.projection.lookup(); if let Some(hir_places) = upvar_map.get(&mir_place.local) { let projections = hir_places.iter().find_map(|hir_place| { let iter = mir_projections @@ -2267,18 +2278,17 @@ pub fn mir_body_for_closure_query<'db>( match projections { Some((skip_projections_up_to, (hir_place, upvar_local))) => { mir_place.local = *upvar_local; - let mut result_projections = Vec::with_capacity( - usize::from(hir_place.is_by_ref()) - + (mir_projections.len() - skip_projections_up_to), - ); - if hir_place.is_by_ref() { - result_projections.push(ProjectionElem::Deref); - } - result_projections - .extend(mir_projections[skip_projections_up_to..].iter().cloned()); - mir_place.projection = store.intern(result_projections.into()); + let maybe_deref: &[PlaceElem] = + if hir_place.is_by_ref() { &[ProjectionElem::Deref] } else { &[] }; + mir_place.projection = Projection::new_from_iter( + maybe_deref + .iter() + .copied() + .chain(mir_projections[skip_projections_up_to..].iter().copied()), + ) + .store(); } - None => err = Some(*mir_place), + None => err = Some(mir_place.clone()), } } }); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index 2ed7aedecffef..bb00a79480d84 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -14,15 +14,14 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, expr_id: ExprId, prev_block: BasicBlockId, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let ty = self.expr_ty_without_adjust(expr_id); - let place = self.temp(ty, prev_block, expr_id.into())?; - let Some(current) = - self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? + let place = self.temp(ty, prev_block, expr_id.into())?.into(); + let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)? else { return Ok(None); }; - Ok(Some((place.into(), current))) + Ok(Some((place, current))) } fn lower_expr_to_some_place_with_adjust( @@ -30,18 +29,18 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id: ExprId, prev_block: BasicBlockId, adjustments: &[Adjustment], - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let ty = adjustments .last() .map(|it| it.target.as_ref()) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); - let place = self.temp(ty, prev_block, expr_id.into())?; + let place = self.temp(ty, prev_block, expr_id.into())?.into(); let Some(current) = - self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? + self.lower_expr_to_place_with_adjust(expr_id, place, prev_block, adjustments)? else { return Ok(None); }; - Ok(Some((place.into(), current))) + Ok(Some((place, current))) } pub(super) fn lower_expr_as_place_with_adjust( @@ -50,7 +49,7 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id: ExprId, upgrade_rvalue: bool, adjustments: &[Adjustment], - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -69,7 +68,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - it.0 = it.0.project(ProjectionElem::Deref, &mut self.result.projection_store); + it.0 = it.0.project(ProjectionElem::Deref); Ok(Some(it)) } Adjust::Deref(Some(od)) => { @@ -108,7 +107,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { match self.infer.expr_adjustments.get(&expr_id) { Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a), None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue), @@ -120,7 +119,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -149,17 +148,14 @@ impl<'db> MirLowerCtx<'_, 'db> { ty, Mutability::Not, ); - let temp: Place = self.temp(ref_ty, current, expr_id.into())?.into(); + let temp = self.temp(ref_ty, current, expr_id.into())?.into(); self.push_assignment( current, temp, Operand { kind: OperandKind::Static(s), span: None }.into(), expr_id.into(), ); - Ok(Some(( - temp.project(ProjectionElem::Deref, &mut self.result.projection_store), - current, - ))) + Ok(Some((temp.project(ProjectionElem::Deref), current))) } _ => try_rvalue(self), } @@ -193,7 +189,7 @@ impl<'db> MirLowerCtx<'_, 'db> { let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; - r = r.project(ProjectionElem::Deref, &mut self.result.projection_store); + r = r.project(ProjectionElem::Deref); Ok(Some((r, current))) } Expr::UnaryOp { .. } => try_rvalue(self), @@ -256,8 +252,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - p_base = p_base - .project(ProjectionElem::Index(l_index), &mut self.result.projection_store); + p_base = p_base.project(ProjectionElem::Index(l_index)); Ok(Some((p_base, current))) } _ => try_rvalue(self), @@ -267,20 +262,20 @@ impl<'db> MirLowerCtx<'_, 'db> { fn lower_overloaded_index( &mut self, current: BasicBlockId, - place: Place, + place: PlaceRef<'db>, base_ty: Ty<'db>, result_ty: Ty<'db>, index_operand: Operand, span: MirSpan, index_fn: (FunctionId, GenericArgs<'db>), - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let mutability = match base_ty.as_reference() { Some((_, _, mutability)) => mutability, None => Mutability::Not, }; let result_ref = Ty::new_ref(self.interner(), Region::error(self.interner()), result_ty, mutability); - let mut result: Place = self.temp(result_ref, current, span)?.into(); + let mut result = self.temp(result_ref, current, span)?.into(); let index_fn_op = Operand::const_zst(Ty::new_fn_def( self.interner(), CallableDefId::FunctionId(index_fn.0).into(), @@ -288,7 +283,10 @@ impl<'db> MirLowerCtx<'_, 'db> { )); let Some(current) = self.lower_call( index_fn_op, - Box::new([Operand { kind: OperandKind::Copy(place), span: None }, index_operand]), + Box::new([ + Operand { kind: OperandKind::Copy(place.store()), span: None }, + index_operand, + ]), result, current, false, @@ -297,19 +295,19 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } fn lower_overloaded_deref( &mut self, current: BasicBlockId, - place: Place, + place: PlaceRef<'db>, source_ty: Ty<'db>, target_ty: Ty<'db>, span: MirSpan, mutability: bool, - ) -> Result<'db, Option<(Place, BasicBlockId)>> { + ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { let lang_items = self.lang_items(); let (mutability, deref_fn, borrow_kind) = if !mutability { (Mutability::Not, lang_items.Deref_deref, BorrowKind::Shared) @@ -323,18 +321,18 @@ impl<'db> MirLowerCtx<'_, 'db> { let error_region = Region::error(self.interner()); let ty_ref = Ty::new_ref(self.interner(), error_region, source_ty, mutability); let target_ty_ref = Ty::new_ref(self.interner(), error_region, target_ty, mutability); - let ref_place: Place = self.temp(ty_ref, current, span)?.into(); - self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span); + let ref_place = self.temp(ty_ref, current, span)?.into(); + self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place.store()), span); let deref_fn = deref_fn.ok_or(MirLowerError::LangItemNotFound)?; let deref_fn_op = Operand::const_zst(Ty::new_fn_def( self.interner(), CallableDefId::FunctionId(deref_fn).into(), GenericArgs::new_from_slice(&[source_ty.into()]), )); - let mut result: Place = self.temp(target_ty_ref, current, span)?.into(); + let mut result = self.temp(target_ty_ref, current, span)?.into(); let Some(current) = self.lower_call( deref_fn_op, - Box::new([Operand { kind: OperandKind::Copy(ref_place), span: None }]), + Box::new([Operand { kind: OperandKind::Copy(ref_place.store()), span: None }]), result, current, false, @@ -343,7 +341,7 @@ impl<'db> MirLowerCtx<'_, 'db> { else { return Ok(None); }; - result = result.project(ProjectionElem::Deref, &mut self.result.projection_store); + result = result.project(ProjectionElem::Deref); Ok(Some((result, current))) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 2cb2143229ee5..c306b6ca15f88 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -6,10 +6,10 @@ use rustc_type_ir::inherent::{IntoKind, Ty as _}; use crate::{ BindingMode, ByRef, mir::{ - LocalId, MutBorrowKind, Operand, OperandKind, + LocalId, MutBorrowKind, Operand, OperandKind, PlaceRef, Projection, lower::{ BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, MemoryMap, - MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, Place, PlaceElem, ProjectionElem, + MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, PlaceElem, ProjectionElem, RecordFieldPat, ResolveValueResult, Result, Rvalue, SwitchTargets, TerminatorKind, TupleFieldId, TupleId, Ty, TyKind, ValueNs, VariantId, }, @@ -65,7 +65,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, current: BasicBlockId, current_else: Option, - cond_place: Place, + cond_place: PlaceRef<'db>, pattern: PatId, ) -> Result<'db, (BasicBlockId, Option)> { let (current, current_else) = self.pattern_match_inner( @@ -88,7 +88,7 @@ impl<'db> MirLowerCtx<'_, 'db> { pub(super) fn pattern_match_assignment( &mut self, current: BasicBlockId, - value: Place, + value: PlaceRef<'db>, pattern: PatId, ) -> Result<'db, BasicBlockId> { let (current, _) = @@ -116,20 +116,18 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, mut current: BasicBlockId, mut current_else: Option, - mut cond_place: Place, + mut cond_place: PlaceRef<'db>, pattern: PatId, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default(); - cond_place.projection = self.result.projection_store.intern( + cond_place.projection = Projection::new_from_iter( cond_place .projection - .lookup(&self.result.projection_store) + .as_slice() .iter() .cloned() - .chain((0..cnt).map(|_| ProjectionElem::Deref)) - .collect::>() - .into(), + .chain((0..cnt).map(|_| ProjectionElem::Deref)), ); Ok(match &self.store[pattern] { Pat::Missing | Pat::Rest | Pat::NotNull => { @@ -215,7 +213,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.lower_literal_or_const_to_operand(self.infer.pat_ty(pattern), l)?; let else_target = *current_else.get_or_insert_with(|| self.new_basic_block()); let next = self.new_basic_block(); - let discr: Place = + let discr = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, @@ -223,11 +221,11 @@ impl<'db> MirLowerCtx<'_, 'db> { Rvalue::CheckedBinaryOp( binop, lv, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -263,13 +261,13 @@ impl<'db> MirLowerCtx<'_, 'db> { // emit runtime length check for slice if let TyKind::Slice(_) = pat_ty.kind() { let pattern_len = prefix.len() + suffix.len(); - let place_len: Place = self + let place_len = self .temp(Ty::new_usize(self.interner()), current, pattern.into())? .into(); self.push_assignment( current, place_len, - Rvalue::Len(cond_place), + Rvalue::Len(cond_place.store()), pattern.into(), ); let else_target = @@ -280,7 +278,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current, TerminatorKind::SwitchInt { discr: Operand { - kind: OperandKind::Copy(place_len), + kind: OperandKind::Copy(place_len.store()), span: None, }, targets: SwitchTargets::static_if( @@ -297,7 +295,7 @@ impl<'db> MirLowerCtx<'_, 'db> { MemoryMap::default(), Ty::new_usize(self.interner()), ); - let discr: Place = self + let discr = self .temp(Ty::new_bool(self.interner()), current, pattern.into())? .into(); self.push_assignment( @@ -306,11 +304,15 @@ impl<'db> MirLowerCtx<'_, 'db> { Rvalue::CheckedBinaryOp( BinOp::Le, c, - Operand { kind: OperandKind::Copy(place_len), span: None }, + Operand { + kind: OperandKind::Copy(place_len.store()), + span: None, + }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = + Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -324,10 +326,10 @@ impl<'db> MirLowerCtx<'_, 'db> { } } for (i, &pat) in prefix.iter().enumerate() { - let next_place = cond_place.project( - ProjectionElem::ConstantIndex { offset: i as u64, from_end: false }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: false, + }); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } @@ -335,13 +337,10 @@ impl<'db> MirLowerCtx<'_, 'db> { && mode != MatchingMode::Check && let Pat::Bind { id, subpat: _ } = self.store[slice] { - let next_place = cond_place.project( - ProjectionElem::Subslice { - from: prefix.len() as u64, - to: suffix.len() as u64, - }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::Subslice { + from: prefix.len() as u64, + to: suffix.len() as u64, + }); let mode = self.infer.binding_modes[slice]; (current, current_else) = self.pattern_match_binding( id, @@ -353,10 +352,10 @@ impl<'db> MirLowerCtx<'_, 'db> { )?; } for (i, &pat) in suffix.iter().enumerate() { - let next_place = cond_place.project( - ProjectionElem::ConstantIndex { offset: i as u64, from_end: true }, - &mut self.result.projection_store, - ); + let next_place = cond_place.project(ProjectionElem::ConstantIndex { + offset: i as u64, + from_end: true, + }); (current, current_else) = self.pattern_match_inner(current, current_else, next_place, pat, mode)?; } @@ -420,19 +419,19 @@ impl<'db> MirLowerCtx<'_, 'db> { } not_supported!("path in pattern position that is not const or variant") }; - let tmp: Place = + let tmp = self.temp(self.infer.pat_ty(pattern), current, pattern.into())?.into(); let span = pattern.into(); self.lower_const(c.into(), current, tmp, subst, span)?; - let tmp2: Place = + let tmp2 = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, tmp2, Rvalue::CheckedBinaryOp( BinOp::Eq, - Operand { kind: OperandKind::Copy(tmp), span: None }, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(tmp.store()), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), span, ); @@ -441,7 +440,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.set_terminator( current, TerminatorKind::SwitchInt { - discr: Operand { kind: OperandKind::Copy(tmp2), span: None }, + discr: Operand { kind: OperandKind::Copy(tmp2.store()), span: None }, targets: SwitchTargets::static_if(1, next, else_target), }, span, @@ -494,8 +493,7 @@ impl<'db> MirLowerCtx<'_, 'db> { )? } Pat::Ref { pat, mutability: _ } => { - let cond_place = - cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store); + let cond_place = cond_place.project(ProjectionElem::Deref); self.pattern_match_inner(current, current_else, cond_place, *pat, mode)? } &Pat::Expr(expr) => { @@ -510,7 +508,7 @@ impl<'db> MirLowerCtx<'_, 'db> { self.push_assignment( current, lhs_place, - Operand { kind: OperandKind::Copy(cond_place), span: None }.into(), + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }.into(), expr.into(), ); (current, current_else) @@ -525,7 +523,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, id: BindingId, mode: BindingMode, - cond_place: Place, + cond_place: PlaceRef<'db>, span: MirSpan, current: BasicBlockId, current_else: Option, @@ -541,7 +539,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, target_place: LocalId, mode: BindingMode, - cond_place: Place, + cond_place: PlaceRef<'db>, span: MirSpan, ) { self.push_assignment( @@ -549,14 +547,15 @@ impl<'db> MirLowerCtx<'_, 'db> { target_place.into(), match mode { BindingMode(ByRef::No, _) => { - Operand { kind: OperandKind::Copy(cond_place), span: None }.into() + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }.into() } BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Not), _) => { - Rvalue::Ref(BorrowKind::Shared, cond_place) - } - BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => { - Rvalue::Ref(BorrowKind::Mut { kind: MutBorrowKind::Default }, cond_place) + Rvalue::Ref(BorrowKind::Shared, cond_place.store()) } + BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => Rvalue::Ref( + BorrowKind::Mut { kind: MutBorrowKind::Default }, + cond_place.store(), + ), }, span, ); @@ -567,24 +566,23 @@ impl<'db> MirLowerCtx<'_, 'db> { current_else: Option, current: BasicBlockId, c: Operand, - cond_place: Place, + cond_place: PlaceRef<'db>, pattern: Idx, ) -> Result<'db, (BasicBlockId, Option)> { let then_target = self.new_basic_block(); let else_target = current_else.unwrap_or_else(|| self.new_basic_block()); - let discr: Place = - self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); + let discr = self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into(); self.push_assignment( current, discr, Rvalue::CheckedBinaryOp( BinOp::Eq, c, - Operand { kind: OperandKind::Copy(cond_place), span: None }, + Operand { kind: OperandKind::Copy(cond_place.store()), span: None }, ), pattern.into(), ); - let discr = Operand { kind: OperandKind::Copy(discr), span: None }; + let discr = Operand { kind: OperandKind::Copy(discr.store()), span: None }; self.set_terminator( current, TerminatorKind::SwitchInt { @@ -598,7 +596,7 @@ impl<'db> MirLowerCtx<'_, 'db> { fn pattern_matching_variant( &mut self, - cond_place: Place, + cond_place: PlaceRef<'db>, variant: VariantId, mut current: BasicBlockId, span: MirSpan, @@ -611,13 +609,18 @@ impl<'db> MirLowerCtx<'_, 'db> { if mode == MatchingMode::Check { let e = self.const_eval_discriminant(v)? as u128; let tmp = self.discr_temp_place(current); - self.push_assignment(current, tmp, Rvalue::Discriminant(cond_place), span); + self.push_assignment( + current, + tmp, + Rvalue::Discriminant(cond_place.store()), + span, + ); let next = self.new_basic_block(); let else_target = current_else.get_or_insert_with(|| self.new_basic_block()); self.set_terminator( current, TerminatorKind::SwitchInt { - discr: Operand { kind: OperandKind::Copy(tmp), span: None }, + discr: Operand { kind: OperandKind::Copy(tmp.store()), span: None }, targets: SwitchTargets::static_if(e, next, *else_target), }, span, @@ -656,7 +659,7 @@ impl<'db> MirLowerCtx<'_, 'db> { v: VariantId, current: BasicBlockId, current_else: Option, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { Ok(match shape { @@ -700,11 +703,11 @@ impl<'db> MirLowerCtx<'_, 'db> { mut current: BasicBlockId, mut current_else: Option, args: impl Iterator, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { for (proj, arg) in args { - let cond_place = cond_place.project(proj, &mut self.result.projection_store); + let cond_place = cond_place.project(proj); (current, current_else) = self.pattern_match_inner(current, current_else, cond_place, arg, mode)?; } @@ -718,7 +721,7 @@ impl<'db> MirLowerCtx<'_, 'db> { args: &[PatId], ellipsis: Option, fields: impl DoubleEndedIterator + Clone, - cond_place: &Place, + cond_place: &PlaceRef<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index a534e50997793..9054987066029 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -373,7 +373,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { } } } - f(self, p.local, p.projection.lookup(&self.body.projection_store)); + f(self, p.local, p.projection.lookup()); } fn operand(&mut self, r: &Operand) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs index 47b4b1dc4a3a3..f0d33ad2ddafc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver.rs @@ -120,6 +120,7 @@ pub struct DefaultEmpty<'db> { pub clauses: Clauses<'db>, pub region_assumptions: RegionAssumptions<'db>, pub consts: Consts<'db>, + pub projection: crate::mir::Projection<'db>, } pub struct DefaultAny<'db> { @@ -237,6 +238,12 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { let ty = ManuallyDrop::new(ty.store()); ty.as_ref() }; + let create_projection = |slice| { + let it = crate::mir::Projection::new_from_slice(slice); + // We need to increase the refcount (forever), so that the types won't be freed. + let it = ManuallyDrop::new(it.store()); + it.as_ref() + }; let str = create_ty(TyKind::Str); let statik = create_region(RegionKind::ReStatic); @@ -303,6 +310,7 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> { clauses: create_clauses(&[]), region_assumptions: create_region_assumptions(&[]), consts: create_consts(&[]), + projection: create_projection(&[]), }, one_invariant: create_variances_of(&[rustc_type_ir::Variance::Invariant]), one_covariant: create_variances_of(&[rustc_type_ir::Variance::Covariant]), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 5ca0e67d292e2..172b1a245f5e0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -2582,6 +2582,7 @@ pub unsafe fn collect_ty_garbage() { gc.add_slice_storage::(); gc.add_slice_storage::(); gc.add_slice_storage::(); + gc.add_slice_storage::(); // SAFETY: // - By our precondition, there are no unrecorded types. @@ -2646,4 +2647,5 @@ impl_gc_visit_slice!( super::region::RegionAssumptionsStorage, super::ty::TysStorage, super::consts::ConstsStorage, + crate::mir::ProjectionStorage, ); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 57aba51b4e859..f6b13cbe0b94d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -43,7 +43,7 @@ pub(super) fn hints( for (_, bb) in mir.basic_blocks.iter() { let terminator = bb.terminator.as_ref()?; - if let TerminatorKind::Drop { place, .. } = terminator.kind { + if let TerminatorKind::Drop { place, .. } = &terminator.kind { if !place.projection.is_empty() { continue; // Ignore complex cases for now } From 5c9c7c3789449133257ff80170a34494e3b5daa0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 30 May 2026 11:07:26 +0200 Subject: [PATCH 100/114] internal: Make `MemDocs` cheap to clone for snapshotting --- .../crates/rust-analyzer/src/mem_docs.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs index 6a93a0ebb4cec..05fc7a8dd6cb9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/mem_docs.rs @@ -3,6 +3,7 @@ use std::mem; use rustc_hash::FxHashMap; +use triomphe::Arc; use vfs::VfsPath; /// Holds the set of in-memory documents. @@ -11,7 +12,7 @@ use vfs::VfsPath; /// might be different from what's on disk. #[derive(Default, Clone)] pub(crate) struct MemDocs { - mem_docs: FxHashMap, + mem_docs: Arc>>, added_or_removed: bool, } @@ -22,7 +23,7 @@ impl MemDocs { pub(crate) fn insert(&mut self, path: VfsPath, data: DocumentData) -> Result<(), ()> { self.added_or_removed = true; - match self.mem_docs.insert(path, data) { + match Arc::make_mut(&mut self.mem_docs).insert(path, Arc::new(data)) { Some(_) => Err(()), None => Ok(()), } @@ -30,20 +31,20 @@ impl MemDocs { pub(crate) fn remove(&mut self, path: &VfsPath) -> Result<(), ()> { self.added_or_removed = true; - match self.mem_docs.remove(path) { + match Arc::make_mut(&mut self.mem_docs).remove(path) { Some(_) => Ok(()), None => Err(()), } } pub(crate) fn get(&self, path: &VfsPath) -> Option<&DocumentData> { - self.mem_docs.get(path) + self.mem_docs.get(path).map(Arc::as_ref) } pub(crate) fn get_mut(&mut self, path: &VfsPath) -> Option<&mut DocumentData> { // NB: don't set `self.added_or_removed` here, as that purposefully only // tracks changes to the key set. - self.mem_docs.get_mut(path) + Arc::make_mut(&mut self.mem_docs).get_mut(path).map(Arc::make_mut) } pub(crate) fn iter(&self) -> impl Iterator { From add64c4f6f832e5c4f603e4cf28125847f86d67c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 30 May 2026 10:52:45 +0200 Subject: [PATCH 101/114] Better release builds with more debug info --- src/tools/rust-analyzer/xtask/src/dist.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/dist.rs b/src/tools/rust-analyzer/xtask/src/dist.rs index 57a6a0eae1be8..8017ff93e8ccd 100644 --- a/src/tools/rust-analyzer/xtask/src/dist.rs +++ b/src/tools/rust-analyzer/xtask/src/dist.rs @@ -106,13 +106,12 @@ fn dist_server( dev_rel: bool, ) -> anyhow::Result<()> { let _e = sh.push_env("CFG_RELEASE", release); + let _e = sh.push_env("CARGO_PROFILE_RELEASE_DEBUG", "limited"); let _e = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "thin"); + let _e = sh.push_env("CARGO_PROFILE_RELEASE_CODEGEN_UNITS", "1"); + let _e = sh.push_env("CARGO_PROFILE_DEV_REL_DEBUG", "limited"); let _e = sh.push_env("CARGO_PROFILE_DEV_REL_LTO", "thin"); - - // Uncomment to enable debug info for releases. Note that: - // * debug info is split on windows and macs, so it does nothing for those platforms, - // * on Linux, this blows up the binary size from 8MB to 43MB, which is unreasonable. - // let _e = sh.push_env("CARGO_PROFILE_RELEASE_DEBUG", "1"); + let _e = sh.push_env("CARGO_PROFILE_DEV_REL_CODEGEN_UNITS", "1"); let linux_target = target.is_linux(); let target_name = match &target.libc_suffix { From 76cee223a595acb79a9c8c91766c3e1ad570c108 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 30 May 2026 11:47:45 +0200 Subject: [PATCH 102/114] minor: Fix garbage collection running too frequently --- .../crates/rust-analyzer/src/main_loop.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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 a9ce6f728b6a5..5ed522ceee4cf 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 @@ -478,6 +478,7 @@ impl GlobalState { (false, false) }; + let mut gc_elapsed = None; if self.is_quiescent() { let became_quiescent = !was_quiescent; if became_quiescent { @@ -541,8 +542,10 @@ impl GlobalState { && self.fmt_pool.handle.is_empty() && current_revision != self.last_gc_revision { + let gc_start = Instant::now(); self.analysis_host.trigger_garbage_collection(); - self.last_gc_revision = current_revision; + self.last_gc_revision = self.analysis_host.raw_database().nonce_and_revision().1; + gc_elapsed = Some(gc_start.elapsed()); } } @@ -588,10 +591,14 @@ impl GlobalState { let loop_duration = loop_start.elapsed(); if loop_duration > Duration::from_millis(100) && was_quiescent { tracing::warn!( - "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}" + "overly long loop turn took {loop_duration:?}:\n\ + (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\ + (garbage collection took {gc_elapsed:?})" ); self.poke_rust_analyzer_developer(format!( - "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}" + "overly long loop turn took {loop_duration:?}:\n\ + (event handling took {event_handling_duration:?}): {event_dbg_msg}\n\ + (garbage collection took {gc_elapsed:?})" )); } } From 9b544b7deda2ff2799d353046f7242541836c81f Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 31 May 2026 01:11:44 +0300 Subject: [PATCH 103/114] Also consider library features internal --- .../crates/ide-completion/src/context.rs | 12 +++++++++++- .../crates/ide-completion/src/tests/expression.rs | 2 +- .../crates/intern/src/symbol/symbols.rs | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 135e222a368f3..f7bb14391b7de 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -611,7 +611,7 @@ impl<'db> CompletionContext<'_, 'db> { let Some(unstable_feature) = attrs.unstable_feature(self.db) else { return true; }; - !INTERNAL_FEATURES.contains(&unstable_feature) + !is_internal_feature(&unstable_feature) || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature) } @@ -977,6 +977,7 @@ const INTERNAL_FEATURES_LIST: &[Symbol] = &[ sym::eii_internals, sym::field_representing_type_raw, sym::intrinsics, + sym::core_intrinsics, sym::lang_items, sym::link_cfg, sym::more_maybe_bounds, @@ -1000,3 +1001,12 @@ const INTERNAL_FEATURES_LIST: &[Symbol] = &[ static INTERNAL_FEATURES: LazyLock> = LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect()); + +fn is_internal_feature(feature: &Symbol) -> bool { + if INTERNAL_FEATURES.contains(feature) { + return true; + } + // Libs features are internal if they end in `_internal` or `_internals`. + let feature = feature.as_str(); + feature.ends_with("_internal") || feature.ends_with("_internals") +} 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 eb99664c35386..3d4ad78c34334 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 @@ -2372,7 +2372,7 @@ fn main() { $0 } //- /std.rs crate:std -#[unstable(feature = "intrinsics")] +#[unstable(feature = "core_intrinsics")] pub mod intrinsics {} "#, expect![[r#" diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index f9ba0582ab809..e5f66a202ee6f 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -646,6 +646,7 @@ define_symbols! { eii_internals, field_representing_type_raw, intrinsics, + core_intrinsics, link_cfg, more_maybe_bounds, negative_bounds, From 2c2d90ad0073183e34a9712ed8c3c8f867860130 Mon Sep 17 00:00:00 2001 From: "workflows-rust-analyzer[bot]" <223433972+workflows-rust-analyzer[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 00:12:42 +0000 Subject: [PATCH 104/114] internal: update generated lints --- .../crates/ide-db/src/generated/lints.rs | 80 +++++++++++-------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 310023ea2ad31..683adff6154af 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -3934,22 +3934,6 @@ The tracking issue for this feature is: [#151828] [#151828]: https://github.com/rust-lang/rust/issues/151828 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "bool_to_result", - description: r##"# `bool_to_result` - - - -The tracking issue for this feature is: [#142748] - -[#142748]: https://github.com/rust-lang/rust/issues/142748 - ------------------------ "##, default_severity: Severity::Allow, @@ -4838,6 +4822,22 @@ The tracking issue for this feature is: [#148519] [#148519]: https://github.com/rust-lang/rust/issues/148519 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "clflushopt_target_feature", + description: r##"# `clflushopt_target_feature` + +The `clflushopt` target feature on x86. + +The tracking issue for this feature is: [#157096] + +[#157096]: https://github.com/rust-lang/rust/issues/157096 + ------------------------ "##, default_severity: Severity::Allow, @@ -11265,6 +11265,22 @@ The tracking issue for this feature is: [#123076] [#123076]: https://github.com/rust-lang/rust/issues/123076 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "mut_restriction", + description: r##"# `mut_restriction` + +Allows `mut(crate) field: Type` restrictions. + +The tracking issue for this feature is: [#105077] + +[#105077]: https://github.com/rust-lang/rust/issues/105077 + ------------------------ "##, default_severity: Severity::Allow, @@ -12273,22 +12289,6 @@ The tracking issue for this feature is: [#153328] [#153328]: https://github.com/rust-lang/rust/issues/153328 ------------------------- -"##, - default_severity: Severity::Allow, - warn_since: None, - deny_since: None, - }, - Lint { - label: "path_is_empty", - description: r##"# `path_is_empty` - - - -The tracking issue for this feature is: [#148494] - -[#148494]: https://github.com/rust-lang/rust/issues/148494 - ------------------------ "##, default_severity: Severity::Allow, @@ -13973,6 +13973,22 @@ The tracking issue for this feature is: [#56975] [#56975]: https://github.com/rust-lang/rust/issues/56975 +------------------------ +"##, + default_severity: Severity::Allow, + warn_since: None, + deny_since: None, + }, + Lint { + label: "share_trait", + description: r##"# `share_trait` + + + +The tracking issue for this feature is: [#156756] + +[#156756]: https://github.com/rust-lang/rust/issues/156756 + ------------------------ "##, default_severity: Severity::Allow, From 00ecc6ec9d387a4581509ae7e03cde55f6299041 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 31 May 2026 12:59:59 +0200 Subject: [PATCH 105/114] Always use crates from sysroot in proc-macro-srv Unlike the rest of rust-analyzer it only compiles on nightly anyway. And this makes it easier to add dependencies on other rustc crates like rustc_metadata for parsing crate metadata in the future. Install rustc-dev for nightly toolchain --- src/tools/rust-analyzer/.github/workflows/ci.yaml | 2 +- src/tools/rust-analyzer/Cargo.lock | 1 - .../rust-analyzer/crates/proc-macro-api/Cargo.toml | 1 - .../rust-analyzer/crates/proc-macro-api/src/lib.rs | 5 ++--- .../crates/proc-macro-srv-cli/Cargo.toml | 1 - .../crates/proc-macro-srv-cli/src/lib.rs | 5 ++--- .../crates/proc-macro-srv-cli/src/main.rs | 4 ++-- .../tests/bidirectional_postcard.rs | 3 +-- .../crates/proc-macro-srv-cli/tests/legacy_json.rs | 3 +-- .../rust-analyzer/crates/proc-macro-srv/Cargo.toml | 3 --- .../rust-analyzer/crates/proc-macro-srv/src/lib.rs | 13 ++----------- .../rust-analyzer/crates/rust-analyzer/Cargo.toml | 1 - 12 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index b9427e1927f6c..3eb4c13465b7e 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -56,7 +56,7 @@ jobs: - name: Install Rust toolchain run: | RUSTC_VERSION=$(cat rust-version) - rustup-toolchain-install-master ${RUSTC_VERSION} -c cargo -c rust-src -c rustfmt + rustup-toolchain-install-master ${RUSTC_VERSION} -c cargo -c rust-src -c rustfmt -c rustc-dev -c llvm-tools rustup default ${RUSTC_VERSION} # Emulate a nightly toolchain, because the toolchain installed above does not have "nightly" diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 0606ad81ba34d..440ceb362dabd 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1887,7 +1887,6 @@ dependencies = [ "object", "paths", "proc-macro-test", - "ra-ap-rustc_lexer", "span", "temp-dir", ] diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 5542c5da8fc54..8c85314843a86 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -39,7 +39,6 @@ span.workspace = true [features] sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] default = [] -in-rust-tree = [] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index e83ddb859498b..a3d6ac5dd62b5 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -8,12 +8,11 @@ #![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] #![cfg_attr( feature = "sysroot-abi", - feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span) + feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private) )] #![allow(internal_features, unused_features)] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#[cfg(feature = "in-rust-tree")] +#[cfg(feature = "sysroot-abi")] extern crate rustc_driver as _; pub mod bidirectional_protocol; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index f586fe7644d77..4641f0878371c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -33,7 +33,6 @@ proc-macro-test.path = "../proc-macro-srv/proc-macro-test" default = [] # default = ["sysroot-abi"] sysroot-abi = ["proc-macro-srv/sysroot-abi", "proc-macro-api/sysroot-abi"] -in-rust-tree = ["proc-macro-srv/in-rust-tree", "sysroot-abi"] [[bin]] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs index 3d0e2027b70f3..d540146c78da9 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs @@ -2,10 +2,9 @@ //! //! This module exposes the server main loop and protocol format for integration testing. -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![cfg(feature = "sysroot-abi")] +#![feature(rustc_private)] -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; -#[cfg(feature = "sysroot-abi")] pub mod main_loop; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index 928753659f1cc..20b1c73ebf2d7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -1,10 +1,10 @@ //! A standalone binary for `proc-macro-srv`. //! Driver for proc macro server -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![cfg_attr(feature = "sysroot-abi", feature(rustc_private))] #![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))] #![allow(clippy::print_stdout, clippy::print_stderr)] -#[cfg(feature = "in-rust-tree")] +#[cfg(feature = "sysroot-abi")] extern crate rustc_driver as _; mod version; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs index 456b9fd70bc9d..4c50f06f4a0b0 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -1,7 +1,6 @@ #![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![feature(rustc_private)] -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod common { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs index 562cf0c2516f4..b807fd46d42cb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs @@ -4,9 +4,8 @@ //! channels without needing to spawn the actual server and client processes. #![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#![feature(rustc_private)] -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; mod common { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 8e5617f8a20eb..c3cd447c85f17 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -23,8 +23,6 @@ paths.workspace = true span = { path = "../span", version = "0.0.0", default-features = false} intern.workspace = true -ra-ap-rustc_lexer.workspace = true - [target.'cfg(unix)'.dependencies] libc.workspace = true @@ -39,7 +37,6 @@ proc-macro-test.path = "./proc-macro-test" [features] default = [] sysroot-abi = [] -in-rust-tree = ["sysroot-abi"] [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index a71323d89d299..7a77f2ab6237d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -11,23 +11,14 @@ //! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… #![cfg(feature = "sysroot-abi")] -#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] -#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] +#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private)] #![expect(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)] #![allow(unused_features, unused_crate_dependencies)] #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)] -#[cfg(not(feature = "in-rust-tree"))] -extern crate proc_macro as rustc_proc_macro; -#[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; -#[cfg(feature = "in-rust-tree")] -extern crate rustc_proc_macro; - -#[cfg(not(feature = "in-rust-tree"))] -extern crate ra_ap_rustc_lexer as rustc_lexer; -#[cfg(feature = "in-rust-tree")] extern crate rustc_lexer; +extern crate rustc_proc_macro; mod bridge; mod dylib; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index 27d9576e29d0a..1745384843295 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -108,7 +108,6 @@ in-rust-tree = [ "ide/in-rust-tree", "load-cargo/in-rust-tree", "parser/in-rust-tree", - "proc-macro-api/in-rust-tree", "syntax/in-rust-tree", ] dhat = ["dep:dhat"] From 5a22cb9e82454926c617edd45356a5c58e5842ad Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 31 May 2026 13:00:11 +0200 Subject: [PATCH 106/114] Remove outdated comment --- src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 7a77f2ab6237d..e989eb62dd008 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -7,8 +7,6 @@ //! //! * We use `tt` for proc-macro `TokenStream` server, it is easier to manipulate and interact with //! RA than `proc-macro2` token stream. -//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable` -//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… #![cfg(feature = "sysroot-abi")] #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span, rustc_private)] From abc0c8645f632362b71512f36a39a1354a6fa341 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 31 May 2026 18:53:07 +0200 Subject: [PATCH 107/114] Fix proc-macro-srv CI job depending on salsa --- .../rust-analyzer/.github/workflows/ci.yaml | 2 +- .../crates/proc-macro-api/Cargo.toml | 3 - .../proc-macro-api/src/legacy_protocol/msg.rs | 63 +-- .../proc-macro-srv-cli/tests/common/utils.rs | 7 +- .../crates/proc-macro-srv/src/tests/mod.rs | 468 +++++++++--------- .../crates/proc-macro-srv/src/tests/utils.rs | 8 +- .../rust-analyzer/crates/span/src/hygiene.rs | 7 + src/tools/rust-analyzer/crates/tt/Cargo.toml | 1 - 8 files changed, 269 insertions(+), 290 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 3eb4c13465b7e..48bce9eb191db 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -72,7 +72,7 @@ jobs: run: cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet - name: Check salsa dependency - run: "! (cargo tree -p proc-macro-srv-cli | grep -q salsa)" + run: "! (cargo tree -p proc-macro-srv-cli -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -i salsa)" rust: if: github.repository == 'rust-lang/rust-analyzer' diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml index 8c85314843a86..2cb76ef6b7e7e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml @@ -33,9 +33,6 @@ postcard.workspace = true semver.workspace = true rayon.workspace = true -[dev-dependencies] -span.workspace = true - [features] sysroot-abi = ["proc-macro-srv", "proc-macro-srv/sysroot-abi"] default = [] diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index bb0dde4728609..b647e6cf8aadb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -193,9 +193,7 @@ impl Message for Response { #[cfg(test)] mod tests { use intern::Symbol; - use span::{ - Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize, - }; + use span::{ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize}; use tt::{ Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree, TopSubtreeBuilder, @@ -205,6 +203,11 @@ mod tests { use super::*; + fn make_ctx() -> SyntaxContext { + // SAFETY: Tests do not use a Database, so this won't ever be used within salsa. + unsafe { SyntaxContext::from_u32(0) } + } + fn fixture_token_tree_top_many_none() -> TopSubtree { let anchor = SpanAnchor { file_id: span::EditionedFileId::new( @@ -215,16 +218,8 @@ mod tests { }; let mut builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Invisible, }); @@ -234,7 +229,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(0), TextSize::of("struct")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, is_raw: tt::IdentIsRaw::No, } @@ -246,7 +241,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, is_raw: tt::IdentIsRaw::Yes, } @@ -257,7 +252,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, tt::LitKind::Str, ))); @@ -266,7 +261,7 @@ mod tests { span: Span { range: TextRange::at(TextSize::new(13), TextSize::of('@')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, spacing: Spacing::Joint, })); @@ -275,7 +270,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(14), TextSize::of('{')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, ); builder.open( @@ -283,7 +278,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(15), TextSize::of('[')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, ); builder.push(Leaf::Literal(Literal::new( @@ -291,7 +286,7 @@ mod tests { Span { range: TextRange::at(TextSize::new(16), TextSize::of("0u32")), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }, tt::LitKind::Integer, "u32", @@ -299,13 +294,13 @@ mod tests { builder.close(Span { range: TextRange::at(TextSize::new(20), TextSize::of(']')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }); builder.close(Span { range: TextRange::at(TextSize::new(21), TextSize::of('}')), anchor, - ctx: SyntaxContext::root(Edition::CURRENT), + ctx: make_ctx(), }); builder.build() @@ -321,16 +316,8 @@ mod tests { }; let builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Invisible, }); @@ -347,16 +334,8 @@ mod tests { }; let builder = TopSubtreeBuilder::new(Delimiter { - open: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, - close: Span { - range: TextRange::empty(TextSize::new(0)), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }, + open: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, + close: Span { range: TextRange::empty(TextSize::new(0)), anchor, ctx: make_ctx() }, kind: DelimiterKind::Brace, }); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs index 3049e98004051..051f28aee2ef4 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -144,11 +144,8 @@ pub(crate) fn create_empty_token_tree( file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT), ast_id: span::ROOT_ERASED_FILE_AST_ID, }; - let span = Span { - range: TextRange::empty(0.into()), - anchor, - ctx: SyntaxContext::root(Edition::CURRENT), - }; + let span = + Span { range: TextRange::empty(0.into()), anchor, ctx: SyntaxContext::from_u32_safe(0) }; let builder = TopSubtreeBuilder::new(Delimiter { open: 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 ebef9a9a519a7..a8ddb216f0e41 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 @@ -31,23 +31,23 @@ fn test_derive_empty() { IDENT 1 r#u32 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..6#ROOT2024 struct - IDENT 42:Root[0000, 0]@7..8#ROOT2024 S - GROUP {} 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@46..47#ROOT2024 42:Root[0000, 0]@9..47#ROOT2024 - IDENT 42:Root[0000, 0]@11..16#ROOT2024 field - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 : [alone] - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 & [joint] - PUNCT 42:Root[0000, 0]@22..23#ROOT2024 ' [joint] - IDENT 42:Root[0000, 0]@22..24#ROOT2024 r#lt - IDENT 42:Root[0000, 0]@25..27#ROOT2024 fn - GROUP () 42:Root[0000, 0]@27..28#ROOT2024 42:Root[0000, 0]@31..32#ROOT2024 42:Root[0000, 0]@27..32#ROOT2024 - IDENT 42:Root[0000, 0]@28..31#ROOT2024 u32 - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 - [joint] - PUNCT 42:Root[0000, 0]@34..35#ROOT2024 > [alone] - PUNCT 42:Root[0000, 0]@36..37#ROOT2024 & [joint] - PUNCT 42:Root[0000, 0]@38..39#ROOT2024 ' [joint] - IDENT 42:Root[0000, 0]@38..39#ROOT2024 a - IDENT 42:Root[0000, 0]@42..45#ROOT2024 r#u32 + IDENT 42:Root[0000, 0]@0..6#0 struct + IDENT 42:Root[0000, 0]@7..8#0 S + GROUP {} 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@46..47#0 42:Root[0000, 0]@9..47#0 + IDENT 42:Root[0000, 0]@11..16#0 field + PUNCT 42:Root[0000, 0]@16..17#0 : [alone] + PUNCT 42:Root[0000, 0]@18..19#0 & [joint] + PUNCT 42:Root[0000, 0]@22..23#0 ' [joint] + IDENT 42:Root[0000, 0]@22..24#0 r#lt + IDENT 42:Root[0000, 0]@25..27#0 fn + GROUP () 42:Root[0000, 0]@27..28#0 42:Root[0000, 0]@31..32#0 42:Root[0000, 0]@27..32#0 + IDENT 42:Root[0000, 0]@28..31#0 u32 + PUNCT 42:Root[0000, 0]@33..34#0 - [joint] + PUNCT 42:Root[0000, 0]@34..35#0 > [alone] + PUNCT 42:Root[0000, 0]@36..37#0 & [joint] + PUNCT 42:Root[0000, 0]@38..39#0 ' [joint] + IDENT 42:Root[0000, 0]@38..39#0 a + IDENT 42:Root[0000, 0]@42..45#0 r#u32 "#]], ); } @@ -137,76 +137,76 @@ pub struct Foo { PUNCT 1 , [alone] "#]], expect![[r#" - PUNCT 42:Root[0000, 0]@1..2#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@2..3#ROOT2024 42:Root[0000, 0]@52..53#ROOT2024 42:Root[0000, 0]@2..53#ROOT2024 - IDENT 42:Root[0000, 0]@3..9#ROOT2024 helper - GROUP () 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@51..52#ROOT2024 42:Root[0000, 0]@9..52#ROOT2024 - IDENT 42:Root[0000, 0]@10..18#ROOT2024 build_fn - GROUP () 42:Root[0000, 0]@18..19#ROOT2024 42:Root[0000, 0]@50..51#ROOT2024 42:Root[0000, 0]@18..51#ROOT2024 - IDENT 42:Root[0000, 0]@19..26#ROOT2024 private - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@28..32#ROOT2024 name - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@35..50#ROOT2024 Str partial_build - IDENT 42:Root[0000, 0]@54..57#ROOT2024 pub - IDENT 42:Root[0000, 0]@58..64#ROOT2024 struct - IDENT 42:Root[0000, 0]@65..68#ROOT2024 Foo - GROUP {} 42:Root[0000, 0]@69..70#ROOT2024 42:Root[0000, 0]@190..191#ROOT2024 42:Root[0000, 0]@69..191#ROOT2024 - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 # [alone] - GROUP [] 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 - IDENT 42:Root[0000, 0]@0..0#ROOT2024 doc - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@75..130#ROOT2024 Str / The domain where this federated instance is running - PUNCT 42:Root[0000, 0]@135..136#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@136..137#ROOT2024 42:Root[0000, 0]@157..158#ROOT2024 42:Root[0000, 0]@136..158#ROOT2024 - IDENT 42:Root[0000, 0]@137..143#ROOT2024 helper - GROUP () 42:Root[0000, 0]@143..144#ROOT2024 42:Root[0000, 0]@156..157#ROOT2024 42:Root[0000, 0]@143..157#ROOT2024 - IDENT 42:Root[0000, 0]@144..150#ROOT2024 setter - GROUP () 42:Root[0000, 0]@150..151#ROOT2024 42:Root[0000, 0]@155..156#ROOT2024 42:Root[0000, 0]@150..156#ROOT2024 - IDENT 42:Root[0000, 0]@151..155#ROOT2024 into - IDENT 42:Root[0000, 0]@163..166#ROOT2024 pub - GROUP () 42:Root[0000, 0]@166..167#ROOT2024 42:Root[0000, 0]@172..173#ROOT2024 42:Root[0000, 0]@166..173#ROOT2024 - IDENT 42:Root[0000, 0]@167..172#ROOT2024 crate - IDENT 42:Root[0000, 0]@174..180#ROOT2024 domain - PUNCT 42:Root[0000, 0]@180..181#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@182..188#ROOT2024 String - PUNCT 42:Root[0000, 0]@188..189#ROOT2024 , [alone] - - - PUNCT 42:Root[0000, 0]@1..2#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@2..3#ROOT2024 42:Root[0000, 0]@52..53#ROOT2024 42:Root[0000, 0]@2..53#ROOT2024 - IDENT 42:Root[0000, 0]@3..9#ROOT2024 helper - GROUP () 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@51..52#ROOT2024 42:Root[0000, 0]@9..52#ROOT2024 - IDENT 42:Root[0000, 0]@10..18#ROOT2024 build_fn - GROUP () 42:Root[0000, 0]@18..19#ROOT2024 42:Root[0000, 0]@50..51#ROOT2024 42:Root[0000, 0]@18..51#ROOT2024 - IDENT 42:Root[0000, 0]@19..26#ROOT2024 private - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@28..32#ROOT2024 name - PUNCT 42:Root[0000, 0]@33..34#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@35..50#ROOT2024 Str partial_build - IDENT 42:Root[0000, 0]@54..57#ROOT2024 pub - IDENT 42:Root[0000, 0]@58..64#ROOT2024 struct - IDENT 42:Root[0000, 0]@65..68#ROOT2024 Foo - GROUP {} 42:Root[0000, 0]@69..70#ROOT2024 42:Root[0000, 0]@190..191#ROOT2024 42:Root[0000, 0]@69..191#ROOT2024 - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 # [alone] - GROUP [] 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 42:Root[0000, 0]@0..0#ROOT2024 - IDENT 42:Root[0000, 0]@0..0#ROOT2024 doc - PUNCT 42:Root[0000, 0]@0..0#ROOT2024 = [alone] - LITER 42:Root[0000, 0]@75..130#ROOT2024 Str / The domain where this federated instance is running - PUNCT 42:Root[0000, 0]@135..136#ROOT2024 # [joint] - GROUP [] 42:Root[0000, 0]@136..137#ROOT2024 42:Root[0000, 0]@157..158#ROOT2024 42:Root[0000, 0]@136..158#ROOT2024 - IDENT 42:Root[0000, 0]@137..143#ROOT2024 helper - GROUP () 42:Root[0000, 0]@143..144#ROOT2024 42:Root[0000, 0]@156..157#ROOT2024 42:Root[0000, 0]@143..157#ROOT2024 - IDENT 42:Root[0000, 0]@144..150#ROOT2024 setter - GROUP () 42:Root[0000, 0]@150..151#ROOT2024 42:Root[0000, 0]@155..156#ROOT2024 42:Root[0000, 0]@150..156#ROOT2024 - IDENT 42:Root[0000, 0]@151..155#ROOT2024 into - IDENT 42:Root[0000, 0]@163..166#ROOT2024 pub - GROUP () 42:Root[0000, 0]@166..167#ROOT2024 42:Root[0000, 0]@172..173#ROOT2024 42:Root[0000, 0]@166..173#ROOT2024 - IDENT 42:Root[0000, 0]@167..172#ROOT2024 crate - IDENT 42:Root[0000, 0]@174..180#ROOT2024 domain - PUNCT 42:Root[0000, 0]@180..181#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@182..188#ROOT2024 String - PUNCT 42:Root[0000, 0]@188..189#ROOT2024 , [alone] + PUNCT 42:Root[0000, 0]@1..2#0 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#0 42:Root[0000, 0]@52..53#0 42:Root[0000, 0]@2..53#0 + IDENT 42:Root[0000, 0]@3..9#0 helper + GROUP () 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@51..52#0 42:Root[0000, 0]@9..52#0 + IDENT 42:Root[0000, 0]@10..18#0 build_fn + GROUP () 42:Root[0000, 0]@18..19#0 42:Root[0000, 0]@50..51#0 42:Root[0000, 0]@18..51#0 + IDENT 42:Root[0000, 0]@19..26#0 private + PUNCT 42:Root[0000, 0]@26..27#0 , [alone] + IDENT 42:Root[0000, 0]@28..32#0 name + PUNCT 42:Root[0000, 0]@33..34#0 = [alone] + LITER 42:Root[0000, 0]@35..50#0 Str partial_build + IDENT 42:Root[0000, 0]@54..57#0 pub + IDENT 42:Root[0000, 0]@58..64#0 struct + IDENT 42:Root[0000, 0]@65..68#0 Foo + GROUP {} 42:Root[0000, 0]@69..70#0 42:Root[0000, 0]@190..191#0 42:Root[0000, 0]@69..191#0 + PUNCT 42:Root[0000, 0]@0..0#0 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 + IDENT 42:Root[0000, 0]@0..0#0 doc + PUNCT 42:Root[0000, 0]@0..0#0 = [alone] + LITER 42:Root[0000, 0]@75..130#0 Str / The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#0 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 + IDENT 42:Root[0000, 0]@137..143#0 helper + GROUP () 42:Root[0000, 0]@143..144#0 42:Root[0000, 0]@156..157#0 42:Root[0000, 0]@143..157#0 + IDENT 42:Root[0000, 0]@144..150#0 setter + GROUP () 42:Root[0000, 0]@150..151#0 42:Root[0000, 0]@155..156#0 42:Root[0000, 0]@150..156#0 + IDENT 42:Root[0000, 0]@151..155#0 into + IDENT 42:Root[0000, 0]@163..166#0 pub + GROUP () 42:Root[0000, 0]@166..167#0 42:Root[0000, 0]@172..173#0 42:Root[0000, 0]@166..173#0 + IDENT 42:Root[0000, 0]@167..172#0 crate + IDENT 42:Root[0000, 0]@174..180#0 domain + PUNCT 42:Root[0000, 0]@180..181#0 : [alone] + IDENT 42:Root[0000, 0]@182..188#0 String + PUNCT 42:Root[0000, 0]@188..189#0 , [alone] + + + PUNCT 42:Root[0000, 0]@1..2#0 # [joint] + GROUP [] 42:Root[0000, 0]@2..3#0 42:Root[0000, 0]@52..53#0 42:Root[0000, 0]@2..53#0 + IDENT 42:Root[0000, 0]@3..9#0 helper + GROUP () 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@51..52#0 42:Root[0000, 0]@9..52#0 + IDENT 42:Root[0000, 0]@10..18#0 build_fn + GROUP () 42:Root[0000, 0]@18..19#0 42:Root[0000, 0]@50..51#0 42:Root[0000, 0]@18..51#0 + IDENT 42:Root[0000, 0]@19..26#0 private + PUNCT 42:Root[0000, 0]@26..27#0 , [alone] + IDENT 42:Root[0000, 0]@28..32#0 name + PUNCT 42:Root[0000, 0]@33..34#0 = [alone] + LITER 42:Root[0000, 0]@35..50#0 Str partial_build + IDENT 42:Root[0000, 0]@54..57#0 pub + IDENT 42:Root[0000, 0]@58..64#0 struct + IDENT 42:Root[0000, 0]@65..68#0 Foo + GROUP {} 42:Root[0000, 0]@69..70#0 42:Root[0000, 0]@190..191#0 42:Root[0000, 0]@69..191#0 + PUNCT 42:Root[0000, 0]@0..0#0 # [alone] + GROUP [] 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 42:Root[0000, 0]@0..0#0 + IDENT 42:Root[0000, 0]@0..0#0 doc + PUNCT 42:Root[0000, 0]@0..0#0 = [alone] + LITER 42:Root[0000, 0]@75..130#0 Str / The domain where this federated instance is running + PUNCT 42:Root[0000, 0]@135..136#0 # [joint] + GROUP [] 42:Root[0000, 0]@136..137#0 42:Root[0000, 0]@157..158#0 42:Root[0000, 0]@136..158#0 + IDENT 42:Root[0000, 0]@137..143#0 helper + GROUP () 42:Root[0000, 0]@143..144#0 42:Root[0000, 0]@156..157#0 42:Root[0000, 0]@143..157#0 + IDENT 42:Root[0000, 0]@144..150#0 setter + GROUP () 42:Root[0000, 0]@150..151#0 42:Root[0000, 0]@155..156#0 42:Root[0000, 0]@150..156#0 + IDENT 42:Root[0000, 0]@151..155#0 into + IDENT 42:Root[0000, 0]@163..166#0 pub + GROUP () 42:Root[0000, 0]@166..167#0 42:Root[0000, 0]@172..173#0 42:Root[0000, 0]@166..173#0 + IDENT 42:Root[0000, 0]@167..172#0 crate + IDENT 42:Root[0000, 0]@174..180#0 domain + PUNCT 42:Root[0000, 0]@180..181#0 : [alone] + IDENT 42:Root[0000, 0]@182..188#0 String + PUNCT 42:Root[0000, 0]@188..189#0 , [alone] "#]], ); } @@ -232,19 +232,19 @@ fn test_derive_error() { PUNCT 1 ; [alone] "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..6#ROOT2024 struct - IDENT 42:Root[0000, 0]@7..8#ROOT2024 S - GROUP {} 42:Root[0000, 0]@9..10#ROOT2024 42:Root[0000, 0]@22..23#ROOT2024 42:Root[0000, 0]@9..23#ROOT2024 - IDENT 42:Root[0000, 0]@11..16#ROOT2024 field - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 : [alone] - IDENT 42:Root[0000, 0]@18..21#ROOT2024 u32 - - - IDENT 42:Root[0000, 0]@0..13#ROOT2024 compile_error - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 ! [joint] - GROUP () 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@62..63#ROOT2024 42:Root[0000, 0]@14..63#ROOT2024 - LITER 42:Root[0000, 0]@15..62#ROOT2024 Str #[derive(DeriveError)] struct S {field : u32} - PUNCT 42:Root[0000, 0]@63..64#ROOT2024 ; [alone] + IDENT 42:Root[0000, 0]@0..6#0 struct + IDENT 42:Root[0000, 0]@7..8#0 S + GROUP {} 42:Root[0000, 0]@9..10#0 42:Root[0000, 0]@22..23#0 42:Root[0000, 0]@9..23#0 + IDENT 42:Root[0000, 0]@11..16#0 field + PUNCT 42:Root[0000, 0]@16..17#0 : [alone] + IDENT 42:Root[0000, 0]@18..21#0 u32 + + + IDENT 42:Root[0000, 0]@0..13#0 compile_error + PUNCT 42:Root[0000, 0]@13..14#0 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@62..63#0 42:Root[0000, 0]@14..63#0 + LITER 42:Root[0000, 0]@15..62#0 Str #[derive(DeriveError)] struct S {field : u32} + PUNCT 42:Root[0000, 0]@63..64#0 ; [alone] "#]], ); } @@ -273,22 +273,22 @@ fn test_fn_like_macro_noop() { GROUP [] 1 1 1 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@7..8#ROOT2024 Integer 0 - PUNCT 42:Root[0000, 0]@8..9#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@10..11#ROOT2024 Integer 1 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@13..15#ROOT2024 - - - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@7..8#ROOT2024 Integer 0 - PUNCT 42:Root[0000, 0]@8..9#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@10..11#ROOT2024 Integer 1 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@13..15#ROOT2024 + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + LITER 42:Root[0000, 0]@7..8#0 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#0 , [alone] + LITER 42:Root[0000, 0]@10..11#0 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#0 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@13..15#0 + + + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + LITER 42:Root[0000, 0]@7..8#0 Integer 0 + PUNCT 42:Root[0000, 0]@8..9#0 , [alone] + LITER 42:Root[0000, 0]@10..11#0 Integer 1 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + GROUP [] 42:Root[0000, 0]@13..14#0 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@13..15#0 "#]], ); } @@ -315,20 +315,20 @@ fn test_fn_like_macro_clone_ident_subtree() { IDENT 1 ident3 "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@7..8#ROOT2024 42:Root[0000, 0]@22..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 - IDENT 42:Root[0000, 0]@8..14#ROOT2024 ident2 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@16..22#ROOT2024 ident3 - - - IDENT 42:Root[0000, 0]@0..5#ROOT2024 ident - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - GROUP [] 42:Root[0000, 0]@7..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 42:Root[0000, 0]@7..23#ROOT2024 - IDENT 42:Root[0000, 0]@8..14#ROOT2024 ident2 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - IDENT 42:Root[0000, 0]@16..22#ROOT2024 ident3 + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + GROUP [] 42:Root[0000, 0]@7..8#0 42:Root[0000, 0]@22..23#0 42:Root[0000, 0]@7..23#0 + IDENT 42:Root[0000, 0]@8..14#0 ident2 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + IDENT 42:Root[0000, 0]@16..22#0 ident3 + + + IDENT 42:Root[0000, 0]@0..5#0 ident + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + GROUP [] 42:Root[0000, 0]@7..23#0 42:Root[0000, 0]@7..23#0 42:Root[0000, 0]@7..23#0 + IDENT 42:Root[0000, 0]@8..14#0 ident2 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + IDENT 42:Root[0000, 0]@16..22#0 ident3 "#]], ); } @@ -345,10 +345,10 @@ fn test_fn_like_macro_clone_raw_ident() { IDENT 1 r#async "#]], expect![[r#" - IDENT 42:Root[0000, 0]@2..7#ROOT2024 r#async + IDENT 42:Root[0000, 0]@2..7#0 r#async - IDENT 42:Root[0000, 0]@2..7#ROOT2024 r#async + IDENT 42:Root[0000, 0]@2..7#0 r#async "#]], ); } @@ -366,11 +366,11 @@ fn test_fn_like_fn_like_span_join() { IDENT 1 r#joined "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..3#ROOT2024 foo - IDENT 42:Root[0000, 0]@8..11#ROOT2024 bar + IDENT 42:Root[0000, 0]@0..3#0 foo + IDENT 42:Root[0000, 0]@8..11#0 bar - IDENT 42:Root[0000, 0]@0..11#ROOT2024 r#joined + IDENT 42:Root[0000, 0]@0..11#0 r#joined "#]], ); } @@ -391,14 +391,14 @@ fn test_fn_like_fn_like_span_ops() { IDENT 1 start_span "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..12#ROOT2024 set_def_site - IDENT 42:Root[0000, 0]@13..33#ROOT2024 resolved_at_def_site - IDENT 42:Root[0000, 0]@34..44#ROOT2024 start_span + IDENT 42:Root[0000, 0]@0..12#0 set_def_site + IDENT 42:Root[0000, 0]@13..33#0 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..44#0 start_span - IDENT 41:Root[0000, 0]@0..150#ROOT2024 set_def_site - IDENT 42:Root[0000, 0]@13..33#ROOT2024 resolved_at_def_site - IDENT 42:Root[0000, 0]@34..34#ROOT2024 start_span + IDENT 41:Root[0000, 0]@0..150#0 set_def_site + IDENT 42:Root[0000, 0]@13..33#0 resolved_at_def_site + IDENT 42:Root[0000, 0]@34..34#0 start_span "#]], ); } @@ -428,19 +428,19 @@ fn test_fn_like_mk_literals() { expect![[r#" - LITER 42:Root[0000, 0]@0..100#ROOT2024 ByteStr byte_string - LITER 42:Root[0000, 0]@0..100#ROOT2024 Char c - LITER 42:Root[0000, 0]@0..100#ROOT2024 Str string - LITER 42:Root[0000, 0]@0..100#ROOT2024 Str -string - LITER 42:Root[0000, 0]@0..100#ROOT2024 CStr cstring - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float 3.14f64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float -3.14f64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float 3.14 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Float -3.14 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 123i64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer -123i64 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 123 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer -123 + LITER 42:Root[0000, 0]@0..100#0 ByteStr byte_string + LITER 42:Root[0000, 0]@0..100#0 Char c + LITER 42:Root[0000, 0]@0..100#0 Str string + LITER 42:Root[0000, 0]@0..100#0 Str -string + LITER 42:Root[0000, 0]@0..100#0 CStr cstring + LITER 42:Root[0000, 0]@0..100#0 Float 3.14f64 + LITER 42:Root[0000, 0]@0..100#0 Float -3.14f64 + LITER 42:Root[0000, 0]@0..100#0 Float 3.14 + LITER 42:Root[0000, 0]@0..100#0 Float -3.14 + LITER 42:Root[0000, 0]@0..100#0 Integer 123i64 + LITER 42:Root[0000, 0]@0..100#0 Integer -123i64 + LITER 42:Root[0000, 0]@0..100#0 Integer 123 + LITER 42:Root[0000, 0]@0..100#0 Integer -123 "#]], ); } @@ -459,8 +459,8 @@ fn test_fn_like_mk_idents() { expect![[r#" - IDENT 42:Root[0000, 0]@0..100#ROOT2024 standard - IDENT 42:Root[0000, 0]@0..100#ROOT2024 r#raw + IDENT 42:Root[0000, 0]@0..100#0 standard + IDENT 42:Root[0000, 0]@0..100#0 r#raw "#]], ); } @@ -515,48 +515,48 @@ fn test_fn_like_macro_clone_literals() { LITER 1 CStr null "#]], expect![[r#" - LITER 42:Root[0000, 0]@0..4#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@4..5#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@6..11#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@14..18#ROOT2024 Integer 4i64 - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@20..27#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@27..28#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@29..43#ROOT2024 Str hello bridge - PUNCT 42:Root[0000, 0]@43..44#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@45..61#ROOT2024 Str suffixedsuffix - PUNCT 42:Root[0000, 0]@61..62#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@63..73#ROOT2024 StrRaw(2) raw - PUNCT 42:Root[0000, 0]@73..74#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@75..78#ROOT2024 Char a - PUNCT 42:Root[0000, 0]@78..79#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@80..84#ROOT2024 Byte b - PUNCT 42:Root[0000, 0]@84..85#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@86..93#ROOT2024 CStr null - - - LITER 42:Root[0000, 0]@0..4#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@4..5#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@6..11#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@11..12#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@14..18#ROOT2024 Integer 4i64 - PUNCT 42:Root[0000, 0]@18..19#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@20..27#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@27..28#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@29..43#ROOT2024 Str hello bridge - PUNCT 42:Root[0000, 0]@43..44#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@45..61#ROOT2024 Str suffixedsuffix - PUNCT 42:Root[0000, 0]@61..62#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@63..73#ROOT2024 StrRaw(2) raw - PUNCT 42:Root[0000, 0]@73..74#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@75..78#ROOT2024 Char a - PUNCT 42:Root[0000, 0]@78..79#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@80..84#ROOT2024 Byte b - PUNCT 42:Root[0000, 0]@84..85#ROOT2024 , [alone] - LITER 42:Root[0000, 0]@86..93#ROOT2024 CStr null + LITER 42:Root[0000, 0]@0..4#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#0 , [alone] + LITER 42:Root[0000, 0]@6..11#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + PUNCT 42:Root[0000, 0]@13..14#0 - [alone] + LITER 42:Root[0000, 0]@14..18#0 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#0 , [alone] + LITER 42:Root[0000, 0]@20..27#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#0 , [alone] + LITER 42:Root[0000, 0]@29..43#0 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#0 , [alone] + LITER 42:Root[0000, 0]@45..61#0 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#0 , [alone] + LITER 42:Root[0000, 0]@63..73#0 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#0 , [alone] + LITER 42:Root[0000, 0]@75..78#0 Char a + PUNCT 42:Root[0000, 0]@78..79#0 , [alone] + LITER 42:Root[0000, 0]@80..84#0 Byte b + PUNCT 42:Root[0000, 0]@84..85#0 , [alone] + LITER 42:Root[0000, 0]@86..93#0 CStr null + + + LITER 42:Root[0000, 0]@0..4#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@4..5#0 , [alone] + LITER 42:Root[0000, 0]@6..11#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@11..12#0 , [alone] + PUNCT 42:Root[0000, 0]@13..14#0 - [alone] + LITER 42:Root[0000, 0]@14..18#0 Integer 4i64 + PUNCT 42:Root[0000, 0]@18..19#0 , [alone] + LITER 42:Root[0000, 0]@20..27#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@27..28#0 , [alone] + LITER 42:Root[0000, 0]@29..43#0 Str hello bridge + PUNCT 42:Root[0000, 0]@43..44#0 , [alone] + LITER 42:Root[0000, 0]@45..61#0 Str suffixedsuffix + PUNCT 42:Root[0000, 0]@61..62#0 , [alone] + LITER 42:Root[0000, 0]@63..73#0 StrRaw(2) raw + PUNCT 42:Root[0000, 0]@73..74#0 , [alone] + LITER 42:Root[0000, 0]@75..78#0 Char a + PUNCT 42:Root[0000, 0]@78..79#0 , [alone] + LITER 42:Root[0000, 0]@80..84#0 Byte b + PUNCT 42:Root[0000, 0]@84..85#0 , [alone] + LITER 42:Root[0000, 0]@86..93#0 CStr null "#]], ); } @@ -593,30 +593,30 @@ fn test_fn_like_macro_negative_literals() { LITER 1 Float 2.7 "#]], expect![[r#" - PUNCT 42:Root[0000, 0]@0..1#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@1..5#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@7..8#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@9..14#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@17..24#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@24..25#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@28..31#ROOT2024 Float 2.7 - - - PUNCT 42:Root[0000, 0]@0..1#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@1..5#ROOT2024 Integer 1u16 - PUNCT 42:Root[0000, 0]@5..6#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@7..8#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@9..14#ROOT2024 Integer 2_u32 - PUNCT 42:Root[0000, 0]@14..15#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@16..17#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@17..24#ROOT2024 Float 3.14f32 - PUNCT 42:Root[0000, 0]@24..25#ROOT2024 , [alone] - PUNCT 42:Root[0000, 0]@26..27#ROOT2024 - [alone] - LITER 42:Root[0000, 0]@28..31#ROOT2024 Float 2.7 + PUNCT 42:Root[0000, 0]@0..1#0 - [alone] + LITER 42:Root[0000, 0]@1..5#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + PUNCT 42:Root[0000, 0]@7..8#0 - [alone] + LITER 42:Root[0000, 0]@9..14#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + PUNCT 42:Root[0000, 0]@16..17#0 - [alone] + LITER 42:Root[0000, 0]@17..24#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#0 , [alone] + PUNCT 42:Root[0000, 0]@26..27#0 - [alone] + LITER 42:Root[0000, 0]@28..31#0 Float 2.7 + + + PUNCT 42:Root[0000, 0]@0..1#0 - [alone] + LITER 42:Root[0000, 0]@1..5#0 Integer 1u16 + PUNCT 42:Root[0000, 0]@5..6#0 , [alone] + PUNCT 42:Root[0000, 0]@7..8#0 - [alone] + LITER 42:Root[0000, 0]@9..14#0 Integer 2_u32 + PUNCT 42:Root[0000, 0]@14..15#0 , [alone] + PUNCT 42:Root[0000, 0]@16..17#0 - [alone] + LITER 42:Root[0000, 0]@17..24#0 Float 3.14f32 + PUNCT 42:Root[0000, 0]@24..25#0 , [alone] + PUNCT 42:Root[0000, 0]@26..27#0 - [alone] + LITER 42:Root[0000, 0]@28..31#0 Float 2.7 "#]], ); } @@ -647,20 +647,20 @@ fn test_attr_macro() { PUNCT 1 ; [alone] "#]], expect![[r#" - IDENT 42:Root[0000, 0]@0..3#ROOT2024 mod - IDENT 42:Root[0000, 0]@4..5#ROOT2024 m - GROUP {} 42:Root[0000, 0]@6..7#ROOT2024 42:Root[0000, 0]@7..8#ROOT2024 42:Root[0000, 0]@6..8#ROOT2024 + IDENT 42:Root[0000, 0]@0..3#0 mod + IDENT 42:Root[0000, 0]@4..5#0 m + GROUP {} 42:Root[0000, 0]@6..7#0 42:Root[0000, 0]@7..8#0 42:Root[0000, 0]@6..8#0 - IDENT 42:Root[0000, 0]@0..4#ROOT2024 some - IDENT 42:Root[0000, 0]@5..14#ROOT2024 arguments + IDENT 42:Root[0000, 0]@0..4#0 some + IDENT 42:Root[0000, 0]@5..14#0 arguments - IDENT 42:Root[0000, 0]@0..13#ROOT2024 compile_error - PUNCT 42:Root[0000, 0]@13..14#ROOT2024 ! [joint] - GROUP () 42:Root[0000, 0]@14..15#ROOT2024 42:Root[0000, 0]@55..56#ROOT2024 42:Root[0000, 0]@14..56#ROOT2024 - LITER 42:Root[0000, 0]@15..55#ROOT2024 Str #[attr_error(some arguments)] mod m {} - PUNCT 42:Root[0000, 0]@56..57#ROOT2024 ; [alone] + IDENT 42:Root[0000, 0]@0..13#0 compile_error + PUNCT 42:Root[0000, 0]@13..14#0 ! [joint] + GROUP () 42:Root[0000, 0]@14..15#0 42:Root[0000, 0]@55..56#0 42:Root[0000, 0]@14..56#0 + LITER 42:Root[0000, 0]@15..55#0 Str #[attr_error(some arguments)] mod m {} + PUNCT 42:Root[0000, 0]@56..57#0 ; [alone] "#]], ); } @@ -722,8 +722,8 @@ fn test_fn_like_span_line_column() { " hello", expect![[r#" - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 2 - LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 1 + LITER 42:Root[0000, 0]@0..100#0 Integer 2 + LITER 42:Root[0000, 0]@0..100#0 Integer 1 "#]], ); } 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 9da4d90d65768..394cbefc12b2b 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 @@ -86,7 +86,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: SyntaxContext::from_u32_safe(0), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -94,7 +94,7 @@ fn assert_expand_impl( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: SyntaxContext::from_u32_safe(0), }; let mixed_site = call_site; @@ -189,7 +189,7 @@ pub fn assert_expand_with_callback( file_id: EditionedFileId::current_edition(FileId::from_raw(41)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: SyntaxContext::from_u32_safe(0), }; let call_site = Span { range: TextRange::new(0.into(), 100.into()), @@ -197,7 +197,7 @@ pub fn assert_expand_with_callback( file_id: EditionedFileId::current_edition(FileId::from_raw(42)), ast_id: ROOT_ERASED_FILE_AST_ID, }, - ctx: SyntaxContext::root(span::Edition::CURRENT), + ctx: SyntaxContext::from_u32_safe(0), }; let mixed_site = call_site; diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 70b0447569bf4..2d9757b5ae9fa 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -460,6 +460,13 @@ impl SyntaxContext { pub const unsafe fn from_u32(u32: u32) -> Self { Self(u32) } + + /// Alternative to [`from_u32`] that is safe to call. + /// + /// The split exists to keep API parity. + pub const fn from_u32_safe(u32: u32) -> Self { + Self(u32) + } } /// A property of a macro expansion that determines how identifiers diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml index 6cfb76400e3cc..9a798b592d03c 100644 --- a/src/tools/rust-analyzer/crates/tt/Cargo.toml +++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml @@ -24,7 +24,6 @@ intern.workspace = true ra-ap-rustc_lexer.workspace = true [features] -default = [] in-rust-tree = [] [lints] From 55e1f1e220742dd2528ed170175160f8715e843f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 31 May 2026 23:34:25 +0300 Subject: [PATCH 108/114] Revert "Merge pull request rust-lang/rust-analyzer#22492 from Veykril/push-onsxmumorqlz" This reverts commit 17957d9d6f9fc6ccede4f8537a3bba59e5573e3c, reversing changes made to 219770cfb7d7ffacbdc41ef48e15c3c81e8b8764. --- src/tools/rust-analyzer/xtask/src/dist.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/dist.rs b/src/tools/rust-analyzer/xtask/src/dist.rs index 8017ff93e8ccd..e8bedbe79e56e 100644 --- a/src/tools/rust-analyzer/xtask/src/dist.rs +++ b/src/tools/rust-analyzer/xtask/src/dist.rs @@ -106,13 +106,16 @@ fn dist_server( dev_rel: bool, ) -> anyhow::Result<()> { let _e = sh.push_env("CFG_RELEASE", release); - let _e = sh.push_env("CARGO_PROFILE_RELEASE_DEBUG", "limited"); let _e = sh.push_env("CARGO_PROFILE_RELEASE_LTO", "thin"); let _e = sh.push_env("CARGO_PROFILE_RELEASE_CODEGEN_UNITS", "1"); - let _e = sh.push_env("CARGO_PROFILE_DEV_REL_DEBUG", "limited"); let _e = sh.push_env("CARGO_PROFILE_DEV_REL_LTO", "thin"); let _e = sh.push_env("CARGO_PROFILE_DEV_REL_CODEGEN_UNITS", "1"); + // Uncomment to enable debug info for releases. Note that: + // * debug info is split on windows and macs, so it does nothing for those platforms, + // * on Linux, this blows up the binary size from 8MB to 43MB, which is unreasonable. + // let _e = sh.push_env("CARGO_PROFILE_RELEASE_DEBUG", "1"); + let linux_target = target.is_linux(); let target_name = match &target.libc_suffix { Some(libc_suffix) if zig => format!("{}.{libc_suffix}", target.name), From e18287453d3669ff1b10e205d64ac693c5e6ccf7 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 1 Jun 2026 01:53:49 +0300 Subject: [PATCH 109/114] Try to improve completion ranking The motivation is https://github.com/rust-lang/rust-analyzer/issues/22455, but I also tinkered with other scores. It's really hard to have a good scoring. --- .../crates/ide-completion/src/item.rs | 58 +++++++++++-------- .../crates/ide-completion/src/render.rs | 8 +-- .../ide-completion/src/tests/expression.rs | 54 +++++++++++++++++ 3 files changed, 93 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index f54c8dd8b0fc0..61281f8cfb8f1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -304,19 +304,19 @@ impl CompletionRelevance { // only applicable for completions within use items // lower rank for conflicting import names if is_name_already_imported { - score -= 1; + score -= 15; } // slightly prefer locals if is_local { - score += 1; + score += 2; } if is_missing { - score += 1; + score += 2; } // lower rank private things if !is_private_editable { - score += 1; + score += 10; } if let Some(trait_) = trait_ { @@ -337,10 +337,10 @@ impl CompletionRelevance { // lower rank for items that need an import if requires_import { - score -= 1; + score -= 12; } if exact_name_match { - score += 20; + score += 40; } match postfix_match { Some(CompletionRelevancePostfixMatch::Exact) => score += 100, @@ -348,16 +348,26 @@ impl CompletionRelevance { None => (), }; score += match type_match { - Some(CompletionRelevanceTypeMatch::Exact) => 18, - Some(CompletionRelevanceTypeMatch::CouldUnify) => 5, + Some(CompletionRelevanceTypeMatch::Exact) => 35, + Some(CompletionRelevanceTypeMatch::CouldUnify) => 15, None => 0, }; if let Some(function) = function { - let mut fn_score = match function.return_type { - CompletionRelevanceReturnType::DirectConstructor => 15, - CompletionRelevanceReturnType::Builder => 10, - CompletionRelevanceReturnType::Constructor => 5, - CompletionRelevanceReturnType::Other => 0u32, + let mut fn_score = if requires_import { + // Rank constructors that require imports lower than those who don't. + match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 8, + CompletionRelevanceReturnType::Builder => 5, + CompletionRelevanceReturnType::Constructor => 3, + CompletionRelevanceReturnType::Other => 0u32, + } + } else { + match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 15, + CompletionRelevanceReturnType::Builder => 10, + CompletionRelevanceReturnType::Constructor => 5, + CompletionRelevanceReturnType::Other => 0u32, + } }; // When a fn is bumped due to return type: @@ -375,12 +385,12 @@ impl CompletionRelevance { }; if has_local_inherent_impl { - score -= 5; + score -= 8; } // lower rank for deprecated items if is_deprecated { - score -= 5; + score -= 15; } score @@ -831,15 +841,17 @@ mod tests { is_private_editable: true, ..default }], - vec![Cr { - trait_: Some(crate::item::CompletionRelevanceTraitInfo { - notable_trait: false, - is_op_method: true, - }), - ..default - }], + vec![ + Cr { + trait_: Some(crate::item::CompletionRelevanceTraitInfo { + notable_trait: false, + is_op_method: true, + }), + ..default + }, + Cr { is_private_editable: true, ..default }, + ], vec![Cr { postfix_match: Some(CompletionRelevancePostfixMatch::NonExact), ..default }], - vec![Cr { is_private_editable: true, ..default }], vec![default], vec![Cr { is_local: true, ..default }], vec![Cr { type_match: Some(CompletionRelevanceTypeMatch::CouldUnify), ..default }], diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index bb92aad1841ef..e48847c983b4e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -1071,12 +1071,12 @@ fn main() { "#, expect![[r#" ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type] - ev Variant Variant [type+requires_import] ex dep::test_mod_b::Enum::Variant [type] - ev Variant Variant [requires_import] + ev Variant Variant [type+requires_import] md dep:: [] fn main() fn() [] fn test(…) fn(Enum) [] + ev Variant Variant [requires_import] "#]], ); } @@ -3989,11 +3989,11 @@ fn foo() { } "#, expect![[r#" - ev Foo::B Foo::B [type_could_unify] - ev Foo::A(…) Foo::A(T) [type_could_unify] lc foo Foo [type+local] ex Foo::B [type] ex foo [type] + ev Foo::B Foo::B [type_could_unify] + ev Foo::A(…) Foo::A(T) [type_could_unify] en Foo Foo [type_could_unify] fn baz() fn() -> Foo [type_could_unify] fn bar() fn() -> Foo [] 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 3d4ad78c34334..e49afa66ad738 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 @@ -4055,3 +4055,57 @@ fn test(test: H) { expect![""], ); } + +#[test] +fn imported_enum_variant_has_lower_priority() { + check( + r#" +pub struct String {} +mod foo { + pub enum Foo { String } +} +fn main() { + Strin$0 +} + "#, + expect![[r#" + fn main() fn() + md foo:: + st String String + ev String (use foo::Foo::String) String + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw impl for + kw let + kw letm + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} From c4ab4a52f7a31c3c4265c1d4c631dbb42337679f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 26 May 2026 08:33:26 +0800 Subject: [PATCH 110/114] fix: use add deref in assign instead add `&mut` for value Example --- ```rust fn test(arg: &mut i32) { arg = $02; } ``` **Before this PR** ```rust fn test(arg: &mut i32) { arg = &mut 2; } ``` **After this PR** ```rust fn test(arg: &mut i32) { *arg = 2; } ``` --- .../src/handlers/type_mismatch.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) 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 f3cf823efe681..82f63aa66eb8a 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 @@ -120,6 +120,19 @@ fn add_or_fix_reference( return None; } + let expr = expr_ptr.to_node(ctx.db()); + let assign = expr.syntax().parent().and_then(ast::BinExpr::cast).filter(|it| { + it.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) && it.rhs() == Some(expr) + }); + if let Some(assign) = assign + && expected_mutability.is_mut() + && let Some(range) = ctx.sema.original_range_opt(assign.syntax()) + { + let edit = TextEdit::insert(range.range.start(), "*".to_owned()); + let source_change = SourceChange::from_text_edit(range.file_id.file_id(ctx.db()), edit); + acc.push(fix("add_deref_here", "Add deref here", source_change, range.range)); + } + let ampersands = format!("&{}", expected_mutability.as_keyword_for_ref()); let edit = TextEdit::insert(range.range.start(), ampersands); @@ -507,6 +520,22 @@ fn test(_arg: &mut i32) {} ); } + #[test] + fn add_deref_in_assign() { + check_fix( + r#" +fn test(arg: &mut i32) { + arg = $02; +} + "#, + r#" +fn test(arg: &mut i32) { + *arg = 2; +} + "#, + ); + } + #[test] fn add_reference_to_array() { check_fix( From f2baaffe96547e3bd1ab36a1dde6b07f1ce07c96 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 27 May 2026 19:38:24 +0800 Subject: [PATCH 111/114] Add missing early return --- .../crates/ide-diagnostics/src/handlers/type_mismatch.rs | 1 + 1 file changed, 1 insertion(+) 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 82f63aa66eb8a..c53208274920e 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 @@ -131,6 +131,7 @@ fn add_or_fix_reference( let edit = TextEdit::insert(range.range.start(), "*".to_owned()); let source_change = SourceChange::from_text_edit(range.file_id.file_id(ctx.db()), edit); acc.push(fix("add_deref_here", "Add deref here", source_change, range.range)); + return Some(()); } let ampersands = format!("&{}", expected_mutability.as_keyword_for_ref()); From 5ffa3d40d8fd8c1a4527c7113fad8b53379ae591 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 1 Jun 2026 07:09:50 +0800 Subject: [PATCH 112/114] Remove redundant filter Co-authored-by: Chayim Refael Friedman --- .../crates/ide-diagnostics/src/handlers/type_mismatch.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 c53208274920e..5a1856c89df3a 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 @@ -121,9 +121,11 @@ fn add_or_fix_reference( } let expr = expr_ptr.to_node(ctx.db()); - let assign = expr.syntax().parent().and_then(ast::BinExpr::cast).filter(|it| { - it.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) && it.rhs() == Some(expr) - }); + let assign = expr + .syntax() + .parent() + .and_then(ast::BinExpr::cast) + .filter(|it| it.op_kind() == Some(ast::BinaryOp::Assignment { op: None })); if let Some(assign) = assign && expected_mutability.is_mut() && let Some(range) = ctx.sema.original_range_opt(assign.syntax()) From e04ef6baef44d77b7d7dd05f92497dc423770e40 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 1 Jun 2026 08:55:01 +0200 Subject: [PATCH 113/114] Enable salsa feature for syntax-bridge --- src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml index c928f23e02e66..a539cea67f027 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml +++ b/src/tools/rust-analyzer/crates/syntax-bridge/Cargo.toml @@ -20,8 +20,7 @@ syntax.workspace = true parser.workspace = true tt.workspace = true stdx.workspace = true -# span = {workspace = true, default-features = false} does not work -span = { path = "../span", version = "0.0.0", default-features = false} +span.workspace = true intern.workspace = true [dev-dependencies] From 2035cde9c038af2588cf47538ded979a17be6ead Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 1 Jun 2026 10:54:59 +0200 Subject: [PATCH 114/114] Kill proc-macro-srv processes on shutdown --- src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml | 4 ++++ src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index c3cd447c85f17..4667542090430 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -36,7 +36,11 @@ proc-macro-test.path = "./proc-macro-test" [features] default = [] +# default = ["sysroot-abi"] sysroot-abi = [] [lints] workspace = true + +[package.metadata.rust-analyzer] +rustc_private=true 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 5ed522ceee4cf..92d5323e96804 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 @@ -1267,6 +1267,10 @@ impl GlobalState { let mut dispatcher = RequestDispatcher { req: Some(req), global_state: self }; dispatcher.on_sync_mut::(|s, ()| { s.shutdown_requested = true; + s.proc_macro_clients = + std::iter::repeat_with(|| None).take(s.proc_macro_clients.len()).collect(); + s.flycheck.iter().for_each(|handle| handle.cancel()); + s.discover_handles.clear(); Ok(()) });