Skip to content

Commit a2ea7c2

Browse files
authored
Rollup merge of rust-lang#116642 - weiznich:diagnostic_on_unimplemented_improvements, r=compiler-errors
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.
2 parents 18fa12a + 232aaeb commit a2ea7c2

File tree

3 files changed

+108
-12
lines changed

3 files changed

+108
-12
lines changed

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

+34-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{ObligationCauseCode, PredicateObligation};
22
use crate::infer::error_reporting::TypeErrCtxt;
3-
use rustc_ast::{MetaItem, NestedMetaItem};
3+
use rustc_ast::{Attribute, MetaItem, NestedMetaItem};
44
use rustc_attr as attr;
55
use rustc_data_structures::fx::FxHashMap;
66
use rustc_errors::{struct_span_err, ErrorGuaranteed};
@@ -474,18 +474,40 @@ impl<'tcx> OnUnimplementedDirective {
474474
}
475475

476476
pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
477-
let mut is_diagnostic_namespace_variant = false;
478-
let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented).or_else(|| {
479-
if tcx.features().diagnostic_namespace {
480-
is_diagnostic_namespace_variant = true;
481-
tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]).next()
482-
} else {
483-
None
484-
}
485-
}) else {
486-
return Ok(None);
487-
};
477+
if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
478+
return Self::parse_attribute(attr, false, tcx, item_def_id);
479+
} else if tcx.features().diagnostic_namespace {
480+
tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented])
481+
.filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
482+
.try_fold(None, |aggr: Option<Self>, directive| {
483+
let directive = directive?;
484+
if let Some(aggr) = aggr {
485+
let mut subcommands = aggr.subcommands;
486+
subcommands.extend(directive.subcommands);
487+
Ok(Some(Self {
488+
condition: aggr.condition.or(directive.condition),
489+
subcommands,
490+
message: aggr.message.or(directive.message),
491+
label: aggr.label.or(directive.label),
492+
note: aggr.note.or(directive.note),
493+
parent_label: aggr.parent_label.or(directive.parent_label),
494+
append_const_msg: aggr.append_const_msg.or(directive.append_const_msg),
495+
}))
496+
} else {
497+
Ok(Some(directive))
498+
}
499+
})
500+
} else {
501+
Ok(None)
502+
}
503+
}
488504

505+
fn parse_attribute(
506+
attr: &Attribute,
507+
is_diagnostic_namespace_variant: bool,
508+
tcx: TyCtxt<'tcx>,
509+
item_def_id: DefId,
510+
) -> Result<Option<Self>, ErrorGuaranteed> {
489511
let result = if let Some(items) = attr.meta_item_list() {
490512
Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant)
491513
} else if let Some(value) = attr.value_str() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#![feature(diagnostic_namespace)]
2+
3+
#[diagnostic::on_unimplemented(
4+
//~^WARN malformed `on_unimplemented` attribute
5+
//~|WARN malformed `on_unimplemented` attribute
6+
if(Self = ()),
7+
message = "not used yet",
8+
label = "not used yet",
9+
note = "not used yet"
10+
)]
11+
#[diagnostic::on_unimplemented(message = "fallback!!")]
12+
#[diagnostic::on_unimplemented(label = "fallback label")]
13+
#[diagnostic::on_unimplemented(note = "fallback note")]
14+
#[diagnostic::on_unimplemented(message = "fallback2!!")]
15+
trait Foo {}
16+
17+
fn takes_foo(_: impl Foo) {}
18+
19+
fn main() {
20+
takes_foo(());
21+
//~^ERROR fallback!!
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
warning: malformed `on_unimplemented` attribute
2+
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:3:1
3+
|
4+
LL | / #[diagnostic::on_unimplemented(
5+
LL | |
6+
LL | |
7+
LL | | if(Self = ()),
8+
... |
9+
LL | | note = "not used yet"
10+
LL | | )]
11+
| |__^
12+
|
13+
= note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
14+
15+
warning: malformed `on_unimplemented` attribute
16+
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:3:1
17+
|
18+
LL | / #[diagnostic::on_unimplemented(
19+
LL | |
20+
LL | |
21+
LL | | if(Self = ()),
22+
... |
23+
LL | | note = "not used yet"
24+
LL | | )]
25+
| |__^
26+
|
27+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
28+
29+
error[E0277]: fallback!!
30+
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15
31+
|
32+
LL | takes_foo(());
33+
| --------- ^^ fallback label
34+
| |
35+
| required by a bound introduced by this call
36+
|
37+
= help: the trait `Foo` is not implemented for `()`
38+
= note: fallback note
39+
help: this trait has no implementations, consider adding one
40+
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1
41+
|
42+
LL | trait Foo {}
43+
| ^^^^^^^^^
44+
note: required by a bound in `takes_foo`
45+
--> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22
46+
|
47+
LL | fn takes_foo(_: impl Foo) {}
48+
| ^^^ required by this bound in `takes_foo`
49+
50+
error: aborting due to previous error; 2 warnings emitted
51+
52+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)