summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs110
1 files changed, 53 insertions, 57 deletions
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 6c9561925..2a4fe48a8 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -18,30 +18,15 @@ use syn::{
};
use synstructure::{BindingInfo, Structure};
-/// What kind of diagnostic is being derived - an error, a warning or a lint?
-#[derive(Copy, Clone)]
+/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
+#[derive(Copy, Clone, PartialEq, Eq)]
pub(crate) enum DiagnosticDeriveKind {
- /// `#[error(..)]`
- Error,
- /// `#[warn(..)]`
- Warn,
- /// `#[lint(..)]`
- Lint,
-}
-
-impl DiagnosticDeriveKind {
- /// Returns human-readable string corresponding to the kind.
- pub fn descr(&self) -> &'static str {
- match self {
- DiagnosticDeriveKind::Error => "error",
- DiagnosticDeriveKind::Warn => "warning",
- DiagnosticDeriveKind::Lint => "lint",
- }
- }
+ SessionDiagnostic,
+ LintDiagnostic,
}
/// Tracks persistent information required for building up individual calls to diagnostic methods
-/// for generated diagnostic derives - both `SessionDiagnostic` for errors/warnings and
+/// for generated diagnostic derives - both `SessionDiagnostic` for fatal/errors/warnings and
/// `LintDiagnostic` for lints.
pub(crate) struct DiagnosticDeriveBuilder {
/// The identifier to use for the generated `DiagnosticBuilder` instance.
@@ -51,8 +36,8 @@ pub(crate) struct DiagnosticDeriveBuilder {
/// derive builder.
pub fields: HashMap<String, TokenStream>,
- /// Kind of diagnostic requested via the struct attribute.
- pub kind: Option<(DiagnosticDeriveKind, proc_macro::Span)>,
+ /// Kind of diagnostic that should be derived.
+ pub kind: DiagnosticDeriveKind,
/// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
/// has the actual diagnostic message.
pub slug: Option<(Path, proc_macro::Span)>,
@@ -143,7 +128,7 @@ impl DiagnosticDeriveBuilder {
}
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
- /// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates
+ /// attributes like `#[diag(..)]`, such as the slug and error code. Generates
/// diagnostic builder calls for setting error code and creating note/help messages.
fn generate_structure_code_for_attr(
&mut self,
@@ -156,16 +141,16 @@ impl DiagnosticDeriveBuilder {
let name = name.as_str();
let meta = attr.parse_meta()?;
- let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_");
+ let is_diag = name == "diag";
let nested = match meta {
- // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
+ // Most attributes are lists, like `#[diag(..)]` for most cases or
// `#[help(..)]`/`#[note(..)]` when the user is specifying a alternative slug.
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_note_or_warn => {
- let fn_name = if name == "warn_" {
+ // paths: `#[help]`, `#[note]` and `#[warning]`
+ Meta::Path(_) if !is_diag => {
+ let fn_name = if name == "warning" {
Ident::new("warn", attr.span())
} else {
Ident::new(name, attr.span())
@@ -178,41 +163,42 @@ impl DiagnosticDeriveBuilder {
// Check the kind before doing any further processing so that there aren't misleading
// "no kind specified" errors if there are failures later.
match name {
- "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" | "warn_" => (),
+ "error" | "lint" => throw_invalid_attr!(attr, &meta, |diag| {
+ diag.help("`error` and `lint` have been replaced by `diag`")
+ }),
+ "warn_" => throw_invalid_attr!(attr, &meta, |diag| {
+ diag.help("`warn_` have been replaced by `warning`")
+ }),
+ "diag" | "help" | "note" | "warning" => (),
_ => throw_invalid_attr!(attr, &meta, |diag| {
- diag.help(
- "only `error`, `warning`, `help`, `note` and `warn_` are valid attributes",
- )
+ diag.help("only `diag`, `help`, `note` and `warning` are valid attributes")
}),
}
- // First nested element should always be the path, e.g. `#[error(typeck::invalid)]` or
+ // First nested element should always be the path, e.g. `#[diag(typeck::invalid)]` or
// `#[help(typeck::another_help)]`.
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_note_or_warn && nested_iter.next().is_some() {
+ if !is_diag && nested_iter.next().is_some() {
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
diag.help(
- "`help`, `note` and `warn_` struct attributes can only have one argument",
+ "`help`, `note` and `warning` struct attributes can only have one argument",
)
});
}
match nested_attr {
- 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); });
- }
NestedMeta::Meta(Meta::Path(path)) => {
- self.slug.set_once((path.clone(), span));
+ if is_diag {
+ self.slug.set_once((path.clone(), span));
+ } else {
+ let fn_name = proc_macro2::Ident::new(name, attr.span());
+ return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
+ }
}
NestedMeta::Meta(meta @ Meta::NameValue(_))
- if !is_help_note_or_warn
- && meta.path().segments.last().unwrap().ident == "code" =>
+ if is_diag && meta.path().segments.last().unwrap().ident == "code" =>
{
// don't error for valid follow-up attributes
}
@@ -253,7 +239,7 @@ impl DiagnosticDeriveBuilder {
}
}
- Ok(tokens.drain(..).collect())
+ Ok(tokens.into_iter().collect())
}
fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
@@ -346,21 +332,31 @@ impl DiagnosticDeriveBuilder {
Ok(quote! {})
}
"primary_span" => {
- report_error_if_not_applied_to_span(attr, &info)?;
- Ok(quote! {
- #diag.set_span(#binding);
- })
+ match self.kind {
+ DiagnosticDeriveKind::SessionDiagnostic => {
+ report_error_if_not_applied_to_span(attr, &info)?;
+
+ Ok(quote! {
+ #diag.set_span(#binding);
+ })
+ }
+ DiagnosticDeriveKind::LintDiagnostic => {
+ throw_invalid_attr!(attr, &meta, |diag| {
+ diag.help("the `primary_span` field attribute is not valid for lint diagnostics")
+ })
+ }
+ }
}
"label" => {
report_error_if_not_applied_to_span(attr, &info)?;
Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
}
- "note" | "help" | "warn_" => {
+ "note" | "help" | "warning" => {
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 }),
+ "warning" => (&warn_ident, parse_quote! { _subdiag::warn }),
_ => unreachable!(),
};
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
@@ -397,7 +393,7 @@ impl DiagnosticDeriveBuilder {
"suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
return self.generate_inner_field_code_suggestion(attr, info);
}
- "label" | "help" | "note" | "warn_" => (),
+ "label" | "help" | "note" | "warning" => (),
_ => throw_invalid_attr!(attr, &meta, |diag| {
diag.help(
"only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \
@@ -429,14 +425,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)),
- // `warn_` must be special-cased because the attribute `warn` already has meaning and
+ // `warning` 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
+ "warning" 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) => {
+ "warning" 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 `()`")?,
+ "note" | "help" | "warning" => report_type_error(attr, "`Span` or `()`")?,
_ => unreachable!(),
}
}