Make derived SessionDiagnostics generic on diagnostic level
Deriving SessionDiagnostic on a type no longer forces that diagnostic to be one of warning, error, or fatal. The level is instead decided when the struct is passed to the respective Handler::emit_*() method.
This commit is contained in:
parent
91ad4e38f5
commit
a960f8304c
5 changed files with 108 additions and 185 deletions
|
@ -21,7 +21,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
|
|||
builder: DiagnosticDeriveBuilder {
|
||||
diag,
|
||||
fields: build_field_mapping(&structure),
|
||||
kind: None,
|
||||
kind: DiagnosticDeriveKind::SessionDiagnostic,
|
||||
code: None,
|
||||
slug: None,
|
||||
},
|
||||
|
@ -34,49 +34,31 @@ impl<'a> SessionDiagnosticDerive<'a> {
|
|||
let SessionDiagnosticDerive { mut structure, sess, mut builder } = self;
|
||||
|
||||
let ast = structure.ast();
|
||||
let (implementation, param_ty) = {
|
||||
let implementation = {
|
||||
if let syn::Data::Struct(..) = ast.data {
|
||||
let preamble = builder.preamble(&structure);
|
||||
let (attrs, args) = builder.body(&mut structure);
|
||||
|
||||
let span = ast.span().unwrap();
|
||||
let diag = &builder.diag;
|
||||
let init = match (builder.kind.value(), builder.slug.value()) {
|
||||
(None, _) => {
|
||||
span_err(span, "diagnostic kind not specified")
|
||||
.help("use the `#[error(...)]` attribute to create an error")
|
||||
.emit();
|
||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||
}
|
||||
(Some(kind), None) => {
|
||||
let init = match builder.slug.value() {
|
||||
None => {
|
||||
span_err(span, "diagnostic slug not specified")
|
||||
.help(&format!(
|
||||
"specify the slug as the first argument to the attribute, such as \
|
||||
`#[{}(typeck::example_error)]`",
|
||||
kind.descr()
|
||||
"specify the slug as the first argument to the `#[diag(...)]` attribute, \
|
||||
such as `#[diag(typeck::example_error)]`",
|
||||
))
|
||||
.emit();
|
||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||
}
|
||||
(Some(DiagnosticDeriveKind::Lint), _) => {
|
||||
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();
|
||||
}
|
||||
(Some(DiagnosticDeriveKind::Error), Some(slug)) => {
|
||||
Some(slug) => {
|
||||
quote! {
|
||||
let mut #diag = #sess.struct_err(rustc_errors::fluent::#slug);
|
||||
}
|
||||
}
|
||||
(Some(DiagnosticDeriveKind::Warn), Some(slug)) => {
|
||||
quote! {
|
||||
let mut #diag = #sess.struct_warn(rustc_errors::fluent::#slug);
|
||||
let mut #diag = #sess.struct_diagnostic(rustc_errors::fluent::#slug);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let implementation = quote! {
|
||||
quote! {
|
||||
#init
|
||||
#preamble
|
||||
match self {
|
||||
|
@ -86,18 +68,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
|
|||
#args
|
||||
}
|
||||
#diag
|
||||
};
|
||||
let param_ty = match builder.kind {
|
||||
Some((DiagnosticDeriveKind::Error, _)) => {
|
||||
quote! { rustc_errors::ErrorGuaranteed }
|
||||
}
|
||||
Some((DiagnosticDeriveKind::Lint | DiagnosticDeriveKind::Warn, _)) => {
|
||||
quote! { () }
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
(implementation, param_ty)
|
||||
}
|
||||
} else {
|
||||
span_err(
|
||||
ast.span().unwrap(),
|
||||
|
@ -105,20 +76,20 @@ impl<'a> SessionDiagnosticDerive<'a> {
|
|||
)
|
||||
.emit();
|
||||
|
||||
let implementation = DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||
let param_ty = quote! { rustc_errors::ErrorGuaranteed };
|
||||
(implementation, param_ty)
|
||||
DiagnosticDeriveError::ErrorHandled.to_compile_error()
|
||||
}
|
||||
};
|
||||
|
||||
structure.gen_impl(quote! {
|
||||
gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess, #param_ty>
|
||||
gen impl<'__session_diagnostic_sess, G>
|
||||
rustc_session::SessionDiagnostic<'__session_diagnostic_sess, G>
|
||||
for @Self
|
||||
where G: rustc_errors::EmissionGuarantee
|
||||
{
|
||||
fn into_diagnostic(
|
||||
self,
|
||||
#sess: &'__session_diagnostic_sess rustc_session::parse::ParseSess
|
||||
) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, #param_ty> {
|
||||
) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, G> {
|
||||
use rustc_errors::IntoDiagnosticArg;
|
||||
#implementation
|
||||
}
|
||||
|
@ -139,7 +110,7 @@ impl<'a> LintDiagnosticDerive<'a> {
|
|||
builder: DiagnosticDeriveBuilder {
|
||||
diag,
|
||||
fields: build_field_mapping(&structure),
|
||||
kind: None,
|
||||
kind: DiagnosticDeriveKind::LintDiagnostic,
|
||||
code: None,
|
||||
slug: None,
|
||||
},
|
||||
|
@ -158,30 +129,17 @@ impl<'a> LintDiagnosticDerive<'a> {
|
|||
|
||||
let diag = &builder.diag;
|
||||
let span = ast.span().unwrap();
|
||||
let init = match (builder.kind.value(), builder.slug.value()) {
|
||||
(None, _) => {
|
||||
span_err(span, "diagnostic kind not specified")
|
||||
.help("use the `#[error(...)]` attribute to create an error")
|
||||
.emit();
|
||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||
}
|
||||
(Some(kind), None) => {
|
||||
let init = match builder.slug.value() {
|
||||
None => {
|
||||
span_err(span, "diagnostic slug not specified")
|
||||
.help(&format!(
|
||||
"specify the slug as the first argument to the attribute, such as \
|
||||
`#[{}(typeck::example_error)]`",
|
||||
kind.descr()
|
||||
`#[diag(typeck::example_error)]`",
|
||||
))
|
||||
.emit();
|
||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||
}
|
||||
(Some(DiagnosticDeriveKind::Error | DiagnosticDeriveKind::Warn), _) => {
|
||||
span_err(span, "only `#[lint(..)]` is supported")
|
||||
.help("use the `#[lint(...)]` attribute to create a lint")
|
||||
.emit();
|
||||
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
|
||||
}
|
||||
(Some(DiagnosticDeriveKind::Lint), Some(slug)) => {
|
||||
Some(slug) => {
|
||||
quote! {
|
||||
let mut #diag = #diag.build(rustc_errors::fluent::#slug);
|
||||
}
|
||||
|
|
|
@ -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,15 +141,15 @@ 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 = matches!(name, "error" | "warning" | "lint" | "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 => {
|
||||
// paths: `#[help]`, `#[note]` and `#[warn_]`
|
||||
Meta::Path(_) if !is_diag => {
|
||||
let fn_name = if name == "warn_" {
|
||||
Ident::new("warn", attr.span())
|
||||
} else {
|
||||
|
@ -178,23 +163,32 @@ 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" | "warning" => {
|
||||
if self.kind == DiagnosticDeriveKind::LintDiagnostic {
|
||||
span_err(span, "only `#[lint(..)]` is supported")
|
||||
.help("use the `#[lint(...)]` attribute to create a lint")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
"lint" => {
|
||||
if self.kind == DiagnosticDeriveKind::SessionDiagnostic {
|
||||
span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported")
|
||||
.help("use the `#[error(...)]` attribute to create a error")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
"diag" | "help" | "note" | "warn_" => (),
|
||||
_ => 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 `warn_` 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",
|
||||
|
@ -203,16 +197,16 @@ impl DiagnosticDeriveBuilder {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -347,6 +341,7 @@ impl DiagnosticDeriveBuilder {
|
|||
}
|
||||
"primary_span" => {
|
||||
report_error_if_not_applied_to_span(attr, &info)?;
|
||||
|
||||
Ok(quote! {
|
||||
#diag.set_span(#binding);
|
||||
})
|
||||
|
|
|
@ -132,6 +132,7 @@ decl_derive!(
|
|||
warning,
|
||||
error,
|
||||
lint,
|
||||
diag,
|
||||
help,
|
||||
note,
|
||||
warn_,
|
||||
|
@ -151,6 +152,7 @@ decl_derive!(
|
|||
warning,
|
||||
error,
|
||||
lint,
|
||||
diag,
|
||||
help,
|
||||
note,
|
||||
warn_,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue