Skip to content

Commit 0b34410

Browse files
committed
Add some additional warnings for duplicated diagnostic items
This commit adds warnings if a user supplies several diagnostic options where we can only apply one of them. We explicitly warn about ignored options here. In addition a small test for these warnings is added.
1 parent d8dbf7c commit 0b34410

7 files changed

+215
-17
lines changed

compiler/rustc_trait_selection/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entri
2222
trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]`
2323
.label = empty on-clause here
2424
25+
trait_selection_ignored_diagnostic_option = `{$option_name}` is ignored due to previous definition of `{$option_name}`
26+
.other_label = `{$option_name}` is first declared here
27+
.label = `{$option_name}` is already declared here
28+
2529
trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}`
2630
2731
trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]`

compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs

+92-12
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
325325
}
326326

327327
#[derive(Clone, Debug)]
328-
pub struct OnUnimplementedFormatString(Symbol);
328+
pub struct OnUnimplementedFormatString(Symbol, Span);
329329

330330
#[derive(Debug)]
331331
pub struct OnUnimplementedDirective {
@@ -354,7 +354,7 @@ pub struct OnUnimplementedNote {
354354
pub enum AppendConstMessage {
355355
#[default]
356356
Default,
357-
Custom(Symbol),
357+
Custom(Symbol, Span),
358358
}
359359

360360
#[derive(LintDiagnostic)]
@@ -376,6 +376,35 @@ impl MalformedOnUnimplementedAttrLint {
376376
#[help]
377377
pub struct MissingOptionsForOnUnimplementedAttr;
378378

379+
#[derive(LintDiagnostic)]
380+
#[diag(trait_selection_ignored_diagnostic_option)]
381+
pub struct IgnoredDiagnosticOption {
382+
pub option_name: &'static str,
383+
#[label]
384+
pub span: Span,
385+
#[label(trait_selection_other_label)]
386+
pub prev_span: Span,
387+
}
388+
389+
impl IgnoredDiagnosticOption {
390+
fn maybe_emit_warning<'tcx>(
391+
tcx: TyCtxt<'tcx>,
392+
item_def_id: DefId,
393+
new: Option<Span>,
394+
old: Option<Span>,
395+
option_name: &'static str,
396+
) {
397+
if let (Some(new_item), Some(old_item)) = (new, old) {
398+
tcx.emit_spanned_lint(
399+
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
400+
tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()),
401+
new_item,
402+
IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name },
403+
);
404+
}
405+
}
406+
}
407+
379408
impl<'tcx> OnUnimplementedDirective {
380409
fn parse(
381410
tcx: TyCtxt<'tcx>,
@@ -388,8 +417,9 @@ impl<'tcx> OnUnimplementedDirective {
388417
let mut errored = None;
389418
let mut item_iter = items.iter();
390419

391-
let parse_value = |value_str| {
392-
OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some)
420+
let parse_value = |value_str, value_span| {
421+
OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span, value_span)
422+
.map(Some)
393423
};
394424

395425
let condition = if is_root {
@@ -402,7 +432,7 @@ impl<'tcx> OnUnimplementedDirective {
402432
.ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
403433
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
404434
if let Some(value) = cfg.value
405-
&& let Err(guar) = parse_value(value)
435+
&& let Err(guar) = parse_value(value, cfg.span)
406436
{
407437
errored = Some(guar);
408438
}
@@ -421,17 +451,17 @@ impl<'tcx> OnUnimplementedDirective {
421451
for item in item_iter {
422452
if item.has_name(sym::message) && message.is_none() {
423453
if let Some(message_) = item.value_str() {
424-
message = parse_value(message_)?;
454+
message = parse_value(message_, item.span())?;
425455
continue;
426456
}
427457
} else if item.has_name(sym::label) && label.is_none() {
428458
if let Some(label_) = item.value_str() {
429-
label = parse_value(label_)?;
459+
label = parse_value(label_, item.span())?;
430460
continue;
431461
}
432462
} else if item.has_name(sym::note) {
433463
if let Some(note_) = item.value_str() {
434-
if let Some(note) = parse_value(note_)? {
464+
if let Some(note) = parse_value(note_, item.span())? {
435465
notes.push(note);
436466
continue;
437467
}
@@ -441,7 +471,7 @@ impl<'tcx> OnUnimplementedDirective {
441471
&& !is_diagnostic_namespace_variant
442472
{
443473
if let Some(parent_label_) = item.value_str() {
444-
parent_label = parse_value(parent_label_)?;
474+
parent_label = parse_value(parent_label_, item.span())?;
445475
continue;
446476
}
447477
} else if item.has_name(sym::on)
@@ -474,7 +504,7 @@ impl<'tcx> OnUnimplementedDirective {
474504
&& !is_diagnostic_namespace_variant
475505
{
476506
if let Some(msg) = item.value_str() {
477-
append_const_msg = Some(AppendConstMessage::Custom(msg));
507+
append_const_msg = Some(AppendConstMessage::Custom(msg, item.span()));
478508
continue;
479509
} else if item.is_word() {
480510
append_const_msg = Some(AppendConstMessage::Default);
@@ -523,6 +553,54 @@ impl<'tcx> OnUnimplementedDirective {
523553
subcommands.extend(directive.subcommands);
524554
let mut notes = aggr.notes;
525555
notes.extend(directive.notes);
556+
IgnoredDiagnosticOption::maybe_emit_warning(
557+
tcx,
558+
item_def_id,
559+
directive.message.as_ref().map(|f| f.1),
560+
aggr.message.as_ref().map(|f| f.1),
561+
"message",
562+
);
563+
IgnoredDiagnosticOption::maybe_emit_warning(
564+
tcx,
565+
item_def_id,
566+
directive.label.as_ref().map(|f| f.1),
567+
aggr.label.as_ref().map(|f| f.1),
568+
"label",
569+
);
570+
IgnoredDiagnosticOption::maybe_emit_warning(
571+
tcx,
572+
item_def_id,
573+
directive.condition.as_ref().map(|i| i.span),
574+
aggr.condition.as_ref().map(|i| i.span),
575+
"condition",
576+
);
577+
IgnoredDiagnosticOption::maybe_emit_warning(
578+
tcx,
579+
item_def_id,
580+
directive.parent_label.as_ref().map(|f| f.1),
581+
aggr.parent_label.as_ref().map(|f| f.1),
582+
"parent_label",
583+
);
584+
IgnoredDiagnosticOption::maybe_emit_warning(
585+
tcx,
586+
item_def_id,
587+
directive.append_const_msg.as_ref().and_then(|c| {
588+
if let AppendConstMessage::Custom(_, s) = c {
589+
Some(*s)
590+
} else {
591+
None
592+
}
593+
}),
594+
aggr.append_const_msg.as_ref().and_then(|c| {
595+
if let AppendConstMessage::Custom(_, s) = c {
596+
Some(*s)
597+
} else {
598+
None
599+
}
600+
}),
601+
"append_const_msg",
602+
);
603+
526604
Ok(Some(Self {
527605
condition: aggr.condition.or(directive.condition),
528606
subcommands,
@@ -560,6 +638,7 @@ impl<'tcx> OnUnimplementedDirective {
560638
item_def_id,
561639
value,
562640
attr.span,
641+
attr.span,
563642
)?),
564643
notes: Vec::new(),
565644
parent_label: None,
@@ -636,7 +715,7 @@ impl<'tcx> OnUnimplementedDirective {
636715
let value = cfg.value.map(|v| {
637716
// `with_no_visible_paths` is also used when generating the options,
638717
// so we need to match it here.
639-
ty::print::with_no_visible_paths!(OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map))
718+
ty::print::with_no_visible_paths!(OnUnimplementedFormatString(v, cfg.span).format(tcx, trait_ref, &options_map))
640719
});
641720

642721
options.contains(&(cfg.name, value))
@@ -680,8 +759,9 @@ impl<'tcx> OnUnimplementedFormatString {
680759
item_def_id: DefId,
681760
from: Symbol,
682761
err_sp: Span,
762+
value_span: Span,
683763
) -> Result<Self, ErrorGuaranteed> {
684-
let result = OnUnimplementedFormatString(from);
764+
let result = OnUnimplementedFormatString(from, value_span);
685765
result.verify(tcx, item_def_id, err_sp)?;
686766
Ok(result)
687767
}

compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2731,7 +2731,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
27312731
Some(format!("{cannot_do_this} in const contexts"))
27322732
}
27332733
// overridden post message
2734-
(true, Some(AppendConstMessage::Custom(custom_msg))) => {
2734+
(true, Some(AppendConstMessage::Custom(custom_msg, _))) => {
27352735
Some(format!("{cannot_do_this}{custom_msg}"))
27362736
}
27372737
// fallback to generic message

tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
note = "custom note"
99
)]
1010
#[diagnostic::on_unimplemented(message = "fallback!!")]
11+
//~^ `message` is ignored due to previous definition of `message`
12+
//~| `message` is ignored due to previous definition of `message`
1113
#[diagnostic::on_unimplemented(label = "fallback label")]
1214
#[diagnostic::on_unimplemented(note = "fallback note")]
1315
trait Foo {}

tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr

+24-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ LL | if(Self = "()"),
77
= help: only `message`, `note` and `label` are allowed as options
88
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
99

10+
warning: `message` is ignored due to previous definition of `message`
11+
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32
12+
|
13+
LL | message = "custom message",
14+
| -------------------------- `message` is first declared here
15+
...
16+
LL | #[diagnostic::on_unimplemented(message = "fallback!!")]
17+
| ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
18+
1019
warning: malformed `on_unimplemented` attribute
1120
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5
1221
|
@@ -16,8 +25,19 @@ LL | if(Self = "()"),
1625
= help: only `message`, `note` and `label` are allowed as options
1726
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
1827

28+
warning: `message` is ignored due to previous definition of `message`
29+
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32
30+
|
31+
LL | message = "custom message",
32+
| -------------------------- `message` is first declared here
33+
...
34+
LL | #[diagnostic::on_unimplemented(message = "fallback!!")]
35+
| ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
36+
|
37+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
38+
1939
error[E0277]: custom message
20-
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:18:15
40+
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15
2141
|
2242
LL | takes_foo(());
2343
| --------- ^^ fallback label
@@ -28,16 +48,16 @@ LL | takes_foo(());
2848
= note: custom note
2949
= note: fallback note
3050
help: this trait has no implementations, consider adding one
31-
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:13:1
51+
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1
3252
|
3353
LL | trait Foo {}
3454
| ^^^^^^^^^
3555
note: required by a bound in `takes_foo`
36-
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:22
56+
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22
3757
|
3858
LL | fn takes_foo(_: impl Foo) {}
3959
| ^^^ required by this bound in `takes_foo`
4060

41-
error: aborting due to previous error; 2 warnings emitted
61+
error: aborting due to previous error; 4 warnings emitted
4262

4363
For more information about this error, try `rustc --explain E0277`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![feature(diagnostic_namespace)]
2+
3+
#[diagnostic::on_unimplemented(
4+
message = "first message",
5+
label = "first label",
6+
note = "custom note"
7+
)]
8+
#[diagnostic::on_unimplemented(
9+
message = "second message",
10+
//~^WARN `message` is ignored due to previous definition of `message`
11+
//~|WARN `message` is ignored due to previous definition of `message`
12+
label = "second label",
13+
//~^WARN `label` is ignored due to previous definition of `label`
14+
//~|WARN `label` is ignored due to previous definition of `label`
15+
note = "second note"
16+
)]
17+
trait Foo {}
18+
19+
20+
fn takes_foo(_: impl Foo) {}
21+
22+
fn main() {
23+
takes_foo(());
24+
//~^ERROR first message
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
warning: `message` is ignored due to previous definition of `message`
2+
--> $DIR/report_warning_on_duplicated_options.rs:9:5
3+
|
4+
LL | message = "first message",
5+
| ------------------------- `message` is first declared here
6+
...
7+
LL | message = "second message",
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
9+
|
10+
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
11+
12+
warning: `label` is ignored due to previous definition of `label`
13+
--> $DIR/report_warning_on_duplicated_options.rs:12:5
14+
|
15+
LL | label = "first label",
16+
| --------------------- `label` is first declared here
17+
...
18+
LL | label = "second label",
19+
| ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here
20+
21+
warning: `message` is ignored due to previous definition of `message`
22+
--> $DIR/report_warning_on_duplicated_options.rs:9:5
23+
|
24+
LL | message = "first message",
25+
| ------------------------- `message` is first declared here
26+
...
27+
LL | message = "second message",
28+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
29+
|
30+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
31+
32+
warning: `label` is ignored due to previous definition of `label`
33+
--> $DIR/report_warning_on_duplicated_options.rs:12:5
34+
|
35+
LL | label = "first label",
36+
| --------------------- `label` is first declared here
37+
...
38+
LL | label = "second label",
39+
| ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here
40+
|
41+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
42+
43+
error[E0277]: first message
44+
--> $DIR/report_warning_on_duplicated_options.rs:23:15
45+
|
46+
LL | takes_foo(());
47+
| --------- ^^ first label
48+
| |
49+
| required by a bound introduced by this call
50+
|
51+
= help: the trait `Foo` is not implemented for `()`
52+
= note: custom note
53+
= note: second note
54+
help: this trait has no implementations, consider adding one
55+
--> $DIR/report_warning_on_duplicated_options.rs:17:1
56+
|
57+
LL | trait Foo {}
58+
| ^^^^^^^^^
59+
note: required by a bound in `takes_foo`
60+
--> $DIR/report_warning_on_duplicated_options.rs:20:22
61+
|
62+
LL | fn takes_foo(_: impl Foo) {}
63+
| ^^^ required by this bound in `takes_foo`
64+
65+
error: aborting due to previous error; 4 warnings emitted
66+
67+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)