Fix subdiagnostics that use non-local variables

This commit is contained in:
Jonathan Brouwer
2026-02-27 15:22:47 +00:00
parent d52424c424
commit 350a964d7c
24 changed files with 123 additions and 65 deletions
@@ -88,6 +88,8 @@ pub(crate) enum AssocItemNotFoundLabel<'a> {
NotFound {
#[primary_span]
span: Span,
assoc_ident: Ident,
assoc_kind: &'static str,
},
#[label(
"there is {$identically_named ->
@@ -149,6 +151,7 @@ pub(crate) enum AssocItemNotFoundSugg<'a> {
trait_ref: String,
suggested_name: Symbol,
identically_named: bool,
assoc_kind: &'static str,
#[applicability]
applicability: Applicability,
},
@@ -141,7 +141,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
);
}
let assoc_kind_str = assoc_tag_str(assoc_tag);
let assoc_kind = assoc_tag_str(assoc_tag);
let qself_str = qself.to_string(tcx);
// The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
@@ -151,7 +151,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
let mut err = errors::AssocItemNotFound {
span: if is_dummy { span } else { assoc_ident.span },
assoc_ident,
assoc_kind: assoc_kind_str,
assoc_kind,
qself: &qself_str,
label: None,
sugg: None,
@@ -161,7 +161,8 @@ pub(super) fn report_unresolved_assoc_item<I>(
};
if is_dummy {
err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span });
err.label =
Some(errors::AssocItemNotFoundLabel::NotFound { span, assoc_ident, assoc_kind });
return self.dcx().emit_err(err);
}
@@ -181,7 +182,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
{
err.sugg = Some(errors::AssocItemNotFoundSugg::Similar {
span: assoc_ident.span,
assoc_kind: assoc_kind_str,
assoc_kind,
suggested_name,
});
return self.dcx().emit_err(err);
@@ -224,7 +225,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
let trait_name = tcx.def_path_str(best_trait);
err.label = Some(errors::AssocItemNotFoundLabel::FoundInOtherTrait {
span: assoc_ident.span,
assoc_kind: assoc_kind_str,
assoc_kind,
trait_name: &trait_name,
suggested_name,
identically_named: suggested_name == assoc_ident.name,
@@ -256,7 +257,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
span: assoc_ident.span,
trait_name: &trait_name,
assoc_kind: assoc_kind_str,
assoc_kind,
suggested_name,
});
return self.dcx().emit_err(err);
@@ -286,6 +287,7 @@ pub(super) fn report_unresolved_assoc_item<I>(
trait_ref,
identically_named,
suggested_name,
assoc_kind,
applicability,
});
} else {
@@ -322,11 +324,15 @@ pub(super) fn report_unresolved_assoc_item<I>(
err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
span: assoc_ident.span,
qself: &qself_str,
assoc_kind: assoc_kind_str,
assoc_kind,
suggested_name: *candidate_name,
});
} else {
err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_ident.span });
err.label = Some(errors::AssocItemNotFoundLabel::NotFound {
span: assoc_ident.span,
assoc_ident,
assoc_kind,
});
}
self.dcx().emit_err(err)
+3 -2
View File
@@ -2320,8 +2320,9 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
if features.incomplete(name) {
let note = rustc_feature::find_feature_issue(name, GateIssue::Language)
.map(|n| BuiltinFeatureIssueNote { n });
let help =
HAS_MIN_FEATURES.contains(&name).then_some(BuiltinIncompleteFeaturesHelp);
let help = HAS_MIN_FEATURES
.contains(&name)
.then_some(BuiltinIncompleteFeaturesHelp { name });
cx.emit_span_lint(
INCOMPLETE_FEATURES,
@@ -87,7 +87,10 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
ty::AssocTag::Type,
item.owner_id.to_def_id(),
)
.map(|label| SupertraitAsDerefTargetLabel { label: tcx.def_span(label.def_id) });
.map(|label| SupertraitAsDerefTargetLabel {
label: tcx.def_span(label.def_id),
self_ty,
});
let span = tcx.def_span(item.owner_id.def_id);
cx.emit_span_lint(
DEREF_INTO_DYN_SUPERTRAIT,
+5 -7
View File
@@ -105,13 +105,11 @@ pub fn decorate_builtin_lint(
BuiltinLintDiag::RedundantImport(spans, ident) => {
let subs = spans
.into_iter()
.map(|(span, is_imported)| {
(match (span.is_dummy(), is_imported) {
(false, true) => lints::RedundantImportSub::ImportedHere,
(false, false) => lints::RedundantImportSub::DefinedHere,
(true, true) => lints::RedundantImportSub::ImportedPrelude,
(true, false) => lints::RedundantImportSub::DefinedPrelude,
})(span)
.map(|(span, is_imported)| match (span.is_dummy(), is_imported) {
(false, true) => lints::RedundantImportSub::ImportedHere { span, ident },
(false, false) => lints::RedundantImportSub::DefinedHere { span, ident },
(true, true) => lints::RedundantImportSub::ImportedPrelude { span, ident },
(true, false) => lints::RedundantImportSub::DefinedPrelude { span, ident },
})
.collect();
lints::RedundantImport { subs, ident }.decorate_lint(diag);
@@ -105,9 +105,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
Some(ConstItemInteriorMutationsSuggestionStatic::Spanful {
const_: const_item.vis_span.between(ident.span),
before: if !vis_span.is_empty() { " " } else { "" },
const_name,
})
} else {
Some(ConstItemInteriorMutationsSuggestionStatic::Spanless)
Some(ConstItemInteriorMutationsSuggestionStatic::Spanless { const_name })
}
} else {
None
+33 -16
View File
@@ -532,7 +532,9 @@ pub(crate) struct BuiltinInternalFeatures {
#[derive(Subdiagnostic)]
#[help("consider using `min_{$name}` instead, which is more stable and complete")]
pub(crate) struct BuiltinIncompleteFeaturesHelp;
pub(crate) struct BuiltinIncompleteFeaturesHelp {
pub name: Symbol,
}
#[derive(Subdiagnostic)]
#[note("see issue #{$n} <https://github.com/rust-lang/rust/issues/{$n}> for more information")]
@@ -663,14 +665,15 @@ pub(crate) struct SupertraitAsDerefTarget<'a> {
)]
pub label: Span,
#[subdiagnostic]
pub label2: Option<SupertraitAsDerefTargetLabel>,
pub label2: Option<SupertraitAsDerefTargetLabel<'a>>,
}
#[derive(Subdiagnostic)]
#[label("target type is a supertrait of `{$self_ty}`")]
pub(crate) struct SupertraitAsDerefTargetLabel {
pub(crate) struct SupertraitAsDerefTargetLabel<'a> {
#[primary_span]
pub label: Span,
pub self_ty: Ty<'a>,
}
// enum_intrinsics_non_enums.rs
@@ -958,9 +961,10 @@ pub(crate) enum ConstItemInteriorMutationsSuggestionStatic {
#[primary_span]
const_: Span,
before: &'static str,
const_name: Ident,
},
#[help("for a shared instance of `{$const_name}`, consider making it a `static` item instead")]
Spanless,
Spanless { const_name: Ident },
}
// reference_casting.rs
@@ -1972,11 +1976,8 @@ pub(crate) enum UseInclusiveRange<'a> {
#[diag("literal out of range for `{$ty}`")]
pub(crate) struct OverflowingBinHex<'a> {
pub ty: &'a str,
pub lit: String,
pub dec: u128,
pub actually: String,
#[subdiagnostic]
pub sign: OverflowingBinHexSign,
pub sign: OverflowingBinHexSign<'a>,
#[subdiagnostic]
pub sub: Option<OverflowingBinHexSub<'a>>,
#[subdiagnostic]
@@ -1984,14 +1985,14 @@ pub(crate) struct OverflowingBinHex<'a> {
}
#[derive(Subdiagnostic)]
pub(crate) enum OverflowingBinHexSign {
pub(crate) enum OverflowingBinHexSign<'a> {
#[note(
"the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}`"
)]
Positive,
Positive { lit: String, ty: &'a str, actually: String, dec: u128 },
#[note("the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}`")]
#[note("and the value `-{$lit}` will become `{$actually}{$ty}`")]
Negative,
Negative { lit: String, ty: &'a str, actually: String, dec: u128 },
}
#[derive(Subdiagnostic)]
@@ -2562,6 +2563,7 @@ pub(crate) struct UnusedDelimSuggestion {
#[suggestion_part(code = "{end_replace}")]
pub end_span: Span,
pub end_replace: &'static str,
pub delim: &'static str,
}
#[derive(Diagnostic)]
@@ -3131,20 +3133,35 @@ pub(crate) enum UnusedImportsSugg {
pub(crate) struct RedundantImport {
#[subdiagnostic]
pub subs: Vec<RedundantImportSub>,
pub ident: Ident,
}
#[derive(Subdiagnostic)]
pub(crate) enum RedundantImportSub {
#[label("the item `{$ident}` is already imported here")]
ImportedHere(#[primary_span] Span),
ImportedHere {
#[primary_span]
span: Span,
ident: Ident,
},
#[label("the item `{$ident}` is already defined here")]
DefinedHere(#[primary_span] Span),
DefinedHere {
#[primary_span]
span: Span,
ident: Ident,
},
#[label("the item `{$ident}` is already imported by the extern prelude")]
ImportedPrelude(#[primary_span] Span),
ImportedPrelude {
#[primary_span]
span: Span,
ident: Ident,
},
#[label("the item `{$ident}` is already defined by the extern prelude")]
DefinedPrelude(#[primary_span] Span),
DefinedPrelude {
#[primary_span]
span: Span,
ident: Ident,
},
}
#[derive(LintDiagnostic)]
+17 -12
View File
@@ -157,8 +157,21 @@ fn report_bin_hex_error(
(t.name_str(), actually.to_string())
}
};
let sign =
if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
let sign = if negative {
OverflowingBinHexSign::Negative {
lit: repr_str.clone(),
dec: val,
actually: actually.clone(),
ty: t,
}
} else {
OverflowingBinHexSign::Positive {
lit: repr_str.clone(),
dec: val,
actually: actually.clone(),
ty: t,
}
};
let sub = get_type_suggestion(cx.typeck_results().node_type(hir_id), val, negative).map(
|suggestion_ty| {
if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
@@ -194,7 +207,7 @@ fn report_bin_hex_error(
Some(OverflowingBinHexSignBitSub {
span,
lit_no_suffix,
negative_val: actually.clone(),
negative_val: actually,
int_ty: int_ty.name_str(),
uint_ty: Integer::fit_unsigned(val).uint_ty_str(),
})
@@ -204,15 +217,7 @@ fn report_bin_hex_error(
cx.emit_span_lint(
OVERFLOWING_LITERALS,
span,
OverflowingBinHex {
ty: t,
lit: repr_str.clone(),
dec: val,
actually,
sign,
sub,
sign_bit_sub,
},
OverflowingBinHex { ty: t, sign, sub, sign_bit_sub },
)
}
+1
View File
@@ -350,6 +350,7 @@ fn emit_unused_delims(
start_replace: lo_replace,
end_span: hi,
end_replace: hi_replace,
delim: Self::DELIM_STR,
}
});
cx.emit_span_lint(
@@ -315,7 +315,7 @@ fn add_library(
crate_name: tcx.crate_name(cnum),
non_static_deps: unavailable_as_static
.drain(..)
.map(|cnum| NonStaticCrateDep { crate_name_: tcx.crate_name(cnum) })
.map(|cnum| NonStaticCrateDep { sub_crate_name: tcx.crate_name(cnum) })
.collect(),
rustc_driver_help: linking_to_rustc_driver,
});
+2 -2
View File
@@ -48,10 +48,10 @@ pub struct CrateDepMultiple {
}
#[derive(Subdiagnostic)]
#[note("`{$crate_name}` was unavailable as a static crate, preventing fully static linking")]
#[note("`{$sub_crate_name}` was unavailable as a static crate, preventing fully static linking")]
pub struct NonStaticCrateDep {
/// It's different from `crate_name` in main Diagnostic.
pub crate_name_: Symbol,
pub sub_crate_name: Symbol,
}
#[derive(Diagnostic)]
@@ -115,12 +115,12 @@ fn emit_deprecated_safe_fn_call(&self, span: Span, kind: &UnsafeOpKind) -> bool
CallToDeprecatedSafeFnRequiresUnsafe {
span,
function: with_no_trimmed_paths!(self.tcx.def_path_str(id)),
guarantee,
sub: CallToDeprecatedSafeFnRequiresUnsafeSub {
start_of_line_suggestion: suggestion,
start_of_line: sm.span_extend_to_line(span).shrink_to_lo(),
left: span.shrink_to_lo(),
right: span.shrink_to_hi(),
guarantee,
},
},
);
+1 -1
View File
@@ -15,7 +15,6 @@ pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe {
#[label("call to unsafe function")]
pub(crate) span: Span,
pub(crate) function: String,
pub(crate) guarantee: String,
#[subdiagnostic]
pub(crate) sub: CallToDeprecatedSafeFnRequiresUnsafeSub,
}
@@ -33,6 +32,7 @@ pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafeSub {
pub(crate) left: Span,
#[suggestion_part(code = " }}")]
pub(crate) right: Span,
pub(crate) guarantee: String,
}
#[derive(Diagnostic)]
@@ -389,4 +389,5 @@ pub(crate) struct ForceInlineFailure {
#[note("`{$callee}` is required to be inlined to: {$sym}")]
pub(crate) struct ForceInlineJustification {
pub sym: Symbol,
pub callee: String,
}
+4 -2
View File
@@ -251,15 +251,17 @@ fn on_inline_failure(&self, callsite: &CallSite<'tcx>, reason: &'static str) {
};
let call_span = callsite.source_info.span;
let callee = tcx.def_path_str(callsite.callee.def_id());
tcx.dcx().emit_err(crate::errors::ForceInlineFailure {
call_span,
attr_span,
caller_span: tcx.def_span(self.def_id),
caller: tcx.def_path_str(self.def_id),
callee_span: tcx.def_span(callsite.callee.def_id()),
callee: tcx.def_path_str(callsite.callee.def_id()),
callee: callee.clone(),
reason,
justification: justification.map(|sym| crate::errors::ForceInlineJustification { sym }),
justification: justification
.map(|sym| crate::errors::ForceInlineJustification { sym, callee }),
});
}
}
+3 -1
View File
@@ -1016,7 +1016,6 @@ pub(crate) struct LeadingPlusNotSupported {
pub(crate) struct ParenthesesWithStructFields {
#[primary_span]
pub span: Span,
pub r#type: Path,
#[subdiagnostic]
pub braces_for_struct: BracesForStructLiteral,
#[subdiagnostic]
@@ -1029,6 +1028,7 @@ pub(crate) struct ParenthesesWithStructFields {
applicability = "maybe-incorrect"
)]
pub(crate) struct BracesForStructLiteral {
pub r#type: Path,
#[suggestion_part(code = " {{ ")]
pub first: Span,
#[suggestion_part(code = " }}")]
@@ -1041,6 +1041,7 @@ pub(crate) struct BracesForStructLiteral {
applicability = "maybe-incorrect"
)]
pub(crate) struct NoFieldsForFnCall {
pub r#type: Path,
#[suggestion_part(code = "")]
pub fields: Vec<Span>,
}
@@ -3176,6 +3177,7 @@ pub(crate) struct UnexpectedVertVertInPattern {
pub(crate) struct TrailingVertSuggestion {
#[primary_span]
pub span: Span,
pub token: Token,
}
#[derive(Diagnostic)]
+2 -1
View File
@@ -1309,12 +1309,13 @@ fn maybe_recover_struct_lit_bad_delims(
self.dcx()
.create_err(errors::ParenthesesWithStructFields {
span,
r#type: path,
braces_for_struct: errors::BracesForStructLiteral {
first: open_paren,
second: close_paren,
r#type: path.clone(),
},
no_fields_for_fn: errors::NoFieldsForFnCall {
r#type: path,
fields: fields
.into_iter()
.map(|field| field.span.until(field.expr.span))
+1
View File
@@ -364,6 +364,7 @@ fn recover_trailing_vert(&mut self, lo: Option<Span>) -> bool {
start: lo,
suggestion: TrailingVertSuggestion {
span: self.prev_token.span.shrink_to_hi().with_hi(self.token.span.hi()),
token: self.token,
},
token: self.token,
note_double_vert: self.token.kind == token::OrOr,
+2 -2
View File
@@ -35,9 +35,9 @@ pub(crate) struct CycleStack {
#[derive(Subdiagnostic)]
pub(crate) enum StackCount {
#[note("...which immediately requires {$stack_bottom} again")]
Single,
Single { stack_bottom: String },
#[note("...which again requires {$stack_bottom}, completing the cycle")]
Multiple,
Multiple { stack_bottom: String },
}
#[derive(Subdiagnostic)]
+7 -2
View File
@@ -438,7 +438,12 @@ pub(crate) fn report_cycle<'a>(
let mut cycle_stack = Vec::new();
use crate::error::StackCount;
let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple };
let stack_bottom = stack[0].frame.info.description.to_owned();
let stack_count = if stack.len() == 1 {
StackCount::Single { stack_bottom: stack_bottom.clone() }
} else {
StackCount::Multiple { stack_bottom: stack_bottom.clone() }
};
for i in 1..stack.len() {
let frame = &stack[i].frame;
@@ -467,7 +472,7 @@ pub(crate) fn report_cycle<'a>(
let cycle_diag = crate::error::Cycle {
span,
cycle_stack,
stack_bottom: stack[0].frame.info.description.to_owned(),
stack_bottom,
alias,
cycle_usage,
stack_count,
+8 -5
View File
@@ -249,20 +249,23 @@ pub(crate) fn report_conflict(
};
let label = match new_binding.is_import_user_facing() {
true => errors::NameDefinedMultipleTimeLabel::Reimported { span },
false => errors::NameDefinedMultipleTimeLabel::Redefined { span },
true => errors::NameDefinedMultipleTimeLabel::Reimported { span, name },
false => errors::NameDefinedMultipleTimeLabel::Redefined { span, name },
};
let old_binding_label =
(!old_binding.span.is_dummy() && old_binding.span != span).then(|| {
let span = self.tcx.sess.source_map().guess_head_span(old_binding.span);
match old_binding.is_import_user_facing() {
true => {
errors::NameDefinedMultipleTimeOldBindingLabel::Import { span, old_kind }
}
true => errors::NameDefinedMultipleTimeOldBindingLabel::Import {
span,
old_kind,
name,
},
false => errors::NameDefinedMultipleTimeOldBindingLabel::Definition {
span,
old_kind,
name,
},
}
});
+4
View File
@@ -1126,11 +1126,13 @@ pub(crate) enum NameDefinedMultipleTimeLabel {
Reimported {
#[primary_span]
span: Span,
name: Symbol,
},
#[label("`{$name}` redefined here")]
Redefined {
#[primary_span]
span: Span,
name: Symbol,
},
}
@@ -1141,12 +1143,14 @@ pub(crate) enum NameDefinedMultipleTimeOldBindingLabel {
#[primary_span]
span: Span,
old_kind: &'static str,
name: Symbol,
},
#[label("previous definition of the {$old_kind} `{$name}` here")]
Definition {
#[primary_span]
span: Span,
old_kind: &'static str,
name: Symbol,
},
}
@@ -3288,12 +3288,14 @@ fn report_closure_error(
err.fn_once_label = Some(ClosureFnOnceLabel {
span: *span,
place: ty::place_to_string_for_capture(self.tcx, place),
trait_prefix,
})
}
(ty::ClosureKind::FnMut, Some((span, place))) => {
err.fn_mut_label = Some(ClosureFnMutLabel {
span: *span,
place: ty::place_to_string_for_capture(self.tcx, place),
trait_prefix,
})
}
_ => {}
@@ -157,6 +157,7 @@ pub struct ClosureFnOnceLabel {
#[primary_span]
pub span: Span,
pub place: String,
pub trait_prefix: &'static str,
}
#[derive(Subdiagnostic)]
@@ -165,6 +166,7 @@ pub struct ClosureFnMutLabel {
#[primary_span]
pub span: Span,
pub place: String,
pub trait_prefix: &'static str,
}
#[derive(Diagnostic)]