diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 027f377b0ac..6b5b8b59320 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } (Some(DiagnosticDeriveKind::Lint), _) => { - span_err(span, "only `#[error(..)]` and `#[warn(..)]` are supported") + span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported") .help("use the `#[error(...)]` attribute to create a error") .emit(); return DiagnosticDeriveError::ErrorHandled.to_compile_error(); diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index db3ba20d8f9..5c5275b7cfb 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -8,7 +8,7 @@ use crate::diagnostics::utils::{ report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path, Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, }; -use proc_macro2::{Ident, TokenStream}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; use std::collections::HashMap; use std::str::FromStr; @@ -156,7 +156,7 @@ impl DiagnosticDeriveBuilder { let name = name.as_str(); let meta = attr.parse_meta()?; - let is_help_or_note = matches!(name, "help" | "note"); + let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_"); let nested = match meta { // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or @@ -164,8 +164,12 @@ impl DiagnosticDeriveBuilder { Meta::List(MetaList { ref nested, .. }) => nested, // Subdiagnostics without spans can be applied to the type too, and these are just // paths: `#[help]` and `#[note]` - Meta::Path(_) if is_help_or_note => { - let fn_name = proc_macro2::Ident::new(name, attr.span()); + Meta::Path(_) if is_help_note_or_warn => { + let fn_name = if name == "warn_" { + Ident::new("warn", attr.span()) + } else { + Ident::new(name, attr.span()) + }; return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); }); } _ => throw_invalid_attr!(attr, &meta), @@ -177,9 +181,11 @@ impl DiagnosticDeriveBuilder { "error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)), "warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)), "lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)), - "help" | "note" => (), + "help" | "note" | "warn_" => (), _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("only `error`, `warning`, `help` and `note` are valid attributes") + diag.help( + "only `error`, `warning`, `help`, `note` and `warn_` are valid attributes", + ) }), } @@ -188,14 +194,16 @@ impl DiagnosticDeriveBuilder { let mut nested_iter = nested.into_iter(); if let Some(nested_attr) = nested_iter.next() { // Report an error if there are any other list items after the path. - if is_help_or_note && nested_iter.next().is_some() { + if is_help_note_or_warn && nested_iter.next().is_some() { throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("`help` and `note` struct attributes can only have one argument") + diag.help( + "`help`, `note` and `warn_` struct attributes can only have one argument", + ) }); } match nested_attr { - NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => { + NestedMeta::Meta(Meta::Path(path)) if is_help_note_or_warn => { let fn_name = proc_macro2::Ident::new(name, attr.span()); return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); }); } @@ -203,7 +211,7 @@ impl DiagnosticDeriveBuilder { self.slug.set_once((path.clone(), span)); } NestedMeta::Meta(meta @ Meta::NameValue(_)) - if !is_help_or_note + if !is_help_note_or_warn && meta.path().segments.last().unwrap().ident.to_string() == "code" => { // don't error for valid follow-up attributes @@ -347,10 +355,12 @@ impl DiagnosticDeriveBuilder { report_error_if_not_applied_to_span(attr, &info)?; Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label })) } - "note" | "help" => { - let path = match name { - "note" => parse_quote! { _subdiag::note }, - "help" => parse_quote! { _subdiag::help }, + "note" | "help" | "warn_" => { + let warn_ident = Ident::new("warn", Span::call_site()); + let (ident, path) = match name { + "note" => (ident, parse_quote! { _subdiag::note }), + "help" => (ident, parse_quote! { _subdiag::help }), + "warn_" => (&warn_ident, parse_quote! { _subdiag::warn }), _ => unreachable!(), }; if type_matches_path(&info.ty, &["rustc_span", "Span"]) { @@ -387,10 +397,10 @@ impl DiagnosticDeriveBuilder { "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => { return self.generate_inner_field_code_suggestion(attr, info); } - "label" | "help" | "note" => (), + "label" | "help" | "note" | "warn_" => (), _ => throw_invalid_attr!(attr, &meta, |diag| { diag.help( - "only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \ + "only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \ valid field attributes", ) }), @@ -419,7 +429,14 @@ impl DiagnosticDeriveBuilder { Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) } "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)), - "note" | "help" => report_type_error(attr, "`Span` or `()`")?, + // `warn_` must be special-cased because the attribute `warn` already has meaning and + // so isn't used, despite the diagnostic API being named `warn`. + "warn_" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self + .add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)), + "warn_" if type_is_unit(&info.ty) => { + Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg)) + } + "note" | "help" | "warn_" => report_type_error(attr, "`Span` or `()`")?, _ => unreachable!(), } } diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs index 2758fcd1310..1170d2b3c59 100644 --- a/compiler/rustc_macros/src/diagnostics/fluent.rs +++ b/compiler/rustc_macros/src/diagnostics/fluent.rs @@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok #generated pub mod _subdiag { - pub const note: crate::SubdiagnosticMessage = - crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note")); pub const help: crate::SubdiagnosticMessage = crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help")); + pub const note: crate::SubdiagnosticMessage = + crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note")); + pub const warn: crate::SubdiagnosticMessage = + crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn")); pub const label: crate::SubdiagnosticMessage = crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label")); pub const suggestion: crate::SubdiagnosticMessage = diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 2a5b6beba94..edf4dbed985 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -37,6 +37,8 @@ enum SubdiagnosticKind { Note, /// `#[help(...)]` Help, + /// `#[warn_(...)]` + Warn, /// `#[suggestion{,_short,_hidden,_verbose}]` Suggestion(SubdiagnosticSuggestionKind), } @@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind { "label" => Ok(SubdiagnosticKind::Label), "note" => Ok(SubdiagnosticKind::Note), "help" => Ok(SubdiagnosticKind::Help), + "warn_" => Ok(SubdiagnosticKind::Warn), "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)), "suggestion_short" => { Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short)) @@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind { SubdiagnosticKind::Label => write!(f, "label"), SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Help => write!(f, "help"), + SubdiagnosticKind::Warn => write!(f, "warn"), SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => { write!(f, "suggestion") } diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 168530c54b9..ab509b26f1c 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -130,8 +130,9 @@ decl_derive!( warning, error, lint, - note, help, + note, + warn_, // field attributes skip_arg, primary_span, @@ -148,8 +149,9 @@ decl_derive!( warning, error, lint, - note, help, + note, + warn_, // field attributes skip_arg, primary_span, @@ -166,6 +168,7 @@ decl_derive!( label, help, note, + warn_, suggestion, suggestion_short, suggestion_hidden, diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index b343bcb7721..0a210cbdc94 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -538,7 +538,7 @@ struct LabelWithTrailingList { #[derive(SessionDiagnostic)] #[lint(typeck::ambiguous_lifetime_bound)] -//~^ ERROR only `#[error(..)]` and `#[warn(..)]` are supported +//~^ ERROR only `#[error(..)]` and `#[warning(..)]` are supported struct LintsBad { } @@ -559,3 +559,10 @@ struct ErrorWithMultiSpan { #[primary_span] span: MultiSpan, } + +#[derive(SessionDiagnostic)] +#[error(typeck::ambiguous_lifetime_bound, code = "E0123")] +#[warn_] +struct ErrorWithWarn { + val: String, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index e2580c6485a..c1080aa2452 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -21,7 +21,7 @@ error: `#[nonsense(...)]` is not a valid attribute LL | #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: only `error`, `warning`, `help` and `note` are valid attributes + = help: only `error`, `warning`, `help`, `note` and `warn_` are valid attributes error: diagnostic kind not specified --> $DIR/diagnostic-derive.rs:53:1 @@ -363,7 +363,7 @@ error: `#[label(...)]` is not a valid attribute LL | #[label(typeck::label, foo("..."))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: only `#[error(..)]` and `#[warn(..)]` are supported +error: only `#[error(..)]` and `#[warning(..)]` are supported --> $DIR/diagnostic-derive.rs:540:1 | LL | / #[lint(typeck::ambiguous_lifetime_bound)] diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index cbb66c13c68..16da25c402b 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -508,3 +508,15 @@ enum AX { span: Span, } } + +#[derive(SessionSubdiagnostic)] +#[warn_(parser::add_paren)] +struct AY { +} + +#[derive(SessionSubdiagnostic)] +#[warn_(parser::add_paren)] +struct AZ { + #[primary_span] + span: Span, +}