diff --git a/Cargo.lock b/Cargo.lock index 545c776b4805e..17a2ff43719b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3546,6 +3546,7 @@ dependencies = [ "rustc_lexer", "rustc_macros", "rustc_parse", + "rustc_parse_format", "rustc_session", "rustc_span", "rustc_target", diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 0a11a2da0dcf8..886df58e8d6f0 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -15,6 +15,7 @@ rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } rustc_parse = { path = "../rustc_parse" } +rustc_parse_format = { path = "../rustc_parse_format" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs new file mode 100644 index 0000000000000..280691f308eec --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -0,0 +1,117 @@ +#![allow(warnings)] + +use std::ops::Range; + +use rustc_hir::attrs::diagnostic::{FormatArg, FormatString, Piece}; +use rustc_hir::lints::FormatWarning; +use rustc_parse_format::{ + Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position, +}; +use rustc_span::{InnerSpan, Span, Symbol, kw, sym}; + +pub mod on_unimplemented; + +#[derive(Copy, Clone)] +pub(crate) enum Ctx { + // `#[rustc_on_unimplemented]` + RustcOnUnimplemented, + // `#[diagnostic::...]` + DiagnosticOnUnimplemented, +} + +pub(crate) fn parse_format_string( + input: Symbol, + snippet: Option, + span: Span, + ctx: Ctx, +) -> Result<(FormatString, Vec), ParseError> { + let s = input.as_str(); + let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic); + let pieces: Vec<_> = parser.by_ref().collect(); + + if let Some(err) = parser.errors.into_iter().next() { + return Err(err); + } + let mut warnings = Vec::new(); + + let pieces = pieces + .into_iter() + .map(|piece| match piece { + RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)), + RpfPiece::NextArgument(arg) => { + warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); + let arg = parse_arg(&arg, ctx, &mut warnings, span, parser.is_source_literal); + Piece::Arg(arg) + } + }) + .collect(); + + Ok((FormatString { input, pieces, span }, warnings)) +} + +fn parse_arg( + arg: &Argument<'_>, + ctx: Ctx, + warnings: &mut Vec, + input_span: Span, + is_source_literal: bool, +) -> FormatArg { + let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); + + match arg.position { + // Something like "hello {name}" + Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) { + // Only `#[rustc_on_unimplemented]` can use these + (Ctx::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext, + (Ctx::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This, + (Ctx::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait, + // Any attribute can use these + ( + Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, + kw::SelfUpper, + ) => FormatArg::SelfUpper, + ( + Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, + generic_param, + ) => FormatArg::GenericParam { generic_param, span }, + }, + + // `{:1}` and `{}` are ignored + Position::ArgumentIs(idx) => { + warnings.push(FormatWarning::PositionalArgument { + span, + help: format!("use `{{{idx}}}` to print a number in braces"), + }); + FormatArg::AsIs(Symbol::intern(&format!("{{{idx}}}"))) + } + Position::ArgumentImplicitlyIs(_) => { + warnings.push(FormatWarning::PositionalArgument { + span, + help: String::from("use `{{}}` to print empty braces"), + }); + FormatArg::AsIs(sym::empty_braces) + } + } +} + +/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything +/// with specifiers, so emit a warning if they are used. +fn warn_on_format_spec( + spec: &FormatSpec<'_>, + warnings: &mut Vec, + input_span: Span, + is_source_literal: bool, +) { + if spec.ty != "" { + let span = spec + .ty_span + .as_ref() + .map(|inner| slice_span(input_span, inner.clone(), is_source_literal)) + .unwrap_or(input_span); + warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() }) + } +} + +fn slice_span(input: Span, Range { start, end }: Range, is_source_literal: bool) -> Span { + if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs new file mode 100644 index 0000000000000..477cc5e14eb08 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_unimplemented.rs @@ -0,0 +1,202 @@ +#![allow(warnings)] +use rustc_hir::attrs::diagnostic::OnUnimplementedDirective; +use rustc_hir::lints::AttributeLintKind; +use rustc_session::lint::builtin::{ + MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, +}; +use thin_vec::thin_vec; + +use crate::attributes::diagnostic::*; +use crate::attributes::prelude::*; +use crate::attributes::template; +/// Folds all uses of `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`. +/// FIXME(mejrs): add an example +#[derive(Default)] +pub struct OnUnimplementedParser { + directive: Option<(Span, OnUnimplementedDirective)>, +} + +impl AttributeParser for OnUnimplementedParser { + const ATTRIBUTES: AcceptMapping = &[ + (&[sym::diagnostic, sym::on_unimplemented], template!(Word), |this, cx, args| { + let span = cx.attr_span; + + let items = match args { + ArgParser::List(items) => items, + ArgParser::NoArgs => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MissingOptionsForOnUnimplemented, + span, + ); + return; + } + ArgParser::NameValue(_) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnimplementedAttr { span }, + span, + ); + return; + } + }; + + let Some(directive) = parse_directive_items(cx, Ctx::DiagnosticOnUnimplemented, items) + else { + return; + }; + merge_directives(cx, &mut this.directive, (span, directive)); + }), + // todo (&[sym::rustc_on_unimplemented], template!(Word), |this, cx, args| {}), + ]; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option { + self.directive.map(|(span, directive)| AttributeKind::OnUnimplemented { + span, + directive: Some(Box::new(directive)), + }) + } +} + +fn merge_directives( + cx: &mut AcceptContext<'_, '_, S>, + first: &mut Option<(Span, OnUnimplementedDirective)>, + later: (Span, OnUnimplementedDirective), +) { + if let Some((first_span, first)) = first { + merge(cx, &mut first.message, later.1.message, "message"); + merge(cx, &mut first.label, later.1.label, "label"); + first.notes.extend(later.1.notes); + } else { + *first = Some(later); + } +} + +fn merge( + cx: &mut AcceptContext<'_, '_, S>, + first: &mut Option<(Span, T)>, + later: Option<(Span, T)>, + option_name: &'static str, +) { + match (first, later) { + (Some(_) | None, None) => {} + (Some((first_span, _)), Some((later_span, _))) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::IgnoredDiagnosticOption { + first_span: *first_span, + later_span, + option_name, + }, + later_span, + ); + } + (first @ None, Some(later)) => { + first.get_or_insert(later); + } + } +} + +fn parse_directive_items( + cx: &mut AcceptContext, + ctx: Ctx, + items: &MetaItemListParser, +) -> Option { + let condition = None; + let mut message = None; + let mut label = None; + let mut notes = ThinVec::new(); + let mut parent_label = None; + let mut subcommands = ThinVec::new(); + let mut append_const_msg = None; + + for item in items.mixed() { + // At this point, we are expecting any of: + // message = "..", label = "..", note = ".." + let Some((name, value, value_span)) = (try { + let item = item.meta_item()?; + let name = item.ident()?.name; + let nv = item.args().name_value()?; + let value = nv.value_as_str()?; + (name, value, nv.value_span) + }) else { + let span = item.span(); + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnimplementedAttr { span }, + span, + ); + continue; + }; + + let mut parse = |input| { + let snippet = cx.sess.source_map().span_to_snippet(value_span).ok(); + let is_snippet = snippet.is_some(); + match parse_format_string(input, snippet, value_span, ctx) { + Ok((f, warnings)) => { + for warning in warnings { + let (FormatWarning::InvalidSpecifier { span, .. } + | FormatWarning::PositionalArgument { span, .. }) = warning; + cx.emit_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + AttributeLintKind::MalformedDiagnosticFormat { warning }, + span, + ); + } + + f + } + Err(e) => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + AttributeLintKind::DiagnosticWrappedParserError { + description: e.description, + label: e.label, + span: slice_span(value_span, e.span, is_snippet), + }, + value_span, + ); + // We could not parse the input, just use it as-is. + FormatString { input, span: value_span, pieces: thin_vec![Piece::Lit(input)] } + } + } + }; + match name { + sym::message => { + if message.is_none() { + message.insert((item.span(), parse(value))); + } else { + // warn + } + } + sym::label => { + if label.is_none() { + label.insert((item.span(), parse(value))); + } else { + // warn + } + } + sym::note => notes.push(parse(value)), + _other => { + let span = item.span(); + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnUnimplementedAttr { span }, + span, + ); + continue; + } + } + } + + Some(OnUnimplementedDirective { + condition, + subcommands, + message, + label, + notes, + parent_label, + append_const_msg, + }) +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 0d328d5cc6a70..9047ac8a86108 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -39,6 +39,7 @@ pub(crate) mod confusables; pub(crate) mod crate_level; pub(crate) mod debugger; pub(crate) mod deprecation; +pub(crate) mod diagnostic; pub(crate) mod do_not_recommend; pub(crate) mod doc; pub(crate) mod dummy; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index ad39b0ec26d93..8b9ee9b639a00 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -26,6 +26,7 @@ use crate::attributes::confusables::*; use crate::attributes::crate_level::*; use crate::attributes::debugger::*; use crate::attributes::deprecation::*; +use crate::attributes::diagnostic::on_unimplemented::*; use crate::attributes::do_not_recommend::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; @@ -147,6 +148,7 @@ attribute_parsers!( DocParser, MacroUseParser, NakedParser, + OnUnimplementedParser, StabilityParser, UsedParser, // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index fe050250e354d..130848c28384c 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -80,6 +80,7 @@ #![feature(decl_macro)] #![feature(if_let_guard)] #![feature(iter_intersperse)] +#![feature(try_blocks)] #![recursion_limit = "256"] // tidy-alphabetical-end diff --git a/compiler/rustc_error_codes/src/error_codes/E0230.md b/compiler/rustc_error_codes/src/error_codes/E0230.md index c30a7e38e9c48..b6dcf4b977741 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0230.md +++ b/compiler/rustc_error_codes/src/error_codes/E0230.md @@ -1,26 +1,4 @@ -The `#[rustc_on_unimplemented]` attribute lets you specify a custom error -message for when a particular trait isn't implemented on a type placed in a -position that needs that trait. For example, when the following code is -compiled: +#### Note: this error code is no longer emitted by the compiler. -```compile_fail,E0230 -#![feature(rustc_attrs)] -#![allow(internal_features)] - -#[rustc_on_unimplemented = "error on `{Self}` with params `<{A},{B}>`"] // error -trait BadAnnotation {} -``` - -There will be an error about `bool` not implementing `Index`, followed by a -note saying "the type `bool` cannot be indexed by `u8`". - -As you can see, you can specify type parameters in curly braces for -instantiation with the actual types (using the regular format string syntax) in -a given situation. Furthermore, `{Self}` will be instantiated to the type (in -this case, `bool`) that we tried to use. - -This error appears when the curly braces contain an identifier which doesn't -match with any of the type parameters or the string `Self`. This might happen -if you misspelled a type parameter, or if you intended to use literal curly -braces. If it is the latter, escape the curly braces with a second curly brace -of the same type; e.g., a literal `{` is `{{`. +The `#[rustc_on_unimplemented]` attribute used to raise this error for various +misuses of the attribute; these are now warnings. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 79452913c2594..10cc73751b58a 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -15,6 +15,7 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol}; pub use rustc_target::spec::SanitizerSet; use thin_vec::ThinVec; +use crate::attrs::diagnostic::*; use crate::attrs::pretty_printing::PrintAttribute; use crate::limit::Limit; use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability}; @@ -981,6 +982,13 @@ pub enum AttributeKind { /// Represents `#[non_exhaustive]` NonExhaustive(Span), + /// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`. + OnUnimplemented { + span: Span, + /// None if the directive was malformed in some way. + directive: Option>, + }, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs new file mode 100644 index 0000000000000..95b10865b34c6 --- /dev/null +++ b/compiler/rustc_hir/src/attrs/diagnostic.rs @@ -0,0 +1,277 @@ +//! Contains the data structures used by the diagnostic attribute family. +pub use rustc_ast::attr::data_structures::*; +use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute}; +use rustc_span::{DesugaringKind, Span, Symbol}; +use thin_vec::ThinVec; + +use crate::attrs::PrintAttribute; + +#[derive(Clone, Default, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct OnUnimplementedDirective { + pub condition: Option, + pub subcommands: ThinVec, + pub message: Option<(Span, FormatString)>, + pub label: Option<(Span, FormatString)>, + pub notes: ThinVec, + pub parent_label: Option, + pub append_const_msg: Option, +} + +impl OnUnimplementedDirective { + pub fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) { + if let Some((_, message)) = &self.message { + message.visit_params(visit); + } + if let Some((_, label)) = &self.label { + label.visit_params(visit); + } + + if let Some(parent_label) = &self.parent_label { + parent_label.visit_params(visit); + } + + for note in &self.notes { + note.visit_params(visit); + } + } +} + +/// For the `#[rustc_on_unimplemented]` attribute +#[derive(Default, Debug)] +pub struct OnUnimplementedNote { + pub message: Option, + pub label: Option, + pub notes: Vec, + pub parent_label: Option, + // If none, should fall back to a generic message + pub append_const_msg: Option, +} + +/// Append a message for `[const] Trait` errors. +#[derive( + Clone, + Copy, + PartialEq, + Eq, + Debug, + Default, + HashStable_Generic, + Encodable, + Decodable, + PrintAttribute +)] +pub enum AppendConstMessage { + #[default] + Default, + Custom(Symbol, Span), +} + +/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", +/// either as string pieces or dynamic arguments. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct FormatString { + pub input: Symbol, + pub span: Span, + pub pieces: ThinVec, +} + +impl FormatString { + fn visit_params(&self, visit: &mut impl FnMut(Symbol, Span)) { + for piece in &self.pieces { + if let Piece::Arg(FormatArg::GenericParam { generic_param, span }) = piece { + visit(*generic_param, *span); + } + } + } +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Piece { + Lit(Symbol), + Arg(FormatArg), +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum FormatArg { + // A generic parameter, like `{T}` if we're on the `From` trait. + GenericParam { + generic_param: Symbol, + span: Span, + }, + // `{Self}` + SelfUpper, + /// `{This}` or `{TraitName}` + This, + /// The sugared form of the trait + Trait, + /// what we're in, like a function, method, closure etc. + ItemContext, + /// What the user typed, if it doesn't match anything we can use. + AsIs(Symbol), +} + +/// Represents the `on` filter in `#[rustc_on_unimplemented]`. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct OnUnimplementedCondition { + pub span: Span, + pub pred: Predicate, +} + +/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`]. +/// +/// It is similar to the predicate in the `cfg` attribute, +/// and may contain nested predicates. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Predicate { + /// A condition like `on(crate_local)`. + Flag(Flag), + /// A match, like `on(Rhs = "Whatever")`. + Match(NameValue), + /// Negation, like `on(not($pred))`. + Not(Box), + /// True if all predicates are true, like `on(all($a, $b, $c))`. + All(ThinVec), + /// True if any predicate is true, like `on(any($a, $b, $c))`. + Any(ThinVec), +} + +impl Predicate { + pub fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool { + match self { + Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)), + Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)), + Predicate::Not(not) => !not.eval(eval), + Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)), + Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)), + } + } +} + +/// Represents a `MetaWord` in an `on`-filter. +#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Flag { + /// Whether the code causing the trait bound to not be fulfilled + /// is part of the user's crate. + CrateLocal, + /// Whether the obligation is user-specified rather than derived. + Direct, + /// Whether we are in some kind of desugaring like + /// `?` or `try { .. }`. + FromDesugaring, +} + +/// A `MetaNameValueStr` in an `on`-filter. +/// +/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct NameValue { + pub name: Name, + /// Something like `"&str"` or `"alloc::string::String"`, + /// in which case it just contains a single string piece. + /// But if it is something like `"&[{A}]"` then it must be formatted later. + pub value: FilterFormatString, +} + +/// The valid names of the `on` filter. +#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum Name { + Cause, + FromDesugaring, + SelfUpper, + GenericArg(Symbol), +} + +#[derive(Debug, Clone)] +pub enum FlagOrNv<'p> { + Flag(&'p Flag), + NameValue(&'p NameValue), +} + +/// Represents a value inside an `on` filter. +/// +/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. +/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`. +/// The `Arg` variant is used when it contains formatting like +/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`. +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub struct FilterFormatString { + pub pieces: ThinVec, +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] +pub enum LitOrArg { + Lit(Symbol), + Arg(Symbol), +} + +/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the +/// [`OnUnimplementedCondition`]. +/// +/// For example, given a +/// ```rust,ignore (just an example) +/// #[rustc_on_unimplemented( +/// on(all(from_desugaring = "QuestionMark"), +/// message = "the `?` operator can only be used in {ItemContext} \ +/// that returns `Result` or `Option` \ +/// (or another type that implements `{FromResidual}`)", +/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", +/// parent_label = "this function should return `Result` or `Option` to accept `?`" +/// ), +/// )] +/// pub trait FromResidual::Residual> { +/// ... +/// } +/// +/// async fn an_async_function() -> u32 { +/// let x: Option = None; +/// x?; //~ ERROR the `?` operator +/// 22 +/// } +/// ``` +/// it will look like this: +/// +/// ```rust,ignore (just an example) +/// ConditionOptions { +/// self_types: ["u32", "{integral}"], +/// from_desugaring: Some("QuestionMark"), +/// cause: None, +/// crate_local: false, +/// direct: true, +/// generic_args: [("Self","u32"), +/// ("R", "core::option::Option"), +/// ("R", "core::option::Option" ), +/// ], +/// } +/// ``` +#[derive(Debug)] +pub struct ConditionOptions { + /// All the self types that may apply. + pub self_types: Vec, + // The kind of compiler desugaring. + pub from_desugaring: Option, + /// Match on a variant of [rustc_infer::traits::ObligationCauseCode]. + pub cause: Option, + pub crate_local: bool, + /// Is the obligation "directly" user-specified, rather than derived? + pub direct: bool, + // A list of the generic arguments and their reified types. + pub generic_args: Vec<(Symbol, String)>, +} + +impl ConditionOptions { + pub fn has_flag(&self, name: Flag) -> bool { + match name { + Flag::CrateLocal => self.crate_local, + Flag::Direct => self.direct, + Flag::FromDesugaring => self.from_desugaring.is_some(), + } + } + pub fn contains(&self, name: Name, value: String) -> bool { + match name { + Name::SelfUpper => self.self_types.contains(&value), + Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)), + Name::Cause => self.cause == Some(value), + Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)), + } + } +} diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index ba351d80544c6..8da70ee3ef436 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -75,6 +75,7 @@ impl AttributeKind { NoMangle(..) => Yes, // Needed for rustdoc NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc + OnUnimplemented { .. } => Yes, Optimize(..) => No, PanicRuntime => No, PatchableFunctionEntry { .. } => Yes, diff --git a/compiler/rustc_hir/src/attrs/mod.rs b/compiler/rustc_hir/src/attrs/mod.rs index 482e6c90739f2..26a891cfbb7dc 100644 --- a/compiler/rustc_hir/src/attrs/mod.rs +++ b/compiler/rustc_hir/src/attrs/mod.rs @@ -9,6 +9,7 @@ pub use encode_cross_crate::EncodeCrossCrate; pub use pretty_printing::PrintAttribute; mod data_structures; +pub mod diagnostic; mod encode_cross_crate; mod pretty_printing; diff --git a/compiler/rustc_hir/src/lints.rs b/compiler/rustc_hir/src/lints.rs index eba2d182d2c48..1589a6de220e7 100644 --- a/compiler/rustc_hir/src/lints.rs +++ b/compiler/rustc_hir/src/lints.rs @@ -1,6 +1,6 @@ use rustc_data_structures::fingerprint::Fingerprint; -pub use rustc_lint_defs::AttributeLintKind; use rustc_lint_defs::LintId; +pub use rustc_lint_defs::{AttributeLintKind, FormatWarning}; use rustc_macros::HashStable_Generic; use rustc_span::Span; diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index e0bfae9617ff1..42481344dc366 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -27,7 +27,7 @@ use rustc_session::lint::builtin::UNINHABITED_STATIC; use rustc_span::source_map::Spanned; use rustc_target::spec::{AbiMap, AbiMapping}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; -use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; +use rustc_trait_selection::error_reporting::traits::on_unimplemented::of_item_directive; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use tracing::{debug, instrument}; @@ -1126,7 +1126,7 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), pub(super) fn check_diagnostic_attrs(tcx: TyCtxt<'_>, def_id: LocalDefId) { // an error would be reported if this fails. - let _ = OnUnimplementedDirective::of_item(tcx, def_id.to_def_id()); + let _ = of_item_directive(tcx, def_id.to_def_id()); } pub(super) fn check_specialization_validity<'tcx>( diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 517d73f517833..e0092d6ddbe91 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -17,6 +17,7 @@ use rustc_errors::{ Applicability, Diag, MultiSpan, StashKey, listify, pluralize, struct_span_code_err, }; use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::diagnostic::OnUnimplementedNote; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; @@ -38,7 +39,6 @@ use rustc_span::{ kw, sym, }; use rustc_trait_selection::error_reporting::traits::DefIdOrName; -use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 7681eedc75ed0..9136dd89e8e63 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -4,7 +4,7 @@ use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{ Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion, }; -use rustc_hir::lints::AttributeLintKind; +use rustc_hir::lints::{AttributeLintKind, FormatWarning}; use rustc_middle::middle::stability; use rustc_middle::ty::TyCtxt; use rustc_session::Session; @@ -442,5 +442,26 @@ pub fn decorate_attribute_lint( &AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.decorate_lint(diag), &AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.decorate_lint(diag), + &AttributeLintKind::MalformedOnUnimplementedAttr { span } => { + lints::MalformedOnUnimplementedAttrLint { span }.decorate_lint(diag) + } + AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning { + FormatWarning::PositionalArgument { .. } => { + lints::DisallowedPositionalArgument.decorate_lint(diag) + } + FormatWarning::InvalidSpecifier { .. } => { + lints::InvalidFormatSpecifier.decorate_lint(diag) + } + }, + AttributeLintKind::DiagnosticWrappedParserError { description, label, span } => { + lints::WrappedParserError { description, label, span: *span }.decorate_lint(diag) + } + &AttributeLintKind::IgnoredDiagnosticOption { option_name, first_span, later_span } => { + lints::IgnoredDiagnosticOption { option_name, first_span, later_span } + .decorate_lint(diag) + } + &AttributeLintKind::MissingOptionsForOnUnimplemented => { + lints::MissingOptionsForOnUnimplementedAttr.decorate_lint(diag) + } } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 0aa5199cffc6e..bce1c7e7f5b68 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3883,3 +3883,47 @@ pub(crate) struct UnreachableCfgSelectPredicateWildcard { #[label("always matches")] pub wildcard_span: Span, } + +#[derive(LintDiagnostic)] +#[diag("positional format arguments are not allowed here")] +#[help( + "only named format arguments with the name of one of the generic types are allowed in this context" +)] +pub(crate) struct DisallowedPositionalArgument; + +#[derive(LintDiagnostic)] +#[diag("invalid format specifier")] +#[help("no format specifier are supported in this position")] +pub(crate) struct InvalidFormatSpecifier; + +#[derive(LintDiagnostic)] +#[diag("{$description}")] +pub(crate) struct WrappedParserError<'a> { + pub description: &'a str, + #[label("{$label}")] + pub span: Span, + pub label: &'a str, +} + +#[derive(LintDiagnostic)] +#[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")] +pub(crate) struct IgnoredDiagnosticOption { + pub option_name: &'static str, + #[label("`{$option_name}` is first declared here")] + pub first_span: Span, + #[label("`{$option_name}` is later redundantly declared here")] + pub later_span: Span, +} + +#[derive(LintDiagnostic)] +#[diag("missing options for `on_unimplemented` attribute")] +#[help("at least one of the `message`, `note` and `label` options are expected")] +pub(crate) struct MissingOptionsForOnUnimplementedAttr; + +#[derive(LintDiagnostic)] +#[diag("malformed `on_unimplemented` attribute")] +#[help("only `message`, `note` and `label` are allowed as options")] +pub(crate) struct MalformedOnUnimplementedAttrLint { + #[label("invalid option found here")] + pub span: Span, +} diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 0c454ec60f4a6..e3c687ebb6576 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -833,6 +833,29 @@ pub enum AttributeLintKind { MalformedDoc, ExpectedNoArgs, ExpectedNameValue, + MalformedOnUnimplementedAttr { + span: Span, + }, + MalformedDiagnosticFormat { + warning: FormatWarning, + }, + DiagnosticWrappedParserError { + description: String, + label: String, + span: Span, + }, + IgnoredDiagnosticOption { + option_name: &'static str, + first_span: Span, + later_span: Span, + }, + MissingOptionsForOnUnimplemented, +} + +#[derive(Debug, Clone, HashStable_Generic)] +pub enum FormatWarning { + PositionalArgument { span: Span, help: String }, + InvalidSpecifier { name: String, span: Span }, } pub type RegisteredTools = FxIndexSet; diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 6c4b76a08ad27..41384a900ab0c 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -20,6 +20,7 @@ use rustc_feature::{ ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, }; +use rustc_hir::attrs::diagnostic::OnUnimplementedDirective; use rustc_hir::attrs::{ AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr, MirDialect, MirPhase, ReprAttr, SanitizerSet, @@ -28,9 +29,9 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ - self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, HirId, Item, ItemKind, - MethodKind, PartialConstStability, Safety, Stability, StabilityLevel, Target, TraitItem, - find_attr, + self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, GenericParamKind, HirId, + Item, ItemKind, MethodKind, Node, ParamName, PartialConstStability, Safety, Stability, + StabilityLevel, Target, TraitItem, find_attr, }; use rustc_macros::LintDiagnostic; use rustc_middle::hir::nested_filter; @@ -43,13 +44,14 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::lint; use rustc_session::lint::builtin::{ - CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MISPLACED_DIAGNOSTIC_ATTRIBUTES, - UNUSED_ATTRIBUTES, + CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + MISPLACED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES, }; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; +use rustc_trait_selection::error_reporting::traits::on_unimplemented_format::errors::UnknownFormatParameterForOnUnimplementedAttr; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; use rustc_trait_selection::traits::ObligationCtxt; use tracing::debug; @@ -231,6 +233,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_rustc_must_implement_one_of(*attr_span, fn_names, hir_id,target) }, Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, + Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())}, Attribute::Parsed( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -361,9 +364,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { - [sym::diagnostic, sym::on_unimplemented, ..] => { - self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) - } [sym::diagnostic, sym::on_const, ..] => { self.check_diagnostic_on_const(attr.span(), hir_id, target, item) } @@ -614,7 +614,13 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition - fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) { + fn check_diagnostic_on_unimplemented( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + directive: Option<&OnUnimplementedDirective>, + ) { if !matches!(target, Target::Trait) { self.tcx.emit_node_span_lint( MISPLACED_DIAGNOSTIC_ATTRIBUTES, @@ -623,6 +629,38 @@ impl<'tcx> CheckAttrVisitor<'tcx> { DiagnosticOnUnimplementedOnlyForTraits, ); } + + if let Some(directive) = directive { + if let Node::Item(Item { + kind: ItemKind::Trait(_, _, _, trait_name, generics, _, _), + .. + }) = self.tcx.hir_node(hir_id) + { + directive.visit_params(&mut |argument_name, span| { + let has_generic = generics.params.iter().any(|p| { + if !matches!(p.kind, GenericParamKind::Lifetime { .. }) + && let ParamName::Plain(name) = p.name + && name.name == argument_name + { + true + } else { + false + } + }); + if !has_generic { + self.tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + hir_id, + span, + UnknownFormatParameterForOnUnimplementedAttr { + argument_name, + trait_name: *trait_name, + }, + ) + } + }) + } + } } /// Checks if `#[diagnostic::on_const]` is applied to a trait impl diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1915ff0380fda..4f71a90b3ff9d 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -967,6 +967,7 @@ symbols! { // it's clearer that it's intended as a dummy value, and more likely // to be detected if it accidentally does get used. empty: "", + empty_braces: "{}", emscripten_wasm_eh, enable, encode, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 3cb3224302b6c..a5f5c79a309a8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -14,6 +14,7 @@ use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions, inline_fluent, pluralize, struct_span_code_err, }; +use rustc_hir::attrs::diagnostic::{AppendConstMessage, OnUnimplementedNote}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem, Node}; @@ -37,14 +38,13 @@ use rustc_span::def_id::CrateNum; use rustc_span::{BytePos, DUMMY_SP, STDLIB_STABLE_CRATES, Span, Symbol, sym}; use tracing::{debug, instrument}; -use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; use super::suggestions::get_explanation_based_on_obligation; use super::{ ArgKind, CandidateSimilarity, FindExprBySpan, GetSafeTransmuteErrorAndReason, ImplCandidate, }; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::TyCategory; -use crate::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; +use crate::error_reporting::traits::on_unimplemented::{evaluate_directive, of_item_directive}; use crate::error_reporting::traits::report_dyn_incompatibility; use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, CoroClosureNotFn}; use crate::infer::{self, InferCtxt, InferCtxtExt as _}; @@ -355,7 +355,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(s) = label { // If it has a custom `#[rustc_on_unimplemented]` // error message, let's display it as the label! - err.span_label(span, s); + err.span_label(span, s.as_str().to_owned()); if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) // When the self type is a type param We don't need to "the trait // `std::marker::Sized` is not implemented for `T`" as we will point @@ -875,9 +875,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { diag.long_ty_path(), ); - if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, impl_did) - { - let note = command.evaluate( + if let Ok(Some(command)) = of_item_directive(self.tcx, impl_did) { + let note = evaluate_directive( + &command, self.tcx, predicate.skip_binder().trait_ref, &condition_options, 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 5eff7bba7c633..2282054049111 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 @@ -5,9 +5,15 @@ use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit}; use rustc_errors::codes::*; use rustc_errors::{ErrorGuaranteed, struct_span_code_err}; use rustc_hir as hir; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::attrs::diagnostic::{ + AppendConstMessage, ConditionOptions, FormatString, OnUnimplementedDirective, + OnUnimplementedNote, Piece, +}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{AttrArgs, Attribute}; +pub use rustc_hir::lints::FormatWarning; +use rustc_hir::{AttrArgs, Attribute, find_attr}; use rustc_macros::LintDiagnostic; use rustc_middle::bug; use rustc_middle::ty::print::PrintTraitRefExt; @@ -16,19 +22,17 @@ use rustc_session::lint::builtin::{ MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, }; use rustc_span::{Span, Symbol, sym}; +use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, info}; use super::{ObligationCauseCode, PredicateObligation}; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::traits::on_unimplemented_condition::{ - ConditionOptions, OnUnimplementedCondition, -}; -use crate::error_reporting::traits::on_unimplemented_format::{ - Ctx, FormatArgs, FormatString, FormatWarning, + matches_predicate, parse_condition, }; +use crate::error_reporting::traits::on_unimplemented_format::{Ctx, FormatArgs}; use crate::errors::{InvalidOnClause, NoValueInOnUnimplemented}; use crate::infer::InferCtxtExt; - impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn impl_similar_to( &self, @@ -106,9 +110,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let (condition_options, format_args) = self.on_unimplemented_components(trait_pred, obligation, long_ty_path); - if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, trait_pred.def_id()) - { - command.evaluate( + if let Ok(Some(command)) = of_item_directive(self.tcx, trait_pred.def_id()) { + evaluate_directive( + &command, self.tcx, trait_pred.skip_binder().trait_ref, &condition_options, @@ -318,47 +322,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } -/// Represents a format string in a on_unimplemented attribute, -/// like the "content" in `#[diagnostic::on_unimplemented(message = "content")]` -#[derive(Clone, Debug)] -pub struct OnUnimplementedFormatString { - /// Symbol of the format string, i.e. `"content"` - symbol: Symbol, - /// The span of the format string, i.e. `"content"` - span: Span, - is_diagnostic_namespace_variant: bool, -} - -#[derive(Debug)] -pub struct OnUnimplementedDirective { - condition: Option, - subcommands: Vec, - message: Option<(Span, OnUnimplementedFormatString)>, - label: Option<(Span, OnUnimplementedFormatString)>, - notes: Vec, - parent_label: Option, - append_const_msg: Option, -} - -/// For the `#[rustc_on_unimplemented]` attribute -#[derive(Default, Debug)] -pub struct OnUnimplementedNote { - pub message: Option, - pub label: Option, - pub notes: Vec, - pub parent_label: Option, - // If none, should fall back to a generic message - pub append_const_msg: Option, -} - -/// Append a message for `[const] Trait` errors. -#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] -pub enum AppendConstMessage { - #[default] - Default, - Custom(Symbol, Span), -} - #[derive(LintDiagnostic)] #[diag("malformed `on_unimplemented` attribute")] #[help("only `message`, `note` and `label` are allowed as options")] @@ -380,16 +343,16 @@ pub struct MissingOptionsForOnUnimplementedAttr; #[derive(LintDiagnostic)] #[diag("`{$option_name}` is ignored due to previous definition of `{$option_name}`")] -pub struct IgnoredDiagnosticOption { +pub(crate) struct IgnoredDiagnosticOption { pub option_name: &'static str, - #[label("`{$option_name}` is already declared here")] - pub span: Span, #[label("`{$option_name}` is first declared here")] - pub prev_span: Span, + pub first_span: Span, + #[label("`{$option_name}` is later redundantly declared here")] + pub later_span: Span, } impl IgnoredDiagnosticOption { - pub fn maybe_emit_warning<'tcx>( + fn maybe_emit_warning<'tcx>( tcx: TyCtxt<'tcx>, item_def_id: DefId, new: Option, @@ -403,7 +366,7 @@ impl IgnoredDiagnosticOption { MALFORMED_DIAGNOSTIC_ATTRIBUTES, tcx.local_def_id_to_hir_id(item_def_id), new_item, - IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name }, + IgnoredDiagnosticOption { later_span: new_item, first_span: old_item, option_name }, ); } } @@ -416,515 +379,467 @@ pub struct WrappedParserError { pub label: String, } -impl<'tcx> OnUnimplementedDirective { - fn parse( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - items: &[MetaItemInner], - span: Span, - is_root: bool, - is_diagnostic_namespace_variant: bool, - ) -> Result, ErrorGuaranteed> { - let mut errored = None; - let mut item_iter = items.iter(); - - let parse_value = |value_str, span| { - OnUnimplementedFormatString::try_parse( - tcx, - item_def_id, - value_str, - span, - is_diagnostic_namespace_variant, - ) +fn parse_directive<'tcx>( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + items: &[MetaItemInner], + span: Span, + is_root: bool, + is_diagnostic_namespace_variant: bool, +) -> Result, ErrorGuaranteed> { + let mut errored = None; + let mut item_iter = items.iter(); + + let parse_value = |value_str, span| { + try_parse_format_string(tcx, item_def_id, value_str, span, is_diagnostic_namespace_variant) .map(Some) - }; + }; - let condition = if is_root { - None + let condition = if is_root { + None + } else { + let cond = + item_iter.next().ok_or_else(|| tcx.dcx().emit_err(InvalidOnClause::Empty { span }))?; + + let generics: Vec = tcx + .generics_of(item_def_id) + .own_params + .iter() + .filter_map(|param| { + if matches!(param.kind, GenericParamDefKind::Lifetime) { + None + } else { + Some(param.name) + } + }) + .collect(); + match parse_condition(cond, &generics) { + Ok(condition) => Some(condition), + Err(e) => return Err(tcx.dcx().emit_err(e)), + } + }; + + let mut message = None; + let mut label = None; + let mut notes = ThinVec::new(); + let mut parent_label = None; + let mut subcommands = ThinVec::new(); + let mut append_const_msg = None; + + let get_value_and_span = |item: &_, key| { + if let MetaItemInner::MetaItem(MetaItem { + path, + kind: MetaItemKind::NameValue(MetaItemLit { span, kind: LitKind::Str(s, _), .. }), + .. + }) = item + && *path == key + { + Some((*s, *span)) } else { - let cond = item_iter - .next() - .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClause::Empty { span }))?; - - let generics: Vec = tcx - .generics_of(item_def_id) - .own_params - .iter() - .filter_map(|param| { - if matches!(param.kind, GenericParamDefKind::Lifetime) { - None - } else { - Some(param.name) - } - }) - .collect(); - match OnUnimplementedCondition::parse(cond, &generics) { - Ok(condition) => Some(condition), - Err(e) => return Err(tcx.dcx().emit_err(e)), - } - }; + None + } + }; - let mut message = None; - let mut label = None; - let mut notes = Vec::new(); - let mut parent_label = None; - let mut subcommands = vec![]; - let mut append_const_msg = None; - - let get_value_and_span = |item: &_, key| { - if let MetaItemInner::MetaItem(MetaItem { - path, - kind: MetaItemKind::NameValue(MetaItemLit { span, kind: LitKind::Str(s, _), .. }), - .. - }) = item - && *path == key - { - Some((*s, *span)) - } else { - None + for item in item_iter { + if let Some((message_, span)) = get_value_and_span(item, sym::message) + && message.is_none() + { + message = parse_value(message_, span)?.map(|l| (item.span(), l)); + continue; + } else if let Some((label_, span)) = get_value_and_span(item, sym::label) + && label.is_none() + { + label = parse_value(label_, span)?.map(|l| (item.span(), l)); + continue; + } else if let Some((note_, span)) = get_value_and_span(item, sym::note) { + if let Some(note) = parse_value(note_, span)? { + notes.push(note); + continue; } - }; - - for item in item_iter { - if let Some((message_, span)) = get_value_and_span(item, sym::message) - && message.is_none() - { - message = parse_value(message_, span)?.map(|l| (item.span(), l)); + } else if item.has_name(sym::parent_label) + && parent_label.is_none() + && !is_diagnostic_namespace_variant + { + if let Some(parent_label_) = item.value_str() { + parent_label = parse_value(parent_label_, item.span())?; continue; - } else if let Some((label_, span)) = get_value_and_span(item, sym::label) - && label.is_none() - { - label = parse_value(label_, span)?.map(|l| (item.span(), l)); + } + } else if item.has_name(sym::on) + && is_root + && message.is_none() + && label.is_none() + && notes.is_empty() + && !is_diagnostic_namespace_variant + // FIXME(diagnostic_namespace): disallow filters for now + { + if let Some(items) = item.meta_item_list() { + match parse_directive( + tcx, + item_def_id, + items, + item.span(), + false, + is_diagnostic_namespace_variant, + ) { + Ok(Some(subcommand)) => subcommands.push(subcommand), + Ok(None) => bug!( + "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false" + ), + Err(reported) => errored = Some(reported), + }; continue; - } else if let Some((note_, span)) = get_value_and_span(item, sym::note) { - if let Some(note) = parse_value(note_, span)? { - notes.push(note); - continue; - } - } else if item.has_name(sym::parent_label) - && parent_label.is_none() - && !is_diagnostic_namespace_variant - { - if let Some(parent_label_) = item.value_str() { - parent_label = parse_value(parent_label_, item.span())?; - continue; - } - } else if item.has_name(sym::on) - && is_root - && message.is_none() - && label.is_none() - && notes.is_empty() - && !is_diagnostic_namespace_variant - // FIXME(diagnostic_namespace): disallow filters for now - { - if let Some(items) = item.meta_item_list() { - match Self::parse( - tcx, - item_def_id, - items, - item.span(), - false, - is_diagnostic_namespace_variant, - ) { - Ok(Some(subcommand)) => subcommands.push(subcommand), - Ok(None) => bug!( - "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false" - ), - Err(reported) => errored = Some(reported), - }; - continue; - } - } else if item.has_name(sym::append_const_msg) - && append_const_msg.is_none() - && !is_diagnostic_namespace_variant - { - if let Some(msg) = item.value_str() { - append_const_msg = Some(AppendConstMessage::Custom(msg, item.span())); - continue; - } else if item.is_word() { - append_const_msg = Some(AppendConstMessage::Default); - continue; - } } - - if is_diagnostic_namespace_variant { - if let Some(def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(def_id), - vec![item.span()], - MalformedOnUnimplementedAttrLint::new(item.span()), - ); - } - } else { - // nothing found - tcx.dcx().emit_err(NoValueInOnUnimplemented { span: item.span() }); + } else if item.has_name(sym::append_const_msg) + && append_const_msg.is_none() + && !is_diagnostic_namespace_variant + { + if let Some(msg) = item.value_str() { + append_const_msg = Some(AppendConstMessage::Custom(msg, item.span())); + continue; + } else if item.is_word() { + append_const_msg = Some(AppendConstMessage::Default); + continue; } } - if let Some(reported) = errored { - if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) } + if is_diagnostic_namespace_variant { + if let Some(def_id) = item_def_id.as_local() { + tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(def_id), + vec![item.span()], + MalformedOnUnimplementedAttrLint::new(item.span()), + ); + } } else { - Ok(Some(OnUnimplementedDirective { - condition, - subcommands, - message, - label, - notes, - parent_label, - append_const_msg, - })) + // nothing found + tcx.dcx().emit_err(NoValueInOnUnimplemented { span: item.span() }); } } - pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result, ErrorGuaranteed> { - let attr = if tcx.is_trait(item_def_id) { - sym::on_unimplemented - } else if let DefKind::Impl { of_trait: true } = tcx.def_kind(item_def_id) { - sym::on_const - } else { - // It could be a trait_alias (`trait MyTrait = SomeOtherTrait`) - // or an implementation (`impl MyTrait for Foo {}`) - // - // We don't support those. - return Ok(None); - }; - if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) { - return Self::parse_attribute(attr, false, tcx, item_def_id); - } else { - tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, attr]) - .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose()) - .try_fold(None, |aggr: Option, directive| { - let directive = directive?; - if let Some(aggr) = aggr { - let mut subcommands = aggr.subcommands; - subcommands.extend(directive.subcommands); - let mut notes = aggr.notes; - notes.extend(directive.notes); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.message.as_ref().map(|f| f.0), - aggr.message.as_ref().map(|f| f.0), - "message", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.label.as_ref().map(|f| f.0), - aggr.label.as_ref().map(|f| f.0), - "label", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.condition.as_ref().map(|i| i.span()), - aggr.condition.as_ref().map(|i| i.span()), - "condition", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.parent_label.as_ref().map(|f| f.span), - aggr.parent_label.as_ref().map(|f| f.span), - "parent_label", - ); - IgnoredDiagnosticOption::maybe_emit_warning( - tcx, - item_def_id, - directive.append_const_msg.as_ref().and_then(|c| { - if let AppendConstMessage::Custom(_, s) = c { - Some(*s) - } else { - None - } - }), - aggr.append_const_msg.as_ref().and_then(|c| { - if let AppendConstMessage::Custom(_, s) = c { - Some(*s) - } else { - None - } - }), - "append_const_msg", - ); - - Ok(Some(Self { - condition: aggr.condition.or(directive.condition), - subcommands, - message: aggr.message.or(directive.message), - label: aggr.label.or(directive.label), - notes, - 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)) - } - }) - } + if let Some(reported) = errored { + if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) } + } else { + Ok(Some(OnUnimplementedDirective { + condition, + subcommands, + message, + label, + notes, + parent_label, + append_const_msg, + })) } +} - fn parse_attribute( - attr: &Attribute, - is_diagnostic_namespace_variant: bool, - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - ) -> Result, ErrorGuaranteed> { - let result = if let Some(items) = attr.meta_item_list() { - Self::parse( - tcx, - item_def_id, - &items, - attr.span(), - true, - is_diagnostic_namespace_variant, - ) - } else if let Some(value) = attr.value_str() { - if !is_diagnostic_namespace_variant { - Ok(Some(OnUnimplementedDirective { - condition: None, - message: None, - subcommands: vec![], - label: Some(( - attr.span(), - OnUnimplementedFormatString::try_parse( - tcx, - item_def_id, - value, - attr.value_span().unwrap_or(attr.span()), - is_diagnostic_namespace_variant, - )?, - )), - notes: Vec::new(), - parent_label: None, - append_const_msg: None, - })) - } else { - let item = attr.get_normal_item(); - let report_span = match &item.args { - AttrArgs::Empty => item.path.span, - AttrArgs::Delimited(args) => args.dspan.entire(), - AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span), - }; +pub fn of_item_directive<'tcx>( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, +) -> Result, ErrorGuaranteed> { + let attr = if tcx.is_trait(item_def_id) { + sym::on_unimplemented + } else if let DefKind::Impl { of_trait: true } = tcx.def_kind(item_def_id) { + sym::on_const + } else { + // It could be a trait_alias (`trait MyTrait = SomeOtherTrait`) + // or an implementation (`impl MyTrait for Foo {}`) + // + // We don't support those. + return Ok(None); + }; + if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) { + return parse_attribute_directive(attr, false, tcx, item_def_id); + } else if attr == sym::on_unimplemented { + Ok(find_attr!(tcx.get_all_attrs(item_def_id), AttributeKind::OnUnimplemented {directive, ..} => directive.as_deref().cloned()).flatten()) + } else { + tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, attr]) + .filter_map(|attr| parse_attribute_directive(attr, true, tcx, item_def_id).transpose()) + .try_fold(None, |aggr: Option, directive| { + let directive = directive?; + if let Some(aggr) = aggr { + let mut subcommands = aggr.subcommands; + subcommands.extend(directive.subcommands); + let mut notes = aggr.notes; + notes.extend(directive.notes); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.message.as_ref().map(|f| f.0), + aggr.message.as_ref().map(|f| f.0), + "message", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.label.as_ref().map(|f| f.0), + aggr.label.as_ref().map(|f| f.0), + "label", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.condition.as_ref().map(|i| i.span), + aggr.condition.as_ref().map(|i| i.span), + "condition", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.parent_label.as_ref().map(|f| f.span), + aggr.parent_label.as_ref().map(|f| f.span), + "parent_label", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.append_const_msg.as_ref().and_then(|c| { + if let AppendConstMessage::Custom(_, s) = c { Some(*s) } else { None } + }), + aggr.append_const_msg.as_ref().and_then(|c| { + if let AppendConstMessage::Custom(_, s) = c { Some(*s) } else { None } + }), + "append_const_msg", + ); + Ok(Some(OnUnimplementedDirective { + condition: aggr.condition.or(directive.condition), + subcommands, + message: aggr.message.or(directive.message), + label: aggr.label.or(directive.label), + notes, + 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)) + } + }) + } +} + +fn parse_attribute_directive<'tcx>( + attr: &Attribute, + is_diagnostic_namespace_variant: bool, + tcx: TyCtxt<'tcx>, + item_def_id: DefId, +) -> Result, ErrorGuaranteed> { + let result = if let Some(items) = attr.meta_item_list() { + parse_directive( + tcx, + item_def_id, + &items, + attr.span(), + true, + is_diagnostic_namespace_variant, + ) + } else if let Some(value) = attr.value_str() { + if !is_diagnostic_namespace_variant { + Ok(Some(OnUnimplementedDirective { + condition: None, + message: None, + subcommands: thin_vec![], + label: Some(( + attr.span(), + try_parse_format_string( + tcx, + item_def_id, + value, + attr.value_span().unwrap_or(attr.span()), + is_diagnostic_namespace_variant, + )?, + )), + notes: thin_vec![], + parent_label: None, + append_const_msg: None, + })) + } else { + let item = attr.get_normal_item(); + let report_span = match &item.args { + AttrArgs::Empty => item.path.span, + AttrArgs::Delimited(args) => args.dspan.entire(), + AttrArgs::Eq { eq_span, expr } => eq_span.to(expr.span), + }; + + if let Some(item_def_id) = item_def_id.as_local() { + tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id), + report_span, + MalformedOnUnimplementedAttrLint::new(report_span), + ); + } + Ok(None) + } + } else if is_diagnostic_namespace_variant { + match attr { + Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => { if let Some(item_def_id) = item_def_id.as_local() { tcx.emit_node_span_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, tcx.local_def_id_to_hir_id(item_def_id), - report_span, - MalformedOnUnimplementedAttrLint::new(report_span), + attr.span(), + MalformedOnUnimplementedAttrLint::new(attr.span()), ); } - Ok(None) } - } else if is_diagnostic_namespace_variant { - match attr { - Attribute::Unparsed(p) if !matches!(p.args, AttrArgs::Empty) => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - attr.span(), - MalformedOnUnimplementedAttrLint::new(attr.span()), - ); - } - } - _ => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - attr.span(), - MissingOptionsForOnUnimplementedAttr, - ) - } + _ => { + if let Some(item_def_id) = item_def_id.as_local() { + tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id), + attr.span(), + MissingOptionsForOnUnimplementedAttr, + ) } - }; - - Ok(None) - } else { - let reported = tcx.dcx().delayed_bug("of_item: neither meta_item_list nor value_str"); - return Err(reported); + } }; - debug!("of_item({:?}) = {:?}", item_def_id, result); - result - } - pub(crate) fn evaluate( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - condition_options: &ConditionOptions, - args: &FormatArgs<'tcx>, - ) -> OnUnimplementedNote { - let mut message = None; - let mut label = None; - let mut notes = Vec::new(); - let mut parent_label = None; - let mut append_const_msg = None; - info!( - "evaluate({:?}, trait_ref={:?}, options={:?}, args ={:?})", - self, trait_ref, condition_options, args - ); - - 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!("evaluate: skipping {:?} due to condition", command); - continue; - } - debug!("evaluate: {:?} succeeded", command); - if let Some(ref message_) = command.message { - message = Some(message_.clone()); - } + Ok(None) + } else { + let reported = + tcx.dcx().delayed_bug("of_item_directive: neither meta_item_list nor value_str"); + return Err(reported); + }; + debug!("of_item_directive({:?}) = {:?}", item_def_id, result); + result +} - if let Some(ref label_) = command.label { - label = Some(label_.clone()); - } +pub(crate) fn evaluate_directive<'tcx>( + slf: &OnUnimplementedDirective, + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + condition_options: &ConditionOptions, + args: &FormatArgs<'tcx>, +) -> OnUnimplementedNote { + let mut message = None; + let mut label = None; + let mut notes = Vec::new(); + let mut parent_label = None; + let mut append_const_msg = None; + info!( + "evaluate_directive({:?}, trait_ref={:?}, options={:?}, args ={:?})", + slf, trait_ref, condition_options, args + ); + + for command in slf.subcommands.iter().chain(Some(slf)).rev() { + debug!(?command); + if let Some(ref condition) = command.condition + && !matches_predicate(condition, condition_options) + { + debug!("evaluate_directive: skipping {:?} due to condition", command); + continue; + } + debug!("evaluate_directive: {:?} succeeded", command); + if let Some(ref message_) = command.message { + message = Some(message_.clone()); + } - notes.extend(command.notes.clone()); + if let Some(ref label_) = command.label { + label = Some(label_.clone()); + } - if let Some(ref parent_label_) = command.parent_label { - parent_label = Some(parent_label_.clone()); - } + notes.extend(command.notes.clone()); - append_const_msg = command.append_const_msg; + if let Some(ref parent_label_) = command.parent_label { + parent_label = Some(parent_label_.clone()); } - OnUnimplementedNote { - label: label.map(|l| l.1.format(tcx, trait_ref, args)), - message: message.map(|m| m.1.format(tcx, trait_ref, args)), - notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, args)).collect(), - parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, args)), - append_const_msg, - } + append_const_msg = command.append_const_msg; } -} -impl<'tcx> OnUnimplementedFormatString { - fn try_parse( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - from: Symbol, - span: Span, - is_diagnostic_namespace_variant: bool, - ) -> Result { - let result = - OnUnimplementedFormatString { symbol: from, span, is_diagnostic_namespace_variant }; - result.verify(tcx, item_def_id)?; - Ok(result) + OnUnimplementedNote { + label: label.map(|l| format_directive(l.1, tcx, trait_ref, args)), + message: message.map(|m| format_directive(m.1, tcx, trait_ref, args)), + notes: notes.into_iter().map(|n| format_directive(n, tcx, trait_ref, args)).collect(), + parent_label: parent_label.map(|e_s| format_directive(e_s, tcx, trait_ref, args)), + append_const_msg, } +} - fn verify(&self, tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> Result<(), ErrorGuaranteed> { - if !tcx.is_trait(trait_def_id) { - return Ok(()); - }; - - let ctx = if self.is_diagnostic_namespace_variant { - Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id } - } else { - Ctx::RustcOnUnimplemented { tcx, trait_def_id } - }; - - let mut result = Ok(()); +fn try_parse_format_string<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + input: Symbol, + span: Span, + is_diagnostic_namespace_variant: bool, +) -> Result { + let ctx = if is_diagnostic_namespace_variant { + Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id } + } else { + Ctx::RustcOnUnimplemented { tcx, trait_def_id } + }; + + let mut result = Ok(()); + + use crate::error_reporting::traits::on_unimplemented_format::parse_format_string; + + let snippet = tcx.sess.source_map().span_to_snippet(span).ok(); + let ret = match parse_format_string(input, snippet, span, &ctx) { + // Warnings about format specifiers, deprecated parameters, wrong parameters etc. + // In other words we'd like to let the author know, but we can still try to format the string later + Ok((f, warnings)) => { + if is_diagnostic_namespace_variant { + for w in warnings { + use crate::error_reporting::traits::on_unimplemented_format::emit_warning; - let snippet = tcx.sess.source_map().span_to_snippet(self.span).ok(); - match FormatString::parse(self.symbol, snippet, self.span, &ctx) { - // Warnings about format specifiers, deprecated parameters, wrong parameters etc. - // In other words we'd like to let the author know, but we can still try to format the string later - Ok(FormatString { warnings, .. }) => { - if self.is_diagnostic_namespace_variant { - for w in warnings { - w.emit_warning(tcx, trait_def_id) - } - } else { - for w in warnings { - match w { - FormatWarning::UnknownParam { argument_name, span } => { - let reported = struct_span_code_err!( - tcx.dcx(), - span, - E0230, - "cannot find parameter {} on this trait", - argument_name, - ) - .emit(); - result = Err(reported); - } - FormatWarning::PositionalArgument { span, .. } => { - let reported = struct_span_code_err!( - tcx.dcx(), - span, - E0231, - "positional format arguments are not allowed here" - ) - .emit(); - result = Err(reported); - } - FormatWarning::InvalidSpecifier { .. } - | FormatWarning::FutureIncompat { .. } => {} - } - } + emit_warning(&w, tcx, trait_def_id) } - } - // Error from the underlying `rustc_parse_format::Parser` - Err(e) => { - // we cannot return errors from processing the format string as hard error here - // as the diagnostic namespace guarantees that malformed input cannot cause an error - // - // if we encounter any error while processing we nevertheless want to show it as warning - // so that users are aware that something is not correct - if self.is_diagnostic_namespace_variant { - if let Some(trait_def_id) = trait_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(trait_def_id), - self.span, - WrappedParserError { description: e.description, label: e.label }, - ); - } - } else { - let reported = - struct_span_code_err!(tcx.dcx(), self.span, E0231, "{}", e.description,) + } else { + for w in warnings { + match w { + FormatWarning::PositionalArgument { span, .. } => { + let reported = struct_span_code_err!( + tcx.dcx(), + span, + E0231, + "positional format arguments are not allowed here" + ) .emit(); - result = Err(reported); + result = Err(reported); + } + FormatWarning::InvalidSpecifier { .. } => {} + } } - } + }; + f } - - result - } - - pub fn format( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - args: &FormatArgs<'tcx>, - ) -> String { - let trait_def_id = trait_ref.def_id; - let ctx = if self.is_diagnostic_namespace_variant { - Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id } - } else { - Ctx::RustcOnUnimplemented { tcx, trait_def_id } - }; - - // No point passing a snippet here, we already did that in `verify` - if let Ok(s) = FormatString::parse(self.symbol, None, self.span, &ctx) { - s.format(args) - } else { + // Error from the underlying `rustc_parse_format::Parser` + Err(e) => { // we cannot return errors from processing the format string as hard error here // as the diagnostic namespace guarantees that malformed input cannot cause an error // - // if we encounter any error while processing the format string - // we don't want to show the potentially half assembled formatted string, - // therefore we fall back to just showing the input string in this case - // - // The actual parser errors are emitted earlier - // as lint warnings in OnUnimplementedFormatString::verify - self.symbol.as_str().into() + // if we encounter any error while processing we nevertheless want to show it as warning + // so that users are aware that something is not correct + if is_diagnostic_namespace_variant { + if let Some(trait_def_id) = trait_def_id.as_local() { + tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + tcx.local_def_id_to_hir_id(trait_def_id), + span, + WrappedParserError { description: e.description, label: e.label }, + ); + } + } else { + let reported = + struct_span_code_err!(tcx.dcx(), span, E0231, "{}", e.description,).emit(); + result = Err(reported); + } + + // We could not parse the input, just use it as-is. + FormatString { input, span, pieces: thin_vec![Piece::Lit(input)] } } - } + }; + + result?; + Ok(ret) +} + +pub fn format_directive<'tcx>( + slf: FormatString, + _tcx: TyCtxt<'tcx>, + _trait_ref: ty::TraitRef<'tcx>, + args: &FormatArgs<'tcx>, +) -> String { + use crate::error_reporting::traits::on_unimplemented_format::format; + format(&slf, args) } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs index 171d05230d468..b18642c785d94 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_condition.rs @@ -1,322 +1,148 @@ use rustc_ast::{MetaItemInner, MetaItemKind, MetaItemLit}; +use rustc_hir::attrs::diagnostic::*; use rustc_parse_format::{ParseMode, Parser, Piece, Position}; -use rustc_span::{DesugaringKind, Ident, Span, Symbol, kw, sym}; +use rustc_span::{Ident, Symbol, kw, sym}; +use thin_vec::ThinVec; use crate::errors::InvalidOnClause; -/// Represents the `on` filter in `#[rustc_on_unimplemented]`. -#[derive(Debug)] -pub(crate) struct OnUnimplementedCondition { - span: Span, - pred: Predicate, -} - -impl OnUnimplementedCondition { - pub(crate) fn span(&self) -> Span { - self.span - } - - pub(crate) fn matches_predicate(&self, options: &ConditionOptions) -> bool { - self.pred.eval(&mut |p| match p { - FlagOrNv::Flag(b) => options.has_flag(*b), - FlagOrNv::NameValue(NameValue { name, value }) => { - let value = value.format(&options.generic_args); - options.contains(*name, value) - } - }) - } - - pub(crate) fn parse( - input: &MetaItemInner, - generics: &[Symbol], - ) -> Result { - let span = input.span(); - let pred = Predicate::parse(input, generics)?; - Ok(OnUnimplementedCondition { span, pred }) - } +pub fn matches_predicate(slf: &OnUnimplementedCondition, options: &ConditionOptions) -> bool { + slf.pred.eval(&mut |p| match p { + FlagOrNv::Flag(b) => options.has_flag(*b), + FlagOrNv::NameValue(NameValue { name, value }) => { + let value = format_filter(value, &options.generic_args); + options.contains(*name, value) + } + }) } -/// Predicate(s) in `#[rustc_on_unimplemented]`'s `on` filter. See [`OnUnimplementedCondition`]. -/// -/// It is similar to the predicate in the `cfg` attribute, -/// and may contain nested predicates. -#[derive(Debug)] -enum Predicate { - /// A condition like `on(crate_local)`. - Flag(Flag), - /// A match, like `on(Rhs = "Whatever")`. - Match(NameValue), - /// Negation, like `on(not($pred))`. - Not(Box), - /// True if all predicates are true, like `on(all($a, $b, $c))`. - All(Vec), - /// True if any predicate is true, like `on(any($a, $b, $c))`. - Any(Vec), +pub(crate) fn parse_condition( + input: &MetaItemInner, + generics: &[Symbol], +) -> Result { + let span = input.span(); + let pred = parse_predicate(input, generics)?; + Ok(OnUnimplementedCondition { span, pred }) } -impl Predicate { - fn parse(input: &MetaItemInner, generics: &[Symbol]) -> Result { - let meta_item = match input { - MetaItemInner::MetaItem(meta_item) => meta_item, - MetaItemInner::Lit(lit) => { - return Err(InvalidOnClause::UnsupportedLiteral { span: lit.span }); - } - }; - - let Some(predicate) = meta_item.ident() else { - return Err(InvalidOnClause::ExpectedIdentifier { - span: meta_item.path.span, - path: meta_item.path.clone(), - }); - }; - - match meta_item.kind { - MetaItemKind::List(ref mis) => match predicate.name { - sym::any => Ok(Predicate::Any(Predicate::parse_sequence(mis, generics)?)), - sym::all => Ok(Predicate::All(Predicate::parse_sequence(mis, generics)?)), - sym::not => match &**mis { - [one] => Ok(Predicate::Not(Box::new(Predicate::parse(one, generics)?))), - [first, .., last] => Err(InvalidOnClause::ExpectedOnePredInNot { - span: first.span().to(last.span()), - }), - [] => Err(InvalidOnClause::ExpectedOnePredInNot { span: meta_item.span }), - }, - invalid_pred => { - Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred }) - } +fn parse_predicate( + input: &MetaItemInner, + generics: &[Symbol], +) -> Result { + let meta_item = match input { + MetaItemInner::MetaItem(meta_item) => meta_item, + MetaItemInner::Lit(lit) => { + return Err(InvalidOnClause::UnsupportedLiteral { span: lit.span }); + } + }; + + let Some(predicate) = meta_item.ident() else { + return Err(InvalidOnClause::ExpectedIdentifier { + span: meta_item.path.span, + path: meta_item.path.clone(), + }); + }; + + match meta_item.kind { + MetaItemKind::List(ref mis) => match predicate.name { + sym::any => Ok(Predicate::Any(parse_predicate_sequence(mis, generics)?)), + sym::all => Ok(Predicate::All(parse_predicate_sequence(mis, generics)?)), + sym::not => match &**mis { + [one] => Ok(Predicate::Not(Box::new(parse_predicate(one, generics)?))), + [first, .., last] => Err(InvalidOnClause::ExpectedOnePredInNot { + span: first.span().to(last.span()), + }), + [] => Err(InvalidOnClause::ExpectedOnePredInNot { span: meta_item.span }), }, - MetaItemKind::NameValue(MetaItemLit { symbol, .. }) => { - let name = Name::parse(predicate, generics)?; - let value = FilterFormatString::parse(symbol); - let kv = NameValue { name, value }; - Ok(Predicate::Match(kv)) - } - MetaItemKind::Word => { - let flag = Flag::parse(predicate)?; - Ok(Predicate::Flag(flag)) + invalid_pred => { + Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred }) } + }, + MetaItemKind::NameValue(MetaItemLit { symbol, .. }) => { + let name = parse_name(predicate, generics)?; + let value = parse_filter(symbol); + let kv = NameValue { name, value }; + Ok(Predicate::Match(kv)) } - } - - fn parse_sequence( - sequence: &[MetaItemInner], - generics: &[Symbol], - ) -> Result, InvalidOnClause> { - sequence.iter().map(|item| Predicate::parse(item, generics)).collect() - } - - fn eval(&self, eval: &mut impl FnMut(FlagOrNv<'_>) -> bool) -> bool { - match self { - Predicate::Flag(flag) => eval(FlagOrNv::Flag(flag)), - Predicate::Match(nv) => eval(FlagOrNv::NameValue(nv)), - Predicate::Not(not) => !not.eval(eval), - Predicate::All(preds) => preds.into_iter().all(|pred| pred.eval(eval)), - Predicate::Any(preds) => preds.into_iter().any(|pred| pred.eval(eval)), + MetaItemKind::Word => { + let flag = parse_flag(predicate)?; + Ok(Predicate::Flag(flag)) } } } -/// Represents a `MetaWord` in an `on`-filter. -#[derive(Debug, Clone, Copy)] -enum Flag { - /// Whether the code causing the trait bound to not be fulfilled - /// is part of the user's crate. - CrateLocal, - /// Whether the obligation is user-specified rather than derived. - Direct, - /// Whether we are in some kind of desugaring like - /// `?` or `try { .. }`. - FromDesugaring, +fn parse_predicate_sequence( + sequence: &[MetaItemInner], + generics: &[Symbol], +) -> Result, InvalidOnClause> { + sequence.iter().map(|item| parse_predicate(item, generics)).collect() } -impl Flag { - fn parse(Ident { name, span }: Ident) -> Result { - match name { - sym::crate_local => Ok(Flag::CrateLocal), - sym::direct => Ok(Flag::Direct), - sym::from_desugaring => Ok(Flag::FromDesugaring), - invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }), - } +fn parse_flag(Ident { name, span }: Ident) -> Result { + match name { + sym::crate_local => Ok(Flag::CrateLocal), + sym::direct => Ok(Flag::Direct), + sym::from_desugaring => Ok(Flag::FromDesugaring), + invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }), } } -/// A `MetaNameValueStr` in an `on`-filter. -/// -/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. -#[derive(Debug, Clone)] -struct NameValue { - name: Name, - /// Something like `"&str"` or `"alloc::string::String"`, - /// in which case it just contains a single string piece. - /// But if it is something like `"&[{A}]"` then it must be formatted later. - value: FilterFormatString, -} - -/// The valid names of the `on` filter. -#[derive(Debug, Clone, Copy)] -enum Name { - Cause, - FromDesugaring, - SelfUpper, - GenericArg(Symbol), -} - -impl Name { - fn parse(Ident { name, span }: Ident, generics: &[Symbol]) -> Result { - match name { - kw::SelfUpper => Ok(Name::SelfUpper), - sym::from_desugaring => Ok(Name::FromDesugaring), - sym::cause => Ok(Name::Cause), - generic if generics.contains(&generic) => Ok(Name::GenericArg(generic)), - invalid_name => Err(InvalidOnClause::InvalidName { invalid_name, span }), - } +fn parse_name(Ident { name, span }: Ident, generics: &[Symbol]) -> Result { + match name { + kw::SelfUpper => Ok(Name::SelfUpper), + sym::from_desugaring => Ok(Name::FromDesugaring), + sym::cause => Ok(Name::Cause), + generic if generics.contains(&generic) => Ok(Name::GenericArg(generic)), + invalid_name => Err(InvalidOnClause::InvalidName { invalid_name, span }), } } -#[derive(Debug, Clone)] -enum FlagOrNv<'p> { - Flag(&'p Flag), - NameValue(&'p NameValue), -} +fn parse_filter(input: Symbol) -> FilterFormatString { + let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic) + .map(|p| match p { + Piece::Lit(s) => LitOrArg::Lit(Symbol::intern(s)), + // We just ignore formatspecs here + Piece::NextArgument(a) => match a.position { + // In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even + // if the integer type has been resolved, to allow targeting all integers. + // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet, + // from the `Display` impl of `InferTy` to be precise. + // + // Don't try to format these later! + Position::ArgumentNamed(arg @ "integer" | arg @ "integral" | arg @ "float") => { + LitOrArg::Lit(Symbol::intern(&format!("{{{arg}}}"))) + } -/// Represents a value inside an `on` filter. -/// -/// For example, `#[rustc_on_unimplemented(on(name = "value", message = "hello"))]`. -/// If it is a simple literal like this then `pieces` will be `[LitOrArg::Lit("value")]`. -/// The `Arg` variant is used when it contains formatting like -/// `#[rustc_on_unimplemented(on(Self = "&[{A}]", message = "hello"))]`. -#[derive(Debug, Clone)] -struct FilterFormatString { - pieces: Vec, -} + // FIXME(mejrs) We should check if these correspond to a generic of the trait. + Position::ArgumentNamed(arg) => LitOrArg::Arg(Symbol::intern(arg)), -#[derive(Debug, Clone)] -enum LitOrArg { - Lit(String), - Arg(String), + // FIXME(mejrs) These should really be warnings/errors + Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(sym::empty_braces), + Position::ArgumentIs(idx) => LitOrArg::Lit(Symbol::intern(&format!("{{{idx}}}"))), + }, + }) + .collect(); + FilterFormatString { pieces } } -impl FilterFormatString { - fn parse(input: Symbol) -> Self { - let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic) - .map(|p| match p { - Piece::Lit(s) => LitOrArg::Lit(s.to_owned()), - // We just ignore formatspecs here - Piece::NextArgument(a) => match a.position { - // In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even - // if the integer type has been resolved, to allow targeting all integers. - // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet, - // from the `Display` impl of `InferTy` to be precise. - // - // Don't try to format these later! - Position::ArgumentNamed(arg @ "integer" | arg @ "integral" | arg @ "float") => { - LitOrArg::Lit(format!("{{{arg}}}")) - } - - // FIXME(mejrs) We should check if these correspond to a generic of the trait. - Position::ArgumentNamed(arg) => LitOrArg::Arg(arg.to_owned()), - - // FIXME(mejrs) These should really be warnings/errors - Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(String::from("{}")), - Position::ArgumentIs(idx) => LitOrArg::Lit(format!("{{{idx}}}")), - }, - }) - .collect(); - Self { pieces } - } - - fn format(&self, generic_args: &[(Symbol, String)]) -> String { - let mut ret = String::new(); - - for piece in &self.pieces { - match piece { - LitOrArg::Lit(s) => ret.push_str(s), - LitOrArg::Arg(arg) => { - let s = Symbol::intern(arg); - match generic_args.iter().find(|(k, _)| *k == s) { - Some((_, val)) => ret.push_str(val), - None => { - // FIXME(mejrs) If we start checking as mentioned in - // FilterFormatString::parse then this shouldn't happen - let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}")); - } +fn format_filter(slf: &FilterFormatString, generic_args: &[(Symbol, String)]) -> String { + let mut ret = String::new(); + + for piece in &slf.pieces { + match piece { + LitOrArg::Lit(s) => ret.push_str(s.as_str()), + LitOrArg::Arg(s) => { + match generic_args.iter().find(|(k, _)| k == s) { + Some((_, val)) => ret.push_str(val), + None => { + // FIXME(mejrs) If we start checking as mentioned in + // FilterFormatString::parse then this shouldn't happen + let _ = std::fmt::write(&mut ret, format_args!("{{{s}}}")); } } } } - - ret } -} -/// Used with `OnUnimplementedCondition::matches_predicate` to evaluate the -/// [`OnUnimplementedCondition`]. -/// -/// For example, given a -/// ```rust,ignore (just an example) -/// #[rustc_on_unimplemented( -/// on(all(from_desugaring = "QuestionMark"), -/// message = "the `?` operator can only be used in {ItemContext} \ -/// that returns `Result` or `Option` \ -/// (or another type that implements `{FromResidual}`)", -/// label = "cannot use the `?` operator in {ItemContext} that returns `{Self}`", -/// parent_label = "this function should return `Result` or `Option` to accept `?`" -/// ), -/// )] -/// pub trait FromResidual::Residual> { -/// ... -/// } -/// -/// async fn an_async_function() -> u32 { -/// let x: Option = None; -/// x?; //~ ERROR the `?` operator -/// 22 -/// } -/// ``` -/// it will look like this: -/// -/// ```rust,ignore (just an example) -/// ConditionOptions { -/// self_types: ["u32", "{integral}"], -/// from_desugaring: Some("QuestionMark"), -/// cause: None, -/// crate_local: false, -/// direct: true, -/// generic_args: [("Self","u32"), -/// ("R", "core::option::Option"), -/// ("R", "core::option::Option" ), -/// ], -/// } -/// ``` -#[derive(Debug)] -pub(crate) struct ConditionOptions { - /// All the self types that may apply. - pub(crate) self_types: Vec, - // The kind of compiler desugaring. - pub(crate) from_desugaring: Option, - /// Match on a variant of [rustc_infer::traits::ObligationCauseCode]. - pub(crate) cause: Option, - pub(crate) crate_local: bool, - /// Is the obligation "directly" user-specified, rather than derived? - pub(crate) direct: bool, - // A list of the generic arguments and their reified types. - pub(crate) generic_args: Vec<(Symbol, String)>, -} - -impl ConditionOptions { - fn has_flag(&self, name: Flag) -> bool { - match name { - Flag::CrateLocal => self.crate_local, - Flag::Direct => self.direct, - Flag::FromDesugaring => self.from_desugaring.is_some(), - } - } - fn contains(&self, name: Name, value: String) -> bool { - match name { - Name::SelfUpper => self.self_types.contains(&value), - Name::FromDesugaring => self.from_desugaring.is_some_and(|ds| ds.matches(&value)), - Name::Cause => self.cause == Some(value), - Name::GenericArg(arg) => self.generic_args.contains(&(arg, value)), - } - } + ret } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs index 8488d76e2c773..30a846d7dd653 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented_format.rs @@ -1,9 +1,10 @@ use std::fmt; use std::ops::Range; -use errors::*; +use rustc_hir::attrs::diagnostic::*; +use rustc_hir::lints::FormatWarning; +use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::TraitRefPrintSugared; -use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position, }; @@ -11,42 +12,7 @@ use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_FORMAT_LITERALS; use rustc_span::def_id::DefId; use rustc_span::{InnerSpan, Span, Symbol, kw, sym}; -/// Like [std::fmt::Arguments] this is a string that has been parsed into "pieces", -/// either as string pieces or dynamic arguments. -#[derive(Debug)] -pub struct FormatString { - #[allow(dead_code, reason = "Debug impl")] - input: Symbol, - span: Span, - pieces: Vec, - /// The formatting string was parsed successfully but with warnings - pub warnings: Vec, -} - -#[derive(Debug)] -enum Piece { - Lit(String), - Arg(FormatArg), -} - -#[derive(Debug)] -enum FormatArg { - // A generic parameter, like `{T}` if we're on the `From` trait. - GenericParam { - generic_param: Symbol, - }, - // `{Self}` - SelfUpper, - /// `{This}` or `{TraitName}` - This, - /// The sugared form of the trait - Trait, - /// what we're in, like a function, method, closure etc. - ItemContext, - /// What the user typed, if it doesn't match anything we can use. - AsIs(String), -} - +use crate::error_reporting::traits::on_unimplemented_format::errors::*; pub enum Ctx<'tcx> { // `#[rustc_on_unimplemented]` RustcOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId }, @@ -54,57 +20,26 @@ pub enum Ctx<'tcx> { DiagnosticOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId }, } -#[derive(Debug)] -pub enum FormatWarning { - UnknownParam { argument_name: Symbol, span: Span }, - PositionalArgument { span: Span, help: String }, - InvalidSpecifier { name: String, span: Span }, - FutureIncompat { span: Span, help: String }, -} - -impl FormatWarning { - pub fn emit_warning<'tcx>(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) { - match *self { - FormatWarning::UnknownParam { argument_name, span } => { - let this = tcx.item_ident(item_def_id); - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(item_def_id), - span, - UnknownFormatParameterForOnUnimplementedAttr { - argument_name, - trait_name: this, - }, - ); - } +pub fn emit_warning<'tcx>(slf: &FormatWarning, tcx: TyCtxt<'tcx>, item_def_id: DefId) { + match *slf { + FormatWarning::PositionalArgument { span, .. } => { + if let Some(item_def_id) = item_def_id.as_local() { + tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + tcx.local_def_id_to_hir_id(item_def_id), + span, + DisallowedPositionalArgument, + ); } - FormatWarning::PositionalArgument { span, .. } => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(item_def_id), - span, - DisallowedPositionalArgument, - ); - } - } - FormatWarning::InvalidSpecifier { span, .. } => { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, - tcx.local_def_id_to_hir_id(item_def_id), - span, - InvalidFormatSpecifier, - ); - } - } - FormatWarning::FutureIncompat { .. } => { - // We've never deprecated anything in diagnostic namespace format strings - // but if we do we will emit a warning here - - // FIXME(mejrs) in a couple releases, start emitting warnings for - // #[rustc_on_unimplemented] deprecated args + } + FormatWarning::InvalidSpecifier { span, .. } => { + if let Some(item_def_id) = item_def_id.as_local() { + tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + tcx.local_def_id_to_hir_id(item_def_id), + span, + InvalidFormatSpecifier, + ); } } } @@ -152,75 +87,72 @@ pub struct FormatArgs<'tcx> { pub generic_args: Vec<(Symbol, String)>, } -impl FormatString { - pub fn span(&self) -> Span { - self.span - } - - pub fn parse<'tcx>( - input: Symbol, - snippet: Option, - span: Span, - ctx: &Ctx<'tcx>, - ) -> Result { - let s = input.as_str(); - let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic); - let pieces: Vec<_> = parser.by_ref().collect(); - - if let Some(err) = parser.errors.into_iter().next() { - return Err(err); - } - let mut warnings = Vec::new(); - - let pieces = pieces - .into_iter() - .map(|piece| match piece { - RpfPiece::Lit(lit) => Piece::Lit(lit.into()), - RpfPiece::NextArgument(arg) => { - warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); - let arg = parse_arg(&arg, ctx, &mut warnings, span, parser.is_source_literal); - Piece::Arg(arg) - } - }) - .collect(); +pub fn parse_format_string<'tcx>( + input: Symbol, + snippet: Option, + span: Span, + ctx: &Ctx<'tcx>, +) -> Result<(FormatString, Vec), ParseError> { + let s = input.as_str(); + let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic); + let pieces: Vec<_> = parser.by_ref().collect(); - Ok(FormatString { input, pieces, span, warnings }) + if let Some(err) = parser.errors.into_iter().next() { + return Err(err); } + let mut warnings = Vec::new(); + + let pieces = pieces + .into_iter() + .map(|piece| match piece { + RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)), + RpfPiece::NextArgument(arg) => { + warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); + let arg = parse_arg(&arg, ctx, &mut warnings, span, parser.is_source_literal); + Piece::Arg(arg) + } + }) + .collect(); - pub fn format(&self, args: &FormatArgs<'_>) -> String { - let mut ret = String::new(); - for piece in &self.pieces { - match piece { - Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(&s), + Ok((FormatString { input, pieces, span }, warnings)) +} - // `A` if we have `trait Trait {}` and `note = "i'm the actual type of {A}"` - Piece::Arg(FormatArg::GenericParam { generic_param }) => { - // Should always be some but we can't raise errors here - let value = match args.generic_args.iter().find(|(p, _)| p == generic_param) { - Some((_, val)) => val.to_string(), - None => generic_param.to_string(), - }; - ret.push_str(&value); - } - // `{Self}` - Piece::Arg(FormatArg::SelfUpper) => { - let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) { - Some((_, val)) => val.to_string(), - None => "Self".to_string(), - }; - ret.push_str(&slf); +pub fn format(slf: &FormatString, args: &FormatArgs<'_>) -> String { + let mut ret = String::new(); + for piece in &slf.pieces { + match piece { + Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(s.as_str()), + + // `A` if we have `trait Trait {}` and `note = "i'm the actual type of {A}"` + Piece::Arg(FormatArg::GenericParam { generic_param, .. }) => { + match args.generic_args.iter().find(|(p, _)| p == generic_param) { + Some((_, val)) => ret.push_str(val.as_str()), + + None => { + // Apparently this was not actually a generic parameter, so lets write + // what the user wrote. + let _ = fmt::write(&mut ret, format_args!("{{{generic_param}}}")); + } } + } + // `{Self}` + Piece::Arg(FormatArg::SelfUpper) => { + let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) { + Some((_, val)) => val.to_string(), + None => "Self".to_string(), + }; + ret.push_str(&slf); + } - // It's only `rustc_onunimplemented` from here - Piece::Arg(FormatArg::This) => ret.push_str(&args.this), - Piece::Arg(FormatArg::Trait) => { - let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared)); - } - Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context), + // It's only `rustc_onunimplemented` from here + Piece::Arg(FormatArg::This) => ret.push_str(&args.this), + Piece::Arg(FormatArg::Trait) => { + let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared)); } + Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context), } - ret } + ret } fn parse_arg<'tcx>( @@ -230,9 +162,6 @@ fn parse_arg<'tcx>( input_span: Span, is_source_literal: bool, ) -> FormatArg { - let (Ctx::RustcOnUnimplemented { tcx, trait_def_id } - | Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = ctx; - let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); match arg.position { @@ -250,17 +179,7 @@ fn parse_arg<'tcx>( ( Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. }, generic_param, - ) if tcx.generics_of(trait_def_id).own_params.iter().any(|param| { - !matches!(param.kind, GenericParamDefKind::Lifetime) && param.name == generic_param - }) => - { - FormatArg::GenericParam { generic_param } - } - - (_, argument_name) => { - warnings.push(FormatWarning::UnknownParam { argument_name, span }); - FormatArg::AsIs(format!("{{{}}}", argument_name.as_str())) - } + ) => FormatArg::GenericParam { generic_param, span }, }, // `{:1}` and `{}` are ignored @@ -269,14 +188,14 @@ fn parse_arg<'tcx>( span, help: format!("use `{{{idx}}}` to print a number in braces"), }); - FormatArg::AsIs(format!("{{{idx}}}")) + FormatArg::AsIs(Symbol::intern(&format!("{{{idx}}}"))) } Position::ArgumentImplicitlyIs(_) => { warnings.push(FormatWarning::PositionalArgument { span, help: String::from("use `{{}}` to print empty braces"), }); - FormatArg::AsIs(String::from("{}")) + FormatArg::AsIs(sym::empty_braces) } } } diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 22e222efa4358..5084095641914 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -732,23 +732,6 @@ help: use `#[rustc_align(...)]` instead LL | #[repr] | ^^^^^^^ -warning: missing options for `on_unimplemented` attribute - --> $DIR/malformed-attrs.rs:143:1 - | -LL | #[diagnostic::on_unimplemented] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: at least one of the `message`, `note` and `label` options are expected - = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default - -warning: malformed `on_unimplemented` attribute - --> $DIR/malformed-attrs.rs:145:1 - | -LL | #[diagnostic::on_unimplemented = 1] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` --> $DIR/malformed-attrs.rs:42:1 | @@ -819,6 +802,23 @@ LL | #[no_implicit_prelude = 23] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = help: `#[no_implicit_prelude]` can be applied to crates and modules +warning: missing options for `on_unimplemented` attribute + --> $DIR/malformed-attrs.rs:143:1 + | +LL | #[diagnostic::on_unimplemented] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: malformed `on_unimplemented` attribute + --> $DIR/malformed-attrs.rs:145:1 + | +LL | #[diagnostic::on_unimplemented = 1] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + warning: `#[diagnostic::do_not_recommend]` does not expect any arguments --> $DIR/malformed-attrs.rs:154:1 | diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs index 82c2db7e26d7f..56255a25b3f39 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.rs @@ -1,34 +1,27 @@ //@ reference: attributes.diagnostic.on_unimplemented.invalid-string #[diagnostic::on_unimplemented(message = "{{Test } thing")] //~^WARN unmatched `}` found -//~|WARN unmatched `}` found trait ImportantTrait1 {} #[diagnostic::on_unimplemented(message = "Test {}")] //~^WARN positional format arguments are not allowed here -//~|WARN positional format arguments are not allowed here trait ImportantTrait2 {} #[diagnostic::on_unimplemented(message = "Test {1:}")] //~^WARN positional format arguments are not allowed here -//~|WARN positional format arguments are not allowed here -//~|WARN invalid format specifier [malformed_diagnostic_format_literals] //~|WARN invalid format specifier [malformed_diagnostic_format_literals] trait ImportantTrait3 {} #[diagnostic::on_unimplemented(message = "Test {Self:123}")] //~^WARN invalid format specifier -//~|WARN invalid format specifier trait ImportantTrait4 {} #[diagnostic::on_unimplemented(message = "Test {Self:!}")] //~^WARN invalid format specifier [malformed_diagnostic_format_literals] -//~|WARN invalid format specifier [malformed_diagnostic_format_literals] trait ImportantTrait5 {} #[diagnostic::on_unimplemented(message = "Test {Self:}")] //~^WARN invalid format specifier [malformed_diagnostic_format_literals] -//~|WARN invalid format specifier [malformed_diagnostic_format_literals] trait ImportantTrait6 {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr index 2138d591ca207..a6ab2f245a3e4 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr @@ -2,12 +2,14 @@ warning: unmatched `}` found --> $DIR/broken_format.rs:2:42 | LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^-^^^^^^^ + | | + | unmatched `}` | = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:7:49 + --> $DIR/broken_format.rs:6:49 | LL | #[diagnostic::on_unimplemented(message = "Test {}")] | ^ @@ -15,7 +17,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {}")] = help: only named format arguments with the name of one of the generic types are allowed in this context warning: invalid format specifier - --> $DIR/broken_format.rs:12:50 + --> $DIR/broken_format.rs:10:50 | LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] | ^ @@ -23,7 +25,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] = help: no format specifier are supported in this position warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:12:49 + --> $DIR/broken_format.rs:10:49 | LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] | ^ @@ -31,7 +33,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] = help: only named format arguments with the name of one of the generic types are allowed in this context warning: invalid format specifier - --> $DIR/broken_format.rs:19:53 + --> $DIR/broken_format.rs:15:53 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] | ^^^^ @@ -39,7 +41,7 @@ LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] = help: no format specifier are supported in this position warning: invalid format specifier - --> $DIR/broken_format.rs:24:53 + --> $DIR/broken_format.rs:19:53 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] | ^^ @@ -47,23 +49,15 @@ LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] = help: no format specifier are supported in this position warning: invalid format specifier - --> $DIR/broken_format.rs:29:53 + --> $DIR/broken_format.rs:23:53 | LL | #[diagnostic::on_unimplemented(message = "Test {Self:}")] | ^ | = help: no format specifier are supported in this position -warning: unmatched `}` found - --> $DIR/broken_format.rs:2:42 - | -LL | #[diagnostic::on_unimplemented(message = "{{Test } thing")] - | ^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: {{Test } thing - --> $DIR/broken_format.rs:43:13 + --> $DIR/broken_format.rs:36:13 | LL | check_1(()); | ------- ^^ the trait `ImportantTrait1` is not implemented for `()` @@ -71,27 +65,18 @@ LL | check_1(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:5:1 + --> $DIR/broken_format.rs:4:1 | LL | trait ImportantTrait1 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_1` - --> $DIR/broken_format.rs:35:20 + --> $DIR/broken_format.rs:28:20 | LL | fn check_1(_: impl ImportantTrait1) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_1` -warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:7:49 - | -LL | #[diagnostic::on_unimplemented(message = "Test {}")] - | ^ - | - = help: only named format arguments with the name of one of the generic types are allowed in this context - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test {} - --> $DIR/broken_format.rs:45:13 + --> $DIR/broken_format.rs:38:13 | LL | check_2(()); | ------- ^^ the trait `ImportantTrait2` is not implemented for `()` @@ -99,36 +84,18 @@ LL | check_2(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:10:1 + --> $DIR/broken_format.rs:8:1 | LL | trait ImportantTrait2 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_2` - --> $DIR/broken_format.rs:36:20 + --> $DIR/broken_format.rs:29:20 | LL | fn check_2(_: impl ImportantTrait2) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_2` -warning: invalid format specifier - --> $DIR/broken_format.rs:12:50 - | -LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] - | ^ - | - = help: no format specifier are supported in this position - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: positional format arguments are not allowed here - --> $DIR/broken_format.rs:12:49 - | -LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] - | ^ - | - = help: only named format arguments with the name of one of the generic types are allowed in this context - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test {1} - --> $DIR/broken_format.rs:47:13 + --> $DIR/broken_format.rs:40:13 | LL | check_3(()); | ------- ^^ the trait `ImportantTrait3` is not implemented for `()` @@ -136,27 +103,18 @@ LL | check_3(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:17:1 + --> $DIR/broken_format.rs:13:1 | LL | trait ImportantTrait3 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_3` - --> $DIR/broken_format.rs:37:20 + --> $DIR/broken_format.rs:30:20 | LL | fn check_3(_: impl ImportantTrait3) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_3` -warning: invalid format specifier - --> $DIR/broken_format.rs:19:53 - | -LL | #[diagnostic::on_unimplemented(message = "Test {Self:123}")] - | ^^^^ - | - = help: no format specifier are supported in this position - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test () - --> $DIR/broken_format.rs:49:13 + --> $DIR/broken_format.rs:42:13 | LL | check_4(()); | ------- ^^ the trait `ImportantTrait4` is not implemented for `()` @@ -164,27 +122,18 @@ LL | check_4(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:22:1 + --> $DIR/broken_format.rs:17:1 | LL | trait ImportantTrait4 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_4` - --> $DIR/broken_format.rs:38:20 + --> $DIR/broken_format.rs:31:20 | LL | fn check_4(_: impl ImportantTrait4) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_4` -warning: invalid format specifier - --> $DIR/broken_format.rs:24:53 - | -LL | #[diagnostic::on_unimplemented(message = "Test {Self:!}")] - | ^^ - | - = help: no format specifier are supported in this position - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test () - --> $DIR/broken_format.rs:51:13 + --> $DIR/broken_format.rs:44:13 | LL | check_5(()); | ------- ^^ the trait `ImportantTrait5` is not implemented for `()` @@ -192,27 +141,18 @@ LL | check_5(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:27:1 + --> $DIR/broken_format.rs:21:1 | LL | trait ImportantTrait5 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_5` - --> $DIR/broken_format.rs:39:20 + --> $DIR/broken_format.rs:32:20 | LL | fn check_5(_: impl ImportantTrait5) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_5` -warning: invalid format specifier - --> $DIR/broken_format.rs:29:53 - | -LL | #[diagnostic::on_unimplemented(message = "Test {Self:}")] - | ^ - | - = help: no format specifier are supported in this position - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Test () - --> $DIR/broken_format.rs:53:13 + --> $DIR/broken_format.rs:46:13 | LL | check_6(()); | ------- ^^ the trait `ImportantTrait6` is not implemented for `()` @@ -220,16 +160,16 @@ LL | check_6(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/broken_format.rs:32:1 + --> $DIR/broken_format.rs:25:1 | LL | trait ImportantTrait6 {} | ^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `check_6` - --> $DIR/broken_format.rs:40:20 + --> $DIR/broken_format.rs:33:20 | LL | fn check_6(_: impl ImportantTrait6) {} | ^^^^^^^^^^^^^^^ required by this bound in `check_6` -error: aborting due to 6 previous errors; 14 warnings emitted +error: aborting due to 6 previous errors; 7 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs index a0e497fa045b3..e64e60f30c0c1 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs @@ -5,22 +5,18 @@ #[diagnostic::on_unimplemented( on(Self = "&str"), //~^WARN malformed `on_unimplemented` attribute - //~|WARN malformed `on_unimplemented` attribute message = "trait has `{Self}` and `{T}` as params", label = "trait has `{Self}` and `{T}` as params", note = "trait has `{Self}` and `{T}` as params", parent_label = "in this scope", //~^WARN malformed `on_unimplemented` attribute - //~|WARN malformed `on_unimplemented` attribute append_const_msg //~^WARN malformed `on_unimplemented` attribute - //~|WARN malformed `on_unimplemented` attribute )] trait Foo {} #[diagnostic::on_unimplemented = "Message"] //~^WARN malformed `on_unimplemented` attribute -//~|WARN malformed `on_unimplemented` attribute trait Bar {} #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")] @@ -32,27 +28,16 @@ impl Bar for i32 {} #[diagnostic::on_unimplemented( message = "{from_desugaring}{direct}{cause}{integral}{integer}", //~^WARN there is no parameter `from_desugaring` on trait `Baz` - //~|WARN there is no parameter `from_desugaring` on trait `Baz` //~|WARN there is no parameter `direct` on trait `Baz` - //~|WARN there is no parameter `direct` on trait `Baz` - //~|WARN there is no parameter `cause` on trait `Baz` //~|WARN there is no parameter `cause` on trait `Baz` //~|WARN there is no parameter `integral` on trait `Baz` - //~|WARN there is no parameter `integral` on trait `Baz` - //~|WARN there is no parameter `integer` on trait `Baz` //~|WARN there is no parameter `integer` on trait `Baz` label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" //~^WARN there is no parameter `float` on trait `Baz` - //~|WARN there is no parameter `float` on trait `Baz` - //~|WARN there is no parameter `_Self` on trait `Baz` //~|WARN there is no parameter `_Self` on trait `Baz` //~|WARN there is no parameter `crate_local` on trait `Baz` - //~|WARN there is no parameter `crate_local` on trait `Baz` - //~|WARN there is no parameter `Trait` on trait `Baz` //~|WARN there is no parameter `Trait` on trait `Baz` //~|WARN there is no parameter `ItemContext` on trait `Baz` - //~|WARN there is no parameter `ItemContext` on trait `Baz` - //~|WARN there is no parameter `This` on trait `Baz` //~|WARN there is no parameter `This` on trait `Baz` )] trait Baz {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr index eaf1de9ccadab..862f805e15ba7 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr @@ -1,46 +1,13 @@ warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:26:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:22:1 | LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:6:5 - | -LL | on(Self = "&str"), - | ^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default - -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:12:5 - | -LL | parent_label = "in this scope", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:15:5 - | -LL | append_const_msg - | ^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:21:32 - | -LL | #[diagnostic::on_unimplemented = "Message"] - | ^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - warning: there is no parameter `from_desugaring` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:17 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:17 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", | ^^^^^^^^^^^^^^^ @@ -49,7 +16,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: there is no parameter `direct` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:34 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:34 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", | ^^^^^^ @@ -57,7 +24,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `cause` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:42 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:42 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", | ^^^^^ @@ -65,7 +32,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `integral` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:49 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:49 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", | ^^^^^^^^ @@ -73,7 +40,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `integer` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:59 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:59 | LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", | ^^^^^^^ @@ -81,7 +48,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `float` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:15 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ @@ -89,7 +56,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `_Self` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:22 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ @@ -97,7 +64,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `crate_local` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:29 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:29 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^^^^^^^ @@ -105,7 +72,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `Trait` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:42 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:42 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ @@ -113,7 +80,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `ItemContext` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:49 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:49 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^^^^^^^ @@ -121,7 +88,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `This` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:62 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:62 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^ @@ -135,28 +102,34 @@ LL | on(Self = "&str"), | ^^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:12:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:11:5 | LL | parent_label = "in this scope", | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:15:5 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:13:5 | LL | append_const_msg | ^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +warning: malformed `on_unimplemented` attribute + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:18:1 + | +LL | #[diagnostic::on_unimplemented = "Message"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options error[E0277]: trait has `()` and `i32` as params - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:65:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:50:15 | LL | takes_foo(()); | --------- ^^ trait has `()` and `i32` as params @@ -166,27 +139,18 @@ LL | takes_foo(()); = help: the trait `Foo` is not implemented for `()` = note: trait has `()` and `i32` as params help: this trait has no implementations, consider adding one - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:19:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:16:1 | LL | trait Foo {} | ^^^^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:60:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:45:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^^^^^^ required by this bound in `takes_foo` -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:21:32 - | -LL | #[diagnostic::on_unimplemented = "Message"] - | ^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:67:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:52:15 | LL | takes_bar(()); | --------- ^^ the trait `Bar` is not implemented for `()` @@ -194,117 +158,18 @@ LL | takes_bar(()); | required by a bound introduced by this call | help: the trait `Bar` is implemented for `i32` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:28:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:24:1 | LL | impl Bar for i32 {} | ^^^^^^^^^^^^^^^^ note: required by a bound in `takes_bar` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:61:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:46:22 | LL | fn takes_bar(_: impl Bar) {} | ^^^ required by this bound in `takes_bar` -warning: there is no parameter `from_desugaring` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:17 - | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `direct` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:34 - | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `cause` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:42 - | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `integral` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:49 - | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `integer` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:33:59 - | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", - | ^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `float` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:15 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `_Self` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:22 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `crate_local` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:29 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `Trait` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:42 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `ItemContext` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:49 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: there is no parameter `This` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:44:62 - | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer} - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:69:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:54:15 | LL | takes_baz(()); | --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext}{This} @@ -313,16 +178,16 @@ LL | takes_baz(()); | = help: the trait `Baz` is not implemented for `()` help: this trait has no implementations, consider adding one - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:58:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:43:1 | LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `takes_baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:62:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:47:22 | LL | fn takes_baz(_: impl Baz) {} | ^^^ required by this bound in `takes_baz` -error: aborting due to 3 previous errors; 31 warnings emitted +error: aborting due to 3 previous errors; 16 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs index 08eb5707e909e..c759acc12565c 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs @@ -2,7 +2,6 @@ //@ reference: attributes.diagnostic.on_unimplemented.unknown-keys #[diagnostic::on_unimplemented(unsupported = "foo")] //~^WARN malformed `on_unimplemented` attribute -//~|WARN malformed `on_unimplemented` attribute trait Foo {} #[diagnostic::on_unimplemented(message = "Baz")] @@ -11,12 +10,10 @@ struct Bar {} #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] //~^WARN malformed `on_unimplemented` attribute -//~|WARN malformed `on_unimplemented` attribute trait Baz {} #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))] //~^WARN malformed `on_unimplemented` attribute -//~|WARN malformed `on_unimplemented` attribute trait Boom {} #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] @@ -29,12 +26,10 @@ trait Doom {} #[diagnostic::on_unimplemented] //~^WARN missing options for `on_unimplemented` attribute -//~|WARN missing options for `on_unimplemented` attribute trait Whatever {} #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] //~^WARN there is no parameter `DoesNotExist` on trait `Test` -//~|WARN there is no parameter `DoesNotExist` on trait `Test` trait Test {} fn take_foo(_: impl Foo) {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr index df5d0d6dccb40..4361e3261a0c1 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr @@ -1,11 +1,20 @@ warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:8:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:7:1 | LL | #[diagnostic::on_unimplemented(message = "Baz")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default +warning: there is no parameter `DoesNotExist` on trait `Test` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:44 + | +LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] + | ^^^^^^^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 | @@ -16,7 +25,7 @@ LL | #[diagnostic::on_unimplemented(unsupported = "foo")] = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:11:50 | LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] | ^^^^^^^^^^^^^^^^^^^ invalid option found here @@ -24,7 +33,7 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] = help: only `message`, `note` and `label` are allowed as options warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:15:50 | LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here @@ -32,7 +41,7 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = = help: only `message`, `note` and `label` are allowed as options warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:22:50 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:19:50 | LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here @@ -40,41 +49,23 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = help: only `message`, `note` and `label` are allowed as options warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:32 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:23:1 | LL | #[diagnostic::on_unimplemented = "boom"] - | ^^^^^^^^ invalid option found here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options warning: missing options for `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:30:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:27:1 | LL | #[diagnostic::on_unimplemented] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: at least one of the `message`, `note` and `label` options are expected -warning: there is no parameter `DoesNotExist` on trait `Test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:44 - | -LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] - | ^^^^^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default - -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 - | -LL | #[diagnostic::on_unimplemented(unsupported = "foo")] - | ^^^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: the trait bound `i32: Foo` is not satisfied - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:47:14 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:42:14 | LL | take_foo(1_i32); | -------- ^^^^^ the trait `Foo` is not implemented for `i32` @@ -82,27 +73,18 @@ LL | take_foo(1_i32); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:6:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:5:1 | LL | trait Foo {} | ^^^^^^^^^ note: required by a bound in `take_foo` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:40:21 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:21 | LL | fn take_foo(_: impl Foo) {} | ^^^ required by this bound in `take_foo` -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50 - | -LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] - | ^^^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Boom - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:49:14 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:44:14 | LL | take_baz(1_i32); | -------- ^^^^^ the trait `Baz` is not implemented for `i32` @@ -110,27 +92,18 @@ LL | take_baz(1_i32); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:15:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:13:1 | LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `take_baz` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:41:21 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:36:21 | LL | fn take_baz(_: impl Baz) {} | ^^^ required by this bound in `take_baz` -warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 - | -LL | #[diagnostic::on_unimplemented(message = "Boom", on(Self = "i32", message = "whatever"))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here - | - = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: Boom - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:51:15 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:46:15 | LL | take_boom(1_i32); | --------- ^^^^^ the trait `Boom` is not implemented for `i32` @@ -138,27 +111,18 @@ LL | take_boom(1_i32); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:20:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:1 | LL | trait Boom {} | ^^^^^^^^^^ note: required by a bound in `take_boom` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:42:22 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:37:22 | LL | fn take_boom(_: impl Boom) {} | ^^^^ required by this bound in `take_boom` -warning: missing options for `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:30:1 - | -LL | #[diagnostic::on_unimplemented] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: at least one of the `message`, `note` and `label` options are expected - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: the trait bound `i32: Whatever` is not satisfied - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:53:19 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:48:19 | LL | take_whatever(1_i32); | ------------- ^^^^^ the trait `Whatever` is not implemented for `i32` @@ -166,27 +130,18 @@ LL | take_whatever(1_i32); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:29:1 | LL | trait Whatever {} | ^^^^^^^^^^^^^^ note: required by a bound in `take_whatever` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:43:26 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:38:26 | LL | fn take_whatever(_: impl Whatever) {} | ^^^^^^^^ required by this bound in `take_whatever` -warning: there is no parameter `DoesNotExist` on trait `Test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:44 - | -LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")] - | ^^^^^^^^^^^^ - | - = help: expect either a generic argument name or `{Self}` as format argument - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0277]: {DoesNotExist} - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:55:15 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:50:15 | LL | take_test(()); | --------- ^^ the trait `Test` is not implemented for `()` @@ -194,16 +149,16 @@ LL | take_test(()); | required by a bound introduced by this call | help: this trait has no implementations, consider adding one - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:38:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:1 | LL | trait Test {} | ^^^^^^^^^^ note: required by a bound in `take_test` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:44:22 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:39:22 | LL | fn take_test(_: impl Test) {} | ^^^^ required by this bound in `take_test` -error: aborting due to 5 previous errors; 13 warnings emitted +error: aborting due to 5 previous errors; 8 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs index b06f56bd66e44..7d1ed6cf3a50c 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs @@ -3,13 +3,11 @@ #[diagnostic::on_unimplemented( if(Self = "()"), //~^WARN malformed `on_unimplemented` attribute - //~|WARN malformed `on_unimplemented` attribute message = "custom message", note = "custom note" )] #[diagnostic::on_unimplemented(message = "fallback!!")] //~^ WARN `message` is ignored due to previous definition of `message` -//~| WARN `message` is ignored due to previous definition of `message` #[diagnostic::on_unimplemented(label = "fallback label")] #[diagnostic::on_unimplemented(note = "fallback note")] trait Foo {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr index 24b3a9c3405fa..2947ca23a0679 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr @@ -1,20 +1,13 @@ -warning: malformed `on_unimplemented` attribute +error: expected identifier, found keyword `if` --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5 | LL | if(Self = "()"), - | ^^^^^^^^^^^^^^^ invalid option found here + | ^^ expected identifier, found keyword | - = help: only `message`, `note` and `label` are allowed as options - = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default - -warning: `message` is ignored due to previous definition of `message` - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32 +help: escape `if` to use it as an identifier | -LL | message = "custom message", - | -------------------------- `message` is first declared here -... -LL | #[diagnostic::on_unimplemented(message = "fallback!!")] - | ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here +LL | r#if(Self = "()"), + | ++ warning: malformed `on_unimplemented` attribute --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5 @@ -23,21 +16,19 @@ LL | if(Self = "()"), | ^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: `message` is ignored due to previous definition of `message` - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:10:32 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:9:32 | LL | message = "custom message", | -------------------------- `message` is first declared here ... LL | #[diagnostic::on_unimplemented(message = "fallback!!")] - | ^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + | ^^^^^^^^^^^^^^^^^^^^^^ `message` is later redundantly declared here error[E0277]: custom message - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:18:15 | LL | takes_foo(()); | --------- ^^ fallback label @@ -48,16 +39,16 @@ LL | takes_foo(()); = note: custom note = note: fallback note help: this trait has no implementations, consider adding one - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:13:1 | LL | trait Foo {} | ^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^ required by this bound in `takes_foo` -error: aborting due to 1 previous error; 4 warnings emitted +error: aborting due to 2 previous errors; 2 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs index d0eb608c40f20..414b7ec93b51f 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.rs @@ -7,10 +7,8 @@ #[diagnostic::on_unimplemented( message = "second message", //~^WARN `message` is ignored due to previous definition of `message` - //~|WARN `message` is ignored due to previous definition of `message` label = "second label", //~^WARN `label` is ignored due to previous definition of `label` - //~|WARN `label` is ignored due to previous definition of `label` note = "second note" )] trait Foo {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr index de43656ff085b..2f20777c8f2f5 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/report_warning_on_duplicated_options.stderr @@ -5,43 +5,21 @@ LL | message = "first message", | ------------------------- `message` is first declared here ... LL | message = "second message", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is later redundantly declared here | = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: `label` is ignored due to previous definition of `label` - --> $DIR/report_warning_on_duplicated_options.rs:11:5 + --> $DIR/report_warning_on_duplicated_options.rs:10:5 | LL | label = "first label", | --------------------- `label` is first declared here ... LL | label = "second label", - | ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here - -warning: `message` is ignored due to previous definition of `message` - --> $DIR/report_warning_on_duplicated_options.rs:8:5 - | -LL | message = "first message", - | ------------------------- `message` is first declared here -... -LL | message = "second message", - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is already declared here - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -warning: `label` is ignored due to previous definition of `label` - --> $DIR/report_warning_on_duplicated_options.rs:11:5 - | -LL | label = "first label", - | --------------------- `label` is first declared here -... -LL | label = "second label", - | ^^^^^^^^^^^^^^^^^^^^^^ `label` is already declared here - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + | ^^^^^^^^^^^^^^^^^^^^^^ `label` is later redundantly declared here error[E0277]: first message - --> $DIR/report_warning_on_duplicated_options.rs:22:15 + --> $DIR/report_warning_on_duplicated_options.rs:20:15 | LL | takes_foo(()); | --------- ^^ first label @@ -52,16 +30,16 @@ LL | takes_foo(()); = note: custom note = note: second note help: this trait has no implementations, consider adding one - --> $DIR/report_warning_on_duplicated_options.rs:16:1 + --> $DIR/report_warning_on_duplicated_options.rs:14:1 | LL | trait Foo {} | ^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/report_warning_on_duplicated_options.rs:19:22 + --> $DIR/report_warning_on_duplicated_options.rs:17:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^ required by this bound in `takes_foo` -error: aborting due to 1 previous error; 4 warnings emitted +error: aborting due to 1 previous error; 2 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/on-unimplemented/bad-annotation.stderr b/tests/ui/on-unimplemented/bad-annotation.stderr index 3fc5453277404..1777dc849d0c1 100644 --- a/tests/ui/on-unimplemented/bad-annotation.stderr +++ b/tests/ui/on-unimplemented/bad-annotation.stderr @@ -11,12 +11,6 @@ LL | #[rustc_on_unimplemented = "message"] LL | #[rustc_on_unimplemented(/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...")] | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -error[E0230]: cannot find parameter C on this trait - --> $DIR/bad-annotation.rs:19:90 - | -LL | #[rustc_on_unimplemented = "Unimplemented trait error on `{Self}` with params `<{A},{B},{C}>`"] - | ^ - error[E0231]: positional format arguments are not allowed here --> $DIR/bad-annotation.rs:23:90 | @@ -137,7 +131,7 @@ error[E0232]: invalid name in `on`-clause LL | #[rustc_on_unimplemented(on(abc = "y", message = "y"))] | ^^^ expected one of `cause`, `from_desugaring`, `Self` or any generic parameter of the trait, not `abc` -error: aborting due to 20 previous errors +error: aborting due to 19 previous errors -Some errors have detailed explanations: E0230, E0231, E0232. -For more information about an error, try `rustc --explain E0230`. +Some errors have detailed explanations: E0231, E0232. +For more information about an error, try `rustc --explain E0231`. diff --git a/tests/ui/unpretty/diagnostic-attr.stdout b/tests/ui/unpretty/diagnostic-attr.stdout index 25349681b02a3..a7caf81e2fed6 100644 --- a/tests/ui/unpretty/diagnostic-attr.stdout +++ b/tests/ui/unpretty/diagnostic-attr.stdout @@ -5,9 +5,14 @@ use ::std::prelude::rust_2015::*; //@ check-pass //@ edition: 2015 -#[diagnostic::on_unimplemented(message = -"My Message for `ImportantTrait<{A}>` implemented for `{Self}`", label = -"My Label", note = "Note 1", note = "Note 2")] +#[attr = OnUnimplemented {directive: OnUnimplementedDirective {subcommands: [], +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 `"), +Arg(SelfUpper), Lit("`")]}, label: FormatString {input: "My Label", +pieces: [Lit("My Label")]}, notes: [FormatString {input: "Note 1", +pieces: [Lit("Note 1")]}, FormatString {input: "Note 2", +pieces: [Lit("Note 2")]}]}}] trait ImportantTrait { } #[attr = DoNotRecommend]