Skip to content

Commit 63c5a84

Browse files
authored
Rollup merge of #142724 - xizheyin:avoid_overwrite_args, r=oli-obk
Add runtime check to avoid overwrite arg in `Diag` ## Origin PR description At first, I set up a `debug_assert` check for the arg method to make sure that `args` in `Diag` aren't easily overwritten, and I added the `remove_arg()` method, so that if you do need to overwrite an arg, then you can explicitly call `remove_arg()` to remove it first, then call `arg()` to overwrite it. For the code before the #142015 change, it won't compile because it will report an error ``` arg `instance`already exists. ``` This PR also modifies all diagnostics that fail the check to pass the check. There are two cases of check failure: 1. ~~Between *the parent diagnostic and the subdiagnostic*, or *between the subdiagnostics* have the same field between them. In this case, I renamed the conflicting fields.~~ 2. ~~For subdiagnostics stored in `Vec`, the rendering may iteratively write the same arg over and over again. In this case, I changed the auto-generation with `derive(SubDiagnostic)` to manually implementing `SubDiagnostic` and manually rendered it with `eagerly_translate()`, similar to #142031 (comment), and after rendering it I manually deleted useless arg with the newly added `remove_arg` method.~~ ## Final Decision After trying and discussing, we made a final decision. For `#[derive(Subdiagnostic)]`, This PR made two changes: 1. After the subdiagnostic is rendered, remove all args of this subdiagnostic, which allows for usage like `Vec<Subdiag>`. 2. Store `diag.args` before setting arguments, so that you can restore the contents of the main diagnostic after deleting the arguments after subdiagnostic is rendered, to avoid deleting the main diagnostic's arg when they have the same name args.
2 parents 131a2e4 + d2d17c6 commit 63c5a84

File tree

22 files changed

+119
-39
lines changed

22 files changed

+119
-39
lines changed

compiler/rustc_borrowck/src/diagnostics/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1284,8 +1284,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12841284
&& !spans.is_empty()
12851285
{
12861286
let mut span: MultiSpan = spans.clone().into();
1287+
err.arg("ty", param_ty.to_string());
1288+
let msg = err.dcx.eagerly_translate_to_string(
1289+
fluent::borrowck_moved_a_fn_once_in_call_def,
1290+
err.args.iter(),
1291+
);
1292+
err.remove_arg("ty");
12871293
for sp in spans {
1288-
span.push_span_label(sp, fluent::borrowck_moved_a_fn_once_in_call_def);
1294+
span.push_span_label(sp, msg.clone());
12891295
}
12901296
span.push_span_label(
12911297
fn_call_span,

compiler/rustc_builtin_macros/src/errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,7 @@ impl Subdiagnostic for FormatUnusedArg {
660660
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
661661
diag.arg("named", self.named);
662662
let msg = diag.eagerly_translate(crate::fluent_generated::builtin_macros_format_unused_arg);
663+
diag.remove_arg("named");
663664
diag.span_label(self.span, msg);
664665
}
665666
}

compiler/rustc_const_eval/src/errors.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@ impl Subdiagnostic for FrameNote {
291291
span.push_span_label(self.span, fluent::const_eval_frame_note_last);
292292
}
293293
let msg = diag.eagerly_translate(fluent::const_eval_frame_note);
294+
diag.remove_arg("times");
295+
diag.remove_arg("where_");
296+
diag.remove_arg("instance");
294297
diag.span_note(span, msg);
295298
}
296299
}

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,9 @@ pub struct DiagInner {
289289
pub suggestions: Suggestions,
290290
pub args: DiagArgMap,
291291

292+
// This is used to store args and restore them after a subdiagnostic is rendered.
293+
pub reserved_args: DiagArgMap,
294+
292295
/// This is not used for highlighting or rendering any error message. Rather, it can be used
293296
/// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
294297
/// `span` if there is one. Otherwise, it is `DUMMY_SP`.
@@ -319,6 +322,7 @@ impl DiagInner {
319322
children: vec![],
320323
suggestions: Suggestions::Enabled(vec![]),
321324
args: Default::default(),
325+
reserved_args: Default::default(),
322326
sort_span: DUMMY_SP,
323327
is_lint: None,
324328
long_ty_path: None,
@@ -390,7 +394,27 @@ impl DiagInner {
390394
}
391395

392396
pub(crate) fn arg(&mut self, name: impl Into<DiagArgName>, arg: impl IntoDiagArg) {
393-
self.args.insert(name.into(), arg.into_diag_arg(&mut self.long_ty_path));
397+
let name = name.into();
398+
let value = arg.into_diag_arg(&mut self.long_ty_path);
399+
// This assertion is to avoid subdiagnostics overwriting an existing diagnostic arg.
400+
debug_assert!(
401+
!self.args.contains_key(&name) || self.args.get(&name) == Some(&value),
402+
"arg {} already exists",
403+
name
404+
);
405+
self.args.insert(name, value);
406+
}
407+
408+
pub fn remove_arg(&mut self, name: &str) {
409+
self.args.swap_remove(name);
410+
}
411+
412+
pub fn store_args(&mut self) {
413+
self.reserved_args = self.args.clone();
414+
}
415+
416+
pub fn restore_args(&mut self) {
417+
self.args = std::mem::take(&mut self.reserved_args);
394418
}
395419

396420
/// Fields used for Hash, and PartialEq trait.
@@ -1423,6 +1447,12 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
14231447
self.downgrade_to_delayed_bug();
14241448
self.emit()
14251449
}
1450+
1451+
pub fn remove_arg(&mut self, name: &str) {
1452+
if let Some(diag) = self.diag.as_mut() {
1453+
diag.remove_arg(name);
1454+
}
1455+
}
14261456
}
14271457

14281458
/// Destructor bomb: every `Diag` must be consumed (emitted, cancelled, etc.)

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ pub(crate) enum AssocItemNotFoundSugg<'a> {
127127
SimilarInOtherTrait {
128128
#[primary_span]
129129
span: Span,
130+
trait_name: &'a str,
130131
assoc_kind: &'static str,
131132
suggested_name: Symbol,
132133
},

compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
309309
// change the associated item.
310310
err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
311311
span: assoc_ident.span,
312+
trait_name: &trait_name,
312313
assoc_kind: assoc_kind_str,
313314
suggested_name,
314315
});

compiler/rustc_lint/src/levels.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
482482
let name = lint_name.as_str();
483483
let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
484484
let requested_level = RequestedLevel { level, lint_name };
485-
let lint = RenamedLintFromCommandLine { name, suggestion, requested_level };
485+
let lint =
486+
RenamedLintFromCommandLine { name, replace, suggestion, requested_level };
486487
self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
487488
}
488489
CheckLintNameResult::Removed(ref reason) => {
@@ -824,7 +825,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
824825
RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
825826
let name =
826827
tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
827-
let lint = RenamedLint { name: name.as_str(), suggestion };
828+
let lint = RenamedLint { name: name.as_str(), replace, suggestion };
828829
self.emit_span_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
829830
}
830831

compiler/rustc_lint/src/lifetime_syntax.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,12 +422,12 @@ fn build_mismatch_suggestion(
422422
lifetime_name: &str,
423423
infos: &[&Info<'_>],
424424
) -> lints::MismatchedLifetimeSyntaxesSuggestion {
425-
let lifetime_name = lifetime_name.to_owned();
425+
let lifetime_name_sugg = lifetime_name.to_owned();
426426

427427
let suggestions = infos.iter().map(|info| info.suggestion(&lifetime_name)).collect();
428428

429429
lints::MismatchedLifetimeSyntaxesSuggestion::Explicit {
430-
lifetime_name,
430+
lifetime_name_sugg,
431431
suggestions,
432432
tool_only: false,
433433
}

compiler/rustc_lint/src/lints.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,7 @@ pub(crate) struct DeprecatedLintNameFromCommandLine<'a> {
10891089
#[diag(lint_renamed_lint)]
10901090
pub(crate) struct RenamedLint<'a> {
10911091
pub name: &'a str,
1092+
pub replace: &'a str,
10921093
#[subdiagnostic]
10931094
pub suggestion: RenamedLintSuggestion<'a>,
10941095
}
@@ -1109,6 +1110,7 @@ pub(crate) enum RenamedLintSuggestion<'a> {
11091110
#[diag(lint_renamed_lint)]
11101111
pub(crate) struct RenamedLintFromCommandLine<'a> {
11111112
pub name: &'a str,
1113+
pub replace: &'a str,
11121114
#[subdiagnostic]
11131115
pub suggestion: RenamedLintSuggestion<'a>,
11141116
#[subdiagnostic]
@@ -3244,7 +3246,7 @@ pub(crate) enum MismatchedLifetimeSyntaxesSuggestion {
32443246
},
32453247

32463248
Explicit {
3247-
lifetime_name: String,
3249+
lifetime_name_sugg: String,
32483250
suggestions: Vec<(Span, String)>,
32493251
tool_only: bool,
32503252
},
@@ -3298,13 +3300,12 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
32983300
);
32993301
}
33003302

3301-
Explicit { lifetime_name, suggestions, tool_only } => {
3302-
diag.arg("lifetime_name", lifetime_name);
3303-
3303+
Explicit { lifetime_name_sugg, suggestions, tool_only } => {
3304+
diag.arg("lifetime_name_sugg", lifetime_name_sugg);
33043305
let msg = diag.eagerly_translate(
33053306
fluent::lint_mismatched_lifetime_syntaxes_suggestion_explicit,
33063307
);
3307-
3308+
diag.remove_arg("lifetime_name_sugg");
33083309
diag.multipart_suggestion_with_style(
33093310
msg,
33103311
suggestions,

compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
220220
}
221221

222222
/// Generates the code for a field with no attributes.
223-
fn generate_field_arg(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
223+
fn generate_field_arg(&mut self, binding_info: &BindingInfo<'_>) -> (TokenStream, TokenStream) {
224224
let diag = &self.parent.diag;
225225

226226
let field = binding_info.ast();
@@ -230,12 +230,16 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
230230
let ident = field.ident.as_ref().unwrap();
231231
let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
232232

233-
quote! {
233+
let args = quote! {
234234
#diag.arg(
235235
stringify!(#ident),
236236
#field_binding
237237
);
238-
}
238+
};
239+
let remove_args = quote! {
240+
#diag.remove_arg(stringify!(#ident));
241+
};
242+
(args, remove_args)
239243
}
240244

241245
/// Generates the necessary code for all attributes on a field.
@@ -600,8 +604,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
600604

601605
calls.extend(call);
602606
}
603-
604-
let plain_args: TokenStream = self
607+
let store_args = quote! {
608+
#diag.store_args();
609+
};
610+
let restore_args = quote! {
611+
#diag.restore_args();
612+
};
613+
let (plain_args, remove_args): (TokenStream, TokenStream) = self
605614
.variant
606615
.bindings()
607616
.iter()
@@ -610,12 +619,23 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
610619
.collect();
611620

612621
let formatting_init = &self.formatting_init;
622+
623+
// For #[derive(Subdiagnostic)]
624+
//
625+
// - Store args of the main diagnostic for later restore.
626+
// - add args of subdiagnostic.
627+
// - Generate the calls, such as note, label, etc.
628+
// - Remove the arguments for allowing Vec<Subdiagnostic> to be used.
629+
// - Restore the arguments for allowing main and subdiagnostic share the same fields.
613630
Ok(quote! {
614631
#init
615632
#formatting_init
616633
#attr_args
634+
#store_args
617635
#plain_args
618636
#calls
637+
#remove_args
638+
#restore_args
619639
})
620640
}
621641
}

