mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
macros: support adding warnings to diags
Both diagnostic and subdiagnostic derives were missing the ability to add warnings to diagnostics - this is made more difficult by the `warn` attribute already existing, so this name being unavailable for the derives to use. `#[warn_]` is used instead, which requires special-casing so that `{span_,}warn` is called instead of `{span_,}warn_`. Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
parent
88c11c5bff
commit
81cf2294b4
@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
|
|||||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||||
}
|
}
|
||||||
(Some(DiagnosticDeriveKind::Lint), _) => {
|
(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")
|
.help("use the `#[error(...)]` attribute to create a error")
|
||||||
.emit();
|
.emit();
|
||||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||||
|
@ -8,7 +8,7 @@ use crate::diagnostics::utils::{
|
|||||||
report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
|
report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
|
||||||
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
|
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
|
||||||
};
|
};
|
||||||
use proc_macro2::{Ident, TokenStream};
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@ -156,7 +156,7 @@ impl DiagnosticDeriveBuilder {
|
|||||||
let name = name.as_str();
|
let name = name.as_str();
|
||||||
let meta = attr.parse_meta()?;
|
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 {
|
let nested = match meta {
|
||||||
// Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
|
// Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
|
||||||
@ -164,8 +164,12 @@ impl DiagnosticDeriveBuilder {
|
|||||||
Meta::List(MetaList { ref nested, .. }) => nested,
|
Meta::List(MetaList { ref nested, .. }) => nested,
|
||||||
// Subdiagnostics without spans can be applied to the type too, and these are just
|
// Subdiagnostics without spans can be applied to the type too, and these are just
|
||||||
// paths: `#[help]` and `#[note]`
|
// paths: `#[help]` and `#[note]`
|
||||||
Meta::Path(_) if is_help_or_note => {
|
Meta::Path(_) if is_help_note_or_warn => {
|
||||||
let fn_name = proc_macro2::Ident::new(name, attr.span());
|
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); });
|
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); });
|
||||||
}
|
}
|
||||||
_ => throw_invalid_attr!(attr, &meta),
|
_ => throw_invalid_attr!(attr, &meta),
|
||||||
@ -177,9 +181,11 @@ impl DiagnosticDeriveBuilder {
|
|||||||
"error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)),
|
"error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)),
|
||||||
"warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)),
|
"warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)),
|
||||||
"lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)),
|
"lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)),
|
||||||
"help" | "note" => (),
|
"help" | "note" | "warn_" => (),
|
||||||
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
_ => 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();
|
let mut nested_iter = nested.into_iter();
|
||||||
if let Some(nested_attr) = nested_iter.next() {
|
if let Some(nested_attr) = nested_iter.next() {
|
||||||
// Report an error if there are any other list items after the path.
|
// 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| {
|
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 {
|
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());
|
let fn_name = proc_macro2::Ident::new(name, attr.span());
|
||||||
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
|
return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
|
||||||
}
|
}
|
||||||
@ -203,7 +211,7 @@ impl DiagnosticDeriveBuilder {
|
|||||||
self.slug.set_once((path.clone(), span));
|
self.slug.set_once((path.clone(), span));
|
||||||
}
|
}
|
||||||
NestedMeta::Meta(meta @ Meta::NameValue(_))
|
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" =>
|
&& meta.path().segments.last().unwrap().ident.to_string() == "code" =>
|
||||||
{
|
{
|
||||||
// don't error for valid follow-up attributes
|
// don't error for valid follow-up attributes
|
||||||
@ -347,10 +355,12 @@ impl DiagnosticDeriveBuilder {
|
|||||||
report_error_if_not_applied_to_span(attr, &info)?;
|
report_error_if_not_applied_to_span(attr, &info)?;
|
||||||
Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
|
Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
|
||||||
}
|
}
|
||||||
"note" | "help" => {
|
"note" | "help" | "warn_" => {
|
||||||
let path = match name {
|
let warn_ident = Ident::new("warn", Span::call_site());
|
||||||
"note" => parse_quote! { _subdiag::note },
|
let (ident, path) = match name {
|
||||||
"help" => parse_quote! { _subdiag::help },
|
"note" => (ident, parse_quote! { _subdiag::note }),
|
||||||
|
"help" => (ident, parse_quote! { _subdiag::help }),
|
||||||
|
"warn_" => (&warn_ident, parse_quote! { _subdiag::warn }),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
|
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
|
||||||
@ -387,10 +397,10 @@ impl DiagnosticDeriveBuilder {
|
|||||||
"suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
|
"suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
|
||||||
return self.generate_inner_field_code_suggestion(attr, info);
|
return self.generate_inner_field_code_suggestion(attr, info);
|
||||||
}
|
}
|
||||||
"label" | "help" | "note" => (),
|
"label" | "help" | "note" | "warn_" => (),
|
||||||
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
diag.help(
|
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",
|
valid field attributes",
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
@ -419,7 +429,14 @@ impl DiagnosticDeriveBuilder {
|
|||||||
Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
|
Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
|
||||||
}
|
}
|
||||||
"note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(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!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
|
|||||||
#generated
|
#generated
|
||||||
|
|
||||||
pub mod _subdiag {
|
pub mod _subdiag {
|
||||||
pub const note: crate::SubdiagnosticMessage =
|
|
||||||
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
|
|
||||||
pub const help: crate::SubdiagnosticMessage =
|
pub const help: crate::SubdiagnosticMessage =
|
||||||
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help"));
|
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 =
|
pub const label: crate::SubdiagnosticMessage =
|
||||||
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
|
crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
|
||||||
pub const suggestion: crate::SubdiagnosticMessage =
|
pub const suggestion: crate::SubdiagnosticMessage =
|
||||||
|
@ -37,6 +37,8 @@ enum SubdiagnosticKind {
|
|||||||
Note,
|
Note,
|
||||||
/// `#[help(...)]`
|
/// `#[help(...)]`
|
||||||
Help,
|
Help,
|
||||||
|
/// `#[warn_(...)]`
|
||||||
|
Warn,
|
||||||
/// `#[suggestion{,_short,_hidden,_verbose}]`
|
/// `#[suggestion{,_short,_hidden,_verbose}]`
|
||||||
Suggestion(SubdiagnosticSuggestionKind),
|
Suggestion(SubdiagnosticSuggestionKind),
|
||||||
}
|
}
|
||||||
@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind {
|
|||||||
"label" => Ok(SubdiagnosticKind::Label),
|
"label" => Ok(SubdiagnosticKind::Label),
|
||||||
"note" => Ok(SubdiagnosticKind::Note),
|
"note" => Ok(SubdiagnosticKind::Note),
|
||||||
"help" => Ok(SubdiagnosticKind::Help),
|
"help" => Ok(SubdiagnosticKind::Help),
|
||||||
|
"warn_" => Ok(SubdiagnosticKind::Warn),
|
||||||
"suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
|
"suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
|
||||||
"suggestion_short" => {
|
"suggestion_short" => {
|
||||||
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
|
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
|
||||||
@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind {
|
|||||||
SubdiagnosticKind::Label => write!(f, "label"),
|
SubdiagnosticKind::Label => write!(f, "label"),
|
||||||
SubdiagnosticKind::Note => write!(f, "note"),
|
SubdiagnosticKind::Note => write!(f, "note"),
|
||||||
SubdiagnosticKind::Help => write!(f, "help"),
|
SubdiagnosticKind::Help => write!(f, "help"),
|
||||||
|
SubdiagnosticKind::Warn => write!(f, "warn"),
|
||||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
|
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
|
||||||
write!(f, "suggestion")
|
write!(f, "suggestion")
|
||||||
}
|
}
|
||||||
|
@ -130,8 +130,9 @@ decl_derive!(
|
|||||||
warning,
|
warning,
|
||||||
error,
|
error,
|
||||||
lint,
|
lint,
|
||||||
note,
|
|
||||||
help,
|
help,
|
||||||
|
note,
|
||||||
|
warn_,
|
||||||
// field attributes
|
// field attributes
|
||||||
skip_arg,
|
skip_arg,
|
||||||
primary_span,
|
primary_span,
|
||||||
@ -148,8 +149,9 @@ decl_derive!(
|
|||||||
warning,
|
warning,
|
||||||
error,
|
error,
|
||||||
lint,
|
lint,
|
||||||
note,
|
|
||||||
help,
|
help,
|
||||||
|
note,
|
||||||
|
warn_,
|
||||||
// field attributes
|
// field attributes
|
||||||
skip_arg,
|
skip_arg,
|
||||||
primary_span,
|
primary_span,
|
||||||
@ -166,6 +168,7 @@ decl_derive!(
|
|||||||
label,
|
label,
|
||||||
help,
|
help,
|
||||||
note,
|
note,
|
||||||
|
warn_,
|
||||||
suggestion,
|
suggestion,
|
||||||
suggestion_short,
|
suggestion_short,
|
||||||
suggestion_hidden,
|
suggestion_hidden,
|
||||||
|
@ -538,7 +538,7 @@ struct LabelWithTrailingList {
|
|||||||
|
|
||||||
#[derive(SessionDiagnostic)]
|
#[derive(SessionDiagnostic)]
|
||||||
#[lint(typeck::ambiguous_lifetime_bound)]
|
#[lint(typeck::ambiguous_lifetime_bound)]
|
||||||
//~^ ERROR only `#[error(..)]` and `#[warn(..)]` are supported
|
//~^ ERROR only `#[error(..)]` and `#[warning(..)]` are supported
|
||||||
struct LintsBad {
|
struct LintsBad {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,3 +559,10 @@ struct ErrorWithMultiSpan {
|
|||||||
#[primary_span]
|
#[primary_span]
|
||||||
span: MultiSpan,
|
span: MultiSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(SessionDiagnostic)]
|
||||||
|
#[error(typeck::ambiguous_lifetime_bound, code = "E0123")]
|
||||||
|
#[warn_]
|
||||||
|
struct ErrorWithWarn {
|
||||||
|
val: String,
|
||||||
|
}
|
||||||
|
@ -21,7 +21,7 @@ error: `#[nonsense(...)]` is not a valid attribute
|
|||||||
LL | #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")]
|
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
|
error: diagnostic kind not specified
|
||||||
--> $DIR/diagnostic-derive.rs:53:1
|
--> $DIR/diagnostic-derive.rs:53:1
|
||||||
@ -363,7 +363,7 @@ error: `#[label(...)]` is not a valid attribute
|
|||||||
LL | #[label(typeck::label, foo("..."))]
|
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
|
--> $DIR/diagnostic-derive.rs:540:1
|
||||||
|
|
|
|
||||||
LL | / #[lint(typeck::ambiguous_lifetime_bound)]
|
LL | / #[lint(typeck::ambiguous_lifetime_bound)]
|
||||||
|
@ -508,3 +508,15 @@ enum AX {
|
|||||||
span: Span,
|
span: Span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(SessionSubdiagnostic)]
|
||||||
|
#[warn_(parser::add_paren)]
|
||||||
|
struct AY {
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SessionSubdiagnostic)]
|
||||||
|
#[warn_(parser::add_paren)]
|
||||||
|
struct AZ {
|
||||||
|
#[primary_span]
|
||||||
|
span: Span,
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user