Skip to content

Commit c88bb6c

Browse files
committed
Allow nesting subdiagnostics
1 parent 5646b65 commit c88bb6c

File tree

3 files changed

+36
-12
lines changed

3 files changed

+36
-12
lines changed

compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

+25-12
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ impl SubdiagnosticDerive {
7171
span_field: None,
7272
applicability: None,
7373
has_suggestion_parts: false,
74+
has_subdiagnostic: false,
7475
is_enum,
7576
};
7677
builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
@@ -133,6 +134,10 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
133134
/// during finalization if still `false`.
134135
has_suggestion_parts: bool,
135136

137+
/// Set to true when a `#[subdiagnostic]` field is encountered, used to suppress the error
138+
/// emitted when no subdiagnostic kinds are specified on the variant itself.
139+
has_subdiagnostic: bool,
140+
136141
/// Set to true when this variant is an enum variant rather than just the body of a struct.
137142
is_enum: bool,
138143
}
@@ -373,6 +378,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
373378

374379
Ok(quote! {})
375380
}
381+
"subdiagnostic" => {
382+
let f = &self.parent.f;
383+
let diag = &self.parent.diag;
384+
let binding = &info.binding;
385+
self.has_subdiagnostic = true;
386+
Ok(quote! { #binding.add_to_diag_with(#diag, #f); })
387+
}
376388
_ => {
377389
let mut span_attrs = vec![];
378390
if kind_stats.has_multipart_suggestion {
@@ -480,18 +492,6 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
480492

481493
pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
482494
let kind_slugs = self.identify_kind()?;
483-
if kind_slugs.is_empty() {
484-
if self.is_enum {
485-
// It's okay for a variant to not be a subdiagnostic at all..
486-
return Ok(quote! {});
487-
} else {
488-
// ..but structs should always be _something_.
489-
throw_span_err!(
490-
self.variant.ast().ident.span().unwrap(),
491-
"subdiagnostic kind not specified"
492-
);
493-
}
494-
};
495495

496496
let kind_stats: KindsStatistics =
497497
kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect();
@@ -510,6 +510,19 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
510510
.map(|binding| self.generate_field_attr_code(binding, kind_stats))
511511
.collect();
512512

513+
if kind_slugs.is_empty() {
514+
if self.is_enum {
515+
// It's okay for a variant to not be a subdiagnostic at all..
516+
return Ok(quote! {});
517+
} else if !self.has_subdiagnostic {
518+
// ..but structs should always be _something_.
519+
throw_span_err!(
520+
self.variant.ast().ident.span().unwrap(),
521+
"subdiagnostic kind not specified"
522+
);
523+
}
524+
};
525+
513526
let span_field = self.span_field.value_ref();
514527

515528
let diag = &self.parent.diag;

compiler/rustc_macros/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ decl_derive!(
144144
help,
145145
note,
146146
warning,
147+
subdiagnostic,
147148
suggestion,
148149
suggestion_short,
149150
suggestion_hidden,

tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs

+10
Original file line numberDiff line numberDiff line change
@@ -827,3 +827,13 @@ struct PrimarySpanOnVec {
827827
//~| NOTE there must be exactly one primary span
828828
sub: Vec<Span>,
829829
}
830+
831+
#[derive(Subdiagnostic)]
832+
struct NestedParent {
833+
#[subdiagnostic]
834+
single_sub: A,
835+
#[subdiagnostic]
836+
option_sub: Option<A>,
837+
#[subdiagnostic]
838+
vec_sub: Vec<A>,
839+
}

0 commit comments

Comments
 (0)