Skip to content
24 changes: 9 additions & 15 deletions compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -131,7 +130,6 @@ fn parse_directive_items<'p, S: Stage>(
let Some(ret) = (||{
Some($($code)*)
})() else {

malformed!()
};
ret
Expand Down Expand Up @@ -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<Ident> = match item.args().name_value() {
Some(nv) => Some(or_malformed!(nv.value_as_ident()?)),
None => None,
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -290,7 +285,6 @@ fn parse_directive_items<'p, S: Stage>(
label,
notes,
parent_label,
append_const_msg,
})
}

Expand Down
16 changes: 0 additions & 16 deletions compiler/rustc_hir/src/attrs/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ pub struct Directive {
pub label: Option<(Span, FormatString)>,
pub notes: ThinVec<FormatString>,
pub parent_label: Option<FormatString>,
pub append_const_msg: Option<AppendConstMessage>,
}

impl Directive {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -91,16 +89,13 @@ 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 {
label: label.map(|l| l.1.format(args)),
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,
}
}
}
Expand All @@ -111,17 +106,6 @@ pub struct OnUnimplementedNote {
pub label: Option<String>,
pub notes: Vec<String>,
pub parent_label: Option<String>,
// If none, should fall back to a generic message
pub append_const_msg: Option<AppendConstMessage>,
}

/// 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",
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
27 changes: 27 additions & 0 deletions compiler/rustc_hir_typeck/src/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -193,10 +193,16 @@ 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();

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(),
Expand All @@ -210,16 +216,15 @@ 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,
);
// 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()],
Some(AppendConstMessage::Default),
)
} else if is_question_mark {
let main_trait_predicate =
Expand All @@ -228,26 +233,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
// trait object: `-> Result<_, Box<dyn Error>` 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()],
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,
);

let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(
main_trait_predicate.def_id(),
LangItem::TransmuteTrait,
Expand All @@ -271,15 +266,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
}
GetSafeTransmuteErrorAndReason::Default => {
(default_err_msg(), None)
(message, None)
}
GetSafeTransmuteErrorAndReason::Error {
err_msg,
safe_transmute_explanation,
} => (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);
Expand Down Expand Up @@ -393,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
Expand Down Expand Up @@ -559,6 +554,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
}

self.note_field_shadowed_by_private_candidate_in_cause(
&mut err,
&obligation.cause,
obligation.param_env,
);
self.try_to_add_help_message(
&root_obligation,
&obligation,
Expand Down Expand Up @@ -857,9 +857,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {

let err_msg = self.get_standard_error_message(
trait_ref,
None,
Some(predicate.constness()),
None,
String::new(),
&mut file,
);
Expand Down Expand Up @@ -919,7 +917,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
label,
notes,
parent_label,
append_const_msg: _,
} = note;

if let Some(message) = message {
Expand Down Expand Up @@ -2836,40 +2833,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn get_standard_error_message(
&self,
trait_predicate: ty::PolyTraitPredicate<'tcx>,
message: Option<String>,
predicate_constness: Option<ty::BoundConstness>,
append_const_msg: Option<AppendConstMessage>,
post_message: String,
long_ty_path: &mut Option<PathBuf>,
) -> String {
message
.and_then(|cannot_do_this| {
match (predicate_constness, append_const_msg) {
// 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(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,
}
})
.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(
Expand Down
Loading
Loading