diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 816d51a6ba395..9b4ffc7ea27f6 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -3,8 +3,8 @@ use std::ops::Range; use rustc_errors::E0232; use rustc_hir::AttrPath; use rustc_hir::attrs::diagnostic::{ - Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name, NameValue, - OnUnimplementedCondition, Piece, Predicate, + Directive, Filter, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name, + NameValue, Piece, Predicate, }; use rustc_macros::Diagnostic; use rustc_parse_format::{ @@ -201,12 +201,11 @@ fn parse_directive_items<'p>( items: impl Iterator, is_root: bool, ) -> Option { - let condition = None; let mut message: Option<(Span, _)> = None; let mut label: Option<(Span, _)> = None; let mut notes = ThinVec::new(); let mut parent_label = None; - let mut subcommands = ThinVec::new(); + let mut filters = ThinVec::new(); for item in items { let span = item.span(); @@ -330,7 +329,7 @@ fn parse_directive_items<'p>( if is_root { let items = or_malformed!(item.args().as_list()?); let mut iter = items.mixed(); - let condition: &MetaItemOrLitParser = match iter.next() { + let filter: &MetaItemOrLitParser = match iter.next() { Some(c) => c, None => { cx.emit_err(InvalidOnClause::Empty { span }); @@ -338,21 +337,19 @@ fn parse_directive_items<'p>( } }; - let condition = parse_condition(condition); + let filter = parse_filter(filter); if items.len() < 2 { // Something like `#[rustc_on_unimplemented(on(.., /* nothing */))]` - // There's a condition but no directive behind it, this is a mistake. + // There's a filter but no directive behind it, this is a mistake. malformed!(); } - let mut directive = - or_malformed!(parse_directive_items(cx, mode, iter, false)?); - - match condition { - Ok(c) => { - directive.condition = Some(c); - subcommands.push(directive); + match filter { + Ok(filter) => { + let directive = + or_malformed!(parse_directive_items(cx, mode, iter, false)?); + filters.push((filter, directive)); } Err(e) => { cx.emit_err(e); @@ -371,8 +368,7 @@ fn parse_directive_items<'p>( Some(Directive { is_rustc_attr: matches!(mode, Mode::RustcOnUnimplemented), - condition, - subcommands, + filters, message, label, notes, @@ -513,12 +509,10 @@ fn slice_span(input: Span, Range { start, end }: Range, is_source_literal if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input } } -pub(crate) fn parse_condition( - input: &MetaItemOrLitParser, -) -> Result { +pub(crate) fn parse_filter(input: &MetaItemOrLitParser) -> Result { let span = input.span(); let pred = parse_predicate(input)?; - Ok(OnUnimplementedCondition { span, pred }) + Ok(Filter { span, pred }) } fn parse_predicate(input: &MetaItemOrLitParser) -> Result { @@ -553,7 +547,7 @@ fn parse_predicate(input: &MetaItemOrLitParser) -> Result Name { } } -fn parse_filter(input: Symbol) -> FilterFormatString { +fn parse_filter_format(input: Symbol) -> FilterFormatString { let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic) .map(|p| match p { RpfPiece::Lit(s) => LitOrArg::Lit(Symbol::intern(s)), diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs index 26be6e2224615..7cbb0ea45b969 100644 --- a/compiler/rustc_hir/src/attrs/diagnostic.rs +++ b/compiler/rustc_hir/src/attrs/diagnostic.rs @@ -13,8 +13,9 @@ use crate::attrs::PrintAttribute; #[derive(Clone, Default, Debug, StableHash, Encodable, Decodable, PrintAttribute)] pub struct Directive { pub is_rustc_attr: bool, - pub condition: Option, - pub subcommands: ThinVec, + /// This is never nested more than once, i.e. the directives in this + /// thinvec have no filters of their own. + pub filters: ThinVec<(Filter, Directive)>, pub message: Option<(Span, FormatString)>, pub label: Option<(Span, FormatString)>, pub notes: ThinVec, @@ -28,11 +29,8 @@ impl Directive { /// We can't check this while parsing the attribute because `rustc_attr_parsing` doesn't have /// access to the item an attribute is on. Instead we later call this function in `check_attr`. pub fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) { - if let Some(condition) = &self.condition { - condition.visit_params(visit); - } - - for subcommand in &self.subcommands { + for (filter, subcommand) in &self.filters { + filter.visit_params(visit); subcommand.visit_params(visit); } @@ -54,61 +52,33 @@ impl Directive { pub fn eval( &self, - condition_options: Option<&ConditionOptions>, + filter_options: Option<&FilterOptions>, args: &FormatArgs, ) -> CustomDiagnostic { let this = &args.this; debug!( - "Directive::eval({self:?}, this={this}, options={condition_options:?}, args ={args:?})" + "Directive::eval({self:?}, this={this}, options={filter_options:?}, args ={args:?})" ); - let Some(condition_options) = condition_options else { + let mut ret = CustomDiagnostic::default(); + + if let Some(filter_options) = filter_options { + for (filter, directive) in &self.filters { + if filter.matches_predicate(filter_options) { + debug!("eval: {filter:?} succeeded"); + ret.update(directive, args); + } else { + debug!("eval: skipping {filter:?} due to {filter_options:?}"); + } + } + } else { debug_assert!( !self.is_rustc_attr, - "Directive::eval called for `rustc_on_unimplemented` without `condition_options`" + "Directive::eval called for `rustc_on_unimplemented` without `filter_options`" ); - return CustomDiagnostic { - label: self.label.as_ref().map(|l| l.1.format(args)), - message: self.message.as_ref().map(|m| m.1.format(args)), - notes: self.notes.iter().map(|n| n.format(args)).collect(), - parent_label: None, - }; }; - let mut message = None; - let mut label = None; - let mut notes = Vec::new(); - let mut parent_label = None; - - for command in self.subcommands.iter().chain(Some(self)).rev() { - debug!(?command); - if let Some(ref condition) = command.condition - && !condition.matches_predicate(condition_options) - { - debug!("eval: skipping {command:?} due to condition"); - continue; - } - debug!("eval: {command:?} succeeded"); - if let Some(ref message_) = command.message { - message = Some(message_.clone()); - } - - if let Some(ref label_) = command.label { - label = Some(label_.clone()); - } - - notes.extend(command.notes.clone()); - - if let Some(ref parent_label_) = command.parent_label { - parent_label = Some(parent_label_.clone()); - } - } - - CustomDiagnostic { - label: label.map(|l| l.1.format(args)), - message: message.map(|m| m.1.format(args)), - notes: notes.into_iter().map(|n| n.format(args)).collect(), - parent_label: parent_label.map(|e_s| e_s.format(args)), - } + ret.update(self, args); + ret } } @@ -121,6 +91,22 @@ pub struct CustomDiagnostic { pub parent_label: Option, } +impl CustomDiagnostic { + fn update(&mut self, di: &Directive, args: &FormatArgs) { + if self.message.is_none() { + self.message = di.message.as_ref().map(|m| m.1.format(args)); + } + if self.label.is_none() { + self.label = di.label.as_ref().map(|l| l.1.format(args)); + } + if self.parent_label.is_none() { + self.parent_label = di.parent_label.as_ref().map(|p| p.format(args)); + } + + self.notes.extend(di.notes.iter().map(|n| n.format(args))) + } +} + /// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", /// either as string pieces or dynamic arguments. #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] @@ -252,12 +238,12 @@ pub enum FormatArg { /// Represents the `on` filter in `#[rustc_on_unimplemented]`. #[derive(Clone, Debug, StableHash, Encodable, Decodable, PrintAttribute)] -pub struct OnUnimplementedCondition { +pub struct Filter { pub span: Span, pub pred: Predicate, } -impl OnUnimplementedCondition { - pub fn matches_predicate(self: &OnUnimplementedCondition, options: &ConditionOptions) -> bool { +impl Filter { + pub fn matches_predicate(&self, options: &FilterOptions) -> bool { self.pred.eval(&mut |p| match p { FlagOrNv::Flag(b) => options.has_flag(*b), FlagOrNv::NameValue(NameValue { name, value }) => { @@ -272,7 +258,7 @@ impl OnUnimplementedCondition { } } -/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`]. +/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`Filter`]. /// /// It is similar to the predicate in the `cfg` attribute, /// and may contain nested predicates. @@ -406,8 +392,7 @@ pub enum LitOrArg { Arg(Symbol), } -/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the -/// [`OnUnimplementedCondition`]. +/// Used with `Filter::matches_predicate` to evaluate the [`Filter`]. /// /// For example, given a /// ```rust,ignore (just an example) @@ -433,7 +418,7 @@ pub enum LitOrArg { /// it will look like this: /// /// ```rust,ignore (just an example) -/// ConditionOptions { +/// FilterOptions { /// self_types: ["u32", "{integral}"], /// from_desugaring: Some("QuestionMark"), /// cause: None, @@ -446,7 +431,7 @@ pub enum LitOrArg { /// } /// ``` #[derive(Debug)] -pub struct ConditionOptions { +pub struct FilterOptions { /// All the self types that may apply. pub self_types: Vec, // The kind of compiler desugaring. @@ -460,7 +445,7 @@ pub struct ConditionOptions { pub generic_args: Vec<(Symbol, String)>, } -impl ConditionOptions { +impl FilterOptions { pub fn has_flag(&self, name: Flag) -> bool { match name { Flag::CrateLocal => self.crate_local, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index a65e612b93e9c..b15f528b7261e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use rustc_hir as hir; -use rustc_hir::attrs::diagnostic::{ConditionOptions, CustomDiagnostic, FormatArgs}; +use rustc_hir::attrs::diagnostic::{CustomDiagnostic, FilterOptions, FormatArgs}; use rustc_hir::def_id::LocalDefId; use rustc_hir::find_attr; use rustc_middle::ty::print::PrintTraitRefExt; @@ -40,11 +40,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if trait_pred.polarity() != ty::PredicatePolarity::Positive { return CustomDiagnostic::default(); } - let (condition_options, format_args) = + let (filter_options, format_args) = self.on_unimplemented_components(trait_pred, obligation, long_ty_path); if let Some(command) = find_attr!(self.tcx, trait_pred.def_id(), OnUnimplemented {directive, ..} => directive.as_deref()).flatten() { command.eval( - Some(&condition_options), + Some(&filter_options), &format_args, ) } else { @@ -57,7 +57,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, obligation: &PredicateObligation<'tcx>, long_ty_path: &mut Option, - ) -> (ConditionOptions, FormatArgs) { + ) -> (FilterOptions, FormatArgs) { let (def_id, args) = (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args); let trait_pred = trait_pred.skip_binder(); @@ -219,14 +219,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id); let this_sugared = trait_pred.trait_ref.print_trait_sugared().to_string(); - let condition_options = ConditionOptions { - self_types, - from_desugaring, - cause, - crate_local, - direct, - generic_args, - }; + let filter_options = + FilterOptions { self_types, from_desugaring, cause, crate_local, direct, generic_args }; // Unlike the generic_args earlier, // this one is *not* collected under `with_no_trimmed_paths!` @@ -256,6 +250,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .collect(); let format_args = FormatArgs { this, this_sugared, generic_args, item_context }; - (condition_options, format_args) + (filter_options, format_args) } } diff --git a/tests/ui/abi/bad-custom.stderr b/tests/ui/abi/bad-custom.stderr index 372ef71375c58..d7c9cae35c7b3 100644 --- a/tests/ui/abi/bad-custom.stderr +++ b/tests/ui/abi/bad-custom.stderr @@ -194,8 +194,8 @@ LL | f | - return type was inferred to be `unsafe extern "custom" fn()` here | = help: the trait `Fn()` is not implemented for `unsafe extern "custom" fn()` - = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe extern "custom" fn()` in a closure with no arguments: `|| { /* code */ }` + = note: unsafe function cannot be called generically without an unsafe block error: items with the "custom" ABI can only be declared externally or defined via naked functions --> $DIR/bad-custom.rs:25:1 diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr index 1c6e3905abb11..52543caad37db 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr @@ -78,8 +78,8 @@ LL | call(foo_unsafe); | required by a bound introduced by this call | = help: the trait `Fn()` is not implemented for fn item `unsafe fn() {foo_unsafe}` - = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` + = note: unsafe function cannot be called generically without an unsafe block = note: `#[target_feature]` functions do not implement the `Fn` traits = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call` @@ -97,8 +97,8 @@ LL | call_mut(foo_unsafe); | required by a bound introduced by this call | = help: the trait `FnMut()` is not implemented for fn item `unsafe fn() {foo_unsafe}` - = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` + = note: unsafe function cannot be called generically without an unsafe block = note: `#[target_feature]` functions do not implement the `Fn` traits = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call_mut` @@ -116,8 +116,8 @@ LL | call_once(foo_unsafe); | required by a bound introduced by this call | = help: the trait `FnOnce()` is not implemented for fn item `unsafe fn() {foo_unsafe}` - = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` + = note: unsafe function cannot be called generically without an unsafe block = note: `#[target_feature]` functions do not implement the `Fn` traits = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call_once` diff --git a/tests/ui/suggestions/path-display.stderr b/tests/ui/suggestions/path-display.stderr index df9e855bb714d..3594be2efcc47 100644 --- a/tests/ui/suggestions/path-display.stderr +++ b/tests/ui/suggestions/path-display.stderr @@ -7,8 +7,8 @@ LL | println!("{}", path); | required by this formatting parameter | = help: the trait `std::fmt::Display` is not implemented for `Path` - = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: required for `&Path` to implement `std::fmt::Display` error[E0277]: `PathBuf` doesn't implement `std::fmt::Display` @@ -20,8 +20,8 @@ LL | println!("{}", path); | required by this formatting parameter | = help: the trait `std::fmt::Display` is not implemented for `PathBuf` - = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead = note: call `.display()` or `.to_string_lossy()` to safely print paths, as they may contain non-Unicode data + = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead error: aborting due to 2 previous errors diff --git a/tests/ui/traits/next-solver/fn-trait.stderr b/tests/ui/traits/next-solver/fn-trait.stderr index 00243fd905950..77c834f377852 100644 --- a/tests/ui/traits/next-solver/fn-trait.stderr +++ b/tests/ui/traits/next-solver/fn-trait.stderr @@ -7,8 +7,8 @@ LL | require_fn(f as unsafe fn() -> i32); | required by a bound introduced by this call | = help: the trait `Fn()` is not implemented for `unsafe fn() -> i32` - = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() -> i32` in a closure with no arguments: `|| { /* code */ }` + = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `require_fn` --> $DIR/fn-trait.rs:3:23 | @@ -56,8 +56,8 @@ LL | require_fn(h); | required by a bound introduced by this call | = help: the trait `Fn()` is not implemented for fn item `unsafe fn() -> i32 {h}` - = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() -> i32 {h}` in a closure with no arguments: `|| { /* code */ }` + = note: unsafe function cannot be called generically without an unsafe block note: required by a bound in `require_fn` --> $DIR/fn-trait.rs:3:23 | diff --git a/tests/ui/unpretty/diagnostic-attr.stdout b/tests/ui/unpretty/diagnostic-attr.stdout index 4dc0c54633d72..b5e0269410fb7 100644 --- a/tests/ui/unpretty/diagnostic-attr.stdout +++ b/tests/ui/unpretty/diagnostic-attr.stdout @@ -7,7 +7,7 @@ use ::std::prelude::rust_2015::*; //@ edition: 2015 #[attr = OnUnimplemented {directive: Directive {is_rustc_attr: false, -subcommands: [], +filters: [], message: FormatString {input: "My Message for `ImportantTrait<{A}>` implemented for `{Self}`", pieces: [Lit("My Message for `ImportantTrait<"), Arg(GenericParam {generic_param: "A"}), Lit(">` implemented for `"), @@ -22,7 +22,7 @@ impl ImportantTrait for T where T: Clone { } struct X; -#[attr = OnConst {directive: Directive {is_rustc_attr: false, subcommands: [], +#[attr = OnConst {directive: Directive {is_rustc_attr: false, filters: [], message: FormatString {input: "My Message for `ImportantTrait` implemented for `{Self}`", pieces: [Lit("My Message for `ImportantTrait` implemented for `"), Arg(SelfUpper), Lit("`")]}, label: FormatString {input: "My Label",