Skip to content

Commit d6969da

Browse files
committed
rustc_on_unimplemented: emit notes in declared order
1 parent 696d592 commit d6969da

7 files changed

Lines changed: 53 additions & 71 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,12 +201,11 @@ fn parse_directive_items<'p>(
201201
items: impl Iterator<Item = &'p MetaItemOrLitParser>,
202202
is_root: bool,
203203
) -> Option<Directive> {
204-
let condition = None;
205204
let mut message: Option<(Span, _)> = None;
206205
let mut label: Option<(Span, _)> = None;
207206
let mut notes = ThinVec::new();
208207
let mut parent_label = None;
209-
let mut subcommands = ThinVec::new();
208+
let mut filters = ThinVec::new();
210209

211210
for item in items {
212211
let span = item.span();
@@ -338,21 +337,19 @@ fn parse_directive_items<'p>(
338337
}
339338
};
340339

341-
let condition = parse_condition(condition);
340+
let filter = parse_condition(condition);
342341

343342
if items.len() < 2 {
344343
// Something like `#[rustc_on_unimplemented(on(.., /* nothing */))]`
345344
// There's a condition but no directive behind it, this is a mistake.
346345
malformed!();
347346
}
348347

349-
let mut directive =
350-
or_malformed!(parse_directive_items(cx, mode, iter, false)?);
351-
352-
match condition {
353-
Ok(c) => {
354-
directive.condition = Some(c);
355-
subcommands.push(directive);
348+
match filter {
349+
Ok(filter) => {
350+
let directive =
351+
or_malformed!(parse_directive_items(cx, mode, iter, false)?);
352+
filters.push((filter, directive));
356353
}
357354
Err(e) => {
358355
cx.emit_err(e);
@@ -371,8 +368,7 @@ fn parse_directive_items<'p>(
371368

372369
Some(Directive {
373370
is_rustc_attr: matches!(mode, Mode::RustcOnUnimplemented),
374-
condition,
375-
subcommands,
371+
filters,
376372
message,
377373
label,
378374
notes,

compiler/rustc_hir/src/attrs/diagnostic.rs

Lines changed: 35 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ use crate::attrs::PrintAttribute;
1313
#[derive(Clone, Default, Debug, StableHash, Encodable, Decodable, PrintAttribute)]
1414
pub struct Directive {
1515
pub is_rustc_attr: bool,
16-
pub condition: Option<OnUnimplementedCondition>,
17-
pub subcommands: ThinVec<Directive>,
16+
/// This is never nested more than once, i.e. the directives in this
17+
/// thinvec have no filters of their own.
18+
pub filters: ThinVec<(OnUnimplementedCondition, Directive)>,
1819
pub message: Option<(Span, FormatString)>,
1920
pub label: Option<(Span, FormatString)>,
2021
pub notes: ThinVec<FormatString>,
@@ -28,11 +29,8 @@ impl Directive {
2829
/// We can't check this while parsing the attribute because `rustc_attr_parsing` doesn't have
2930
/// access to the item an attribute is on. Instead we later call this function in `check_attr`.
3031
pub fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) {
31-
if let Some(condition) = &self.condition {
32-
condition.visit_params(visit);
33-
}
34-
35-
for subcommand in &self.subcommands {
32+
for (filter, subcommand) in &self.filters {
33+
filter.visit_params(visit);
3634
subcommand.visit_params(visit);
3735
}
3836

@@ -62,53 +60,25 @@ impl Directive {
6260
"Directive::eval({self:?}, this={this}, options={condition_options:?}, args ={args:?})"
6361
);
6462

65-
let Some(condition_options) = condition_options else {
63+
let mut ret = CustomDiagnostic::default();
64+
65+
if let Some(condition_options) = condition_options {
66+
for (filter, directive) in &self.filters {
67+
if filter.matches_predicate(condition_options) {
68+
debug!("eval: {filter:?} succeeded");
69+
ret.update(directive, args);
70+
} else {
71+
debug!("eval: skipping {filter:?} due to condition");
72+
}
73+
}
74+
} else {
6675
debug_assert!(
6776
!self.is_rustc_attr,
6877
"Directive::eval called for `rustc_on_unimplemented` without `condition_options`"
6978
);
70-
return CustomDiagnostic {
71-
label: self.label.as_ref().map(|l| l.1.format(args)),
72-
message: self.message.as_ref().map(|m| m.1.format(args)),
73-
notes: self.notes.iter().map(|n| n.format(args)).collect(),
74-
parent_label: None,
75-
};
7679
};
77-
let mut message = None;
78-
let mut label = None;
79-
let mut notes = Vec::new();
80-
let mut parent_label = None;
81-
82-
for command in self.subcommands.iter().chain(Some(self)).rev() {
83-
debug!(?command);
84-
if let Some(ref condition) = command.condition
85-
&& !condition.matches_predicate(condition_options)
86-
{
87-
debug!("eval: skipping {command:?} due to condition");
88-
continue;
89-
}
90-
debug!("eval: {command:?} succeeded");
91-
if let Some(ref message_) = command.message {
92-
message = Some(message_.clone());
93-
}
94-
95-
if let Some(ref label_) = command.label {
96-
label = Some(label_.clone());
97-
}
98-
99-
notes.extend(command.notes.clone());
100-
101-
if let Some(ref parent_label_) = command.parent_label {
102-
parent_label = Some(parent_label_.clone());
103-
}
104-
}
105-
106-
CustomDiagnostic {
107-
label: label.map(|l| l.1.format(args)),
108-
message: message.map(|m| m.1.format(args)),
109-
notes: notes.into_iter().map(|n| n.format(args)).collect(),
110-
parent_label: parent_label.map(|e_s| e_s.format(args)),
111-
}
80+
ret.update(self, args);
81+
ret
11282
}
11383
}
11484

@@ -121,6 +91,22 @@ pub struct CustomDiagnostic {
12191
pub parent_label: Option<String>,
12292
}
12393

94+
impl CustomDiagnostic {
95+
fn update(&mut self, di: &Directive, args: &FormatArgs) {
96+
if self.message.is_none() {
97+
self.message = di.message.as_ref().map(|m| m.1.format(args));
98+
}
99+
if self.label.is_none() {
100+
self.label = di.label.as_ref().map(|l| l.1.format(args));
101+
}
102+
if self.parent_label.is_none() {
103+
self.parent_label = di.parent_label.as_ref().map(|p| p.format(args));
104+
}
105+
106+
self.notes.extend(di.notes.iter().map(|n| n.format(args)))
107+
}
108+
}
109+
124110
/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces",
125111
/// either as string pieces or dynamic arguments.
126112
#[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)]

tests/ui/abi/bad-custom.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,8 @@ LL | f
194194
| - return type was inferred to be `unsafe extern "custom" fn()` here
195195
|
196196
= help: the trait `Fn()` is not implemented for `unsafe extern "custom" fn()`
197-
= note: unsafe function cannot be called generically without an unsafe block
198197
= note: wrap the `unsafe extern "custom" fn()` in a closure with no arguments: `|| { /* code */ }`
198+
= note: unsafe function cannot be called generically without an unsafe block
199199

200200
error: items with the "custom" ABI can only be declared externally or defined via naked functions
201201
--> $DIR/bad-custom.rs:25:1

tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ LL | call(foo_unsafe);
7878
| required by a bound introduced by this call
7979
|
8080
= help: the trait `Fn()` is not implemented for fn item `unsafe fn() {foo_unsafe}`
81-
= note: unsafe function cannot be called generically without an unsafe block
8281
= note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }`
82+
= note: unsafe function cannot be called generically without an unsafe block
8383
= note: `#[target_feature]` functions do not implement the `Fn` traits
8484
= note: try casting the function to a `fn` pointer or wrapping it in a closure
8585
note: required by a bound in `call`
@@ -97,8 +97,8 @@ LL | call_mut(foo_unsafe);
9797
| required by a bound introduced by this call
9898
|
9999
= help: the trait `FnMut()` is not implemented for fn item `unsafe fn() {foo_unsafe}`
100-
= note: unsafe function cannot be called generically without an unsafe block
101100
= note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }`
101+
= note: unsafe function cannot be called generically without an unsafe block
102102
= note: `#[target_feature]` functions do not implement the `Fn` traits
103103
= note: try casting the function to a `fn` pointer or wrapping it in a closure
104104
note: required by a bound in `call_mut`
@@ -116,8 +116,8 @@ LL | call_once(foo_unsafe);
116116
| required by a bound introduced by this call
117117
|
118118
= help: the trait `FnOnce()` is not implemented for fn item `unsafe fn() {foo_unsafe}`
119-
= note: unsafe function cannot be called generically without an unsafe block
120119
= note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }`
120+
= note: unsafe function cannot be called generically without an unsafe block
121121
= note: `#[target_feature]` functions do not implement the `Fn` traits
122122
= note: try casting the function to a `fn` pointer or wrapping it in a closure
123123
note: required by a bound in `call_once`

tests/ui/suggestions/path-display.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ LL | println!("{}", path);
77
| required by this formatting parameter
88
|
99
= help: the trait `std::fmt::Display` is not implemented for `Path`
10-
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
1110
= note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data
11+
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
1212
= note: required for `&Path` to implement `std::fmt::Display`
1313

1414
error[E0277]: `PathBuf` doesn't implement `std::fmt::Display`
@@ -20,8 +20,8 @@ LL | println!("{}", path);
2020
| required by this formatting parameter
2121
|
2222
= help: the trait `std::fmt::Display` is not implemented for `PathBuf`
23-
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
2423
= note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data
24+
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
2525

2626
error: aborting due to 2 previous errors
2727

tests/ui/traits/next-solver/fn-trait.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ LL | require_fn(f as unsafe fn() -> i32);
77
| required by a bound introduced by this call
88
|
99
= help: the trait `Fn()` is not implemented for `unsafe fn() -> i32`
10-
= note: unsafe function cannot be called generically without an unsafe block
1110
= note: wrap the `unsafe fn() -> i32` in a closure with no arguments: `|| { /* code */ }`
11+
= note: unsafe function cannot be called generically without an unsafe block
1212
note: required by a bound in `require_fn`
1313
--> $DIR/fn-trait.rs:3:23
1414
|
@@ -56,8 +56,8 @@ LL | require_fn(h);
5656
| required by a bound introduced by this call
5757
|
5858
= help: the trait `Fn()` is not implemented for fn item `unsafe fn() -> i32 {h}`
59-
= note: unsafe function cannot be called generically without an unsafe block
6059
= note: wrap the `unsafe fn() -> i32 {h}` in a closure with no arguments: `|| { /* code */ }`
60+
= note: unsafe function cannot be called generically without an unsafe block
6161
note: required by a bound in `require_fn`
6262
--> $DIR/fn-trait.rs:3:23
6363
|

tests/ui/unpretty/diagnostic-attr.stdout

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use ::std::prelude::rust_2015::*;
77
//@ edition: 2015
88

99
#[attr = OnUnimplemented {directive: Directive {is_rustc_attr: false,
10-
subcommands: [],
10+
filters: [],
1111
message: FormatString {input: "My Message for `ImportantTrait<{A}>` implemented for `{Self}`",
1212
pieces: [Lit("My Message for `ImportantTrait<"),
1313
Arg(GenericParam {generic_param: "A"}), Lit(">` implemented for `"),
@@ -22,7 +22,7 @@ impl <T> ImportantTrait<T> for T where T: Clone { }
2222

2323
struct X;
2424

25-
#[attr = OnConst {directive: Directive {is_rustc_attr: false, subcommands: [],
25+
#[attr = OnConst {directive: Directive {is_rustc_attr: false, filters: [],
2626
message: FormatString {input: "My Message for `ImportantTrait<u8>` implemented for `{Self}`",
2727
pieces: [Lit("My Message for `ImportantTrait<u8>` implemented for `"),
2828
Arg(SelfUpper), Lit("`")]}, label: FormatString {input: "My Label",

0 commit comments

Comments
 (0)