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.
This commit is contained in:
Georg Semmler 2023-11-09 08:33:21 +01:00
parent 4770d91093
commit 10538d4d2b
No known key found for this signature in database
GPG key ID: A87BCEE5205CE489
7 changed files with 219 additions and 17 deletions

View file

@ -22,6 +22,10 @@ trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entri
trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]` trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]`
.label = empty on-clause here .label = empty on-clause here
trait_selection_ignored_diagnostic_option = `{$option_name}` is ignored due to previous definition of `{$option_name}`
.other_label = `{$option_name}` is first declared here
.label = `{$option_name}` is already declared here
trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}` trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}`
trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]` trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]`

View file

@ -321,7 +321,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct OnUnimplementedFormatString(Symbol); pub struct OnUnimplementedFormatString(Symbol, Span);
#[derive(Debug)] #[derive(Debug)]
pub struct OnUnimplementedDirective { pub struct OnUnimplementedDirective {
@ -350,7 +350,7 @@ pub struct OnUnimplementedNote {
pub enum AppendConstMessage { pub enum AppendConstMessage {
#[default] #[default]
Default, Default,
Custom(Symbol), Custom(Symbol, Span),
} }
#[derive(LintDiagnostic)] #[derive(LintDiagnostic)]
@ -372,6 +372,35 @@ impl MalformedOnUnimplementedAttrLint {
#[help] #[help]
pub struct MissingOptionsForOnUnimplementedAttr; pub struct MissingOptionsForOnUnimplementedAttr;
#[derive(LintDiagnostic)]
#[diag(trait_selection_ignored_diagnostic_option)]
pub struct IgnoredDiagnosticOption {
pub option_name: &'static str,
#[label]
pub span: Span,
#[label(trait_selection_other_label)]
pub prev_span: Span,
}
impl IgnoredDiagnosticOption {
fn maybe_emit_warning<'tcx>(
tcx: TyCtxt<'tcx>,
item_def_id: DefId,
new: Option<Span>,
old: Option<Span>,
option_name: &'static str,
) {
if let (Some(new_item), Some(old_item)) = (new, old) {
tcx.emit_spanned_lint(
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()),
new_item,
IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name },
);
}
}
}
impl<'tcx> OnUnimplementedDirective { impl<'tcx> OnUnimplementedDirective {
fn parse( fn parse(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
@ -384,8 +413,9 @@ impl<'tcx> OnUnimplementedDirective {
let mut errored = None; let mut errored = None;
let mut item_iter = items.iter(); let mut item_iter = items.iter();
let parse_value = |value_str| { let parse_value = |value_str, value_span| {
OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some) OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span, value_span)
.map(Some)
}; };
let condition = if is_root { let condition = if is_root {
@ -398,7 +428,7 @@ impl<'tcx> OnUnimplementedDirective {
.ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?; .ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| { attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
if let Some(value) = cfg.value if let Some(value) = cfg.value
&& let Err(guar) = parse_value(value) && let Err(guar) = parse_value(value, cfg.span)
{ {
errored = Some(guar); errored = Some(guar);
} }
@ -417,17 +447,17 @@ impl<'tcx> OnUnimplementedDirective {
for item in item_iter { for item in item_iter {
if item.has_name(sym::message) && message.is_none() { if item.has_name(sym::message) && message.is_none() {
if let Some(message_) = item.value_str() { if let Some(message_) = item.value_str() {
message = parse_value(message_)?; message = parse_value(message_, item.span())?;
continue; continue;
} }
} else if item.has_name(sym::label) && label.is_none() { } else if item.has_name(sym::label) && label.is_none() {
if let Some(label_) = item.value_str() { if let Some(label_) = item.value_str() {
label = parse_value(label_)?; label = parse_value(label_, item.span())?;
continue; continue;
} }
} else if item.has_name(sym::note) { } else if item.has_name(sym::note) {
if let Some(note_) = item.value_str() { if let Some(note_) = item.value_str() {
if let Some(note) = parse_value(note_)? { if let Some(note) = parse_value(note_, item.span())? {
notes.push(note); notes.push(note);
continue; continue;
} }
@ -437,7 +467,7 @@ impl<'tcx> OnUnimplementedDirective {
&& !is_diagnostic_namespace_variant && !is_diagnostic_namespace_variant
{ {
if let Some(parent_label_) = item.value_str() { if let Some(parent_label_) = item.value_str() {
parent_label = parse_value(parent_label_)?; parent_label = parse_value(parent_label_, item.span())?;
continue; continue;
} }
} else if item.has_name(sym::on) } else if item.has_name(sym::on)
@ -470,7 +500,7 @@ impl<'tcx> OnUnimplementedDirective {
&& !is_diagnostic_namespace_variant && !is_diagnostic_namespace_variant
{ {
if let Some(msg) = item.value_str() { if let Some(msg) = item.value_str() {
append_const_msg = Some(AppendConstMessage::Custom(msg)); append_const_msg = Some(AppendConstMessage::Custom(msg, item.span()));
continue; continue;
} else if item.is_word() { } else if item.is_word() {
append_const_msg = Some(AppendConstMessage::Default); append_const_msg = Some(AppendConstMessage::Default);
@ -519,6 +549,54 @@ impl<'tcx> OnUnimplementedDirective {
subcommands.extend(directive.subcommands); subcommands.extend(directive.subcommands);
let mut notes = aggr.notes; let mut notes = aggr.notes;
notes.extend(directive.notes); notes.extend(directive.notes);
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.message.as_ref().map(|f| f.1),
aggr.message.as_ref().map(|f| f.1),
"message",
);
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.label.as_ref().map(|f| f.1),
aggr.label.as_ref().map(|f| f.1),
"label",
);
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.condition.as_ref().map(|i| i.span),
aggr.condition.as_ref().map(|i| i.span),
"condition",
);
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.parent_label.as_ref().map(|f| f.1),
aggr.parent_label.as_ref().map(|f| f.1),
"parent_label",
);
IgnoredDiagnosticOption::maybe_emit_warning(
tcx,
item_def_id,
directive.append_const_msg.as_ref().and_then(|c| {
if let AppendConstMessage::Custom(_, s) = c {
Some(*s)
} else {
None
}
}),
aggr.append_const_msg.as_ref().and_then(|c| {
if let AppendConstMessage::Custom(_, s) = c {
Some(*s)
} else {
None
}
}),
"append_const_msg",
);
Ok(Some(Self { Ok(Some(Self {
condition: aggr.condition.or(directive.condition), condition: aggr.condition.or(directive.condition),
subcommands, subcommands,
@ -556,6 +634,7 @@ impl<'tcx> OnUnimplementedDirective {
item_def_id, item_def_id,
value, value,
attr.span, attr.span,
attr.span,
)?), )?),
notes: Vec::new(), notes: Vec::new(),
parent_label: None, parent_label: None,
@ -633,7 +712,11 @@ impl<'tcx> OnUnimplementedDirective {
// `with_no_visible_paths` is also used when generating the options, // `with_no_visible_paths` is also used when generating the options,
// so we need to match it here. // so we need to match it here.
ty::print::with_no_visible_paths!( ty::print::with_no_visible_paths!(
OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map) OnUnimplementedFormatString(v, cfg.span).format(
tcx,
trait_ref,
&options_map
)
) )
}); });
@ -678,8 +761,9 @@ impl<'tcx> OnUnimplementedFormatString {
item_def_id: DefId, item_def_id: DefId,
from: Symbol, from: Symbol,
err_sp: Span, err_sp: Span,
value_span: Span,
) -> Result<Self, ErrorGuaranteed> { ) -> Result<Self, ErrorGuaranteed> {
let result = OnUnimplementedFormatString(from); let result = OnUnimplementedFormatString(from, value_span);
result.verify(tcx, item_def_id, err_sp)?; result.verify(tcx, item_def_id, err_sp)?;
Ok(result) Ok(result)
} }

View file

@ -2729,7 +2729,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
Some(format!("{cannot_do_this} in const contexts")) Some(format!("{cannot_do_this} in const contexts"))
} }
// overridden post message // overridden post message
(true, Some(AppendConstMessage::Custom(custom_msg))) => { (true, Some(AppendConstMessage::Custom(custom_msg, _))) => {
Some(format!("{cannot_do_this}{custom_msg}")) Some(format!("{cannot_do_this}{custom_msg}"))
} }
// fallback to generic message // fallback to generic message

View file

@ -8,6 +8,8 @@
note = "custom note" note = "custom note"
)] )]
#[diagnostic::on_unimplemented(message = "fallback!!")] #[diagnostic::on_unimplemented(message = "fallback!!")]
//~^ `message` is ignored due to previous definition of `message`
//~| `message` is ignored due to previous definition of `message`
#[diagnostic::on_unimplemented(label = "fallback label")] #[diagnostic::on_unimplemented(label = "fallback label")]
#[diagnostic::on_unimplemented(note = "fallback note")] #[diagnostic::on_unimplemented(note = "fallback note")]
trait Foo {} trait Foo {}