compiler/rustc_metadata/src/dependency_format.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ fn add_library(
314314
crate_name: tcx.crate_name(cnum),
315315
non_static_deps: unavailable_as_static
316316
.drain(..)
317-
.map(|cnum| NonStaticCrateDep { crate_name: tcx.crate_name(cnum) })
317+
.map(|cnum| NonStaticCrateDep { crate_name_: tcx.crate_name(cnum) })
318318
.collect(),
319319
rustc_driver_help: linking_to_rustc_driver.then_some(RustcDriverHelp),
320320
});

compiler/rustc_metadata/src/errors.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ pub struct CrateDepMultiple {
4545
#[derive(Subdiagnostic)]
4646
#[note(metadata_crate_dep_not_static)]
4747
pub struct NonStaticCrateDep {
48-
pub crate_name: Symbol,
48+
/// It's different from `crate_name` in main Diagnostic.
49+
pub crate_name_: Symbol,
4950
}
5051

5152
#[derive(Subdiagnostic)]

compiler/rustc_mir_build/src/errors.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -994,14 +994,15 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> {
994994
pub(crate) uncovered: Uncovered,
995995
#[subdiagnostic]
996996
pub(crate) inform: Option<Inform>,
997-
#[label(mir_build_confused)]
998-
pub(crate) interpreted_as_const: Option<Span>,
999997
#[subdiagnostic]
1000-
pub(crate) interpreted_as_const_sugg: Option<InterpretedAsConst>,
998+
pub(crate) interpreted_as_const: Option<InterpretedAsConst>,
999+
#[subdiagnostic]
1000+
pub(crate) interpreted_as_const_sugg: Option<InterpretedAsConstSugg>,
10011001
#[subdiagnostic]
10021002
pub(crate) adt_defined_here: Option<AdtDefinedHere<'tcx>>,
10031003
#[note(mir_build_privately_uninhabited)]
10041004
pub(crate) witness_1_is_privately_uninhabited: bool,
1005+
pub(crate) witness_1: String,
10051006
#[note(mir_build_pattern_ty)]
10061007
pub(crate) _p: (),
10071008
pub(crate) pattern_ty: Ty<'tcx>,
@@ -1016,6 +1017,14 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> {
10161017
#[note(mir_build_more_information)]
10171018
pub(crate) struct Inform;
10181019

1020+
#[derive(Subdiagnostic)]
1021+
#[label(mir_build_confused)]
1022+
pub(crate) struct InterpretedAsConst {
1023+
#[primary_span]
1024+
pub(crate) span: Span,
1025+
pub(crate) variable: String,
1026+
}
1027+
10191028
pub(crate) struct AdtDefinedHere<'tcx> {
10201029
pub(crate) adt_def_span: Span,
10211030
pub(crate) ty: Ty<'tcx>,
@@ -1046,7 +1055,7 @@ impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> {
10461055
applicability = "maybe-incorrect",
10471056
style = "verbose"
10481057
)]
1049-
pub(crate) struct InterpretedAsConst {
1058+
pub(crate) struct InterpretedAsConstSugg {
10501059
#[primary_span]
10511060
pub(crate) span: Span,
10521061
pub(crate) variable: String,

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -690,8 +690,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
690690
let span = self.tcx.def_span(def_id);
691691
let variable = self.tcx.item_name(def_id).to_string();
692692
// When we encounter a constant as the binding name, point at the `const` definition.
693-
interpreted_as_const = Some(span);
694-
interpreted_as_const_sugg = Some(InterpretedAsConst { span: pat.span, variable });
693+
interpreted_as_const = Some(InterpretedAsConst { span, variable: variable.clone() });
694+
interpreted_as_const_sugg = Some(InterpretedAsConstSugg { span: pat.span, variable });
695695
} else if let PatKind::Constant { .. } = unpeeled_pat.kind
696696
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(pat.span)
697697
{
@@ -743,6 +743,8 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
743743
false
744744
};
745745

746+
let witness_1 = cx.print_witness_pat(witnesses.get(0).unwrap());
747+
746748
self.error = Err(self.tcx.dcx().emit_err(PatternNotCovered {
747749
span: pat.span,
748750
origin,
@@ -751,6 +753,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
751753
interpreted_as_const,
752754
interpreted_as_const_sugg,
753755
witness_1_is_privately_uninhabited,
756+
witness_1,
754757
_p: (),
755758
pattern_ty,
756759
let_suggestion,

compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,12 @@ struct LocalLabel<'a> {
516516
/// A custom `Subdiagnostic` implementation so that the notes are delivered in a specific order
517517
impl Subdiagnostic for LocalLabel<'_> {
518518
fn add_to_diag<G: rustc_errors::EmissionGuarantee>(self, diag: &mut rustc_errors::Diag<'_, G>) {
519+
// Becuase parent uses this field , we need to remove it delay before adding it.
520+
diag.remove_arg("name");
519521
diag.arg("name", self.name);
522+
diag.remove_arg("is_generated_name");
520523
diag.arg("is_generated_name", self.is_generated_name);
524+
diag.remove_arg("is_dropped_first_edition_2024");
521525
diag.arg("is_dropped_first_edition_2024", self.is_dropped_first_edition_2024);
522526
let msg = diag.eagerly_translate(crate::fluent_generated::mir_transform_tail_expr_local);
523527
diag.span_label(self.span, msg);

compiler/rustc_passes/messages.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ passes_duplicate_lang_item_crate_depends =
294294
.second_definition_path = second definition in `{$crate_name}` loaded from {$path}
295295
296296
passes_enum_variant_same_name =
297-
it is impossible to refer to the {$descr} `{$dead_name}` because it is shadowed by this enum variant with the same name
297+
it is impossible to refer to the {$dead_descr} `{$dead_name}` because it is shadowed by this enum variant with the same name
298298
299299
passes_export_name =
300300
attribute should be applied to a free function, impl method or static

compiler/rustc_passes/src/dead.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,7 @@ impl<'tcx> DeadVisitor<'tcx> {
10561056
maybe_enum.variants().iter().find(|i| i.name == dead_item.name)
10571057
{
10581058
Some(crate::errors::EnumVariantSameName {
1059-
descr: tcx.def_descr(dead_item.def_id.to_def_id()),
1059+
dead_descr: tcx.def_descr(dead_item.def_id.to_def_id()),
10601060
dead_name: dead_item.name,
10611061
variant_span: tcx.def_span(variant.def_id),
10621062
})

compiler/rustc_passes/src/errors.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1516,7 +1516,7 @@ pub(crate) struct EnumVariantSameName<'tcx> {
15161516
#[primary_span]
15171517
pub variant_span: Span,
15181518
pub dead_name: Symbol,
1519-
pub descr: &'tcx str,
1519+
pub dead_descr: &'tcx str,
15201520
}
15211521

15221522
#[derive(Subdiagnostic)]
@@ -1714,6 +1714,7 @@ impl Subdiagnostic for UnusedVariableStringInterp {
17141714
#[derive(LintDiagnostic)]
17151715
#[diag(passes_unused_variable_try_ignore)]
17161716
pub(crate) struct UnusedVarTryIgnore {
1717+
pub name: String,
17171718
#[subdiagnostic]
17181719
pub sugg: UnusedVarTryIgnoreSugg,
17191720
}

compiler/rustc_passes/src/liveness.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,6 +1744,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
17441744
.map(|(_, pat_span, _)| *pat_span)
17451745
.collect::<Vec<_>>(),
17461746
errors::UnusedVarTryIgnore {
1747+
name: name.clone(),
17471748
sugg: errors::UnusedVarTryIgnoreSugg {
17481749
shorthands,
17491750
non_shorthands,

0 commit comments

Comments
 (0)