Skip to content

Commit 25cfe1d

Browse files
committed
macros: #[subdiagnostic(eager)]
Add support for `eager` argument to the `subdiagnostic` attribute which generates a call to `eager_subdiagnostic`. Signed-off-by: David Wood <[email protected]>
1 parent 24f60a7 commit 25cfe1d

File tree

4 files changed

+151
-22
lines changed

4 files changed

+151
-22
lines changed

compiler/rustc_macros/src/diagnostics/diagnostic.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,31 @@ use synstructure::Structure;
1010
/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
1111
pub(crate) struct DiagnosticDerive<'a> {
1212
structure: Structure<'a>,
13-
handler: syn::Ident,
1413
builder: DiagnosticDeriveBuilder,
1514
}
1615

1716
impl<'a> DiagnosticDerive<'a> {
1817
pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self {
1918
Self {
20-
builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::Diagnostic },
21-
handler,
19+
builder: DiagnosticDeriveBuilder {
20+
diag,
21+
kind: DiagnosticDeriveKind::Diagnostic { handler },
22+
},
2223
structure,
2324
}
2425
}
2526

2627
pub(crate) fn into_tokens(self) -> TokenStream {
27-
let DiagnosticDerive { mut structure, handler, mut builder } = self;
28+
let DiagnosticDerive { mut structure, mut builder } = self;
2829

2930
let implementation = builder.each_variant(&mut structure, |mut builder, variant| {
3031
let preamble = builder.preamble(&variant);
3132
let body = builder.body(&variant);
3233

3334
let diag = &builder.parent.diag;
35+
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else {
36+
unreachable!()
37+
};
3438
let init = match builder.slug.value_ref() {
3539
None => {
3640
span_err(builder.span, "diagnostic slug not specified")
@@ -56,6 +60,7 @@ impl<'a> DiagnosticDerive<'a> {
5660
}
5761
});
5862

63+
let DiagnosticDeriveKind::Diagnostic { handler } = &builder.kind else { unreachable!() };
5964
structure.gen_impl(quote! {
6065
gen impl<'__diagnostic_handler_sess, G>
6166
rustc_errors::IntoDiagnostic<'__diagnostic_handler_sess, G>

compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs

+54-17
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ use syn::{
1717
use synstructure::{BindingInfo, Structure, VariantInfo};
1818

1919
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
20-
#[derive(Copy, Clone, PartialEq, Eq)]
20+
#[derive(Clone, PartialEq, Eq)]
2121
pub(crate) enum DiagnosticDeriveKind {
22-
Diagnostic,
22+
Diagnostic { handler: syn::Ident },
2323
LintDiagnostic,
2424
}
2525

@@ -340,18 +340,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
340340
let diag = &self.parent.diag;
341341
let meta = attr.parse_meta()?;
342342

343-
if let Meta::Path(_) = meta {
344-
let ident = &attr.path.segments.last().unwrap().ident;
345-
let name = ident.to_string();
346-
let name = name.as_str();
347-
match name {
348-
"skip_arg" => {
349-
// Don't need to do anything - by virtue of the attribute existing, the
350-
// `set_arg` call will not be generated.
351-
return Ok(quote! {});
352-
}
353-
"primary_span" => match self.parent.kind {
354-
DiagnosticDeriveKind::Diagnostic => {
343+
let ident = &attr.path.segments.last().unwrap().ident;
344+
let name = ident.to_string();
345+
match (&meta, name.as_str()) {
346+
// Don't need to do anything - by virtue of the attribute existing, the
347+
// `set_arg` call will not be generated.
348+
(Meta::Path(_), "skip_arg") => return Ok(quote! {}),
349+
(Meta::Path(_), "primary_span") => {
350+
match self.parent.kind {
351+
DiagnosticDeriveKind::Diagnostic { .. } => {
355352
report_error_if_not_applied_to_span(attr, &info)?;
356353

357354
return Ok(quote! {
@@ -363,10 +360,50 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
363360
diag.help("the `primary_span` field attribute is not valid for lint diagnostics")
364361
})
365362
}
366-
},
367-
"subdiagnostic" => return Ok(quote! { #diag.subdiagnostic(#binding); }),
368-
_ => {}
363+
}
364+
}
365+
(Meta::Path(_), "subdiagnostic") => {
366+
return Ok(quote! { #diag.subdiagnostic(#binding); });
367+
}
368+
(Meta::NameValue(_), "subdiagnostic") => {
369+
throw_invalid_attr!(attr, &meta, |diag| {
370+
diag.help("`eager` is the only supported nested attribute for `subdiagnostic`")
371+
})
372+
}
373+
(Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => {
374+
if nested.len() != 1 {
375+
throw_invalid_attr!(attr, &meta, |diag| {
376+
diag.help(
377+
"`eager` is the only supported nested attribute for `subdiagnostic`",
378+
)
379+
})
380+
}
381+
382+
let handler = match &self.parent.kind {
383+
DiagnosticDeriveKind::Diagnostic { handler } => handler,
384+
DiagnosticDeriveKind::LintDiagnostic => {
385+
throw_invalid_attr!(attr, &meta, |diag| {
386+
diag.help("eager subdiagnostics are not supported on lints")
387+
})
388+
}
389+
};
390+
391+
let nested_attr = nested.first().expect("pop failed for single element list");
392+
match nested_attr {
393+
NestedMeta::Meta(meta @ Meta::Path(_))
394+
if meta.path().segments.last().unwrap().ident.to_string().as_str()
395+
== "eager" =>
396+
{
397+
return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); });
398+
}
399+
_ => {
400+
throw_invalid_nested_attr!(attr, nested_attr, |diag| {
401+
diag.help("`eager` is the only supported nested attribute for `subdiagnostic`")
402+
})
403+
}
404+
}
369405
}
406+
_ => (),
370407
}
371408

372409
let (subdiag, slug) = self.parse_subdiag_attribute(attr)?;

src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs

+47
Original file line numberDiff line numberDiff line change
@@ -678,3 +678,50 @@ enum ExampleEnum {
678678
struct RawIdentDiagnosticArg {
679679
pub r#type: String,
680680
}
681+
682+
#[derive(Diagnostic)]
683+
#[diag(compiletest::example)]
684+
struct SubdiagnosticBad {
685+
#[subdiagnostic(bad)]
686+
//~^ ERROR `#[subdiagnostic(bad)]` is not a valid attribute
687+
note: Note,
688+
}
689+
690+
#[derive(Diagnostic)]
691+
#[diag(compiletest::example)]
692+
struct SubdiagnosticBadStr {
693+
#[subdiagnostic = "bad"]
694+
//~^ ERROR `#[subdiagnostic = ...]` is not a valid attribute
695+
note: Note,
696+
}
697+
698+
#[derive(Diagnostic)]
699+
#[diag(compiletest::example)]
700+
struct SubdiagnosticBadTwice {
701+
#[subdiagnostic(bad, bad)]
702+
//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute
703+
note: Note,
704+
}
705+
706+
#[derive(Diagnostic)]
707+
#[diag(compiletest::example)]
708+
struct SubdiagnosticBadLitStr {
709+
#[subdiagnostic("bad")]
710+
//~^ ERROR `#[subdiagnostic("...")]` is not a valid attribute
711+
note: Note,
712+
}
713+
714+
#[derive(LintDiagnostic)]
715+
#[diag(compiletest::example)]
716+
struct SubdiagnosticEagerLint {
717+
#[subdiagnostic(eager)]
718+
//~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute
719+
note: Note,
720+
}
721+
722+
#[derive(Diagnostic)]
723+
#[diag(compiletest::example)]
724+
struct SubdiagnosticEagerCorrect {
725+
#[subdiagnostic(eager)]
726+
note: Note,
727+
}

src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr

+41-1
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,46 @@ LL | #[label]
533533
|
534534
= help: `#[label]` and `#[suggestion]` can only be applied to fields
535535

536+
error: `#[subdiagnostic(bad)]` is not a valid attribute
537+
--> $DIR/diagnostic-derive.rs:685:21
538+
|
539+
LL | #[subdiagnostic(bad)]
540+
| ^^^
541+
|
542+
= help: `eager` is the only supported nested attribute for `subdiagnostic`
543+
544+
error: `#[subdiagnostic = ...]` is not a valid attribute
545+
--> $DIR/diagnostic-derive.rs:693:5
546+
|
547+
LL | #[subdiagnostic = "bad"]
548+
| ^^^^^^^^^^^^^^^^^^^^^^^^
549+
|
550+
= help: `eager` is the only supported nested attribute for `subdiagnostic`
551+
552+
error: `#[subdiagnostic(...)]` is not a valid attribute
553+
--> $DIR/diagnostic-derive.rs:701:5
554+
|
555+
LL | #[subdiagnostic(bad, bad)]
556+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
557+
|
558+
= help: `eager` is the only supported nested attribute for `subdiagnostic`
559+
560+
error: `#[subdiagnostic("...")]` is not a valid attribute
561+
--> $DIR/diagnostic-derive.rs:709:21
562+
|
563+
LL | #[subdiagnostic("bad")]
564+
| ^^^^^
565+
|
566+
= help: `eager` is the only supported nested attribute for `subdiagnostic`
567+
568+
error: `#[subdiagnostic(...)]` is not a valid attribute
569+
--> $DIR/diagnostic-derive.rs:717:5
570+
|
571+
LL | #[subdiagnostic(eager)]
572+
| ^^^^^^^^^^^^^^^^^^^^^^^
573+
|
574+
= help: eager subdiagnostics are not supported on lints
575+
536576
error: cannot find attribute `nonsense` in this scope
537577
--> $DIR/diagnostic-derive.rs:55:3
538578
|
@@ -607,7 +647,7 @@ LL | arg: impl IntoDiagnosticArg,
607647
| ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg`
608648
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
609649

610-
error: aborting due to 75 previous errors
650+
error: aborting due to 80 previous errors
611651

612652
Some errors have detailed explanations: E0277, E0425.
613653
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)