Skip to content

Commit 7a7f8df

Browse files
committed
rustc_on_unimplemented: emit notes in declared order
1 parent f53b654 commit 7a7f8df

7 files changed

Lines changed: 53 additions & 72 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
@@ -210,12 +210,11 @@ fn parse_directive_items<'p>(
210210
items: impl Iterator<Item = &'p MetaItemOrLitParser>,
211211
is_root: bool,
212212
) -> Option<Directive> {
213-
let condition = None;
214213
let mut message: Option<(Span, _)> = None;
215214
let mut label: Option<(Span, _)> = None;
216215
let mut notes = ThinVec::new();
217216
let mut parent_label = None;
218-
let mut subcommands = ThinVec::new();
217+
let mut filters = ThinVec::new();
219218

220219
for item in items {
221220
let span = item.span();
@@ -357,21 +356,19 @@ fn parse_directive_items<'p>(
357356
}
358357
};
359358

360-
let condition = parse_condition(condition);
359+
let filter = parse_condition(condition);
361360

362361
if items.len() < 2 {
363362
// Something like `#[rustc_on_unimplemented(on(.., /* nothing */))]`
364363
// There's a condition but no directive behind it, this is a mistake.
365364
malformed!();
366365
}
367366

368-
let mut directive =
369-
or_malformed!(parse_directive_items(cx, mode, iter, false)?);
370-
371-
match condition {
372-
Ok(c) => {
373-
directive.condition = Some(c);
374-
subcommands.push(directive);
367+
match filter {
368+
Ok(filter) => {
369+
let directive =
370+
or_malformed!(parse_directive_items(cx, mode, iter, false)?);
371+
filters.push((filter, directive));
375372
}
376373
Err(e) => {
377374
cx.emit_err(e);
@@ -390,8 +387,7 @@ fn parse_directive_items<'p>(
390387

391388
Some(Directive {
392389
is_rustc_attr: matches!(mode, Mode::RustcOnUnimplemented),
393-
condition,
394-
subcommands,
390+
filters,
395391
message,
396392
label,
397393
notes,

compiler/rustc_hir/src/attrs/diagnostic.rs

Lines changed: 35 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
use std::fmt;
33
use std::fmt::Debug;
44

5-
pub use rustc_ast::attr::data_structures::*;
65
use rustc_macros::{Decodable, Encodable, HashStable, PrintAttribute};
76
use rustc_span::{DesugaringKind, Span, Symbol, kw};
87
use thin_vec::ThinVec;
@@ -13,8 +12,9 @@ use crate::attrs::PrintAttribute;
1312
#[derive(Clone, Default, Debug, HashStable, Encodable, Decodable, PrintAttribute)]
1413
pub struct Directive {
1514
pub is_rustc_attr: bool,
16-
pub condition: Option<OnUnimplementedCondition>,
17-
pub subcommands: ThinVec<Directive>,
15+
/// This is never nested more than once, i.e. the directives in this
16+
/// thinvec have no filters of their own.
17+
pub filters: ThinVec<(OnUnimplementedCondition, Directive)>,
1818
pub message: Option<(Span, FormatString)>,
1919
pub label: Option<(Span, FormatString)>,
2020
pub notes: ThinVec<FormatString>,
@@ -28,11 +28,8 @@ impl Directive {
2828
/// We can't check this while parsing the attribute because `rustc_attr_parsing` doesn't have
2929
/// access to the item an attribute is on. Instead we later call this function in `check_attr`.
3030
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 {
31+
for (filter, subcommand) in &self.filters {
32+
filter.visit_params(visit);
3633
subcommand.visit_params(visit);
3734
}
3835

@@ -62,53 +59,25 @@ impl Directive {
6259
"Directive::eval({self:?}, this={this}, options={condition_options:?}, args ={args:?})"
6360
);
6461

65-
let Some(condition_options) = condition_options else {
62+
let mut ret = CustomDiagnostic::default();
63+
64+
if let Some(condition_options) = condition_options {
65+
for (filter, directive) in &self.filters {
66+
if filter.matches_predicate(condition_options) {
67+
debug!("eval: {filter:?} succeeded");
68+
ret.update(directive, args);
69+
} else {
70+
debug!("eval: skipping {filter:?} due to condition");
71+
}
72+
}
73+
} else {
6674
debug_assert!(
6775
!self.is_rustc_attr,
6876
"Directive::eval called for `rustc_on_unimplemented` without `condition_options`"
6977
);
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-
};
7678
};
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-
}
79+
ret.update(self, args);
80+
ret
11281
}
11382
}
11483

@@ -121,6 +90,22 @@ pub struct CustomDiagnostic {
12190
pub parent_label: Option<String>,
12291
}
12392

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