Skip to content

Commit 547ace8

Browse files
committed
Auto merge of #117742 - weiznich:turn_overlapping_diagnostic_options_into_warnings, r=compiler-errors
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. r? `@compiler-errors` For now that's the last PR to improve the warnings generated by misused `#[diagnostic::on_unimplemented]` attributes. I'm not sure what needs to be done next to move this closer to stabilization.
2 parents 1a740c3 + 10538d4 commit 547ace8

7 files changed

+219
-17
lines changed

Diff for: 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]`

Diff for: compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs

+96-12
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
321321
}
322322

323323
#[derive(Clone, Debug)]
324-
pub struct OnUnimplementedFormatString(Symbol);
324+
pub struct OnUnimplementedFormatString(Symbol, Span);
325325

326326
#[derive(Debug)]
327327
pub struct OnUnimplementedDirective {
@@ -350,7 +350,7 @@ pub struct OnUnimplementedNote {
350350
pub enum AppendConstMessage {
351351
#[default]
352352
Default,
353-
Custom(Symbol),
353+
Custom(Symbol, Span),
354354
}
355355

356356
#[derive(LintDiagnostic)]
@@ -372,6 +372,35 @@ impl MalformedOnUnimplementedAttrLint {
372372
#[help]
373373
pub struct MissingOptionsForOnUnimplementedAttr;
374374

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

387-
let parse_value = |value_str| {
388-
OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some)
416+
let parse_value = |value_str, value_span| {
417+
OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span, value_span)
418+
.map(Some)
389419
};
390420

391421
let condition = if is_root {
@@ -398,7 +428,7 @@ impl<'tcx> OnUnimplementedDirective {
398428
.ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
399429
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
400430
if let Some(value) = cfg.value
401-
&& let Err(guar) = parse_value(value)
431+
&& let Err(guar) = parse_value(value, cfg.span)
402432
{
403433
errored = Some(guar);
404434
}
@@ -417,17 +447,17 @@ impl<'tcx> OnUnimplementedDirective {
417447
for item in item_iter {
418448
if item.has_name(sym::message) && message.is_none() {
419449
if let Some(message_) = item.value_str() {
420-
message = parse_value(message_)?;
450+
message = parse_value(message_, item.span())?;
421451
continue;
422452
}
423453
} else if item.has_name(sym::label) && label.is_none() {
424454
if let Some(label_) = item.value_str() {
425-
label = parse_value(label_)?;
455+
label = parse_value(label_, item.span())?;
426456
continue;
427457
}
428458
} else if item.has_name(sym::note) {
429459
if let Some(note_) = item.value_str() {
430-
if let Some(note) = parse_value(note_)? {
460+
if let Some(note) = parse_value(note_, item.span())? {
431461
notes.push(note);
432462
continue;
433463
}
@@ -437,7 +467,7 @@ impl<'tcx> OnUnimplementedDirective {
437467
&& !is_diagnostic_namespace_variant
438468
{
439469
if let Some(parent_label_) = item.value_str() {
440-
parent_label = parse_value(parent_label_)?;
470+
parent_label = parse_value(parent_label_, item.span())?;
441471
continue;
442472
}
443473
} else if item.has_name(sym::on)
@@ -470,7 +500,7 @@ impl<'tcx> OnUnimplementedDirective {
470500
&& !is_diagnostic_namespace_variant
471501
{
472502
if let Some(msg) = item.value_str() {
473-
append_const_msg = Some(AppendConstMessage::Custom(msg));
503+
append_const_msg = Some(AppendConstMessage::Custom(msg, item.span()));
474504
continue;
475505
} else if item.is_word() {
476506
append_const_msg = Some(AppendConstMessage::Default);
@@ -519,6 +549,54 @@ impl<'tcx> OnUnimplementedDirective {
519549
subcommands.extend(directive.subcommands);
520550
let mut notes = aggr.notes;
521551
notes.extend(directive.notes);
552+
IgnoredDiagnosticOption::maybe_emit_warning(
553+
tcx,
554+
item_def_id,
555+
directive.message.as_ref().map(|f| f.1),
556+
aggr.message.as_ref().map(|f| f.1),
557+
"message",
558+
);
559+
IgnoredDiagnosticOption::maybe_emit_warning(
560+
tcx,
561+
item_def_id,
562+
directive.label.as_ref().map(|f| f.1),
563+
aggr.label.as_ref().map(|f| f.1),
564+
"label",
565+
);
566+
IgnoredDiagnosticOption::maybe_emit_warning(
567+
tcx,
568+
item_def_id,
569+
directive.condition.as_ref().map(|i| i.span),
570+
aggr.condition.as_ref().map(|i| i.span),
571+
"condition",
572+
);
573+
IgnoredDiagnosticOption::maybe_emit_warning(
574+
tcx,
575+
item_def_id,
576+
directive.parent_label.as_ref().map(|f| f.1),
577+
aggr.parent_label.as_ref().map(|f| f.1),
578+
"parent_label",
579+
);
580+
IgnoredDiagnosticOption::maybe_emit_warning(
581+
tcx,
582+
item_def_id,
583+
directive.append_const_msg.as_ref().and_then(|c| {
584+
if let AppendConstMessage::Custom(_, s) = c {
585+
Some(*s)
586+
} else {
587+
None
588+
}
589+
}),
590+
aggr.append_const_msg.as_ref().and_then(|c| {
591+
if let AppendConstMessage::Custom(_, s) = c {
592+
Some(*s)
593+
} else {
594+
None
595+
}
596+
}),
597+
"append_const_msg",
598+
);
599+
522600
Ok(Some(Self {
523601
condition: aggr.condition.or(directive.condition),
524602
subcommands,
@@ -556,6 +634,7 @@ impl<'tcx> OnUnimplementedDirective {
556634
item_def_id,
557635
value,
558636
attr.span,
637+
attr.span,
559638
)?),
560639
notes: Vec::new(),
561640
parent_label: None,
@@ -633,7 +712,11 @@ impl<'tcx> OnUnimplementedDirective {
633712
// `with_no_visible_paths` is also used when generating the options,
634713
// so we need to match it here.
635714
ty::print::with_no_visible_paths!(
636-
OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)
715+
OnUnimplementedFormatString(v, cfg.span).format(
716+
tcx,
717+
trait_ref,
718+
&options_map
719+
)
637720
)
638721
});
639722

@@ -678,8 +761,9 @@ impl<'tcx> OnUnimplementedFormatString {
678761
item_def_id: DefId,
679762
from: Symbol,
680763
err_sp: Span,
764+
value_span: Span,
681765
) -> Result<Self, ErrorGuaranteed> {
682-
let result = OnUnimplementedFormatString(from);
766+
let result = OnUnimplementedFormatString(from, value_span);
683767
result.verify(tcx, item_def_id, err_sp)?;
684768
Ok(result)
685769
}

Diff for: compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs

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

Diff for: 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 {}

Diff for: 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)