From ad0d25a81fe7b6459dc9e316fce4b2eb34edd62b Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Wed, 24 Dec 2025 15:04:08 +0900 Subject: [PATCH 01/34] rename the `lower_ty` and `lower_ty_direct` to `lower_ty_and_alloc` and `lower_ty` --- compiler/rustc_ast_lowering/src/block.rs | 2 +- compiler/rustc_ast_lowering/src/expr.rs | 17 ++++---- compiler/rustc_ast_lowering/src/item.rs | 51 ++++++++++++++---------- compiler/rustc_ast_lowering/src/lib.rs | 38 +++++++++--------- compiler/rustc_ast_lowering/src/path.rs | 15 +++---- 5 files changed, 70 insertions(+), 53 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 5bfe63008516c..cf3f331a701b4 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -98,7 +98,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Let statements are allowed to have impl trait in bindings. let super_ = l.super_.map(|span| self.lower_span(span)); let ty = l.ty.as_ref().map(|t| { - self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable)) + self.lower_ty_alloc(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable)) }); let init = l.kind.init().map(|init| self.lower_expr(init)); let hir_id = self.lower_node_id(l.id); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 0502fd2873e92..9fbfeb7a11e6e 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -158,14 +158,14 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::Cast(expr, ty) => { let expr = self.lower_expr(expr); - let ty = - self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast)); + let ty = self + .lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast)); hir::ExprKind::Cast(expr, ty) } ExprKind::Type(expr, ty) => { let expr = self.lower_expr(expr); - let ty = - self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast)); + let ty = self + .lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast)); hir::ExprKind::Type(expr, ty) } ExprKind::AddrOf(k, m, ohs) => { @@ -335,7 +335,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::FormatArgs(fmt) => self.lower_format_args(e.span, fmt), ExprKind::OffsetOf(container, fields) => hir::ExprKind::OffsetOf( - self.lower_ty( + self.lower_ty_alloc( container, ImplTraitContext::Disallowed(ImplTraitPosition::OffsetOf), ), @@ -371,7 +371,10 @@ impl<'hir> LoweringContext<'_, 'hir> { *kind, self.lower_expr(expr), ty.as_ref().map(|ty| { - self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Cast)) + self.lower_ty_alloc( + ty, + ImplTraitContext::Disallowed(ImplTraitPosition::Cast), + ) }), ), @@ -617,7 +620,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }); if let Some(ty) = opt_ty { - let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path)); + let ty = self.lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path)); let block_expr = self.arena.alloc(self.expr_block(whole_block)); hir::ExprKind::Type(block_expr, ty) } else { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index bfce7c25b75db..f8b98a5ece40e 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -264,8 +264,8 @@ impl<'hir> LoweringContext<'_, 'hir> { define_opaque, }) => { let ident = self.lower_ident(*ident); - let ty = - self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); + let ty = self + .lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); let body_id = self.lower_const_body(span, e.as_deref()); self.lower_define_opaque(hir_id, define_opaque); hir::ItemKind::Static(*m, ident, ty, body_id) @@ -279,8 +279,10 @@ impl<'hir> LoweringContext<'_, 'hir> { id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { - let ty = this - .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy)); + let ty = this.lower_ty_alloc( + ty, + ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy), + ); let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), span); (ty, rhs) }, @@ -379,7 +381,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ); this.arena.alloc(this.ty(span, hir::TyKind::Err(guar))) } - Some(ty) => this.lower_ty( + Some(ty) => this.lower_ty_alloc( ty, ImplTraitContext::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias { @@ -453,7 +455,7 @@ impl<'hir> LoweringContext<'_, 'hir> { .as_deref() .map(|of_trait| this.lower_trait_impl_header(of_trait)); - let lowered_ty = this.lower_ty( + let lowered_ty = this.lower_ty_alloc( ty, ImplTraitContext::Disallowed(ImplTraitPosition::ImplSelf), ); @@ -758,8 +760,8 @@ impl<'hir> LoweringContext<'_, 'hir> { safety, define_opaque, }) => { - let ty = - self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); + let ty = self + .lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::StaticTy)); let safety = self.lower_safety(*safety, hir::Safety::Unsafe); if define_opaque.is_some() { self.dcx().span_err(i.span, "foreign statics cannot define opaque types"); @@ -870,7 +872,8 @@ impl<'hir> LoweringContext<'_, 'hir> { &mut self, (index, f): (usize, &FieldDef), ) -> hir::FieldDef<'hir> { - let ty = self.lower_ty(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy)); + let ty = + self.lower_ty_alloc(&f.ty, ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy)); let hir_id = self.lower_node_id(f.id); self.lower_attrs(hir_id, &f.attrs, f.span, Target::Field); hir::FieldDef { @@ -908,8 +911,10 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { - let ty = this - .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy)); + let ty = this.lower_ty_alloc( + ty, + ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy), + ); let rhs = rhs .as_ref() .map(|rhs| this.lower_const_item_rhs(attrs, Some(rhs), i.span)); @@ -1008,7 +1013,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { let ty = ty.as_ref().map(|x| { - this.lower_ty( + this.lower_ty_alloc( x, ImplTraitContext::Disallowed(ImplTraitPosition::AssocTy), ) @@ -1120,8 +1125,10 @@ impl<'hir> LoweringContext<'_, 'hir> { i.id, ImplTraitContext::Disallowed(ImplTraitPosition::Generic), |this| { - let ty = this - .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy)); + let ty = this.lower_ty_alloc( + ty, + ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy), + ); this.lower_define_opaque(hir_id, &define_opaque); let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), i.span); hir::ImplItemKind::Const(ty, rhs) @@ -1180,7 +1187,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ImplItemKind::Type(ty) } Some(ty) => { - let ty = this.lower_ty( + let ty = this.lower_ty_alloc( ty, ImplTraitContext::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias { @@ -1916,7 +1923,7 @@ impl<'hir> LoweringContext<'_, 'hir> { bound_generic_params, hir::GenericParamSource::Binder, ), - bounded_ty: self.lower_ty( + bounded_ty: self.lower_ty_alloc( bounded_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound), ), @@ -1945,10 +1952,14 @@ impl<'hir> LoweringContext<'_, 'hir> { } WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => { hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate { - lhs_ty: self - .lower_ty(lhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)), - rhs_ty: self - .lower_ty(rhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)), + lhs_ty: self.lower_ty_alloc( + lhs_ty, + ImplTraitContext::Disallowed(ImplTraitPosition::Bound), + ), + rhs_ty: self.lower_ty_alloc( + rhs_ty, + ImplTraitContext::Disallowed(ImplTraitPosition::Bound), + ), }) } }); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 416fef8e3af38..25acbb9e0b304 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1125,7 +1125,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let kind = match &constraint.kind { AssocItemConstraintKind::Equality { term } => { let term = match term { - Term::Ty(ty) => self.lower_ty(ty, itctx).into(), + Term::Ty(ty) => self.lower_ty_alloc(ty, itctx).into(), Term::Const(c) => self.lower_anon_const_to_const_arg(c).into(), }; hir::AssocItemConstraintKind::Equality { term } @@ -1250,7 +1250,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } _ => {} } - GenericArg::Type(self.lower_ty(ty, itctx).try_as_ambig_ty().unwrap()) + GenericArg::Type(self.lower_ty_alloc(ty, itctx).try_as_ambig_ty().unwrap()) } ast::GenericArg::Const(ct) => { GenericArg::Const(self.lower_anon_const_to_const_arg(ct).try_as_ambig_ct().unwrap()) @@ -1259,8 +1259,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } #[instrument(level = "debug", skip(self))] - fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> &'hir hir::Ty<'hir> { - self.arena.alloc(self.lower_ty_direct(t, itctx)) + fn lower_ty_alloc(&mut self, t: &Ty, itctx: ImplTraitContext) -> &'hir hir::Ty<'hir> { + self.arena.alloc(self.lower_ty(t, itctx)) } fn lower_path_ty( @@ -1324,11 +1324,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.ty(span, hir::TyKind::Tup(tys)) } - fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> { + fn lower_ty(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> { let kind = match &t.kind { TyKind::Infer => hir::TyKind::Infer(()), TyKind::Err(guar) => hir::TyKind::Err(*guar), - TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), + TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty_alloc(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { let lifetime = self.lower_ty_direct_lifetime(t, *region); @@ -1362,15 +1362,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let generic_params = self.lower_lifetime_binder(t.id, &f.generic_params); hir::TyKind::UnsafeBinder(self.arena.alloc(hir::UnsafeBinderTy { generic_params, - inner_ty: self.lower_ty(&f.inner_ty, itctx), + inner_ty: self.lower_ty_alloc(&f.inner_ty, itctx), })) } TyKind::Never => hir::TyKind::Never, TyKind::Tup(tys) => hir::TyKind::Tup( - self.arena.alloc_from_iter(tys.iter().map(|ty| self.lower_ty_direct(ty, itctx))), + self.arena.alloc_from_iter(tys.iter().map(|ty| self.lower_ty(ty, itctx))), ), TyKind::Paren(ty) => { - return self.lower_ty_direct(ty, itctx); + return self.lower_ty(ty, itctx); } TyKind::Path(qself, path) => { return self.lower_path_ty(t, qself, path, ParamMode::Explicit, itctx); @@ -1393,7 +1393,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { )) } TyKind::Array(ty, length) => hir::TyKind::Array( - self.lower_ty(ty, itctx), + self.lower_ty_alloc(ty, itctx), self.lower_array_length_to_const_arg(length), ), TyKind::TraitObject(bounds, kind) => { @@ -1493,7 +1493,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } TyKind::Pat(ty, pat) => { - hir::TyKind::Pat(self.lower_ty(ty, itctx), self.lower_ty_pat(pat, ty.span)) + hir::TyKind::Pat(self.lower_ty_alloc(ty, itctx), self.lower_ty_pat(pat, ty.span)) } TyKind::MacCall(_) => { span_bug!(t.span, "`TyKind::MacCall` should have been expanded by now") @@ -1693,7 +1693,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::PointerParam) } }; - self.lower_ty_direct(¶m.ty, itctx) + self.lower_ty(¶m.ty, itctx) })); let output = match coro { @@ -1732,7 +1732,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::PointerReturn) } }; - hir::FnRetTy::Return(self.lower_ty(ty, itctx)) + hir::FnRetTy::Return(self.lower_ty_alloc(ty, itctx)) } FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)), }, @@ -1843,7 +1843,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // Not `OpaqueTyOrigin::AsyncFn`: that's only used for the // `impl Future` opaque type that `async fn` implicitly // generates. - self.lower_ty(ty, itctx) + self.lower_ty_alloc(ty, itctx) } FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])), }; @@ -2036,7 +2036,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } }) .map(|def| { - self.lower_ty( + self.lower_ty_alloc( def, ImplTraitContext::Disallowed(ImplTraitPosition::GenericDefault), ) @@ -2047,8 +2047,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (hir::ParamName::Plain(self.lower_ident(param.ident)), kind) } GenericParamKind::Const { ty, span: _, default } => { - let ty = self - .lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::GenericDefault)); + let ty = self.lower_ty_alloc( + ty, + ImplTraitContext::Disallowed(ImplTraitPosition::GenericDefault), + ); // Not only do we deny const param defaults in binders but we also map them to `None` // since later compiler stages cannot handle them (and shouldn't need to be able to). @@ -2198,7 +2200,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } fn lower_mt(&mut self, mt: &MutTy, itctx: ImplTraitContext) -> hir::MutTy<'hir> { - hir::MutTy { ty: self.lower_ty(&mt.ty, itctx), mutbl: mt.mutbl } + hir::MutTy { ty: self.lower_ty_alloc(&mt.ty, itctx), mutbl: mt.mutbl } } #[instrument(level = "debug", skip(self), ret)] diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 3322e0fb66b44..bfb42efb684f1 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -36,7 +36,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let qself = qself .as_ref() // Reject cases like `::Assoc` and `::Assoc`. - .map(|q| self.lower_ty(&q.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path))); + .map(|q| { + self.lower_ty_alloc(&q.ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path)) + }); let partial_res = self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err)); @@ -510,7 +512,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // we generally don't permit such things (see #51008). let ParenthesizedArgs { span, inputs, inputs_span, output } = data; let inputs = self.arena.alloc_from_iter(inputs.iter().map(|ty| { - self.lower_ty_direct(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam)) + self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitParam)) })); let output_ty = match output { // Only allow `impl Trait` in return position. i.e.: @@ -520,9 +522,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // ``` FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::OpaqueTy { .. }) => { if self.tcx.features().impl_trait_in_fn_trait_return() { - self.lower_ty(ty, itctx) + self.lower_ty_alloc(ty, itctx) } else { - self.lower_ty( + self.lower_ty_alloc( ty, ImplTraitContext::FeatureGated( ImplTraitPosition::FnTraitReturn, @@ -531,9 +533,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } } - FnRetTy::Ty(ty) => { - self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn)) - } + FnRetTy::Ty(ty) => self + .lower_ty_alloc(ty, ImplTraitContext::Disallowed(ImplTraitPosition::FnTraitReturn)), FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])), }; let args = smallvec![GenericArg::Type( From 598d01b2d9dbad4e51b1a4bcd42ca061f29cc564 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Wed, 24 Dec 2025 15:07:52 +0900 Subject: [PATCH 02/34] rename the `lower_anon_const_to_const_arg` and `lower_anon_const_to_const_arg_direct` to `lower_anon_const_to_const_arg_and_alloc` and `lower_anon_const_to_const_arg` --- compiler/rustc_ast_lowering/src/lib.rs | 25 ++++++----- compiler/rustc_ast_lowering/src/pat.rs | 59 +++++++++++++------------- 2 files changed, 44 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 25acbb9e0b304..0ca88bc547de7 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1126,7 +1126,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { AssocItemConstraintKind::Equality { term } => { let term = match term { Term::Ty(ty) => self.lower_ty_alloc(ty, itctx).into(), - Term::Const(c) => self.lower_anon_const_to_const_arg(c).into(), + Term::Const(c) => self.lower_anon_const_to_const_arg_alloc(c).into(), }; hir::AssocItemConstraintKind::Equality { term } } @@ -1252,9 +1252,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } GenericArg::Type(self.lower_ty_alloc(ty, itctx).try_as_ambig_ty().unwrap()) } - ast::GenericArg::Const(ct) => { - GenericArg::Const(self.lower_anon_const_to_const_arg(ct).try_as_ambig_ct().unwrap()) - } + ast::GenericArg::Const(ct) => GenericArg::Const( + self.lower_anon_const_to_const_arg_alloc(ct).try_as_ambig_ct().unwrap(), + ), } } @@ -2066,7 +2066,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { false } }) - .map(|def| self.lower_anon_const_to_const_arg(def)); + .map(|def| self.lower_anon_const_to_const_arg_alloc(def)); ( hir::ParamName::Plain(self.lower_ident(param.ident)), @@ -2288,7 +2288,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span), ()); self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind }) } - _ => self.lower_anon_const_to_const_arg(c), + _ => self.lower_anon_const_to_const_arg_alloc(c), } } @@ -2367,7 +2367,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) -> hir::ConstItemRhs<'hir> { match rhs { Some(ConstItemRhs::TypeConst(anon)) => { - hir::ConstItemRhs::TypeConst(self.lower_anon_const_to_const_arg(anon)) + hir::ConstItemRhs::TypeConst(self.lower_anon_const_to_const_arg_alloc(anon)) } None if attr::contains_name(attrs, sym::type_const) => { let const_arg = ConstArg { @@ -2438,7 +2438,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let def_kind = self.tcx.def_kind(def_id); assert_eq!(DefKind::AnonConst, def_kind); - self.lower_anon_const_to_const_arg_direct(anon_const) + self.lower_anon_const_to_const_arg(anon_const) } else { self.lower_expr_to_const_arg_direct(&f.expr) }; @@ -2476,12 +2476,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// See [`hir::ConstArg`] for when to use this function vs /// [`Self::lower_anon_const_to_anon_const`]. - fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> &'hir hir::ConstArg<'hir> { - self.arena.alloc(self.lower_anon_const_to_const_arg_direct(anon)) + fn lower_anon_const_to_const_arg_alloc( + &mut self, + anon: &AnonConst, + ) -> &'hir hir::ConstArg<'hir> { + self.arena.alloc(self.lower_anon_const_to_const_arg(anon)) } #[instrument(level = "debug", skip(self))] - fn lower_anon_const_to_const_arg_direct(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> { + fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> { let tcx = self.tcx; // We cannot change parsing depending on feature gates available, diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 3571fd6523974..bc694c4ab509d 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -441,36 +441,37 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_ty_pat_mut(&mut self, pattern: &TyPat, base_type: Span) -> hir::TyPat<'hir> { // loop here to avoid recursion let pat_hir_id = self.lower_node_id(pattern.id); - let node = match &pattern.kind { - TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range( - e1.as_deref().map(|e| self.lower_anon_const_to_const_arg(e)).unwrap_or_else(|| { - self.lower_ty_pat_range_end( - hir::LangItem::RangeMin, - span.shrink_to_lo(), - base_type, - ) - }), - e2.as_deref() - .map(|e| match end { - RangeEnd::Included(..) => self.lower_anon_const_to_const_arg(e), - RangeEnd::Excluded => self.lower_excluded_range_end(e), - }) - .unwrap_or_else(|| { - self.lower_ty_pat_range_end( - hir::LangItem::RangeMax, - span.shrink_to_hi(), - base_type, - ) - }), - ), - TyPatKind::NotNull => hir::TyPatKind::NotNull, - TyPatKind::Or(variants) => { - hir::TyPatKind::Or(self.arena.alloc_from_iter( + let node = + match &pattern.kind { + TyPatKind::Range(e1, e2, Spanned { node: end, span }) => hir::TyPatKind::Range( + e1.as_deref() + .map(|e| self.lower_anon_const_to_const_arg_alloc(e)) + .unwrap_or_else(|| { + self.lower_ty_pat_range_end( + hir::LangItem::RangeMin, + span.shrink_to_lo(), + base_type, + ) + }), + e2.as_deref() + .map(|e| match end { + RangeEnd::Included(..) => self.lower_anon_const_to_const_arg_alloc(e), + RangeEnd::Excluded => self.lower_excluded_range_end(e), + }) + .unwrap_or_else(|| { + self.lower_ty_pat_range_end( + hir::LangItem::RangeMax, + span.shrink_to_hi(), + base_type, + ) + }), + ), + TyPatKind::NotNull => hir::TyPatKind::NotNull, + TyPatKind::Or(variants) => hir::TyPatKind::Or(self.arena.alloc_from_iter( variants.iter().map(|pat| self.lower_ty_pat_mut(pat, base_type)), - )) - } - TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar), - }; + )), + TyPatKind::Err(guar) => hir::TyPatKind::Err(*guar), + }; hir::TyPat { hir_id: pat_hir_id, kind: node, span: self.lower_span(pattern.span) } } From c516c284755130dac7d3232b7c40567a57cfb936 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 29 Dec 2025 06:45:17 +0800 Subject: [PATCH 03/34] Add useless prefix `try_into_` for suggest_name Example --- ```rust enum Foo { Num(i32) } impl Foo { fn try_into_num(self) -> Result { if let Self::Num(v) = self { Ok(v) } else { Err(self) } } } fn handle(foo: Foo) { foo.try_into_num().$0 } ``` **Before this PR** ```rust fn handle(foo: Foo) { if let Ok(${1:try_into_num}) = foo.try_into_num() { $0 } } ``` **After this PR** ```rust fn handle(foo: Foo) { if let Ok(${1:num}) = foo.try_into_num() { $0 } } ``` --- .../crates/ide-db/src/syntax_helpers/suggest_name.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 273328a8d2700..b8b9a7a768168 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 @@ -44,7 +44,7 @@ const SEQUENCE_TYPES: &[&str] = &["Vec", "VecDeque", "LinkedList"]; /// `vec.as_slice()` -> `slice` /// `args.into_config()` -> `config` /// `bytes.to_vec()` -> `vec` -const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"]; +const USELESS_METHOD_PREFIXES: &[&str] = &["try_into_", "into_", "as_", "to_"]; /// Useless methods that are stripped from expression /// From 72c6b0714142f45b7c8989b2bc54de22c4a38c6d Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 30 Dec 2025 10:57:15 +0800 Subject: [PATCH 04/34] Migrate `move_arm_cond_to_match_guard` assist to use `SyntaxEditor` --- .../ide-assists/src/handlers/move_guard.rs | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index 8daf86923d921..1c0c6e43d53be 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -1,6 +1,11 @@ +use itertools::Itertools; use syntax::{ SyntaxKind::WHITESPACE, - ast::{AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make}, + ast::{ + AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make, + syntax_factory::SyntaxFactory, + }, + syntax_editor::Element, }; use crate::{AssistContext, AssistId, Assists}; @@ -131,8 +136,10 @@ pub(crate) fn move_arm_cond_to_match_guard( AssistId::refactor_rewrite("move_arm_cond_to_match_guard"), "Move condition to match guard", replace_node.text_range(), - |edit| { - edit.delete(match_arm.syntax().text_range()); + |builder| { + let make = SyntaxFactory::without_mappings(); + let mut replace_arms = vec![]; + // Dedent if if_expr is in a BlockExpr let dedent = if needs_dedent { cov_mark::hit!(move_guard_ifelse_in_block); @@ -141,47 +148,30 @@ pub(crate) fn move_arm_cond_to_match_guard( cov_mark::hit!(move_guard_ifelse_else_block); 0 }; - let then_arm_end = match_arm.syntax().text_range().end(); let indent_level = match_arm.indent_level(); - let spaces = indent_level; - let mut first = true; for (cond, block) in conds_blocks { - if !first { - edit.insert(then_arm_end, format!("\n{spaces}")); - } else { - first = false; - } - let guard = format!("{match_pat} if {cond} => "); - edit.insert(then_arm_end, guard); let only_expr = block.statements().next().is_none(); - match &block.tail_expr() { - Some(then_expr) if only_expr => { - edit.insert(then_arm_end, then_expr.syntax().text()); - edit.insert(then_arm_end, ","); - } - _ => { - let to_insert = block.dedent(dedent.into()).syntax().text(); - edit.insert(then_arm_end, to_insert) - } - } + let expr = match block.tail_expr() { + Some(then_expr) if only_expr => then_expr, + _ => block.dedent(dedent.into()).into(), + }; + let guard = make.match_guard(cond); + let new_arm = make.match_arm(match_pat.clone(), Some(guard), expr); + replace_arms.push(new_arm); } - if let Some(e) = tail { + if let Some(block) = tail { cov_mark::hit!(move_guard_ifelse_else_tail); - let guard = format!("\n{spaces}{match_pat} => "); - edit.insert(then_arm_end, guard); - let only_expr = e.statements().next().is_none(); - match &e.tail_expr() { + let only_expr = block.statements().next().is_none(); + let expr = match block.tail_expr() { Some(expr) if only_expr => { cov_mark::hit!(move_guard_ifelse_expr_only); - edit.insert(then_arm_end, expr.syntax().text()); - edit.insert(then_arm_end, ","); - } - _ => { - let to_insert = e.dedent(dedent.into()).syntax().text(); - edit.insert(then_arm_end, to_insert) + expr } - } + _ => block.dedent(dedent.into()).into(), + }; + let new_arm = make.match_arm(match_pat, None, expr); + replace_arms.push(new_arm); } else { // There's no else branch. Add a pattern without guard, unless the following match // arm is `_ => ...` @@ -193,9 +183,21 @@ pub(crate) fn move_arm_cond_to_match_guard( { cov_mark::hit!(move_guard_ifelse_has_wildcard); } - _ => edit.insert(then_arm_end, format!("\n{spaces}{match_pat} => {{}}")), + _ => { + let block_expr = make.expr_empty_block().into(); + replace_arms.push(make.match_arm(match_pat, None, block_expr)); + } } } + + let mut edit = builder.make_editor(match_arm.syntax()); + + let newline = make.whitespace(&format!("\n{indent_level}")); + let replace_arms = replace_arms.iter().map(|it| it.syntax().syntax_element()); + let replace_arms = Itertools::intersperse(replace_arms, newline.syntax_element()); + edit.replace_with_many(match_arm.syntax(), replace_arms.collect()); + + builder.add_file_edits(ctx.vfs_file_id(), edit); }, ) } From 862ab4cab6f2031800838c62f6a8b06b825a054a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 31 Dec 2025 14:23:56 +0530 Subject: [PATCH 05/34] remove unwanted comment --- .../rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs | 1 - 1 file changed, 1 deletion(-) 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 25a5104c5df34..1a929a02ac3b3 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 @@ -81,7 +81,6 @@ fn run_new() -> io::Result<()> { } bidirectional::Request::ApiVersionCheck {} => { - // bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION).write::<_, C>(stdout) send_response::( &stdout, bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION), From ec7db072ee3cc62f825f1b918cd8043cca03c6c7 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 31 Dec 2025 12:59:40 +0200 Subject: [PATCH 06/34] Allow finding references from doc comments --- .../crates/ide/src/references.rs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index ffae7bf6c7c5a..4918fe4ff9a47 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -38,7 +38,8 @@ use syntax::{ }; use crate::{ - Analysis, FilePosition, HighlightedRange, NavigationTarget, TryToNav, highlight_related, + Analysis, FilePosition, HighlightedRange, NavigationTarget, TryToNav, + doc_links::token_as_doc_comment, highlight_related, }; /// Result of a reference search operation. @@ -211,6 +212,13 @@ pub(crate) fn find_defs( syntax: &SyntaxNode, offset: TextSize, ) -> Option> { + if let Some(token) = syntax.token_at_offset(offset).left_biased() + && let Some(doc_comment) = token_as_doc_comment(&token) + { + return doc_comment + .get_definition_with_descend_at(sema, offset, |def, _, _| Some(vec![def])); + } + let token = syntax.token_at_offset(offset).find(|t| { matches!( t.kind(), @@ -785,6 +793,23 @@ fn main() { ); } + #[test] + fn test_find_all_refs_in_comments() { + check( + r#" +struct Foo; + +/// $0[`Foo`] is just above +struct Bar; +"#, + expect![[r#" + Foo Struct FileId(0) 0..11 7..10 + + (no references) + "#]], + ); + } + #[test] fn search_filters_by_range() { check( From 2a7e3e6f2361d081d94822ac53474cd9e1010f6a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 31 Dec 2025 17:50:14 +0530 Subject: [PATCH 07/34] add bidirectional flow for file method --- .../crates/load-cargo/src/lib.rs | 14 ++++++++++ .../src/bidirectional_protocol/msg.rs | 2 ++ .../proc-macro-srv-cli/src/main_loop.rs | 26 +++++++++++++++++++ .../crates/proc-macro-srv/src/lib.rs | 1 + .../src/server_impl/rust_analyzer_span.rs | 15 ++++++----- 5 files changed, 51 insertions(+), 7 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 5122051fb3c2f..c8a157e88e17d 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -546,6 +546,20 @@ impl ProcMacroExpander for Expander { let slice = text.get(start as usize..end as usize).map(ToOwned::to_owned); Ok(SubResponse::SourceTextResult { text: slice }) } + SubRequest::FileName { file_id } => { + let file = FileId::from_raw(file_id); + let source_root_id = db.file_source_root(file).source_root_id(db); + let source_root = db.source_root(source_root_id).source_root(db); + + let name = source_root.path_for_file(&file).and_then(|path| { + path.name_and_extension().map(|(name, ext)| match ext { + Some(ext) => format!("{name}.{ext}"), + None => name.to_owned(), + }) + }); + + Ok(SubResponse::FileNameResult { name: name.unwrap_or_default() }) + } }; match self.0.expand( subtree.view(), 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 cf8becd922de2..7edd9062d9943 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 @@ -10,11 +10,13 @@ use crate::{ #[derive(Debug, Serialize, Deserialize)] pub enum SubRequest { + FileName { file_id: u32 }, SourceText { file_id: u32, start: u32, end: u32 }, } #[derive(Debug, Serialize, Deserialize)] pub enum SubResponse { + FileNameResult { name: String }, SourceTextResult { text: Option }, } 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 1a929a02ac3b3..279eab06c0b41 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 @@ -167,6 +167,32 @@ struct ProcMacroClientHandle<'a, C: Codec> { } impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { + fn file(&mut self, file_id: u32) -> String { + let req = + bidirectional::BidirectionalMessage::SubRequest(bidirectional::SubRequest::FileName { + file_id, + }); + + if req.write::<_, C>(&mut self.stdout.lock()).is_err() { + return String::new(); + } + + let msg = match bidirectional::BidirectionalMessage::read::<_, C>( + &mut self.stdin.lock(), + self.buf, + ) { + Ok(Some(msg)) => msg, + _ => return String::new(), + }; + + match msg { + bidirectional::BidirectionalMessage::SubResponse( + bidirectional::SubResponse::FileNameResult { name }, + ) => name, + _ => String::new(), + } + } + fn source_text(&mut self, file_id: u32, start: u32, end: u32) -> Option { let req = bidirectional::BidirectionalMessage::SubRequest( bidirectional::SubRequest::SourceText { file_id, start, end }, 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 17ffa29ce172d..1804238a32a1d 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 @@ -94,6 +94,7 @@ impl<'env> ProcMacroSrv<'env> { pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); pub trait ProcMacroClientInterface { + fn file(&mut self, file_id: u32) -> String; fn source_text(&mut self, file_id: u32, start: u32, end: u32) -> Option; } 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 5d9090c060e04..7a9d655431f51 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 @@ -127,13 +127,14 @@ impl server::Span for RaSpanServer<'_> { fn debug(&mut self, span: Self::Span) -> String { format!("{:?}", span) } - fn file(&mut self, _: Self::Span) -> String { - // FIXME - String::new() - } - fn local_file(&mut self, _: Self::Span) -> Option { - // FIXME - None + fn file(&mut self, span: Self::Span) -> String { + self.callback + .as_mut() + .map(|cb| cb.file(span.anchor.file_id.file_id().index())) + .unwrap_or_default() + } + fn local_file(&mut self, span: Self::Span) -> Option { + self.callback.as_mut().and_then(|cb| cb.local_file(span.anchor.file_id.file_id().index())) } fn save_span(&mut self, _span: Self::Span) -> usize { // FIXME, quote is incompatible with third-party tools From abbf0272fa13f49532ece6a125de288ba39c82c7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 31 Dec 2025 17:50:20 +0530 Subject: [PATCH 08/34] add bidirectional flow for local file method --- .../crates/load-cargo/src/lib.rs | 14 +++++++++++ .../src/bidirectional_protocol/msg.rs | 2 ++ .../proc-macro-srv-cli/src/main_loop.rs | 25 +++++++++++++++++++ .../crates/proc-macro-srv/src/lib.rs | 1 + 4 files changed, 42 insertions(+) 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 c8a157e88e17d..dc75abe7ad8cc 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -540,6 +540,20 @@ impl ProcMacroExpander for Expander { current_dir: String, ) -> Result { let mut cb = |req| match req { + SubRequest::LocalFileName { file_id } => { + let file = FileId::from_raw(file_id); + let source_root_id = db.file_source_root(file).source_root_id(db); + let source_root = db.source_root(source_root_id).source_root(db); + + let name = source_root + .path_for_file(&file) + .and_then(|path| path.clone().into_abs_path()) + .and_then(|path| { + path.as_path().file_name().map(|filename| filename.to_owned()) + }); + + Ok(SubResponse::LocalFileNameResult { name }) + } SubRequest::SourceText { file_id, start, end } => { let file = FileId::from_raw(file_id); let text = db.file_text(file).text(db); 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 7edd9062d9943..2819a92fe33a1 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 @@ -12,12 +12,14 @@ use crate::{ pub enum SubRequest { FileName { file_id: u32 }, SourceText { file_id: u32, start: u32, end: u32 }, + LocalFileName { file_id: u32 }, } #[derive(Debug, Serialize, Deserialize)] pub enum SubResponse { FileNameResult { name: String }, SourceTextResult { text: Option }, + LocalFileNameResult { name: Option }, } #[derive(Debug, Serialize, Deserialize)] 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 279eab06c0b41..76119b27a8b65 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 @@ -217,6 +217,31 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl _ => None, } } + + fn local_file(&mut self, file_id: u32) -> Option { + let req = bidirectional::BidirectionalMessage::SubRequest( + bidirectional::SubRequest::LocalFileName { file_id }, + ); + + if req.write::<_, C>(&mut self.stdout.lock()).is_err() { + return Some(String::new()); + } + + let msg = match bidirectional::BidirectionalMessage::read::<_, C>( + &mut self.stdin.lock(), + self.buf, + ) { + Ok(msg) => msg, + _ => return None, + }; + + match msg { + Some(bidirectional::BidirectionalMessage::SubResponse( + bidirectional::SubResponse::LocalFileNameResult { name }, + )) => Some(name.unwrap_or_default()), + _ => None, + } + } } fn handle_expand_ra( 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 1804238a32a1d..8de712dbd3863 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 @@ -96,6 +96,7 @@ pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Syn pub trait ProcMacroClientInterface { fn file(&mut self, file_id: u32) -> String; fn source_text(&mut self, file_id: u32, start: u32, end: u32) -> Option; + fn local_file(&mut self, file_id: u32) -> Option; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; From c4dd25f6cd2e11e0a4f055bba2cffed5a1553cb2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 31 Dec 2025 17:54:22 +0530 Subject: [PATCH 09/34] add roundtrip abstraction to remove subrequest subresponse boilerplate code --- .../proc-macro-srv-cli/src/main_loop.rs | 81 ++++++------------- 1 file changed, 25 insertions(+), 56 deletions(-) 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 76119b27a8b65..77cb8760a100c 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 @@ -166,79 +166,48 @@ struct ProcMacroClientHandle<'a, C: Codec> { buf: &'a mut C::Buf, } -impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { - fn file(&mut self, file_id: u32) -> String { - let req = - bidirectional::BidirectionalMessage::SubRequest(bidirectional::SubRequest::FileName { - file_id, - }); - - if req.write::<_, C>(&mut self.stdout.lock()).is_err() { - return String::new(); +impl<'a, C: Codec> ProcMacroClientHandle<'a, C> { + fn roundtrip( + &mut self, + req: bidirectional::SubRequest, + ) -> Option { + let msg = bidirectional::BidirectionalMessage::SubRequest(req); + + if msg.write::<_, C>(&mut self.stdout.lock()).is_err() { + return None; } - let msg = match bidirectional::BidirectionalMessage::read::<_, C>( - &mut self.stdin.lock(), - self.buf, - ) { - Ok(Some(msg)) => msg, - _ => return String::new(), - }; + match bidirectional::BidirectionalMessage::read::<_, C>(&mut self.stdin.lock(), self.buf) { + Ok(Some(msg)) => Some(msg), + _ => None, + } + } +} - match msg { - bidirectional::BidirectionalMessage::SubResponse( +impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { + fn file(&mut self, file_id: u32) -> String { + match self.roundtrip(bidirectional::SubRequest::FileName { file_id }) { + Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::FileNameResult { name }, - ) => name, + )) => name, _ => String::new(), } } fn source_text(&mut self, file_id: u32, start: u32, end: u32) -> Option { - let req = bidirectional::BidirectionalMessage::SubRequest( - bidirectional::SubRequest::SourceText { file_id, start, end }, - ); - - if req.write::<_, C>(&mut self.stdout.lock()).is_err() { - return None; - } - - let msg = match bidirectional::BidirectionalMessage::read::<_, C>( - &mut self.stdin.lock(), - self.buf, - ) { - Ok(Some(msg)) => msg, - _ => return None, - }; - - match msg { - bidirectional::BidirectionalMessage::SubResponse( + match self.roundtrip(bidirectional::SubRequest::SourceText { file_id, start, end }) { + Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::SourceTextResult { text }, - ) => text, + )) => text, _ => None, } } fn local_file(&mut self, file_id: u32) -> Option { - let req = bidirectional::BidirectionalMessage::SubRequest( - bidirectional::SubRequest::LocalFileName { file_id }, - ); - - if req.write::<_, C>(&mut self.stdout.lock()).is_err() { - return Some(String::new()); - } - - let msg = match bidirectional::BidirectionalMessage::read::<_, C>( - &mut self.stdin.lock(), - self.buf, - ) { - Ok(msg) => msg, - _ => return None, - }; - - match msg { + match self.roundtrip(bidirectional::SubRequest::LocalFileName { file_id }) { Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::LocalFileNameResult { name }, - )) => Some(name.unwrap_or_default()), + )) => name, _ => None, } } From cde301fabc81b25b6ef54268139d93721448b118 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jan 2026 19:20:42 +0000 Subject: [PATCH 10/34] Bump qs from 6.14.0 to 6.14.1 in /editors/code Bumps [qs](https://github.com/ljharb/qs) from 6.14.0 to 6.14.1. - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md) - [Commits](https://github.com/ljharb/qs/compare/v6.14.0...v6.14.1) --- updated-dependencies: - dependency-name: qs dependency-version: 6.14.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/tools/rust-analyzer/editors/code/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 00d83e9068482..57f6bf69beb08 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -5584,9 +5584,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { From 6394373dccc7b4bc49617b03bd505ea0f75a57a7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 2 Jan 2026 05:44:43 +0530 Subject: [PATCH 11/34] add nits --- src/tools/rust-analyzer/crates/load-cargo/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 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 dc75abe7ad8cc..5aa96582e9f3f 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -547,10 +547,8 @@ impl ProcMacroExpander for Expander { let name = source_root .path_for_file(&file) - .and_then(|path| path.clone().into_abs_path()) - .and_then(|path| { - path.as_path().file_name().map(|filename| filename.to_owned()) - }); + .and_then(|path| path.as_path()) + .and_then(|path| path.file_name().map(|filename| filename.to_owned())); Ok(SubResponse::LocalFileNameResult { name }) } From 4b34f4f4fbc1cb43307d31ed349909c53843a6d1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 2 Jan 2026 10:19:02 +0100 Subject: [PATCH 12/34] minor: Fix some minor FIXMEs --- .../crates/hir-def/src/expr_store.rs | 8 ++++---- .../crates/hir-def/src/expr_store/expander.rs | 7 +------ .../crates/hir-def/src/expr_store/lower.rs | 19 +++++++++---------- .../src/expr_store/lower/format_args.rs | 3 ++- .../crates/hir-def/src/hir/generics.rs | 3 +-- .../hir-def/src/nameres/path_resolution.rs | 2 -- .../crates/hir-def/src/resolver.rs | 2 +- .../crates/hir/src/has_source.rs | 2 +- .../crates/rust-analyzer/src/lib.rs | 3 +++ 9 files changed, 22 insertions(+), 27 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 66f7e25ffac45..10cd460d1d36b 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 @@ -57,8 +57,7 @@ impl HygieneId { Self(ctx) } - // FIXME: Inline this - pub(crate) fn lookup(self) -> SyntaxContext { + pub(crate) fn syntax_context(self) -> SyntaxContext { self.0 } @@ -73,7 +72,8 @@ pub type ExprSource = InFile; pub type PatPtr = AstPtr; pub type PatSource = InFile; -pub type LabelPtr = AstPtr; +/// BlockExpr -> Desugared label from try block +pub type LabelPtr = AstPtr>; pub type LabelSource = InFile; pub type FieldPtr = AstPtr; @@ -942,7 +942,7 @@ impl ExpressionStoreSourceMap { } pub fn node_label(&self, node: InFile<&ast::Label>) -> Option { - let src = node.map(AstPtr::new); + let src = node.map(AstPtr::new).map(AstPtr::wrap_left); self.expr_only()?.label_map.get(&src).cloned() } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs index 6a2f06b0a6f68..de5974fff1ad8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/expander.rs @@ -11,7 +11,7 @@ use hir_expand::{ ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId, eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap, }; -use span::{AstIdMap, Edition, SyntaxContext}; +use span::{AstIdMap, SyntaxContext}; use syntax::ast::HasAttrs; use syntax::{AstNode, Parse, ast}; use triomphe::Arc; @@ -75,11 +75,6 @@ impl Expander { AttrFlags::is_cfg_enabled_for(owner, cfg_options) } - pub(super) fn call_syntax_ctx(&self) -> SyntaxContext { - // FIXME: - SyntaxContext::root(Edition::CURRENT_FIXME) - } - pub(super) fn enter_expand( &mut self, db: &dyn DefDatabase, 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 42b076abb2a6e..af274f1a485b0 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 @@ -1096,7 +1096,7 @@ impl<'db> ExprCollector<'db> { ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e), ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e), ast::Expr::CallExpr(e) => { - // FIXME: Remove this once we drop support for <1.86, https://github.com/rust-lang/rust/commit/ac9cb908ac4301dfc25e7a2edee574320022ae2c + // FIXME(MINIMUM_SUPPORTED_TOOLCHAIN_VERSION): Remove this once we drop support for <1.86, https://github.com/rust-lang/rust/commit/ac9cb908ac4301dfc25e7a2edee574320022ae2c let is_rustc_box = { let attrs = e.attrs(); attrs.filter_map(|it| it.as_simple_atom()).any(|it| it == "rustc_box") @@ -1649,7 +1649,7 @@ impl<'db> ExprCollector<'db> { fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId { let try_from_output = self.lang_path(self.lang_items().TryTraitFromOutput); let label = self.generate_new_name(); - let label = self.alloc_label_desugared(Label { name: label }); + let label = self.alloc_label_desugared(Label { name: label }, AstPtr::new(&e).wrap_right()); let old_label = self.current_try_block_label.replace(label); let ptr = AstPtr::new(&e).upcast(); @@ -2399,7 +2399,6 @@ impl<'db> ExprCollector<'db> { }; let start = range_part_lower(p.start()); let end = range_part_lower(p.end()); - // FIXME: Exclusive ended pattern range is stabilised match p.op_kind() { Some(range_type) => Pat::Range { start, end, range_type }, None => Pat::Missing, @@ -2519,9 +2518,9 @@ impl<'db> ExprCollector<'db> { let mut hygiene_info = if hygiene_id.is_root() { None } else { - hygiene_id.lookup().outer_expn(self.db).map(|expansion| { + hygiene_id.syntax_context().outer_expn(self.db).map(|expansion| { let expansion = self.db.lookup_intern_macro_call(expansion.into()); - (hygiene_id.lookup().parent(self.db), expansion.def) + (hygiene_id.syntax_context().parent(self.db), expansion.def) }) }; let name = Name::new_lifetime(&lifetime.text()); @@ -2727,17 +2726,17 @@ impl ExprCollector<'_> { self.store.pats.alloc(Pat::Missing) } - fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId { + fn alloc_label(&mut self, label: Label, ptr: AstPtr) -> LabelId { + self.alloc_label_desugared(label, ptr.wrap_left()) + } + + fn alloc_label_desugared(&mut self, label: Label, ptr: LabelPtr) -> LabelId { let src = self.expander.in_file(ptr); let id = self.store.labels.alloc(label); self.store.label_map_back.insert(id, src); self.store.label_map.insert(src, id); id } - // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow. - fn alloc_label_desugared(&mut self, label: Label) -> LabelId { - self.store.labels.alloc(label) - } fn is_lowering_awaitable_block(&self) -> &Awaitable { self.awaitable_context.as_ref().unwrap_or(&Awaitable::No("unknown")) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs index 32c9df3dfefb2..7ef84f27f6641 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs @@ -3,6 +3,7 @@ use base_db::FxIndexSet; use hir_expand::name::Name; use intern::{Symbol, sym}; +use span::SyntaxContext; use syntax::{AstPtr, AstToken as _, ast}; use crate::{ @@ -49,7 +50,7 @@ impl<'db> ExprCollector<'db> { self.expand_macros_to_string(template.clone()).map(|it| (it, template)) }) { Some(((s, is_direct_literal), template)) => { - let call_ctx = self.expander.call_syntax_ctx(); + let call_ctx = SyntaxContext::root(self.def_map.edition()); let hygiene = self.hygiene_id_for(s.syntax().text_range()); let fmt = format_args::parse( &s, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs index 1a2d5ebba4673..482cf36f95b0b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/generics.rs @@ -20,8 +20,7 @@ pub type LocalLifetimeParamId = Idx; /// Data about a generic type parameter (to a function, struct, impl, ...). #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct TypeParamData { - /// [`None`] only if the type ref is an [`crate::type_ref::TypeRef::ImplTrait`]. FIXME: Might be better to just - /// make it always be a value, giving impl trait a special name. + /// [`None`] only if the type ref is an [`crate::type_ref::TypeRef::ImplTrait`]. pub name: Option, pub default: Option, pub provenance: TypeParamProvenance, 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 e4b1d2a987355..cf33cecf5fba9 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 @@ -267,7 +267,6 @@ impl DefMap { // plain import or absolute path in 2015: crate-relative with // fallback to extern prelude (with the simplification in // rust-lang/rust#57745) - // FIXME there must be a nicer way to write this condition PathKind::Plain | PathKind::Abs if self.data.edition == Edition::Edition2015 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => @@ -383,7 +382,6 @@ impl DefMap { // plain import or absolute path in 2015: crate-relative with // fallback to extern prelude (with the simplification in // rust-lang/rust#57745) - // FIXME there must be a nicer way to write this condition PathKind::Plain | PathKind::Abs if self.data.edition == Edition::Edition2015 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => 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 263f603a0bfbb..f643ed31ad530 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -950,7 +950,7 @@ fn hygiene_info( hygiene_id: HygieneId, ) -> Option<(SyntaxContext, MacroDefId)> { if !hygiene_id.is_root() { - let ctx = hygiene_id.lookup(); + let ctx = hygiene_id.syntax_context(); ctx.outer_expn(db).map(|expansion| { let expansion = db.lookup_intern_macro_call(expansion.into()); (ctx.parent(db), expansion.def) diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index 09c5b1cca7f3e..e032a16989ff6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -330,7 +330,7 @@ impl HasSource for Label { let (_body, source_map) = db.body_with_source_map(self.parent); let src = source_map.label_syntax(self.label_id); let root = src.file_syntax(db); - Some(src.map(|ast| ast.to_node(&root))) + src.map(|ast| ast.to_node(&root).left()).transpose() } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 971ae2a601f7a..4a37bb34aba3f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -16,6 +16,9 @@ extern crate rustc_driver as _; extern crate ra_ap_rustc_type_ir as rustc_type_ir; +/* + If you bump this, grep for `FIXME(MINIMUM_SUPPORTED_TOOLCHAIN_VERSION)` to check for old support code we can drop +*/ /// Any toolchain less than this version will likely not work with rust-analyzer built from this revision. pub const MINIMUM_SUPPORTED_TOOLCHAIN_VERSION: semver::Version = semver::Version { major: 1, From cd7a58e4127a66f8a641c42c6a12c5419c8d5585 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 2 Jan 2026 13:01:07 +0100 Subject: [PATCH 13/34] Pre-allocate intern storages with 64kb of data To reduce the amount of re-allocating the hashtables which can be expensive given the need to re-hash --- src/tools/rust-analyzer/crates/hir-ty/src/lower.rs | 2 +- .../crates/hir-ty/src/method_resolution.rs | 8 ++++---- .../crates/hir-ty/src/next_solver/infer/mod.rs | 8 +++++--- .../src/next_solver/infer/opaque_types/table.rs | 2 +- .../crates/hir-ty/src/tests/incremental.rs | 3 --- .../rust-analyzer/crates/hir-ty/src/variance.rs | 13 +++++++------ src/tools/rust-analyzer/crates/intern/src/intern.rs | 2 +- .../rust-analyzer/crates/intern/src/intern_slice.rs | 7 ++++++- 8 files changed, 25 insertions(+), 20 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 9307868f39826..e45ef113a0865 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1319,7 +1319,7 @@ fn type_for_struct_constructor( db: &dyn HirDatabase, def: StructId, ) -> Option> { - let struct_data = def.fields(db); + let struct_data = db.struct_signature(def); match struct_data.shape { FieldsShape::Record => None, FieldsShape::Unit => Some(type_for_adt(db, def.into())), 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 50dbd87d693e6..3d7cc4ffbf8a5 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 @@ -731,6 +731,10 @@ impl TraitImpls { ) { for (_module_id, module_data) in def_map.modules() { for impl_id in module_data.scope.impls() { + let trait_ref = match db.impl_trait(impl_id) { + Some(tr) => tr.instantiate_identity(), + None => continue, + }; // Reservation impls should be ignored during trait resolution, so we never need // them during type analysis. See rust-lang/rust#64631 for details. // @@ -742,10 +746,6 @@ impl TraitImpls { { continue; } - let trait_ref = match db.impl_trait(impl_id) { - Some(tr) => tr.instantiate_identity(), - None => continue, - }; let self_ty = trait_ref.self_ty(); let interner = DbInterner::new_no_crate(db); let entry = map.entry(trait_ref.def_id.0).or_default(); 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 2926dc30def7c..7d291f7ddbedb 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 @@ -878,9 +878,11 @@ impl<'db> InferCtxt<'db> { self.tainted_by_errors.set(Some(e)); } - #[instrument(level = "debug", skip(self), ret)] - pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> { - self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect() + #[instrument(level = "debug", skip(self))] + pub fn take_opaque_types( + &self, + ) -> impl IntoIterator, OpaqueHiddenType<'db>)> + use<'db> { + self.inner.borrow_mut().opaque_type_storage.take_opaque_types() } #[instrument(level = "debug", skip(self), ret)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs index 00177d21ac760..894fe5eb7b87d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/opaque_types/table.rs @@ -61,7 +61,7 @@ impl<'db> OpaqueTypeStorage<'db> { pub(crate) fn take_opaque_types( &mut self, - ) -> impl Iterator, OpaqueHiddenType<'db>)> { + ) -> impl IntoIterator, OpaqueHiddenType<'db>)> + use<'db> { let OpaqueTypeStorage { opaque_types, duplicate_entries } = self; std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries)) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 1457bb2e10171..633ab43c4cec5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -632,8 +632,6 @@ fn main() { "struct_signature_with_source_map_shim", "GenericPredicates::query_with_diagnostics_", "value_ty_query", - "VariantFields::firewall_", - "VariantFields::query_", "InherentImpls::for_crate_", "impl_signature_shim", "impl_signature_with_source_map_shim", @@ -723,7 +721,6 @@ fn main() { "expr_scopes_shim", "struct_signature_with_source_map_shim", "GenericPredicates::query_with_diagnostics_", - "VariantFields::query_", "InherentImpls::for_crate_", "impl_signature_with_source_map_shim", "impl_signature_shim", 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 e5cfe85573b8c..6f415a5289c9a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -41,7 +41,6 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Variances )] fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariancesOf { tracing::debug!("variances_of(def={:?})", def); - let interner = DbInterner::new_no_crate(db); match def { GenericDefId::FunctionId(_) => (), GenericDefId::AdtId(adt) => { @@ -55,15 +54,17 @@ fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariance } } } - _ => return VariancesOf::empty(interner).store(), + _ => return VariancesOf::empty(DbInterner::new_no_crate(db)).store(), } let generics = generics(db, def); let count = generics.len(); if count == 0 { - return VariancesOf::empty(interner).store(); + return VariancesOf::empty(DbInterner::new_no_crate(db)).store(); } - let variances = Context { generics, variances: vec![Variance::Bivariant; count], db }.solve(); + let variances = + Context { generics, variances: vec![Variance::Bivariant; count].into_boxed_slice(), db } + .solve(); VariancesOf::new_from_slice(&variances).store() } @@ -113,11 +114,11 @@ pub(crate) fn variances_of_cycle_initial( struct Context<'db> { db: &'db dyn HirDatabase, generics: Generics, - variances: Vec, + variances: Box<[Variance]>, } impl<'db> Context<'db> { - fn solve(mut self) -> Vec { + fn solve(mut self) -> Box<[Variance]> { tracing::debug!("solve(generics={:?})", self.generics); match self.generics.def() { GenericDefId::AdtId(adt) => { diff --git a/src/tools/rust-analyzer/crates/intern/src/intern.rs b/src/tools/rust-analyzer/crates/intern/src/intern.rs index b7acd6624b99a..a96dfcfa9fe3c 100644 --- a/src/tools/rust-analyzer/crates/intern/src/intern.rs +++ b/src/tools/rust-analyzer/crates/intern/src/intern.rs @@ -334,7 +334,7 @@ impl InternStorage { impl InternStorage { pub(crate) fn get(&self) -> &InternMap { - self.map.get_or_init(DashMap::default) + self.map.get_or_init(|| DashMap::with_capacity_and_hasher(1024, Default::default())) } } diff --git a/src/tools/rust-analyzer/crates/intern/src/intern_slice.rs b/src/tools/rust-analyzer/crates/intern/src/intern_slice.rs index 58de6e17bdff6..8857771d2e014 100644 --- a/src/tools/rust-analyzer/crates/intern/src/intern_slice.rs +++ b/src/tools/rust-analyzer/crates/intern/src/intern_slice.rs @@ -292,7 +292,12 @@ impl InternSliceStorage { impl InternSliceStorage { pub(crate) fn get(&self) -> &InternMap { - self.map.get_or_init(DashMap::default) + self.map.get_or_init(|| { + DashMap::with_capacity_and_hasher( + (64 * 1024) / std::mem::size_of::(), + Default::default(), + ) + }) } } From f841758cf5136bd982631e497df35ddc5f80521a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 2 Jan 2026 11:04:27 +0100 Subject: [PATCH 14/34] Remove unnecessary `ConstLiteralRef` enum --- .../crates/hir-def/src/expr_store/lower.rs | 1 - .../crates/hir-def/src/hir/type_ref.rs | 58 +------- .../crates/hir-ty/src/consteval.rs | 127 +++++++++++++++--- .../rust-analyzer/crates/hir-ty/src/lower.rs | 24 +--- .../crates/hir-ty/src/next_solver/ty.rs | 18 +++ 5 files changed, 130 insertions(+), 98 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 af274f1a485b0..d3774ded39dc0 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 @@ -2319,7 +2319,6 @@ impl<'db> ExprCollector<'db> { ast::Pat::SlicePat(p) => { let SlicePatComponents { prefix, slice, suffix } = p.components(); - // FIXME properly handle `RestPat` Pat::Slice { prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(), slice: slice.map(|p| self.collect_pat(p, binding_list)), 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 ad8535413d671..b64199fa2697e 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 @@ -1,8 +1,6 @@ //! HIR for references to types. Paths in these are not yet resolved. They can //! be directly created from an ast::TypeRef, without further queries. -use std::fmt::Write; - use hir_expand::name::Name; use intern::Symbol; use la_arena::Idx; @@ -10,12 +8,11 @@ use thin_vec::ThinVec; use crate::{ LifetimeParamId, TypeParamId, - builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, expr_store::{ ExpressionStore, path::{GenericArg, Path}, }, - hir::{ExprId, Literal}, + hir::ExprId, }; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -275,56 +272,3 @@ impl TypeBound { pub struct ConstRef { pub expr: ExprId, } - -/// A literal constant value -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum LiteralConstRef { - Int(i128), - UInt(u128), - Bool(bool), - Char(char), - - /// Case of an unknown value that rustc might know but we don't - // FIXME: this is a hack to get around chalk not being able to represent unevaluatable - // constants - // https://github.com/rust-lang/rust-analyzer/pull/8813#issuecomment-840679177 - // https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348 - Unknown, -} - -impl LiteralConstRef { - pub fn builtin_type(&self) -> BuiltinType { - match self { - LiteralConstRef::UInt(_) | LiteralConstRef::Unknown => { - BuiltinType::Uint(BuiltinUint::U128) - } - LiteralConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128), - LiteralConstRef::Char(_) => BuiltinType::Char, - LiteralConstRef::Bool(_) => BuiltinType::Bool, - } - } -} - -impl From for LiteralConstRef { - fn from(literal: Literal) -> Self { - match literal { - Literal::Char(c) => Self::Char(c), - Literal::Bool(flag) => Self::Bool(flag), - Literal::Int(num, _) => Self::Int(num), - Literal::Uint(num, _) => Self::UInt(num), - _ => Self::Unknown, - } - } -} - -impl std::fmt::Display for LiteralConstRef { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { - match self { - LiteralConstRef::Int(num) => num.fmt(f), - LiteralConstRef::UInt(num) => num.fmt(f), - LiteralConstRef::Bool(flag) => flag.fmt(f), - LiteralConstRef::Char(c) => write!(f, "'{c}'"), - LiteralConstRef::Unknown => f.write_char('_'), - } - } -} 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 f11240e0f78ce..5bc2446fdd2dd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -7,9 +7,9 @@ use base_db::Crate; use hir_def::{ ConstId, EnumVariantId, GeneralConstId, HasModule, StaticId, attrs::AttrFlags, + builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, expr_store::Body, - hir::{Expr, ExprId}, - type_ref::LiteralConstRef, + hir::{Expr, ExprId, Literal}, }; use hir_expand::Lookup; use rustc_type_ir::inherent::IntoKind; @@ -23,7 +23,7 @@ use crate::{ mir::{MirEvalError, MirLowerError}, next_solver::{ Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, - ParamEnv, StoredConst, StoredGenericArgs, Ty, ValueConst, + StoredConst, StoredGenericArgs, Ty, ValueConst, }, traits::StoredParamEnvAndCrate, }; @@ -81,47 +81,122 @@ impl From for ConstEvalError { /// Interns a constant scalar with the given type pub fn intern_const_ref<'a>( db: &'a dyn HirDatabase, - value: &LiteralConstRef, + value: &Literal, ty: Ty<'a>, - krate: Crate, + _krate: Crate, ) -> Const<'a> { let interner = DbInterner::new_no_crate(db); - let layout = db - .layout_of_ty(ty.store(), ParamEnvAndCrate { param_env: ParamEnv::empty(), krate }.store()); let kind = match value { - LiteralConstRef::Int(i) => { - // FIXME: We should handle failure of layout better. - let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + &Literal::Uint(i, builtin_ty) + if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Uint) => + { + let memory = match ty.as_builtin() { + Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint { + BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>, + BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()), + BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()), + BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()), + BuiltinUint::U128 => Box::new((i).to_le_bytes()), + BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()), + }, + _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)), + }; rustc_type_ir::ConstKind::Value(ValueConst::new( ty, - ConstBytes { - memory: i.to_le_bytes()[0..size].into(), - memory_map: MemoryMap::default(), + ConstBytes { memory, memory_map: MemoryMap::default() }, + )) + } + &Literal::Int(i, None) + if ty + .as_builtin() + .is_some_and(|builtin_ty| matches!(builtin_ty, BuiltinType::Uint(_))) => + { + let memory = match ty.as_builtin() { + Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint { + BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>, + BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()), + BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()), + BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()), + BuiltinUint::U128 => Box::new((i as u128).to_le_bytes()), + BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()), }, + _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)), + }; + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes { memory, memory_map: MemoryMap::default() }, )) } - LiteralConstRef::UInt(i) => { - let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + &Literal::Int(i, builtin_ty) + if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Int) => + { + let memory = match ty.as_builtin() { + Some(BuiltinType::Int(builtin_int)) => match builtin_int { + BuiltinInt::I8 => Box::new([i as u8]) as Box<[u8]>, + BuiltinInt::I16 => Box::new((i as i16).to_le_bytes()), + BuiltinInt::I32 => Box::new((i as i32).to_le_bytes()), + BuiltinInt::I64 => Box::new((i as i64).to_le_bytes()), + BuiltinInt::I128 => Box::new((i).to_le_bytes()), + BuiltinInt::Isize => Box::new((i as isize).to_le_bytes()), + }, + _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)), + }; rustc_type_ir::ConstKind::Value(ValueConst::new( ty, - ConstBytes { - memory: i.to_le_bytes()[0..size].into(), - memory_map: MemoryMap::default(), + ConstBytes { memory, memory_map: MemoryMap::default() }, + )) + } + Literal::Float(float_type_wrapper, builtin_float) + if builtin_float.is_none() + || ty.as_builtin() == builtin_float.map(BuiltinType::Float) => + { + let memory = match ty.as_builtin().unwrap() { + BuiltinType::Float(builtin_float) => match builtin_float { + // FIXME: + hir_def::builtin_type::BuiltinFloat::F16 => Box::new([0u8; 2]) as Box<[u8]>, + hir_def::builtin_type::BuiltinFloat::F32 => { + Box::new(float_type_wrapper.to_f32().to_le_bytes()) + } + hir_def::builtin_type::BuiltinFloat::F64 => { + Box::new(float_type_wrapper.to_f64().to_le_bytes()) + } + // FIXME: + hir_def::builtin_type::BuiltinFloat::F128 => Box::new([0; 16]), }, + _ => unreachable!(), + }; + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes { memory, memory_map: MemoryMap::default() }, )) } - LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new( + Literal::Bool(b) if ty.is_bool() => rustc_type_ir::ConstKind::Value(ValueConst::new( ty, ConstBytes { memory: Box::new([*b as u8]), memory_map: MemoryMap::default() }, )), - LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new( + Literal::Char(c) if ty.is_char() => rustc_type_ir::ConstKind::Value(ValueConst::new( ty, ConstBytes { memory: (*c as u32).to_le_bytes().into(), memory_map: MemoryMap::default(), }, )), - LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + Literal::String(symbol) if ty.is_str() => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes { + memory: symbol.as_str().as_bytes().into(), + memory_map: MemoryMap::default(), + }, + )), + Literal::ByteString(items) if ty.as_slice().is_some_and(|ty| ty.is_u8()) => { + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes { memory: items.clone(), memory_map: MemoryMap::default() }, + )) + } + // FIXME + Literal::CString(_items) => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + _ => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), }; Const::new(interner, kind) } @@ -130,7 +205,15 @@ pub fn intern_const_ref<'a>( pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option, krate: Crate) -> Const<'db> { intern_const_ref( db, - &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt), + &match value { + Some(value) => Literal::Uint(value, Some(BuiltinUint::Usize)), + None => { + return Const::new( + DbInterner::new_no_crate(db), + rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + ); + } + }, Ty::new_uint(DbInterner::new_no_crate(db), rustc_type_ir::UintTy::Usize), krate, ) 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 9307868f39826..6688cf3d60258 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -27,8 +27,8 @@ use hir_def::{ resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs}, signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, type_ref::{ - ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier, - TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, + ConstRef, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, + TypeRef, TypeRefId, }, }; use hir_expand::name::Name; @@ -281,21 +281,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { hir_def::hir::Expr::Path(path) => { self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type)) } - hir_def::hir::Expr::Literal(literal) => intern_const_ref( - self.db, - &match *literal { - hir_def::hir::Literal::Float(_, _) - | hir_def::hir::Literal::String(_) - | hir_def::hir::Literal::ByteString(_) - | hir_def::hir::Literal::CString(_) => LiteralConstRef::Unknown, - hir_def::hir::Literal::Char(c) => LiteralConstRef::Char(c), - hir_def::hir::Literal::Bool(b) => LiteralConstRef::Bool(b), - hir_def::hir::Literal::Int(val, _) => LiteralConstRef::Int(val), - hir_def::hir::Literal::Uint(val, _) => LiteralConstRef::UInt(val), - }, - const_type, - self.resolver.krate(), - ), + hir_def::hir::Expr::Literal(literal) => { + intern_const_ref(self.db, literal, const_type, self.resolver.krate()) + } hir_def::hir::Expr::UnaryOp { expr: inner_expr, op: hir_def::hir::UnaryOp::Neg } => { if let hir_def::hir::Expr::Literal(literal) = &self.store[*inner_expr] { // Only handle negation for signed integers and floats @@ -304,7 +292,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { if let Some(negated_literal) = literal.clone().negate() { intern_const_ref( self.db, - &negated_literal.into(), + &negated_literal, const_type, self.resolver.krate(), ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs index 030ba37015f2e..66a24d3949908 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs @@ -383,6 +383,11 @@ impl<'db> Ty<'db> { matches!(self.kind(), TyKind::Bool) } + #[inline] + pub fn is_char(self) -> bool { + matches!(self.kind(), TyKind::Char) + } + /// A scalar type is one that denotes an atomic datum, with no sub-components. /// (A RawPtr is scalar because it represents a non-managed pointer, so its /// contents are abstract to rustc.) @@ -422,6 +427,11 @@ impl<'db> Ty<'db> { matches!(self.kind(), TyKind::Tuple(tys) if tys.is_empty()) } + #[inline] + pub fn is_u8(self) -> bool { + matches!(self.kind(), TyKind::Uint(UintTy::U8)) + } + #[inline] pub fn is_raw_ptr(self) -> bool { matches!(self.kind(), TyKind::RawPtr(..)) @@ -456,6 +466,14 @@ impl<'db> Ty<'db> { } } + #[inline] + pub fn as_slice(self) -> Option> { + match self.kind() { + TyKind::Slice(ty) => Some(ty), + _ => None, + } + } + #[inline] pub fn ty_vid(self) -> Option { match self.kind() { From 8d5a7b685e63fe8e313da974b7e5b70117db1680 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 2 Jan 2026 13:16:21 +0100 Subject: [PATCH 15/34] Reduce `impl_signature` query dependencies in method resolution --- .../crates/hir-def/src/item_scope.rs | 16 ++++++++++++---- .../crates/hir-def/src/item_tree.rs | 4 +++- .../crates/hir-def/src/item_tree/lower.rs | 2 +- .../crates/hir-def/src/item_tree/pretty.rs | 2 +- .../crates/hir-def/src/lang_item.rs | 2 +- .../crates/hir-def/src/nameres/collector.rs | 4 +++- .../crates/hir-ty/src/method_resolution.rs | 9 ++------- .../crates/hir-ty/src/tests/incremental.rs | 18 ++++++------------ src/tools/rust-analyzer/crates/hir/src/lib.rs | 11 +++++------ .../ide-completion/src/tests/flyimport.rs | 4 ++-- 10 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 9e7868b273efa..a3278dd76c868 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -158,7 +158,7 @@ pub struct ItemScope { /// declared. declarations: ThinVec, - impls: ThinVec, + impls: ThinVec<(ImplId, /* trait impl */ bool)>, builtin_derive_impls: ThinVec, extern_blocks: ThinVec, unnamed_consts: ThinVec, @@ -327,7 +327,15 @@ impl ItemScope { } pub fn impls(&self) -> impl ExactSizeIterator + '_ { - self.impls.iter().copied() + self.impls.iter().map(|&(id, _)| id) + } + + pub fn trait_impls(&self) -> impl Iterator + '_ { + self.impls.iter().filter(|&&(_, is_trait_impl)| is_trait_impl).map(|&(id, _)| id) + } + + pub fn inherent_impls(&self) -> impl Iterator + '_ { + self.impls.iter().filter(|&&(_, is_trait_impl)| !is_trait_impl).map(|&(id, _)| id) } pub fn builtin_derive_impls(&self) -> impl ExactSizeIterator + '_ { @@ -472,8 +480,8 @@ impl ItemScope { self.legacy_macros.get(name).map(|it| &**it) } - pub(crate) fn define_impl(&mut self, imp: ImplId) { - self.impls.push(imp); + pub(crate) fn define_impl(&mut self, imp: ImplId, is_trait_impl: bool) { + self.impls.push((imp, is_trait_impl)); } pub(crate) fn define_builtin_derive_impl(&mut self, imp: BuiltinDeriveImplId) { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs index 2e838fad2e9f8..a1707f17beb09 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs @@ -614,7 +614,9 @@ pub struct Trait { } #[derive(Debug, Clone, Eq, PartialEq)] -pub struct Impl {} +pub struct Impl { + pub is_trait_impl: bool, +} #[derive(Debug, Clone, PartialEq, Eq)] pub struct TypeAlias { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs index d8519f7393df4..3f19e001548ee 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs @@ -271,7 +271,7 @@ impl<'a> Ctx<'a> { let ast_id = self.source_ast_id_map.ast_id(impl_def); // Note that trait impls don't get implicit `Self` unlike traits, because here they are a // type alias rather than a type parameter, so this is handled by the resolver. - let res = Impl {}; + let res = Impl { is_trait_impl: impl_def.trait_().is_some() }; self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Impl(res)); ast_id } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs index c89299e6d863b..4113a778eaafd 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs @@ -258,7 +258,7 @@ impl Printer<'_> { w!(self, "trait {} {{ ... }}", name.display(self.db, self.edition)); } ModItemId::Impl(ast_id) => { - let Impl {} = &self.tree[ast_id]; + let Impl { is_trait_impl: _ } = &self.tree[ast_id]; self.print_ast_id(ast_id.erase()); w!(self, "impl {{ ... }}"); } 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 41d69c1fd6243..d3f4480b207e7 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 @@ -41,7 +41,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option { let impl_id = ImplLoc { container: module_id, id: InFile::new(self.file_id(), imp) } .intern(db); - self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id) + self.def_collector.def_map.modules[self.module_id] + .scope + .define_impl(impl_id, self.item_tree[imp].is_trait_impl) } ModItemId::Function(id) => { let it = &self.item_tree[id]; 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 3d7cc4ffbf8a5..e4681b464fec3 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 @@ -609,12 +609,7 @@ impl InherentImpls { map: &mut FxHashMap>, ) { for (_module_id, module_data) in def_map.modules() { - for impl_id in module_data.scope.impls() { - let data = db.impl_signature(impl_id); - if data.target_trait.is_some() { - continue; - } - + for impl_id in module_data.scope.inherent_impls() { let interner = DbInterner::new_no_crate(db); let self_ty = db.impl_self_ty(impl_id); let self_ty = self_ty.instantiate_identity(); @@ -730,7 +725,7 @@ impl TraitImpls { map: &mut FxHashMap, ) { for (_module_id, module_data) in def_map.modules() { - for impl_id in module_data.scope.impls() { + for impl_id in module_data.scope.trait_impls() { let trait_ref = match db.impl_trait(impl_id) { Some(tr) => tr.instantiate_identity(), None => continue, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 633ab43c4cec5..118f433a24e6a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -539,12 +539,6 @@ impl SomeStruct { "AttrFlags::query_", "AttrFlags::query_", "AttrFlags::query_", - "impl_trait_with_diagnostics_query", - "impl_signature_shim", - "impl_signature_with_source_map_shim", - "impl_self_ty_with_diagnostics_query", - "struct_signature_shim", - "struct_signature_with_source_map_shim", ] "#]], ); @@ -617,7 +611,6 @@ fn main() { "lang_items", "crate_lang_items", "AttrFlags::query_", - "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", @@ -633,13 +626,14 @@ fn main() { "GenericPredicates::query_with_diagnostics_", "value_ty_query", "InherentImpls::for_crate_", - "impl_signature_shim", - "impl_signature_with_source_map_shim", "callable_item_signature_query", "TraitImpls::for_crate_and_deps_", "TraitImpls::for_crate_", "impl_trait_with_diagnostics_query", + "impl_signature_shim", + "impl_signature_with_source_map_shim", "impl_self_ty_with_diagnostics_query", + "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", ] "#]], @@ -710,7 +704,6 @@ fn main() { "crate_lang_items", "AttrFlags::query_", "AttrFlags::query_", - "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", @@ -722,12 +715,13 @@ fn main() { "struct_signature_with_source_map_shim", "GenericPredicates::query_with_diagnostics_", "InherentImpls::for_crate_", - "impl_signature_with_source_map_shim", - "impl_signature_shim", "callable_item_signature_query", "TraitImpls::for_crate_", + "impl_signature_with_source_map_shim", + "impl_signature_shim", "impl_trait_with_diagnostics_query", "impl_self_ty_with_diagnostics_query", + "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", ] "#]], diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 1ab57c4489cfb..527614d56a014 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -803,22 +803,21 @@ impl Module { emit_def_diagnostic(db, acc, diag, edition, loc.container.krate(db)); } - if impl_signature.target_trait.is_none() - && !is_inherent_impl_coherent(db, def_map, impl_id) - { + let trait_impl = impl_signature.target_trait.is_some(); + if !trait_impl && !is_inherent_impl_coherent(db, def_map, impl_id) { acc.push(IncoherentImpl { impl_: ast_id_map.get(loc.id.value), file_id }.into()) } - if !impl_def.check_orphan_rules(db) { + if trait_impl && !impl_def.check_orphan_rules(db) { acc.push(TraitImplOrphan { impl_: ast_id_map.get(loc.id.value), file_id }.into()) } - let trait_ = impl_def.trait_(db); + let trait_ = trait_impl.then(|| impl_def.trait_(db)).flatten(); let mut trait_is_unsafe = trait_.is_some_and(|t| t.is_unsafe(db)); let impl_is_negative = impl_def.is_negative(db); let impl_is_unsafe = impl_def.is_unsafe(db); - let trait_is_unresolved = trait_.is_none() && impl_signature.target_trait.is_some(); + let trait_is_unresolved = trait_.is_none() && trait_impl; if trait_is_unresolved { // Ignore trait safety errors when the trait is unresolved, as otherwise we'll treat it as safe, // which may not be correct. 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 aad881f8ce4f1..2912457da1f73 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#" - fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED - ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED 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 "#]], ); } From 0451cc012a4890abcbc217d4105b8ecf3daecf43 Mon Sep 17 00:00:00 2001 From: benodiwal Date: Sat, 3 Jan 2026 00:52:19 +0530 Subject: [PATCH 16/34] fix: add location links for generic type parameters in inlay hints --- .../crates/hir-ty/src/display.rs | 14 ++++++++++++-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 8 ++++---- .../crates/ide/src/inlay_hints.rs | 19 +++++++++++++++++-- 3 files changed, 33 insertions(+), 8 deletions(-) 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 44bbd84003da9..4f9406908ffd9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -10,7 +10,8 @@ use std::{ use base_db::{Crate, FxIndexMap}; use either::Either; use hir_def::{ - FindPathConfig, GenericDefId, HasModule, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, + FindPathConfig, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, ModuleDefId, + ModuleId, TraitId, db::DefDatabase, expr_store::{ExpressionStore, path::Path}, find_path::{self, PrefixKind}, @@ -66,6 +67,7 @@ pub type Result = std::result::Result; pub trait HirWrite: fmt::Write { fn start_location_link(&mut self, _location: ModuleDefId) {} + fn start_location_link_generic(&mut self, _location: GenericParamId) {} fn end_location_link(&mut self) {} } @@ -147,6 +149,10 @@ impl<'db> HirFormatter<'_, 'db> { self.fmt.start_location_link(location); } + pub fn start_location_link_generic(&mut self, location: GenericParamId) { + self.fmt.start_location_link_generic(location); + } + pub fn end_location_link(&mut self) { self.fmt.end_location_link(); } @@ -1489,6 +1495,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> { match param_data { TypeOrConstParamData::TypeParamData(p) => match p.provenance { TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => { + f.start_location_link_generic(param.id.into()); write!( f, "{}", @@ -1496,7 +1503,8 @@ impl<'db> HirDisplay<'db> for Ty<'db> { .clone() .unwrap_or_else(Name::missing) .display(f.db, f.edition()) - )? + )?; + f.end_location_link(); } TypeParamProvenance::ArgumentImplTrait => { let bounds = GenericPredicates::query_all(f.db, param.id.parent()) @@ -1519,7 +1527,9 @@ impl<'db> HirDisplay<'db> for Ty<'db> { } }, TypeOrConstParamData::ConstParamData(p) => { + f.start_location_link_generic(param.id.into()); write!(f, "{}", p.name.display(f.db, f.edition()))?; + f.end_location_link(); } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 1ab57c4489cfb..65de6c0a554a5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -50,9 +50,9 @@ use either::Either; use hir_def::{ AdtId, AssocItemId, AssocItemLoc, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, - GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, - MacroExpander, MacroId, StaticId, StructId, SyntheticSyntax, TupleId, TypeAliasId, - TypeOrConstParamId, TypeParamId, UnionId, + HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander, + MacroId, StaticId, StructId, SyntheticSyntax, TupleId, TypeAliasId, TypeOrConstParamId, + TypeParamId, UnionId, attrs::AttrFlags, builtin_derive::BuiltinDeriveImplMethod, expr_store::{ExpressionStoreDiagnostics, ExpressionStoreSourceMap}, @@ -150,7 +150,7 @@ pub use { visibility::Visibility, // FIXME: This is here since some queries take it as input that are used // outside of hir. - {ModuleDefId, TraitId}, + {GenericParamId, ModuleDefId, TraitId}, }, hir_expand::{ EditionedFileId, ExpandResult, HirFileId, MacroCallId, MacroKind, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 06ae0b1d73d1e..a58dc6f030559 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -5,8 +5,8 @@ use std::{ use either::Either; use hir::{ - ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError, - HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym, + ClosureStyle, DisplayTarget, EditionedFileId, GenericParam, GenericParamId, HasVisibility, + HirDisplay, HirDisplayError, HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym, }; use ide_db::{ FileRange, MiniCore, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder, @@ -709,6 +709,21 @@ impl HirWrite for InlayHintLabelBuilder<'_> { }); } + fn start_location_link_generic(&mut self, def: GenericParamId) { + never!(self.location.is_some(), "location link is already started"); + self.make_new_part(); + + self.location = Some(if self.resolve { + LazyProperty::Lazy + } else { + LazyProperty::Computed({ + let Some(location) = GenericParam::from(def).try_to_nav(self.sema) else { return }; + let location = location.call_site(); + FileRange { file_id: location.file_id, range: location.focus_or_full_range() } + }) + }); + } + fn end_location_link(&mut self) { self.make_new_part(); } From 1222bbf29c7a0484c322006d8771dadcd47447c0 Mon Sep 17 00:00:00 2001 From: benodiwal Date: Sat, 3 Jan 2026 00:59:03 +0530 Subject: [PATCH 17/34] feat: added tests --- .../crates/ide/src/inlay_hints/bind_pat.rs | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 547004687c73a..79cc2bdf8f7b8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -183,7 +183,8 @@ mod tests { use crate::{ClosureReturnTypeHints, fixture, inlay_hints::InlayHintsConfig}; use crate::inlay_hints::tests::{ - DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_no_edit, check_with_config, + DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_expect, check_no_edit, + check_with_config, }; #[track_caller] @@ -1255,4 +1256,40 @@ where "#, ); } + + #[test] + fn generic_param_inlay_hint_has_location_link() { + check_expect( + InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, + r#" +fn identity(t: T) -> T { + let x = t; + x +} +"#, + expect![[r#" + [ + ( + 36..37, + [ + InlayHintLabelPart { + text: "T", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 12..13, + }, + ), + ), + tooltip: "", + }, + ], + ), + ] + "#]], + ); + } } From dd97e41d47b1bec301db3c334f4c378c946d9224 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 3 Jan 2026 01:24:03 +0530 Subject: [PATCH 18/34] for filename, use localfilename first and then resort to other construction --- .../crates/load-cargo/src/lib.rs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 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 5aa96582e9f3f..023253f23f423 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -563,14 +563,23 @@ impl ProcMacroExpander for Expander { let source_root_id = db.file_source_root(file).source_root_id(db); let source_root = db.source_root(source_root_id).source_root(db); - let name = source_root.path_for_file(&file).and_then(|path| { - path.name_and_extension().map(|(name, ext)| match ext { - Some(ext) => format!("{name}.{ext}"), - None => name.to_owned(), + let path = source_root.path_for_file(&file); + + let name = path + .and_then(|p| p.as_path()) + .and_then(|p| p.file_name()) + .map(|n| n.to_owned()) + .or_else(|| { + path.and_then(|p| { + p.name_and_extension().map(|(name, ext)| match ext { + Some(ext) => format!("{name}.{ext}"), + None => name.into(), + }) + }) }) - }); + .unwrap_or_default(); - Ok(SubResponse::FileNameResult { name: name.unwrap_or_default() }) + Ok(SubResponse::FileNameResult { name }) } }; match self.0.expand( From 3114decb04b1f4d8edb46a513df2f948ded778d2 Mon Sep 17 00:00:00 2001 From: benodiwal Date: Sat, 3 Jan 2026 01:58:48 +0530 Subject: [PATCH 19/34] fix: add location links for type, const, and lifetime parameters in inlay hints --- .../crates/hir-ty/src/display.rs | 4 + .../crates/ide/src/inlay_hints/bind_pat.rs | 92 ++++++++++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) 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 4f9406908ffd9..43b428c3fa51a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -692,7 +692,9 @@ impl<'db> HirDisplay<'db> for Const<'db> { ConstKind::Param(param) => { let generics = generics(f.db, param.id.parent()); let param_data = &generics[param.id.local_id()]; + f.start_location_link_generic(param.id.into()); write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?; + f.end_location_link(); Ok(()) } ConstKind::Value(const_bytes) => render_const_scalar( @@ -2041,7 +2043,9 @@ impl<'db> HirDisplay<'db> for Region<'db> { RegionKind::ReEarlyParam(param) => { let generics = generics(f.db, param.id.parent); let param_data = &generics[param.id.local_id]; + f.start_location_link_generic(param.id.into()); write!(f, "{}", param_data.name.display(f.db, f.edition()))?; + f.end_location_link(); Ok(()) } RegionKind::ReBound(BoundVarIndexKind::Bound(db), idx) => { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 79cc2bdf8f7b8..c74e3104c14e0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -1258,7 +1258,7 @@ where } #[test] - fn generic_param_inlay_hint_has_location_link() { + fn type_param_inlay_hint_has_location_link() { check_expect( InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, r#" @@ -1292,4 +1292,94 @@ fn identity(t: T) -> T { "#]], ); } + + #[test] + fn const_param_inlay_hint_has_location_link() { + check_expect( + InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, + r#" +fn f() { + let x = [0; N]; +} +"#, + expect![[r#" + [ + ( + 33..34, + [ + "[i32; ", + InlayHintLabelPart { + text: "N", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 11..12, + }, + ), + ), + tooltip: "", + }, + "]", + ], + ), + ] + "#]], + ); + } + + #[test] + fn lifetime_param_inlay_hint_has_location_link() { + check_expect( + InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, + r#" +struct S<'lt>(*mut &'lt ()); + +fn f<'a>() { + let x = S::<'a>(loop {}); +} +"#, + expect![[r#" + [ + ( + 51..52, + [ + InlayHintLabelPart { + text: "S", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), + ), + tooltip: "", + }, + "<", + InlayHintLabelPart { + text: "'a", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 35..37, + }, + ), + ), + tooltip: "", + }, + ">", + ], + ), + ] + "#]], + ); + } } From 6326896b0c42eea27125b78fbed455a035e5e064 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 3 Jan 2026 09:12:32 +0530 Subject: [PATCH 20/34] emit same info via localfilename and filename --- .../rust-analyzer/crates/load-cargo/src/lib.rs | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 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 023253f23f423..4ffc8383b6202 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -563,20 +563,10 @@ impl ProcMacroExpander for Expander { let source_root_id = db.file_source_root(file).source_root_id(db); let source_root = db.source_root(source_root_id).source_root(db); - let path = source_root.path_for_file(&file); - - let name = path - .and_then(|p| p.as_path()) - .and_then(|p| p.file_name()) - .map(|n| n.to_owned()) - .or_else(|| { - path.and_then(|p| { - p.name_and_extension().map(|(name, ext)| match ext { - Some(ext) => format!("{name}.{ext}"), - None => name.into(), - }) - }) - }) + let name = source_root + .path_for_file(&file) + .and_then(|path| path.as_path()) + .and_then(|path| path.file_name().map(|filename| filename.to_owned())) .unwrap_or_default(); Ok(SubResponse::FileNameResult { name }) From c56c36ea7d1e0f15c41bfaf0dfe62b8e7954758e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 3 Jan 2026 14:58:47 +0530 Subject: [PATCH 21/34] use fullpath instead of filename --- src/tools/rust-analyzer/crates/load-cargo/src/lib.rs | 12 ++++++------ .../proc-macro-api/src/bidirectional_protocol/msg.rs | 8 ++++---- .../crates/proc-macro-srv-cli/src/main_loop.rs | 8 ++++---- 3 files changed, 14 insertions(+), 14 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 4ffc8383b6202..e01ce0b129daf 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -540,7 +540,7 @@ impl ProcMacroExpander for Expander { current_dir: String, ) -> Result { let mut cb = |req| match req { - SubRequest::LocalFileName { file_id } => { + SubRequest::LocalFilePath { file_id } => { let file = FileId::from_raw(file_id); let source_root_id = db.file_source_root(file).source_root_id(db); let source_root = db.source_root(source_root_id).source_root(db); @@ -548,9 +548,9 @@ impl ProcMacroExpander for Expander { let name = source_root .path_for_file(&file) .and_then(|path| path.as_path()) - .and_then(|path| path.file_name().map(|filename| filename.to_owned())); + .map(|path| path.to_string()); - Ok(SubResponse::LocalFileNameResult { name }) + Ok(SubResponse::LocalFilePathResult { name }) } SubRequest::SourceText { file_id, start, end } => { let file = FileId::from_raw(file_id); @@ -558,7 +558,7 @@ impl ProcMacroExpander for Expander { let slice = text.get(start as usize..end as usize).map(ToOwned::to_owned); Ok(SubResponse::SourceTextResult { text: slice }) } - SubRequest::FileName { file_id } => { + SubRequest::FilePath { file_id } => { let file = FileId::from_raw(file_id); let source_root_id = db.file_source_root(file).source_root_id(db); let source_root = db.source_root(source_root_id).source_root(db); @@ -566,10 +566,10 @@ impl ProcMacroExpander for Expander { let name = source_root .path_for_file(&file) .and_then(|path| path.as_path()) - .and_then(|path| path.file_name().map(|filename| filename.to_owned())) + .map(|path| path.to_string()) .unwrap_or_default(); - Ok(SubResponse::FileNameResult { name }) + Ok(SubResponse::FilePathResult { name }) } }; match self.0.expand( 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 2819a92fe33a1..558954f7619d6 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 @@ -10,16 +10,16 @@ use crate::{ #[derive(Debug, Serialize, Deserialize)] pub enum SubRequest { - FileName { file_id: u32 }, + FilePath { file_id: u32 }, SourceText { file_id: u32, start: u32, end: u32 }, - LocalFileName { file_id: u32 }, + LocalFilePath { file_id: u32 }, } #[derive(Debug, Serialize, Deserialize)] pub enum SubResponse { - FileNameResult { name: String }, + FilePathResult { name: String }, SourceTextResult { text: Option }, - LocalFileNameResult { name: Option }, + LocalFilePathResult { name: Option }, } #[derive(Debug, Serialize, Deserialize)] 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 77cb8760a100c..8fe3e93e4702f 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 @@ -186,9 +186,9 @@ impl<'a, C: Codec> ProcMacroClientHandle<'a, C> { impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { fn file(&mut self, file_id: u32) -> String { - match self.roundtrip(bidirectional::SubRequest::FileName { file_id }) { + match self.roundtrip(bidirectional::SubRequest::FilePath { file_id }) { Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::FileNameResult { name }, + bidirectional::SubResponse::FilePathResult { name }, )) => name, _ => String::new(), } @@ -204,9 +204,9 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl } fn local_file(&mut self, file_id: u32) -> Option { - match self.roundtrip(bidirectional::SubRequest::LocalFileName { file_id }) { + match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id }) { Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::LocalFileNameResult { name }, + bidirectional::SubResponse::LocalFilePathResult { name }, )) => name, _ => None, } From 7af3130d257bf06c4cb4ff4e160d69d8180b9c60 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 3 Jan 2026 10:47:41 +0100 Subject: [PATCH 22/34] perf: Only compute lang items for `#![feature(lang_items)]` crates --- .../crates/hir-def/src/lang_item.rs | 4 ++ .../crates/hir-ty/src/consteval/tests.rs | 3 ++ .../crates/hir-ty/src/tests/incremental.rs | 37 +++---------------- .../crates/hir-ty/src/tests/patterns.rs | 19 ++++++---- .../crates/hir-ty/src/tests/regression.rs | 1 + .../hir-ty/src/tests/regression/new_solver.rs | 1 + .../crates/hir-ty/src/tests/simple.rs | 36 ++++++++++-------- .../ide-completion/src/tests/flyimport.rs | 2 +- .../handlers/trait_impl_incorrect_safety.rs | 1 + .../crates/ide/src/hover/tests.rs | 1 + .../crates/ide/src/inlay_hints/bounds.rs | 2 +- .../crates/intern/src/symbol/symbols.rs | 1 + .../crates/test-utils/src/minicore.rs | 1 + 13 files changed, 51 insertions(+), 58 deletions(-) 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 d3f4480b207e7..eba4d87ec9f8a 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 @@ -40,6 +40,10 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option i32 { "trait_environment_query", "lang_items", "crate_lang_items", - "AttrFlags::query_", - "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", "expr_scopes_shim", "InferenceResult::for_body_", "function_signature_shim", "function_signature_with_source_map_shim", + "AttrFlags::query_", "body_shim", "body_with_source_map_shim", "trait_environment_query", @@ -149,6 +148,7 @@ fn baz() -> i32 { "InferenceResult::for_body_", "function_signature_shim", "function_signature_with_source_map_shim", + "AttrFlags::query_", "body_shim", "body_with_source_map_shim", "trait_environment_query", @@ -197,13 +197,13 @@ fn baz() -> i32 { "body_with_source_map_shim", "body_shim", "AttrFlags::query_", - "AttrFlags::query_", "function_signature_with_source_map_shim", "function_signature_shim", "body_with_source_map_shim", "body_shim", "InferenceResult::for_body_", "expr_scopes_shim", + "AttrFlags::query_", "function_signature_with_source_map_shim", "function_signature_shim", "body_with_source_map_shim", @@ -245,8 +245,6 @@ $0", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", - "AttrFlags::query_", - "AttrFlags::query_", ] "#]], ); @@ -284,9 +282,6 @@ pub struct NewStruct { "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", - "AttrFlags::query_", - "AttrFlags::query_", - "AttrFlags::query_", ] "#]], ); @@ -324,8 +319,6 @@ $0", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", - "AttrFlags::query_", - "AttrFlags::query_", ] "#]], ); @@ -364,12 +357,6 @@ pub enum SomeEnum { "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", - "AttrFlags::query_", - "AttrFlags::query_", - "AttrFlags::query_", - "EnumVariants::of_", - "AttrFlags::query_", - "AttrFlags::query_", ] "#]], ); @@ -407,8 +394,6 @@ $0", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", - "AttrFlags::query_", - "AttrFlags::query_", ] "#]], ); @@ -444,8 +429,6 @@ fn bar() -> f32 { "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", - "AttrFlags::query_", - "AttrFlags::query_", ] "#]], ); @@ -487,9 +470,6 @@ $0", "TraitImpls::for_crate_", "lang_items", "crate_lang_items", - "AttrFlags::query_", - "AttrFlags::query_", - "AttrFlags::query_", ] "#]], ); @@ -533,12 +513,6 @@ impl SomeStruct { "crate_local_def_map", "TraitImpls::for_crate_", "crate_lang_items", - "AttrFlags::query_", - "ImplItems::of_", - "AttrFlags::query_", - "AttrFlags::query_", - "AttrFlags::query_", - "AttrFlags::query_", ] "#]], ); @@ -610,7 +584,6 @@ fn main() { "trait_environment_query", "lang_items", "crate_lang_items", - "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", @@ -623,6 +596,7 @@ fn main() { "expr_scopes_shim", "struct_signature_shim", "struct_signature_with_source_map_shim", + "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "value_ty_query", "InherentImpls::for_crate_", @@ -702,8 +676,6 @@ fn main() { "body_with_source_map_shim", "body_shim", "crate_lang_items", - "AttrFlags::query_", - "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "GenericPredicates::query_with_diagnostics_", "ImplTraits::return_type_impl_traits_", @@ -713,6 +685,7 @@ fn main() { "ImplTraits::return_type_impl_traits_", "expr_scopes_shim", "struct_signature_with_source_map_shim", + "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "InherentImpls::for_crate_", "callable_item_signature_query", 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 c312b167596ff..0b776938c5b3b 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 @@ -794,6 +794,8 @@ fn slice_tail_pattern() { fn box_pattern() { check_infer( r#" + #![feature(lang_items)] + pub struct Global; #[lang = "owned_box"] pub struct Box(T); @@ -805,13 +807,13 @@ fn box_pattern() { } "#, expect![[r#" - 83..89 'params': Box - 101..155 '{ ... } }': () - 107..153 'match ... }': () - 113..119 'params': Box - 130..141 'box integer': Box - 134..141 'integer': i32 - 145..147 '{}': () + 108..114 'params': Box + 126..180 '{ ... } }': () + 132..178 'match ... }': () + 138..144 'params': Box + 155..166 'box integer': Box + 159..166 'integer': i32 + 170..172 '{}': () "#]], ); check_infer( @@ -831,7 +833,6 @@ fn box_pattern() { 76..122 'match ... }': () 82..88 'params': Box 99..110 'box integer': Box - 103..110 'integer': i32 114..116 '{}': () "#]], ); @@ -1142,6 +1143,7 @@ fn my_fn(#[cfg(feature = "feature")] u8: u8, u32: u32) {} fn var_args() { check_types( r#" +#![feature(lang_items)] #[lang = "va_list"] pub struct VaListImpl<'f>; fn my_fn(foo: ...) {} @@ -1156,6 +1158,7 @@ fn my_fn2(bar: u32, foo: ...) {} fn var_args_cond() { check_types( r#" +#![feature(lang_items)] #[lang = "va_list"] pub struct VaListImpl<'f>; fn my_fn(bar: u32, #[cfg(FALSE)] foo: ..., #[cfg(not(FALSE))] foo: u32) { 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 f03f8d754f2a6..c805f030446cb 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 @@ -2374,6 +2374,7 @@ fn rust_destruct_option_clone() { check_types( r#" //- minicore: option, drop +#![feature(lang_items)] fn test(o: &Option) { o.my_clone(); //^^^^^^^^^^^^ Option 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 e11cc85e7ff17..a4554673cdd5e 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 @@ -234,6 +234,7 @@ fn main() { // toolchains <= 1.88.0, before sized-hierarchy. check_no_mismatches( r#" +#![feature(lang_items)] #[lang = "sized"] pub trait Sized {} 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 db557b75071f6..6367521841abd 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 @@ -2702,6 +2702,8 @@ fn box_into_vec() { check_infer( r#" //- /core.rs crate:core +#![feature(lang_items)] + #[lang = "sized"] pub trait Sized {} @@ -2745,22 +2747,22 @@ struct Astruct; impl B for Astruct {} "#, expect![[r#" - 614..618 'self': Box<[T], A> - 647..679 '{ ... }': Vec - 693..863 '{ ...])); }': () - 703..706 'vec': Vec - 709..724 '<[_]>::into_vec': fn into_vec(Box<[i32], Global>) -> Vec - 709..755 '<[_]>:...i32]))': Vec - 725..754 '#[rust...1i32])': Box<[i32; 1], Global> - 747..753 '[1i32]': [i32; 1] - 748..752 '1i32': i32 - 765..766 'v': Vec, Global> - 786..803 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> - 786..860 '<[_]> ...ct)]))': Vec, Global> - 804..859 '#[rust...uct)])': Box<[Box; 1], Global> - 826..858 '[#[rus...ruct)]': [Box; 1] - 827..857 '#[rust...truct)': Box - 849..856 'Astruct': Astruct + 639..643 'self': Box<[T], A> + 672..704 '{ ... }': Vec + 718..888 '{ ...])); }': () + 728..731 'vec': Vec + 734..749 '<[_]>::into_vec': fn into_vec(Box<[i32], Global>) -> Vec + 734..780 '<[_]>:...i32]))': Vec + 750..779 '#[rust...1i32])': Box<[i32; 1], Global> + 772..778 '[1i32]': [i32; 1] + 773..777 '1i32': i32 + 790..791 'v': Vec, Global> + 811..828 '<[_]> ...to_vec': fn into_vec, Global>(Box<[Box], Global>) -> Vec, Global> + 811..885 '<[_]> ...ct)]))': Vec, Global> + 829..884 '#[rust...uct)])': Box<[Box; 1], Global> + 851..883 '[#[rus...ruct)]': [Box; 1] + 852..882 '#[rust...truct)': Box + 874..881 'Astruct': Astruct "#]], ) } @@ -3647,6 +3649,8 @@ fn main() { fn cstring_literals() { check_types( r#" +#![feature(lang_items)] + #[lang = "CStr"] pub struct CStr; 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 2912457da1f73..797df3f163dab 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 "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs index 3414e972d5c90..c5b2f499d3067 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs @@ -64,6 +64,7 @@ unsafe trait Unsafe {} fn drop_may_dangle() { check_diagnostics( r#" +#![feature(lang_items)] #[lang = "drop"] trait Drop {} struct S; 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 f42d3cf0dc413..0b518021e39e4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -4089,6 +4089,7 @@ fn foo() { let fo$0o = async { S }; } //- /core.rs crate:core +#![feature(lang_items)] pub mod future { #[lang = "future_trait"] pub trait Future {} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs index c9fbdf3ae7546..045559fd7f464 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs @@ -143,7 +143,7 @@ fn foo() {} file_id: FileId( 1, ), - range: 446..451, + range: 470..475, }, ), ), 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 3e325b2f990d4..b6efc599f181c 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -297,6 +297,7 @@ define_symbols! { iterator, keyword, lang, + lang_items, le, Left, len, 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 b7c09391ec379..01274a9835f40 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -80,6 +80,7 @@ //! offset_of: #![rustc_coherence_is_core] +#![feature(lang_items)] pub mod marker { // region:sized From c99f9c249b017c6909ea44bba6fe3efcd56208ed Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 3 Jan 2026 19:04:35 +0530 Subject: [PATCH 23/34] Replace sourcedb with expanddb --- .../hir-def/src/macro_expansion_tests/mod.rs | 4 ++-- .../crates/hir-expand/src/proc_macro.rs | 4 ++-- .../crates/load-cargo/src/lib.rs | 19 +++++++++--------- .../crates/test-fixture/src/lib.rs | 20 +++++++++---------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 3f136bc591728..c63f2c1d786b5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -16,7 +16,7 @@ mod proc_macros; use std::{any::TypeId, iter, ops::Range, sync}; -use base_db::{RootQueryDb, SourceDatabase}; +use base_db::RootQueryDb; use expect_test::Expect; use hir_expand::{ AstId, ExpansionInfo, InFile, MacroCallId, MacroCallKind, MacroKind, @@ -387,7 +387,7 @@ struct IdentityWhenValidProcMacroExpander; impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &base_db::Env, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index d2614aa5f1491..467eae3122dfb 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -4,7 +4,7 @@ use core::fmt; use std::any::Any; use std::{panic::RefUnwindSafe, sync}; -use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError, SourceDatabase}; +use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError}; use intern::Symbol; use rustc_hash::FxHashMap; use span::Span; @@ -25,7 +25,7 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + Any { /// [`ProcMacroKind::Attr`]), environment variables, and span information. fn expand( &self, - db: &dyn SourceDatabase, + db: &dyn ExpandDatabase, subtree: &tt::TopSubtree, attrs: Option<&tt::TopSubtree>, env: &Env, 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 e01ce0b129daf..94fac0bd33f7b 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -11,15 +11,16 @@ extern crate rustc_driver as _; use std::{any::Any, collections::hash_map::Entry, mem, path::Path, sync}; use crossbeam_channel::{Receiver, unbounded}; -use hir_expand::proc_macro::{ - ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacroLoadResult, - ProcMacrosBuilder, +use hir_expand::{ + db::ExpandDatabase, + proc_macro::{ + ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacroLoadResult, + ProcMacrosBuilder, + }, }; use ide_db::{ - ChangeWithProcMacros, FxHashMap, RootDatabase, - base_db::{ - CrateGraphBuilder, Env, ProcMacroLoadingError, SourceDatabase, SourceRoot, SourceRootId, - }, + ChangeWithProcMacros, EditionedFileId, FxHashMap, RootDatabase, + base_db::{CrateGraphBuilder, Env, ProcMacroLoadingError, SourceRoot, SourceRootId}, prime_caches, }; use itertools::Itertools; @@ -33,7 +34,7 @@ use proc_macro_api::{ use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; use span::Span; use vfs::{ - AbsPath, AbsPathBuf, FileId, VfsPath, + AbsPath, AbsPathBuf, VfsPath, file_set::FileSetConfig, loader::{Handle, LoadingProgress}, }; @@ -530,7 +531,7 @@ struct Expander(proc_macro_api::ProcMacro); impl ProcMacroExpander for Expander { fn expand( &self, - db: &dyn SourceDatabase, + db: &dyn ExpandDatabase, subtree: &tt::TopSubtree, attrs: Option<&tt::TopSubtree>, env: &Env, diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index b9c389c7694e5..d81f27d7c3b1d 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -738,7 +738,7 @@ struct IdentityProcMacroExpander; impl ProcMacroExpander for IdentityProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -761,7 +761,7 @@ struct Issue18089ProcMacroExpander; impl ProcMacroExpander for Issue18089ProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -797,7 +797,7 @@ struct AttributeInputReplaceProcMacroExpander; impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, _: &TopSubtree, attrs: Option<&TopSubtree>, _: &Env, @@ -821,7 +821,7 @@ struct Issue18840ProcMacroExpander; impl ProcMacroExpander for Issue18840ProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, fn_: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -858,7 +858,7 @@ struct MirrorProcMacroExpander; impl ProcMacroExpander for MirrorProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, input: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -897,7 +897,7 @@ struct ShortenProcMacroExpander; impl ProcMacroExpander for ShortenProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, input: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -942,7 +942,7 @@ struct Issue17479ProcMacroExpander; impl ProcMacroExpander for Issue17479ProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -973,7 +973,7 @@ struct Issue18898ProcMacroExpander; impl ProcMacroExpander for Issue18898ProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -1027,7 +1027,7 @@ struct DisallowCfgProcMacroExpander; impl ProcMacroExpander for DisallowCfgProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, subtree: &TopSubtree, _: Option<&TopSubtree>, _: &Env, @@ -1059,7 +1059,7 @@ struct GenerateSuffixedTypeProcMacroExpander; impl ProcMacroExpander for GenerateSuffixedTypeProcMacroExpander { fn expand( &self, - _: &dyn SourceDatabase, + _: &dyn ExpandDatabase, subtree: &TopSubtree, _attrs: Option<&TopSubtree>, _env: &Env, From 38b2e9262075debeb7dfb125d9ef78268e0a069d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 3 Jan 2026 19:06:26 +0530 Subject: [PATCH 24/34] added serde to span --- src/tools/rust-analyzer/Cargo.lock | 2 ++ .../rust-analyzer/crates/span/Cargo.toml | 1 + .../rust-analyzer/crates/span/src/ast_id.rs | 3 ++- .../rust-analyzer/crates/span/src/hygiene.rs | 5 +++- .../rust-analyzer/crates/span/src/lib.rs | 23 ++++++++++++++++--- 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 42eaeb01f1f2a..453c35a574d78 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1881,6 +1881,7 @@ dependencies = [ "postcard", "proc-macro-api", "proc-macro-srv", + "span", ] [[package]] @@ -2648,6 +2649,7 @@ dependencies = [ "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 2.1.1", "salsa", + "serde", "stdx", "syntax", "text-size 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/tools/rust-analyzer/crates/span/Cargo.toml b/src/tools/rust-analyzer/crates/span/Cargo.toml index cfb319d688b6a..d331f63d0c536 100644 --- a/src/tools/rust-analyzer/crates/span/Cargo.toml +++ b/src/tools/rust-analyzer/crates/span/Cargo.toml @@ -21,6 +21,7 @@ text-size.workspace = true vfs.workspace = true syntax.workspace = true stdx.workspace = true +serde.workspace = true [dev-dependencies] syntax.workspace = true diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs index 599b3c7175228..37954ca0b0627 100644 --- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs +++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs @@ -29,6 +29,7 @@ use std::{ use la_arena::{Arena, Idx, RawIdx}; use rustc_hash::{FxBuildHasher, FxHashMap}; +use serde::{Deserialize, Serialize}; use syntax::{ AstNode, AstPtr, SyntaxKind, SyntaxNode, SyntaxNodePtr, ast::{self, HasName}, @@ -54,7 +55,7 @@ pub const NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId = ErasedFileAstId(pack_hash_index_and_kind(0, 0, ErasedFileAstIdKind::NoDownmap as u32)); /// This is a type erased FileAstId. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct ErasedFileAstId(u32); impl fmt::Debug for ErasedFileAstId { diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 6805417177cde..1a82f221c6e4d 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -21,11 +21,14 @@ //! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer. use std::fmt; +#[cfg(feature = "salsa")] +use serde::{Deserialize, Serialize}; + use crate::Edition; /// A syntax context describes a hierarchy tracking order of macro definitions. #[cfg(feature = "salsa")] -#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] pub struct SyntaxContext( /// # Invariant /// diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index bfe7b2620d56c..f6581de38ce26 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -20,6 +20,7 @@ pub use self::{ map::{RealSpanMap, SpanMap}, }; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; pub use syntax::Edition; pub use text_size::{TextRange, TextSize}; pub use vfs::FileId; @@ -68,11 +69,12 @@ impl Span { /// Spans represent a region of code, used by the IDE to be able link macro inputs and outputs /// together. Positions in spans are relative to some [`SpanAnchor`] to make them more incremental /// friendly. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct Span { /// The text range of this span, relative to the anchor. /// We need the anchor for incrementality, as storing absolute ranges will require /// recomputation on every change in a file at all times. + #[serde(serialize_with = "serialize_text_range", deserialize_with = "deserialize_text_range")] pub range: TextRange, /// The anchor this span is relative to. pub anchor: SpanAnchor, @@ -80,6 +82,21 @@ pub struct Span { pub ctx: SyntaxContext, } +fn serialize_text_range(range: &TextRange, serializer: S) -> Result +where + S: Serializer, +{ + (u32::from(range.start()), u32::from(range.end())).serialize(serializer) +} + +fn deserialize_text_range<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let (start, end) = <(u32, u32)>::deserialize(deserializer)?; + Ok(TextRange::new(TextSize::from(start), TextSize::from(end))) +} + impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { @@ -112,7 +129,7 @@ impl fmt::Display for Span { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct SpanAnchor { pub file_id: EditionedFileId, pub ast_id: ErasedFileAstId, @@ -126,7 +143,7 @@ impl fmt::Debug for SpanAnchor { /// A [`FileId`] and [`Edition`] bundled up together. /// The MSB is reserved for `HirFileId` encoding, more upper bits are used to then encode the edition. -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] pub struct EditionedFileId(u32); impl fmt::Debug for EditionedFileId { From 3941fed5017b6dce330fb14972fca968f80b3303 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 3 Jan 2026 19:13:06 +0530 Subject: [PATCH 25/34] add span to proc-macro-srv-cliy --- src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml | 1 + 1 file changed, 1 insertion(+) 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 2c6e5a16ee06f..875d9f6801a3a 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 @@ -14,6 +14,7 @@ publish = false proc-macro-srv.workspace = true proc-macro-api.workspace = true postcard.workspace = true +span.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} [features] From be566f4e6b0f3d160d060d0b0508256d549e24bb Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 3 Jan 2026 19:15:42 +0530 Subject: [PATCH 26/34] refactor subreq/resp variants to carry span --- .../src/bidirectional_protocol/msg.rs | 7 ++++--- .../crates/proc-macro-srv-cli/src/main_loop.rs | 13 +++++++------ .../rust-analyzer/crates/proc-macro-srv/src/lib.rs | 6 +++--- .../src/server_impl/rust_analyzer_span.rs | 13 +++---------- 4 files changed, 17 insertions(+), 22 deletions(-) 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 558954f7619d6..73cf58bd9ec75 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 @@ -2,6 +2,7 @@ use paths::Utf8PathBuf; use serde::{Deserialize, Serialize}; +use tt::Span; use crate::{ ProcMacroKind, @@ -10,9 +11,9 @@ use crate::{ #[derive(Debug, Serialize, Deserialize)] pub enum SubRequest { - FilePath { file_id: u32 }, - SourceText { file_id: u32, start: u32, end: u32 }, - LocalFilePath { file_id: u32 }, + FilePath { span: Span }, + SourceText { span: Span }, + LocalFilePath { span: Span }, } #[derive(Debug, Serialize, Deserialize)] 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 8fe3e93e4702f..a5bf84df04de9 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 @@ -6,6 +6,7 @@ use proc_macro_api::{ transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, version::CURRENT_API_VERSION, }; +use span::Span; use std::io; use legacy::Message; @@ -185,8 +186,8 @@ impl<'a, C: Codec> ProcMacroClientHandle<'a, C> { } impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { - fn file(&mut self, file_id: u32) -> String { - match self.roundtrip(bidirectional::SubRequest::FilePath { file_id }) { + fn file(&mut self, span: Span) -> String { + match self.roundtrip(bidirectional::SubRequest::FilePath { span }) { Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::FilePathResult { name }, )) => name, @@ -194,8 +195,8 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl } } - fn source_text(&mut self, file_id: u32, start: u32, end: u32) -> Option { - match self.roundtrip(bidirectional::SubRequest::SourceText { file_id, start, end }) { + fn source_text(&mut self, span: Span) -> Option { + match self.roundtrip(bidirectional::SubRequest::SourceText { span }) { Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::SourceTextResult { text }, )) => text, @@ -203,8 +204,8 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl } } - fn local_file(&mut self, file_id: u32) -> Option { - match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id }) { + fn local_file(&mut self, span: Span) -> Option { + match self.roundtrip(bidirectional::SubRequest::LocalFilePath { span }) { Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::LocalFilePathResult { name }, )) => name, 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 8de712dbd3863..6620800779bde 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 @@ -94,9 +94,9 @@ impl<'env> ProcMacroSrv<'env> { pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); pub trait ProcMacroClientInterface { - fn file(&mut self, file_id: u32) -> String; - fn source_text(&mut self, file_id: u32, start: u32, end: u32) -> Option; - fn local_file(&mut self, file_id: u32) -> Option; + fn file(&mut self, span: Span) -> String; + fn source_text(&mut self, span: Span) -> Option; + fn local_file(&mut self, span: Span) -> Option; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; 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 7a9d655431f51..c4392a9ae1101 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 @@ -128,13 +128,10 @@ impl server::Span for RaSpanServer<'_> { format!("{:?}", span) } fn file(&mut self, span: Self::Span) -> String { - self.callback - .as_mut() - .map(|cb| cb.file(span.anchor.file_id.file_id().index())) - .unwrap_or_default() + self.callback.as_mut().map(|cb| cb.file(span)).unwrap_or_default() } fn local_file(&mut self, span: Self::Span) -> Option { - self.callback.as_mut().and_then(|cb| cb.local_file(span.anchor.file_id.file_id().index())) + self.callback.as_mut().and_then(|cb| cb.local_file(span)) } fn save_span(&mut self, _span: Self::Span) -> usize { // FIXME, quote is incompatible with third-party tools @@ -153,11 +150,7 @@ impl server::Span for RaSpanServer<'_> { /// See PR: /// https://github.com/rust-lang/rust/pull/55780 fn source_text(&mut self, span: Self::Span) -> Option { - let file_id = span.anchor.file_id; - let start: u32 = span.range.start().into(); - let end: u32 = span.range.end().into(); - - self.callback.as_mut()?.source_text(file_id.file_id().index(), start, end) + self.callback.as_mut()?.source_text(span) } fn parent(&mut self, _span: Self::Span) -> Option { From 4f5502e8fb2679b76390bc7bebf4b457e853176d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 3 Jan 2026 19:18:36 +0530 Subject: [PATCH 27/34] Add span to callbacks and correct the source_text implementation --- .../crates/load-cargo/src/lib.rs | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 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 94fac0bd33f7b..474046ee2fa7f 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -541,31 +541,37 @@ impl ProcMacroExpander for Expander { current_dir: String, ) -> Result { let mut cb = |req| match req { - SubRequest::LocalFilePath { file_id } => { - let file = FileId::from_raw(file_id); - let source_root_id = db.file_source_root(file).source_root_id(db); + SubRequest::LocalFilePath { span } => { + let file_id = span.anchor.file_id.file_id(); + let source_root_id = db.file_source_root(file_id).source_root_id(db); let source_root = db.source_root(source_root_id).source_root(db); - let name = source_root - .path_for_file(&file) + .path_for_file(&file_id) .and_then(|path| path.as_path()) .map(|path| path.to_string()); Ok(SubResponse::LocalFilePathResult { name }) } - SubRequest::SourceText { file_id, start, end } => { - let file = FileId::from_raw(file_id); - let text = db.file_text(file).text(db); - let slice = text.get(start as usize..end as usize).map(ToOwned::to_owned); - Ok(SubResponse::SourceTextResult { text: slice }) + SubRequest::SourceText { span } => { + let anchor = span.anchor; + let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id); + let range = db + .ast_id_map(hir_expand::HirFileId::FileId(file_id)) + .get_erased(anchor.ast_id) + .text_range(); + let source = db.file_text(anchor.file_id.file_id()).text(db); + let text = source + .get(usize::from(range.start())..usize::from(range.end())) + .map(ToOwned::to_owned); + + Ok(SubResponse::SourceTextResult { text }) } - SubRequest::FilePath { file_id } => { - let file = FileId::from_raw(file_id); - let source_root_id = db.file_source_root(file).source_root_id(db); + SubRequest::FilePath { span } => { + let file_id = span.anchor.file_id.file_id(); + let source_root_id = db.file_source_root(file_id).source_root_id(db); let source_root = db.source_root(source_root_id).source_root(db); - let name = source_root - .path_for_file(&file) + .path_for_file(&file_id) .and_then(|path| path.as_path()) .map(|path| path.to_string()) .unwrap_or_default(); From ce19860f800c974dd1eb66a3aa9c2a6716d90bb7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 3 Jan 2026 20:10:00 +0530 Subject: [PATCH 28/34] remove span related API from proc-macro-srv/cli/api and remove span from procmacrosrvcli --- src/tools/rust-analyzer/Cargo.lock | 1 - .../crates/load-cargo/src/lib.rs | 36 ++++++++++--------- .../src/bidirectional_protocol/msg.rs | 7 ++-- .../crates/proc-macro-srv-cli/Cargo.toml | 1 - .../proc-macro-srv-cli/src/main_loop.rs | 14 ++++---- .../crates/proc-macro-srv/src/lib.rs | 6 ++-- .../src/server_impl/rust_analyzer_span.rs | 19 ++++++++-- 7 files changed, 49 insertions(+), 35 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 453c35a574d78..fbaeff1eb5608 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1881,7 +1881,6 @@ dependencies = [ "postcard", "proc-macro-api", "proc-macro-srv", - "span", ] [[package]] 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 474046ee2fa7f..c302e266febd2 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -34,7 +34,7 @@ use proc_macro_api::{ use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; use span::Span; use vfs::{ - AbsPath, AbsPathBuf, VfsPath, + AbsPath, AbsPathBuf, FileId, VfsPath, file_set::FileSetConfig, loader::{Handle, LoadingProgress}, }; @@ -541,8 +541,8 @@ impl ProcMacroExpander for Expander { current_dir: String, ) -> Result { let mut cb = |req| match req { - SubRequest::LocalFilePath { span } => { - let file_id = span.anchor.file_id.file_id(); + SubRequest::LocalFilePath { file_id } => { + let file_id = FileId::from_raw(file_id); let source_root_id = db.file_source_root(file_id).source_root_id(db); let source_root = db.source_root(source_root_id).source_root(db); let name = source_root @@ -552,22 +552,26 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::LocalFilePathResult { name }) } - SubRequest::SourceText { span } => { - let anchor = span.anchor; - let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id); - let range = db - .ast_id_map(hir_expand::HirFileId::FileId(file_id)) - .get_erased(anchor.ast_id) - .text_range(); - let source = db.file_text(anchor.file_id.file_id()).text(db); - let text = source - .get(usize::from(range.start())..usize::from(range.end())) - .map(ToOwned::to_owned); + SubRequest::SourceText { file_id, ast_id, start, end } => { + let raw_file_id = FileId::from_raw(file_id); + let editioned_file_id = span::EditionedFileId::from_raw(file_id); + let ast_id = span::ErasedFileAstId::from_raw(ast_id); + let hir_file_id = EditionedFileId::from_span_guess_origin(db, editioned_file_id); + let anchor_offset = db + .ast_id_map(hir_expand::HirFileId::FileId(hir_file_id)) + .get_erased(ast_id) + .text_range() + .start(); + let anchor_offset = u32::from(anchor_offset); + let abs_start = start + anchor_offset; + let abs_end = end + anchor_offset; + let source = db.file_text(raw_file_id).text(db); + let text = source.get(abs_start as usize..abs_end as usize).map(ToOwned::to_owned); Ok(SubResponse::SourceTextResult { text }) } - SubRequest::FilePath { span } => { - let file_id = span.anchor.file_id.file_id(); + SubRequest::FilePath { file_id } => { + let file_id = FileId::from_raw(file_id); let source_root_id = db.file_source_root(file_id).source_root_id(db); let source_root = db.source_root(source_root_id).source_root(db); let name = source_root 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 73cf58bd9ec75..e41f8a5d7da76 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 @@ -2,7 +2,6 @@ use paths::Utf8PathBuf; use serde::{Deserialize, Serialize}; -use tt::Span; use crate::{ ProcMacroKind, @@ -11,9 +10,9 @@ use crate::{ #[derive(Debug, Serialize, Deserialize)] pub enum SubRequest { - FilePath { span: Span }, - SourceText { span: Span }, - LocalFilePath { span: Span }, + FilePath { file_id: u32 }, + SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 }, + LocalFilePath { file_id: u32 }, } #[derive(Debug, Serialize, Deserialize)] 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 875d9f6801a3a..2c6e5a16ee06f 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 @@ -14,7 +14,6 @@ publish = false proc-macro-srv.workspace = true proc-macro-api.workspace = true postcard.workspace = true -span.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} [features] 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 a5bf84df04de9..4891e073142de 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 @@ -6,7 +6,6 @@ use proc_macro_api::{ transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, version::CURRENT_API_VERSION, }; -use span::Span; use std::io; use legacy::Message; @@ -186,8 +185,8 @@ impl<'a, C: Codec> ProcMacroClientHandle<'a, C> { } impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { - fn file(&mut self, span: Span) -> String { - match self.roundtrip(bidirectional::SubRequest::FilePath { span }) { + fn file(&mut self, file_id: u32) -> String { + match self.roundtrip(bidirectional::SubRequest::FilePath { file_id }) { Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::FilePathResult { name }, )) => name, @@ -195,8 +194,9 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl } } - fn source_text(&mut self, span: Span) -> Option { - match self.roundtrip(bidirectional::SubRequest::SourceText { span }) { + fn source_text(&mut self, file_id: u32, ast_id: u32, start: u32, end: u32) -> Option { + match self.roundtrip(bidirectional::SubRequest::SourceText { file_id, ast_id, start, end }) + { Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::SourceTextResult { text }, )) => text, @@ -204,8 +204,8 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl } } - fn local_file(&mut self, span: Span) -> Option { - match self.roundtrip(bidirectional::SubRequest::LocalFilePath { span }) { + fn local_file(&mut self, file_id: u32) -> Option { + match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id }) { Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::LocalFilePathResult { name }, )) => name, 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 6620800779bde..d63aea947c1d1 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 @@ -94,9 +94,9 @@ impl<'env> ProcMacroSrv<'env> { pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); pub trait ProcMacroClientInterface { - fn file(&mut self, span: Span) -> String; - fn source_text(&mut self, span: Span) -> Option; - fn local_file(&mut self, span: Span) -> Option; + fn file(&mut self, file_id: u32) -> String; + fn source_text(&mut self, file_id: u32, ast_id: u32, start: u32, end: u32) -> Option; + fn local_file(&mut self, file_id: u32) -> Option; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; 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 c4392a9ae1101..2ce3b717cb0df 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 @@ -128,10 +128,13 @@ impl server::Span for RaSpanServer<'_> { format!("{:?}", span) } fn file(&mut self, span: Self::Span) -> String { - self.callback.as_mut().map(|cb| cb.file(span)).unwrap_or_default() + self.callback + .as_mut() + .map(|cb| cb.file(span.anchor.file_id.file_id().index())) + .unwrap_or_default() } fn local_file(&mut self, span: Self::Span) -> Option { - self.callback.as_mut().and_then(|cb| cb.local_file(span)) + self.callback.as_mut().and_then(|cb| cb.local_file(span.anchor.file_id.file_id().index())) } fn save_span(&mut self, _span: Self::Span) -> usize { // FIXME, quote is incompatible with third-party tools @@ -150,7 +153,17 @@ impl server::Span for RaSpanServer<'_> { /// See PR: /// https://github.com/rust-lang/rust/pull/55780 fn source_text(&mut self, span: Self::Span) -> Option { - self.callback.as_mut()?.source_text(span) + let file_id = span.anchor.file_id; + let ast_id = span.anchor.ast_id; + let start: u32 = span.range.start().into(); + let end: u32 = span.range.end().into(); + + self.callback.as_mut()?.source_text( + file_id.file_id().index(), + ast_id.into_raw(), + start, + end, + ) } fn parent(&mut self, _span: Self::Span) -> Option { From f3b06a10f259fffd2ae92334f91fd0365e33f66a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 3 Jan 2026 20:13:41 +0530 Subject: [PATCH 29/34] remove serde from span --- src/tools/rust-analyzer/Cargo.lock | 1 - .../rust-analyzer/crates/span/Cargo.toml | 1 - .../rust-analyzer/crates/span/src/ast_id.rs | 3 +-- .../rust-analyzer/crates/span/src/hygiene.rs | 8 ++----- .../rust-analyzer/crates/span/src/lib.rs | 23 +++---------------- 5 files changed, 6 insertions(+), 30 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index fbaeff1eb5608..42eaeb01f1f2a 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2648,7 +2648,6 @@ dependencies = [ "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-hash 2.1.1", "salsa", - "serde", "stdx", "syntax", "text-size 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/tools/rust-analyzer/crates/span/Cargo.toml b/src/tools/rust-analyzer/crates/span/Cargo.toml index d331f63d0c536..cfb319d688b6a 100644 --- a/src/tools/rust-analyzer/crates/span/Cargo.toml +++ b/src/tools/rust-analyzer/crates/span/Cargo.toml @@ -21,7 +21,6 @@ text-size.workspace = true vfs.workspace = true syntax.workspace = true stdx.workspace = true -serde.workspace = true [dev-dependencies] syntax.workspace = true diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs index 37954ca0b0627..599b3c7175228 100644 --- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs +++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs @@ -29,7 +29,6 @@ use std::{ use la_arena::{Arena, Idx, RawIdx}; use rustc_hash::{FxBuildHasher, FxHashMap}; -use serde::{Deserialize, Serialize}; use syntax::{ AstNode, AstPtr, SyntaxKind, SyntaxNode, SyntaxNodePtr, ast::{self, HasName}, @@ -55,7 +54,7 @@ pub const NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId = ErasedFileAstId(pack_hash_index_and_kind(0, 0, ErasedFileAstIdKind::NoDownmap as u32)); /// This is a type erased FileAstId. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct ErasedFileAstId(u32); impl fmt::Debug for ErasedFileAstId { diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 1a82f221c6e4d..ea4f4c5efb42f 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -19,16 +19,12 @@ //! # The Call-site Hierarchy //! //! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer. -use std::fmt; - -#[cfg(feature = "salsa")] -use serde::{Deserialize, Serialize}; - use crate::Edition; +use std::fmt; /// A syntax context describes a hierarchy tracking order of macro definitions. #[cfg(feature = "salsa")] -#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] pub struct SyntaxContext( /// # Invariant /// diff --git a/src/tools/rust-analyzer/crates/span/src/lib.rs b/src/tools/rust-analyzer/crates/span/src/lib.rs index f6581de38ce26..bfe7b2620d56c 100644 --- a/src/tools/rust-analyzer/crates/span/src/lib.rs +++ b/src/tools/rust-analyzer/crates/span/src/lib.rs @@ -20,7 +20,6 @@ pub use self::{ map::{RealSpanMap, SpanMap}, }; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; pub use syntax::Edition; pub use text_size::{TextRange, TextSize}; pub use vfs::FileId; @@ -69,12 +68,11 @@ impl Span { /// Spans represent a region of code, used by the IDE to be able link macro inputs and outputs /// together. Positions in spans are relative to some [`SpanAnchor`] to make them more incremental /// friendly. -#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct Span { /// The text range of this span, relative to the anchor. /// We need the anchor for incrementality, as storing absolute ranges will require /// recomputation on every change in a file at all times. - #[serde(serialize_with = "serialize_text_range", deserialize_with = "deserialize_text_range")] pub range: TextRange, /// The anchor this span is relative to. pub anchor: SpanAnchor, @@ -82,21 +80,6 @@ pub struct Span { pub ctx: SyntaxContext, } -fn serialize_text_range(range: &TextRange, serializer: S) -> Result -where - S: Serializer, -{ - (u32::from(range.start()), u32::from(range.end())).serialize(serializer) -} - -fn deserialize_text_range<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let (start, end) = <(u32, u32)>::deserialize(deserializer)?; - Ok(TextRange::new(TextSize::from(start), TextSize::from(end))) -} - impl fmt::Debug for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { @@ -129,7 +112,7 @@ impl fmt::Display for Span { } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct SpanAnchor { pub file_id: EditionedFileId, pub ast_id: ErasedFileAstId, @@ -143,7 +126,7 @@ impl fmt::Debug for SpanAnchor { /// A [`FileId`] and [`Edition`] bundled up together. /// The MSB is reserved for `HirFileId` encoding, more upper bits are used to then encode the edition. -#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct EditionedFileId(u32); impl fmt::Debug for EditionedFileId { From d4b9ea2e865f8de6fe994f1e9a7c0fa80b83dcbf Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 4 Jan 2026 11:44:42 +0100 Subject: [PATCH 30/34] internal: Clean up proc-macro-srv callback trait --- .../crates/base-db/src/editioned_file_id.rs | 3 ++ .../rust-analyzer/crates/hir-expand/src/db.rs | 14 +++++++-- .../crates/hir-expand/src/lib.rs | 24 ++++----------- .../crates/load-cargo/src/lib.rs | 29 +++++++++---------- .../crates/proc-macro-srv-cli/Cargo.toml | 1 + .../proc-macro-srv-cli/src/main_loop.rs | 22 +++++++++----- .../crates/proc-macro-srv/src/lib.rs | 7 +++-- .../src/server_impl/rust_analyzer_span.rs | 19 ++---------- 8 files changed, 58 insertions(+), 61 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs b/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs index e2791ffe6f0a2..13fb05d565479 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/editioned_file_id.rs @@ -26,6 +26,9 @@ const _: () = { krate: Crate, } + // FIXME: This poses an invalidation problem, if one constructs an `EditionedFileId` with a + // different crate then whatever the input of a memo used, it will invalidate the memo causing + // it to recompute even if the crate is not really used. /// We like to include the origin crate in an `EditionedFileId` (for use in the item tree), /// but this poses us a problem. /// 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 40d44cd1dba26..51767f87ffb9d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -9,8 +9,8 @@ use triomphe::Arc; use crate::{ AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, - EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, HirFileId, MacroCallId, - MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, + EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, FileRange, HirFileId, + MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, attrs::Meta, builtin::pseudo_derive_attr_expansion, cfg_process::attr_macro_input_to_token_tree, @@ -61,6 +61,9 @@ pub trait ExpandDatabase: RootQueryDb { #[salsa::lru(1024)] fn ast_id_map(&self, file_id: HirFileId) -> Arc; + #[salsa::transparent] + fn resolve_span(&self, span: Span) -> FileRange; + #[salsa::transparent] fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode; @@ -158,6 +161,13 @@ fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) -> } } +fn resolve_span(db: &dyn ExpandDatabase, Span { range, anchor, ctx: _ }: Span) -> FileRange { + let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id); + let anchor_offset = + db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start(); + FileRange { file_id, range: range + anchor_offset } +} + /// This expands the given macro call, but with different arguments. This is /// used for completion, where we want to see what 'would happen' if we insert a /// token. The `token_to_map` mapped down into the expansion, with the mapped 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 7b6a6135b3509..05541e782efd4 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -901,11 +901,8 @@ impl ExpansionInfo { let span = self.exp_map.span_at(token.start()); match &self.arg_map { SpanMap::RealSpanMap(_) => { - let file_id = - EditionedFileId::from_span_guess_origin(db, span.anchor.file_id).into(); - let anchor_offset = - db.ast_id_map(file_id).get_erased(span.anchor.ast_id).text_range().start(); - InFile { file_id, value: smallvec::smallvec![span.range + anchor_offset] } + let range = db.resolve_span(span); + InFile { file_id: range.file_id.into(), value: smallvec::smallvec![range.range] } } SpanMap::ExpansionSpanMap(arg_map) => { let Some(arg_node) = &self.arg.value else { @@ -947,7 +944,7 @@ pub fn map_node_range_up_rooted( range: TextRange, ) -> Option { let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root()); - let Span { range, anchor, ctx: _ } = spans.next()?; + let Span { range, anchor, ctx } = spans.next()?; let mut start = range.start(); let mut end = range.end(); @@ -958,10 +955,7 @@ pub fn map_node_range_up_rooted( start = start.min(span.range.start()); end = end.max(span.range.end()); } - let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id); - let anchor_offset = - db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start(); - Some(FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }) + Some(db.resolve_span(Span { range: TextRange::new(start, end), anchor, ctx })) } /// Maps up the text range out of the expansion hierarchy back into the original file its from. @@ -984,10 +978,7 @@ pub fn map_node_range_up( start = start.min(span.range.start()); end = end.max(span.range.end()); } - let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id); - let anchor_offset = - db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start(); - Some((FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }, ctx)) + Some((db.resolve_span(Span { range: TextRange::new(start, end), anchor, ctx }), ctx)) } /// Looks up the span at the given offset. @@ -997,10 +988,7 @@ pub fn span_for_offset( offset: TextSize, ) -> (FileRange, SyntaxContext) { let span = exp_map.span_at(offset); - let file_id = EditionedFileId::from_span_guess_origin(db, span.anchor.file_id); - let anchor_offset = - db.ast_id_map(file_id.into()).get_erased(span.anchor.ast_id).text_range().start(); - (FileRange { file_id, range: span.range + anchor_offset }, span.ctx) + (db.resolve_span(span), span.ctx) } /// In Rust, macros expand token trees to token trees. When we want to turn a 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 c302e266febd2..e8d98b1ce661d 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -19,7 +19,7 @@ use hir_expand::{ }, }; use ide_db::{ - ChangeWithProcMacros, EditionedFileId, FxHashMap, RootDatabase, + ChangeWithProcMacros, FxHashMap, RootDatabase, base_db::{CrateGraphBuilder, Env, ProcMacroLoadingError, SourceRoot, SourceRootId}, prime_caches, }; @@ -32,7 +32,8 @@ use proc_macro_api::{ }, }; use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace}; -use span::Span; +use span::{Span, SpanAnchor, SyntaxContext}; +use tt::{TextRange, TextSize}; use vfs::{ AbsPath, AbsPathBuf, FileId, VfsPath, file_set::FileSetConfig, @@ -553,20 +554,18 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::LocalFilePathResult { name }) } SubRequest::SourceText { file_id, ast_id, start, end } => { - let raw_file_id = FileId::from_raw(file_id); - let editioned_file_id = span::EditionedFileId::from_raw(file_id); let ast_id = span::ErasedFileAstId::from_raw(ast_id); - let hir_file_id = EditionedFileId::from_span_guess_origin(db, editioned_file_id); - let anchor_offset = db - .ast_id_map(hir_expand::HirFileId::FileId(hir_file_id)) - .get_erased(ast_id) - .text_range() - .start(); - let anchor_offset = u32::from(anchor_offset); - let abs_start = start + anchor_offset; - let abs_end = end + anchor_offset; - let source = db.file_text(raw_file_id).text(db); - let text = source.get(abs_start as usize..abs_end as usize).map(ToOwned::to_owned); + let editioned_file_id = span::EditionedFileId::from_raw(file_id); + let span = Span { + range: TextRange::new(TextSize::from(start), TextSize::from(end)), + anchor: SpanAnchor { file_id: editioned_file_id, ast_id }, + ctx: SyntaxContext::root(editioned_file_id.edition()), + }; + let range = db.resolve_span(span); + let source = db.file_text(range.file_id.file_id(db)).text(db); + let text = source + .get(usize::from(range.range.start())..usize::from(range.range.end())) + .map(ToOwned::to_owned); Ok(SubResponse::SourceTextResult { text }) } 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 2c6e5a16ee06f..6b2db0b269d57 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 @@ -18,6 +18,7 @@ clap = {version = "4.5.42", default-features = false, features = ["std"]} [features] 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"] 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 4891e073142de..b2f4b96bd2551 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 @@ -185,8 +185,8 @@ impl<'a, C: Codec> ProcMacroClientHandle<'a, C> { } impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { - fn file(&mut self, file_id: u32) -> String { - match self.roundtrip(bidirectional::SubRequest::FilePath { file_id }) { + fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String { + match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::FilePathResult { name }, )) => name, @@ -194,9 +194,16 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl } } - fn source_text(&mut self, file_id: u32, ast_id: u32, start: u32, end: u32) -> Option { - match self.roundtrip(bidirectional::SubRequest::SourceText { file_id, ast_id, start, end }) - { + fn source_text( + &mut self, + proc_macro_srv::span::Span { range, anchor, ctx: _ }: proc_macro_srv::span::Span, + ) -> Option { + match self.roundtrip(bidirectional::SubRequest::SourceText { + file_id: anchor.file_id.as_u32(), + ast_id: anchor.ast_id.into_raw(), + start: range.start().into(), + end: range.end().into(), + }) { Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::SourceTextResult { text }, )) => text, @@ -204,8 +211,9 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl } } - fn local_file(&mut self, file_id: u32) -> Option { - match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id }) { + fn local_file(&mut self, file_id: proc_macro_srv::span::FileId) -> Option { + match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id: file_id.index() }) + { Some(bidirectional::BidirectionalMessage::SubResponse( bidirectional::SubResponse::LocalFilePathResult { name }, )) => name, 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 d63aea947c1d1..f2d1dfbba4ccb 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 @@ -53,6 +53,7 @@ use temp_dir::TempDir; pub use crate::server_impl::token_id::SpanId; pub use proc_macro::Delimiter; +pub use span; pub use crate::bridge::*; pub use crate::server_impl::literal_from_str; @@ -94,9 +95,9 @@ impl<'env> ProcMacroSrv<'env> { pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); pub trait ProcMacroClientInterface { - fn file(&mut self, file_id: u32) -> String; - fn source_text(&mut self, file_id: u32, ast_id: u32, start: u32, end: u32) -> Option; - fn local_file(&mut self, file_id: u32) -> Option; + fn file(&mut self, file_id: span::FileId) -> String; + fn source_text(&mut self, span: Span) -> Option; + fn local_file(&mut self, file_id: span::FileId) -> Option; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; 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 2ce3b717cb0df..32725afc55272 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 @@ -128,13 +128,10 @@ impl server::Span for RaSpanServer<'_> { format!("{:?}", span) } fn file(&mut self, span: Self::Span) -> String { - self.callback - .as_mut() - .map(|cb| cb.file(span.anchor.file_id.file_id().index())) - .unwrap_or_default() + self.callback.as_mut().map(|cb| cb.file(span.anchor.file_id.file_id())).unwrap_or_default() } fn local_file(&mut self, span: Self::Span) -> Option { - self.callback.as_mut().and_then(|cb| cb.local_file(span.anchor.file_id.file_id().index())) + self.callback.as_mut().and_then(|cb| cb.local_file(span.anchor.file_id.file_id())) } fn save_span(&mut self, _span: Self::Span) -> usize { // FIXME, quote is incompatible with third-party tools @@ -153,17 +150,7 @@ impl server::Span for RaSpanServer<'_> { /// See PR: /// https://github.com/rust-lang/rust/pull/55780 fn source_text(&mut self, span: Self::Span) -> Option { - let file_id = span.anchor.file_id; - let ast_id = span.anchor.ast_id; - let start: u32 = span.range.start().into(); - let end: u32 = span.range.end().into(); - - self.callback.as_mut()?.source_text( - file_id.file_id().index(), - ast_id.into_raw(), - start, - end, - ) + self.callback.as_mut()?.source_text(span) } fn parent(&mut self, _span: Self::Span) -> Option { From 50b78f1bb4c8bd3a0c2d5cb5b4323be16121354d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 4 Jan 2026 12:08:50 +0100 Subject: [PATCH 31/34] Add a README.md to proc-macro-srv-cli --- .../crates/proc-macro-srv-cli/README.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md new file mode 100644 index 0000000000000..02a67ac3ecc16 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/README.md @@ -0,0 +1,65 @@ +# proc-macro-srv-cli + +A standalone binary for the `proc-macro-srv` crate that provides procedural macro expansion for rust-analyzer. + +## Overview + +rust-analyzer uses a RPC (via stdio) client-server architecture for procedural macro expansion. This is necessary because: + +1. Proc macros are dynamic libraries that can segfault, bringing down the entire process, so running them out of process allows rust-analyzer to recover from fatal errors. +2. Proc macro dylibs are compiled against a specific rustc version and require matching internal APIs to load and execute, as such having this binary shipped as a rustup component allows us to always match the rustc version irrespective of the rust-analyzer version used. + +## The `sysroot-abi` Feature + +**The `sysroot-abi` feature is required for the binary to actually function.** Without it, the binary will return an error: + +``` +proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function +``` + +This feature is necessary because the proc-macro server needs access to unstable rustc internals (`proc_macro_internals`, `proc_macro_diagnostic`, `proc_macro_span`) which are only available on nightly or with `RUSTC_BOOTSTRAP=1`. +rust-analyzer is a stable toolchain project though, so the feature flag is used to have it remain compilable on stable by default. + +### Building + +```bash +# Using nightly toolchain +cargo build -p proc-macro-srv-cli --features sysroot-abi + +# Or with RUSTC_BOOTSTRAP on stable +RUSTC_BOOTSTRAP=1 cargo build -p proc-macro-srv-cli --features sysroot-abi +``` + +### Installing the proc-macro server + +For local testing purposes, you can install the proc-macro server using the xtask command: + +```bash +# Recommended: use the xtask command +cargo xtask install --proc-macro-server +``` + +## Testing + +```bash +cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api +``` + +The tests use a test proc macro dylib built by the `proc-macro-test` crate, which compiles a small proc macro implementation during build time. + +**Note**: Tests only compile on nightly toolchains (or with `RUSTC_BOOTSTRAP=1`). + +## Usage + +The binary requires the `RUST_ANALYZER_INTERNALS_DO_NOT_USE` environment variable to be set. This is intentional—the binary is an implementation detail of rust-analyzer and its API is still unstable: + +```bash +RUST_ANALYZER_INTERNALS_DO_NOT_USE=1 rust-analyzer-proc-macro-srv --version +``` + +## Related Crates + +- `proc-macro-srv`: The core server library that handles loading dylibs and expanding macros, but not the RPC protocol. +- `proc-macro-api`: The client library used by rust-analyzer to communicate with this server as well as the protocol definitions. +- `proc-macro-test`: Test harness with sample proc macros for testing +- `proc-macro-srv-cli`: The actual server binary that handles the RPC protocol. From 7dbe12ec729ed3ce6d7b228cbb213d8e73df30b4 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 5 Jan 2026 00:37:40 +0900 Subject: [PATCH 32/34] fix: Suppress false positive missing assoc item diag on specialization --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 42 +++++++++++++++++++ .../handlers/trait_impl_missing_assoc_item.rs | 18 ++++++++ 2 files changed, 60 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index ef35694774e00..78be5a7e8fa94 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -920,6 +920,48 @@ impl Module { } } + // HACK: When specialization is enabled in the current crate, and there exists + // *any* blanket impl that provides a default implementation for the missing item, + // suppress the missing associated item diagnostic. + // This can lead to false negatives when the impl in question does not actually + // specialize that blanket impl, but determining the exact specialization + // relationship here would be significantly more expensive. + if !missing.is_empty() { + let krate = self.krate(db).id; + let def_map = crate_def_map(db, krate); + if def_map.is_unstable_feature_enabled(&sym::specialization) + || def_map.is_unstable_feature_enabled(&sym::min_specialization) + { + missing.retain(|(assoc_name, assoc_item)| { + let AssocItem::Function(_) = assoc_item else { + return true; + }; + + for &impl_ in TraitImpls::for_crate(db, krate).blanket_impls(trait_.id) + { + if impl_ == impl_id { + continue; + } + + for (name, item) in &impl_.impl_items(db).items { + let AssocItemId::FunctionId(fn_) = item else { + continue; + }; + if name != assoc_name { + continue; + } + + if db.function_signature(*fn_).is_default() { + return false; + } + } + } + + true + }); + } + } + if !missing.is_empty() { acc.push( TraitImplMissingAssocItems { 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 0e18ce9674044..2c05544701872 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 @@ -156,4 +156,22 @@ impl Trait for dyn OtherTrait {} "#, ) } + + #[test] + fn no_false_positive_on_specialization() { + check_diagnostics( + r#" +#![feature(specialization)] + +pub trait Foo { + fn foo(); +} + +impl Foo for T { + default fn foo() {} +} +impl Foo for bool {} +"#, + ); + } } From 0f5cf5ec5e1e7d68f4ba3e21a815313e00b0ae6f Mon Sep 17 00:00:00 2001 From: The 8472 Date: Sun, 4 Jan 2026 11:23:35 +0100 Subject: [PATCH 33/34] Unix implementation for stdio set/take/replace --- library/std/src/os/unix/io/mod.rs | 128 ++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/library/std/src/os/unix/io/mod.rs b/library/std/src/os/unix/io/mod.rs index 6d4090ee31cfc..938a71c835ec9 100644 --- a/library/std/src/os/unix/io/mod.rs +++ b/library/std/src/os/unix/io/mod.rs @@ -92,9 +92,137 @@ #![stable(feature = "rust1", since = "1.0.0")] +use crate::io::{self, Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, Write}; #[stable(feature = "rust1", since = "1.0.0")] pub use crate::os::fd::*; +#[allow(unused_imports)] // not used on all targets +use crate::sys::cvt; // Tests for this module #[cfg(test)] mod tests; + +#[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")] +pub trait StdioExt: crate::sealed::Sealed { + /// Redirects the stdio file descriptor to point to the file description underpinning `fd`. + /// + /// Rust std::io write buffers (if any) are flushed, but other runtimes + /// (e.g. C stdio) or libraries that acquire a clone of the file descriptor + /// will not be aware of this change. + /// + /// # Platform-specific behavior + /// + /// This is [currently] implemented using + /// + /// - `fd_renumber` on wasip1 + /// - `dup2` on most unixes + /// + /// [currently]: crate::io#platform-specific-behavior + /// + /// ``` + /// #![feature(stdio_swap)] + /// use std::io::{self, Read, Write}; + /// use std::os::unix::io::StdioExt; + /// + /// fn main() -> io::Result<()> { + /// let (reader, mut writer) = io::pipe()?; + /// let mut stdin = io::stdin(); + /// stdin.set_fd(reader)?; + /// writer.write_all(b"Hello, world!")?; + /// let mut buffer = vec![0; 13]; + /// assert_eq!(stdin.read(&mut buffer)?, 13); + /// assert_eq!(&buffer, b"Hello, world!"); + /// Ok(()) + /// } + /// ``` + fn set_fd>(&mut self, fd: T) -> io::Result<()>; + + /// Redirects the stdio file descriptor and returns a new `OwnedFd` + /// backed by the previous file description. + /// + /// See [`set_fd()`] for details. + /// + /// [`set_fd()`]: StdioExt::set_fd + fn replace_fd>(&mut self, replace_with: T) -> io::Result; + + /// Redirects the stdio file descriptor to the null device (`/dev/null`) + /// and returns a new `OwnedFd` backed by the previous file description. + /// + /// Programs that communicate structured data via stdio can use this early in `main()` to + /// extract the fds, treat them as other IO types (`File`, `UnixStream`, etc), + /// apply custom buffering or avoid interference from stdio use later in the program. + /// + /// See [`set_fd()`] for additional details. + /// + /// [`set_fd()`]: StdioExt::set_fd + fn take_fd(&mut self) -> io::Result; +} + +macro io_ext_impl($stdio_ty:ty, $stdio_lock_ty:ty, $writer:literal) { + #[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")] + impl StdioExt for $stdio_ty { + fn set_fd>(&mut self, fd: T) -> io::Result<()> { + self.lock().set_fd(fd) + } + + fn take_fd(&mut self) -> io::Result { + self.lock().take_fd() + } + + fn replace_fd>(&mut self, replace_with: T) -> io::Result { + self.lock().replace_fd(replace_with) + } + } + + #[unstable(feature = "stdio_swap", issue = "150667", reason = "recently added")] + impl StdioExt for $stdio_lock_ty { + fn set_fd>(&mut self, fd: T) -> io::Result<()> { + #[cfg($writer)] + self.flush()?; + replace_stdio_fd(self.as_fd(), fd.into()) + } + + fn take_fd(&mut self) -> io::Result { + let null = null_fd()?; + let cloned = self.as_fd().try_clone_to_owned()?; + self.set_fd(null)?; + Ok(cloned) + } + + fn replace_fd>(&mut self, replace_with: T) -> io::Result { + let cloned = self.as_fd().try_clone_to_owned()?; + self.set_fd(replace_with)?; + Ok(cloned) + } + } +} + +io_ext_impl!(Stdout, StdoutLock<'_>, true); +io_ext_impl!(Stdin, StdinLock<'_>, false); +io_ext_impl!(Stderr, StderrLock<'_>, true); + +fn null_fd() -> io::Result { + let null_dev = crate::fs::OpenOptions::new().read(true).write(true).open("/dev/null")?; + Ok(null_dev.into()) +} + +/// Replaces the underlying file descriptor with the one from `other`. +/// Does not set CLOEXEC. +fn replace_stdio_fd(this: BorrowedFd<'_>, other: OwnedFd) -> io::Result<()> { + cfg_select! { + all(target_os = "wasi", target_env = "p1") => { + cvt(unsafe { libc::__wasilibc_fd_renumber(other.as_raw_fd(), this.as_raw_fd()) }).map(|_| ()) + } + not(any( + target_arch = "wasm32", + target_os = "hermit", + target_os = "trusty", + target_os = "motor" + )) => { + cvt(unsafe {libc::dup2(other.as_raw_fd(), this.as_raw_fd())}).map(|_| ()) + } + _ => { + Err(io::Error::UNSUPPORTED_PLATFORM) + } + } +} From bf2308f6f294638e3f11d71df212b0ed615a04b2 Mon Sep 17 00:00:00 2001 From: The 8472 Date: Sat, 27 Dec 2025 00:26:21 +0100 Subject: [PATCH 34/34] use PIDFD_GET_INFO ioctl when available This way using pidfd_spawnp won't have to rely on procfs, avoiding an unpleasant edge-case where the child is spawned but we can't get the pid. And `pidfd.{try_}wait` will be able to return the exit status even after a process has been reaped. At least on newer kernels. --- library/std/src/os/linux/process.rs | 10 +- library/std/src/sys/pal/unix/linux/pidfd.rs | 98 +++++++++++++++---- .../std/src/sys/pal/unix/linux/pidfd/tests.rs | 38 ++++--- library/std/src/sys/process/unix/unix.rs | 40 ++++---- 4 files changed, 129 insertions(+), 57 deletions(-) diff --git a/library/std/src/os/linux/process.rs b/library/std/src/os/linux/process.rs index fef321436f6a3..60851db831bf9 100644 --- a/library/std/src/os/linux/process.rs +++ b/library/std/src/os/linux/process.rs @@ -67,8 +67,10 @@ impl PidFd { /// Waits for the child to exit completely, returning the status that it exited with. /// /// Unlike [`Child::wait`] it does not ensure that the stdin handle is closed. - /// Additionally it will not return an `ExitStatus` if the child - /// has already been reaped. Instead an error will be returned. + /// + /// Additionally on kernels prior to 6.15 only the first attempt to + /// reap a child will return an ExitStatus, further attempts + /// will return an Error. /// /// [`Child::wait`]: process::Child::wait pub fn wait(&self) -> Result { @@ -77,8 +79,8 @@ impl PidFd { /// Attempts to collect the exit status of the child if it has already exited. /// - /// Unlike [`Child::try_wait`] this method will return an Error - /// if the child has already been reaped. + /// On kernels prior to 6.15, and unlike [`Child::try_wait`], only the first attempt + /// to reap a child will return an ExitStatus, further attempts will return an Error. /// /// [`Child::try_wait`]: process::Child::try_wait pub fn try_wait(&self) -> Result> { diff --git a/library/std/src/sys/pal/unix/linux/pidfd.rs b/library/std/src/sys/pal/unix/linux/pidfd.rs index 7859854e96b4b..312a0c551f6a8 100644 --- a/library/std/src/sys/pal/unix/linux/pidfd.rs +++ b/library/std/src/sys/pal/unix/linux/pidfd.rs @@ -1,7 +1,8 @@ use crate::io; -use crate::os::fd::{AsRawFd, FromRawFd, RawFd}; +use crate::os::fd::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys::process::ExitStatus; +use crate::sys::weak::weak; use crate::sys::{AsInner, FromInner, IntoInner, cvt}; #[cfg(test)] @@ -15,6 +16,69 @@ impl PidFd { self.send_signal(libc::SIGKILL) } + pub(crate) fn current_process() -> io::Result { + let pid = crate::process::id(); + let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, pid, 0) })?; + Ok(unsafe { PidFd::from_raw_fd(pidfd as RawFd) }) + } + + pub(crate) fn pid(&self) -> io::Result { + // since kernel 6.13 + // https://lore.kernel.org/all/20241010155401.2268522-1-luca.boccassi@gmail.com/ + let mut pidfd_info: libc::pidfd_info = unsafe { crate::mem::zeroed() }; + pidfd_info.mask = libc::PIDFD_INFO_PID as u64; + match cvt(unsafe { libc::ioctl(self.0.as_raw_fd(), libc::PIDFD_GET_INFO, &mut pidfd_info) }) + { + Ok(_) => {} + Err(e) if e.raw_os_error() == Some(libc::EINVAL) => { + // kernel doesn't support that ioctl, try the glibc helper that looks at procfs + weak!( + fn pidfd_getpid(pidfd: RawFd) -> libc::pid_t; + ); + if let Some(pidfd_getpid) = pidfd_getpid.get() { + let pid: libc::c_int = cvt(unsafe { pidfd_getpid(self.0.as_raw_fd()) })?; + return Ok(pid as u32); + } + return Err(e); + } + Err(e) => return Err(e), + } + + Ok(pidfd_info.pid) + } + + fn exit_for_reaped_child(&self) -> io::Result { + // since kernel 6.15 + // https://lore.kernel.org/linux-fsdevel/20250305-work-pidfs-kill_on_last_close-v3-0-c8c3d8361705@kernel.org/T/ + let mut pidfd_info: libc::pidfd_info = unsafe { crate::mem::zeroed() }; + pidfd_info.mask = libc::PIDFD_INFO_EXIT as u64; + cvt(unsafe { libc::ioctl(self.0.as_raw_fd(), libc::PIDFD_GET_INFO, &mut pidfd_info) })?; + Ok(ExitStatus::new(pidfd_info.exit_code)) + } + + fn waitid(&self, options: libc::c_int) -> io::Result> { + let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; + let r = cvt(unsafe { + libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, options) + }); + match r { + Err(waitid_err) if waitid_err.raw_os_error() == Some(libc::ECHILD) => { + // already reaped + match self.exit_for_reaped_child() { + Ok(exit_status) => return Ok(Some(exit_status)), + Err(_) => return Err(waitid_err), + } + } + Err(e) => return Err(e), + Ok(_) => {} + } + if unsafe { siginfo.si_pid() } == 0 { + Ok(None) + } else { + Ok(Some(ExitStatus::from_waitid_siginfo(siginfo))) + } + } + pub(crate) fn send_signal(&self, signal: i32) -> io::Result<()> { cvt(unsafe { libc::syscall( @@ -29,29 +93,15 @@ impl PidFd { } pub fn wait(&self) -> io::Result { - let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; - cvt(unsafe { - libc::waitid(libc::P_PIDFD, self.0.as_raw_fd() as u32, &mut siginfo, libc::WEXITED) - })?; - Ok(ExitStatus::from_waitid_siginfo(siginfo)) + let r = self.waitid(libc::WEXITED)?; + match r { + Some(exit_status) => Ok(exit_status), + None => unreachable!("waitid with WEXITED should not return None"), + } } pub fn try_wait(&self) -> io::Result> { - let mut siginfo: libc::siginfo_t = unsafe { crate::mem::zeroed() }; - - cvt(unsafe { - libc::waitid( - libc::P_PIDFD, - self.0.as_raw_fd() as u32, - &mut siginfo, - libc::WEXITED | libc::WNOHANG, - ) - })?; - if unsafe { siginfo.si_pid() } == 0 { - Ok(None) - } else { - Ok(Some(ExitStatus::from_waitid_siginfo(siginfo))) - } + self.waitid(libc::WEXITED | libc::WNOHANG) } } @@ -78,3 +128,9 @@ impl FromRawFd for PidFd { Self(FileDesc::from_raw_fd(fd)) } } + +impl IntoRawFd for PidFd { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} diff --git a/library/std/src/sys/pal/unix/linux/pidfd/tests.rs b/library/std/src/sys/pal/unix/linux/pidfd/tests.rs index 17b06bea91278..a3bb5d5d64ba5 100644 --- a/library/std/src/sys/pal/unix/linux/pidfd/tests.rs +++ b/library/std/src/sys/pal/unix/linux/pidfd/tests.rs @@ -1,8 +1,11 @@ +use super::PidFd as InternalPidFd; use crate::assert_matches::assert_matches; -use crate::os::fd::{AsRawFd, RawFd}; +use crate::io::ErrorKind; +use crate::os::fd::AsRawFd; use crate::os::linux::process::{ChildExt, CommandExt as _}; use crate::os::unix::process::{CommandExt as _, ExitStatusExt}; use crate::process::Command; +use crate::sys::AsInner; #[test] fn test_command_pidfd() { @@ -48,11 +51,22 @@ fn test_command_pidfd() { let mut cmd = Command::new("false"); let mut child = unsafe { cmd.pre_exec(|| Ok(())) }.create_pidfd(true).spawn().unwrap(); - assert!(child.id() > 0 && child.id() < -1i32 as u32); + let id = child.id(); + + assert!(id > 0 && id < -1i32 as u32, "spawning with pidfd still returns a sane pid"); if pidfd_open_available { assert!(child.pidfd().is_ok()) } + + if let Ok(pidfd) = child.pidfd() { + match pidfd.as_inner().pid() { + Ok(pid) => assert_eq!(pid, id), + Err(e) if e.kind() == ErrorKind::InvalidInput => { /* older kernel */ } + Err(e) => panic!("unexpected error getting pid from pidfd: {}", e), + } + } + child.wait().expect("error waiting on child"); } @@ -77,9 +91,15 @@ fn test_pidfd() { assert_eq!(status.signal(), Some(libc::SIGKILL)); // Trying to wait again for a reaped child is safe since there's no pid-recycling race. - // But doing so will return an error. + // But doing so may return an error. let res = fd.wait(); - assert_matches!(res, Err(e) if e.raw_os_error() == Some(libc::ECHILD)); + match res { + // older kernels + Err(e) if e.raw_os_error() == Some(libc::ECHILD) => {} + // 6.15+ + Ok(exit) if exit.signal() == Some(libc::SIGKILL) => {} + other => panic!("expected ECHILD error, got {:?}", other), + } // Ditto for additional attempts to kill an already-dead child. let res = fd.kill(); @@ -87,13 +107,5 @@ fn test_pidfd() { } fn probe_pidfd_support() -> bool { - // pidfds require the pidfd_open syscall - let our_pid = crate::process::id(); - let pidfd = unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) }; - if pidfd >= 0 { - unsafe { libc::close(pidfd as RawFd) }; - true - } else { - false - } + InternalPidFd::current_process().is_ok() } diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 5ba57e11679cf..df64a1716d523 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -482,10 +482,6 @@ impl Command { ) -> libc::c_int; ); - weak!( - fn pidfd_getpid(pidfd: libc::c_int) -> libc::c_int; - ); - static PIDFD_SUPPORTED: Atomic = AtomicU8::new(0); const UNKNOWN: u8 = 0; const SPAWN: u8 = 1; @@ -502,24 +498,26 @@ impl Command { } if support == UNKNOWN { support = NO; - let our_pid = crate::process::id(); - let pidfd = cvt(unsafe { libc::syscall(libc::SYS_pidfd_open, our_pid, 0) } as c_int); - match pidfd { + + match PidFd::current_process() { Ok(pidfd) => { + // if pidfd_open works then we at least know the fork path is available. support = FORK_EXEC; - if let Some(Ok(pid)) = pidfd_getpid.get().map(|f| cvt(unsafe { f(pidfd) } as i32)) { - if pidfd_spawnp.get().is_some() && pid as u32 == our_pid { - support = SPAWN - } + // but for the fast path we need both spawnp and the + // pidfd -> pid conversion to work. + if pidfd_spawnp.get().is_some() && let Ok(pid) = pidfd.pid() { + assert_eq!(pid, crate::process::id(), "sanity check"); + support = SPAWN; } - unsafe { libc::close(pidfd) }; } Err(e) if e.raw_os_error() == Some(libc::EMFILE) => { - // We're temporarily(?) out of file descriptors. In this case obtaining a pidfd would also fail + // We're temporarily(?) out of file descriptors. In this case pidfd_spawnp would also fail // Don't update the support flag so we can probe again later. return Err(e) } - _ => {} + _ => { + // pidfd_open not available? likely an old kernel without pidfd support. + } } PIDFD_SUPPORTED.store(support, Ordering::Relaxed); if support == FORK_EXEC { @@ -791,13 +789,17 @@ impl Command { } spawn_res?; - let pid = match cvt(pidfd_getpid.get().unwrap()(pidfd)) { + use crate::os::fd::{FromRawFd, IntoRawFd}; + + let pidfd = PidFd::from_raw_fd(pidfd); + let pid = match pidfd.pid() { Ok(pid) => pid, Err(e) => { // The child has been spawned and we are holding its pidfd. - // But we cannot obtain its pid even though pidfd_getpid support was verified earlier. - // This might happen if libc can't open procfs because the file descriptor limit has been reached. - libc::close(pidfd); + // But we cannot obtain its pid even though pidfd_spawnp and getpid support + // was verified earlier. + // This is quite unlikely, but might happen if the ioctl is not supported, + // glibc tries to use procfs and we're out of file descriptors. return Err(Error::new( e.kind(), "pidfd_spawnp succeeded but the child's PID could not be obtained", @@ -805,7 +807,7 @@ impl Command { } }; - return Ok(Some(Process::new(pid, pidfd))); + return Ok(Some(Process::new(pid as i32, pidfd.into_raw_fd()))); } // Safety: -1 indicates we don't have a pidfd.