From 70dd3bceca234ba2957f14d4083ea29ce29f1872 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:46:21 +0200 Subject: [PATCH 1/8] Remove rustc_on_unimplemented's `append_const_msg` --- .../src/attributes/diagnostic/mod.rs | 24 ++++++-------- compiler/rustc_hir/src/attrs/diagnostic.rs | 16 ---------- compiler/rustc_span/src/symbol.rs | 1 - .../traits/fulfillment_errors.rs | 31 +++++-------------- library/core/src/cmp.rs | 10 +++--- library/core/src/iter/range.rs | 2 +- library/core/src/marker.rs | 2 +- library/core/src/ops/arith.rs | 8 ++--- tests/auxiliary/minicore.rs | 2 +- 9 files changed, 27 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 8abcaeb5fbf53..e63baf77c0852 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -3,8 +3,8 @@ use std::ops::Range; use rustc_errors::E0232; use rustc_hir::AttrPath; use rustc_hir::attrs::diagnostic::{ - AppendConstMessage, Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, - Name, NameValue, OnUnimplementedCondition, Piece, Predicate, + Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name, NameValue, + OnUnimplementedCondition, Piece, Predicate, }; use rustc_hir::lints::{AttributeLintKind, FormatWarning}; use rustc_macros::Diagnostic; @@ -92,7 +92,6 @@ fn parse_directive_items<'p, S: Stage>( let mut notes = ThinVec::new(); let mut parent_label = None; let mut subcommands = ThinVec::new(); - let mut append_const_msg = None; for item in items { let span = item.span(); @@ -131,7 +130,6 @@ fn parse_directive_items<'p, S: Stage>( let Some(ret) = (||{ Some($($code)*) })() else { - malformed!() }; ret @@ -159,8 +157,13 @@ fn parse_directive_items<'p, S: Stage>( let item: &MetaItemParser = or_malformed!(item.meta_item()?); let name = or_malformed!(item.ident()?).name; - // Some things like `message = "message"` must have a value. - // But with things like `append_const_msg` that is optional. + // Currently, as of April 2026, all arguments of all diagnostic attrs + // must have a value, like `message = "message"`. Thus in a well-formed + // diagnostic attribute this is never `None`. + // + // But we don't assert its presence yet because we don't want to mention it + // if someone does something like `#[diagnostic::on_unimplemented(doesnt_exist)]`. + // That happens in the big `match` below. let value: Option = match item.args().name_value() { Some(nv) => Some(or_malformed!(nv.value_as_ident()?)), None => None, @@ -223,14 +226,6 @@ fn parse_directive_items<'p, S: Stage>( let value = or_malformed!(value?); notes.push(parse_format(value)) } - - (Mode::RustcOnUnimplemented, sym::append_const_msg) => { - append_const_msg = if let Some(msg) = value { - Some(AppendConstMessage::Custom(msg.name, item.span())) - } else { - Some(AppendConstMessage::Default) - } - } (Mode::RustcOnUnimplemented, sym::parent_label) => { let value = or_malformed!(value?); if parent_label.is_none() { @@ -290,7 +285,6 @@ fn parse_directive_items<'p, S: Stage>( label, notes, parent_label, - append_const_msg, }) } diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs index 7c66b3f844691..c700ca142759e 100644 --- a/compiler/rustc_hir/src/attrs/diagnostic.rs +++ b/compiler/rustc_hir/src/attrs/diagnostic.rs @@ -19,7 +19,6 @@ pub struct Directive { pub label: Option<(Span, FormatString)>, pub notes: ThinVec, pub parent_label: Option, - pub append_const_msg: Option, } impl Directive { @@ -63,7 +62,6 @@ impl Directive { let mut label = None; let mut notes = Vec::new(); let mut parent_label = None; - let mut append_const_msg = None; info!( "evaluate_directive({:?}, trait_ref={:?}, options={:?}, args ={:?})", self, trait_name, condition_options, args @@ -91,8 +89,6 @@ impl Directive { if let Some(ref parent_label_) = command.parent_label { parent_label = Some(parent_label_.clone()); } - - append_const_msg = command.append_const_msg; } OnUnimplementedNote { @@ -100,7 +96,6 @@ impl Directive { message: message.map(|m| m.1.format(args)), notes: notes.into_iter().map(|n| n.format(args)).collect(), parent_label: parent_label.map(|e_s| e_s.format(args)), - append_const_msg, } } } @@ -111,17 +106,6 @@ pub struct OnUnimplementedNote { pub label: Option, pub notes: Vec, pub parent_label: Option, - // If none, should fall back to a generic message - pub append_const_msg: Option, -} - -/// Append a message for `[const] Trait` errors. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] -#[derive(HashStable_Generic, Encodable, Decodable, PrintAttribute)] -pub enum AppendConstMessage { - #[default] - Default, - Custom(Symbol, Span), } /// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7b359dcd6b252..a35813abdc210 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -399,7 +399,6 @@ symbols! { anon_assoc, anonymous_lifetime_in_impl_trait, any, - append_const_msg, apx_target_feature, arbitrary_enum_discriminant, arbitrary_self_types, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d6cfc993c8b83..3aa32a2feb4d4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -14,7 +14,7 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions, msg, pluralize, struct_span_code_err, }; -use rustc_hir::attrs::diagnostic::{AppendConstMessage, OnUnimplementedNote}; +use rustc_hir::attrs::diagnostic::OnUnimplementedNote; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem, Node, find_attr}; @@ -193,7 +193,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { label, notes, parent_label, - append_const_msg, } = self.on_unimplemented_note(main_trait_predicate, main_obligation, &mut long_ty_file); let have_alt_message = message.is_some() || label.is_some(); @@ -210,7 +209,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let question_mark_message = "the question mark operation (`?`) implicitly \ performs a conversion on the error value \ using the `From` trait"; - let (message, notes, append_const_msg) = if is_try_conversion { + let (message, notes) = if is_try_conversion { let ty = self.tcx.short_string( main_trait_predicate.skip_binder().self_ty(), &mut long_ty_file, @@ -219,7 +218,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ( Some(format!("`?` couldn't convert the error to `{ty}`")), vec![question_mark_message.to_owned()], - Some(AppendConstMessage::Default), ) } else if is_question_mark { let main_trait_predicate = @@ -233,17 +231,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { not satisfied", )), vec![question_mark_message.to_owned()], - Some(AppendConstMessage::Default), ) } else { - (message, notes, append_const_msg) + (message, notes) }; let default_err_msg = || self.get_standard_error_message( main_trait_predicate, message, None, - append_const_msg, post_message, &mut long_ty_file, ); @@ -859,7 +855,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_ref, None, Some(predicate.constness()), - None, String::new(), &mut file, ); @@ -919,7 +914,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { label, notes, parent_label, - append_const_msg: _, } = note; if let Some(message) = message { @@ -2838,27 +2832,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_predicate: ty::PolyTraitPredicate<'tcx>, message: Option, predicate_constness: Option, - append_const_msg: Option, post_message: String, long_ty_path: &mut Option, ) -> String { message .and_then(|cannot_do_this| { - match (predicate_constness, append_const_msg) { + match predicate_constness { // do nothing if predicate is not const - (None, _) => Some(cannot_do_this), + None => Some(cannot_do_this), // suggested using default post message - ( - Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe), - Some(AppendConstMessage::Default), - ) => Some(format!("{cannot_do_this} in const contexts")), - // overridden post message - ( - Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe), - Some(AppendConstMessage::Custom(custom_msg, _)), - ) => Some(format!("{cannot_do_this}{custom_msg}")), - // fallback to generic message - (Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe), None) => None, + Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe) => { + Some(format!("{cannot_do_this} in const contexts")) + } } }) .unwrap_or_else(|| { diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 49d7487c2803b..2051a806af642 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -241,10 +241,9 @@ use crate::ops::ControlFlow; #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "==")] #[doc(alias = "!=")] -#[rustc_on_unimplemented( +#[diagnostic::on_unimplemented( message = "can't compare `{Self}` with `{Rhs}`", - label = "no implementation for `{Self} == {Rhs}`", - append_const_msg + label = "no implementation for `{Self} == {Rhs}`" )] #[rustc_diagnostic_item = "PartialEq"] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] @@ -1356,10 +1355,9 @@ pub macro Ord($item:item) { #[doc(alias = "<")] #[doc(alias = "<=")] #[doc(alias = ">=")] -#[rustc_on_unimplemented( +#[diagnostic::on_unimplemented( message = "can't compare `{Self}` with `{Rhs}`", - label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`", - append_const_msg + label = "no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`" )] #[rustc_diagnostic_item = "PartialOrd"] #[allow(multiple_supertrait_upcastable)] // FIXME(sized_hierarchy): remove this diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 951bb5d0029f6..695e1f27eff41 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -21,7 +21,7 @@ unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u6 /// The *successor* operation moves towards values that compare greater. /// The *predecessor* operation moves towards values that compare lesser. #[rustc_diagnostic_item = "range_step"] -#[rustc_on_unimplemented( +#[diagnostic::on_unimplemented( message = "`std::ops::Range<{Self}>` is not an iterator", label = "`Range<{Self}>` is not an iterator", note = "`Range` only implements `Iterator` for select types in the standard library, \ diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index c79e8fc4060c1..3ca6466d122a4 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1054,7 +1054,7 @@ marker_impls! { #[unstable(feature = "const_destruct", issue = "133214")] #[rustc_const_unstable(feature = "const_destruct", issue = "133214")] #[lang = "destruct"] -#[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] +#[diagnostic::on_unimplemented(message = "can't drop `{Self}`")] #[rustc_deny_explicit_impl] #[rustc_dyn_incompatible_trait] pub const trait Destruct: PointeeSized {} diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index aec52424af3c4..afb814dbc6b33 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -70,8 +70,7 @@ on(all(Self = "{integer}", Rhs = "{float}"), message = "cannot add a float to an integer",), on(all(Self = "{float}", Rhs = "{integer}"), message = "cannot add an integer to a float",), message = "cannot add `{Rhs}` to `{Self}`", - label = "no implementation for `{Self} + {Rhs}`", - append_const_msg + label = "no implementation for `{Self} + {Rhs}`" )] #[doc(alias = "+")] pub const trait Add { @@ -181,10 +180,9 @@ add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 #[lang = "sub"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_ops", issue = "143802")] -#[rustc_on_unimplemented( +#[diagnostic::on_unimplemented( message = "cannot subtract `{Rhs}` from `{Self}`", - label = "no implementation for `{Self} - {Rhs}`", - append_const_msg + label = "no implementation for `{Self} - {Rhs}`" )] #[doc(alias = "-")] pub const trait Sub { diff --git a/tests/auxiliary/minicore.rs b/tests/auxiliary/minicore.rs index d84357edd0423..5c6eb54832437 100644 --- a/tests/auxiliary/minicore.rs +++ b/tests/auxiliary/minicore.rs @@ -63,7 +63,7 @@ pub trait MetaSized: PointeeSized {} pub trait Sized: MetaSized {} #[lang = "destruct"] -#[rustc_on_unimplemented(message = "can't drop `{Self}`", append_const_msg)] +#[diagnostic::on_unimplemented(message = "can't drop `{Self}`")] pub trait Destruct: PointeeSized {} #[lang = "legacy_receiver"] From 8c043676a112155d824c9748534639704c145652 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Tue, 31 Mar 2026 23:21:46 +0200 Subject: [PATCH 2/8] Refactor `get_standard_error_message` --- .../traits/fulfillment_errors.rs | 54 +++++++------------ 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 3aa32a2feb4d4..855cce9b32b9b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -196,6 +196,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } = self.on_unimplemented_note(main_trait_predicate, main_obligation, &mut long_ty_file); let have_alt_message = message.is_some() || label.is_some(); + + let message = message.unwrap_or_else(|| self.get_standard_error_message( + main_trait_predicate, + None, + post_message, + &mut long_ty_file, + )); let is_try_conversion = self.is_try_conversion(span, main_trait_predicate.def_id()); let is_question_mark = matches!( root_obligation.cause.code().peel_derives(), @@ -216,7 +223,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); // We have a `-> Result<_, E1>` and `gives_E2()?`. ( - Some(format!("`?` couldn't convert the error to `{ty}`")), + format!("`?` couldn't convert the error to `{ty}`"), vec![question_mark_message.to_owned()], ) } else if is_question_mark { @@ -226,24 +233,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // trait object: `-> Result<_, Box` and `gives_E()?` when // `E: Error` isn't met. ( - Some(format!( + format!( "`?` couldn't convert the error: `{main_trait_predicate}` is \ not satisfied", - )), + ), vec![question_mark_message.to_owned()], ) } else { (message, notes) }; - let default_err_msg = || self.get_standard_error_message( - main_trait_predicate, - message, - None, - post_message, - &mut long_ty_file, - ); - let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item( main_trait_predicate.def_id(), LangItem::TransmuteTrait, @@ -267,7 +266,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } GetSafeTransmuteErrorAndReason::Default => { - (default_err_msg(), None) + (message, None) } GetSafeTransmuteErrorAndReason::Error { err_msg, @@ -275,7 +274,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } => (err_msg, safe_transmute_explanation), } } else { - (default_err_msg(), None) + (message, None) }; let mut err = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg); @@ -853,7 +852,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let err_msg = self.get_standard_error_message( trait_ref, - None, Some(predicate.constness()), String::new(), &mut file, @@ -2830,31 +2828,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn get_standard_error_message( &self, trait_predicate: ty::PolyTraitPredicate<'tcx>, - message: Option, predicate_constness: Option, post_message: String, long_ty_path: &mut Option, ) -> String { - message - .and_then(|cannot_do_this| { - match predicate_constness { - // do nothing if predicate is not const - None => Some(cannot_do_this), - // suggested using default post message - Some(ty::BoundConstness::Const | ty::BoundConstness::Maybe) => { - Some(format!("{cannot_do_this} in const contexts")) - } - } - }) - .unwrap_or_else(|| { - format!( - "the trait bound `{}` is not satisfied{post_message}", - self.tcx.short_string( - trait_predicate.print_with_bound_constness(predicate_constness), - long_ty_path, - ), - ) - }) + format!( + "the trait bound `{}` is not satisfied{post_message}", + self.tcx.short_string( + trait_predicate.print_with_bound_constness(predicate_constness), + long_ty_path, + ), + ) } fn select_transmute_obligation_for_reporting( From b6bdfedad65384dedcdb94953384c1f52893ce80 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Tue, 31 Mar 2026 23:34:40 +0200 Subject: [PATCH 3/8] Avoid needless clone. --- .../src/error_reporting/traits/fulfillment_errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 855cce9b32b9b..5013d87493841 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -388,7 +388,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(s) = label { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! - err.span_label(span, s.as_str().to_owned()); + err.span_label(span, s); if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point From 62db1ebca3c1f40ef42bd62283e060f3c8b88192 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 31 Mar 2026 09:54:34 +0800 Subject: [PATCH 4/8] Improve shadowed private field diagnostics --- compiler/rustc_hir_typeck/src/op.rs | 27 +++++ .../traits/fulfillment_errors.rs | 24 +++++ .../src/error_reporting/traits/suggestions.rs | 63 +++++++++++ ...vate-field-deref-confusion-issue-149546.rs | 81 ++++++++++++++ ...-field-deref-confusion-issue-149546.stderr | 100 ++++++++++++++++++ 5 files changed, 295 insertions(+) create mode 100644 tests/ui/privacy/private-field-deref-confusion-issue-149546.rs create mode 100644 tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index cf61728f7c2a3..f21db0bf85550 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -249,6 +249,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty_var, Some(lhs_expr), |err, ty| { + self.err_ctxt().note_field_shadowed_by_private_candidate( + err, + rhs_expr.hir_id, + self.param_env, + ); if let Op::BinOp(binop) = op && binop.node == hir::BinOpKind::Eq { @@ -331,6 +336,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_expr.span, format!("cannot use `{}` on type `{}`", s, lhs_ty_str), ); + let err_ctxt = self.err_ctxt(); + err_ctxt.note_field_shadowed_by_private_candidate( + &mut err, + lhs_expr.hir_id, + self.param_env, + ); + err_ctxt.note_field_shadowed_by_private_candidate( + &mut err, + rhs_expr.hir_id, + self.param_env, + ); self.note_unmet_impls_on_type(&mut err, &errors, false); (err, None) } @@ -391,6 +407,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(lhs_expr.span, lhs_ty_str.clone()); err.span_label(rhs_expr.span, rhs_ty_str); } + let err_ctxt = self.err_ctxt(); + err_ctxt.note_field_shadowed_by_private_candidate( + &mut err, + lhs_expr.hir_id, + self.param_env, + ); + err_ctxt.note_field_shadowed_by_private_candidate( + &mut err, + rhs_expr.hir_id, + self.param_env, + ); let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty); self.note_unmet_impls_on_type(&mut err, &errors, suggest_derive); (err, output_def_id) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d0358b03af197..fe3c7b8797b01 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -559,6 +559,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } + self.note_shadowed_private_fields_in_binop( + &mut err, + &obligation, + obligation.param_env, + ); self.try_to_add_help_message( &root_obligation, &obligation, @@ -3226,6 +3231,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_shadowed_inherent_method(err, obligation, trait_predicate); } + fn note_shadowed_private_fields_in_binop( + &self, + err: &mut Diag<'_>, + obligation: &PredicateObligation<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) { + if self.typeck_results.is_none() { + return; + } + + let ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } = obligation.cause.code() + else { + return; + }; + + self.note_field_shadowed_by_private_candidate(err, *lhs_hir_id, param_env); + self.note_field_shadowed_by_private_candidate(err, *rhs_hir_id, param_env); + } + fn add_help_message_for_fn_trait( &self, trait_pred: ty::PolyTraitPredicate<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 2d9574ea8c546..01a1d7f42cdfc 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -242,6 +242,69 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( } impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + pub fn note_field_shadowed_by_private_candidate( + &self, + err: &mut Diag<'_>, + hir_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + ) { + let Some(typeck_results) = &self.typeck_results else { + return; + }; + let Node::Expr(expr) = self.tcx.hir_node(hir_id) else { + return; + }; + let hir::ExprKind::Field(base_expr, field_ident) = expr.kind else { + return; + }; + + let Some(base_ty) = typeck_results.expr_ty_opt(base_expr) else { + return; + }; + let base_ty = self.resolve_vars_if_possible(base_ty); + if base_ty.references_error() { + return; + } + + let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(typeck_results.hir_owner.def_id); + let mut private_candidate = None; + + for (deref_base_ty, _) in (self.autoderef_steps)(base_ty) { + let ty::Adt(base_def, args) = deref_base_ty.kind() else { + continue; + }; + + if base_def.is_enum() { + continue; + } + + let (adjusted_ident, def_scope) = + self.tcx.adjust_ident_and_get_scope(field_ident, base_def.did(), fn_body_hir_id); + + let Some((_, field_def)) = + base_def.non_enum_variant().fields.iter_enumerated().find(|(_, field)| { + field.ident(self.tcx).normalize_to_macros_2_0() == adjusted_ident + }) + else { + continue; + }; + + if field_def.vis.is_accessible_from(def_scope, self.tcx) { + let accessible_field_ty = field_def.ty(self.tcx, args); + if let Some((private_base_ty, private_field_ty)) = private_candidate + && !self.can_eq(param_env, private_field_ty, accessible_field_ty) + { + err.note(format!( + "there is a field `{field_ident}` on `{private_base_ty}` with type `{private_field_ty}`, but it is private" + )); + } + return; + } + + private_candidate.get_or_insert((deref_base_ty, field_def.ty(self.tcx, args))); + } + } + pub fn suggest_restricting_param_bound( &self, err: &mut Diag<'_>, diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs new file mode 100644 index 0000000000000..ee76ef7a5ed01 --- /dev/null +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs @@ -0,0 +1,81 @@ +// Regression test for issue #149546. +// Field lookup still resolves to the public field on the Deref target, but +// follow-up diagnostics should explain that the original type has a same-named +// private field with a different type. + +mod structs { + pub struct A { + field: usize, + b: B, + } + + pub struct B { + pub field: bool, + } + + impl std::ops::Deref for A { + type Target = B; + + fn deref(&self) -> &Self::Target { + &self.b + } + } +} + +use structs::A; + +fn by_value(a: A) { + a.field + 5; + //~^ ERROR cannot add `{integer}` to `bool` + //~| NOTE bool + //~| NOTE {integer} + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn by_ref(a: &A) { + a.field + 5; + //~^ ERROR cannot add `{integer}` to `bool` + //~| NOTE bool + //~| NOTE {integer} + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn rhs_by_value(a: A) { + 5 + a.field; + //~^ ERROR cannot add `bool` to `{integer}` + //~| NOTE no implementation for `{integer} + bool` + //~| HELP the trait `Add` is not implemented for `{integer}` + //~| HELP the following other types implement trait `Add`: + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn rhs_by_ref(a: &A) { + 5 + a.field; + //~^ ERROR cannot add `bool` to `{integer}` + //~| NOTE no implementation for `{integer} + bool` + //~| HELP the trait `Add` is not implemented for `{integer}` + //~| HELP the following other types implement trait `Add`: + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn rhs_assign_op_by_value(a: A) { + let mut n = 5; + n += a.field; + //~^ ERROR cannot add-assign `bool` to `{integer}` + //~| NOTE no implementation for `{integer} += bool` + //~| HELP the trait `AddAssign` is not implemented for `{integer}` + //~| HELP the following other types implement trait `AddAssign`: + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn rhs_assign_op_by_ref(a: &A) { + let mut n = 5; + n += a.field; + //~^ ERROR cannot add-assign `bool` to `{integer}` + //~| NOTE no implementation for `{integer} += bool` + //~| HELP the trait `AddAssign` is not implemented for `{integer}` + //~| HELP the following other types implement trait `AddAssign`: + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn main() {} diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr new file mode 100644 index 0000000000000..db0bc632c0acc --- /dev/null +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr @@ -0,0 +1,100 @@ +error[E0369]: cannot add `{integer}` to `bool` + --> $DIR/private-field-deref-confusion-issue-149546.rs:28:13 + | +LL | a.field + 5; + | ------- ^ - {integer} + | | + | bool + | + = note: there is a field `field` on `A` with type `usize`, but it is private + +error[E0369]: cannot add `{integer}` to `bool` + --> $DIR/private-field-deref-confusion-issue-149546.rs:36:13 + | +LL | a.field + 5; + | ------- ^ - {integer} + | | + | bool + | + = note: there is a field `field` on `A` with type `usize`, but it is private + +error[E0277]: cannot add `bool` to `{integer}` + --> $DIR/private-field-deref-confusion-issue-149546.rs:44:7 + | +LL | 5 + a.field; + | ^ no implementation for `{integer} + bool` + | + = help: the trait `Add` is not implemented for `{integer}` + = note: there is a field `field` on `A` with type `usize`, but it is private + = help: the following other types implement trait `Add`: + `&f128` implements `Add` + `&f128` implements `Add` + `&f16` implements `Add` + `&f16` implements `Add` + `&f32` implements `Add` + `&f32` implements `Add` + `&f64` implements `Add` + `&f64` implements `Add` + and 56 others + +error[E0277]: cannot add `bool` to `{integer}` + --> $DIR/private-field-deref-confusion-issue-149546.rs:53:7 + | +LL | 5 + a.field; + | ^ no implementation for `{integer} + bool` + | + = help: the trait `Add` is not implemented for `{integer}` + = note: there is a field `field` on `A` with type `usize`, but it is private + = help: the following other types implement trait `Add`: + `&f128` implements `Add` + `&f128` implements `Add` + `&f16` implements `Add` + `&f16` implements `Add` + `&f32` implements `Add` + `&f32` implements `Add` + `&f64` implements `Add` + `&f64` implements `Add` + and 56 others + +error[E0277]: cannot add-assign `bool` to `{integer}` + --> $DIR/private-field-deref-confusion-issue-149546.rs:63:7 + | +LL | n += a.field; + | ^^ no implementation for `{integer} += bool` + | + = help: the trait `AddAssign` is not implemented for `{integer}` + = note: there is a field `field` on `A` with type `usize`, but it is private + = help: the following other types implement trait `AddAssign`: + `f128` implements `AddAssign<&f128>` + `f128` implements `AddAssign` + `f16` implements `AddAssign<&f16>` + `f16` implements `AddAssign` + `f32` implements `AddAssign<&f32>` + `f32` implements `AddAssign` + `f64` implements `AddAssign<&f64>` + `f64` implements `AddAssign` + and 24 others + +error[E0277]: cannot add-assign `bool` to `{integer}` + --> $DIR/private-field-deref-confusion-issue-149546.rs:73:7 + | +LL | n += a.field; + | ^^ no implementation for `{integer} += bool` + | + = help: the trait `AddAssign` is not implemented for `{integer}` + = note: there is a field `field` on `A` with type `usize`, but it is private + = help: the following other types implement trait `AddAssign`: + `f128` implements `AddAssign<&f128>` + `f128` implements `AddAssign` + `f16` implements `AddAssign<&f16>` + `f16` implements `AddAssign` + `f32` implements `AddAssign<&f32>` + `f32` implements `AddAssign` + `f64` implements `AddAssign<&f64>` + `f64` implements `AddAssign` + and 24 others + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0277, E0369. +For more information about an error, try `rustc --explain E0277`. From 257d65531477cd48e89131161b184465753a5443 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 31 Mar 2026 10:39:24 +0800 Subject: [PATCH 5/8] extend note for private field to more diagnostics --- .../src/error_reporting/infer/mod.rs | 4 ++ .../traits/fulfillment_errors.rs | 23 +------ .../src/error_reporting/traits/suggestions.rs | 49 +++++++++++++++ ...vate-field-deref-confusion-issue-149546.rs | 41 ++++++++++++ ...-field-deref-confusion-issue-149546.stderr | 62 ++++++++++++++++--- 5 files changed, 150 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index d9ea2e0057895..f7486a23bf731 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1528,6 +1528,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { label_or_note(span, terr.to_string(self.tcx)); } + if let Some(param_env) = param_env { + self.note_field_shadowed_by_private_candidate_in_cause(diag, cause, param_env); + } + if self.check_and_note_conflicting_crates(diag, terr) { return; } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index fe3c7b8797b01..3ff08bbaf9dc2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -559,9 +559,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } - self.note_shadowed_private_fields_in_binop( + self.note_field_shadowed_by_private_candidate_in_cause( &mut err, - &obligation, + &obligation.cause, obligation.param_env, ); self.try_to_add_help_message( @@ -3231,25 +3231,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_shadowed_inherent_method(err, obligation, trait_predicate); } - fn note_shadowed_private_fields_in_binop( - &self, - err: &mut Diag<'_>, - obligation: &PredicateObligation<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) { - if self.typeck_results.is_none() { - return; - } - - let ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } = obligation.cause.code() - else { - return; - }; - - self.note_field_shadowed_by_private_candidate(err, *lhs_hir_id, param_env); - self.note_field_shadowed_by_private_candidate(err, *rhs_hir_id, param_env); - } - fn add_help_message_for_fn_trait( &self, trait_pred: ty::PolyTraitPredicate<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 01a1d7f42cdfc..95dfe6ffbcf35 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -8,6 +8,7 @@ use itertools::{EitherOrBoth, Itertools}; use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_data_structures::unord::UnordMap; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, pluralize, @@ -242,6 +243,54 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( } impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + pub fn note_field_shadowed_by_private_candidate_in_cause( + &self, + err: &mut Diag<'_>, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) { + let mut hir_ids = UnordMap::default(); + // Walk the parent chain so we can recover + // the source expression from whichever layer carries them. + let mut next_code = Some(cause.code()); + while let Some(cause_code) = next_code { + match cause_code { + ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } => { + hir_ids.insert(lhs_hir_id.local_id.as_u32(), *lhs_hir_id); + hir_ids.insert(rhs_hir_id.local_id.as_u32(), *rhs_hir_id); + } + ObligationCauseCode::FunctionArg { arg_hir_id, .. } + | ObligationCauseCode::ReturnValue(arg_hir_id) + | ObligationCauseCode::AwaitableExpr(arg_hir_id) + | ObligationCauseCode::BlockTailExpression(arg_hir_id, _) + | ObligationCauseCode::UnOp { hir_id: arg_hir_id } => { + hir_ids.insert(arg_hir_id.local_id.as_u32(), *arg_hir_id); + } + ObligationCauseCode::OpaqueReturnType(Some((_, hir_id))) => { + hir_ids.insert(hir_id.local_id.as_u32(), *hir_id); + } + _ => {} + } + next_code = cause_code.parent(); + } + + if cause.span != DUMMY_SP + && hir_ids.is_empty() + && let Some(body) = self.tcx.hir_maybe_body_owned_by(cause.body_id) + { + let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx); + expr_finder.visit_body(body); + if let Some(expr) = expr_finder.result { + hir_ids.insert(expr.hir_id.local_id.as_u32(), expr.hir_id); + } + } + + let hir_ids = hir_ids.into_sorted_stable_ord(); + for (_, hir_id) in hir_ids { + self.note_field_shadowed_by_private_candidate(err, hir_id, param_env); + } + } + pub fn note_field_shadowed_by_private_candidate( &self, err: &mut Diag<'_>, diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs index ee76ef7a5ed01..44981a801afa3 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs @@ -24,6 +24,24 @@ mod structs { use structs::A; +fn takes_usize(_: usize) {} +//~^ NOTE function defined here + +trait Marker {} + +impl Marker for usize {} +//~^ HELP the trait `Marker` is implemented for `usize` + +struct Wrapper(i32); + +impl std::ops::Add for Wrapper { +//~^ NOTE required for `Wrapper` to implement `Add` +//~| NOTE unsatisfied trait bound introduced here + type Output = (); + + fn add(self, _: T) {} +} + fn by_value(a: A) { a.field + 5; //~^ ERROR cannot add `{integer}` to `bool` @@ -78,4 +96,27 @@ fn rhs_assign_op_by_ref(a: &A) { //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } +fn rhs_nested_obligation(a: A) { + Wrapper(5) + a.field; + //~^ ERROR the trait bound `bool: Marker` is not satisfied + //~| NOTE the trait `Marker` is not implemented for `bool` + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn function_arg(a: A) { + takes_usize(a.field); + //~^ ERROR mismatched types + //~| NOTE expected `usize`, found `bool` + //~| NOTE arguments to this function are incorrect + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn return_value(a: &A) -> usize { +//~^ NOTE expected `usize` because of return type + a.field + //~^ ERROR mismatched types + //~| NOTE expected `usize`, found `bool` + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + fn main() {} diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr index db0bc632c0acc..511d4a3d9075b 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:28:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:46:13 | LL | a.field + 5; | ------- ^ - {integer} @@ -9,7 +9,7 @@ LL | a.field + 5; = note: there is a field `field` on `A` with type `usize`, but it is private error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:36:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:54:13 | LL | a.field + 5; | ------- ^ - {integer} @@ -19,7 +19,7 @@ LL | a.field + 5; = note: there is a field `field` on `A` with type `usize`, but it is private error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:44:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:62:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` @@ -38,7 +38,7 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:53:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:71:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` @@ -57,7 +57,7 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:63:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:81:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` @@ -76,7 +76,7 @@ LL | n += a.field; and 24 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:73:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:91:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` @@ -94,7 +94,53 @@ LL | n += a.field; `f64` implements `AddAssign` and 24 others -error: aborting due to 6 previous errors +error[E0277]: the trait bound `bool: Marker` is not satisfied + --> $DIR/private-field-deref-confusion-issue-149546.rs:100:16 + | +LL | Wrapper(5) + a.field; + | ^ the trait `Marker` is not implemented for `bool` + | + = note: there is a field `field` on `A` with type `usize`, but it is private +help: the trait `Marker` is implemented for `usize` + --> $DIR/private-field-deref-confusion-issue-149546.rs:32:1 + | +LL | impl Marker for usize {} + | ^^^^^^^^^^^^^^^^^^^^^ +note: required for `Wrapper` to implement `Add` + --> $DIR/private-field-deref-confusion-issue-149546.rs:37:17 + | +LL | impl std::ops::Add for Wrapper { + | ------ ^^^^^^^^^^^^^^^^ ^^^^^^^ + | | + | unsatisfied trait bound introduced here + +error[E0308]: mismatched types + --> $DIR/private-field-deref-confusion-issue-149546.rs:107:17 + | +LL | takes_usize(a.field); + | ----------- ^^^^^^^ expected `usize`, found `bool` + | | + | arguments to this function are incorrect + | + = note: there is a field `field` on `A` with type `usize`, but it is private +note: function defined here + --> $DIR/private-field-deref-confusion-issue-149546.rs:27:4 + | +LL | fn takes_usize(_: usize) {} + | ^^^^^^^^^^^ -------- + +error[E0308]: mismatched types + --> $DIR/private-field-deref-confusion-issue-149546.rs:116:5 + | +LL | fn return_value(a: &A) -> usize { + | ----- expected `usize` because of return type +LL | +LL | a.field + | ^^^^^^^ expected `usize`, found `bool` + | + = note: there is a field `field` on `A` with type `usize`, but it is private + +error: aborting due to 9 previous errors -Some errors have detailed explanations: E0277, E0369. +Some errors have detailed explanations: E0277, E0308, E0369. For more information about an error, try `rustc --explain E0277`. From 86f99d2d4dd83e85a2d2cf5328cca4f95c0106d5 Mon Sep 17 00:00:00 2001 From: yukang Date: Tue, 31 Mar 2026 11:40:36 +0800 Subject: [PATCH 6/8] extend note for private field to method call --- .../rustc_hir_typeck/src/method/suggest.rs | 7 +++ .../src/error_reporting/traits/suggestions.rs | 1 - ...vate-field-deref-confusion-issue-149546.rs | 23 ++++++++-- ...-field-deref-confusion-issue-149546.stderr | 46 +++++++++++++------ 4 files changed, 58 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index c5b3d7065fa92..85bcc2745254d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1214,6 +1214,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unsatisfied_predicates, ) }; + if let SelfSource::MethodCall(rcvr_expr) = source { + self.err_ctxt().note_field_shadowed_by_private_candidate( + &mut err, + rcvr_expr.hir_id, + self.param_env, + ); + } self.set_label_for_method_error( &mut err, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 95dfe6ffbcf35..a1a4cc1bc129c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -275,7 +275,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if cause.span != DUMMY_SP - && hir_ids.is_empty() && let Some(body) = self.tcx.hir_maybe_body_owned_by(cause.body_id) { let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx); diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs index 44981a801afa3..e9013990d74cb 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs @@ -1,4 +1,3 @@ -// Regression test for issue #149546. // Field lookup still resolves to the public field on the Deref target, but // follow-up diagnostics should explain that the original type has a same-named // private field with a different type. @@ -35,8 +34,8 @@ impl Marker for usize {} struct Wrapper(i32); impl std::ops::Add for Wrapper { -//~^ NOTE required for `Wrapper` to implement `Add` -//~| NOTE unsatisfied trait bound introduced here + //~^ NOTE required for `Wrapper` to implement `Add` + //~| NOTE unsatisfied trait bound introduced here type Output = (); fn add(self, _: T) {} @@ -103,6 +102,22 @@ fn rhs_nested_obligation(a: A) { //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } +fn method_call(a: A) { + a.field.count_ones(); + //~^ ERROR no method named `count_ones` found for type `bool` in the current scope + //~| NOTE method not found in `bool` + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private +} + +fn type_mismatch(a: A) { + let value: usize = a.field; + //~^ ERROR mismatched types + //~| NOTE expected `usize`, found `bool` + //~| NOTE expected due to this + //~| NOTE there is a field `field` on `A` with type `usize`, but it is private + eprintln!("value: {value}"); +} + fn function_arg(a: A) { takes_usize(a.field); //~^ ERROR mismatched types @@ -112,7 +127,7 @@ fn function_arg(a: A) { } fn return_value(a: &A) -> usize { -//~^ NOTE expected `usize` because of return type + //~^ NOTE expected `usize` because of return type a.field //~^ ERROR mismatched types //~| NOTE expected `usize`, found `bool` diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr index 511d4a3d9075b..d10d9a771b079 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr @@ -1,5 +1,5 @@ error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:46:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:45:13 | LL | a.field + 5; | ------- ^ - {integer} @@ -9,7 +9,7 @@ LL | a.field + 5; = note: there is a field `field` on `A` with type `usize`, but it is private error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:54:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:53:13 | LL | a.field + 5; | ------- ^ - {integer} @@ -19,7 +19,7 @@ LL | a.field + 5; = note: there is a field `field` on `A` with type `usize`, but it is private error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:62:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:61:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` @@ -38,7 +38,7 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:71:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:70:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` @@ -57,7 +57,7 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:81:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:80:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` @@ -76,7 +76,7 @@ LL | n += a.field; and 24 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:91:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:90:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` @@ -95,27 +95,45 @@ LL | n += a.field; and 24 others error[E0277]: the trait bound `bool: Marker` is not satisfied - --> $DIR/private-field-deref-confusion-issue-149546.rs:100:16 + --> $DIR/private-field-deref-confusion-issue-149546.rs:99:16 | LL | Wrapper(5) + a.field; | ^ the trait `Marker` is not implemented for `bool` | = note: there is a field `field` on `A` with type `usize`, but it is private help: the trait `Marker` is implemented for `usize` - --> $DIR/private-field-deref-confusion-issue-149546.rs:32:1 + --> $DIR/private-field-deref-confusion-issue-149546.rs:31:1 | LL | impl Marker for usize {} | ^^^^^^^^^^^^^^^^^^^^^ note: required for `Wrapper` to implement `Add` - --> $DIR/private-field-deref-confusion-issue-149546.rs:37:17 + --> $DIR/private-field-deref-confusion-issue-149546.rs:36:17 | LL | impl std::ops::Add for Wrapper { | ------ ^^^^^^^^^^^^^^^^ ^^^^^^^ | | | unsatisfied trait bound introduced here +error[E0599]: no method named `count_ones` found for type `bool` in the current scope + --> $DIR/private-field-deref-confusion-issue-149546.rs:106:13 + | +LL | a.field.count_ones(); + | ^^^^^^^^^^ method not found in `bool` + | + = note: there is a field `field` on `A` with type `usize`, but it is private + +error[E0308]: mismatched types + --> $DIR/private-field-deref-confusion-issue-149546.rs:113:24 + | +LL | let value: usize = a.field; + | ----- ^^^^^^^ expected `usize`, found `bool` + | | + | expected due to this + | + = note: there is a field `field` on `A` with type `usize`, but it is private + error[E0308]: mismatched types - --> $DIR/private-field-deref-confusion-issue-149546.rs:107:17 + --> $DIR/private-field-deref-confusion-issue-149546.rs:122:17 | LL | takes_usize(a.field); | ----------- ^^^^^^^ expected `usize`, found `bool` @@ -124,13 +142,13 @@ LL | takes_usize(a.field); | = note: there is a field `field` on `A` with type `usize`, but it is private note: function defined here - --> $DIR/private-field-deref-confusion-issue-149546.rs:27:4 + --> $DIR/private-field-deref-confusion-issue-149546.rs:26:4 | LL | fn takes_usize(_: usize) {} | ^^^^^^^^^^^ -------- error[E0308]: mismatched types - --> $DIR/private-field-deref-confusion-issue-149546.rs:116:5 + --> $DIR/private-field-deref-confusion-issue-149546.rs:131:5 | LL | fn return_value(a: &A) -> usize { | ----- expected `usize` because of return type @@ -140,7 +158,7 @@ LL | a.field | = note: there is a field `field` on `A` with type `usize`, but it is private -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors -Some errors have detailed explanations: E0277, E0308, E0369. +Some errors have detailed explanations: E0277, E0308, E0369, E0599. For more information about an error, try `rustc --explain E0277`. From ce46df2fca06b65e213f10054965ccbde13dc6df Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 2 Apr 2026 17:42:27 +0800 Subject: [PATCH 7/8] Sort shadowed field notes by source order --- .../src/error_reporting/traits/suggestions.rs | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index a1a4cc1bc129c..e855d51aac1ca 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -8,7 +8,6 @@ use itertools::{EitherOrBoth, Itertools}; use rustc_abi::ExternAbi; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::unord::UnordMap; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, pluralize, @@ -249,25 +248,30 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, ) { - let mut hir_ids = UnordMap::default(); + let mut hir_ids = Vec::new(); + let mut push_hir_id = |hir_id| { + if !hir_ids.contains(&hir_id) { + hir_ids.push(hir_id); + } + }; // Walk the parent chain so we can recover // the source expression from whichever layer carries them. let mut next_code = Some(cause.code()); while let Some(cause_code) = next_code { match cause_code { ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } => { - hir_ids.insert(lhs_hir_id.local_id.as_u32(), *lhs_hir_id); - hir_ids.insert(rhs_hir_id.local_id.as_u32(), *rhs_hir_id); + push_hir_id(*lhs_hir_id); + push_hir_id(*rhs_hir_id); } ObligationCauseCode::FunctionArg { arg_hir_id, .. } | ObligationCauseCode::ReturnValue(arg_hir_id) | ObligationCauseCode::AwaitableExpr(arg_hir_id) | ObligationCauseCode::BlockTailExpression(arg_hir_id, _) | ObligationCauseCode::UnOp { hir_id: arg_hir_id } => { - hir_ids.insert(arg_hir_id.local_id.as_u32(), *arg_hir_id); + push_hir_id(*arg_hir_id); } ObligationCauseCode::OpaqueReturnType(Some((_, hir_id))) => { - hir_ids.insert(hir_id.local_id.as_u32(), *hir_id); + push_hir_id(*hir_id); } _ => {} } @@ -280,12 +284,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx); expr_finder.visit_body(body); if let Some(expr) = expr_finder.result { - hir_ids.insert(expr.hir_id.local_id.as_u32(), expr.hir_id); + push_hir_id(expr.hir_id); } } - let hir_ids = hir_ids.into_sorted_stable_ord(); - for (_, hir_id) in hir_ids { + let source_map = self.tcx.sess.source_map(); + hir_ids.sort_by_cached_key(|hir_id| { + let span = self.tcx.hir_span(*hir_id); + let lo = source_map.lookup_byte_offset(span.lo()); + let hi = source_map.lookup_byte_offset(span.hi()); + (lo.sf.name.prefer_remapped_unconditionally().to_string(), lo.pos.0, hi.pos.0) + }); + + for hir_id in hir_ids { self.note_field_shadowed_by_private_candidate(err, hir_id, param_env); } } From ae899cc4e2bf91e553ddb369b83deb6425500ff4 Mon Sep 17 00:00:00 2001 From: yukang Date: Thu, 2 Apr 2026 17:57:33 +0800 Subject: [PATCH 8/8] Clarify private field autoderef notes --- .../src/error_reporting/traits/suggestions.rs | 125 +++++++++-- ...vate-field-deref-confusion-issue-149546.rs | 51 +---- ...-field-deref-confusion-issue-149546.stderr | 203 +++++++++++++++--- 3 files changed, 285 insertions(+), 94 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index e855d51aac1ca..4912d5f4582e7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -22,8 +22,10 @@ use rustc_hir::{ expr_needs_parens, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; +use rustc_infer::traits::ImplSource; use rustc_middle::middle::privacy::Level; use rustc_middle::traits::IsConstable; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::{ PrintPolyTraitPredicateExt as _, PrintPolyTraitRefExt, PrintTraitPredicateExt as _, @@ -49,7 +51,7 @@ use crate::error_reporting::TypeErrCtxt; use crate::errors; use crate::infer::InferCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt}; +use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt, SelectionContext}; #[derive(Debug)] pub enum CoroutineInteriorOrUpvar { @@ -248,46 +250,44 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, ) { - let mut hir_ids = Vec::new(); - let mut push_hir_id = |hir_id| { - if !hir_ids.contains(&hir_id) { - hir_ids.push(hir_id); - } - }; + let mut hir_ids = FxHashSet::default(); // Walk the parent chain so we can recover // the source expression from whichever layer carries them. let mut next_code = Some(cause.code()); while let Some(cause_code) = next_code { match cause_code { ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } => { - push_hir_id(*lhs_hir_id); - push_hir_id(*rhs_hir_id); + hir_ids.insert(*lhs_hir_id); + hir_ids.insert(*rhs_hir_id); } ObligationCauseCode::FunctionArg { arg_hir_id, .. } | ObligationCauseCode::ReturnValue(arg_hir_id) | ObligationCauseCode::AwaitableExpr(arg_hir_id) | ObligationCauseCode::BlockTailExpression(arg_hir_id, _) | ObligationCauseCode::UnOp { hir_id: arg_hir_id } => { - push_hir_id(*arg_hir_id); + hir_ids.insert(*arg_hir_id); } ObligationCauseCode::OpaqueReturnType(Some((_, hir_id))) => { - push_hir_id(*hir_id); + hir_ids.insert(*hir_id); } _ => {} } next_code = cause_code.parent(); } - if cause.span != DUMMY_SP + if !cause.span.is_dummy() && let Some(body) = self.tcx.hir_maybe_body_owned_by(cause.body_id) { let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx); expr_finder.visit_body(body); if let Some(expr) = expr_finder.result { - push_hir_id(expr.hir_id); + hir_ids.insert(expr.hir_id); } } + // we will sort immediately by source order before emitting any diagnostics + #[allow(rustc::potential_query_instability)] + let mut hir_ids: Vec<_> = hir_ids.into_iter().collect(); let source_map = self.tcx.sess.source_map(); hir_ids.sort_by_cached_key(|hir_id| { let span = self.tcx.hir_span(*hir_id); @@ -326,7 +326,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(typeck_results.hir_owner.def_id); - let mut private_candidate = None; + let mut private_candidate: Option<(Ty<'tcx>, Ty<'tcx>, Span)> = None; for (deref_base_ty, _) in (self.autoderef_steps)(base_ty) { let ty::Adt(base_def, args) = deref_base_ty.kind() else { @@ -347,20 +347,107 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { else { continue; }; + let field_span = self + .tcx + .def_ident_span(field_def.did) + .unwrap_or_else(|| self.tcx.def_span(field_def.did)); if field_def.vis.is_accessible_from(def_scope, self.tcx) { let accessible_field_ty = field_def.ty(self.tcx, args); - if let Some((private_base_ty, private_field_ty)) = private_candidate + if let Some((private_base_ty, private_field_ty, private_field_span)) = + private_candidate && !self.can_eq(param_env, private_field_ty, accessible_field_ty) { - err.note(format!( - "there is a field `{field_ident}` on `{private_base_ty}` with type `{private_field_ty}`, but it is private" - )); + let private_struct_span = match private_base_ty.kind() { + ty::Adt(private_base_def, _) => self + .tcx + .def_ident_span(private_base_def.did()) + .unwrap_or_else(|| self.tcx.def_span(private_base_def.did())), + _ => DUMMY_SP, + }; + let accessible_struct_span = self + .tcx + .def_ident_span(base_def.did()) + .unwrap_or_else(|| self.tcx.def_span(base_def.did())); + let deref_impl_span = (typeck_results + .expr_adjustments(base_expr) + .iter() + .filter(|adj| { + matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_))) + }) + .count() + == 1) + .then(|| { + self.probe(|_| { + let deref_trait_did = + self.tcx.require_lang_item(LangItem::Deref, DUMMY_SP); + let trait_ref = + ty::TraitRef::new(self.tcx, deref_trait_did, [private_base_ty]); + let obligation: Obligation<'tcx, ty::Predicate<'tcx>> = + Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + trait_ref, + ); + let Ok(Some(ImplSource::UserDefined(impl_data))) = + SelectionContext::new(self) + .select(&obligation.with(self.tcx, trait_ref)) + else { + return None; + }; + Some(self.tcx.def_span(impl_data.impl_def_id)) + }) + }) + .flatten(); + + let mut note_spans: MultiSpan = private_struct_span.into(); + if private_struct_span != DUMMY_SP { + note_spans.push_span_label(private_struct_span, "in this struct"); + } + if private_field_span != DUMMY_SP { + note_spans.push_span_label( + private_field_span, + "if this field wasn't private, it would be accessible", + ); + } + if accessible_struct_span != DUMMY_SP { + note_spans.push_span_label( + accessible_struct_span, + "this struct is accessible through auto-deref", + ); + } + if field_span != DUMMY_SP { + note_spans + .push_span_label(field_span, "this is the field that was accessed"); + } + if let Some(deref_impl_span) = deref_impl_span + && deref_impl_span != DUMMY_SP + { + note_spans.push_span_label( + deref_impl_span, + "the field was accessed through this `Deref`", + ); + } + + err.span_note( + note_spans, + format!( + "there is a field `{field_ident}` on `{private_base_ty}` with type `{private_field_ty}` but it is private; `{field_ident}` from `{deref_base_ty}` was accessed through auto-deref instead" + ), + ); } + + // we finally get to the accessible field, + // so we can return early without checking the rest of the autoderef candidates return; } - private_candidate.get_or_insert((deref_base_ty, field_def.ty(self.tcx, args))); + private_candidate.get_or_insert(( + deref_base_ty, + field_def.ty(self.tcx, args), + field_span, + )); } } diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs index e9013990d74cb..ec26c879d4eb3 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.rs @@ -1,6 +1,7 @@ // Field lookup still resolves to the public field on the Deref target, but // follow-up diagnostics should explain that the original type has a same-named // private field with a different type. +//@ dont-require-annotations: ERROR mod structs { pub struct A { @@ -24,18 +25,14 @@ mod structs { use structs::A; fn takes_usize(_: usize) {} -//~^ NOTE function defined here trait Marker {} impl Marker for usize {} -//~^ HELP the trait `Marker` is implemented for `usize` struct Wrapper(i32); impl std::ops::Add for Wrapper { - //~^ NOTE required for `Wrapper` to implement `Add` - //~| NOTE unsatisfied trait bound introduced here type Output = (); fn add(self, _: T) {} @@ -43,95 +40,49 @@ impl std::ops::Add for Wrapper { fn by_value(a: A) { a.field + 5; - //~^ ERROR cannot add `{integer}` to `bool` - //~| NOTE bool - //~| NOTE {integer} - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn by_ref(a: &A) { a.field + 5; - //~^ ERROR cannot add `{integer}` to `bool` - //~| NOTE bool - //~| NOTE {integer} - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn rhs_by_value(a: A) { 5 + a.field; - //~^ ERROR cannot add `bool` to `{integer}` - //~| NOTE no implementation for `{integer} + bool` - //~| HELP the trait `Add` is not implemented for `{integer}` - //~| HELP the following other types implement trait `Add`: - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn rhs_by_ref(a: &A) { 5 + a.field; - //~^ ERROR cannot add `bool` to `{integer}` - //~| NOTE no implementation for `{integer} + bool` - //~| HELP the trait `Add` is not implemented for `{integer}` - //~| HELP the following other types implement trait `Add`: - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn rhs_assign_op_by_value(a: A) { let mut n = 5; n += a.field; - //~^ ERROR cannot add-assign `bool` to `{integer}` - //~| NOTE no implementation for `{integer} += bool` - //~| HELP the trait `AddAssign` is not implemented for `{integer}` - //~| HELP the following other types implement trait `AddAssign`: - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn rhs_assign_op_by_ref(a: &A) { let mut n = 5; n += a.field; - //~^ ERROR cannot add-assign `bool` to `{integer}` - //~| NOTE no implementation for `{integer} += bool` - //~| HELP the trait `AddAssign` is not implemented for `{integer}` - //~| HELP the following other types implement trait `AddAssign`: - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn rhs_nested_obligation(a: A) { Wrapper(5) + a.field; - //~^ ERROR the trait bound `bool: Marker` is not satisfied - //~| NOTE the trait `Marker` is not implemented for `bool` - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn method_call(a: A) { a.field.count_ones(); - //~^ ERROR no method named `count_ones` found for type `bool` in the current scope - //~| NOTE method not found in `bool` - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn type_mismatch(a: A) { let value: usize = a.field; - //~^ ERROR mismatched types - //~| NOTE expected `usize`, found `bool` - //~| NOTE expected due to this - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private eprintln!("value: {value}"); } fn function_arg(a: A) { takes_usize(a.field); - //~^ ERROR mismatched types - //~| NOTE expected `usize`, found `bool` - //~| NOTE arguments to this function are incorrect - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn return_value(a: &A) -> usize { - //~^ NOTE expected `usize` because of return type a.field - //~^ ERROR mismatched types - //~| NOTE expected `usize`, found `bool` - //~| NOTE there is a field `field` on `A` with type `usize`, but it is private } fn main() {} diff --git a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr index d10d9a771b079..9f2bb523913c8 100644 --- a/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr +++ b/tests/ui/privacy/private-field-deref-confusion-issue-149546.stderr @@ -1,31 +1,73 @@ error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:45:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:42:13 | LL | a.field + 5; | ------- ^ - {integer} | | | bool | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` error[E0369]: cannot add `{integer}` to `bool` - --> $DIR/private-field-deref-confusion-issue-149546.rs:53:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:46:13 | LL | a.field + 5; | ------- ^ - {integer} | | | bool | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:61:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:50:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` | = help: the trait `Add` is not implemented for `{integer}` - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` = help: the following other types implement trait `Add`: `&f128` implements `Add` `&f128` implements `Add` @@ -38,13 +80,27 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:70:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:54:7 | LL | 5 + a.field; | ^ no implementation for `{integer} + bool` | = help: the trait `Add` is not implemented for `{integer}` - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` = help: the following other types implement trait `Add`: `&f128` implements `Add` `&f128` implements `Add` @@ -57,13 +113,27 @@ LL | 5 + a.field; and 56 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:80:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:59:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` | = help: the trait `AddAssign` is not implemented for `{integer}` - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` = help: the following other types implement trait `AddAssign`: `f128` implements `AddAssign<&f128>` `f128` implements `AddAssign` @@ -76,13 +146,27 @@ LL | n += a.field; and 24 others error[E0277]: cannot add-assign `bool` to `{integer}` - --> $DIR/private-field-deref-confusion-issue-149546.rs:90:7 + --> $DIR/private-field-deref-confusion-issue-149546.rs:64:7 | LL | n += a.field; | ^^ no implementation for `{integer} += bool` | = help: the trait `AddAssign` is not implemented for `{integer}` - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` = help: the following other types implement trait `AddAssign`: `f128` implements `AddAssign<&f128>` `f128` implements `AddAssign` @@ -95,19 +179,33 @@ LL | n += a.field; and 24 others error[E0277]: the trait bound `bool: Marker` is not satisfied - --> $DIR/private-field-deref-confusion-issue-149546.rs:99:16 + --> $DIR/private-field-deref-confusion-issue-149546.rs:68:16 | LL | Wrapper(5) + a.field; | ^ the trait `Marker` is not implemented for `bool` | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` help: the trait `Marker` is implemented for `usize` --> $DIR/private-field-deref-confusion-issue-149546.rs:31:1 | LL | impl Marker for usize {} | ^^^^^^^^^^^^^^^^^^^^^ note: required for `Wrapper` to implement `Add` - --> $DIR/private-field-deref-confusion-issue-149546.rs:36:17 + --> $DIR/private-field-deref-confusion-issue-149546.rs:35:17 | LL | impl std::ops::Add for Wrapper { | ------ ^^^^^^^^^^^^^^^^ ^^^^^^^ @@ -115,48 +213,103 @@ LL | impl std::ops::Add for Wrapper { | unsatisfied trait bound introduced here error[E0599]: no method named `count_ones` found for type `bool` in the current scope - --> $DIR/private-field-deref-confusion-issue-149546.rs:106:13 + --> $DIR/private-field-deref-confusion-issue-149546.rs:72:13 | LL | a.field.count_ones(); | ^^^^^^^^^^ method not found in `bool` | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` error[E0308]: mismatched types - --> $DIR/private-field-deref-confusion-issue-149546.rs:113:24 + --> $DIR/private-field-deref-confusion-issue-149546.rs:76:24 | LL | let value: usize = a.field; | ----- ^^^^^^^ expected `usize`, found `bool` | | | expected due to this | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` error[E0308]: mismatched types - --> $DIR/private-field-deref-confusion-issue-149546.rs:122:17 + --> $DIR/private-field-deref-confusion-issue-149546.rs:81:17 | LL | takes_usize(a.field); | ----------- ^^^^^^^ expected `usize`, found `bool` | | | arguments to this function are incorrect | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` note: function defined here - --> $DIR/private-field-deref-confusion-issue-149546.rs:26:4 + --> $DIR/private-field-deref-confusion-issue-149546.rs:27:4 | LL | fn takes_usize(_: usize) {} | ^^^^^^^^^^^ -------- error[E0308]: mismatched types - --> $DIR/private-field-deref-confusion-issue-149546.rs:131:5 + --> $DIR/private-field-deref-confusion-issue-149546.rs:85:5 | LL | fn return_value(a: &A) -> usize { | ----- expected `usize` because of return type -LL | LL | a.field | ^^^^^^^ expected `usize`, found `bool` | - = note: there is a field `field` on `A` with type `usize`, but it is private +note: there is a field `field` on `A` with type `usize` but it is private; `field` from `B` was accessed through auto-deref instead + --> $DIR/private-field-deref-confusion-issue-149546.rs:7:16 + | +LL | pub struct A { + | ^ in this struct +LL | field: usize, + | ----- if this field wasn't private, it would be accessible +... +LL | pub struct B { + | - this struct is accessible through auto-deref +LL | pub field: bool, + | ----- this is the field that was accessed +... +LL | impl std::ops::Deref for A { + | -------------------------- the field was accessed through this `Deref` error: aborting due to 11 previous errors