View file

@ -7,6 +7,15 @@ LL | if(Self = "()"),
= help: only `message`, `note` and `label` are allowed as options = help: only `message`, `note` and `label` are allowed as options
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
warning: `message` is ignored due to previous definition of `message`
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32
|
LL | message = "custom message",
| -------------------------- `message` is first declared here
...
LL | #[diagnostic::on_unimplemented(message = "fallback!!")]
| ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
warning: malformed `on_unimplemented` attribute warning: malformed `on_unimplemented` attribute
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5 --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5
| |
@ -16,8 +25,19 @@ LL | if(Self = "()"),
= help: only `message`, `note` and `label` are allowed as options = help: only `message`, `note` and `label` are allowed as options
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
warning: `message` is ignored due to previous definition of `message`
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32
|
LL | message = "custom message",
| -------------------------- `message` is first declared here
...
LL | #[diagnostic::on_unimplemented(message = "fallback!!")]
| ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: custom message error[E0277]: custom message
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:18:15 --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15
| |
LL | takes_foo(()); LL | takes_foo(());
| --------- ^^ fallback label | --------- ^^ fallback label
@ -28,16 +48,16 @@ LL | takes_foo(());
= note: custom note = note: custom note
= note: fallback note = note: fallback note
help: this trait has no implementations, consider adding one help: this trait has no implementations, consider adding one
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:13:1 --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1
| |
LL | trait Foo {} LL | trait Foo {}
| ^^^^^^^^^ | ^^^^^^^^^
note: required by a bound in `takes_foo` note: required by a bound in `takes_foo`
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:22 --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22
| |
LL | fn takes_foo(_: impl Foo) {} LL | fn takes_foo(_: impl Foo) {}
| ^^^ required by this bound in `takes_foo` | ^^^ required by this bound in `takes_foo`
error: aborting due to previous error; 2 warnings emitted error: aborting due to previous error; 4 warnings emitted
For more information about this error, try `rustc --explain E0277`. For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,25 @@
#![feature(diagnostic_namespace)]
#[diagnostic::on_unimplemented(
message = "first message",
label = "first label",
note = "custom note"
)]
#[diagnostic::on_unimplemented(
message = "second message",
//~^WARN `message` is ignored due to previous definition of `message`
//~|WARN `message` is ignored due to previous definition of `message`
label = "second label",
//~^WARN `label` is ignored due to previous definition of `label`
//~|WARN `label` is ignored due to previous definition of `label`
note = "second note"
)]
trait Foo {}
fn takes_foo(_: impl Foo) {}
fn main() {
takes_foo(());
//~^ERROR first message
}

View file

@ -0,0 +1,67 @@
warning: `message` is ignored due to previous definition of `message`
--> $DIR/report_warning_on_duplicated_options.rs:9:5
|
LL | message = "first message",
| ------------------------- `message` is first declared here
...
LL | message = "second message",
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
|
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
warning: `label` is ignored due to previous definition of `label`
--> $DIR/report_warning_on_duplicated_options.rs:12:5
|
LL | label = "first label",
| --------------------- `label` is first declared here
...
LL | label = "second label",
| ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here
warning: `message` is ignored due to previous definition of `message`
--> $DIR/report_warning_on_duplicated_options.rs:9:5
|
LL | message = "first message",
| ------------------------- `message` is first declared here
...
LL | message = "second message",
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
warning: `label` is ignored due to previous definition of `label`
--> $DIR/report_warning_on_duplicated_options.rs:12:5
|
LL | label = "first label",
| --------------------- `label` is first declared here
...
LL | label = "second label",
| ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error[E0277]: first message
--> $DIR/report_warning_on_duplicated_options.rs:23:15
|
LL | takes_foo(());
| --------- ^^ first label
| |
| required by a bound introduced by this call
|
= help: the trait `Foo` is not implemented for `()`
= note: custom note
= note: second note
help: this trait has no implementations, consider adding one
--> $DIR/report_warning_on_duplicated_options.rs:17:1
|
LL | trait Foo {}
| ^^^^^^^^^
note: required by a bound in `takes_foo`
--> $DIR/report_warning_on_duplicated_options.rs:20:22
|
LL | fn takes_foo(_: impl Foo) {}
| ^^^ required by this bound in `takes_foo`
error: aborting due to previous error; 4 warnings emitted
For more information about this error, try `rustc --explain E0277`.