From 73e35ee5ee14b3447ed604933a05f5458cd2d410 Mon Sep 17 00:00:00 2001 From: yukang Date: Sun, 18 Jan 2026 11:14:13 +0800 Subject: [PATCH 01/55] Fix incorrect trailing comma suggested in no_accessible_fields --- compiler/rustc_hir_typeck/src/pat.rs | 14 +++++++++-- ...private-fields-trailing-comma-149787.fixed | 23 +++++++++++++++++++ ...le-private-fields-trailing-comma-149787.rs | 23 +++++++++++++++++++ ...rivate-fields-trailing-comma-149787.stderr | 17 ++++++++++++++ 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.fixed create mode 100644 tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.rs create mode 100644 tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.stderr diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index b56ab6dcb4ab2..84db29fe4d7a2 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -2434,10 +2434,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields"); if let Some(field) = fields.last() { + let tail_span = field.span.shrink_to_hi().to(pat.span.shrink_to_hi()); + let comma_hi_offset = + self.tcx.sess.source_map().span_to_snippet(tail_span).ok().and_then(|snippet| { + let trimmed = snippet.trim_start(); + trimmed.starts_with(',').then(|| (snippet.len() - trimmed.len() + 1) as u32) + }); err.span_suggestion_verbose( - field.span.shrink_to_hi(), + if let Some(comma_hi_offset) = comma_hi_offset { + tail_span.with_hi(tail_span.lo() + BytePos(comma_hi_offset)).shrink_to_hi() + } else { + field.span.shrink_to_hi() + }, "ignore the inaccessible and unused fields", - ", ..", + if comma_hi_offset.is_some() { " .." } else { ", .." }, Applicability::MachineApplicable, ); } else { diff --git a/tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.fixed b/tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.fixed new file mode 100644 index 0000000000000..6a73475f202f4 --- /dev/null +++ b/tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.fixed @@ -0,0 +1,23 @@ +#![allow(dead_code)] +#![allow(unused)] +//@ run-rustfix + +mod m { + pub(crate) struct S { + pub(crate) visible: u64, + hidden: u64, + } + + impl S { + pub(crate) fn new() -> Self { + loop {} + } + } +} + +fn main() { + let m::S { + //~^ ERROR pattern requires `..` due to inaccessible fields + visible, .. + } = m::S::new(); +} diff --git a/tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.rs b/tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.rs new file mode 100644 index 0000000000000..1a4fed68381a1 --- /dev/null +++ b/tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.rs @@ -0,0 +1,23 @@ +#![allow(dead_code)] +#![allow(unused)] +//@ run-rustfix + +mod m { + pub(crate) struct S { + pub(crate) visible: u64, + hidden: u64, + } + + impl S { + pub(crate) fn new() -> Self { + loop {} + } + } +} + +fn main() { + let m::S { + //~^ ERROR pattern requires `..` due to inaccessible fields + visible, + } = m::S::new(); +} diff --git a/tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.stderr b/tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.stderr new file mode 100644 index 0000000000000..4eacdecc6810c --- /dev/null +++ b/tests/ui/privacy/inaccessible-private-fields-trailing-comma-149787.stderr @@ -0,0 +1,17 @@ +error: pattern requires `..` due to inaccessible fields + --> $DIR/inaccessible-private-fields-trailing-comma-149787.rs:19:9 + | +LL | let m::S { + | _________^ +LL | | +LL | | visible, +LL | | } = m::S::new(); + | |_____^ + | +help: ignore the inaccessible and unused fields + | +LL | visible, .. + | ++ + +error: aborting due to 1 previous error + From 0f786b966bc519fc31463062d6d4ee8e747541f6 Mon Sep 17 00:00:00 2001 From: Ada Bohm Date: Fri, 13 Feb 2026 19:43:23 +0100 Subject: [PATCH 02/55] ValTreeKind::Branch use List --- compiler/rustc_middle/src/ty/codec.rs | 2 ++ compiler/rustc_middle/src/ty/consts/valtree.rs | 6 ++++-- compiler/rustc_middle/src/ty/context.rs | 2 +- compiler/rustc_middle/src/ty/context/impl_interner.rs | 1 + compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- compiler/rustc_middle/src/ty/structural_impls.rs | 1 + compiler/rustc_mir_build/src/builder/matches/mod.rs | 6 ++++-- compiler/rustc_type_ir/src/const_kind.rs | 8 ++++---- compiler/rustc_type_ir/src/flags.rs | 4 ++-- compiler/rustc_type_ir/src/interner.rs | 1 + compiler/rustc_type_ir/src/relate.rs | 5 +++-- 11 files changed, 24 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 4856df3a6222a..2cd8695a1d325 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -13,6 +13,7 @@ use std::marker::{DiscriminantKind, PointeeSized}; use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::Const; use rustc_serialize::{Decodable, Encodable}; use rustc_span::source_map::Spanned; use rustc_span::{Span, SpanDecoder, SpanEncoder}; @@ -498,6 +499,7 @@ impl_decodable_via_ref! { &'tcx ty::List>, &'tcx ty::List>, &'tcx ty::ListWithCachedTypeInfo>, + &'tcx ty::List>, } #[macro_export] diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index e3fc9bfee49c4..8cd119feb6f01 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -44,7 +44,7 @@ impl<'tcx> ValTree<'tcx> { } pub fn is_zst(self) -> bool { - matches!(*self, ty::ValTreeKind::Branch(box [])) + matches!(*self, ty::ValTreeKind::Branch(consts) if consts.is_empty()) } pub fn from_raw_bytes(tcx: TyCtxt<'tcx>, bytes: &[u8]) -> Self { @@ -58,7 +58,9 @@ impl<'tcx> ValTree<'tcx> { tcx: TyCtxt<'tcx>, branches: impl IntoIterator>, ) -> Self { - tcx.intern_valtree(ty::ValTreeKind::Branch(branches.into_iter().collect())) + tcx.intern_valtree(ty::ValTreeKind::Branch( + tcx.mk_const_list_from_iter(branches.into_iter()), + )) } pub fn from_scalar_int(tcx: TyCtxt<'tcx>, i: ScalarInt) -> Self { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f638dd80864cb..347fe5568d25b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -565,7 +565,7 @@ impl<'tcx> CommonConsts<'tcx> { )) }; - let valtree_zst = mk_valtree(ty::ValTreeKind::Branch(Box::default())); + let valtree_zst = mk_valtree(ty::ValTreeKind::Branch(List::empty())); let valtree_true = mk_valtree(ty::ValTreeKind::Leaf(ty::ScalarInt::TRUE)); let valtree_false = mk_valtree(ty::ValTreeKind::Leaf(ty::ScalarInt::FALSE)); diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 5a15d132048dd..fcf70597d396a 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -95,6 +95,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type Safety = hir::Safety; type Abi = ExternAbi; type Const = ty::Const<'tcx>; + type Consts = &'tcx List; type ParamConst = ty::ParamConst; type ValueConst = ty::Value<'tcx>; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 02b804c1ab29c..7df08708ef421 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1920,7 +1920,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } // Otherwise, print the array separated by commas (or if it's a tuple) (ty::ValTreeKind::Branch(fields), ty::Array(..) | ty::Tuple(..)) => { - let fields_iter = fields.iter().copied(); + let fields_iter = fields.as_slice().iter().copied(); match *cv.ty.kind() { ty::Array(..) => { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 8707b03e4b8f2..5f5085075a19a 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -805,4 +805,5 @@ list_fold! { &'tcx ty::List> : mk_place_elems, &'tcx ty::List> : mk_patterns, &'tcx ty::List> : mk_outlives, + &'tcx ty::List> : mk_const_list, } diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index d04322c320b4c..e89f6658bd142 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2941,10 +2941,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { match pat.ctor() { Constructor::Variant(variant_index) => { - let ValTreeKind::Branch(box [actual_variant_idx]) = *valtree else { + let ValTreeKind::Branch(branch) = *valtree else { + bug!("malformed valtree for an enum") + }; + let Some(actual_variant_idx) = branch.get(0) else { bug!("malformed valtree for an enum") }; - let ValTreeKind::Leaf(actual_variant_idx) = *actual_variant_idx.to_value().valtree else { bug!("malformed valtree for an enum") diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index 1c39f31469b1c..b8f0b64f62bf0 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -9,6 +9,7 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; +use crate::inherent::*; use crate::{self as ty, BoundVarIndexKind, Interner}; /// Represents a constant in Rust. @@ -159,8 +160,7 @@ pub enum ValTreeKind { /// the fields of the variant. /// /// ZST types are represented as an empty slice. - // FIXME(mgca): Use a `List` here instead of a boxed slice - Branch(Box<[I::Const]>), + Branch(I::Consts), } impl ValTreeKind { @@ -179,7 +179,7 @@ impl ValTreeKind { #[inline] pub fn to_branch(&self) -> &[I::Const] { match self { - ValTreeKind::Branch(branch) => &**branch, + ValTreeKind::Branch(branch) => branch.as_slice(), ValTreeKind::Leaf(..) => panic!("expected branch, got {:?}", self), } } @@ -195,7 +195,7 @@ impl ValTreeKind { /// Attempts to convert to a `ValTreeKind::Branch` value. pub fn try_to_branch(&self) -> Option<&[I::Const]> { match self { - ValTreeKind::Branch(branch) => Some(&**branch), + ValTreeKind::Branch(branch) => Some(branch.as_slice()), ValTreeKind::Leaf(_) => None, } } diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 8b057e5866cd9..7e905da0785f8 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -482,8 +482,8 @@ impl FlagComputation { match cv.valtree().kind() { ty::ValTreeKind::Leaf(_) => (), ty::ValTreeKind::Branch(cts) => { - for ct in cts { - self.add_const(*ct); + for ct in cts.iter() { + self.add_const(ct); } } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 8f446cdfba6d5..4cafaadf38d85 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -150,6 +150,7 @@ pub trait Interner: // Kinds of consts type Const: Const; + type Consts: Copy + Debug + Hash + Eq + SliceLike + Default; type ParamConst: Copy + Debug + Hash + Eq + ParamLike; type ValueConst: ValueConst; type ExprConst: ExprConst; diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 3610605462ba9..744331b0caa53 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -577,8 +577,9 @@ pub fn structurally_relate_consts>( if branches_a.len() == branches_b.len() => { branches_a - .into_iter() - .zip(branches_b) + .as_slice() + .iter() + .zip(branches_b.as_slice().iter()) .all(|(a, b)| relation.relate(*a, *b).is_ok()) } _ => false, From c5a613119e121d63e70c05de463d6226eb6146b8 Mon Sep 17 00:00:00 2001 From: Albab-Hasan Date: Mon, 16 Feb 2026 13:47:46 +0600 Subject: [PATCH 03/55] fix: use `ExprIsRead::Yes` for binary operator operands both operands of binary operators (including compound assignments like `+=`) were inferred with `ExprIsRead::No`, which prevented divergence detection for place expressions of type `!`. this caused false type mismatches when a function's return type depended on the block diverging after something like `x += *never_ptr` or `let _ = *never_ptr + 1`. both operands of any binary operator are always consumed (read), so this uses `ExprIsRead::Yes` to be consistent with the ordinary assignment and let binding paths. --- .../crates/hir-ty/src/infer/op.rs | 12 ++++--- .../crates/hir-ty/src/tests/never_type.rs | 31 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs index c79c828cd4420..95d63ffb508f6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs @@ -178,9 +178,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // trait matching creating lifetime constraints that are too strict. // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. - let lhs_ty = self.infer_expr_no_expect(lhs_expr, ExprIsRead::No); + let lhs_ty = self.infer_expr_no_expect(lhs_expr, ExprIsRead::Yes); let fresh_var = self.table.next_ty_var(); - self.demand_coerce(lhs_expr, lhs_ty, fresh_var, AllowTwoPhase::No, ExprIsRead::No) + self.demand_coerce(lhs_expr, lhs_ty, fresh_var, AllowTwoPhase::No, ExprIsRead::Yes) } }; let lhs_ty = self.table.resolve_vars_with_obligations(lhs_ty); @@ -200,7 +200,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // see `NB` above let rhs_ty = - self.infer_expr_coerce(rhs_expr, &Expectation::HasType(rhs_ty_var), ExprIsRead::No); + self.infer_expr_coerce(rhs_expr, &Expectation::HasType(rhs_ty_var), ExprIsRead::Yes); let rhs_ty = self.table.resolve_vars_with_obligations(rhs_ty); let return_ty = match result { @@ -320,7 +320,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> { if let Some((rhs_expr, rhs_ty)) = opt_rhs && rhs_ty.is_ty_var() { - self.infer_expr_coerce(rhs_expr, &Expectation::HasType(rhs_ty), ExprIsRead::No); + self.infer_expr_coerce( + rhs_expr, + &Expectation::HasType(rhs_ty), + ExprIsRead::Yes, + ); } // Construct an obligation `self_ty : Trait` diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index e4e7b4cc38859..912b4a5ad58fe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -802,6 +802,37 @@ fn foo() { ); } +#[test] +fn binop_rhs_never_place_diverges() { + check_no_mismatches( + r#" +//- minicore: sized, add +fn foo() -> i32 { + unsafe { + let p: *mut ! = 0 as _; + let mut x: i32 = 0; + x += *p; + } +} +"#, + ); +} + +#[test] +fn binop_lhs_never_place_diverges() { + check_no_mismatches( + r#" +//- minicore: sized, add +fn foo() -> i32 { + unsafe { + let p: *const ! = 0 as _; + let _x = *p + 1; + } +} +"#, + ); +} + #[test] fn never_place_isnt_diverging() { check_infer_with_mismatches( From 85de83b5b2485e4038c2cd8a168590e99b3cee0c Mon Sep 17 00:00:00 2001 From: Albab-Hasan Date: Mon, 16 Feb 2026 15:52:32 +0600 Subject: [PATCH 04/55] fix: use check_infer_with_mismatches in binop_lhs_never_place_diverges test --- .../crates/hir-ty/src/tests/never_type.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 912b4a5ad58fe..4349c22e0c39c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -820,7 +820,7 @@ fn foo() -> i32 { #[test] fn binop_lhs_never_place_diverges() { - check_no_mismatches( + check_infer_with_mismatches( r#" //- minicore: sized, add fn foo() -> i32 { @@ -830,6 +830,18 @@ fn foo() -> i32 { } } "#, + expect![[r#" + 16..97 '{ ... } }': i32 + 22..95 'unsafe... }': i32 + 43..44 'p': *const ! + 57..58 '0': i32 + 57..63 '0 as _': *const ! + 77..79 '_x': {unknown} + 82..84 '*p': ! + 82..88 '*p + 1': >::Output + 83..84 'p': *const ! + 87..88 '1': i32 + "#]], ); } From 05cd01b8879f599e064ffd7294a5ae09320e25b5 Mon Sep 17 00:00:00 2001 From: Albab-Hasan Date: Mon, 16 Feb 2026 18:59:04 +0600 Subject: [PATCH 05/55] fix: verify NeverToAny adjustment in binop_lhs_never_place_diverges test --- .../crates/hir-ty/src/tests/never_type.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 4349c22e0c39c..6db256f499a5a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -820,28 +820,17 @@ fn foo() -> i32 { #[test] fn binop_lhs_never_place_diverges() { - check_infer_with_mismatches( + check_no_mismatches( r#" //- minicore: sized, add fn foo() -> i32 { unsafe { let p: *const ! = 0 as _; let _x = *p + 1; + // ^^ adjustments: NeverToAny } } "#, - expect![[r#" - 16..97 '{ ... } }': i32 - 22..95 'unsafe... }': i32 - 43..44 'p': *const ! - 57..58 '0': i32 - 57..63 '0 as _': *const ! - 77..79 '_x': {unknown} - 82..84 '*p': ! - 82..88 '*p + 1': >::Output - 83..84 'p': *const ! - 87..88 '1': i32 - "#]], ); } From 115bfdbb35014162be380f061ee08691f2503601 Mon Sep 17 00:00:00 2001 From: arferreira Date: Mon, 16 Feb 2026 22:21:54 -0500 Subject: [PATCH 06/55] Suppress invalid suggestions in destructuring assignment Signed-off-by: arferreira --- .../src/diagnostics/move_errors.rs | 73 ++++++++++++++----- ...ggestions-destructuring-assignment-drop.rs | 14 ++++ ...tions-destructuring-assignment-drop.stderr | 12 +++ 3 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 tests/ui/moves/invalid-suggestions-destructuring-assignment-drop.rs create mode 100644 tests/ui/moves/invalid-suggestions-destructuring-assignment-drop.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 986ade57fb31d..7bb484ee73f32 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -8,7 +8,7 @@ use rustc_middle::mir::*; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex}; use rustc_span::def_id::DefId; -use rustc_span::{BytePos, ExpnKind, MacroKind, Span}; +use rustc_span::{BytePos, ExpnKind, MacroKind, Span, sym}; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; @@ -700,14 +700,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { binds_to.sort(); binds_to.dedup(); - self.add_move_error_details(err, &binds_to); + self.add_move_error_details(err, &binds_to, &[]); } } GroupedMoveError::MovesFromValue { mut binds_to, .. } => { binds_to.sort(); binds_to.dedup(); - self.add_move_error_suggestions(err, &binds_to); - self.add_move_error_details(err, &binds_to); + let desugar_spans = self.add_move_error_suggestions(err, &binds_to); + self.add_move_error_details(err, &binds_to, &desugar_spans); } // No binding. Nothing to suggest. GroupedMoveError::OtherIllegalMove { ref original_path, use_spans, .. } => { @@ -823,7 +823,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } } - fn add_move_error_suggestions(&self, err: &mut Diag<'_>, binds_to: &[Local]) { + fn add_move_error_suggestions(&self, err: &mut Diag<'_>, binds_to: &[Local]) -> Vec { /// A HIR visitor to associate each binding with a `&` or `&mut` that could be removed to /// make it bind by reference instead (if possible) struct BindingFinder<'tcx> { @@ -843,6 +843,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ref_pat_for_binding: Vec<(Span, Option<&'tcx hir::Pat<'tcx>>)>, /// Output: ref patterns that can't be removed straightforwardly cannot_remove: FxHashSet, + /// Output: binding spans from destructuring assignment desugaring + desugar_binding_spans: Vec, } impl<'tcx> Visitor<'tcx> for BindingFinder<'tcx> { type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies; @@ -883,16 +885,38 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } if let hir::PatKind::Binding(_, _, ident, _) = p.kind { - // the spans in `binding_spans` encompass both the ident and binding mode - if let Some(&bind_sp) = - self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span)) - { - self.ref_pat_for_binding.push((bind_sp, self.ref_pat)); + // Skip synthetic bindings from destructuring assignment desugaring + // These have name `lhs` and their parent is a `LetStmt` with + // `LocalSource::AssignDesugar` + let dominated_by_desugar_assign = ident.name == sym::lhs + && self.tcx.hir_parent_iter(p.hir_id).any(|(_, node)| { + matches!( + node, + hir::Node::LetStmt(&hir::LetStmt { + source: hir::LocalSource::AssignDesugar, + .. + }) + ) + }); + + if dominated_by_desugar_assign { + if let Some(&bind_sp) = + self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span)) + { + self.desugar_binding_spans.push(bind_sp); + } } else { - // we've encountered a binding that we're not reporting a move error for. - // we don't want to change its type, so don't remove the surrounding `&`. - if let Some(ref_pat) = self.ref_pat { - self.cannot_remove.insert(ref_pat.hir_id); + // the spans in `binding_spans` encompass both the ident and binding mode + if let Some(&bind_sp) = + self.binding_spans.iter().find(|bind_sp| bind_sp.contains(ident.span)) + { + self.ref_pat_for_binding.push((bind_sp, self.ref_pat)); + } else { + // we've encountered a binding that we're not reporting a move error for. + // we don't want to change its type, so don't remove the surrounding `&`. + if let Some(ref_pat) = self.ref_pat { + self.cannot_remove.insert(ref_pat.hir_id); + } } } } @@ -913,10 +937,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { binding_spans.push(bind_to.source_info.span); } } - let Some(pat_span) = pat_span else { return }; + let Some(pat_span) = pat_span else { return Vec::new() }; let tcx = self.infcx.tcx; - let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return }; + let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) else { return Vec::new() }; let typeck_results = self.infcx.tcx.typeck(self.mir_def_id()); let mut finder = BindingFinder { typeck_results, @@ -928,6 +952,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { has_adjustments: false, ref_pat_for_binding: Vec::new(), cannot_remove: FxHashSet::default(), + desugar_binding_spans: Vec::new(), }; finder.visit_body(body); @@ -952,9 +977,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { for (span, msg, suggestion) in suggestions { err.span_suggestion_verbose(span, msg, suggestion, Applicability::MachineApplicable); } + finder.desugar_binding_spans } - fn add_move_error_details(&self, err: &mut Diag<'_>, binds_to: &[Local]) { + fn add_move_error_details( + &self, + err: &mut Diag<'_>, + binds_to: &[Local], + desugar_spans: &[Span], + ) { for (j, local) in binds_to.iter().enumerate() { let bind_to = &self.body.local_decls[*local]; let binding_span = bind_to.source_info.span; @@ -968,9 +999,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if binds_to.len() == 1 { let place_desc = self.local_name(*local).map(|sym| format!("`{sym}`")); - if let Some(expr) = self.find_expr(binding_span) { - let local_place: PlaceRef<'tcx> = (*local).into(); - self.suggest_cloning(err, local_place, bind_to.ty, expr, None); + if !desugar_spans.contains(&binding_span) { + if let Some(expr) = self.find_expr(binding_span) { + let local_place: PlaceRef<'tcx> = (*local).into(); + self.suggest_cloning(err, local_place, bind_to.ty, expr, None); + } } err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { diff --git a/tests/ui/moves/invalid-suggestions-destructuring-assignment-drop.rs b/tests/ui/moves/invalid-suggestions-destructuring-assignment-drop.rs new file mode 100644 index 0000000000000..23d5d4c9e1096 --- /dev/null +++ b/tests/ui/moves/invalid-suggestions-destructuring-assignment-drop.rs @@ -0,0 +1,14 @@ +// Regression test for #152694 +// Verify that we don't emit invalid suggestions (adding `ref` or `clone()`) +// when a destructuring assignment involves a type that implements `Drop`. + +struct Thing(String); + +impl Drop for Thing { + fn drop(&mut self) {} +} + +fn main() { + Thing(*&mut String::new()) = Thing(String::new()); + //~^ ERROR cannot move out of type `Thing`, which implements the `Drop` trait +} diff --git a/tests/ui/moves/invalid-suggestions-destructuring-assignment-drop.stderr b/tests/ui/moves/invalid-suggestions-destructuring-assignment-drop.stderr new file mode 100644 index 0000000000000..c2bc85ee6beee --- /dev/null +++ b/tests/ui/moves/invalid-suggestions-destructuring-assignment-drop.stderr @@ -0,0 +1,12 @@ +error[E0509]: cannot move out of type `Thing`, which implements the `Drop` trait + --> $DIR/invalid-suggestions-destructuring-assignment-drop.rs:12:34 + | +LL | Thing(*&mut String::new()) = Thing(String::new()); + | ------------------- ^^^^^^^^^^^^^^^^^^^^ cannot move out of here + | | + | data moved here + | move occurs because the place has type `String`, which does not implement the `Copy` trait + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0509`. From 46ddd484fd28e7deb1097158fff748b8e0972578 Mon Sep 17 00:00:00 2001 From: Ada Bohm Date: Sun, 15 Feb 2026 08:45:07 +0100 Subject: [PATCH 07/55] Changes after code review --- .../rustc_const_eval/src/const_eval/valtrees.rs | 2 +- compiler/rustc_middle/src/arena.rs | 1 - compiler/rustc_middle/src/ty/consts/valtree.rs | 14 ++++++++------ .../rustc_mir_build/src/builder/matches/mod.rs | 5 ++--- compiler/rustc_type_ir/src/const_kind.rs | 11 +++++------ compiler/rustc_type_ir/src/inherent.rs | 6 ------ compiler/rustc_type_ir/src/interner.rs | 2 +- compiler/rustc_type_ir/src/relate.rs | 5 ++--- 8 files changed, 19 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index b771addb8df55..4323debd80149 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -413,7 +413,7 @@ fn valtree_into_mplace<'tcx>( Some(variant_idx), ) } - _ => (place.clone(), branches, None), + _ => (place.clone(), branches.as_slice(), None), }; debug!(?place_adjusted, ?branches); diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 0f254aaa9fa0a..e832ac9f1cd34 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -92,7 +92,6 @@ macro_rules! arena_types { [] name_set: rustc_data_structures::unord::UnordSet, [] autodiff_item: rustc_ast::expand::autodiff_attrs::AutoDiffItem, [] ordered_name_set: rustc_data_structures::fx::FxIndexSet, - [] valtree: rustc_middle::ty::ValTreeKind>, [] stable_order_of_exportable_impls: rustc_data_structures::fx::FxIndexMap, diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 8cd119feb6f01..6065121f27339 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -31,12 +31,6 @@ impl<'tcx> ty::ValTreeKind> { // recurses through pub struct ValTree<'tcx>(pub(crate) Interned<'tcx, ty::ValTreeKind>>); -impl<'tcx> rustc_type_ir::inherent::ValTree> for ValTree<'tcx> { - fn kind(&self) -> &ty::ValTreeKind> { - &self - } -} - impl<'tcx> ValTree<'tcx> { /// Returns the zero-sized valtree: `Branch([])`. pub fn zst(tcx: TyCtxt<'tcx>) -> Self { @@ -83,6 +77,14 @@ impl fmt::Debug for ValTree<'_> { } } +impl<'tcx> rustc_type_ir::inherent::IntoKind for ty::ValTree<'tcx> { + type Kind = ty::ValTreeKind>; + + fn kind(self) -> Self::Kind { + *self.0 + } +} + /// `Ok(Err(ty))` indicates the constant was fine, but the valtree couldn't be constructed /// because the value contains something of type `ty` that is not valtree-compatible. /// The caller can then show an appropriate error; the query does not have the diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index e89f6658bd142..38055471e83a8 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -2944,11 +2944,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let ValTreeKind::Branch(branch) = *valtree else { bug!("malformed valtree for an enum") }; - let Some(actual_variant_idx) = branch.get(0) else { + if branch.len() != 1 { bug!("malformed valtree for an enum") }; - let ValTreeKind::Leaf(actual_variant_idx) = *actual_variant_idx.to_value().valtree - else { + let ValTreeKind::Leaf(actual_variant_idx) = **branch[0].to_value().valtree else { bug!("malformed valtree for an enum") }; diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index b8f0b64f62bf0..49fb94c830e24 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -9,7 +9,6 @@ use rustc_type_ir_macros::{ GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic, }; -use crate::inherent::*; use crate::{self as ty, BoundVarIndexKind, Interner}; /// Represents a constant in Rust. @@ -141,7 +140,7 @@ impl HashStable for InferConst { /// /// `ValTree` does not have this problem with representation, as it only contains integers or /// lists of (nested) `ty::Const`s (which may indirectly contain more `ValTree`s). -#[derive_where(Clone, Debug, Hash, Eq, PartialEq; I: Interner)] +#[derive_where(Clone, Copy, Debug, Hash, Eq, PartialEq; I: Interner)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", @@ -177,9 +176,9 @@ impl ValTreeKind { /// Converts to a `ValTreeKind::Branch` value, `panic`'ing /// if this valtree is some other kind. #[inline] - pub fn to_branch(&self) -> &[I::Const] { + pub fn to_branch(&self) -> I::Consts { match self { - ValTreeKind::Branch(branch) => branch.as_slice(), + ValTreeKind::Branch(branch) => *branch, ValTreeKind::Leaf(..) => panic!("expected branch, got {:?}", self), } } @@ -193,9 +192,9 @@ impl ValTreeKind { } /// Attempts to convert to a `ValTreeKind::Branch` value. - pub fn try_to_branch(&self) -> Option<&[I::Const]> { + pub fn try_to_branch(&self) -> Option { match self { - ValTreeKind::Branch(branch) => Some(branch.as_slice()), + ValTreeKind::Branch(branch) => Some(*branch), ValTreeKind::Leaf(_) => None, } } diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index c9580d589d217..bcc1929d97b53 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -292,12 +292,6 @@ pub trait ValueConst>: Copy + Debug + Hash + Eq { fn valtree(self) -> I::ValTree; } -// FIXME(mgca): This trait can be removed once we're not using a `Box` in `Branch` -pub trait ValTree>: Copy + Debug + Hash + Eq { - // This isnt' `IntoKind` because then we can't return a reference - fn kind(&self) -> &ty::ValTreeKind; -} - pub trait ExprConst>: Copy + Debug + Hash + Eq + Relate { fn args(self) -> I::GenericArgs; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 4cafaadf38d85..e77e7af071b90 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -154,7 +154,7 @@ pub trait Interner: type ParamConst: Copy + Debug + Hash + Eq + ParamLike; type ValueConst: ValueConst; type ExprConst: ExprConst; - type ValTree: ValTree; + type ValTree: Copy + Debug + Hash + Eq + IntoKind>; type ScalarInt: Copy + Debug + Hash + Eq; // Kinds of regions diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs index 744331b0caa53..d33c6036dadd8 100644 --- a/compiler/rustc_type_ir/src/relate.rs +++ b/compiler/rustc_type_ir/src/relate.rs @@ -577,10 +577,9 @@ pub fn structurally_relate_consts>( if branches_a.len() == branches_b.len() => { branches_a - .as_slice() .iter() - .zip(branches_b.as_slice().iter()) - .all(|(a, b)| relation.relate(*a, *b).is_ok()) + .zip(branches_b.iter()) + .all(|(a, b)| relation.relate(a, b).is_ok()) } _ => false, } From 515d05522c4c12f65d816684f11b5ad6fc5a0df7 Mon Sep 17 00:00:00 2001 From: Antonio Souza Date: Tue, 17 Feb 2026 22:46:37 -0500 Subject: [PATCH 08/55] Update compiler/rustc_borrowck/src/diagnostics/move_errors.rs Co-authored-by: Esteban Kuber --- .../rustc_borrowck/src/diagnostics/move_errors.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index 7bb484ee73f32..157234ce4229a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -999,11 +999,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if binds_to.len() == 1 { let place_desc = self.local_name(*local).map(|sym| format!("`{sym}`")); - if !desugar_spans.contains(&binding_span) { - if let Some(expr) = self.find_expr(binding_span) { - let local_place: PlaceRef<'tcx> = (*local).into(); - self.suggest_cloning(err, local_place, bind_to.ty, expr, None); - } + if !desugar_spans.contains(&binding_span) + && let Some(expr) = self.find_expr(binding_span) + { + // The binding_span doesn't correspond to a let binding desugaring + // and is an expression where calling `.clone()` would be valid. + let local_place: PlaceRef<'tcx> = (*local).into(); + self.suggest_cloning(err, local_place, bind_to.ty, expr, None); } err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { From c1b0b32fe9c150392345c9f9c35f6c056f5ffcd4 Mon Sep 17 00:00:00 2001 From: Albab-Hasan Date: Wed, 18 Feb 2026 11:42:35 +0600 Subject: [PATCH 09/55] fix: verify NeverToAny adjustment on binop lhs never place --- .../rust-analyzer/crates/hir-ty/src/tests/never_type.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 6db256f499a5a..d150df5567200 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -823,11 +823,11 @@ fn binop_lhs_never_place_diverges() { check_no_mismatches( r#" //- minicore: sized, add -fn foo() -> i32 { +fn foo() { unsafe { - let p: *const ! = 0 as _; - let _x = *p + 1; - // ^^ adjustments: NeverToAny + let p: *mut ! = 0 as _; + *p + 1; +// ^^ adjustments: NeverToAny } } "#, From f4dcfa2e7f0f354220114069bc45c867cd2ee36f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 17 Feb 2026 23:09:29 +0800 Subject: [PATCH 10/55] fix: offer block `.let` in ref-expr in match arm Example --- ```rust fn main() { match 2 { bar => &bar.l$0 } } ``` **Before this PR** ```text sn deref *expr sn match match expr {} ``` **After this PR** ```text sn deref *expr sn let let sn letm let mut sn match match expr {} ``` --- .../ide-completion/src/completions/postfix.rs | 64 +++++++++++++------ 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index a58592b1365b8..ea53aef40c2e7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -16,7 +16,7 @@ use itertools::Itertools; use stdx::never; use syntax::{ SmolStr, - SyntaxKind::{BLOCK_EXPR, EXPR_STMT, STMT_LIST}, + SyntaxKind::{EXPR_STMT, STMT_LIST}, T, TextRange, TextSize, ToSmolStr, ast::{self, AstNode, AstToken}, format_smolstr, match_ast, @@ -52,6 +52,7 @@ pub(crate) fn complete_postfix( _ => return, }; let expr_ctx = &dot_access.ctx; + let receiver_accessor = receiver_accessor(dot_receiver); let receiver_text = get_receiver_text(&ctx.sema, dot_receiver, receiver_is_ambiguous_float_literal); @@ -90,7 +91,7 @@ pub(crate) fn complete_postfix( // The rest of the postfix completions create an expression that moves an argument, // so it's better to consider references now to avoid breaking the compilation - let (dot_receiver_including_refs, prefix) = include_references(dot_receiver); + let (dot_receiver_including_refs, prefix) = include_references(&receiver_accessor); let mut receiver_text = receiver_text; receiver_text.insert_str(0, &prefix); let postfix_snippet = @@ -111,13 +112,8 @@ pub(crate) fn complete_postfix( .add_to(acc, ctx.db); let try_enum = TryEnum::from_ty(&ctx.sema, receiver_ty); - let mut is_in_cond = false; - if let Some(parent) = dot_receiver_including_refs.syntax().parent() - && let Some(second_ancestor) = parent.parent() - { - if let Some(parent_expr) = ast::Expr::cast(parent) { - is_in_cond = is_in_condition(&parent_expr); - } + let is_in_cond = is_in_condition(&dot_receiver_including_refs); + if let Some(parent) = dot_receiver_including_refs.syntax().parent() { let placeholder = suggest_receiver_name(dot_receiver, "0", &ctx.sema); match &try_enum { Some(try_enum) if is_in_cond => match try_enum { @@ -154,13 +150,13 @@ pub(crate) fn complete_postfix( postfix_snippet("let", "let", &format!("let $1 = {receiver_text}")) .add_to(acc, ctx.db); } - _ if matches!(second_ancestor.kind(), STMT_LIST | EXPR_STMT | BLOCK_EXPR) => { + _ if matches!(parent.kind(), STMT_LIST | EXPR_STMT) => { postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")) .add_to(acc, ctx.db); postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};")) .add_to(acc, ctx.db); } - _ if ast::MatchArm::can_cast(second_ancestor.kind()) => { + _ if ast::MatchArm::can_cast(parent.kind()) => { postfix_snippet( "let", "let", @@ -305,7 +301,7 @@ pub(crate) fn complete_postfix( postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db); } - if let ast::Expr::Literal(literal) = dot_receiver_including_refs.clone() + if let ast::Expr::Literal(literal) = dot_receiver.clone() && let Some(literal_text) = ast::String::cast(literal.token()) { add_format_like_completions(acc, ctx, &dot_receiver_including_refs, cap, &literal_text); @@ -392,14 +388,22 @@ fn escape_snippet_bits(text: &mut String) { stdx::replace(text, '$', "\\$"); } +fn receiver_accessor(receiver: &ast::Expr) -> ast::Expr { + receiver + .syntax() + .parent() + .and_then(ast::Expr::cast) + .filter(|it| { + matches!( + it, + ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) + ) + }) + .unwrap_or_else(|| receiver.clone()) +} + fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) { let mut resulting_element = initial_element.clone(); - - while let Some(field_expr) = resulting_element.syntax().parent().and_then(ast::FieldExpr::cast) - { - resulting_element = ast::Expr::from(field_expr); - } - let mut prefix = String::new(); let mut found_ref_or_deref = false; @@ -898,6 +902,30 @@ fn main() { bar => bar.$0 } } +"#, + expect![[r#" + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); + check( + r#" +fn main() { + match 2 { + bar => &bar.l$0 + } +} "#, expect![[r#" sn box Box::new(expr) From 5ea071619908692ffb205aa09c0b5f7bbe3fcc6f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 20 Feb 2026 12:03:28 +0800 Subject: [PATCH 11/55] feat: offer destructure_struct_binding on self param Example --- ```rust struct Foo { bar: i32, baz: i32 } impl Foo { fn foo(&mut $0self) { self.bar = 5; } } ``` **Before this PR** Assist not applicable **After this PR** ```rust struct Foo { bar: i32, baz: i32 } impl Foo { fn foo(&mut self) { let Foo { bar, baz } = self; *bar = 5; } } ``` --- .../handlers/destructure_struct_binding.rs | 231 +++++++++++++++--- .../src/handlers/destructure_tuple_binding.rs | 1 + 2 files changed, 198 insertions(+), 34 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs index bb5d11221087e..4c4cee1d7811f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_struct_binding.rs @@ -1,15 +1,19 @@ -use hir::HasVisibility; +use hir::{HasVisibility, Semantics}; use ide_db::{ - FxHashMap, FxHashSet, + FxHashMap, FxHashSet, RootDatabase, assists::AssistId, defs::Definition, helpers::mod_path_to_ast, search::{FileReference, SearchScope}, }; use itertools::Itertools; -use syntax::ast::{HasName, syntax_factory::SyntaxFactory}; use syntax::syntax_editor::SyntaxEditor; use syntax::{AstNode, Edition, SmolStr, SyntaxNode, ToSmolStr, ast}; +use syntax::{ + SyntaxToken, + ast::{HasName, edit::IndentLevel, syntax_factory::SyntaxFactory}, + syntax_editor::Position, +}; use crate::{ assist_context::{AssistContext, Assists, SourceChangeBuilder}, @@ -44,33 +48,90 @@ use crate::{ // } // ``` pub(crate) fn destructure_struct_binding(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let ident_pat = ctx.find_node_at_offset::()?; - let data = collect_data(ident_pat, ctx)?; + let target = ctx.find_node_at_offset::()?; + let data = collect_data(target, ctx)?; acc.add( AssistId::refactor_rewrite("destructure_struct_binding"), "Destructure struct binding", - data.ident_pat.syntax().text_range(), + data.target.syntax().text_range(), |edit| destructure_struct_binding_impl(ctx, edit, &data), ); Some(()) } +enum Target { + IdentPat(ast::IdentPat), + SelfParam { param: ast::SelfParam, insert_after: SyntaxToken }, +} + +impl Target { + fn ty<'db>(&self, sema: &Semantics<'db, RootDatabase>) -> Option> { + match self { + Target::IdentPat(pat) => sema.type_of_binding_in_pat(pat), + Target::SelfParam { param, .. } => sema.type_of_self(param), + } + } + + fn is_ref(&self) -> bool { + match self { + Target::IdentPat(ident_pat) => ident_pat.ref_token().is_some(), + Target::SelfParam { .. } => false, + } + } + + fn is_mut(&self) -> bool { + match self { + Target::IdentPat(ident_pat) => ident_pat.mut_token().is_some(), + Target::SelfParam { param, .. } => { + param.mut_token().is_some() && param.amp_token().is_none() + } + } + } +} + +impl HasName for Target {} + +impl AstNode for Target { + fn cast(node: SyntaxNode) -> Option { + if ast::IdentPat::can_cast(node.kind()) { + ast::IdentPat::cast(node).map(Self::IdentPat) + } else { + let param = ast::SelfParam::cast(node)?; + let param_list = param.syntax().parent().and_then(ast::ParamList::cast)?; + let block = param_list.syntax().parent()?.children().find_map(ast::BlockExpr::cast)?; + let insert_after = block.stmt_list()?.l_curly_token()?; + Some(Self::SelfParam { param, insert_after }) + } + } + + fn can_cast(kind: syntax::SyntaxKind) -> bool { + ast::IdentPat::can_cast(kind) || ast::SelfParam::can_cast(kind) + } + + fn syntax(&self) -> &SyntaxNode { + match self { + Target::IdentPat(ident_pat) => ident_pat.syntax(), + Target::SelfParam { param, .. } => param.syntax(), + } + } +} + fn destructure_struct_binding_impl( ctx: &AssistContext<'_>, builder: &mut SourceChangeBuilder, data: &StructEditData, ) { let field_names = generate_field_names(ctx, data); - let mut editor = builder.make_editor(data.ident_pat.syntax()); + let mut editor = builder.make_editor(data.target.syntax()); destructure_pat(ctx, &mut editor, data, &field_names); update_usages(ctx, &mut editor, data, &field_names.into_iter().collect()); builder.add_file_edits(ctx.vfs_file_id(), editor); } struct StructEditData { - ident_pat: ast::IdentPat, + target: Target, name: ast::Name, kind: hir::StructKind, struct_def_path: hir::ModPath, @@ -83,11 +144,44 @@ struct StructEditData { edition: Edition, } -fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option { - let ty = ctx.sema.type_of_binding_in_pat(&ident_pat)?; +impl StructEditData { + fn apply_to_destruct( + &self, + new_pat: ast::Pat, + editor: &mut SyntaxEditor, + make: &SyntaxFactory, + ) { + match &self.target { + Target::IdentPat(pat) => { + // If the binding is nested inside a record, we need to wrap the new + // destructured pattern in a non-shorthand record field + if self.need_record_field_name { + let new_pat = + make.record_pat_field(make.name_ref(&self.name.to_string()), new_pat); + editor.replace(pat.syntax(), new_pat.syntax()) + } else { + editor.replace(pat.syntax(), new_pat.syntax()) + } + } + Target::SelfParam { insert_after, .. } => { + let indent = IndentLevel::from_token(insert_after) + 1; + let newline = make.whitespace(&format!("\n{indent}")); + let initializer = make.expr_path(make.ident_path("self")); + let let_stmt = make.let_stmt(new_pat, None, Some(initializer)); + editor.insert_all( + Position::after(insert_after), + vec![newline.into(), let_stmt.syntax().clone().into()], + ); + } + } + } +} + +fn collect_data(target: Target, ctx: &AssistContext<'_>) -> Option { + let ty = target.ty(&ctx.sema)?; let hir::Adt::Struct(struct_type) = ty.strip_references().as_adt()? else { return None }; - let module = ctx.sema.scope(ident_pat.syntax())?.module(); + let module = ctx.sema.scope(target.syntax())?.module(); let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.db()))); let struct_def = hir::ModuleDef::from(struct_type); let kind = struct_type.kind(ctx.db()); @@ -116,15 +210,17 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option ctx.sema.to_def(pat), + Target::SelfParam { param, .. } => ctx.sema.to_def(param), + }; + let usages = def .and_then(|def| { Definition::Local(def) .usages(&ctx.sema) @@ -136,11 +232,11 @@ fn collect_data(ident_pat: ast::IdentPat, ctx: &AssistContext<'_>) -> Option) -> Option, - ident_pat: &ast::IdentPat, + target: &Target, usages: &[FileReference], ) -> Option> { fn last_usage(usages: &[FileReference]) -> Option { @@ -165,7 +261,7 @@ fn get_names_in_scope( // If available, find names visible to the last usage of the binding // else, find names visible to the binding itself let last_usage = last_usage(usages); - let node = last_usage.as_ref().unwrap_or(ident_pat.syntax()); + let node = last_usage.as_ref().unwrap_or(target.syntax()); let scope = ctx.sema.scope(node)?; let mut names = FxHashSet::default(); @@ -183,12 +279,9 @@ fn destructure_pat( data: &StructEditData, field_names: &[(SmolStr, SmolStr)], ) { - let ident_pat = &data.ident_pat; - let name = &data.name; - let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition); - let is_ref = ident_pat.ref_token().is_some(); - let is_mut = ident_pat.mut_token().is_some(); + let is_ref = data.target.is_ref(); + let is_mut = data.target.is_mut(); let make = SyntaxFactory::with_mappings(); let new_pat = match data.kind { @@ -221,16 +314,8 @@ fn destructure_pat( hir::StructKind::Unit => make.path_pat(struct_path), }; - // If the binding is nested inside a record, we need to wrap the new - // destructured pattern in a non-shorthand record field - let destructured_pat = if data.need_record_field_name { - make.record_pat_field(make.name_ref(&name.to_string()), new_pat).syntax().clone() - } else { - new_pat.syntax().clone() - }; - + data.apply_to_destruct(new_pat, editor, &make); editor.add_mappings(make.finish_with_mappings()); - editor.replace(data.ident_pat.syntax(), destructured_pat); } fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(SmolStr, SmolStr)> { @@ -698,6 +783,84 @@ mod tests { ) } + #[test] + fn mut_self_param() { + check_assist( + destructure_struct_binding, + r#" + struct Foo { bar: i32, baz: i32 } + + impl Foo { + fn foo(mut $0self) { + self.bar = 5; + } + } + "#, + r#" + struct Foo { bar: i32, baz: i32 } + + impl Foo { + fn foo(mut self) { + let Foo { mut bar, mut baz } = self; + bar = 5; + } + } + "#, + ) + } + + #[test] + fn ref_mut_self_param() { + check_assist( + destructure_struct_binding, + r#" + struct Foo { bar: i32, baz: i32 } + + impl Foo { + fn foo(&mut $0self) { + self.bar = 5; + } + } + "#, + r#" + struct Foo { bar: i32, baz: i32 } + + impl Foo { + fn foo(&mut self) { + let Foo { bar, baz } = self; + *bar = 5; + } + } + "#, + ) + } + + #[test] + fn ref_self_param() { + check_assist( + destructure_struct_binding, + r#" + struct Foo { bar: i32, baz: i32 } + + impl Foo { + fn foo(&$0self) -> &i32 { + &self.bar + } + } + "#, + r#" + struct Foo { bar: i32, baz: i32 } + + impl Foo { + fn foo(&self) -> &i32 { + let Foo { bar, baz } = self; + bar + } + } + "#, + ) + } + #[test] fn ref_not_add_parenthesis_and_deref_record() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index b8dc59f87dee7..d51a3a26a3c7c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -164,6 +164,7 @@ enum RefType { Mutable, } struct TupleData { + // FIXME: After removing ted, it may be possible to reuse destructure_struct_binding::Target ident_pat: IdentPat, ref_type: Option, field_names: Vec, From 14b3a40c2d33c293561a0285c6fb8c75f70a37c5 Mon Sep 17 00:00:00 2001 From: Ada Bohm Date: Mon, 23 Feb 2026 15:17:01 +0100 Subject: [PATCH 12/55] Test updated --- tests/debuginfo/function-names.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/debuginfo/function-names.rs b/tests/debuginfo/function-names.rs index e2f106e565439..5973f63bbae9e 100644 --- a/tests/debuginfo/function-names.rs +++ b/tests/debuginfo/function-names.rs @@ -39,7 +39,7 @@ // Const generic parameter //@ gdb-command:info functions -q function_names::const_generic_fn.* //@ gdb-check:[...]static fn function_names::const_generic_fn_bool(); -//@ gdb-check:[...]static fn function_names::const_generic_fn_non_int<{CONST#ffa3db4ca1d52dce}>(); +//@ gdb-check:[...]static fn function_names::const_generic_fn_non_int<{CONST#6dd80cc0c950c171}>(); //@ gdb-check:[...]static fn function_names::const_generic_fn_signed_int<-7>(); //@ gdb-check:[...]static fn function_names::const_generic_fn_unsigned_int<14>(); From 05ab6d14865f241b9651221db08e1733949d0e83 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 17 Feb 2026 15:12:14 +0000 Subject: [PATCH 13/55] internal: Configure codecov --- src/tools/rust-analyzer/.codecov.yml | 10 +++++ .../.github/workflows/coverage.yaml | 44 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/tools/rust-analyzer/.codecov.yml create mode 100644 src/tools/rust-analyzer/.github/workflows/coverage.yaml diff --git a/src/tools/rust-analyzer/.codecov.yml b/src/tools/rust-analyzer/.codecov.yml new file mode 100644 index 0000000000000..68eacb7d08cbb --- /dev/null +++ b/src/tools/rust-analyzer/.codecov.yml @@ -0,0 +1,10 @@ +coverage: + range: 40...60 + status: + patch: off + project: + default: + informational: true + +# Don't leave comments on PRs +comment: false diff --git a/src/tools/rust-analyzer/.github/workflows/coverage.yaml b/src/tools/rust-analyzer/.github/workflows/coverage.yaml new file mode 100644 index 0000000000000..bd4edba747ca1 --- /dev/null +++ b/src/tools/rust-analyzer/.github/workflows/coverage.yaml @@ -0,0 +1,44 @@ +name: Coverage + +on: [pull_request, push] + +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + CI: 1 + RUST_BACKTRACE: short + RUSTUP_MAX_RETRIES: 10 + +jobs: + coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Rust toolchain + run: | + rustup update --no-self-update nightly + rustup default nightly + rustup component add --toolchain nightly rust-src rustc-dev rustfmt + # We also install a nightly rustfmt, because we use `--file-lines` in + # a test. + rustup toolchain install nightly --profile minimal --component rustfmt + + rustup toolchain install nightly --component llvm-tools-preview + + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + + - name: Install nextest + uses: taiki-e/install-action@nextest + + - name: Generate code coverage + run: cargo llvm-cov --workspace --lcov --output-path lcov.info + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v5 + with: + files: lcov.info + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} + verbose: true From cde693cc5d74a132af8149585000fcfd6b1c4fbd Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 27 Feb 2026 18:51:42 +0800 Subject: [PATCH 14/55] fix: Improve newline for make `match`, `if`, `while` etc - Remove unused `ast::make::match_arm_with_guard` Example --- ```rust fn main() { if true { $0if true && false { foo() } } } ``` **Before this PR** ```rust fn main() { if true { match true && false { true => foo(), false => (), } } } ``` **After this PR** ```rust fn main() { if true { match true && false { true => foo(), false => (), } } } ``` --- .../ide-assists/src/handlers/move_guard.rs | 3 +- .../src/handlers/replace_if_let_with_match.rs | 15 ++++++--- .../crates/syntax/src/ast/make.rs | 31 ++++++++----------- 3 files changed, 25 insertions(+), 24 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 fdc5a0fbda359..b4c347ff36a7c 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 @@ -567,7 +567,8 @@ fn main() { match 92 { x => if true && true - && true { + && true + { { false } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs index dcadb5368d525..8ff30fce5b5d4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_if_let_with_match.rs @@ -972,7 +972,8 @@ fn main() { fn main() { if true { match path.strip_prefix(root_path) - .and(x) { + .and(x) + { Ok(rel_path) => { let rel_path = RelativePathBuf::from_path(rel_path) .ok()?; @@ -1012,7 +1013,8 @@ fn main() { fn main() { if true { match path.strip_prefix(root_path) - .and(x) { + .and(x) + { Ok(rel_path) => { Foo { x: 1 @@ -1046,7 +1048,8 @@ fn main() { fn main() { if true { match true - && false { + && false + { true => foo(), false => (), } @@ -1925,7 +1928,8 @@ fn main() { fn main() { if true { if let Ok(rel_path) = path.strip_prefix(root_path) - .and(x) { + .and(x) + { Foo { x: 2 } @@ -1965,7 +1969,8 @@ fn main() { fn main() { if true { if let Ok(rel_path) = path.strip_prefix(root_path) - .and(x) { + .and(x) + { let rel_path = RelativePathBuf::from_path(rel_path) .ok()?; Some((*id, rel_path)) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 98d759aef2093..f5d1d009a579b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -644,7 +644,8 @@ pub fn expr_await(expr: ast::Expr) -> ast::Expr { expr_from_text(&format!("{expr}.await")) } pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::MatchExpr { - expr_from_text(&format!("match {expr} {match_arm_list}")) + let ws = block_whitespace(&expr); + expr_from_text(&format!("match {expr}{ws}{match_arm_list}")) } pub fn expr_if( condition: ast::Expr, @@ -656,14 +657,17 @@ pub fn expr_if( Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {if_expr}"), None => String::new(), }; - expr_from_text(&format!("if {condition} {then_branch} {else_branch}")) + let ws = block_whitespace(&condition); + expr_from_text(&format!("if {condition}{ws}{then_branch} {else_branch}")) } pub fn expr_for_loop(pat: ast::Pat, expr: ast::Expr, block: ast::BlockExpr) -> ast::ForExpr { - expr_from_text(&format!("for {pat} in {expr} {block}")) + let ws = block_whitespace(&expr); + expr_from_text(&format!("for {pat} in {expr}{ws}{block}")) } pub fn expr_while_loop(condition: ast::Expr, block: ast::BlockExpr) -> ast::WhileExpr { - expr_from_text(&format!("while {condition} {block}")) + let ws = block_whitespace(&condition); + expr_from_text(&format!("while {condition}{ws}{block}")) } pub fn expr_loop(block: ast::BlockExpr) -> ast::Expr { @@ -723,6 +727,9 @@ pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr { fn expr_from_text + AstNode>(text: &str) -> E { ast_from_text(&format!("const C: () = {text};")) } +fn block_whitespace(after: &impl AstNode) -> &'static str { + if after.syntax().text().contains_char('\n') { "\n" } else { " " } +} pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr { ast_from_text(&format!("const _: () = while let {pattern} = {expr} {{}};")) } @@ -880,8 +887,9 @@ pub fn ref_pat(pat: ast::Pat) -> ast::RefPat { pub fn match_arm(pat: ast::Pat, guard: Option, expr: ast::Expr) -> ast::MatchArm { let comma_str = if expr.is_block_like() { "" } else { "," }; + let ws = guard.as_ref().filter(|_| expr.is_block_like()).map_or(" ", block_whitespace); return match guard { - Some(guard) => from_text(&format!("{pat} {guard} => {expr}{comma_str}")), + Some(guard) => from_text(&format!("{pat} {guard} =>{ws}{expr}{comma_str}")), None => from_text(&format!("{pat} => {expr}{comma_str}")), }; @@ -890,19 +898,6 @@ pub fn match_arm(pat: ast::Pat, guard: Option, expr: ast::Expr) } } -pub fn match_arm_with_guard( - pats: impl IntoIterator, - guard: ast::Expr, - expr: ast::Expr, -) -> ast::MatchArm { - let pats_str = pats.into_iter().join(" | "); - return from_text(&format!("{pats_str} if {guard} => {expr}")); - - fn from_text(text: &str) -> ast::MatchArm { - ast_from_text(&format!("fn f() {{ match () {{{text}}} }}")) - } -} - pub fn match_guard(condition: ast::Expr) -> ast::MatchGuard { return from_text(&format!("if {condition}")); From f19a73cdffce846d9f6db1d854536baf931b3178 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 02:14:32 +0000 Subject: [PATCH 15/55] build(deps-dev): bump minimatch from 3.1.2 to 3.1.5 in /editors/code Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.1.2 to 3.1.5. - [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md) - [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5) --- updated-dependencies: - dependency-name: minimatch dependency-version: 3.1.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- .../editors/code/package-lock.json | 94 +++++++++---------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index d3fbea2336b0b..be0bdc8d55bbf 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -749,9 +749,9 @@ } }, "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -810,9 +810,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -934,29 +934,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1851,9 +1828,9 @@ } }, "node_modules/@vscode/vsce/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -3463,9 +3440,9 @@ } }, "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -3907,17 +3884,40 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/glob/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "brace-expansion": "^5.0.2" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4895,13 +4895,13 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6840,9 +6840,9 @@ } }, "node_modules/vscode-languageclient/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" From aa53530f2720f92a442290706539d8275ffd4f72 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 28 Feb 2026 18:00:32 +0100 Subject: [PATCH 16/55] fix: Fix wrong confiditon in `Visibility::min` --- src/tools/rust-analyzer/crates/hir-def/src/visibility.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs index a1645de6ec236..6fe9c186cc757 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs @@ -234,7 +234,7 @@ impl Visibility { if mod_.krate(db) == krate { Some(Visibility::Module(mod_, exp)) } else { None } } (Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => { - if mod_a.krate(db) != mod_b.krate(db) { + if mod_a == mod_b { // Most module visibilities are `pub(self)`, and assuming no errors // this will be the common and thus fast path. return Some(Visibility::Module( From a4c683f7dfdce6781cccd2830e67c6b179fd665f Mon Sep 17 00:00:00 2001 From: Albab-Hasan Date: Sun, 1 Mar 2026 00:04:13 +0600 Subject: [PATCH 17/55] fix: use compound assignment in binop_lhs_never_place_diverges test --- src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index d150df5567200..1ff803ca422c6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -826,8 +826,7 @@ fn binop_lhs_never_place_diverges() { fn foo() { unsafe { let p: *mut ! = 0 as _; - *p + 1; -// ^^ adjustments: NeverToAny + *p += 1; } } "#, From c9763e717d6f72baaee69acdea26b98999638992 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 1 Mar 2026 02:35:57 +0800 Subject: [PATCH 18/55] triagebot: roll rustfmt reviewers for in-tree rustfmt --- triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/triagebot.toml b/triagebot.toml index 7e4c9c1644ae1..74c110cc74b2a 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1620,6 +1620,7 @@ dep-bumps = [ "/src/tools/rustdoc" = ["rustdoc"] "/src/tools/rustdoc-js" = ["rustdoc"] "/src/tools/rustdoc-themes" = ["rustdoc"] +"/src/tools/rustfmt" = ["rustfmt", "rustfmt-contributors"] "/src/tools/tidy" = ["bootstrap"] "/src/tools/x" = ["bootstrap"] "/src/tools/rustdoc-gui-test" = ["bootstrap"] From 747aa5beec13a622ac570e9cab2ee0040f94a097 Mon Sep 17 00:00:00 2001 From: Albab-Hasan Date: Sun, 1 Mar 2026 17:09:21 +0600 Subject: [PATCH 19/55] Fix: use `FnAbi::Rust` for tuple struct/enum constructors and align `is_rust()` with rustc --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 4 +-- .../crates/hir-ty/src/next_solver/abi.rs | 3 +- .../crates/hir-ty/src/tests/traits.rs | 34 +++++++++++++++++++ 3 files changed, 37 insertions(+), 4 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 1290874177ac4..bb126f16ffa28 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -2468,7 +2468,7 @@ fn fn_sig_for_struct_constructor( let inputs_and_output = Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret.as_ref()))); StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { - abi: FnAbi::RustCall, + abi: FnAbi::Rust, c_variadic: false, safety: Safety::Safe, inputs_and_output, @@ -2487,7 +2487,7 @@ fn fn_sig_for_enum_variant_constructor( let inputs_and_output = Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret.as_ref()))); StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { - abi: FnAbi::RustCall, + abi: FnAbi::Rust, c_variadic: false, safety: Safety::Safe, inputs_and_output, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs index 80d1ea4aa4d00..1813abab86e7b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/abi.rs @@ -62,7 +62,6 @@ impl<'db> rustc_type_ir::inherent::Abi> for FnAbi { } fn is_rust(self) -> bool { - // TODO: rustc does not consider `RustCall` to be true here, but Chalk does - matches!(self, FnAbi::Rust | FnAbi::RustCall) + matches!(self, FnAbi::Rust) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index cdf7b40003b5b..74e9a8dac0e79 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -2217,6 +2217,40 @@ fn test() { ); } +#[test] +fn tuple_struct_constructor_as_fn_trait() { + check_types( + r#" +//- minicore: fn +struct S(u32, u64); + +fn takes_fn S>(f: F) -> S { f(1, 2) } + +fn test() { + takes_fn(S); + //^^^^^^^^^^^ S +} +"#, + ); +} + +#[test] +fn enum_variant_constructor_as_fn_trait() { + check_types( + r#" +//- minicore: fn +enum E { A(u32) } + +fn takes_fn E>(f: F) -> E { f(1) } + +fn test() { + takes_fn(E::A); + //^^^^^^^^^^^^^^ E +} +"#, + ); +} + #[test] fn fn_item_fn_trait() { check_types( From 21674fed54a8c2493323d0ae58a73cce99b885e3 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Mon, 2 Mar 2026 00:21:19 +0800 Subject: [PATCH 20/55] fix: Fix wrong descend range for add_missing_match_arms Example --- ```rust macro_rules! m { ($expr:expr) => {$expr}} enum Test { A, B, C, } fn foo(t: Test) { m!(match t { Test::A => (), $0}); } ``` **Before this PR** Assist not applicable **After this PR** ```rust macro_rules! m { ($expr:expr) => {$expr}} enum Test { A, B, C, } fn foo(t: Test) { m!(match t { Test::A=>(), Test::B => ${1:todo!()}, Test::C => ${2:todo!()},$0 }); } ``` --- .../src/handlers/add_missing_match_arms.rs | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index 10c3ff0e4d2be..c0ce057d7798d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -44,8 +44,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let arm_list_range = ctx.sema.original_range_opt(match_arm_list.syntax())?; if cursor_at_trivial_match_arm_list(ctx, &match_expr, &match_arm_list).is_none() { - let arm_list_range = ctx.sema.original_range(match_arm_list.syntax()).range; - let cursor_in_range = arm_list_range.contains_range(ctx.selection_trimmed()); + let cursor_in_range = arm_list_range.range.contains_range(ctx.selection_trimmed()); if cursor_in_range { cov_mark::hit!(not_applicable_outside_of_range_right); return None; @@ -348,8 +347,8 @@ fn cursor_at_trivial_match_arm_list( // $0 // } if let Some(last_arm) = match_arm_list.arms().last() { - let last_arm_range = last_arm.syntax().text_range(); - let match_expr_range = match_expr.syntax().text_range(); + let last_arm_range = ctx.sema.original_range_opt(last_arm.syntax())?.range; + let match_expr_range = ctx.sema.original_range_opt(match_expr.syntax())?.range; if last_arm_range.end() <= ctx.offset() && ctx.offset() < match_expr_range.end() { cov_mark::hit!(add_missing_match_arms_end_of_last_arm); return Some(()); @@ -1612,6 +1611,38 @@ fn foo(t: Test) { Test::B => ${2:todo!()}, Test::C => ${3:todo!()},$0 }); +}"#, + ); + + check_assist( + add_missing_match_arms, + r#" +macro_rules! m { ($expr:expr) => {$expr}} +enum Test { + A, + B, + C, +} + +fn foo(t: Test) { + m!(match t { + Test::A => (), + $0}); +}"#, + r#" +macro_rules! m { ($expr:expr) => {$expr}} +enum Test { + A, + B, + C, +} + +fn foo(t: Test) { + m!(match t { + Test::A=>(), + Test::B => ${1:todo!()}, + Test::C => ${2:todo!()},$0 + }); }"#, ); } From 7a81a8e66f4273beb647d5f72616794299a4fa5e Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 2 Mar 2026 04:38:05 +0000 Subject: [PATCH 21/55] Prepare for merging from rust-lang/rust This updates the rust-version file to e7d90c695a39426baf5ae705de2f9570a72229de. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index b6e1b2bc55df4..4dedd81b00666 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -c78a29473a68f07012904af11c92ecffa68fcc75 +e7d90c695a39426baf5ae705de2f9570a72229de From 1d892797203ad533106a8c1491e0a2e05af0e374 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 2 Mar 2026 04:38:42 +0000 Subject: [PATCH 22/55] Format code --- .../rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs | 5 +---- src/tools/rust-analyzer/crates/hir-ty/src/lower.rs | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index ae8eef2bbb5d9..949b22b0adaa6 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -5,10 +5,7 @@ use std::borrow::Cow; use base_db::AnchoredPath; use cfg::CfgExpr; use either::Either; -use intern::{ - Symbol, - sym, -}; +use intern::{Symbol, sym}; use itertools::Itertools; use mbe::{DelimiterKind, expect_fragment}; use span::{Edition, FileId, Span}; 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 a0945c3bdce66..895c2e78468e6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -40,8 +40,7 @@ use rustc_hash::FxHashSet; use rustc_type_ir::{ AliasTyKind, BoundVarIndexKind, ConstKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, Interner, OutlivesPredicate, TermKind, - TyKind, - TypeFoldable, TypeVisitableExt, Upcast, UpcastFrom, elaborate, + TyKind, TypeFoldable, TypeVisitableExt, Upcast, UpcastFrom, elaborate, inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _}, }; use smallvec::SmallVec; From 7bd3c42113ad7ead7a00a4e8a2de0e0ad930b7c2 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 2 Mar 2026 11:32:20 +0000 Subject: [PATCH 23/55] internal: Define rules for LLMs rust-analyzer allows AI usage (see #21314), but requires contributors to declare usage. This adds a rule file that improves LLM output quality and instructs the LLM to declare usage in commit messages. I've written the rules in CLAUDE.md, but also symlinked it to AGENTS.md so other LLM tools pick it up. ## Rules file contents (1) Instructions for both humans and AIs to declare AI usage. (2) Relevant commands for testing, linting and codegen. Note that I deliberately didn't include an overview of the project structure on a folder-by-folder basis. This can go stale, and there's some evidence that project structure can hurt LLM output quality overall. See the following paper: > Evaluating AGENTS.md: > Are Repository-Level Context Files Helpful for Coding Agents? > https://arxiv.org/pdf/2602.11988 ## Testing I exercised this change with the following contrived prompt. Note that in practice rust-analyzer is hitting review scaling limits for new code actions, but it was easy to test end-to-end. > Add a new code action that replaces the content of a string literal > with the text "banana". ... > commit it This produced a functional code action with both Codex and Claude, and in both cases the commit message mentioned that it was AI generated. Example commit message: Add "Replace string with banana" code action Add a new assist that replaces a string literal's content with "banana" when the cursor is on a STRING token. AI: Generated with Claude Code (claude-opus-4-6). I confirmed that the code action worked by testing a rust-analyzer build in Emacs, and also confirmed that the generated tests looked sensible. ## AI Usage Disclosures I wrote the first draft of the rules file with Opus 4.6, manually reviewed everything. --- src/tools/rust-analyzer/AGENTS.md | 1 + src/tools/rust-analyzer/CLAUDE.md | 40 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 120000 src/tools/rust-analyzer/AGENTS.md create mode 100644 src/tools/rust-analyzer/CLAUDE.md diff --git a/src/tools/rust-analyzer/AGENTS.md b/src/tools/rust-analyzer/AGENTS.md new file mode 120000 index 0000000000000..681311eb9cf45 --- /dev/null +++ b/src/tools/rust-analyzer/AGENTS.md @@ -0,0 +1 @@ +CLAUDE.md \ No newline at end of file diff --git a/src/tools/rust-analyzer/CLAUDE.md b/src/tools/rust-analyzer/CLAUDE.md new file mode 100644 index 0000000000000..e8f699d92813a --- /dev/null +++ b/src/tools/rust-analyzer/CLAUDE.md @@ -0,0 +1,40 @@ +**Reminder: All AI usage must be disclosed in commit messages, see +CONTRIBUTING.md for more details.** + +## Build Commands + +```bash +cargo build # Build all crates +cargo test # Run all tests +cargo test -p # Run tests for a specific crate (e.g., cargo test -p hir-ty) +cargo lint # Run clippy on all targets +cargo xtask codegen # Run code generation +cargo xtask tidy # Run tidy checks +UPDATE_EXPECT=1 cargo test # Update test expectations (snapshot tests) +RUN_SLOW_TESTS=1 cargo test # Run heavy/slow tests +``` + +## Key Architectural Invariants + +- Typing in a function body never invalidates global derived data +- Parser/syntax tree is built per-file to enable parallel parsing +- The server is stateless (HTTP-like); context must be re-created from request parameters +- Cancellation uses salsa's cancellation mechanism; computations panic with a `Cancelled` payload + +### Code Generation + +Generated code is committed to the repo. Grammar and AST are generated from `ungrammar`. Run `cargo test -p xtask` after adding inline parser tests (`// test test_name` comments). + +## Testing + +Tests are snapshot-based using `expect-test`. Test fixtures use a mini-language: +- `$0` marks cursor position +- `// ^^^^` labels attach to the line above +- `//- minicore: sized, fn` includes parts of minicore (minimal core library) +- `//- /path/to/file.rs crate:name deps:dep1,dep2` declares files/crates + +## Style Notes + +- Use `stdx::never!` and `stdx::always!` instead of `assert!` for recoverable invariants +- Use `T![fn]` macro instead of `SyntaxKind::FN_KW` +- Use keyword name mangling over underscore prefixing for identifiers: `crate` → `krate`, `fn` → `func`, `struct` → `strukt`, `type` → `ty` From 2c69534c684ba34f45dbcad4960a96cda8ef0551 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 4 Mar 2026 08:27:38 +0200 Subject: [PATCH 24/55] Release a new smol-str minor version with borsh fix --- src/tools/rust-analyzer/Cargo.lock | 2 +- src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md | 3 +++ src/tools/rust-analyzer/lib/smol_str/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 9db4dd7cb1a3a..f31dda4a107bc 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2635,7 +2635,7 @@ dependencies = [ [[package]] name = "smol_str" -version = "0.3.5" +version = "0.3.6" dependencies = [ "arbitrary", "borsh", diff --git a/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md b/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md index 4aa25fa13446f..6327275d07dcb 100644 --- a/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md +++ b/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 0.3.6 - 2026-03-04 +- Fix the `borsh` feature. + ## 0.3.5 - 2026-01-08 - Optimise `SmolStr::clone` 4-5x speedup inline, 0.5x heap (slow down). diff --git a/src/tools/rust-analyzer/lib/smol_str/Cargo.toml b/src/tools/rust-analyzer/lib/smol_str/Cargo.toml index 4e7844b49e195..22068fe8418b4 100644 --- a/src/tools/rust-analyzer/lib/smol_str/Cargo.toml +++ b/src/tools/rust-analyzer/lib/smol_str/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smol_str" -version = "0.3.5" +version = "0.3.6" description = "small-string optimized string type with O(1) clone" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/smol_str" From 6b13e5ee3e0f9e73092222b794b4e6476f1d0ba7 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 4 Mar 2026 09:24:16 +0200 Subject: [PATCH 25/55] Fix a bug in associated type lowering When rewriting assoc type shorthand lowering (`TypeParam::AssocType`), I took into account that if two traits have the same assoc type, even if one is a supertrait of the other, it's an error. However it turns out that assoc type predicates (`Trait`) uses the same infrastructure, and there it's allowed to specify an ambiguous assoc type when it refers to a sub-trait. So make a distinction between ambiguous assoc type that comes from a supertrait and one that does not, and if it comes from a supertrait, keep the resolved assoc type for cases that need it. --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 105 +++++++++++------- .../crates/hir-ty/src/lower/path.rs | 39 ++++--- .../crates/hir-ty/src/tests/regression.rs | 20 ++++ 3 files changed, 110 insertions(+), 54 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 895c2e78468e6..1289f9a73a738 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -1680,10 +1680,16 @@ impl SupertraitsInfo { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -enum TypeParamAssocTypeShorthandError { - AssocTypeNotFound, - AmbiguousAssocType, +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum AssocTypeShorthandResolution { + Resolved(StoredEarlyBinder<(TypeAliasId, StoredGenericArgs)>), + Ambiguous { + /// If one resolution belongs to a sub-trait and one to a supertrait, this contains + /// the sub-trait's resolution. This can be `None` if there is no trait inheritance + /// relationship between the resolutions. + sub_trait_resolution: Option>, + }, + NotFound, Cycle, } @@ -1707,7 +1713,7 @@ fn resolve_type_param_assoc_type_shorthand( def: GenericDefId, param: TypeParamId, assoc_name: Name, -) -> Result, TypeParamAssocTypeShorthandError> { +) -> AssocTypeShorthandResolution { let generics = generics(db, def); let resolver = def.resolver(db); let mut ctx = TyLoweringContext::new( @@ -1718,13 +1724,13 @@ fn resolve_type_param_assoc_type_shorthand( LifetimeElisionKind::AnonymousReportError, ); let interner = ctx.interner; - let mut result = None; let param_ty = Ty::new_param( interner, param, generics.type_or_const_param_idx(param.into()).unwrap() as u32, ); + let mut this_trait_resolution = None; if let GenericDefId::TraitId(containing_trait) = param.parent() && param.local_id() == GenericParams::SELF_PARAM_ID_IN_SELF { @@ -1733,10 +1739,11 @@ fn resolve_type_param_assoc_type_shorthand( containing_trait.trait_items(db).associated_type_by_name(&assoc_name) { let args = GenericArgs::identity_for_item(interner, containing_trait.into()); - result = Some(StoredEarlyBinder::bind((assoc_type, args.store()))); + this_trait_resolution = Some(StoredEarlyBinder::bind((assoc_type, args.store()))); } } + let mut supertraits_resolution = None; for maybe_parent_generics in std::iter::successors(Some(&generics), |generics| generics.parent_generics()) { @@ -1782,34 +1789,53 @@ fn resolve_type_param_assoc_type_shorthand( TypeParamId::trait_self(bounded_trait), assoc_name.clone(), ); - let lookup_on_bounded_trait = match lookup_on_bounded_trait { - Ok(it) => it, - Err( - err @ (TypeParamAssocTypeShorthandError::AmbiguousAssocType - | TypeParamAssocTypeShorthandError::Cycle), - ) => return Err(*err), - Err(TypeParamAssocTypeShorthandError::AssocTypeNotFound) => { + let assoc_type_and_args = match &lookup_on_bounded_trait { + AssocTypeShorthandResolution::Resolved(trait_ref) => trait_ref, + AssocTypeShorthandResolution::Ambiguous { + sub_trait_resolution: Some(trait_ref), + } => trait_ref, + AssocTypeShorthandResolution::Ambiguous { sub_trait_resolution: None } => { + return AssocTypeShorthandResolution::Ambiguous { + sub_trait_resolution: this_trait_resolution, + }; + } + AssocTypeShorthandResolution::NotFound => { never!("we checked that the trait defines this assoc type"); continue; } + AssocTypeShorthandResolution::Cycle => return AssocTypeShorthandResolution::Cycle, }; - let (assoc_type, args) = lookup_on_bounded_trait - .get_with(|(assoc_type, args)| (*assoc_type, args.as_ref())) - .skip_binder(); - let args = EarlyBinder::bind(args).instantiate(interner, bounded_trait_ref.args); - let current_result = StoredEarlyBinder::bind((assoc_type, args.store())); - // If we already have a result, this is an ambiguity - unless this is the same result, then we are fine - // (e.g. rustc allows to write the same bound twice without ambiguity). - if let Some(existing_result) = result - && existing_result != current_result - { - return Err(TypeParamAssocTypeShorthandError::AmbiguousAssocType); + if let Some(this_trait_resolution) = this_trait_resolution { + return AssocTypeShorthandResolution::Ambiguous { + sub_trait_resolution: Some(this_trait_resolution), + }; + } else if supertraits_resolution.is_some() { + return AssocTypeShorthandResolution::Ambiguous { sub_trait_resolution: None }; + } else { + let (assoc_type, args) = assoc_type_and_args + .get_with(|(assoc_type, args)| (*assoc_type, args.as_ref())) + .skip_binder(); + let args = EarlyBinder::bind(args).instantiate(interner, bounded_trait_ref.args); + let current_result = StoredEarlyBinder::bind((assoc_type, args.store())); + supertraits_resolution = Some(match lookup_on_bounded_trait { + AssocTypeShorthandResolution::Resolved(_) => { + AssocTypeShorthandResolution::Resolved(current_result) + } + AssocTypeShorthandResolution::Ambiguous { .. } => { + AssocTypeShorthandResolution::Ambiguous { + sub_trait_resolution: Some(current_result), + } + } + AssocTypeShorthandResolution::NotFound + | AssocTypeShorthandResolution::Cycle => unreachable!(), + }); } - result = Some(current_result); } } - result.ok_or(TypeParamAssocTypeShorthandError::AssocTypeNotFound) + supertraits_resolution + .or_else(|| this_trait_resolution.map(AssocTypeShorthandResolution::Resolved)) + .unwrap_or(AssocTypeShorthandResolution::NotFound) } fn resolve_type_param_assoc_type_shorthand_cycle_result( @@ -1818,8 +1844,8 @@ fn resolve_type_param_assoc_type_shorthand_cycle_result( _def: GenericDefId, _param: TypeParamId, _assoc_name: Name, -) -> Result, TypeParamAssocTypeShorthandError> { - Err(TypeParamAssocTypeShorthandError::Cycle) +) -> AssocTypeShorthandResolution { + AssocTypeShorthandResolution::Cycle } #[inline] @@ -2568,19 +2594,22 @@ pub(crate) fn associated_ty_item_bounds<'db>( EarlyBinder::bind(BoundExistentialPredicates::new_from_slice(&bounds)) } -pub(crate) fn associated_type_by_name_including_super_traits<'db>( +pub(crate) fn associated_type_by_name_including_super_traits_allow_ambiguity<'db>( db: &'db dyn HirDatabase, trait_ref: TraitRef<'db>, name: Name, ) -> Option<(TypeAliasId, GenericArgs<'db>)> { - let assoc_type = resolve_type_param_assoc_type_shorthand( - db, - trait_ref.def_id.0.into(), - TypeParamId::trait_self(trait_ref.def_id.0), - name.clone(), - ) - .as_ref() - .ok()?; + let (AssocTypeShorthandResolution::Resolved(assoc_type) + | AssocTypeShorthandResolution::Ambiguous { sub_trait_resolution: Some(assoc_type) }) = + resolve_type_param_assoc_type_shorthand( + db, + trait_ref.def_id.0.into(), + TypeParamId::trait_self(trait_ref.def_id.0), + name.clone(), + ) + else { + return None; + }; let (assoc_type, trait_args) = assoc_type .get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref())) .skip_binder(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs index d47d696259d54..79f29d370f5cd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs @@ -30,7 +30,10 @@ use crate::{ consteval::{unknown_const, unknown_const_as_generic}, db::HirDatabase, generics::{Generics, generics}, - lower::{GenericPredicateSource, LifetimeElisionKind, PathDiagnosticCallbackData}, + lower::{ + AssocTypeShorthandResolution, GenericPredicateSource, LifetimeElisionKind, + PathDiagnosticCallbackData, + }, next_solver::{ Binder, Clause, Const, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, GenericArgs, Predicate, ProjectionPredicate, Region, TraitRef, Ty, @@ -38,8 +41,8 @@ use crate::{ }; use super::{ - ImplTraitLoweringMode, TyLoweringContext, associated_type_by_name_including_super_traits, - const_param_ty_query, ty_query, + ImplTraitLoweringMode, TyLoweringContext, + associated_type_by_name_including_super_traits_allow_ambiguity, const_param_ty_query, ty_query, }; type CallbackData<'a> = @@ -482,12 +485,14 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { let error_ty = || Ty::new_error(self.ctx.interner, ErrorGuaranteed); let (assoc_type, trait_args) = match res { Some(TypeNs::GenericParam(param)) => { - let Ok(assoc_type) = super::resolve_type_param_assoc_type_shorthand( - db, - def, - param, - assoc_name.clone(), - ) else { + let AssocTypeShorthandResolution::Resolved(assoc_type) = + super::resolve_type_param_assoc_type_shorthand( + db, + def, + param, + assoc_name.clone(), + ) + else { return error_ty(); }; assoc_type @@ -500,12 +505,14 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { }; let impl_trait = impl_trait.instantiate_identity(); // Searching for `Self::Assoc` in `impl Trait for Type` is like searching for `Self::Assoc` in `Trait`. - let Ok(assoc_type) = super::resolve_type_param_assoc_type_shorthand( - db, - impl_trait.def_id.0.into(), - TypeParamId::trait_self(impl_trait.def_id.0), - assoc_name.clone(), - ) else { + let AssocTypeShorthandResolution::Resolved(assoc_type) = + super::resolve_type_param_assoc_type_shorthand( + db, + impl_trait.def_id.0.into(), + TypeParamId::trait_self(impl_trait.def_id.0), + assoc_name.clone(), + ) + else { return error_ty(); }; let (assoc_type, trait_args) = assoc_type @@ -869,7 +876,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { let interner = self.ctx.interner; self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| { - let found = associated_type_by_name_including_super_traits( + let found = associated_type_by_name_including_super_traits_allow_ambiguity( self.ctx.db, trait_ref, binding.name.clone(), 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 658c304aac012..1939db0ef5a76 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 @@ -2795,3 +2795,23 @@ fn foo() { "#]], ); } + +#[test] +fn regression_21742() { + check_no_mismatches( + r#" +pub trait IntoIterator { + type Item; +} + +pub trait Collection: IntoIterator::Item> { + type Item; + fn contains(&self, item: &::Item); +} + +fn contains_0>(points: &S) { + points.contains(&0) +} + "#, + ); +} From 0f1a1ad813e2751805c70d62d3bf8d5c5b2465a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 4 Mar 2026 13:49:32 +0200 Subject: [PATCH 26/55] Only complain about uncanonicalized issue links --- src/tools/rust-analyzer/triagebot.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/triagebot.toml b/src/tools/rust-analyzer/triagebot.toml index ac4efd0a24bcb..5fd97f52d8c2c 100644 --- a/src/tools/rust-analyzer/triagebot.toml +++ b/src/tools/rust-analyzer/triagebot.toml @@ -24,4 +24,5 @@ labels = ["has-merge-commits", "S-waiting-on-author"] [transfer] # Canonicalize issue numbers to avoid closing the wrong issue when upstreaming this subtree -[canonicalize-issue-links] +[issue-links] +check-commits = "uncanonicalized" From 83bedb359f83d5b623331318c773ba9e788ab069 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 4 Mar 2026 17:57:39 +0200 Subject: [PATCH 27/55] Allow never coercions in struct update syntax --- .../rust-analyzer/crates/hir-ty/src/infer/expr.rs | 2 +- .../crates/hir-ty/src/tests/never_type.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 16e7d51e8734f..45b181eff8f0a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -658,7 +658,7 @@ impl<'db> InferenceContext<'_, 'db> { } } if let RecordSpread::Expr(expr) = *spread { - self.infer_expr(expr, &Expectation::has_type(ty), ExprIsRead::Yes); + self.infer_expr_coerce_never(expr, &Expectation::has_type(ty), ExprIsRead::Yes); } ty } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 1ff803ca422c6..993293bb56857 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -886,3 +886,18 @@ fn foo() { "#]], ); } + +#[test] +fn never_coercion_in_struct_update_syntax() { + check_no_mismatches( + r#" +struct Struct { + field: i32, +} + +fn example() -> Struct { + Struct { ..loop {} } +} + "#, + ); +} From bd8c9f38e5e1838c6b1f7838273939a0d716b071 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 4 Mar 2026 22:06:36 +0200 Subject: [PATCH 28/55] Do not use PostAnalysis TypingMode for IDE method resolution As explained in the comments, PostAnalysis is good for most IDE things but not method resolution. This fixes a bug which should not be impacted by this at all - return position impl trait in trait. It is currently lowered to an opaque, while it should be lowered to an anonymous associated type. But today when it is lowered as an opaque, this opaque of course has no definition and will normalize to an error, preventing method resolution on it from succeeding in some cases. --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 11 +++++- .../ide-completion/src/tests/expression.rs | 35 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9dbee16dae6b7..43cdf80e0d6ed 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -5952,7 +5952,16 @@ impl<'db> Type<'db> { ) -> R { let module = resolver.module(); let interner = DbInterner::new_with(db, module.krate(db)); - let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + // Most IDE operations want to operate in PostAnalysis mode, revealing opaques. This makes + // for a nicer IDE experience. However, method resolution is always done on real code (either + // existing code or code to be inserted), and there using PostAnalysis is dangerous - we may + // suggest invalid methods. So we're using the TypingMode of the body we're in. + let typing_mode = if let Some(body_owner) = resolver.body_owner() { + TypingMode::analysis_in_body(interner, body_owner.into()) + } else { + TypingMode::non_body_analysis() + }; + let infcx = interner.infer_ctxt().build(typing_mode); let unstable_features = MethodResolutionUnstableFeatures::from_def_map(resolver.top_level_def_map()); let environment = param_env_from_resolver(db, resolver); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 5fef8c44deb12..8e50ef10eca65 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -3659,3 +3659,38 @@ fn main() { "#]], ); } + +#[test] +fn rpitit_with_reference() { + check( + r#" +trait Foo { + fn foo(&self); +} + +trait Bar { + fn bar(&self) -> &impl Foo; +} + +fn baz(v: impl Bar) { + v.bar().$0 +} + "#, + expect![[r#" + me foo() (as Foo) fn(&self) + sn box Box::new(expr) + sn call function(expr) + sn const const {} + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn deref *expr + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + sn return return expr + sn unsafe unsafe {} + "#]], + ); +} From 8bfd9819f89fd55e36b32f5672ad58bd7fa4c455 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 5 Mar 2026 01:25:35 +0200 Subject: [PATCH 29/55] When going to def on `?` on `Result` that goes through `From`, go to the `From` impl It's more useful to users than going to `Try::branch()` impl from `Result`, which is in std. --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 7 ++ .../rust-analyzer/crates/hir/src/semantics.rs | 22 ++++++ .../crates/ide/src/goto_definition.rs | 69 ++++++++++++++++++- 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9dbee16dae6b7..569177793cba6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -6149,6 +6149,13 @@ impl<'db> Type<'db> { Some(adt.into()) } + /// Holes in the args can come from lifetime/const params. + pub fn as_adt_with_args(&self) -> Option<(Adt, Vec>>)> { + let (adt, args) = self.ty.as_adt()?; + let args = args.iter().map(|arg| Some(self.derived(arg.ty()?))).collect(); + Some((adt.into(), args)) + } + pub fn as_builtin(&self) -> Option { self.ty.as_builtin().map(|inner| BuiltinType { inner }) } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 4bc757da44174..1cf3b98160820 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1819,6 +1819,28 @@ impl<'db> SemanticsImpl<'db> { self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) } + /// The type that the associated `try` block, closure or function expects. + pub fn try_expr_returned_type(&self, try_expr: &ast::TryExpr) -> Option> { + self.ancestors_with_macros(try_expr.syntax().clone()).find_map(|parent| { + if let Some(try_block) = ast::BlockExpr::cast(parent.clone()) + && try_block.try_block_modifier().is_some() + { + Some(self.type_of_expr(&try_block.into())?.original) + } else if let Some(closure) = ast::ClosureExpr::cast(parent.clone()) { + Some( + self.type_of_expr(&closure.into())? + .original + .as_callable(self.db)? + .return_type(), + ) + } else if let Some(function) = ast::Fn::cast(parent) { + Some(self.to_def(&function)?.ret_type(self.db)) + } else { + None + } + }) + } + // This does not resolve the method call to the correct trait impl! // We should probably fix that. pub fn resolve_method_call_as_callable( diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 3c3ac9d3bbe61..3890bcad7fc61 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -95,6 +95,13 @@ pub(crate) fn goto_definition( continue; } + let parent = token.value.parent()?; + + if let Some(question_mark_conversion) = goto_question_mark_conversions(sema, &parent) { + navs.extend(def_to_nav(sema, question_mark_conversion.into())); + continue; + } + if let Some(token) = ast::String::cast(token.value.clone()) && let Some(original_token) = ast::String::cast(original_token.clone()) && let Some((analysis, fixture_analysis)) = @@ -113,8 +120,6 @@ pub(crate) fn goto_definition( }); } - let parent = token.value.parent()?; - let token_file_id = token.file_id; if let Some(token) = ast::String::cast(token.value.clone()) && let Some(x) = @@ -149,6 +154,45 @@ pub(crate) fn goto_definition( Some(RangeInfo::new(original_token.text_range(), navs)) } +/// When the `?` operator is used on `Result`, go to the `From` impl if it exists as this provides more value. +fn goto_question_mark_conversions( + sema: &Semantics<'_, RootDatabase>, + node: &SyntaxNode, +) -> Option { + let node = ast::TryExpr::cast(node.clone())?; + let try_expr_ty = sema.type_of_expr(&node.expr()?)?.adjusted(); + + let fd = FamousDefs(sema, try_expr_ty.krate(sema.db)); + let result_enum = fd.core_result_Result()?.into(); + + let (try_expr_ty_adt, try_expr_ty_args) = try_expr_ty.as_adt_with_args()?; + if try_expr_ty_adt != result_enum { + // FIXME: Support `Poll`. + return None; + } + let original_err_ty = try_expr_ty_args.get(1)?.clone()?; + + let returned_ty = sema.try_expr_returned_type(&node)?; + let (returned_adt, returned_ty_args) = returned_ty.as_adt_with_args()?; + if returned_adt != result_enum { + return None; + } + let returned_err_ty = returned_ty_args.get(1)?.clone()?; + + if returned_err_ty.could_unify_with_deeply(sema.db, &original_err_ty) { + return None; + } + + let from_trait = fd.core_convert_From()?; + let from_fn = from_trait.function(sema.db, sym::from)?; + sema.resolve_trait_impl_method( + returned_err_ty.clone(), + from_trait, + from_fn, + [returned_err_ty, original_err_ty], + ) +} + // If the token is into(), try_into(), search the definition of From, TryFrom. fn find_definition_for_known_blanket_dual_impls( sema: &Semantics<'_, RootDatabase>, @@ -4034,4 +4078,25 @@ where "#, ) } + + #[test] + fn question_mark_on_result_goes_to_conversion() { + check( + r#" +//- minicore: try, result, from + +struct Foo; +struct Bar; +impl From for Bar { + fn from(_: Foo) -> Bar { Bar } + // ^^^^ +} + +fn foo() -> Result<(), Bar> { + Err(Foo)?$0; + Ok(()) +} + "#, + ); + } } From e502782e4330730f112ca8180eb58bab786bf6c0 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 5 Mar 2026 04:36:54 +0000 Subject: [PATCH 30/55] Prepare for merging from rust-lang/rust This updates the rust-version file to f8704be04fe1150527fc2cf21dd44327f0fe87fb. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 4dedd81b00666..7c89bcb9ab045 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -e7d90c695a39426baf5ae705de2f9570a72229de +f8704be04fe1150527fc2cf21dd44327f0fe87fb From 4343bc75af6ba0209492153d63d2517be9c361e9 Mon Sep 17 00:00:00 2001 From: oeb25 Date: Thu, 5 Mar 2026 09:45:54 +0100 Subject: [PATCH 31/55] Add `has_pending` methods to `Incoming` and `Outgoing` in `lsp_server` Adds methods for checking if there's pending requests/responses in the incoming/outgoing channels of `ReqQueue` in `lsp_server`. --- .../rust-analyzer/lib/lsp-server/src/req_queue.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs b/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs index c216864bee8c2..84748bbca8d77 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs @@ -18,6 +18,12 @@ impl Default for ReqQueue { } } +impl ReqQueue { + pub fn has_pending(&self) -> bool { + self.incoming.has_pending() || self.outgoing.has_pending() + } +} + #[derive(Debug)] pub struct Incoming { pending: HashMap, @@ -51,6 +57,10 @@ impl Incoming { pub fn is_completed(&self, id: &RequestId) -> bool { !self.pending.contains_key(id) } + + pub fn has_pending(&self) -> bool { + !self.pending.is_empty() + } } impl Outgoing { @@ -64,4 +74,8 @@ impl Outgoing { pub fn complete(&mut self, id: RequestId) -> Option { self.pending.remove(&id) } + + pub fn has_pending(&self) -> bool { + !self.pending.is_empty() + } } From 96f2261db1cc7367962a0c82599fe26edd82b414 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 5 Mar 2026 11:46:49 +0100 Subject: [PATCH 32/55] Remove unused `LintContext::lint` method --- compiler/rustc_lint/src/context.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 5cd5c95f1ec14..d47f6ec1d7e0e 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -565,13 +565,6 @@ pub trait LintContext { self.opt_span_diag_lint(lint, None as Option, decorator); } - /// Emit a lint at the appropriate level, with no associated span. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - fn lint(&self, lint: &'static Lint, decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)) { - self.opt_span_lint(lint, None as Option, decorate); - } - /// This returns the lint level for the given lint at the current location. fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource; From 25c779800409ee5066e0a7cfb6a112a2f737b154 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 5 Mar 2026 12:11:56 +0100 Subject: [PATCH 33/55] Create new `Diagnostic` type for panic calls without literals --- compiler/rustc_lint/src/non_fmt_panic.rs | 105 +++++++++++------- .../const_panic_stability.e2018.stderr | 2 +- .../imports/ambiguous-panic-pick-core.stderr | 2 +- .../imports/ambiguous-panic-pick-std.stderr | 2 +- tests/ui/macros/non-fmt-panic.stderr | 52 ++++----- 5 files changed, 92 insertions(+), 71 deletions(-) diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index bb04da96140a6..7f137d5bf0603 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -1,5 +1,6 @@ use rustc_ast as ast; -use rustc_errors::{Applicability, msg}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level, msg}; +use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::{bug, ty}; @@ -85,49 +86,26 @@ impl<'tcx> LateLintPass<'tcx> for NonPanicFmt { } } -fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Lit(lit) = &arg.kind { - if let ast::LitKind::Str(sym, _) = lit.node { - // The argument is a string literal. - check_panic_str(cx, f, arg, sym.as_str()); - return; - } - } - - // The argument is *not* a string literal. - - let (span, panic, symbol) = panic_call(cx, f); - - if span.in_external_macro(cx.sess().source_map()) { - // Nothing that can be done about it in the current crate. - return; - } - - // Find the span of the argument to `panic!()` or `unreachable!`, before expansion in the - // case of `panic!(some_macro!())` or `unreachable!(some_macro!())`. - // We don't use source_callsite(), because this `panic!(..)` might itself - // be expanded from another macro, in which case we want to stop at that - // expansion. - let mut arg_span = arg.span; - let mut arg_macro = None; - while !span.contains(arg_span) { - let ctxt = arg_span.ctxt(); - if ctxt.is_root() { - break; - } - let expn = ctxt.outer_expn_data(); - arg_macro = expn.macro_def_id; - arg_span = expn.call_site; - } +struct PanicMessageNotLiteral<'a, 'tcx> { + arg_span: Span, + symbol: Symbol, + span: Span, + arg_macro: Option, + cx: &'a LateContext<'tcx>, + arg: &'tcx hir::Expr<'tcx>, + panic: Option, +} - cx.span_lint(NON_FMT_PANICS, arg_span, |lint| { - lint.primary_message(msg!("panic message is not a string literal")); - lint.arg("name", symbol); - lint.note(msg!("this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021")); - lint.note(msg!("for more information, see ")); +impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for PanicMessageNotLiteral<'b, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { arg_span, symbol, span, arg_macro, cx, arg, panic } = self; + let mut lint = Diag::new(dcx, level, "panic message is not a string literal") + .with_arg("name", symbol) + .with_note(msg!("this usage of `{$name}!()` is deprecated; it will be a hard error in Rust 2021")) + .with_note("for more information, see "); if !is_arg_inside_call(arg_span, span) { // No clue where this argument is coming from. - return; + return lint; } if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { // A case of `panic!(format!(..))`. @@ -215,7 +193,50 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc } } } - }); + lint + } +} + +fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) { + if let hir::ExprKind::Lit(lit) = &arg.kind { + if let ast::LitKind::Str(sym, _) = lit.node { + // The argument is a string literal. + check_panic_str(cx, f, arg, sym.as_str()); + return; + } + } + + // The argument is *not* a string literal. + + let (span, panic, symbol) = panic_call(cx, f); + + if span.in_external_macro(cx.sess().source_map()) { + // Nothing that can be done about it in the current crate. + return; + } + + // Find the span of the argument to `panic!()` or `unreachable!`, before expansion in the + // case of `panic!(some_macro!())` or `unreachable!(some_macro!())`. + // We don't use source_callsite(), because this `panic!(..)` might itself + // be expanded from another macro, in which case we want to stop at that + // expansion. + let mut arg_span = arg.span; + let mut arg_macro = None; + while !span.contains(arg_span) { + let ctxt = arg_span.ctxt(); + if ctxt.is_root() { + break; + } + let expn = ctxt.outer_expn_data(); + arg_macro = expn.macro_def_id; + arg_span = expn.call_site; + } + + cx.emit_span_lint( + NON_FMT_PANICS, + arg_span, + PanicMessageNotLiteral { arg_span, symbol, span, arg_macro, cx, arg, panic }, + ); } fn check_panic_str<'tcx>( diff --git a/tests/ui/consts/const-eval/const_panic_stability.e2018.stderr b/tests/ui/consts/const-eval/const_panic_stability.e2018.stderr index c33d5b003042e..e8fdfc3d6136e 100644 --- a/tests/ui/consts/const-eval/const_panic_stability.e2018.stderr +++ b/tests/ui/consts/const-eval/const_panic_stability.e2018.stderr @@ -4,9 +4,9 @@ warning: panic message is not a string literal LL | panic!({ "foo" }); | ^^^^^^^^^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see = note: `#[warn(non_fmt_panics)]` (part of `#[warn(rust_2021_compatibility)]`) on by default help: add a "{}" format string to `Display` the message | diff --git a/tests/ui/imports/ambiguous-panic-pick-core.stderr b/tests/ui/imports/ambiguous-panic-pick-core.stderr index 0a43c83934acc..5c4c881f6bc73 100644 --- a/tests/ui/imports/ambiguous-panic-pick-core.stderr +++ b/tests/ui/imports/ambiguous-panic-pick-core.stderr @@ -24,9 +24,9 @@ warning: panic message is not a string literal LL | panic!(&std::string::String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see = note: `#[warn(non_fmt_panics)]` (part of `#[warn(rust_2021_compatibility)]`) on by default help: add a "{}" format string to `Display` the message | diff --git a/tests/ui/imports/ambiguous-panic-pick-std.stderr b/tests/ui/imports/ambiguous-panic-pick-std.stderr index 53e51afe051c2..8938450d1c579 100644 --- a/tests/ui/imports/ambiguous-panic-pick-std.stderr +++ b/tests/ui/imports/ambiguous-panic-pick-std.stderr @@ -24,9 +24,9 @@ warning: panic message is not a string literal LL | panic!(std::string::String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see = note: `#[warn(non_fmt_panics)]` (part of `#[warn(rust_2021_compatibility)]`) on by default help: add a "{}" format string to `Display` the message | diff --git a/tests/ui/macros/non-fmt-panic.stderr b/tests/ui/macros/non-fmt-panic.stderr index 9cadaf3fbe388..959fcf6bfcf61 100644 --- a/tests/ui/macros/non-fmt-panic.stderr +++ b/tests/ui/macros/non-fmt-panic.stderr @@ -78,9 +78,9 @@ warning: panic message is not a string literal LL | assert!(false, S); | ^ | - = note: for more information, see = note: this usage of `assert!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | assert!(false, "{}", S); @@ -92,9 +92,9 @@ warning: panic message is not a string literal LL | assert!(false, 123); | ^^^ | - = note: for more information, see = note: this usage of `assert!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | assert!(false, "{}", 123); @@ -106,9 +106,9 @@ warning: panic message is not a string literal LL | assert!(false, Some(123)); | ^^^^^^^^^ | - = note: for more information, see = note: this usage of `assert!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{:?}" format string to use the `Debug` implementation of `Option` | LL | assert!(false, "{:?}", Some(123)); @@ -133,9 +133,9 @@ warning: panic message is not a string literal LL | panic!(C); | ^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | panic!("{}", C); @@ -147,9 +147,9 @@ warning: panic message is not a string literal LL | panic!(S); | ^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | panic!("{}", S); @@ -161,9 +161,9 @@ warning: panic message is not a string literal LL | unreachable!(S); | ^ | - = note: for more information, see = note: this usage of `unreachable!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | unreachable!("{}", S); @@ -175,9 +175,9 @@ warning: panic message is not a string literal LL | unreachable!(S); | ^ | - = note: for more information, see = note: this usage of `unreachable!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | unreachable!("{}", S); @@ -189,9 +189,9 @@ warning: panic message is not a string literal LL | std::panic!(123); | ^^^ | - = note: for more information, see = note: this usage of `std::panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | std::panic!("{}", 123); @@ -208,9 +208,9 @@ warning: panic message is not a string literal LL | core::panic!(&*"abc"); | ^^^^^^^ | - = note: for more information, see = note: this usage of `core::panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | core::panic!("{}", &*"abc"); @@ -222,9 +222,9 @@ warning: panic message is not a string literal LL | panic!(Some(123)); | ^^^^^^^^^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{:?}" format string to use the `Debug` implementation of `Option` | LL | panic!("{:?}", Some(123)); @@ -280,9 +280,9 @@ warning: panic message is not a string literal LL | panic!(a!()); | ^^^^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | panic!("{}", a!()); @@ -299,9 +299,9 @@ warning: panic message is not a string literal LL | unreachable!(a!()); | ^^^^ | - = note: for more information, see = note: this usage of `unreachable!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | unreachable!("{}", a!()); @@ -313,10 +313,10 @@ warning: panic message is not a string literal LL | panic!(format!("{}", 1)); | ^^^^^^^^^^^^^^^^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see = note: the `panic!()` macro supports formatting, so there's no need for the `format!()` macro here + = note: for more information, see help: remove the `format!(..)` macro call | LL - panic!(format!("{}", 1)); @@ -329,10 +329,10 @@ warning: panic message is not a string literal LL | unreachable!(format!("{}", 1)); | ^^^^^^^^^^^^^^^^ | - = note: for more information, see = note: this usage of `unreachable!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see = note: the `unreachable!()` macro supports formatting, so there's no need for the `format!()` macro here + = note: for more information, see help: remove the `format!(..)` macro call | LL - unreachable!(format!("{}", 1)); @@ -345,10 +345,10 @@ warning: panic message is not a string literal LL | assert!(false, format!("{}", 1)); | ^^^^^^^^^^^^^^^^ | - = note: for more information, see = note: this usage of `assert!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see = note: the `assert!()` macro supports formatting, so there's no need for the `format!()` macro here + = note: for more information, see help: remove the `format!(..)` macro call | LL - assert!(false, format!("{}", 1)); @@ -361,10 +361,10 @@ warning: panic message is not a string literal LL | debug_assert!(false, format!("{}", 1)); | ^^^^^^^^^^^^^^^^ | - = note: for more information, see = note: this usage of `debug_assert!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see = note: the `debug_assert!()` macro supports formatting, so there's no need for the `format!()` macro here + = note: for more information, see help: remove the `format!(..)` macro call | LL - debug_assert!(false, format!("{}", 1)); @@ -377,9 +377,9 @@ warning: panic message is not a string literal LL | panic![123]; | ^^^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | panic!["{}", 123]; @@ -396,9 +396,9 @@ warning: panic message is not a string literal LL | panic!{123}; | ^^^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | panic!{"{}", 123}; @@ -415,9 +415,9 @@ warning: panic message is not a string literal LL | panic!(v); | ^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: use std::panic::panic_any instead | LL - panic!(v); @@ -430,9 +430,9 @@ warning: panic message is not a string literal LL | assert!(false, v); | ^ | - = note: for more information, see = note: this usage of `assert!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see warning: panic message is not a string literal --> $DIR/non-fmt-panic.rs:84:12 @@ -440,9 +440,9 @@ warning: panic message is not a string literal LL | panic!(v); | ^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{:?}" format string to use the `Debug` implementation of `T` | LL | panic!("{:?}", v); @@ -459,9 +459,9 @@ warning: panic message is not a string literal LL | assert!(false, v); | ^ | - = note: for more information, see = note: this usage of `assert!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{:?}" format string to use the `Debug` implementation of `T` | LL | assert!(false, "{:?}", v); @@ -473,9 +473,9 @@ warning: panic message is not a string literal LL | panic!(v); | ^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | panic!("{}", v); @@ -492,9 +492,9 @@ warning: panic message is not a string literal LL | assert!(false, v); | ^ | - = note: for more information, see = note: this usage of `assert!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | assert!(false, "{}", v); @@ -506,9 +506,9 @@ warning: panic message is not a string literal LL | panic!(v); | ^ | - = note: for more information, see = note: this usage of `panic!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | panic!("{}", v); @@ -525,9 +525,9 @@ warning: panic message is not a string literal LL | assert!(false, v); | ^ | - = note: for more information, see = note: this usage of `assert!()` is deprecated; it will be a hard error in Rust 2021 = note: for more information, see + = note: for more information, see help: add a "{}" format string to `Display` the message | LL | assert!(false, "{}", v); From 07a3da167852a074e1934f198ab44f7629e19207 Mon Sep 17 00:00:00 2001 From: Julien Cretin Date: Thu, 5 Mar 2026 15:49:14 +0100 Subject: [PATCH 34/55] Consider try blocks as block-like for overflowed expr --- src/tools/rustfmt/src/expr.rs | 5 ++++- src/tools/rustfmt/tests/source/try_block.rs | 8 ++++++++ .../rustfmt/tests/source/try_blocks_heterogeneous.rs | 10 ++++++++++ src/tools/rustfmt/tests/target/try_block.rs | 5 +++++ .../rustfmt/tests/target/try_blocks_heterogeneous.rs | 11 +++++++++++ 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 51b73351fe6a5..d34706a2ba5cd 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -1553,7 +1553,10 @@ pub(crate) fn can_be_overflowed_expr( } // Handle always block-like expressions - ast::ExprKind::Gen(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true, + ast::ExprKind::Gen(..) + | ast::ExprKind::Block(..) + | ast::ExprKind::Closure(..) + | ast::ExprKind::TryBlock(..) => true, // Handle `[]` and `{}`-like expressions ast::ExprKind::Array(..) | ast::ExprKind::Struct(..) => { diff --git a/src/tools/rustfmt/tests/source/try_block.rs b/src/tools/rustfmt/tests/source/try_block.rs index e324a13317584..62f0492f96620 100644 --- a/src/tools/rustfmt/tests/source/try_block.rs +++ b/src/tools/rustfmt/tests/source/try_block.rs @@ -27,5 +27,13 @@ fn baz() -> Option { let x: Option = try { baz()?; baz()?; baz()?; 7 }; + let _ = overflowed_expr( + x, + try { + foo()?; + bar()?; + }, + ); + return None; } diff --git a/src/tools/rustfmt/tests/source/try_blocks_heterogeneous.rs b/src/tools/rustfmt/tests/source/try_blocks_heterogeneous.rs index 7a1135cfbc764..056bedafef6cb 100644 --- a/src/tools/rustfmt/tests/source/try_blocks_heterogeneous.rs +++ b/src/tools/rustfmt/tests/source/try_blocks_heterogeneous.rs @@ -35,5 +35,15 @@ fn baz() -> Option { let x = try bikeshed Foo {}; + let x = try bikeshed Result {}; + + let _ = overflowed_expr( + x, + try bikeshed Option<_> { + foo()?; + bar()?; + }, + ); + return None; } diff --git a/src/tools/rustfmt/tests/target/try_block.rs b/src/tools/rustfmt/tests/target/try_block.rs index 61da123b735d8..f9d9909f524fe 100644 --- a/src/tools/rustfmt/tests/target/try_block.rs +++ b/src/tools/rustfmt/tests/target/try_block.rs @@ -26,5 +26,10 @@ fn baz() -> Option { 7 }; + let _ = overflowed_expr(x, try { + foo()?; + bar()?; + }); + return None; } diff --git a/src/tools/rustfmt/tests/target/try_blocks_heterogeneous.rs b/src/tools/rustfmt/tests/target/try_blocks_heterogeneous.rs index 018d53ed35e41..ce0a3cf704bbf 100644 --- a/src/tools/rustfmt/tests/target/try_blocks_heterogeneous.rs +++ b/src/tools/rustfmt/tests/target/try_blocks_heterogeneous.rs @@ -37,5 +37,16 @@ fn baz() -> Option { let x = try bikeshed Foo {}; + let x = try bikeshed Result< + VeryVeryVeryVeryVeryLongTypeForSuccess, + VeryVeryVeryVeryVeryLongTypeForFailure, + > { + }; + + let _ = overflowed_expr(x, try bikeshed Option<_> { + foo()?; + bar()?; + }); + return None; } From 2093158de7fe26e2f30dfaed1034c3fa79779db0 Mon Sep 17 00:00:00 2001 From: JayanAXHF Date: Thu, 5 Mar 2026 18:46:00 +0000 Subject: [PATCH 35/55] refactor: move `check_align` to `parse_alignment` * refactor: move `check_align` from check_attrs.rs to `parse_alignment` rel: attribute parser rework * fix: fix error messages * test: fix test * fix: fix invalid error message * ops(test): Fix CI --- .../rustc_attr_parsing/src/attributes/repr.rs | 50 +++++++++----- .../src/session_diagnostics.rs | 6 +- compiler/rustc_passes/src/check_attr.rs | 69 +++++++------------ compiler/rustc_passes/src/errors.rs | 9 --- .../repr_align_greater_usize.msp430.stderr | 16 ++--- tests/ui/repr/repr_align_greater_usize.rs | 4 +- 6 files changed, 65 insertions(+), 89 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index f0fc2a5b5e939..f8ccc8594e333 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -1,4 +1,4 @@ -use rustc_abi::Align; +use rustc_abi::{Align, Size}; use rustc_ast::{IntTy, LitIntType, LitKind, UintTy}; use rustc_hir::attrs::{IntType, ReprAttr}; @@ -229,7 +229,7 @@ fn parse_repr_align( return None; }; - match parse_alignment(&lit.kind) { + match parse_alignment(&lit.kind, cx) { Ok(literal) => Some(match align_kind { AlignKind::Packed => ReprAttr::ReprPacked(literal), AlignKind::Align => ReprAttr::ReprAlign(literal), @@ -248,23 +248,35 @@ fn parse_repr_align( } } -fn parse_alignment(node: &LitKind) -> Result { - if let LitKind::Int(literal, LitIntType::Unsuffixed) = node { - // `Align::from_bytes` accepts 0 as an input, check is_power_of_two() first - if literal.get().is_power_of_two() { - // Only possible error is larger than 2^29 - literal - .get() - .try_into() - .ok() - .and_then(|v| Align::from_bytes(v).ok()) - .ok_or("larger than 2^29") - } else { - Err("not a power of two") - } - } else { - Err("not an unsuffixed integer") +fn parse_alignment( + node: &LitKind, + cx: &AcceptContext<'_, '_, S>, +) -> Result { + let LitKind::Int(literal, LitIntType::Unsuffixed) = node else { + return Err("not an unsuffixed integer".to_string()); + }; + + // `Align::from_bytes` accepts 0 as a valid input, + // so we check if its a power of two first + if !literal.get().is_power_of_two() { + return Err("not a power of two".to_string()); + } + // lit must be < 2^29 + let align = literal + .get() + .try_into() + .ok() + .and_then(|a| Align::from_bytes(a).ok()) + .ok_or("larger than 2^29".to_string())?; + + // alignment must not be larger than the pointer width (`isize::MAX`) + let max = Size::from_bits(cx.sess.target.pointer_width).signed_int_max() as u64; + if align.bytes() > max { + return Err(format!( + "alignment larger than `isize::MAX` bytes ({max} for the current target)" + )); } + Ok(align) } /// Parse #[align(N)]. @@ -294,7 +306,7 @@ impl RustcAlignParser { return; }; - match parse_alignment(&lit.kind) { + match parse_alignment(&lit.kind, cx) { Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))), Err(message) => { cx.emit_err(session_diagnostics::InvalidAlignmentValue { diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 987c0cb04c84d..7c2044ec235a7 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -227,12 +227,12 @@ pub(crate) struct InvalidReprAlignNeedArg { #[derive(Diagnostic)] #[diag("invalid `repr({$repr_arg})` attribute: {$error_part}", code = E0589)] -pub(crate) struct InvalidReprGeneric<'a> { +pub(crate) struct InvalidReprGeneric { #[primary_span] pub span: Span, pub repr_arg: String, - pub error_part: &'a str, + pub error_part: String, } #[derive(Diagnostic)] @@ -479,7 +479,7 @@ pub(crate) struct InvalidTarget { pub(crate) struct InvalidAlignmentValue { #[primary_span] pub span: Span, - pub error_part: &'static str, + pub error_part: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index dacb02afe1612..648163bd1d148 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -9,7 +9,7 @@ use std::cell::Cell; use std::collections::hash_map::Entry; use std::slice; -use rustc_abi::{Align, ExternAbi, Size}; +use rustc_abi::ExternAbi; use rustc_ast::{AttrStyle, MetaItemKind, ast}; use rustc_attr_parsing::{AttributeParser, Late}; use rustc_data_structures::fx::FxHashMap; @@ -190,8 +190,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { &Attribute::Parsed(AttributeKind::RustcPubTransparent(attr_span)) => { self.check_rustc_pub_transparent(attr_span, span, attrs) } - Attribute::Parsed(AttributeKind::RustcAlign { align, span: attr_span }) => { - self.check_align(*align, *attr_span) + Attribute::Parsed(AttributeKind::RustcAlign {..}) => { + } Attribute::Parsed(AttributeKind::Naked(..)) => { self.check_naked(hir_id, target) @@ -1335,31 +1335,27 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } } - ReprAttr::ReprAlign(align) => { - match target { - Target::Struct | Target::Union | Target::Enum => {} - Target::Fn | Target::Method(_) if self.tcx.features().fn_align() => { - self.dcx().emit_err(errors::ReprAlignShouldBeAlign { - span: *repr_span, - item: target.plural_name(), - }); - } - Target::Static if self.tcx.features().static_align() => { - self.dcx().emit_err(errors::ReprAlignShouldBeAlignStatic { - span: *repr_span, - item: target.plural_name(), - }); - } - _ => { - self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { - hint_span: *repr_span, - span, - }); - } + ReprAttr::ReprAlign(..) => match target { + Target::Struct | Target::Union | Target::Enum => {} + Target::Fn | Target::Method(_) if self.tcx.features().fn_align() => { + self.dcx().emit_err(errors::ReprAlignShouldBeAlign { + span: *repr_span, + item: target.plural_name(), + }); } - - self.check_align(*align, *repr_span); - } + Target::Static if self.tcx.features().static_align() => { + self.dcx().emit_err(errors::ReprAlignShouldBeAlignStatic { + span: *repr_span, + item: target.plural_name(), + }); + } + _ => { + self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { + hint_span: *repr_span, + span, + }); + } + }, ReprAttr::ReprPacked(_) => { if target != Target::Struct && target != Target::Union { self.dcx().emit_err(errors::AttrApplication::StructUnion { @@ -1475,25 +1471,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - fn check_align(&self, align: Align, span: Span) { - if align.bytes() > 2_u64.pow(29) { - // for values greater than 2^29, a different error will be emitted, make sure that happens - self.dcx().span_delayed_bug( - span, - "alignment greater than 2^29 should be errored on elsewhere", - ); - } else { - // only do this check when <= 2^29 to prevent duplicate errors: - // alignment greater than 2^29 not supported - // alignment is too large for the current target - - let max = Size::from_bits(self.tcx.sess.target.pointer_width).signed_int_max() as u64; - if align.bytes() > max { - self.dcx().emit_err(errors::InvalidReprAlignForTarget { span, size: max }); - } - } - } - /// Outputs an error for attributes that can only be applied to macros, such as /// `#[allow_internal_unsafe]` and `#[allow_internal_unstable]`. /// (Allows proc_macro functions) diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 0cf0d1a5c80ff..b0783d3fe1cca 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -236,15 +236,6 @@ pub(crate) struct ReprConflicting { pub hint_spans: Vec, } -#[derive(Diagnostic)] -#[diag("alignment must not be greater than `isize::MAX` bytes", code = E0589)] -#[note("`isize::MAX` is {$size} for the current target")] -pub(crate) struct InvalidReprAlignForTarget { - #[primary_span] - pub span: Span, - pub size: u64, -} - #[derive(Diagnostic)] #[diag("conflicting representation hints", code = E0566)] pub(crate) struct ReprConflictingLint; diff --git a/tests/ui/repr/repr_align_greater_usize.msp430.stderr b/tests/ui/repr/repr_align_greater_usize.msp430.stderr index a7b06acb6752b..4fcd33d50d749 100644 --- a/tests/ui/repr/repr_align_greater_usize.msp430.stderr +++ b/tests/ui/repr/repr_align_greater_usize.msp430.stderr @@ -1,18 +1,14 @@ -error[E0589]: alignment must not be greater than `isize::MAX` bytes - --> $DIR/repr_align_greater_usize.rs:23:8 +error[E0589]: invalid `repr(align)` attribute: alignment larger than `isize::MAX` bytes (32767 for the current target) + --> $DIR/repr_align_greater_usize.rs:23:14 | LL | #[repr(align(32768))] - | ^^^^^^^^^^^^ - | - = note: `isize::MAX` is 32767 for the current target + | ^^^^^ -error[E0589]: alignment must not be greater than `isize::MAX` bytes - --> $DIR/repr_align_greater_usize.rs:26:8 +error[E0589]: invalid `repr(align)` attribute: alignment larger than `isize::MAX` bytes (32767 for the current target) + --> $DIR/repr_align_greater_usize.rs:26:14 | LL | #[repr(align(65536))] - | ^^^^^^^^^^^^ - | - = note: `isize::MAX` is 32767 for the current target + | ^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/repr/repr_align_greater_usize.rs b/tests/ui/repr/repr_align_greater_usize.rs index 52a4d23b1eca2..7df1fbe762661 100644 --- a/tests/ui/repr/repr_align_greater_usize.rs +++ b/tests/ui/repr/repr_align_greater_usize.rs @@ -20,8 +20,8 @@ use minicore::*; #[repr(align(16384))] struct Kitten; -#[repr(align(32768))] //[msp430]~ ERROR alignment must not be greater than `isize::MAX` +#[repr(align(32768))] //[msp430]~ ERROR invalid `repr(align)` attribute: alignment larger than `isize::MAX` bytes (32767 for the current target) [E0589] struct Cat; -#[repr(align(65536))] //[msp430]~ ERROR alignment must not be greater than `isize::MAX` +#[repr(align(65536))] //[msp430]~ ERROR invalid `repr(align)` attribute: alignment larger than `isize::MAX` bytes (32767 for the current target) [E0589] struct BigCat; From f8998e9ad97bdda94aba7613b47feea1bac94839 Mon Sep 17 00:00:00 2001 From: Ada Bohm Date: Thu, 5 Mar 2026 20:10:44 +0100 Subject: [PATCH 36/55] as_slice removed --- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 7df08708ef421..38ec744937f58 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1920,7 +1920,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { } // Otherwise, print the array separated by commas (or if it's a tuple) (ty::ValTreeKind::Branch(fields), ty::Array(..) | ty::Tuple(..)) => { - let fields_iter = fields.as_slice().iter().copied(); + let fields_iter = fields.iter(); match *cv.ty.kind() { ty::Array(..) => { From 9dc456a3662f7885775cab9d2b7b35c2a08760cc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 5 Mar 2026 19:55:51 +0100 Subject: [PATCH 37/55] Remove usage of `LintContext::span_lint` in clippy --- compiler/rustc_errors/src/diagnostic.rs | 1 + compiler/rustc_lint/src/context.rs | 1 + src/tools/clippy/clippy_utils/src/diagnostics.rs | 16 +++++++++++++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index cfc697b521fc5..f7fdbfa0cd57e 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -106,6 +106,7 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError { pub trait Diagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> { /// Write out as a diagnostic out of `DiagCtxt`. #[must_use] + #[track_caller] fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G>; } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d47f6ec1d7e0e..ad764bf76799f 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -537,6 +537,7 @@ pub trait LintContext { /// Emit a lint at `span` from a lint struct (some type that implements `Diagnostic`, /// typically generated by `#[derive(Diagnostic)]`). + #[track_caller] fn emit_span_lint>( &self, lint: &'static Lint, diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 81b06ea0c539b..65c962826900d 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -8,7 +8,7 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{Applicability, Diag, DiagMessage, MultiSpan}; +use rustc_errors::{Applicability, Diag, Diagnostic, DiagCtxtHandle, DiagMessage, Level, MultiSpan}; #[cfg(debug_assertions)] use rustc_errors::{EmissionGuarantee, SubstitutionPart, Suggestions}; use rustc_hir::HirId; @@ -240,15 +240,25 @@ where M: Into, F: FnOnce(&mut Diag<'_, ()>), { + struct ClippyDiag)>(F); + + impl<'a, F: FnOnce(&mut Diag<'_, ()>)> Diagnostic<'a, ()> for ClippyDiag { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let mut lint = Diag::new(dcx, level, ""); + (self.0)(&mut lint); + lint + } + } + #[expect(clippy::disallowed_methods)] - cx.span_lint(lint, sp, |diag| { + cx.emit_span_lint(lint, sp, ClippyDiag(|diag: &mut Diag<'_, ()>| { diag.primary_message(msg); f(diag); docs_link(diag, lint); #[cfg(debug_assertions)] validate_diag(diag); - }); + })); } /// Like [`span_lint`], but emits the lint at the node identified by the given `HirId`. From 1f62cd294290b537d5d0914c248d3a720c92da1a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 5 Mar 2026 12:19:05 +0100 Subject: [PATCH 38/55] Remove unused `LintContext::span_lint` method --- compiler/rustc_lint/src/context.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index ad764bf76799f..d51fa7f276fc5 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -547,19 +547,6 @@ pub trait LintContext { self.opt_span_diag_lint(lint, Some(span), decorator); } - /// Emit a lint at the appropriate level, with an associated span. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - #[track_caller] - fn span_lint>( - &self, - lint: &'static Lint, - span: S, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), - ) { - self.opt_span_lint(lint, Some(span), decorate); - } - /// Emit a lint from a lint struct (some type that implements `Diagnostic`, typically /// generated by `#[derive(Diagnostic)]`). fn emit_diag_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) { From a0b24e39b9c240891674513191debc1a7d60ffae Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 5 Mar 2026 12:23:20 +0100 Subject: [PATCH 39/55] Remove unused `LintContext::opt_span_lint` method --- compiler/rustc_lint/src/context.rs | 38 +----------------------------- compiler/rustc_lint/src/levels.rs | 15 +----------- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index d51fa7f276fc5..98f087892a398 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -11,7 +11,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sync; use rustc_data_structures::unord::UnordMap; -use rustc_errors::{Diag, Diagnostic, LintBuffer, MultiSpan}; +use rustc_errors::{Diagnostic, LintBuffer, MultiSpan}; use rustc_feature::Features; use rustc_hir::def::Res; use rustc_hir::def_id::{CrateNum, DefId}; @@ -509,19 +509,6 @@ pub struct EarlyContext<'a> { pub trait LintContext { fn sess(&self) -> &Session; - // FIXME: These methods should not take an Into -- instead, callers should need to - // set the span in their `decorate` function (preferably using set_span). - /// Emit a lint at the appropriate level, with an optional associated span. - /// - /// [`lint_level`]: rustc_middle::lint::lint_level#decorate-signature - #[track_caller] - fn opt_span_lint>( - &self, - lint: &'static Lint, - span: Option, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), - ); - // FIXME: These methods should not take an Into -- instead, callers should need to // set the span in their `decorate` function (preferably using set_span). /// Emit a lint at the appropriate level, with an optional associated span. @@ -606,20 +593,6 @@ impl<'tcx> LintContext for LateContext<'tcx> { self.tcx.sess } - fn opt_span_lint>( - &self, - lint: &'static Lint, - span: Option, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), - ) { - let hir_id = self.last_node_with_lint_attrs; - - match span { - Some(s) => self.tcx.node_span_lint(lint, hir_id, s, decorate), - None => self.tcx.node_lint(lint, hir_id, decorate), - } - } - fn opt_span_diag_lint>( &self, lint: &'static Lint, @@ -645,15 +618,6 @@ impl LintContext for EarlyContext<'_> { self.builder.sess() } - fn opt_span_lint>( - &self, - lint: &'static Lint, - span: Option, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), - ) { - self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate) - } - fn opt_span_diag_lint>( &self, lint: &'static Lint, diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index a134b623d7bd4..1dd59e5d6d7cb 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -2,7 +2,7 @@ use rustc_ast::attr::AttributeExt; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{Diag, Diagnostic, MultiSpan, msg}; +use rustc_errors::{Diagnostic, MultiSpan, msg}; use rustc_feature::{Features, GateIssue}; use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; @@ -974,19 +974,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { self.provider.get_lint_level(lint, self.sess) } - /// Used to emit a lint-related diagnostic based on the current state of - /// this lint context. - #[track_caller] - pub(crate) fn opt_span_lint( - &self, - lint: &'static Lint, - span: Option, - decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), - ) { - let level = self.lint_level(lint); - lint_level(self.sess, lint, level, span, decorate) - } - /// Used to emit a lint-related diagnostic based on the current state of /// this lint context. #[track_caller] From 6e47be7ad96b784fd351eb925022e2a53f3b6e07 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 5 Mar 2026 12:38:41 +0100 Subject: [PATCH 40/55] Remove last use of `rustc_middle::lint_level` function in `rustc_lint` --- compiler/rustc_lint/src/levels.rs | 47 +++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 1dd59e5d6d7cb..8b7a8c9ed5d87 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -2,7 +2,7 @@ use rustc_ast::attr::AttributeExt; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{Diagnostic, MultiSpan, msg}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, MultiSpan, msg}; use rustc_feature::{Features, GateIssue}; use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; @@ -11,7 +11,7 @@ use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::lint::{ LevelAndSource, LintExpectation, LintLevelSource, ShallowLintLevelMap, diag_lint_level, - lint_level, reveal_actual_level, + reveal_actual_level, }; use rustc_middle::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; @@ -948,22 +948,45 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { return true; }; - if self.lint_added_lints { - let lint = builtin::UNKNOWN_LINTS; - let level = self.lint_level(builtin::UNKNOWN_LINTS); - lint_level(self.sess, lint, level, Some(span.into()), |lint| { - lint.primary_message(msg!("unknown lint: `{$name}`")); - lint.arg("name", lint_id.lint.name_lower()); - lint.note(msg!("the `{$name}` lint is unstable")); + struct UnknownLint<'a> { + sess: &'a Session, + lint_id: LintId, + feature: Symbol, + lint_from_cli: bool, + } + + impl<'a, 'b> Diagnostic<'a, ()> for UnknownLint<'b> { + fn into_diag( + self, + dcx: DiagCtxtHandle<'a>, + level: rustc_errors::Level, + ) -> Diag<'a, ()> { + let Self { sess, lint_id, feature, lint_from_cli } = self; + let mut lint = Diag::new(dcx, level, msg!("unknown lint: `{$name}`")) + .with_arg("name", lint_id.lint.name_lower()) + .with_note(msg!("the `{$name}` lint is unstable")); rustc_session::parse::add_feature_diagnostics_for_issue( - lint, - &self.sess, + &mut lint, + sess, feature, GateIssue::Language, lint_from_cli, None, ); - }); + lint + } + } + + if self.lint_added_lints { + let lint = builtin::UNKNOWN_LINTS; + let level = self.lint_level(builtin::UNKNOWN_LINTS); + diag_lint_level( + self.sess, + lint, + level, + Some(span.into()), + UnknownLint { sess: &self.sess, lint_id, feature, lint_from_cli }, + ); } false From 140ad033e76c31fb138a49e1324832f1714509a6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 5 Mar 2026 12:49:20 +0100 Subject: [PATCH 41/55] Remove `TyCtxt::node_span_lint` usage from `rustc_codegen_ssa` --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 1ceb01337b118..b8a8bb3ad419d 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -5,6 +5,7 @@ use rustc_hir::attrs::{ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{self as hir, Attribute, find_attr}; +use rustc_macros::Diagnostic; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs, }; @@ -385,6 +386,17 @@ fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut Code } } +#[derive(Diagnostic)] +#[diag("non-default `sanitize` will have no effect after inlining")] +struct SanitizeOnInline { + #[note("inlining requested here")] + inline_span: Span, +} + +#[derive(Diagnostic)] +#[diag("the async executor can run blocking code, without realtime sanitizer catching it")] +struct AsyncBlocking; + fn check_result( tcx: TyCtxt<'_>, did: LocalDefId, @@ -425,10 +437,12 @@ fn check_result( (interesting_spans.sanitize, interesting_spans.inline) { let hir_id = tcx.local_def_id_to_hir_id(did); - tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, sanitize_span, |lint| { - lint.primary_message("non-default `sanitize` will have no effect after inlining"); - lint.span_note(inline_span, "inlining requested here"); - }) + tcx.emit_node_span_lint( + lint::builtin::INLINE_NO_SANITIZE, + hir_id, + sanitize_span, + SanitizeOnInline { inline_span }, + ) } // warn for nonblocking async functions, blocks and closures. @@ -445,13 +459,11 @@ fn check_result( != rustc_hir::ClosureKind::Closure)) { let hir_id = tcx.local_def_id_to_hir_id(did); - tcx.node_span_lint( + tcx.emit_node_span_lint( lint::builtin::RTSAN_NONBLOCKING_ASYNC, hir_id, sanitize_span, - |lint| { - lint.primary_message(r#"the async executor can run blocking code, without realtime sanitizer catching it"#); - } + AsyncBlocking, ); } From e1ee66935e0f1b7606e5d1dfa850cf730fd85000 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 5 Mar 2026 16:22:25 +0100 Subject: [PATCH 42/55] Remove `TyCtxt::node_span_lint` usage from `rustc_hir_analysis` --- .../rustc_hir_analysis/src/check/check.rs | 99 ++++++++++++------- compiler/rustc_hir_analysis/src/check/mod.rs | 2 +- .../rustc_hir_analysis/src/check_unused.rs | 31 ++++-- compiler/rustc_hir_analysis/src/collect.rs | 25 +++-- .../src/collect/generics_of.rs | 18 +++- .../src/hir_ty_lowering/dyn_trait.rs | 46 ++++++--- .../src/hir_ty_lowering/generics.rs | 22 ++++- .../src/hir_ty_lowering/mod.rs | 91 ++++++++++++----- tests/ui/abi/unsupported.aarch64.stderr | 8 +- tests/ui/abi/unsupported.arm.stderr | 8 +- tests/ui/abi/unsupported.riscv32.stderr | 8 +- tests/ui/abi/unsupported.riscv64.stderr | 8 +- tests/ui/abi/unsupported.x64.stderr | 8 +- tests/ui/abi/unsupported.x64_win.stderr | 18 ++-- .../repr-c-big-discriminant1.ptr64.stderr | 20 ++-- .../repr-c-big-discriminant2.ptr64.stderr | 4 +- .../raw-dylib/windows/unsupported-abi.stderr | 2 +- .../future-incompatible-lint-group.stderr | 4 +- .../lint/improper-ctypes/lint-ctypes.stderr | 2 +- tests/ui/lint/improper-ctypes/lint-fn.stderr | 2 +- .../repr-transparent-non-exhaustive.stderr | 68 ++++++------- tests/ui/repr/repr-transparent-repr-c.stderr | 12 +-- tests/ui/statics/uninhabited-static.stderr | 8 +- ...ity-lint-ambiguous_associated_items.stderr | 4 +- 24 files changed, 329 insertions(+), 189 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 94988f3c92750..f3d234afef05e 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -4,7 +4,7 @@ use std::ops::ControlFlow; use rustc_abi::{ExternAbi, FieldIdx, ScalableElt}; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; -use rustc_errors::{EmissionGuarantee, MultiSpan}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan}; use rustc_hir as hir; use rustc_hir::attrs::ReprAttr::ReprPacked; use rustc_hir::def::{CtorKind, DefKind}; @@ -12,6 +12,7 @@ use rustc_hir::{LangItem, Node, find_attr, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode, WellFormedLoc}; use rustc_lint_defs::builtin::{REPR_TRANSPARENT_NON_ZST_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS}; +use rustc_macros::Diagnostic; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars::ResolvedArg; use rustc_middle::middle::stability::EvalResult; @@ -53,6 +54,22 @@ fn add_abi_diag_help(abi: ExternAbi, diag: &mut Diag<'_, T } pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi) { + struct UnsupportedCallingConventions { + abi: ExternAbi, + } + + impl<'a> Diagnostic<'a, ()> for UnsupportedCallingConventions { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { abi } = self; + let mut lint = Diag::new( + dcx, + level, + format!("{abi} is not a supported ABI for the current target"), + ); + add_abi_diag_help(abi, &mut lint); + lint + } + } // FIXME: This should be checked earlier, e.g. in `rustc_ast_lowering`, as this // currently only guards function imports, function definitions, and function pointer types. // Functions in trait declarations can still use "deprecated" ABIs without any warning. @@ -64,12 +81,12 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi tcx.dcx().span_delayed_bug(span, format!("{abi} should be rejected in ast_lowering")); } AbiMapping::Deprecated(..) => { - tcx.node_span_lint(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| { - lint.primary_message(format!( - "{abi} is not a supported ABI for the current target" - )); - add_abi_diag_help(abi, lint); - }); + tcx.emit_node_span_lint( + UNSUPPORTED_CALLING_CONVENTIONS, + hir_id, + span, + UnsupportedCallingConventions { abi }, + ); } } } @@ -174,6 +191,11 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b /// Check that a `static` is inhabited. fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { + #[derive(Diagnostic)] + #[diag("static of uninhabited type")] + #[note("uninhabited statics cannot be initialized, and any access would be an immediate error")] + struct StaticOfUninhabitedType; + // Make sure statics are inhabited. // Other parts of the compiler assume that there are no uninhabited places. In principle it // would be enough to check this for `extern` statics, as statics with an initializer will @@ -204,15 +226,11 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { } }; if layout.is_uninhabited() { - tcx.node_span_lint( + tcx.emit_node_span_lint( UNINHABITED_STATIC, tcx.local_def_id_to_hir_id(def_id), span, - |lint| { - lint.primary_message("static of uninhabited type"); - lint - .note("uninhabited statics cannot be initialized, and any access would be an immediate error"); - }, + StaticOfUninhabitedType, ); } } @@ -1637,6 +1655,39 @@ pub(super) fn check_packed_inner( } pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) { + struct ZeroSizedFieldReprTransparentIncompatibility<'tcx> { + unsuited: UnsuitedInfo<'tcx>, + } + + impl<'a, 'tcx> Diagnostic<'a, ()> for ZeroSizedFieldReprTransparentIncompatibility<'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { unsuited } = self; + let (title, note) = match unsuited.reason { + UnsuitedReason::NonExhaustive => ( + "external non-exhaustive types", + "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.", + ), + UnsuitedReason::PrivateField => ( + "external types with private fields", + "contains private fields, so it could become non-zero-sized in the future.", + ), + UnsuitedReason::ReprC => ( + "`repr(C)` types", + "is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.", + ), + }; + Diag::new( + dcx, + level, + format!("zero-sized fields in `repr(transparent)` cannot contain {title}"), + ) + .with_note(format!( + "this field contains `{field_ty}`, which {note}", + field_ty = unsuited.ty, + )) + } + } + if !adt.repr().transparent() { return; } @@ -1747,29 +1798,11 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts. // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst. if non_trivial_count > 0 || prev_unsuited_1zst { - tcx.node_span_lint( + tcx.emit_node_span_lint( REPR_TRANSPARENT_NON_ZST_FIELDS, tcx.local_def_id_to_hir_id(adt.did().expect_local()), field.span, - |lint| { - let title = match unsuited.reason { - UnsuitedReason::NonExhaustive => "external non-exhaustive types", - UnsuitedReason::PrivateField => "external types with private fields", - UnsuitedReason::ReprC => "`repr(C)` types", - }; - lint.primary_message( - format!("zero-sized fields in `repr(transparent)` cannot contain {title}"), - ); - let note = match unsuited.reason { - UnsuitedReason::NonExhaustive => "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.", - UnsuitedReason::PrivateField => "contains private fields, so it could become non-zero-sized in the future.", - UnsuitedReason::ReprC => "is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.", - }; - lint.note(format!( - "this field contains `{field_ty}`, which {note}", - field_ty = unsuited.ty, - )); - }, + ZeroSizedFieldReprTransparentIncompatibility { unsuited }, ); } else { prev_unsuited_1zst = true; diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 61e6133306066..458bb6ddd2117 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -77,7 +77,7 @@ use std::num::NonZero; pub use check::{check_abi, check_custom_abi}; use rustc_abi::VariantIdx; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err}; +use rustc_errors::{ErrorGuaranteed, pluralize, struct_span_code_err}; use rustc_hir::LangItem; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index 3fb33c741c9dc..7302913cc1ae7 100644 --- a/compiler/rustc_hir_analysis/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -1,10 +1,28 @@ use rustc_data_structures::unord::{ExtendUnord, UnordSet}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::TyCtxt; use rustc_session::lint; +use rustc_span::Span; use tracing::debug; +struct UnusedImport<'tcx> { + tcx: TyCtxt<'tcx>, + span: Span, +} + +impl<'a, 'tcx> Diagnostic<'a, ()> for UnusedImport<'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { tcx, span } = self; + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) { + Diag::new(dcx, level, format!("unused import: `{snippet}`")) + } else { + Diag::new(dcx, level, "unused import") + } + } +} + pub(super) fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) { let mut used_trait_imports = UnordSet::::default(); @@ -31,12 +49,11 @@ pub(super) fn check_unused_traits(tcx: TyCtxt<'_>, (): ()) { continue; } let (path, _) = item.expect_use(); - tcx.node_span_lint(lint::builtin::UNUSED_IMPORTS, item.hir_id(), path.span, |lint| { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { - lint.primary_message(format!("unused import: `{snippet}`")); - } else { - lint.primary_message("unused import"); - } - }); + tcx.emit_node_span_lint( + lint::builtin::UNUSED_IMPORTS, + item.hir_id(), + path.span, + UnusedImport { tcx, span: path.span }, + ); } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index f4c7234cd8f83..6850e67aff1a7 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -22,7 +22,9 @@ use rustc_abi::{ExternAbi, Size}; use rustc_ast::Recovered; use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_errors::{Applicability, Diag, DiagCtxtHandle, E0228, ErrorGuaranteed, StashKey}; +use rustc_errors::{ + Applicability, Diag, DiagCtxtHandle, Diagnostic, E0228, ErrorGuaranteed, Level, StashKey, +}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt}; @@ -610,6 +612,19 @@ pub(super) fn lower_variant_ctor(tcx: TyCtxt<'_>, def_id: LocalDefId) { } pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) { + struct ReprCIssue { + msg: &'static str, + } + + impl<'a> Diagnostic<'a, ()> for ReprCIssue { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { msg } = self; + Diag::new(dcx, level, msg) + .with_note("`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C") + .with_help("use `repr($int_ty)` instead to explicitly set the size of this enum") + } + } + let def = tcx.adt_def(def_id); let repr_type = def.repr().discr_type(); let initial = repr_type.initial_discriminant(tcx); @@ -659,15 +674,11 @@ pub(super) fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: LocalDefId) { } else { "`repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int`" }; - tcx.node_span_lint( + tcx.emit_node_span_lint( rustc_session::lint::builtin::REPR_C_ENUMS_LARGER_THAN_INT, tcx.local_def_id_to_hir_id(def_id), span, - |d| { - d.primary_message(msg) - .note("`repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C") - .help("use `repr($int_ty)` instead to explicitly set the size of this enum"); - } + ReprCIssue { msg }, ); } } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index b4c264c3a2364..e1ec57aea9217 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -1,6 +1,7 @@ use std::ops::ControlFlow; use rustc_data_structures::assert_matches; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; @@ -17,6 +18,17 @@ use crate::middle::resolve_bound_vars as rbv; pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { use rustc_hir::*; + struct GenericParametersForbiddenHere { + msg: &'static str, + } + + impl<'a> Diagnostic<'a, ()> for GenericParametersForbiddenHere { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { msg } = self; + Diag::new(dcx, level, msg) + } + } + // For an RPITIT, synthesize generics which are equal to the opaque's generics // and parent fn's generics compressed into one list. if let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) = @@ -269,13 +281,11 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { match param_default_policy.expect("no policy for generic param default") { ParamDefaultPolicy::Allowed => {} ParamDefaultPolicy::FutureCompatForbidden => { - tcx.node_span_lint( + tcx.emit_node_span_lint( lint::builtin::INVALID_TYPE_PARAM_DEFAULT, param.hir_id, param.span, - |lint| { - lint.primary_message(MESSAGE); - }, + GenericParametersForbiddenHere { msg: MESSAGE }, ); } ParamDefaultPolicy::Forbidden => { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs index 7b8e09943df71..8397ff61a3b3c 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_trait.rs @@ -2,11 +2,12 @@ use rustc_ast::TraitObjectSyntax; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, EmissionGuarantee, StashKey, Suggestions, struct_span_code_err, + Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, StashKey, + Suggestions, struct_span_code_err, }; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, HirId, LangItem}; use rustc_lint_defs::builtin::{BARE_TRAIT_OBJECTS, UNUSED_ASSOCIATED_TYPE_BOUNDS}; use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan; use rustc_middle::ty::{ @@ -523,6 +524,30 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { hir_id: hir::HirId, hir_bounds: &[hir::PolyTraitRef<'tcx>], ) -> Option { + struct TraitObjectWithoutDyn<'a, 'tcx> { + span: Span, + hir_id: HirId, + sugg: Vec<(Span, String)>, + this: &'a dyn HirTyLowerer<'tcx>, + } + + impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for TraitObjectWithoutDyn<'b, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { span, hir_id, sugg, this } = self; + let mut lint = + Diag::new(dcx, level, "trait objects without an explicit `dyn` are deprecated"); + if span.can_be_used_for_suggestions() { + lint.multipart_suggestion( + "if this is a dyn-compatible trait, use `dyn`", + sugg, + Applicability::MachineApplicable, + ); + } + this.maybe_suggest_blanket_trait_impl(span, hir_id, &mut lint); + lint + } + } + let tcx = self.tcx(); let [poly_trait_ref, ..] = hir_bounds else { return None }; @@ -606,17 +631,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } Some(diag.emit()) } else { - tcx.node_span_lint(BARE_TRAIT_OBJECTS, hir_id, span, |lint| { - lint.primary_message("trait objects without an explicit `dyn` are deprecated"); - if span.can_be_used_for_suggestions() { - lint.multipart_suggestion( - "if this is a dyn-compatible trait, use `dyn`", - sugg, - Applicability::MachineApplicable, - ); - } - self.maybe_suggest_blanket_trait_impl(span, hir_id, lint); - }); + tcx.emit_node_span_lint( + BARE_TRAIT_OBJECTS, + hir_id, + span, + TraitObjectWithoutDyn { span, hir_id, sugg, this: self }, + ); None } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index ff0a5a8df0faf..0ca57cb50cf25 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -1,6 +1,9 @@ use rustc_ast::ast::ParamKindOrd; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, struct_span_code_err}; +use rustc_errors::{ + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, Level, MultiSpan, + struct_span_code_err, +}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, GenericArg}; @@ -625,6 +628,17 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( args: &hir::GenericArgs<'_>, position: GenericArgPosition, ) -> ExplicitLateBound { + struct LifetimeArgsIssue { + msg: &'static str, + } + + impl<'a> Diagnostic<'a, ()> for LifetimeArgsIssue { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { msg } = self; + Diag::new(dcx, level, msg) + } + } + let param_counts = def.own_counts(); if let Some(span_late) = def.has_late_bound_regions @@ -644,13 +658,11 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes( } else { let mut multispan = MultiSpan::from_span(span); multispan.push_span_label(span_late, note); - cx.tcx().node_span_lint( + cx.tcx().emit_node_span_lint( LATE_BOUND_LIFETIME_ARGUMENTS, args.args[0].hir_id(), multispan, - |lint| { - lint.primary_message(msg); - }, + LifetimeArgsIssue { msg }, ); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 25911d3e9e462..3a41ef47ac526 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -27,7 +27,8 @@ use rustc_data_structures::assert_matches; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, struct_span_code_err, + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, FatalError, Level, + struct_span_code_err, }; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -1481,6 +1482,54 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, mode: LowerTypeRelativePathMode, ) -> Result, ErrorGuaranteed> { + struct AmbiguousAssocItem<'tcx> { + variant_def_id: DefId, + item_def_id: DefId, + span: Span, + segment_ident: Ident, + bound_def_id: DefId, + self_ty: Ty<'tcx>, + tcx: TyCtxt<'tcx>, + mode: LowerTypeRelativePathMode, + } + + impl<'a, 'tcx> Diagnostic<'a, ()> for AmbiguousAssocItem<'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { + variant_def_id, + item_def_id, + span, + segment_ident, + bound_def_id, + self_ty, + tcx, + mode, + } = self; + let mut lint = Diag::new(dcx, level, "ambiguous associated item"); + + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + segment_ident, + also, + tcx.def_kind_descr(kind, def_id) + ); + lint.span_note(tcx.def_span(def_id), note_msg); + }; + + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(mode.def_kind_for_diagnostics(), item_def_id, " also"); + + lint.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", self_ty, tcx.item_name(bound_def_id), segment_ident), + Applicability::MachineApplicable, + ); + lint + } + } + debug!(%self_ty, ?segment.ident); let tcx = self.tcx(); @@ -1556,33 +1605,21 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let (item_def_id, args) = self.lower_assoc_item_path(span, item_def_id, segment, bound)?; if let Some(variant_def_id) = variant_def_id { - tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, qpath_hir_id, span, |lint| { - lint.primary_message("ambiguous associated item"); - let mut could_refer_to = |kind: DefKind, def_id, also| { - let note_msg = format!( - "`{}` could{} refer to the {} defined here", - segment.ident, - also, - tcx.def_kind_descr(kind, def_id) - ); - lint.span_note(tcx.def_span(def_id), note_msg); - }; - - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(mode.def_kind_for_diagnostics(), item_def_id, " also"); - - lint.span_suggestion( + tcx.emit_node_span_lint( + AMBIGUOUS_ASSOCIATED_ITEMS, + qpath_hir_id, + span, + AmbiguousAssocItem { + variant_def_id, + item_def_id, span, - "use fully-qualified syntax", - format!( - "<{} as {}>::{}", - self_ty, - tcx.item_name(bound.def_id()), - segment.ident - ), - Applicability::MachineApplicable, - ); - }); + segment_ident: segment.ident, + bound_def_id: bound.def_id(), + self_ty, + tcx, + mode, + }, + ); } Ok(TypeRelativePath::AssocItem(item_def_id, args)) diff --git a/tests/ui/abi/unsupported.aarch64.stderr b/tests/ui/abi/unsupported.aarch64.stderr index 00cc31170b74f..6add008aa60fa 100644 --- a/tests/ui/abi/unsupported.aarch64.stderr +++ b/tests/ui/abi/unsupported.aarch64.stderr @@ -162,9 +162,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | fn cdecl_ptr(f: extern "cdecl" fn()) { | ^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead = note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default warning: "cdecl" is not a supported ABI for the current target @@ -173,9 +173,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" {} | ^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead warning: "cdecl-unwind" is not a supported ABI for the current target --> $DIR/unsupported.rs:108:1 @@ -183,9 +183,9 @@ warning: "cdecl-unwind" is not a supported ABI for the current target LL | extern "cdecl-unwind" {} | ^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C-unwind"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead warning: "cdecl" is not a supported ABI for the current target --> $DIR/unsupported.rs:97:1 @@ -193,9 +193,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" fn cdecl() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead error: aborting due to 25 previous errors; 4 warnings emitted diff --git a/tests/ui/abi/unsupported.arm.stderr b/tests/ui/abi/unsupported.arm.stderr index 23bdcc2272950..ab345f9e42e96 100644 --- a/tests/ui/abi/unsupported.arm.stderr +++ b/tests/ui/abi/unsupported.arm.stderr @@ -144,9 +144,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | fn cdecl_ptr(f: extern "cdecl" fn()) { | ^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead = note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default warning: "cdecl" is not a supported ABI for the current target @@ -155,9 +155,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" {} | ^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead warning: "cdecl-unwind" is not a supported ABI for the current target --> $DIR/unsupported.rs:108:1 @@ -165,9 +165,9 @@ warning: "cdecl-unwind" is not a supported ABI for the current target LL | extern "cdecl-unwind" {} | ^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C-unwind"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead warning: "cdecl" is not a supported ABI for the current target --> $DIR/unsupported.rs:97:1 @@ -175,9 +175,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" fn cdecl() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead error: aborting due to 22 previous errors; 4 warnings emitted diff --git a/tests/ui/abi/unsupported.riscv32.stderr b/tests/ui/abi/unsupported.riscv32.stderr index ef1f1b53cdc56..e2ca35d6a50e2 100644 --- a/tests/ui/abi/unsupported.riscv32.stderr +++ b/tests/ui/abi/unsupported.riscv32.stderr @@ -156,9 +156,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | fn cdecl_ptr(f: extern "cdecl" fn()) { | ^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead = note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default warning: "cdecl" is not a supported ABI for the current target @@ -167,9 +167,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" {} | ^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead warning: "cdecl-unwind" is not a supported ABI for the current target --> $DIR/unsupported.rs:108:1 @@ -177,9 +177,9 @@ warning: "cdecl-unwind" is not a supported ABI for the current target LL | extern "cdecl-unwind" {} | ^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C-unwind"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead warning: "cdecl" is not a supported ABI for the current target --> $DIR/unsupported.rs:97:1 @@ -187,9 +187,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" fn cdecl() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead error: aborting due to 24 previous errors; 4 warnings emitted diff --git a/tests/ui/abi/unsupported.riscv64.stderr b/tests/ui/abi/unsupported.riscv64.stderr index ef1f1b53cdc56..e2ca35d6a50e2 100644 --- a/tests/ui/abi/unsupported.riscv64.stderr +++ b/tests/ui/abi/unsupported.riscv64.stderr @@ -156,9 +156,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | fn cdecl_ptr(f: extern "cdecl" fn()) { | ^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead = note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default warning: "cdecl" is not a supported ABI for the current target @@ -167,9 +167,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" {} | ^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead warning: "cdecl-unwind" is not a supported ABI for the current target --> $DIR/unsupported.rs:108:1 @@ -177,9 +177,9 @@ warning: "cdecl-unwind" is not a supported ABI for the current target LL | extern "cdecl-unwind" {} | ^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C-unwind"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead warning: "cdecl" is not a supported ABI for the current target --> $DIR/unsupported.rs:97:1 @@ -187,9 +187,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" fn cdecl() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead error: aborting due to 24 previous errors; 4 warnings emitted diff --git a/tests/ui/abi/unsupported.x64.stderr b/tests/ui/abi/unsupported.x64.stderr index 0c338849ae9d8..41842eecbd020 100644 --- a/tests/ui/abi/unsupported.x64.stderr +++ b/tests/ui/abi/unsupported.x64.stderr @@ -138,9 +138,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | fn cdecl_ptr(f: extern "cdecl" fn()) { | ^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead = note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default warning: "cdecl" is not a supported ABI for the current target @@ -149,9 +149,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" {} | ^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead warning: "cdecl-unwind" is not a supported ABI for the current target --> $DIR/unsupported.rs:108:1 @@ -159,9 +159,9 @@ warning: "cdecl-unwind" is not a supported ABI for the current target LL | extern "cdecl-unwind" {} | ^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C-unwind"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead warning: "cdecl" is not a supported ABI for the current target --> $DIR/unsupported.rs:97:1 @@ -169,9 +169,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" fn cdecl() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead error: aborting due to 21 previous errors; 4 warnings emitted diff --git a/tests/ui/abi/unsupported.x64_win.stderr b/tests/ui/abi/unsupported.x64_win.stderr index 07c4eb691fe97..79938f04c5996 100644 --- a/tests/ui/abi/unsupported.x64_win.stderr +++ b/tests/ui/abi/unsupported.x64_win.stderr @@ -106,9 +106,9 @@ warning: "stdcall" is not a supported ABI for the current target LL | fn stdcall_ptr(f: extern "stdcall" fn()) { | ^^^^^^^^^^^^^^^^^^^^^ | + = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` = note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default warning: "stdcall" is not a supported ABI for the current target @@ -117,9 +117,9 @@ warning: "stdcall" is not a supported ABI for the current target LL | extern "stdcall" {} | ^^^^^^^^^^^^^^^^^^^ | + = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` warning: "stdcall-unwind" is not a supported ABI for the current target --> $DIR/unsupported.rs:92:1 @@ -127,9 +127,9 @@ warning: "stdcall-unwind" is not a supported ABI for the current target LL | extern "stdcall-unwind" {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: if you need `extern "stdcall-unwind"` on win32 and `extern "C-unwind"` everywhere else, use `extern "system-unwind"` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: if you need `extern "stdcall-unwind"` on win32 and `extern "C-unwind"` everywhere else, use `extern "system-unwind"` warning: "cdecl" is not a supported ABI for the current target --> $DIR/unsupported.rs:100:17 @@ -137,9 +137,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | fn cdecl_ptr(f: extern "cdecl" fn()) { | ^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead warning: "cdecl" is not a supported ABI for the current target --> $DIR/unsupported.rs:105:1 @@ -147,9 +147,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" {} | ^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead warning: "cdecl-unwind" is not a supported ABI for the current target --> $DIR/unsupported.rs:108:1 @@ -157,9 +157,9 @@ warning: "cdecl-unwind" is not a supported ABI for the current target LL | extern "cdecl-unwind" {} | ^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C-unwind"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C-unwind"` instead warning: "cdecl" is not a supported ABI for the current target --> $DIR/unsupported.rs:137:1 @@ -167,9 +167,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" {} | ^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead warning: "stdcall" is not a supported ABI for the current target --> $DIR/unsupported.rs:78:1 @@ -177,9 +177,9 @@ warning: "stdcall" is not a supported ABI for the current target LL | extern "stdcall" fn stdcall() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` warning: "cdecl" is not a supported ABI for the current target --> $DIR/unsupported.rs:97:1 @@ -187,9 +187,9 @@ warning: "cdecl" is not a supported ABI for the current target LL | extern "cdecl" fn cdecl() {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: use `extern "C"` instead = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: use `extern "C"` instead error: aborting due to 17 previous errors; 9 warnings emitted diff --git a/tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr64.stderr b/tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr64.stderr index e2517ab342f45..2b8326018bc93 100644 --- a/tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr64.stderr +++ b/tests/ui/enum-discriminant/repr-c-big-discriminant1.ptr64.stderr @@ -4,10 +4,10 @@ error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigne LL | A = 9223372036854775807, // i64::MAX | ^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #124403 = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C = help: use `repr($int_ty)` instead to explicitly set the size of this enum + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 note: the lint level is defined here --> $DIR/repr-c-big-discriminant1.rs:8:9 | @@ -20,10 +20,10 @@ error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigne LL | A = -2147483649, // i32::MIN-1 | ^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #124403 = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C = help: use `repr($int_ty)` instead to explicitly set the size of this enum + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 error: `repr(C)` enum discriminant does not fit into C `unsigned int`, and a previous discriminant does not fit into C `int` --> $DIR/repr-c-big-discriminant1.rs:36:5 @@ -31,10 +31,10 @@ error: `repr(C)` enum discriminant does not fit into C `unsigned int`, and a pre LL | B = -1, | ^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #124403 = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C = help: use `repr($int_ty)` instead to explicitly set the size of this enum + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 error: `repr(C)` enum discriminant does not fit into C `int`, and a previous discriminant does not fit into C `unsigned int` --> $DIR/repr-c-big-discriminant1.rs:43:5 @@ -42,10 +42,10 @@ error: `repr(C)` enum discriminant does not fit into C `int`, and a previous dis LL | A = 2147483648, // i32::MAX+1 | ^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #124403 = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C = help: use `repr($int_ty)` instead to explicitly set the size of this enum + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigned int` --> $DIR/repr-c-big-discriminant1.rs:53:5 @@ -53,10 +53,10 @@ error: `repr(C)` enum discriminant does not fit into C `int` nor into C `unsigne LL | A = I64_MAX as isize, | ^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #124403 = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C = help: use `repr($int_ty)` instead to explicitly set the size of this enum + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 error: aborting due to 5 previous errors diff --git a/tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr64.stderr b/tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr64.stderr index 8cd978ccb2fb1..ebf14059b314d 100644 --- a/tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr64.stderr +++ b/tests/ui/enum-discriminant/repr-c-big-discriminant2.ptr64.stderr @@ -4,10 +4,10 @@ error: `repr(C)` enum discriminant does not fit into C `int`, and a previous dis LL | B, // +1 | ^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #124403 = note: `repr(C)` enums with big discriminants are non-portable, and their size in Rust might not match their size in C = help: use `repr($int_ty)` instead to explicitly set the size of this enum + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #124403 note: the lint level is defined here --> $DIR/repr-c-big-discriminant2.rs:8:9 | diff --git a/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr index 5a574636245d3..9d945eb23491c 100644 --- a/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr +++ b/tests/ui/linkage-attr/raw-dylib/windows/unsupported-abi.stderr @@ -9,9 +9,9 @@ LL | | LL | | } | |_^ | + = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #137018 - = help: if you need `extern "stdcall"` on win32 and `extern "C"` everywhere else, use `extern "system"` = note: `#[warn(unsupported_calling_conventions)]` (part of `#[warn(future_incompatible)]`) on by default error: ABI not supported by `#[link(kind = "raw-dylib")]` on this architecture diff --git a/tests/ui/lint/future-incompatible-lint-group.stderr b/tests/ui/lint/future-incompatible-lint-group.stderr index ff1e54f5dea30..8f234c6216065 100644 --- a/tests/ui/lint/future-incompatible-lint-group.stderr +++ b/tests/ui/lint/future-incompatible-lint-group.stderr @@ -14,8 +14,6 @@ error: ambiguous associated item LL | fn foo() -> Self::V { 0 } | ^^^^^^^ help: use fully-qualified syntax: `::V` | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57644 note: `V` could refer to the variant defined here --> $DIR/future-incompatible-lint-group.rs:8:10 | @@ -26,6 +24,8 @@ note: `V` could also refer to the associated type defined here | LL | type V; | ^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57644 note: the lint level is defined here --> $DIR/future-incompatible-lint-group.rs:6:9 | diff --git a/tests/ui/lint/improper-ctypes/lint-ctypes.stderr b/tests/ui/lint/improper-ctypes/lint-ctypes.stderr index ef23f3ca6c919..82eacf901d25d 100644 --- a/tests/ui/lint/improper-ctypes/lint-ctypes.stderr +++ b/tests/ui/lint/improper-ctypes/lint-ctypes.stderr @@ -218,7 +218,7 @@ warning: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types LL | pub struct TransparentCustomZst(i32, ZeroSize); | ^^^^^^^^ | + = note: this field contains `ZeroSize`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `ZeroSize`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. diff --git a/tests/ui/lint/improper-ctypes/lint-fn.stderr b/tests/ui/lint/improper-ctypes/lint-fn.stderr index a0d1ab232c6ad..45681cc9f1122 100644 --- a/tests/ui/lint/improper-ctypes/lint-fn.stderr +++ b/tests/ui/lint/improper-ctypes/lint-fn.stderr @@ -169,7 +169,7 @@ warning: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types LL | pub struct TransparentCustomZst(i32, ZeroSize); | ^^^^^^^^ | + = note: this field contains `ZeroSize`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `ZeroSize`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. diff --git a/tests/ui/repr/repr-transparent-non-exhaustive.stderr b/tests/ui/repr/repr-transparent-non-exhaustive.stderr index ac5493bf7e59c..0a575e5f5829d 100644 --- a/tests/ui/repr/repr-transparent-non-exhaustive.stderr +++ b/tests/ui/repr/repr-transparent-non-exhaustive.stderr @@ -4,9 +4,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external types wi LL | pub struct T5(Sized, Private); | ^^^^^^^ | + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -19,9 +19,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T6(Sized, NonExhaustive); | ^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:54:23 @@ -29,9 +29,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T6a(Sized, ::Assoc); // normalizes to `NonExhaustive` | ^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:59:22 @@ -39,9 +39,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T7(Sized, NonExhaustiveEnum); | ^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:64:22 @@ -49,9 +49,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T8(Sized, NonExhaustiveVariant); | ^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields --> $DIR/repr-transparent-non-exhaustive.rs:69:22 @@ -59,9 +59,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external types wi LL | pub struct T9(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:74:23 @@ -69,9 +69,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T10(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:79:23 @@ -79,9 +79,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T11(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:84:23 @@ -89,9 +89,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T12(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields --> $DIR/repr-transparent-non-exhaustive.rs:89:23 @@ -99,9 +99,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external types wi LL | pub struct T13(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:94:23 @@ -109,9 +109,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T14(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:99:23 @@ -119,9 +119,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T15(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:104:23 @@ -129,9 +129,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T16(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:109:16 @@ -139,9 +139,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T17(NonExhaustive, Sized); | ^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:114:31 @@ -149,9 +149,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T18(NonExhaustive, NonExhaustive); | ^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external types with private fields --> $DIR/repr-transparent-non-exhaustive.rs:119:31 @@ -159,9 +159,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external types wi LL | pub struct T19(NonExhaustive, Private); | ^^^^^^^ | + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. error: zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types --> $DIR/repr-transparent-non-exhaustive.rs:124:32 @@ -169,9 +169,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T19Flipped(Private, NonExhaustive); | ^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. error: aborting due to 17 previous errors @@ -182,9 +182,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external types wi LL | pub struct T5(Sized, Private); | ^^^^^^^ | + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -198,9 +198,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T6(Sized, NonExhaustive); | ^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -214,9 +214,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T6a(Sized, ::Assoc); // normalizes to `NonExhaustive` | ^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -230,9 +230,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T7(Sized, NonExhaustiveEnum); | ^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -246,9 +246,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T8(Sized, NonExhaustiveVariant); | ^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -262,9 +262,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external types wi LL | pub struct T9(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -278,9 +278,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T10(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -294,9 +294,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T11(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -310,9 +310,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T12(Sized, InternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -326,9 +326,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external types wi LL | pub struct T13(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -342,9 +342,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T14(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -358,9 +358,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T15(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveEnum`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -374,9 +374,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T16(Sized, ExternalIndirection); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustiveVariant`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -390,9 +390,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T17(NonExhaustive, Sized); | ^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -406,9 +406,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T18(NonExhaustive, NonExhaustive); | ^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -422,9 +422,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external types wi LL | pub struct T19(NonExhaustive, Private); | ^^^^^^^ | + = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `Private`, which contains private fields, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | @@ -438,9 +438,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain external non-exha LL | pub struct T19Flipped(Private, NonExhaustive); | ^^^^^^^^^^^^^ | + = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `NonExhaustive`, which is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future. note: the lint level is defined here --> $DIR/repr-transparent-non-exhaustive.rs:1:9 | diff --git a/tests/ui/repr/repr-transparent-repr-c.stderr b/tests/ui/repr/repr-transparent-repr-c.stderr index 5724845afdc1e..2a670fed80704 100644 --- a/tests/ui/repr/repr-transparent-repr-c.stderr +++ b/tests/ui/repr/repr-transparent-repr-c.stderr @@ -4,9 +4,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types LL | pub struct T5(Sized, ReprC1Zst); | ^^^^^^^^^ | + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. note: the lint level is defined here --> $DIR/repr-transparent-repr-c.rs:1:9 | @@ -19,9 +19,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types LL | pub struct T6(ReprC1Zst, Sized); | ^^^^^^^^^ | + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types --> $DIR/repr-transparent-repr-c.rs:28:15 @@ -29,9 +29,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types LL | pub struct T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type | ^^ | + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. error: aborting due to 3 previous errors @@ -42,9 +42,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types LL | pub struct T5(Sized, ReprC1Zst); | ^^^^^^^^^ | + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. note: the lint level is defined here --> $DIR/repr-transparent-repr-c.rs:1:9 | @@ -58,9 +58,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types LL | pub struct T6(ReprC1Zst, Sized); | ^^^^^^^^^ | + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. note: the lint level is defined here --> $DIR/repr-transparent-repr-c.rs:1:9 | @@ -74,9 +74,9 @@ error: zero-sized fields in `repr(transparent)` cannot contain `repr(C)` types LL | pub struct T7(T1, Sized); // still wrong, even when the repr(C) is hidden inside another type | ^^ | + = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #78586 - = note: this field contains `ReprC1Zst`, which is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets. note: the lint level is defined here --> $DIR/repr-transparent-repr-c.rs:1:9 | diff --git a/tests/ui/statics/uninhabited-static.stderr b/tests/ui/statics/uninhabited-static.stderr index a0f9ad6772de5..4762784574dc7 100644 --- a/tests/ui/statics/uninhabited-static.stderr +++ b/tests/ui/statics/uninhabited-static.stderr @@ -4,9 +4,9 @@ error: static of uninhabited type LL | static VOID2: Void = unsafe { std::mem::transmute(()) }; | ^^^^^^^^^^^^^^^^^^ | + = note: uninhabited statics cannot be initialized, and any access would be an immediate error = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #74840 - = note: uninhabited statics cannot be initialized, and any access would be an immediate error note: the lint level is defined here --> $DIR/uninhabited-static.rs:2:9 | @@ -19,9 +19,9 @@ error: static of uninhabited type LL | static NEVER2: Void = unsafe { std::mem::transmute(()) }; | ^^^^^^^^^^^^^^^^^^^ | + = note: uninhabited statics cannot be initialized, and any access would be an immediate error = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #74840 - = note: uninhabited statics cannot be initialized, and any access would be an immediate error error: static of uninhabited type --> $DIR/uninhabited-static.rs:6:5 @@ -29,9 +29,9 @@ error: static of uninhabited type LL | static VOID: Void; | ^^^^^^^^^^^^^^^^^ | + = note: uninhabited statics cannot be initialized, and any access would be an immediate error = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #74840 - = note: uninhabited statics cannot be initialized, and any access would be an immediate error error: static of uninhabited type --> $DIR/uninhabited-static.rs:8:5 @@ -39,9 +39,9 @@ error: static of uninhabited type LL | static NEVER: !; | ^^^^^^^^^^^^^^^ | + = note: uninhabited statics cannot be initialized, and any access would be an immediate error = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #74840 - = note: uninhabited statics cannot be initialized, and any access would be an immediate error error[E0080]: constructing invalid value: encountered a value of uninhabited type `Void` --> $DIR/uninhabited-static.rs:12:31 diff --git a/tests/ui/type-alias-enum-variants/enum-variant-priority-lint-ambiguous_associated_items.stderr b/tests/ui/type-alias-enum-variants/enum-variant-priority-lint-ambiguous_associated_items.stderr index 79bd1f2adc17b..918d05b5d6781 100644 --- a/tests/ui/type-alias-enum-variants/enum-variant-priority-lint-ambiguous_associated_items.stderr +++ b/tests/ui/type-alias-enum-variants/enum-variant-priority-lint-ambiguous_associated_items.stderr @@ -4,8 +4,6 @@ error: ambiguous associated item LL | fn f() -> Self::V { 0 } | ^^^^^^^ help: use fully-qualified syntax: `::V` | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57644 note: `V` could refer to the variant defined here --> $DIR/enum-variant-priority-lint-ambiguous_associated_items.rs:22:5 | @@ -16,6 +14,8 @@ note: `V` could also refer to the associated type defined here | LL | type V; | ^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57644 = note: `#[deny(ambiguous_associated_items)]` (part of `#[deny(future_incompatible)]`) on by default error: aborting due to 1 previous error From c98094bd32e70f8a6f583262217f273b9e90a4c1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 5 Mar 2026 18:34:49 +0100 Subject: [PATCH 43/55] Remove `TyCtxt::node_span_lint` usage from `rustc_hir_typeck` --- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 38 +- compiler/rustc_hir_typeck/src/inline_asm.rs | 55 ++- .../src/method/prelude_edition_lints.rs | 432 ++++++++++-------- compiler/rustc_hir_typeck/src/method/probe.rs | 110 +++-- compiler/rustc_hir_typeck/src/pat.rs | 40 +- compiler/rustc_hir_typeck/src/upvar.rs | 321 +++++++------ tests/ui/inference/inference_unstable.stderr | 8 +- tests/ui/resolve/slice-as-slice.stderr | 2 +- 8 files changed, 614 insertions(+), 392 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index d2152b763a8e2..c24d1127d5a62 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -3,7 +3,9 @@ use std::slice; use rustc_abi::FieldIdx; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, Level, MultiSpan, +}; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::VisitorExt; @@ -80,6 +82,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Produces warning on the given node, if the current point in the /// function is unreachable, and there hasn't been another warning. pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) { + struct UnreachableItem<'a, 'b> { + kind: &'a str, + span: Span, + orig_span: Span, + custom_note: Option<&'b str>, + } + + impl<'a, 'b, 'c> Diagnostic<'a, ()> for UnreachableItem<'b, 'c> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { kind, span, orig_span, custom_note } = self; + let msg = format!("unreachable {kind}"); + Diag::new(dcx, level, msg.clone()).with_span_label(span, msg).with_span_label( + orig_span, + custom_note.map(|c| c.to_owned()).unwrap_or_else(|| { + "any code following this expression is unreachable".to_owned() + }), + ) + } + } + let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() else { return; }; @@ -102,14 +124,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - let msg = format!("unreachable {kind}"); - self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - lint.primary_message(msg.clone()); - lint.span_label(span, msg).span_label( - orig_span, - custom_note.unwrap_or("any code following this expression is unreachable"), - ); - }) + self.tcx().emit_node_span_lint( + lint::builtin::UNREACHABLE_CODE, + id, + span, + UnreachableItem { kind, span, orig_span, custom_note }, + ); } /// Resolves type and const variables in `t` if possible. Unlike the infcx diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index 7c1655f8201d7..f2b746ac96b10 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -1,6 +1,7 @@ use rustc_abi::FieldIdx; use rustc_ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxIndexSet; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_middle::bug; @@ -168,6 +169,40 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { is_input: bool, tied_input: Option<(&'tcx hir::Expr<'tcx>, Option)>, ) -> Option { + struct FormattingSubRegisterArg<'a> { + expr_span: Span, + idx: usize, + suggested_modifier: char, + suggested_result: &'a str, + suggested_size: u16, + default_modifier: char, + default_result: &'a str, + default_size: u16, + } + + impl<'a, 'b> Diagnostic<'a, ()> for FormattingSubRegisterArg<'b> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { + expr_span, + idx, + suggested_modifier, + suggested_result, + suggested_size, + default_modifier, + default_result, + default_size, + } = self; + Diag::new(dcx, level, "formatting may not be suitable for sub-register argument") + .with_span_label(expr_span, "for this argument") + .with_help(format!( + "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", + )) + .with_help(format!( + "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", + )) + } + } + let ty = self.expr_ty(expr); if ty.has_non_region_infer() { bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty); @@ -362,19 +397,19 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { result: default_result, size: default_size, } = reg_class.default_modifier(asm_arch).unwrap(); - self.tcx().node_span_lint( + self.tcx().emit_node_span_lint( lint::builtin::ASM_SUB_REGISTER, expr.hir_id, spans, - |lint| { - lint.primary_message("formatting may not be suitable for sub-register argument"); - lint.span_label(expr.span, "for this argument"); - lint.help(format!( - "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", - )); - lint.help(format!( - "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", - )); + FormattingSubRegisterArg { + expr_span: expr.span, + idx, + suggested_modifier, + suggested_result, + suggested_size, + default_modifier, + default_result, + default_size, }, ); } diff --git a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs index 38413cca633c9..d846f4433dc70 100644 --- a/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude_edition_lints.rs @@ -3,19 +3,157 @@ use std::fmt::Write; use hir::def_id::DefId; use hir::{HirId, ItemKind}; use rustc_ast::join_path_idents; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir as hir; use rustc_lint::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER}; use rustc_middle::span_bug; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint::builtin::{RUST_2021_PRELUDE_COLLISIONS, RUST_2024_PRELUDE_COLLISIONS}; -use rustc_span::{Ident, STDLIB_STABLE_CRATES, Span, kw, sym}; +use rustc_span::{Ident, STDLIB_STABLE_CRATES, Span, Symbol, kw, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; use crate::FnCtxt; use crate::method::probe::{self, Pick}; +struct AmbiguousTraitMethodCall<'a, 'b, 'tcx> { + segment_name: Symbol, + self_expr_span: Span, + pick: &'a Pick<'tcx>, + tcx: TyCtxt<'tcx>, + edition: &'b str, +} + +impl<'a, 'b, 'c, 'tcx> Diagnostic<'a, ()> for AmbiguousTraitMethodCall<'b, 'c, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { segment_name, self_expr_span, pick, tcx, edition } = self; + let mut lint = Diag::new( + dcx, + level, + format!("trait method `{}` will become ambiguous in Rust {edition}", segment_name), + ); + let derefs = "*".repeat(pick.autoderefs); + + let autoref = match pick.autoref_or_ptr_adjustment { + Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => mutbl.ref_prefix_str(), + Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", + Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl { + hir::Mutability::Mut => "Pin<&mut ", + hir::Mutability::Not => "Pin<&", + }, + }; + if let Ok(self_expr) = tcx.sess.source_map().span_to_snippet(self_expr_span) { + let mut self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = + pick.autoref_or_ptr_adjustment + { + format!("{derefs}{self_expr} as *const _") + } else { + format!("{autoref}{derefs}{self_expr}") + }; + + if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) = + pick.autoref_or_ptr_adjustment + { + self_adjusted.push('>'); + } + + lint.span_suggestion( + self_expr_span, + "disambiguate the method call", + format!("({self_adjusted})"), + Applicability::MachineApplicable, + ); + } else { + let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = + pick.autoref_or_ptr_adjustment + { + format!("{derefs}(...) as *const _") + } else { + format!("{autoref}{derefs}...") + }; + lint.span_help( + self_expr_span, + format!("disambiguate the method call with `({self_adjusted})`",), + ); + } + lint + } +} + +struct AmbiguousTraitMethod<'a, 'b, 'tcx, 'pcx, 'fnctx> { + segment: &'a hir::PathSegment<'pcx>, + call_expr: &'tcx hir::Expr<'tcx>, + self_expr: &'tcx hir::Expr<'tcx>, + pick: &'a Pick<'tcx>, + args: &'tcx [hir::Expr<'tcx>], + edition: &'b str, + span: Span, + this: &'a FnCtxt<'fnctx, 'tcx>, +} + +impl<'a, 'b, 'c, 'tcx, 'pcx, 'fnctx> Diagnostic<'a, ()> + for AmbiguousTraitMethod<'b, 'c, 'tcx, 'pcx, 'fnctx> +{ + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { segment, call_expr, self_expr, pick, args, edition, span, this } = self; + let mut lint = Diag::new( + dcx, + level, + format!( + "trait method `{}` will become ambiguous in Rust {edition}", + segment.ident.name + ), + ); + + let sp = call_expr.span; + let trait_name = + this.trait_path_or_bare_name(span, call_expr.hir_id, pick.item.container_id(this.tcx)); + + let (self_adjusted, precise) = this.adjust_expr(pick, self_expr, sp); + if precise { + let args = args.iter().fold(String::new(), |mut string, arg| { + let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); + write!(string, ", {}", this.sess().source_map().span_to_snippet(span).unwrap()) + .unwrap(); + string + }); + + lint.span_suggestion( + sp, + "disambiguate the associated function", + format!( + "{}::{}{}({}{})", + trait_name, + segment.ident.name, + if let Some(args) = segment.args.as_ref().and_then(|args| this + .sess() + .source_map() + .span_to_snippet(args.span_ext) + .ok()) + { + // Keep turbofish. + format!("::{args}") + } else { + String::new() + }, + self_adjusted, + args, + ), + Applicability::MachineApplicable, + ); + } else { + lint.span_help( + sp, + format!( + "disambiguate the associated function with `{}::{}(...)`", + trait_name, segment.ident, + ), + ); + } + lint + } +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(super) fn lint_edition_dependent_dot_call( &self, @@ -101,133 +239,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Inherent impls only require not relying on autoref and autoderef in order to // ensure that the trait implementation won't be used - self.tcx.node_span_lint( + self.tcx.emit_node_span_lint( prelude_or_array_lint, self_expr.hir_id, self_expr.span, - |lint| { - lint.primary_message(format!( - "trait method `{}` will become ambiguous in Rust {edition}", - segment.ident.name - )); - - let sp = self_expr.span; - - let derefs = "*".repeat(pick.autoderefs); - - let autoref = match pick.autoref_or_ptr_adjustment { - Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, .. }) => { - mutbl.ref_prefix_str() - } - Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", - Some(probe::AutorefOrPtrAdjustment::ReborrowPin(mutbl)) => match mutbl { - hir::Mutability::Mut => "Pin<&mut ", - hir::Mutability::Not => "Pin<&", - }, - }; - if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) - { - let mut self_adjusted = - if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = - pick.autoref_or_ptr_adjustment - { - format!("{derefs}{self_expr} as *const _") - } else { - format!("{autoref}{derefs}{self_expr}") - }; - - if let Some(probe::AutorefOrPtrAdjustment::ReborrowPin(_)) = - pick.autoref_or_ptr_adjustment - { - self_adjusted.push('>'); - } - - lint.span_suggestion( - sp, - "disambiguate the method call", - format!("({self_adjusted})"), - Applicability::MachineApplicable, - ); - } else { - let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = - pick.autoref_or_ptr_adjustment - { - format!("{derefs}(...) as *const _") - } else { - format!("{autoref}{derefs}...") - }; - lint.span_help( - sp, - format!("disambiguate the method call with `({self_adjusted})`",), - ); - } + AmbiguousTraitMethodCall { + segment_name: segment.ident.name, + self_expr_span: self_expr.span, + pick, + tcx: self.tcx, + edition, }, ); } else { // trait implementations require full disambiguation to not clash with the new prelude // additions (i.e. convert from dot-call to fully-qualified call) - self.tcx.node_span_lint( + self.tcx.emit_node_span_lint( prelude_or_array_lint, call_expr.hir_id, call_expr.span, - |lint| { - lint.primary_message(format!( - "trait method `{}` will become ambiguous in Rust {edition}", - segment.ident.name - )); - - let sp = call_expr.span; - let trait_name = self.trait_path_or_bare_name( - span, - call_expr.hir_id, - pick.item.container_id(self.tcx), - ); - - let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp); - if precise { - let args = args.iter().fold(String::new(), |mut string, arg| { - let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); - write!( - string, - ", {}", - self.sess().source_map().span_to_snippet(span).unwrap() - ) - .unwrap(); - string - }); - - lint.span_suggestion( - sp, - "disambiguate the associated function", - format!( - "{}::{}{}({}{})", - trait_name, - segment.ident.name, - if let Some(args) = segment.args.as_ref().and_then(|args| self - .sess() - .source_map() - .span_to_snippet(args.span_ext) - .ok()) - { - // Keep turbofish. - format!("::{args}") - } else { - String::new() - }, - self_adjusted, - args, - ), - Applicability::MachineApplicable, - ); - } else { - lint.span_help( - sp, - format!( - "disambiguate the associated function with `{}::{}(...)`", - trait_name, segment.ident, - ), - ); - } + AmbiguousTraitMethod { + segment, + call_expr, + self_expr, + pick, + args, + edition, + span, + this: self, }, ); } @@ -242,6 +281,88 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr_id: hir::HirId, pick: &Pick<'tcx>, ) { + struct AmbiguousTraitAssocFunc<'a, 'fnctx, 'tcx> { + method_name: Symbol, + this: &'a FnCtxt<'fnctx, 'tcx>, + pick: &'a Pick<'tcx>, + span: Span, + expr_id: hir::HirId, + self_ty_span: Span, + self_ty: Ty<'tcx>, + } + + impl<'a, 'b, 'fnctx, 'tcx> Diagnostic<'a, ()> for AmbiguousTraitAssocFunc<'b, 'fnctx, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { method_name, this, pick, span, expr_id, self_ty_span, self_ty } = self; + let mut lint = Diag::new( + dcx, + level, + format!( + "trait-associated function `{}` will become ambiguous in Rust 2021", + method_name + ), + ); + + // "type" refers to either a type or, more likely, a trait from which + // the associated function or method is from. + let container_id = pick.item.container_id(this.tcx); + let trait_path = this.trait_path_or_bare_name(span, expr_id, container_id); + let trait_generics = this.tcx.generics_of(container_id); + + let trait_name = + if trait_generics.own_params.len() <= trait_generics.has_self as usize { + trait_path + } else { + let counts = trait_generics.own_counts(); + format!( + "{}<{}>", + trait_path, + std::iter::repeat("'_") + .take(counts.lifetimes) + .chain(std::iter::repeat("_").take( + counts.types + counts.consts - trait_generics.has_self as usize + )) + .collect::>() + .join(", ") + ) + }; + + let mut self_ty_name = self_ty_span + .find_ancestor_inside(span) + .and_then(|span| this.sess().source_map().span_to_snippet(span).ok()) + .unwrap_or_else(|| self_ty.to_string()); + + // Get the number of generics the self type has (if an Adt) unless we can determine that + // the user has written the self type with generics already which we (naively) do by looking + // for a "<" in `self_ty_name`. + if !self_ty_name.contains('<') { + if let ty::Adt(def, _) = self_ty.kind() { + let generics = this.tcx.generics_of(def.did()); + if !generics.is_own_empty() { + let counts = generics.own_counts(); + self_ty_name += &format!( + "<{}>", + std::iter::repeat("'_") + .take(counts.lifetimes) + .chain( + std::iter::repeat("_").take(counts.types + counts.consts) + ) + .collect::>() + .join(", ") + ); + } + } + } + lint.span_suggestion( + span, + "disambiguate the associated function", + format!("<{} as {}>::{}", self_ty_name, trait_name, method_name), + Applicability::MachineApplicable, + ); + lint + } + } + // Rust 2021 and later is already using the new prelude if span.at_least_rust_2021() { return; @@ -278,67 +399,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - self.tcx.node_span_lint(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| { - lint.primary_message(format!( - "trait-associated function `{}` will become ambiguous in Rust 2021", - method_name.name - )); - - // "type" refers to either a type or, more likely, a trait from which - // the associated function or method is from. - let container_id = pick.item.container_id(self.tcx); - let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); - let trait_generics = self.tcx.generics_of(container_id); - - let trait_name = - if trait_generics.own_params.len() <= trait_generics.has_self as usize { - trait_path - } else { - let counts = trait_generics.own_counts(); - format!( - "{}<{}>", - trait_path, - std::iter::repeat("'_") - .take(counts.lifetimes) - .chain(std::iter::repeat("_").take( - counts.types + counts.consts - trait_generics.has_self as usize - )) - .collect::>() - .join(", ") - ) - }; - - let mut self_ty_name = self_ty_span - .find_ancestor_inside(span) - .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) - .unwrap_or_else(|| self_ty.to_string()); - - // Get the number of generics the self type has (if an Adt) unless we can determine that - // the user has written the self type with generics already which we (naively) do by looking - // for a "<" in `self_ty_name`. - if !self_ty_name.contains('<') { - if let ty::Adt(def, _) = self_ty.kind() { - let generics = self.tcx.generics_of(def.did()); - if !generics.is_own_empty() { - let counts = generics.own_counts(); - self_ty_name += &format!( - "<{}>", - std::iter::repeat("'_") - .take(counts.lifetimes) - .chain(std::iter::repeat("_").take(counts.types + counts.consts)) - .collect::>() - .join(", ") - ); - } - } - } - lint.span_suggestion( + self.tcx.emit_node_span_lint( + RUST_2021_PRELUDE_COLLISIONS, + expr_id, + span, + AmbiguousTraitAssocFunc { + method_name: method_name.name, + this: self, + pick, span, - "disambiguate the associated function", - format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), - Applicability::MachineApplicable, - ); - }); + expr_id, + self_ty_span, + self_ty, + }, + ); } fn trait_path_or_bare_name( diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index b6162f2556d36..8d92ee03ff472 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -5,13 +5,14 @@ use std::ops::Deref; use rustc_data_structures::debug_assert_matches; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sso::SsoHashSet; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level}; use rustc_hir::def::DefKind; use rustc_hir::{self as hir, ExprKind, HirId, Node, find_attr}; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query}; +use rustc_macros::Diagnostic; use rustc_middle::middle::stability; use rustc_middle::ty::elaborate::supertrait_def_ids; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; @@ -389,6 +390,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { where OP: FnOnce(ProbeContext<'_, 'tcx>) -> Result>, { + #[derive(Diagnostic)] + #[diag("type annotations needed")] + struct MissingTypeAnnot; + let mut orig_values = OriginalQueryValues::default(); let predefined_opaques_in_body = if self.next_trait_solver() { self.tcx.mk_predefined_opaques_in_body_from_iter( @@ -471,13 +476,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // this case used to be allowed by the compiler, // so we do a future-compat lint here for the 2015 edition // (see https://github.com/rust-lang/rust/issues/46906) - self.tcx.node_span_lint( + self.tcx.emit_node_span_lint( lint::builtin::TYVAR_BEHIND_RAW_POINTER, scope_expr_id, span, - |lint| { - lint.primary_message("type annotations needed"); - }, + MissingTypeAnnot, ); } else { // Ended up encountering a type variable when doing autoderef, @@ -1823,47 +1826,68 @@ impl<'tcx> Pick<'tcx> { span: Span, scope_expr_id: HirId, ) { - if self.unstable_candidates.is_empty() { - return; + struct ItemMaybeBeAddedToStd<'a, 'tcx> { + this: &'a Pick<'tcx>, + tcx: TyCtxt<'tcx>, + span: Span, } - let def_kind = self.item.as_def_kind(); - tcx.node_span_lint(lint::builtin::UNSTABLE_NAME_COLLISIONS, scope_expr_id, span, |lint| { - lint.primary_message(format!( - "{} {} with this name may be added to the standard library in the future", - tcx.def_kind_descr_article(def_kind, self.item.def_id), - tcx.def_kind_descr(def_kind, self.item.def_id), - )); - - match (self.item.kind, self.item.container) { - (ty::AssocKind::Fn { .. }, _) => { - // FIXME: This should be a `span_suggestion` instead of `help` - // However `self.span` only - // highlights the method name, so we can't use it. Also consider reusing - // the code from `report_method_error()`. - lint.help(format!( - "call with fully qualified syntax `{}(...)` to keep using the current \ - method", - tcx.def_path_str(self.item.def_id), - )); - } - (ty::AssocKind::Const { name, .. }, ty::AssocContainer::Trait) => { - let def_id = self.item.container_id(tcx); - lint.span_suggestion( - span, - "use the fully qualified path to the associated const", - format!("<{} as {}>::{}", self.self_ty, tcx.def_path_str(def_id), name), - Applicability::MachineApplicable, - ); + + impl<'a, 'b, 'tcx> Diagnostic<'a, ()> for ItemMaybeBeAddedToStd<'b, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { this, tcx, span } = self; + let def_kind = this.item.as_def_kind(); + let mut lint = Diag::new( + dcx, + level, + format!( + "{} {} with this name may be added to the standard library in the future", + tcx.def_kind_descr_article(def_kind, this.item.def_id), + tcx.def_kind_descr(def_kind, this.item.def_id), + ), + ); + + match (this.item.kind, this.item.container) { + (ty::AssocKind::Fn { .. }, _) => { + // FIXME: This should be a `span_suggestion` instead of `help` + // However `this.span` only + // highlights the method name, so we can't use it. Also consider reusing + // the code from `report_method_error()`. + lint.help(format!( + "call with fully qualified syntax `{}(...)` to keep using the current \ + method", + tcx.def_path_str(this.item.def_id), + )); + } + (ty::AssocKind::Const { name, .. }, ty::AssocContainer::Trait) => { + let def_id = this.item.container_id(tcx); + lint.span_suggestion( + span, + "use the fully qualified path to the associated const", + format!("<{} as {}>::{}", this.self_ty, tcx.def_path_str(def_id), name), + Applicability::MachineApplicable, + ); + } + _ => {} } - _ => {} + tcx.disabled_nightly_features( + &mut lint, + this.unstable_candidates.iter().map(|(candidate, feature)| { + (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature) + }), + ); + lint } - tcx.disabled_nightly_features( - lint, - self.unstable_candidates.iter().map(|(candidate, feature)| { - (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature) - }), - ); - }); + } + + if self.unstable_candidates.is_empty() { + return; + } + tcx.emit_node_span_lint( + lint::builtin::UNSTABLE_NAME_COLLISIONS, + scope_expr_id, + span, + ItemMaybeBeAddedToStd { this: self, tcx, span }, + ); } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index ea4edb3ccf3cd..8f50228d86896 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -7,7 +7,8 @@ use rustc_data_structures::assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err, + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, Level, MultiSpan, pluralize, + struct_span_code_err, }; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -2471,6 +2472,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unmentioned_fields: &[(&ty::FieldDef, Ident)], ty: Ty<'tcx>, ) { + struct FieldsNotListed<'a, 'b, 'tcx> { + pat_span: Span, + unmentioned_fields: &'a [(&'b ty::FieldDef, Ident)], + joined_patterns: String, + ty: Ty<'tcx>, + } + + impl<'a, 'b, 'c, 'tcx> Diagnostic<'a, ()> for FieldsNotListed<'b, 'c, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { pat_span, unmentioned_fields, joined_patterns, ty } = self; + Diag::new(dcx, level, "some fields are not explicitly listed") + .with_span_label(pat_span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)) + .with_help( + "ensure that all fields are mentioned explicitly by adding the suggested fields", + ) + .with_note(format!( + "the pattern is of type `{ty}` and the `non_exhaustive_omitted_patterns` attribute was found", + )) + } + } + fn joined_uncovered_patterns(witnesses: &[&Ident]) -> String { const LIMIT: usize = 3; match witnesses { @@ -2495,16 +2517,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &unmentioned_fields.iter().map(|(_, i)| i).collect::>(), ); - self.tcx.node_span_lint(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |lint| { - lint.primary_message("some fields are not explicitly listed"); - lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)); - lint.help( - "ensure that all fields are mentioned explicitly by adding the suggested fields", - ); - lint.note(format!( - "the pattern is of type `{ty}` and the `non_exhaustive_omitted_patterns` attribute was found", - )); - }); + self.tcx.emit_node_span_lint( + NON_EXHAUSTIVE_OMITTED_PATTERNS, + pat.hir_id, + pat.span, + FieldsNotListed { pat_span: pat.span, unmentioned_fields, joined_patterns, ty }, + ); } /// Returns a diagnostic reporting a struct pattern which does not mention some fields. diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 1f680ca5b09a7..df02974d2fb2d 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -35,7 +35,7 @@ use std::iter; use rustc_abi::FIRST_VARIANT; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::{ExtendUnord, UnordSet}; -use rustc_errors::{Applicability, MultiSpan}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{self as hir, HirId, find_attr}; @@ -964,165 +964,216 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_clause: hir::CaptureBy, span: Span, ) { - let (need_migrations, reasons) = self.compute_2229_migrations( - closure_def_id, - span, - capture_clause, - self.typeck_results.borrow().closure_min_captures.get(&closure_def_id), - ); + struct MigrationLint<'a, 'b, 'tcx> { + closure_def_id: LocalDefId, + this: &'a FnCtxt<'b, 'tcx>, + body_id: hir::BodyId, + need_migrations: Vec, + migration_message: String, + } - if !need_migrations.is_empty() { - let (migration_string, migrated_variables_concat) = - migration_suggestion_for_2229(self.tcx, &need_migrations); + impl<'a, 'b, 'c, 'tcx> Diagnostic<'a, ()> for MigrationLint<'b, 'c, 'tcx> { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { + let Self { closure_def_id, this, body_id, need_migrations, migration_message } = + self; + let mut lint = Diag::new(dcx, level, migration_message); + + let (migration_string, migrated_variables_concat) = + migration_suggestion_for_2229(this.tcx, &need_migrations); + + let closure_hir_id = this.tcx.local_def_id_to_hir_id(closure_def_id); + let closure_head_span = this.tcx.def_span(closure_def_id); + + for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations { + // Labels all the usage of the captured variable and why they are responsible + // for migration being needed + for lint_note in diagnostics_info.iter() { + match &lint_note.captures_info { + UpvarMigrationInfo::CapturingPrecise { + source_expr: Some(capture_expr_id), + var_name: captured_name, + } => { + let cause_span = this.tcx.hir_span(*capture_expr_id); + lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", + this.tcx.hir_name(*var_hir_id), + captured_name, + )); + } + UpvarMigrationInfo::CapturingNothing { use_span } => { + lint.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", + this.tcx.hir_name(*var_hir_id), + )); + } + + _ => {} + } + + // Add a label pointing to where a captured variable affected by drop order + // is dropped + if lint_note.reason.drop_order { + let drop_location_span = drop_location_span(this.tcx, closure_hir_id); - let closure_hir_id = self.tcx.local_def_id_to_hir_id(closure_def_id); - let closure_head_span = self.tcx.def_span(closure_def_id); - self.tcx.node_span_lint( - lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, - closure_hir_id, - closure_head_span, - |lint| { - lint.primary_message(reasons.migration_message()); - - for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations { - // Labels all the usage of the captured variable and why they are responsible - // for migration being needed - for lint_note in diagnostics_info.iter() { match &lint_note.captures_info { - UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => { - let cause_span = self.tcx.hir_span(*capture_expr_id); - lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", - self.tcx.hir_name(*var_hir_id), + UpvarMigrationInfo::CapturingPrecise { + var_name: captured_name, + .. + } => { + lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", + this.tcx.hir_name(*var_hir_id), captured_name, )); } - UpvarMigrationInfo::CapturingNothing { use_span } => { - lint.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", - self.tcx.hir_name(*var_hir_id), + UpvarMigrationInfo::CapturingNothing { use_span: _ } => { + lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", + v = this.tcx.hir_name(*var_hir_id), )); } - - _ => { } } + } - // Add a label pointing to where a captured variable affected by drop order - // is dropped - if lint_note.reason.drop_order { - let drop_location_span = drop_location_span(self.tcx, closure_hir_id); - - match &lint_note.captures_info { - UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", - self.tcx.hir_name(*var_hir_id), - captured_name, - )); - } - UpvarMigrationInfo::CapturingNothing { use_span: _ } => { - lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", - v = self.tcx.hir_name(*var_hir_id), - )); - } + // Add a label explaining why a closure no longer implements a trait + for &missing_trait in &lint_note.reason.auto_traits { + // not capturing something anymore cannot cause a trait to fail to be implemented: + match &lint_note.captures_info { + UpvarMigrationInfo::CapturingPrecise { + var_name: captured_name, + .. + } => { + let var_name = this.tcx.hir_name(*var_hir_id); + lint.span_label( + closure_head_span, + format!( + "\ + in Rust 2018, this closure implements {missing_trait} \ + as `{var_name}` implements {missing_trait}, but in Rust 2021, \ + this closure will no longer implement {missing_trait} \ + because `{var_name}` is not fully captured \ + and `{captured_name}` does not implement {missing_trait}" + ), + ); } - } - // Add a label explaining why a closure no longer implements a trait - for &missing_trait in &lint_note.reason.auto_traits { - // not capturing something anymore cannot cause a trait to fail to be implemented: - match &lint_note.captures_info { - UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - let var_name = self.tcx.hir_name(*var_hir_id); - lint.span_label(closure_head_span, format!("\ - in Rust 2018, this closure implements {missing_trait} \ - as `{var_name}` implements {missing_trait}, but in Rust 2021, \ - this closure will no longer implement {missing_trait} \ - because `{var_name}` is not fully captured \ - and `{captured_name}` does not implement {missing_trait}")); - } - - // Cannot happen: if we don't capture a variable, we impl strictly more traits - UpvarMigrationInfo::CapturingNothing { use_span } => span_bug!(*use_span, "missing trait from not capturing something"), - } + // Cannot happen: if we don't capture a variable, we impl strictly more traits + UpvarMigrationInfo::CapturingNothing { use_span } => span_bug!( + *use_span, + "missing trait from not capturing something" + ), } } } + } - let diagnostic_msg = format!( - "add a dummy let to cause {migrated_variables_concat} to be fully captured" - ); + let diagnostic_msg = format!( + "add a dummy let to cause {migrated_variables_concat} to be fully captured" + ); - let closure_span = self.tcx.hir_span_with_body(closure_hir_id); - let mut closure_body_span = { - // If the body was entirely expanded from a macro - // invocation, i.e. the body is not contained inside the - // closure span, then we walk up the expansion until we - // find the span before the expansion. - let s = self.tcx.hir_span_with_body(body_id.hir_id); - s.find_ancestor_inside(closure_span).unwrap_or(s) - }; + let closure_span = this.tcx.hir_span_with_body(closure_hir_id); + let mut closure_body_span = { + // If the body was entirely expanded from a macro + // invocation, i.e. the body is not contained inside the + // closure span, then we walk up the expansion until we + // find the span before the expansion. + let s = this.tcx.hir_span_with_body(body_id.hir_id); + s.find_ancestor_inside(closure_span).unwrap_or(s) + }; - if let Ok(mut s) = self.tcx.sess.source_map().span_to_snippet(closure_body_span) { - if s.starts_with('$') { - // Looks like a macro fragment. Try to find the real block. - if let hir::Node::Expr(&hir::Expr { - kind: hir::ExprKind::Block(block, ..), .. - }) = self.tcx.hir_node(body_id.hir_id) { - // If the body is a block (with `{..}`), we use the span of that block. - // E.g. with a `|| $body` expanded from a `m!({ .. })`, we use `{ .. }`, and not `$body`. - // Since we know it's a block, we know we can insert the `let _ = ..` without - // breaking the macro syntax. - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(block.span) { - closure_body_span = block.span; - s = snippet; - } + if let Ok(mut s) = this.tcx.sess.source_map().span_to_snippet(closure_body_span) { + if s.starts_with('$') { + // Looks like a macro fragment. Try to find the real block. + if let hir::Node::Expr(&hir::Expr { + kind: hir::ExprKind::Block(block, ..), + .. + }) = this.tcx.hir_node(body_id.hir_id) + { + // If the body is a block (with `{..}`), we use the span of that block. + // E.g. with a `|| $body` expanded from a `m!({ .. })`, we use `{ .. }`, and not `$body`. + // Since we know it's a block, we know we can insert the `let _ = ..` without + // breaking the macro syntax. + if let Ok(snippet) = + this.tcx.sess.source_map().span_to_snippet(block.span) + { + closure_body_span = block.span; + s = snippet; } } + } - let mut lines = s.lines(); - let line1 = lines.next().unwrap_or_default(); - - if line1.trim_end() == "{" { - // This is a multi-line closure with just a `{` on the first line, - // so we put the `let` on its own line. - // We take the indentation from the next non-empty line. - let line2 = lines.find(|line| !line.is_empty()).unwrap_or_default(); - let indent = line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0; - lint.span_suggestion( - closure_body_span.with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())).shrink_to_lo(), - diagnostic_msg, - format!("\n{indent}{migration_string};"), - Applicability::MachineApplicable, - ); - } else if line1.starts_with('{') { - // This is a closure with its body wrapped in - // braces, but with more than just the opening - // brace on the first line. We put the `let` - // directly after the `{`. - lint.span_suggestion( - closure_body_span.with_lo(closure_body_span.lo() + BytePos(1)).shrink_to_lo(), - diagnostic_msg, - format!(" {migration_string};"), - Applicability::MachineApplicable, - ); - } else { - // This is a closure without braces around the body. - // We add braces to add the `let` before the body. - lint.multipart_suggestion( - diagnostic_msg, - vec![ - (closure_body_span.shrink_to_lo(), format!("{{ {migration_string}; ")), - (closure_body_span.shrink_to_hi(), " }".to_string()), - ], - Applicability::MachineApplicable - ); - } - } else { + let mut lines = s.lines(); + let line1 = lines.next().unwrap_or_default(); + + if line1.trim_end() == "{" { + // This is a multi-line closure with just a `{` on the first line, + // so we put the `let` on its own line. + // We take the indentation from the next non-empty line. + let line2 = lines.find(|line| !line.is_empty()).unwrap_or_default(); + let indent = + line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0; lint.span_suggestion( - closure_span, + closure_body_span + .with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())) + .shrink_to_lo(), diagnostic_msg, - migration_string, - Applicability::HasPlaceholders + format!("\n{indent}{migration_string};"), + Applicability::MachineApplicable, + ); + } else if line1.starts_with('{') { + // This is a closure with its body wrapped in + // braces, but with more than just the opening + // brace on the first line. We put the `let` + // directly after the `{`. + lint.span_suggestion( + closure_body_span + .with_lo(closure_body_span.lo() + BytePos(1)) + .shrink_to_lo(), + diagnostic_msg, + format!(" {migration_string};"), + Applicability::MachineApplicable, + ); + } else { + // This is a closure without braces around the body. + // We add braces to add the `let` before the body. + lint.multipart_suggestion( + diagnostic_msg, + vec![ + ( + closure_body_span.shrink_to_lo(), + format!("{{ {migration_string}; "), + ), + (closure_body_span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, ); } + } else { + lint.span_suggestion( + closure_span, + diagnostic_msg, + migration_string, + Applicability::HasPlaceholders, + ); + } + lint + } + } + + let (need_migrations, reasons) = self.compute_2229_migrations( + closure_def_id, + span, + capture_clause, + self.typeck_results.borrow().closure_min_captures.get(&closure_def_id), + ); + + if !need_migrations.is_empty() { + self.tcx.emit_node_span_lint( + lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, + self.tcx.local_def_id_to_hir_id(closure_def_id), + self.tcx.def_span(closure_def_id), + MigrationLint { + this: self, + migration_message: reasons.migration_message(), + closure_def_id, + body_id, + need_migrations, }, ); } diff --git a/tests/ui/inference/inference_unstable.stderr b/tests/ui/inference/inference_unstable.stderr index 0072175d51403..b547293052a2a 100644 --- a/tests/ui/inference/inference_unstable.stderr +++ b/tests/ui/inference/inference_unstable.stderr @@ -4,9 +4,9 @@ warning: a method with this name may be added to the standard library in the fut LL | assert_eq!('x'.ipu_flatten(), 1); | ^^^^^^^^^^^ | + = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_flatten(...)` to keep using the current method = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior! = note: for more information, see issue #48919 - = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_flatten(...)` to keep using the current method = note: `#[warn(unstable_name_collisions)]` (part of `#[warn(future_incompatible)]`) on by default help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_flatten` | @@ -19,9 +19,9 @@ warning: a method with this name may be added to the standard library in the fut LL | assert_eq!('x'.ipu_by_value_vs_by_ref(), 1); | ^^^^^^^^^^^^^^^^^^^^^^ | + = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_by_value_vs_by_ref(...)` to keep using the current method = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior! = note: for more information, see issue #48919 - = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_by_value_vs_by_ref(...)` to keep using the current method help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_by_value_vs_by_ref` | LL + #![feature(ipu_flatten)] @@ -33,9 +33,9 @@ warning: a method with this name may be added to the standard library in the fut LL | assert_eq!('x'.ipu_by_ref_vs_by_ref_mut(), 1); | ^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_by_ref_vs_by_ref_mut(...)` to keep using the current method = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior! = note: for more information, see issue #48919 - = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_by_ref_vs_by_ref_mut(...)` to keep using the current method help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_by_ref_vs_by_ref_mut` | LL + #![feature(ipu_flatten)] @@ -47,9 +47,9 @@ warning: a method with this name may be added to the standard library in the fut LL | assert_eq!((&mut 'x' as *mut char).ipu_by_mut_ptr_vs_by_const_ptr(), 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | + = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_by_mut_ptr_vs_by_const_ptr(...)` to keep using the current method = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior! = note: for more information, see issue #48919 - = help: call with fully qualified syntax `inference_unstable_itertools::IpuItertools::ipu_by_mut_ptr_vs_by_const_ptr(...)` to keep using the current method help: add `#![feature(ipu_flatten)]` to the crate attributes to enable `inference_unstable_iterator::IpuIterator::ipu_by_mut_ptr_vs_by_const_ptr` | LL + #![feature(ipu_flatten)] diff --git a/tests/ui/resolve/slice-as-slice.stderr b/tests/ui/resolve/slice-as-slice.stderr index c93363431c595..f22a0dd6ac58e 100644 --- a/tests/ui/resolve/slice-as-slice.stderr +++ b/tests/ui/resolve/slice-as-slice.stderr @@ -4,9 +4,9 @@ warning: a method with this name may be added to the standard library in the fut LL | b(data.as_slice()); | ^^^^^^^^ | + = help: call with fully qualified syntax `ComponentSlice::as_slice(...)` to keep using the current method = warning: once this associated item is added to the standard library, the ambiguity may cause an error or change in behavior! = note: for more information, see issue #48919 - = help: call with fully qualified syntax `ComponentSlice::as_slice(...)` to keep using the current method = note: `#[warn(unstable_name_collisions)]` (part of `#[warn(future_incompatible)]`) on by default help: add `#![feature(str_as_str)]` to the crate attributes to enable `core::slice::::as_slice` | From 06f959c6e6b956b6c996cfdc844815caf5f99bc1 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 6 Mar 2026 07:10:59 +0530 Subject: [PATCH 44/55] fix clippy on master --- src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs index 205072a6ce0cf..564d9cc24efe8 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs @@ -189,9 +189,9 @@ pub(crate) fn generate_kind_src( } } }); - PUNCT.iter().zip(used_puncts).filter(|(_, used)| !used).for_each(|((punct, _), _)| { + if let Some(punct) = PUNCT.iter().zip(used_puncts).find(|(_, used)| !used) { panic!("Punctuation {punct:?} is not used in grammar"); - }); + } keywords.extend(RESERVED.iter().copied()); keywords.sort(); keywords.dedup(); From 006929f1e6143f304a76320abc2eb37a0f883820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Fri, 6 Mar 2026 07:18:44 +0200 Subject: [PATCH 45/55] Tweak pre-release check for beta --- .../rust-analyzer/crates/project-model/src/cargo_config_file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs index 9c7f109a626c3..9ce88484f781f 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs @@ -164,7 +164,7 @@ pub(crate) fn make_lockfile_copy( major: 1, minor: 95, patch: 0, - pre: semver::Prerelease::new("nightly").unwrap(), + pre: semver::Prerelease::new("beta").unwrap(), build: semver::BuildMetadata::EMPTY, }; From a62c6717ff4da33ac304aaebdbb19547b82a9f60 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 5 Mar 2026 23:39:05 -0800 Subject: [PATCH 46/55] bootstrap.py: fix typo "parallle" --- src/bootstrap/bootstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index db7c7b69cba94..105e41ca45f63 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -696,7 +696,7 @@ def download_toolchain(self): for download_info in tarballs_download_info: download_component(download_info) - # Unpack the tarballs in parallle. + # Unpack the tarballs in parallel. # In Python 2.7, Pool cannot be used as a context manager. pool_size = min(len(tarballs_download_info), get_cpus()) if self.verbose: From 6a950e74b7b891584fcf74354ac3d44731230a2c Mon Sep 17 00:00:00 2001 From: Florian Date: Fri, 27 Feb 2026 15:34:24 +0545 Subject: [PATCH 47/55] add wasm64 to sync::Once and thread_parking atomics cfg guard Update library/std/src/sys/sync/once/mod.rs Update library/std/src/sys/sync/thread_parking/mod.rs Co-Authored-By: Taiki Endo --- library/std/src/sys/sync/once/mod.rs | 2 +- library/std/src/sys/sync/thread_parking/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/sync/once/mod.rs b/library/std/src/sys/sync/once/mod.rs index aeea884b9f617..5796c6d207dba 100644 --- a/library/std/src/sys/sync/once/mod.rs +++ b/library/std/src/sys/sync/once/mod.rs @@ -12,7 +12,7 @@ cfg_select! { all(target_os = "windows", not(target_vendor="win7")), target_os = "linux", target_os = "android", - all(target_arch = "wasm32", target_feature = "atomics"), + all(target_family = "wasm", target_feature = "atomics"), target_os = "freebsd", target_os = "motor", target_os = "openbsd", diff --git a/library/std/src/sys/sync/thread_parking/mod.rs b/library/std/src/sys/sync/thread_parking/mod.rs index 74b5b72b19a75..9d5a0a996b115 100644 --- a/library/std/src/sys/sync/thread_parking/mod.rs +++ b/library/std/src/sys/sync/thread_parking/mod.rs @@ -3,7 +3,7 @@ cfg_select! { all(target_os = "windows", not(target_vendor = "win7")), target_os = "linux", target_os = "android", - all(target_arch = "wasm32", target_feature = "atomics"), + all(target_family = "wasm", target_feature = "atomics"), target_os = "freebsd", target_os = "openbsd", target_os = "dragonfly", From cb66c855559bb175013cc076f9533ac4985fe702 Mon Sep 17 00:00:00 2001 From: "Andrew V. Teylu" Date: Fri, 6 Mar 2026 10:08:22 +0000 Subject: [PATCH 48/55] Preserve parentheses around Fn trait bounds in pretty printer The AST pretty printer was dropping parentheses around `Fn` trait bounds in `dyn`/`impl` types when additional `+` bounds were present. For example: dyn (FnMut(&mut T) -> &mut dyn ResourceLimiter) + Send + Sync was pretty-printed as: dyn FnMut(&mut T) -> &mut dyn ResourceLimiter + Send + Sync Without parens, `+ Send + Sync` binds to the inner `dyn ResourceLimiter` instead of the outer type, producing invalid Rust. The parser already tracks parentheses via `PolyTraitRef.parens`, but `print_poly_trait_ref` never checked this field. This adds `popen()` and `pclose()` calls when `parens == Parens::Yes`. Signed-off-by: Andrew V. Teylu --- compiler/rustc_ast_pretty/src/pprust/state.rs | 8 +++++++- tests/pretty/paren-trait-bound.rs | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/pretty/paren-trait-bound.rs diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 1ca0715606181..c85d6f454321e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1415,6 +1415,9 @@ impl<'a> State<'a> { } fn print_poly_trait_ref(&mut self, t: &ast::PolyTraitRef) { + if let ast::Parens::Yes = t.parens { + self.popen(); + } self.print_formal_generic_params(&t.bound_generic_params); let ast::TraitBoundModifiers { constness, asyncness, polarity } = t.modifiers; @@ -1437,7 +1440,10 @@ impl<'a> State<'a> { } } - self.print_trait_ref(&t.trait_ref) + self.print_trait_ref(&t.trait_ref); + if let ast::Parens::Yes = t.parens { + self.pclose(); + } } fn print_stmt(&mut self, st: &ast::Stmt) { diff --git a/tests/pretty/paren-trait-bound.rs b/tests/pretty/paren-trait-bound.rs new file mode 100644 index 0000000000000..98e75308abfe8 --- /dev/null +++ b/tests/pretty/paren-trait-bound.rs @@ -0,0 +1,11 @@ +//@ pp-exact + +trait Dummy {} + +// Without parens, `+ Send` would bind to `dyn Dummy` instead of the outer `dyn`. +fn f1(_: Box Box) + Send>) {} + +// Without parens, `+ Send + Sync` would bind to `dyn Dummy` instead of the outer `impl`. +fn f2(_: impl (FnMut(&mut u8) -> &mut dyn Dummy) + Send + Sync) {} + +fn main() {} From 0fd3ac4c97f88ebf8e861baf58002154bb77ee69 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 6 Mar 2026 11:32:31 +0100 Subject: [PATCH 49/55] libcore float tests: replace macro shadowing by const-compatible macro --- .../0029-sysroot_tests-disable-f16-math.patch | 26 +- library/coretests/tests/floats/mod.rs | 266 +++++++----------- library/coretests/tests/lib.rs | 2 + 3 files changed, 120 insertions(+), 174 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch index 6a0244cfde3fb..340f6cc9b0e7c 100644 --- a/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch +++ b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch @@ -19,7 +19,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1557,7 +1557,7 @@ fn s_nan() -> Float { name: exp, attrs: { @@ -28,7 +28,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1578,7 +1578,7 @@ fn s_nan() -> Float { name: exp2, attrs: { @@ -37,7 +37,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1598,7 +1598,7 @@ fn s_nan() -> Float { name: ln, attrs: { @@ -46,7 +46,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1620,7 +1620,7 @@ fn s_nan() -> Float { name: log, attrs: { @@ -55,7 +55,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1645,7 +1645,7 @@ fn s_nan() -> Float { name: log2, attrs: { @@ -64,7 +64,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1668,7 +1668,7 @@ fn s_nan() -> Float { name: log10, attrs: { @@ -73,7 +73,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1692,7 +1692,7 @@ fn s_nan() -> Float { name: asinh, attrs: { @@ -82,7 +82,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1725,7 +1725,7 @@ fn s_nan() -> Float { name: acosh, attrs: { @@ -91,7 +91,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1753,7 +1753,7 @@ fn s_nan() -> Float { name: atanh, attrs: { @@ -100,7 +100,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1779,7 +1779,7 @@ fn s_nan() -> Float { name: gamma, attrs: { @@ -109,7 +109,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -1814,7 +1814,7 @@ fn s_nan() -> Float { name: ln_gamma, attrs: { @@ -118,7 +118,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { @@ -2027,7 +2027,7 @@ fn s_nan() -> Float { attrs: { // FIXME(f16_f128): add math tests when available @@ -127,7 +127,7 @@ index c61961f8584..d7b4fa20322 100644 + f16: #[cfg(false)], // FIXME(rust-lang/rustc_codegen_cranelift#1622) f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { -- 2.50.1 diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs index b729cdf8458d7..3e39528dfbf43 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/floats/mod.rs @@ -218,31 +218,28 @@ const fn lim_for_ty(_x: T) -> T { // We have runtime ("rt") and const versions of these macros. /// Verify that floats are within a tolerance of each other. -macro_rules! assert_approx_eq_rt { - ($a:expr, $b:expr) => {{ assert_approx_eq_rt!($a, $b, $crate::floats::lim_for_ty($a)) }}; +macro_rules! assert_approx_eq { + ($a:expr, $b:expr $(,)?) => {{ assert_approx_eq!($a, $b, $crate::floats::lim_for_ty($a)) }}; ($a:expr, $b:expr, $lim:expr) => {{ let (a, b) = (&$a, &$b); let diff = (*a - *b).abs(); - assert!( + core::panic::const_assert!( diff <= $lim, + "actual value is not approximately equal to expected value", "{a:?} is not approximately equal to {b:?} (threshold {lim:?}, difference {diff:?})", - lim = $lim + // We rely on the `Float` type being brought in scope by the macros below. + a: Float = *a, + b: Float = *b, + diff: Float = diff, + lim: Float = $lim, ); }}; } -macro_rules! assert_approx_eq_const { - ($a:expr, $b:expr) => {{ assert_approx_eq_const!($a, $b, $crate::floats::lim_for_ty($a)) }}; - ($a:expr, $b:expr, $lim:expr) => {{ - let (a, b) = (&$a, &$b); - let diff = (*a - *b).abs(); - assert!(diff <= $lim); - }}; -} /// Verify that floats have the same bitwise representation. Used to avoid the default `0.0 == -0.0` /// behavior, as well as to ensure exact NaN bitpatterns. -macro_rules! assert_biteq_rt { - (@inner $left:expr, $right:expr, $msg_sep:literal, $($tt:tt)*) => {{ +macro_rules! assert_biteq { + ($left:expr, $right:expr $(,)?) => {{ let l = $left; let r = $right; @@ -252,63 +249,21 @@ macro_rules! assert_biteq_rt { // Hack to get the width from a value let bits = (l.to_bits() - l.to_bits()).leading_zeros(); - assert!( + core::panic::const_assert!( l.to_bits() == r.to_bits(), - "{msg}{nl}l: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})", - msg = format_args!($($tt)*), - nl = $msg_sep, - lb = l.to_bits(), - rb = r.to_bits(), - width = ((bits / 4) + 2) as usize, + "actual value and expected value are not bit-equal", + "actual value and expected value are not bit-equal\n\ + l: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})", + // We rely on the `Float` type being brought in scope by the macros below. + l: Float = l, + r: Float = r, + lb: ::Int = l.to_bits(), + rb: ::Int = r.to_bits(), + width: usize = ((bits / 4) + 2) as usize, ); - - if !l.is_nan() && !r.is_nan() { - // Also check that standard equality holds, since most tests use `assert_biteq` rather - // than `assert_eq`. - assert_eq!(l, r); - } - }}; - ($left:expr, $right:expr , $($tt:tt)*) => { - assert_biteq_rt!(@inner $left, $right, "\n", $($tt)*) - }; - ($left:expr, $right:expr $(,)?) => { - assert_biteq_rt!(@inner $left, $right, "", "") - }; -} -macro_rules! assert_biteq_const { - (@inner $left:expr, $right:expr, $msg_sep:literal, $($tt:tt)*) => {{ - let l = $left; - let r = $right; - - // Hack to coerce left and right to the same type - let mut _eq_ty = l; - _eq_ty = r; - - assert!(l.to_bits() == r.to_bits()); - - if !l.is_nan() && !r.is_nan() { - // Also check that standard equality holds, since most tests use `assert_biteq` rather - // than `assert_eq`. - assert!(l == r); - } }}; - ($left:expr, $right:expr , $($tt:tt)*) => { - assert_biteq_const!(@inner $left, $right, "\n", $($tt)*) - }; - ($left:expr, $right:expr $(,)?) => { - assert_biteq_const!(@inner $left, $right, "", "") - }; } -// Use the runtime version by default. -// This way, they can be shadowed by the const versions. -pub(crate) use {assert_approx_eq_rt as assert_approx_eq, assert_biteq_rt as assert_biteq}; - -// Also make the const version available for re-exports. -#[rustfmt::skip] -pub(crate) use assert_biteq_const; -pub(crate) use assert_approx_eq_const; - /// Generate float tests for all our float types, for compile-time and run-time behavior. /// /// By default all tests run for all float types. Configuration can be applied via `attrs`. @@ -341,7 +296,7 @@ macro_rules! float_test { $(f128: #[ $($f128_meta:meta),+ ] ,)? $(const f128: #[ $($f128_const_meta:meta),+ ] ,)? }, - test<$fty:ident> $test:block + test $test:block ) => { mod $name { use super::*; @@ -351,9 +306,9 @@ macro_rules! float_test { fn test_f16() { #[allow(unused_imports)] use core::f16::consts; - type $fty = f16; + type Float = f16; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } $test } @@ -362,9 +317,9 @@ macro_rules! float_test { fn test_f32() { #[allow(unused_imports)] use core::f32::consts; - type $fty = f32; + type Float = f32; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } $test } @@ -373,9 +328,9 @@ macro_rules! float_test { fn test_f64() { #[allow(unused_imports)] use core::f64::consts; - type $fty = f64; + type Float = f64; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } $test } @@ -384,35 +339,24 @@ macro_rules! float_test { fn test_f128() { #[allow(unused_imports)] use core::f128::consts; - type $fty = f128; + type Float = f128; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } $test } $( $( #[$const_meta] )+ )? mod const_ { - #[allow(unused)] - use super::TestableFloat; - #[allow(unused)] - use std::num::FpCategory as Fp; - #[allow(unused)] - use std::ops::{Add, Div, Mul, Rem, Sub}; - // Shadow the runtime versions of the macro with const-compatible versions. - #[allow(unused)] - use $crate::floats::{ - assert_approx_eq_const as assert_approx_eq, - assert_biteq_const as assert_biteq, - }; + use super::*; #[test] $( $( #[$f16_const_meta] )+ )? fn test_f16() { #[allow(unused_imports)] use core::f16::consts; - type $fty = f16; + type Float = f16; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } const { $test } } @@ -421,9 +365,9 @@ macro_rules! float_test { fn test_f32() { #[allow(unused_imports)] use core::f32::consts; - type $fty = f32; + type Float = f32; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } const { $test } } @@ -432,9 +376,9 @@ macro_rules! float_test { fn test_f64() { #[allow(unused_imports)] use core::f64::consts; - type $fty = f64; + type Float = f64; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } const { $test } } @@ -443,9 +387,9 @@ macro_rules! float_test { fn test_f128() { #[allow(unused_imports)] use core::f128::consts; - type $fty = f128; + type Float = f128; #[allow(unused)] - const fn flt (x: $fty) -> $fty { x } + const fn flt (x: Float) -> Float { x } const { $test } } } @@ -459,7 +403,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let two: Float = 2.0; let ten: Float = 10.0; assert_biteq!(ten.add(two), ten + two); @@ -477,7 +421,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { let two: Float = 2.0; let ten: Float = 10.0; assert_biteq!(ten.rem(two), ten % two); @@ -490,7 +434,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; assert!(nan.is_nan()); assert!(!nan.is_infinite()); @@ -510,7 +454,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let inf: Float = Float::INFINITY; assert!(inf.is_infinite()); assert!(!inf.is_finite()); @@ -528,7 +472,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let neg_inf: Float = Float::NEG_INFINITY; assert!(neg_inf.is_infinite()); assert!(!neg_inf.is_finite()); @@ -546,7 +490,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(0.0, Float::ZERO); assert!(!Float::ZERO.is_infinite()); assert!(Float::ZERO.is_finite()); @@ -564,7 +508,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let neg_zero: Float = -0.0; assert!(0.0 == neg_zero); assert_biteq!(-0.0, neg_zero); @@ -584,7 +528,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(1.0, Float::ONE); assert!(!Float::ONE.is_infinite()); assert!(Float::ONE.is_finite()); @@ -602,7 +546,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -623,7 +567,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -644,7 +588,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -665,7 +609,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -686,7 +630,7 @@ float_test! { attrs: { f16: #[cfg(any(miri, target_has_reliable_f16))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -708,7 +652,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(0.0).min(0.0), 0.0); assert_biteq!(flt(-0.0).min(-0.0), -0.0); assert_biteq!(flt(9.0).min(9.0), 9.0); @@ -738,7 +682,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(0.0).max(0.0), 0.0); assert_biteq!(flt(-0.0).max(-0.0), -0.0); assert_biteq!(flt(9.0).max(9.0), 9.0); @@ -769,7 +713,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(0.0).minimum(0.0), 0.0); assert_biteq!(flt(-0.0).minimum(0.0), -0.0); assert_biteq!(flt(-0.0).minimum(-0.0), -0.0); @@ -800,7 +744,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(0.0).maximum(0.0), 0.0); assert_biteq!(flt(-0.0).maximum(0.0), 0.0); assert_biteq!(flt(-0.0).maximum(-0.0), -0.0); @@ -832,7 +776,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(0.5).midpoint(0.5), 0.5); assert_biteq!(flt(0.5).midpoint(2.5), 1.5); assert_biteq!(flt(3.0).midpoint(4.0), 3.5); @@ -885,7 +829,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { // test if large differences in magnitude are still correctly computed. // NOTE: that because of how small x and y are, x + y can never overflow // so (x + y) / 2.0 is always correct @@ -915,7 +859,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(Float::INFINITY.abs(), Float::INFINITY); assert_biteq!(Float::ONE.abs(), Float::ONE); assert_biteq!(Float::ZERO.abs(), Float::ZERO); @@ -933,7 +877,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(1.0).copysign(-2.0), -1.0); assert_biteq!(flt(-1.0).copysign(2.0), 1.0); assert_biteq!(Float::INFINITY.copysign(-0.0), Float::NEG_INFINITY); @@ -948,7 +892,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert!(Float::INFINITY.rem_euclid(42.0).is_nan()); assert_biteq!(flt(42.0).rem_euclid(Float::INFINITY), 42.0); assert!(flt(42.0).rem_euclid(Float::NAN).is_nan()); @@ -965,7 +909,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(42.0).div_euclid(Float::INFINITY), 0.0); assert!(flt(42.0).div_euclid(Float::NAN).is_nan()); assert!(Float::INFINITY.div_euclid(Float::INFINITY).is_nan()); @@ -980,7 +924,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(1.0).floor(), 1.0); assert_biteq!(flt(1.3).floor(), 1.0); assert_biteq!(flt(1.5).floor(), 1.0); @@ -1009,7 +953,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(1.0).ceil(), 1.0); assert_biteq!(flt(1.3).ceil(), 2.0); assert_biteq!(flt(1.5).ceil(), 2.0); @@ -1038,7 +982,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(2.5).round(), 3.0); assert_biteq!(flt(1.0).round(), 1.0); assert_biteq!(flt(1.3).round(), 1.0); @@ -1068,7 +1012,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(2.5).round_ties_even(), 2.0); assert_biteq!(flt(1.0).round_ties_even(), 1.0); assert_biteq!(flt(1.3).round_ties_even(), 1.0); @@ -1098,7 +1042,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(1.0).trunc(), 1.0); assert_biteq!(flt(1.3).trunc(), 1.0); assert_biteq!(flt(1.5).trunc(), 1.0); @@ -1127,7 +1071,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(1.0).fract(), 0.0); assert_approx_eq!(flt(1.3).fract(), 0.3); // rounding differs between float types assert_biteq!(flt(1.5).fract(), 0.5); @@ -1158,7 +1102,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(Float::INFINITY.signum(), Float::ONE); assert_biteq!(Float::ONE.signum(), Float::ONE); assert_biteq!(Float::ZERO.signum(), Float::ONE); @@ -1176,7 +1120,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert!(Float::INFINITY.is_sign_positive()); assert!(Float::ONE.is_sign_positive()); assert!(Float::ZERO.is_sign_positive()); @@ -1195,7 +1139,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert!(!Float::INFINITY.is_sign_negative()); assert!(!Float::ONE.is_sign_negative()); assert!(!Float::ZERO.is_sign_negative()); @@ -1214,7 +1158,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(Float::NEG_INFINITY.next_up(), Float::MIN); assert_biteq!(Float::MIN.next_up(), -Float::MAX_DOWN); assert_biteq!((-Float::ONE - Float::EPSILON).next_up(), -Float::ONE); @@ -1245,7 +1189,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(Float::NEG_INFINITY.next_down(), Float::NEG_INFINITY); assert_biteq!(Float::MIN.next_down(), Float::NEG_INFINITY); assert_biteq!((-Float::MAX_DOWN).next_down(), Float::MIN); @@ -1278,7 +1222,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { assert!(Float::NAN.sqrt().is_nan()); assert!(Float::NEG_INFINITY.sqrt().is_nan()); assert!((-Float::ONE).sqrt().is_nan()); @@ -1298,7 +1242,7 @@ float_test! { f64: #[should_panic], f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let _ = Float::ONE.clamp(3.0, 1.0); } } @@ -1312,7 +1256,7 @@ float_test! { f64: #[should_panic], f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let _ = Float::ONE.clamp(Float::NAN, 1.0); } } @@ -1326,7 +1270,7 @@ float_test! { f64: #[should_panic], f128: #[should_panic, cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let _ = Float::ONE.clamp(3.0, Float::NAN); } } @@ -1337,7 +1281,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { use core::cmp::Ordering; const fn quiet_bit_mask() -> ::Int { @@ -1442,7 +1386,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { use core::cmp::Ordering; fn quiet_bit_mask() -> ::Int { @@ -1498,7 +1442,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16_math))], f128: #[cfg(any(miri, target_has_reliable_f128_math))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -1522,7 +1466,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -1543,7 +1487,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -1566,7 +1510,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(1.0, flt(0.0).exp()); assert_approx_eq!(consts::E, flt(1.0).exp(), Float::EXP_APPROX); assert_approx_eq!(148.41315910257660342111558004055227962348775, flt(5.0).exp(), Float::EXP_APPROX); @@ -1587,7 +1531,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { assert_approx_eq!(32.0, flt(5.0).exp2(), Float::EXP_APPROX); assert_biteq!(1.0, flt(0.0).exp2()); @@ -1607,7 +1551,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -1629,7 +1573,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -1654,7 +1598,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -1677,7 +1621,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -1701,7 +1645,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(0.0).asinh(), 0.0); assert_biteq!(flt(-0.0).asinh(), -0.0); @@ -1734,7 +1678,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(1.0).acosh(), 0.0); assert!(flt(0.999).acosh().is_nan()); @@ -1762,7 +1706,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { assert_biteq!(flt(0.0).atanh(), 0.0); assert_biteq!(flt(-0.0).atanh(), -0.0); @@ -1788,7 +1732,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { assert_approx_eq!(flt(1.0).gamma(), 1.0, Float::GAMMA_APPROX); assert_approx_eq!(flt(2.0).gamma(), 1.0, Float::GAMMA_APPROX); assert_approx_eq!(flt(3.0).gamma(), 2.0, Float::GAMMA_APPROX); @@ -1823,7 +1767,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { assert_approx_eq!(flt(1.0).ln_gamma().0, 0.0, Float::LNGAMMA_APPROX); assert_eq!(flt(1.0).ln_gamma().1, 1); assert_approx_eq!(flt(2.0).ln_gamma().0, 0.0, Float::LNGAMMA_APPROX); @@ -1841,7 +1785,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let pi: Float = consts::PI; let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; @@ -1862,7 +1806,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let pi: Float = consts::PI; let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; @@ -1883,7 +1827,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let a: Float = flt(123.0); let b: Float = flt(456.0); @@ -1907,7 +1851,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(flt(1.0), Float::RAW_1); assert_biteq!(flt(12.5), Float::RAW_12_DOT_5); assert_biteq!(flt(1337.0), Float::RAW_1337); @@ -1937,7 +1881,7 @@ float_test! { f64: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { let nan: Float = Float::NAN; let inf: Float = Float::INFINITY; let neg_inf: Float = Float::NEG_INFINITY; @@ -1959,7 +1903,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(Float::from(false), Float::ZERO); assert_biteq!(Float::from(true), Float::ONE); @@ -1980,7 +1924,7 @@ float_test! { const f16: #[cfg(false)], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(Float::from(u16::MIN), Float::ZERO); assert_biteq!(Float::from(42_u16), 42.0); assert_biteq!(Float::from(u16::MAX), 65535.0); @@ -1999,7 +1943,7 @@ float_test! { const f32: #[cfg(false)], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { assert_biteq!(Float::from(u32::MIN), Float::ZERO); assert_biteq!(Float::from(42_u32), 42.0); assert_biteq!(Float::from(u32::MAX), 4294967295.0); @@ -2016,7 +1960,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { // The maximum integer that converts to a unique floating point // value. const MAX_EXACT_INTEGER: ::SInt = Float::MAX_EXACT_INTEGER; @@ -2058,7 +2002,7 @@ float_test! { f16: #[cfg(any(miri, target_has_reliable_f16))], f128: #[cfg(any(miri, target_has_reliable_f128))], }, - test { + test { // The minimum integer that converts to a unique floating point // value. const MIN_EXACT_INTEGER: ::SInt = Float::MIN_EXACT_INTEGER; @@ -2105,7 +2049,7 @@ float_test! { // f64: #[cfg(false)], // f128: #[cfg(any(miri, target_has_reliable_f128))], // }, -// test { +// test { // assert_biteq!(Float::from(u64::MIN), Float::ZERO); // assert_biteq!(Float::from(42_u64), 42.0); // assert_biteq!(Float::from(u64::MAX), 18446744073709551615.0); @@ -2123,7 +2067,7 @@ float_test! { f16: #[cfg(all(not(miri), target_has_reliable_f16_math))], f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, - test { + test { let pi: Float = consts::PI; assert_approx_eq!(consts::FRAC_PI_2, pi / 2.0); assert_approx_eq!(consts::FRAC_PI_3, pi / 3.0, Float::APPROX); diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 5e303a3c6b790..d04b9a2d6e136 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -90,11 +90,13 @@ #![feature(nonzero_from_str_radix)] #![feature(numfmt)] #![feature(one_sided_range)] +#![feature(panic_internals)] #![feature(pattern)] #![feature(pointer_is_aligned_to)] #![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(result_option_map_or_default)] +#![feature(rustc_attrs)] #![feature(signed_bigint_helpers)] #![feature(slice_from_ptr_range)] #![feature(slice_index_methods)] From 1190845a3dd6b44f14bad12431a10e57dedc21bc Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Tue, 3 Feb 2026 20:45:16 +0900 Subject: [PATCH 50/55] Do not emit ConstEvaluatable goals if type-const --- .../src/collect/predicates_of.rs | 5 +++ .../rustc_trait_selection/src/traits/wf.rs | 25 +++++++------ .../type-const-ice-issue-151631.rs | 18 +++++++++ .../type-const-ice-issue-151631.stderr | 37 +++++++++++++++++++ 4 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 tests/ui/const-generics/type-const-ice-issue-151631.rs create mode 100644 tests/ui/const-generics/type-const-ice-issue-151631.stderr diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 777610402893b..c3d8865817830 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -438,6 +438,11 @@ fn const_evaluatable_predicates_of<'tcx>( return; } + // Skip type consts as mGCA doesn't support evaluatable clauses. + if self.tcx.is_type_const(uv.def) { + return; + } + let span = self.tcx.def_span(uv.def); self.preds.insert((ty::ClauseKind::ConstEvaluatable(c).upcast(self.tcx), span)); } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 52f86be310fb9..4a0a99ea27640 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1016,17 +1016,20 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { match c.kind() { ty::ConstKind::Unevaluated(uv) => { if !c.has_escaping_bound_vars() { - let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( - ty::ClauseKind::ConstEvaluatable(c), - )); - let cause = self.cause(ObligationCauseCode::WellFormed(None)); - self.out.push(traits::Obligation::with_depth( - tcx, - cause, - self.recursion_depth, - self.param_env, - predicate, - )); + // Skip type consts as mGCA doesn't support evaluatable clauses + if !tcx.is_type_const(uv.def) { + let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstEvaluatable(c), + )); + let cause = self.cause(ObligationCauseCode::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + tcx, + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } if matches!(tcx.def_kind(uv.def), DefKind::AssocConst { .. }) && tcx.def_kind(tcx.parent(uv.def)) == (DefKind::Impl { of_trait: false }) diff --git a/tests/ui/const-generics/type-const-ice-issue-151631.rs b/tests/ui/const-generics/type-const-ice-issue-151631.rs new file mode 100644 index 0000000000000..53c6d0c6dd6d4 --- /dev/null +++ b/tests/ui/const-generics/type-const-ice-issue-151631.rs @@ -0,0 +1,18 @@ +// issue: +//@ compile-flags: -Znext-solver +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait SuperTrait {} +trait Trait: SuperTrait { + type const K: u32; +} +impl Trait for () { //~ ERROR: the trait bound `(): SuperTrait` is not satisfied + type const K: u32 = const { 1 }; +} + +fn check(_: impl Trait) {} + +fn main() { + check(()); //~ ERROR: the trait bound `(): SuperTrait` is not satisfied +} diff --git a/tests/ui/const-generics/type-const-ice-issue-151631.stderr b/tests/ui/const-generics/type-const-ice-issue-151631.stderr new file mode 100644 index 0000000000000..6f287293e27cf --- /dev/null +++ b/tests/ui/const-generics/type-const-ice-issue-151631.stderr @@ -0,0 +1,37 @@ +error[E0277]: the trait bound `(): SuperTrait` is not satisfied + --> $DIR/type-const-ice-issue-151631.rs:10:16 + | +LL | impl Trait for () { + | ^^ the trait `SuperTrait` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/type-const-ice-issue-151631.rs:6:1 + | +LL | trait SuperTrait {} + | ^^^^^^^^^^^^^^^^ +note: required by a bound in `Trait` + --> $DIR/type-const-ice-issue-151631.rs:7:14 + | +LL | trait Trait: SuperTrait { + | ^^^^^^^^^^ required by this bound in `Trait` + +error[E0277]: the trait bound `(): SuperTrait` is not satisfied + --> $DIR/type-const-ice-issue-151631.rs:17:5 + | +LL | check(()); + | ^^^^^^^^^ the trait `SuperTrait` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/type-const-ice-issue-151631.rs:6:1 + | +LL | trait SuperTrait {} + | ^^^^^^^^^^^^^^^^ +note: required by a bound in `Trait` + --> $DIR/type-const-ice-issue-151631.rs:7:14 + | +LL | trait Trait: SuperTrait { + | ^^^^^^^^^^ required by this bound in `Trait` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From ca2178dd9fe7f036537414def4593258ca4adb7b Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 6 Mar 2026 22:19:11 +0900 Subject: [PATCH 51/55] add UI regression test for offset_of! recovery --- tests/ui/offset-of/offset-of-error-recovery.rs | 14 ++++++++++++++ tests/ui/offset-of/offset-of-error-recovery.stderr | 8 ++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/ui/offset-of/offset-of-error-recovery.rs create mode 100644 tests/ui/offset-of/offset-of-error-recovery.stderr diff --git a/tests/ui/offset-of/offset-of-error-recovery.rs b/tests/ui/offset-of/offset-of-error-recovery.rs new file mode 100644 index 0000000000000..659140877cd14 --- /dev/null +++ b/tests/ui/offset-of/offset-of-error-recovery.rs @@ -0,0 +1,14 @@ +use std::mem::offset_of; + +struct S { + x: (), +} + +impl S { + fn a() { + offset_of!(Self, Self::x); + //~^ ERROR offset_of expects dot-separated field and variant names + } +} + +fn main() {} diff --git a/tests/ui/offset-of/offset-of-error-recovery.stderr b/tests/ui/offset-of/offset-of-error-recovery.stderr new file mode 100644 index 0000000000000..fce3616c213ec --- /dev/null +++ b/tests/ui/offset-of/offset-of-error-recovery.stderr @@ -0,0 +1,8 @@ +error: offset_of expects dot-separated field and variant names + --> $DIR/offset-of-error-recovery.rs:9:26 + | +LL | offset_of!(Self, Self::x); + | ^^^^^^^ + +error: aborting due to 1 previous error + From ed12fe81b07cf7c2371306a3400bccc9e2b117e8 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 6 Mar 2026 22:19:23 +0900 Subject: [PATCH 52/55] fix ICE in offset_of! error recovery --- compiler/rustc_mir_build/src/thir/cx/expr.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 02deff6474080..8cc5cda59ab0b 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -806,6 +806,10 @@ impl<'tcx> ThirBuildCx<'tcx> { lit: ScalarInt::try_from_uint(val, Size::from_bits(32)).unwrap(), user_ty: None, }; + let mk_usize_kind = |val: u64| ExprKind::NonHirLiteral { + lit: ScalarInt::try_from_uint(val, tcx.data_layout.pointer_size()).unwrap(), + user_ty: None, + }; let mk_call = |thir: &mut Thir<'tcx>, ty: Ty<'tcx>, variant: VariantIdx, field: FieldIdx| { let fun_ty = @@ -842,7 +846,7 @@ impl<'tcx> ThirBuildCx<'tcx> { }); } - expr.unwrap_or(mk_u32_kind(0)) + expr.unwrap_or_else(|| mk_usize_kind(0)) } hir::ExprKind::ConstBlock(ref anon_const) => { From e62f17cfc4c3fa13fef99b90df8e1c87d2ea8f89 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 6 Mar 2026 22:31:01 +0900 Subject: [PATCH 53/55] use try_from_target_usize instead of try_from_uint --- compiler/rustc_mir_build/src/thir/cx/expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 8cc5cda59ab0b..c5eeb8b1aa856 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -807,7 +807,7 @@ impl<'tcx> ThirBuildCx<'tcx> { user_ty: None, }; let mk_usize_kind = |val: u64| ExprKind::NonHirLiteral { - lit: ScalarInt::try_from_uint(val, tcx.data_layout.pointer_size()).unwrap(), + lit: ScalarInt::try_from_target_usize(val, tcx).unwrap(), user_ty: None, }; let mk_call = From 8d96e603b1c3e65bba9e944616de6260e7e40781 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 6 Mar 2026 15:15:43 +0100 Subject: [PATCH 54/55] Preserve the `DiagLocation` in `diag_lint_level` --- compiler/rustc_errors/src/diagnostic.rs | 6 +++--- compiler/rustc_errors/src/lib.rs | 4 ++-- compiler/rustc_middle/src/lint.rs | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index f7fdbfa0cd57e..a093fede8c198 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -140,7 +140,7 @@ where } #[derive(Clone, Debug, Encodable, Decodable)] -pub(crate) struct DiagLocation { +pub struct DiagLocation { file: Cow<'static, str>, line: u32, col: u32, @@ -148,7 +148,7 @@ pub(crate) struct DiagLocation { impl DiagLocation { #[track_caller] - fn caller() -> Self { + pub fn caller() -> Self { let loc = panic::Location::caller(); DiagLocation { file: loc.file().into(), line: loc.line(), col: loc.column() } } @@ -249,7 +249,7 @@ pub struct DiagInner { pub long_ty_path: Option, /// With `-Ztrack_diagnostics` enabled, /// we print where in rustc this error was emitted. - pub(crate) emitted_at: DiagLocation, + pub emitted_at: DiagLocation, } impl DiagInner { diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 7fc3e4a45d0ab..c881339564570 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -37,8 +37,8 @@ pub use anstyle::{ pub use codes::*; pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; pub use diagnostic::{ - BugAbort, Diag, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee, FatalAbort, - StringPart, Subdiag, Subdiagnostic, + BugAbort, Diag, DiagInner, DiagLocation, DiagStyledString, Diagnostic, EmissionGuarantee, + FatalAbort, StringPart, Subdiag, Subdiagnostic, }; pub use diagnostic_impls::{ DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter, diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 223b47c9044be..eb1628762ffe8 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -2,7 +2,7 @@ use std::cmp; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; -use rustc_errors::{Diag, Diagnostic, MultiSpan}; +use rustc_errors::{Diag, DiagLocation, Diagnostic, MultiSpan}; use rustc_hir::{HirId, ItemLocalId}; use rustc_lint_defs::EditionFcw; use rustc_macros::{Decodable, Encodable, HashStable}; @@ -602,6 +602,9 @@ pub fn diag_lint_level<'a, D: Diagnostic<'a, ()> + 'a>( } else { Diag::new(sess.dcx(), err_level, "") }; + // FIXME: Find a nicer way to expose the `DiagLocation` + err.emitted_at = DiagLocation::caller(); + if let Some(span) = span && err.span.primary_span().is_none() { From e01a7e7ba1a8adb7bb3477838d6f1b4b57d9761a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 6 Mar 2026 16:38:53 +0100 Subject: [PATCH 55/55] Fix panic by setting the span before it is actually needed --- src/tools/clippy/clippy_utils/src/diagnostics.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 65c962826900d..c0d02aaa6ee88 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -250,9 +250,11 @@ where } } + let sp = sp.into(); #[expect(clippy::disallowed_methods)] - cx.emit_span_lint(lint, sp, ClippyDiag(|diag: &mut Diag<'_, ()>| { + cx.emit_span_lint(lint, sp.clone(), ClippyDiag(|diag: &mut Diag<'_, ()>| { diag.primary_message(msg); + diag.span(sp); f(diag); docs_link(diag, lint);