Allow nesting subdiagnostics

This commit is contained in:
Xiretza 2024-04-20 12:52:05 +00:00
parent 5646b65cf5
commit c88bb6c011
3 changed files with 36 additions and 12 deletions

View File

@ -71,6 +71,7 @@ impl SubdiagnosticDerive {
span_field: None, span_field: None,
applicability: None, applicability: None,
has_suggestion_parts: false, has_suggestion_parts: false,
has_subdiagnostic: false,
is_enum, is_enum,
}; };
builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
@ -133,6 +134,10 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
/// during finalization if still `false`. /// during finalization if still `false`.
has_suggestion_parts: bool, has_suggestion_parts: bool,
/// Set to true when a `#[subdiagnostic]` field is encountered, used to suppress the error
/// emitted when no subdiagnostic kinds are specified on the variant itself.
has_subdiagnostic: bool,
/// Set to true when this variant is an enum variant rather than just the body of a struct. /// Set to true when this variant is an enum variant rather than just the body of a struct.
is_enum: bool, is_enum: bool,
} }
@ -373,6 +378,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
Ok(quote! {}) Ok(quote! {})
} }
"subdiagnostic" => {
let f = &self.parent.f;
let diag = &self.parent.diag;
let binding = &info.binding;
self.has_subdiagnostic = true;
Ok(quote! { #binding.add_to_diag_with(#diag, #f); })
}
_ => { _ => {
let mut span_attrs = vec![]; let mut span_attrs = vec![];
if kind_stats.has_multipart_suggestion { if kind_stats.has_multipart_suggestion {
@ -480,18 +492,6 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> { pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
let kind_slugs = self.identify_kind()?; let kind_slugs = self.identify_kind()?;
if kind_slugs.is_empty() {
if self.is_enum {
// It's okay for a variant to not be a subdiagnostic at all..
return Ok(quote! {});
} else {
// ..but structs should always be _something_.
throw_span_err!(
self.variant.ast().ident.span().unwrap(),
"subdiagnostic kind not specified"
);
}
};
let kind_stats: KindsStatistics = let kind_stats: KindsStatistics =
kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect(); kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect();
@ -510,6 +510,19 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
.map(|binding| self.generate_field_attr_code(binding, kind_stats)) .map(|binding| self.generate_field_attr_code(binding, kind_stats))
.collect(); .collect();
if kind_slugs.is_empty() {
if self.is_enum {
// It's okay for a variant to not be a subdiagnostic at all..
return Ok(quote! {});
} else if !self.has_subdiagnostic {
// ..but structs should always be _something_.
throw_span_err!(
self.variant.ast().ident.span().unwrap(),
"subdiagnostic kind not specified"
);
}
};
let span_field = self.span_field.value_ref(); let span_field = self.span_field.value_ref();
let diag = &self.parent.diag; let diag = &self.parent.diag;

View File

@ -144,6 +144,7 @@ decl_derive!(
help, help,
note, note,
warning, warning,
subdiagnostic,
suggestion, suggestion,
suggestion_short, suggestion_short,
suggestion_hidden, suggestion_hidden,

View File

@ -827,3 +827,13 @@ struct PrimarySpanOnVec {
//~| NOTE there must be exactly one primary span //~| NOTE there must be exactly one primary span
sub: Vec<Span>, sub: Vec<Span>,
} }
#[derive(Subdiagnostic)]
struct NestedParent {
#[subdiagnostic]
single_sub: A,
#[subdiagnostic]
option_sub: Option<A>,
#[subdiagnostic]
vec_sub: Vec<A>,
}