macros: #[subdiagnostic(eager)]
Add support for `eager` argument to the `subdiagnostic` attribute which generates a call to `eager_subdiagnostic`. Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
parent
540b203bf9
commit
291a4736d9
4 changed files with 151 additions and 22 deletions
|
@ -10,27 +10,31 @@ use synstructure::Structure;
|
||||||
/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
|
/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
|
||||||
pub(crate) struct DiagnosticDerive<'a> {
|
pub(crate) struct DiagnosticDerive<'a> {
|
||||||
structure: Structure<'a>,
|
structure: Structure<'a>,
|
||||||
handler: syn::Ident,
|
|
||||||
builder: DiagnosticDeriveBuilder,
|
builder: DiagnosticDeriveBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> DiagnosticDerive<'a> {
|
impl<'a> DiagnosticDerive<'a> {
|
||||||
pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
|
pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::Diagnostic },
|
builder: DiagnosticDeriveBuilder {
|
||||||
handler,
|
diag,
|
||||||
|
kind: DiagnosticDeriveKind::Diagnostic { handler },
|
||||||
|
},
|
||||||
structure,
|
structure,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_tokens(self) -> TokenStream {
|
pub(crate) fn into_tokens(self) -> TokenStream {
|
||||||
let DiagnosticDerive { mut structure, handler, mut builder } = self;
|
let DiagnosticDerive { mut structure, mut builder } = self;
|
||||||
|
|
||||||
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
|
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
|
||||||
let preamble = builder.preamble(&variant);
|
let preamble = builder.preamble(&variant);
|
||||||
let body = builder.body(&variant);
|
let body = builder.body(&variant);
|
||||||
|
|
||||||
let diag = &builder.parent.diag;
|
let diag = &builder.parent.diag;
|
||||||
|
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else {
|
||||||
|
unreachable!()
|
||||||
|
};
|
||||||
let init = match builder.slug.value_ref() {
|
let init = match builder.slug.value_ref() {
|
||||||
None => {
|
None => {
|
||||||
span_err(builder.span, "diagnostic slug not specified")
|
span_err(builder.span, "diagnostic slug not specified")
|
||||||
|
@ -56,6 +60,7 @@ impl<'a> DiagnosticDerive<'a> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() };
|
||||||
structure.gen_impl(quote! {
|
structure.gen_impl(quote! {
|
||||||
gen impl<'__diagnostic_handler_sess, G>
|
gen impl<'__diagnostic_handler_sess, G>
|
||||||
rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>
|
rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>
|
||||||
|
|
|
@ -17,9 +17,9 @@ use syn::{
|
||||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
use synstructure::{BindingInfo, Structure, VariantInfo};
|
||||||
|
|
||||||
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
|
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
pub(crate) enum DiagnosticDeriveKind {
|
pub(crate) enum DiagnosticDeriveKind {
|
||||||
Diagnostic,
|
Diagnostic { handler: syn::Ident },
|
||||||
LintDiagnostic,
|
LintDiagnostic,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,18 +340,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||||
let diag = &self.parent.diag;
|
let diag = &self.parent.diag;
|
||||||
let meta = attr.parse_meta()?;
|
let meta = attr.parse_meta()?;
|
||||||
|
|
||||||
if let Meta::Path(_) = meta {
|
let ident = &attr.path.segments.last().unwrap().ident;
|
||||||
let ident = &attr.path.segments.last().unwrap().ident;
|
let name = ident.to_string();
|
||||||
let name = ident.to_string();
|
match (&meta, name.as_str()) {
|
||||||
let name = name.as_str();
|
// Don't need to do anything - by virtue of the attribute existing, the
|
||||||
match name {
|
// `set_arg` call will not be generated.
|
||||||
"skip_arg" => {
|
(Meta::Path(_), "skip_arg") => return Ok(quote! {}),
|
||||||
// Don't need to do anything - by virtue of the attribute existing, the
|
(Meta::Path(_), "primary_span") => {
|
||||||
// `set_arg` call will not be generated.
|
match self.parent.kind {
|
||||||
return Ok(quote! {});
|
DiagnosticDeriveKind::Diagnostic { .. } => {
|
||||||
}
|
|
||||||
"primary_span" => match self.parent.kind {
|
|
||||||
DiagnosticDeriveKind::Diagnostic => {
|
|
||||||
report_error_if_not_applied_to_span(attr, &info)?;
|
report_error_if_not_applied_to_span(attr, &info)?;
|
||||||
|
|
||||||
return Ok(quote! {
|
return Ok(quote! {
|
||||||
|
@ -363,10 +360,50 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
|
||||||
diag.help("the `primary_span` field attribute is not valid for lint diagnostics")
|
diag.help("the `primary_span` field attribute is not valid for lint diagnostics")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"subdiagnostic" => return Ok(quote! { #diag.subdiagnostic(#binding); }),
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
(Meta::Path(_), "subdiagnostic") => {
|
||||||
|
return Ok(quote! { #diag.subdiagnostic(#binding); });
|
||||||
|
}
|
||||||
|
(Meta::NameValue(_), "subdiagnostic") => {
|
||||||
|
throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
|
diag.help("`eager` is the only supported nested attribute for `subdiagnostic`")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
(Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => {
|
||||||
|
if nested.len() != 1 {
|
||||||
|
throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
|
diag.help(
|
||||||
|
"`eager` is the only supported nested attribute for `subdiagnostic`",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let handler = match &self.parent.kind {
|
||||||
|
DiagnosticDeriveKind::Diagnostic { handler } => handler,
|
||||||
|
DiagnosticDeriveKind::LintDiagnostic => {
|
||||||
|
throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
|
diag.help("eager subdiagnostics are not supported on lints")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let nested_attr = nested.first().expect("pop failed for single element list");
|
||||||
|
match nested_attr {
|
||||||
|
NestedMeta::Meta(meta @ Meta::Path(_))
|
||||||
|
if meta.path().segments.last().unwrap().ident.to_string().as_str()
|
||||||
|
== "eager" =>
|
||||||
|
{
|
||||||
|
return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); });
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
throw_invalid_nested_attr!(attr, nested_attr, |diag| {
|
||||||
|
diag.help("`eager` is the only supported nested attribute for `subdiagnostic`")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let (subdiag, slug) = self.parse_subdiag_attribute(attr)?;
|
let (subdiag, slug) = self.parse_subdiag_attribute(attr)?;
|
||||||
|
|
|
@ -678,3 +678,50 @@ enum ExampleEnum {
|
||||||
struct RawIdentDiagnosticArg {
|
struct RawIdentDiagnosticArg {
|
||||||
pub r#type: String,
|
pub r#type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticBad {
|
||||||
|
#[subdiagnostic(bad)]
|
||||||
|
//~^ ERROR `#[subdiagnostic(bad)]` is not a valid attribute
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticBadStr {
|
||||||
|
#[subdiagnostic = "bad"]
|
||||||
|
//~^ ERROR `#[subdiagnostic = ...]` is not a valid attribute
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticBadTwice {
|
||||||
|
#[subdiagnostic(bad, bad)]
|
||||||
|
//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticBadLitStr {
|
||||||
|
#[subdiagnostic("bad")]
|
||||||
|
//~^ ERROR `#[subdiagnostic("...")]` is not a valid attribute
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticEagerLint {
|
||||||
|
#[subdiagnostic(eager)]
|
||||||
|
//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(compiletest::example)]
|
||||||
|
struct SubdiagnosticEagerCorrect {
|
||||||
|
#[subdiagnostic(eager)]
|
||||||
|
note: Note,
|
||||||
|
}
|
||||||
|
|
|
@ -533,6 +533,46 @@ LL | #[label]
|
||||||
|
|
|
|
||||||
= help: `#[label]` and `#[suggestion]` can only be applied to fields
|
= help: `#[label]` and `#[suggestion]` can only be applied to fields
|
||||||
|
|
||||||
|
error: `#[subdiagnostic(bad)]` is not a valid attribute
|
||||||
|
--> $DIR/diagnostic-derive.rs:685:21
|
||||||
|
|
|
||||||
|
LL | #[subdiagnostic(bad)]
|
||||||
|
| ^^^
|
||||||
|
|
|
||||||
|
= help: `eager` is the only supported nested attribute for `subdiagnostic`
|
||||||
|
|
||||||
|
error: `#[subdiagnostic = ...]` is not a valid attribute
|
||||||
|
--> $DIR/diagnostic-derive.rs:693:5
|
||||||
|
|
|
||||||
|
LL | #[subdiagnostic = "bad"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: `eager` is the only supported nested attribute for `subdiagnostic`
|
||||||
|
|
||||||
|
error: `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
|
--> $DIR/diagnostic-derive.rs:701:5
|
||||||
|
|
|
||||||
|
LL | #[subdiagnostic(bad, bad)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: `eager` is the only supported nested attribute for `subdiagnostic`
|
||||||
|
|
||||||
|
error: `#[subdiagnostic("...")]` is not a valid attribute
|
||||||
|
--> $DIR/diagnostic-derive.rs:709:21
|
||||||
|
|
|
||||||
|
LL | #[subdiagnostic("bad")]
|
||||||
|
| ^^^^^
|
||||||
|
|
|
||||||
|
= help: `eager` is the only supported nested attribute for `subdiagnostic`
|
||||||
|
|
||||||
|
error: `#[subdiagnostic(...)]` is not a valid attribute
|
||||||
|
--> $DIR/diagnostic-derive.rs:717:5
|
||||||
|
|
|
||||||
|
LL | #[subdiagnostic(eager)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: eager subdiagnostics are not supported on lints
|
||||||
|
|
||||||
error: cannot find attribute `nonsense` in this scope
|
error: cannot find attribute `nonsense` in this scope
|
||||||
--> $DIR/diagnostic-derive.rs:55:3
|
--> $DIR/diagnostic-derive.rs:55:3
|
||||||
|
|
|
|
||||||
|
@ -607,7 +647,7 @@ LL | arg: impl IntoDiagnosticArg,
|
||||||
| ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg`
|
| ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg`
|
||||||
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: aborting due to 75 previous errors
|
error: aborting due to 80 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0277, E0425.
|
Some errors have detailed explanations: E0277, E0425.
|
||||||
For more information about an error, try `rustc --explain E0277`.
|
For more information about an error, try `rustc --explain E0277`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue