Handle several #[diagnostic::on_unimplemented]
attributes correctly
This PR fixes an issues where rustc would ignore subsequent `#[diagnostic::on_unimplemented]` attributes. The [corresponding RFC](https://rust-lang.github.io/rfcs/3368-diagnostic-attribute-namespace.html) specifies that the first matching instance of each option is used. Invalid attributes are linted and otherwise ignored.
This commit is contained in:
parent
71704c4f84
commit
232aaeba7c
@ -1,6 +1,6 @@
|
|||||||
use super::{ObligationCauseCode, PredicateObligation};
|
use super::{ObligationCauseCode, PredicateObligation};
|
||||||
use crate::infer::error_reporting::TypeErrCtxt;
|
use crate::infer::error_reporting::TypeErrCtxt;
|
||||||
use rustc_ast::{MetaItem, NestedMetaItem};
|
use rustc_ast::{Attribute, MetaItem, NestedMetaItem};
|
||||||
use rustc_attr as attr;
|
use rustc_attr as attr;
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_errors::{struct_span_err, ErrorGuaranteed};
|
use rustc_errors::{struct_span_err, ErrorGuaranteed};
|
||||||
@ -474,18 +474,40 @@ fn parse(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
|
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
|
||||||
let mut is_diagnostic_namespace_variant = false;
|
if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
|
||||||
let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented).or_else(|| {
|
return Self::parse_attribute(attr, false, tcx, item_def_id);
|
||||||
if tcx.features().diagnostic_namespace {
|
} else if tcx.features().diagnostic_namespace {
|
||||||
is_diagnostic_namespace_variant = true;
|
tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented])
|
||||||
tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]).next()
|
.filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
|
||||||
} else {
|
.try_fold(None, |aggr: Option<Self>, directive| {
|
||||||
None
|
let directive = directive?;
|
||||||
}
|
if let Some(aggr) = aggr {
|
||||||
}) else {
|
let mut subcommands = aggr.subcommands;
|
||||||
return Ok(None);
|
subcommands.extend(directive.subcommands);
|
||||||
};
|
Ok(Some(Self {
|
||||||
|
condition: aggr.condition.or(directive.condition),
|
||||||
|
subcommands,
|
||||||
|
message: aggr.message.or(directive.message),
|
||||||
|
label: aggr.label.or(directive.label),
|
||||||
|
note: aggr.note.or(directive.note),
|
||||||
|
parent_label: aggr.parent_label.or(directive.parent_label),
|
||||||
|
append_const_msg: aggr.append_const_msg.or(directive.append_const_msg),
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Ok(Some(directive))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_attribute(
|
||||||
|
attr: &Attribute,
|
||||||
|
is_diagnostic_namespace_variant: bool,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
item_def_id: DefId,
|
||||||
|
) -> Result<Option<Self>, ErrorGuaranteed> {
|
||||||
let result = if let Some(items) = attr.meta_item_list() {
|
let result = if let Some(items) = attr.meta_item_list() {
|
||||||
Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant)
|
Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant)
|
||||||
} else if let Some(value) = attr.value_str() {
|
} else if let Some(value) = attr.value_str() {
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
#![feature(diagnostic_namespace)]
|
||||||
|
|
||||||
|
#[diagnostic::on_unimplemented(
|
||||||
|
//~^WARN malformed `on_unimplemented` attribute
|
||||||
|
//~|WARN malformed `on_unimplemented` attribute
|
||||||
|
if(Self = ()),
|
||||||
|
message = "not used yet",
|
||||||
|
label = "not used yet",
|
||||||
|
note = "not used yet"
|
||||||
|
)]
|
||||||
|
#[diagnostic::on_unimplemented(message = "fallback!!")]
|
||||||
|
#[diagnostic::on_unimplemented(label = "fallback label")]
|
||||||
|
#[diagnostic::on_unimplemented(note = "fallback note")]
|
||||||
|
#[diagnostic::on_unimplemented(message = "fallback2!!")]
|
||||||
|
trait Foo {}
|
||||||
|
|
||||||
|
fn takes_foo(_: impl Foo) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
takes_foo(());
|
||||||
|
//~^ERROR fallback!!
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
warning: malformed `on_unimplemented` attribute
|
||||||
|
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:3:1
|
||||||
|
|
|
||||||
|
LL | / #[diagnostic::on_unimplemented(
|
||||||
|
LL | |
|
||||||
|
LL | |
|
||||||
|
LL | | if(Self = ()),
|
||||||
|
... |
|
||||||
|
LL | | note = "not used yet"
|
||||||
|
LL | | )]
|
||||||
|
| |__^
|
||||||
|
|
|
||||||
|
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
|
||||||
|
|
||||||
|
warning: malformed `on_unimplemented` attribute
|
||||||
|
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:3:1
|
||||||
|
|
|
||||||
|
LL | / #[diagnostic::on_unimplemented(
|
||||||
|
LL | |
|
||||||
|
LL | |
|
||||||
|
LL | | if(Self = ()),
|
||||||
|
... |
|
||||||
|
LL | | note = "not used yet"
|
||||||
|
LL | | )]
|
||||||
|
| |__^
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error[E0277]: fallback!!
|
||||||
|
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15
|
||||||
|
|
|
||||||
|
LL | takes_foo(());
|
||||||
|
| --------- ^^ fallback label
|
||||||
|
| |
|
||||||
|
| required by a bound introduced by this call
|
||||||
|
|
|
||||||
|
= help: the trait `Foo` is not implemented for `()`
|
||||||
|
= note: fallback note
|
||||||
|
help: this trait has no implementations, consider adding one
|
||||||
|
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1
|
||||||
|
|
|
||||||
|
LL | trait Foo {}
|
||||||
|
| ^^^^^^^^^
|
||||||
|
note: required by a bound in `takes_foo`
|
||||||
|
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22
|
||||||
|
|
|
||||||
|
LL | fn takes_foo(_: impl Foo) {}
|
||||||
|
| ^^^ required by this bound in `takes_foo`
|
||||||
|
|
||||||
|
error: aborting due to previous error; 2 warnings emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Reference in New Issue
Block a user