From ef434f04e9629132f59d3f90fac0d4cc797166e8 Mon Sep 17 00:00:00 2001
From: clubby789 <jamie@hill-daniel.co.uk>
Date: Mon, 27 Feb 2023 12:54:11 +0000
Subject: [PATCH] Allow using `bool` for optional diagnostics

---
 .../src/diagnostics/diagnostic_builder.rs     | 13 +--
 .../rustc_macros/src/diagnostics/utils.rs     | 10 +++
 .../session-diagnostic/diagnostic-derive.rs   | 13 +++
 .../diagnostic-derive.stderr                  | 86 ++++++++++---------
 4 files changed, 77 insertions(+), 45 deletions(-)

diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 46068f8c868cd..7fbe3bc2888c3 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -6,8 +6,8 @@ use crate::diagnostics::error::{
 };
 use crate::diagnostics::utils::{
     build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error,
-    should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo, FieldInnerTy, FieldMap,
-    HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
+    should_generate_set_arg, type_is_bool, type_is_unit, type_matches_path, FieldInfo,
+    FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
 };
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote};
@@ -414,12 +414,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
                 Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug))
             }
             SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => {
-                if type_matches_path(info.ty.inner_type(), &["rustc_span", "Span"]) {
+                let inner = info.ty.inner_type();
+                if type_matches_path(inner, &["rustc_span", "Span"]) {
                     Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug))
-                } else if type_is_unit(info.ty.inner_type()) {
+                } else if type_is_unit(inner)
+                    || (matches!(info.ty, FieldInnerTy::Plain(_)) && type_is_bool(inner))
+                {
                     Ok(self.add_subdiagnostic(&fn_ident, slug))
                 } else {
-                    report_type_error(attr, "`Span` or `()`")?
+                    report_type_error(attr, "`Span`, `bool` or `()`")?
                 }
             }
             SubdiagnosticKind::Suggestion {
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index 27b8f676f3fbb..65bb154d7f39d 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -50,6 +50,11 @@ pub(crate) fn type_is_unit(ty: &Type) -> bool {
     if let Type::Tuple(TypeTuple { elems, .. }) = ty { elems.is_empty() } else { false }
 }
 
+/// Checks whether the type `ty` is `bool`.
+pub(crate) fn type_is_bool(ty: &Type) -> bool {
+    type_matches_path(ty, &["bool"])
+}
+
 /// Reports a type error for field with `attr`.
 pub(crate) fn report_type_error(
     attr: &Attribute,
@@ -192,6 +197,11 @@ impl<'ty> FieldInnerTy<'ty> {
                     #inner
                 }
             },
+            FieldInnerTy::Plain(t) if type_is_bool(t) => quote! {
+                if #binding {
+                    #inner
+                }
+            },
             FieldInnerTy::Plain(..) => quote! { #inner },
         }
     }
diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
index 01e6434b07567..3151c712566af 100644
--- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
+++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
@@ -513,6 +513,19 @@ struct OptUnitField {
     bar: Option<()>,
 }
 
+#[derive(Diagnostic)]
+#[diag(no_crate_example)]
+struct BoolField {
+    #[primary_span]
+    spans: Span,
+    #[help]
+    foo: bool,
+    #[help(no_crate_help)]
+    //~^ ERROR the `#[help(...)]` attribute can only be applied to fields of type `Span`, `bool` or `()`
+    // only allow plain 'bool' fields
+    bar: Option<bool>,
+}
+
 #[derive(Diagnostic)]
 #[diag(no_crate_example, code = "E0123")]
 struct LabelWithTrailingPath {
diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
index fc0cd8419e440..513b675e5dd41 100644
--- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
+++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
@@ -352,8 +352,14 @@ error: invalid applicability
 LL |     #[suggestion(no_crate_suggestion, code = "...", applicability = "batman")]
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^
 
+error: the `#[help(...)]` attribute can only be applied to fields of type `Span`, `bool` or `()`
+  --> $DIR/diagnostic-derive.rs:523:5
+   |
+LL |     #[help(no_crate_help)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
 error: `#[label(foo)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:519:29
+  --> $DIR/diagnostic-derive.rs:532:29
    |
 LL |     #[label(no_crate_label, foo)]
    |                             ^^^
@@ -361,19 +367,19 @@ LL |     #[label(no_crate_label, foo)]
    = help: a diagnostic slug must be the first argument to the attribute
 
 error: `#[label(foo = ...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:527:29
+  --> $DIR/diagnostic-derive.rs:540:29
    |
 LL |     #[label(no_crate_label, foo = "...")]
    |                             ^^^^^^^^^^^
 
 error: `#[label(foo(...))]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:535:29
+  --> $DIR/diagnostic-derive.rs:548:29
    |
 LL |     #[label(no_crate_label, foo("..."))]
    |                             ^^^^^^^^^^
 
 error: `#[primary_span]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:547:5
+  --> $DIR/diagnostic-derive.rs:560:5
    |
 LL |     #[primary_span]
    |     ^^^^^^^^^^^^^^^
@@ -381,13 +387,13 @@ LL |     #[primary_span]
    = help: the `primary_span` field attribute is not valid for lint diagnostics
 
 error: `#[error(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:567:1
+  --> $DIR/diagnostic-derive.rs:580:1
    |
 LL | #[error(no_crate_example, code = "E0123")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: diagnostic slug not specified
-  --> $DIR/diagnostic-derive.rs:567:1
+  --> $DIR/diagnostic-derive.rs:580:1
    |
 LL | / #[error(no_crate_example, code = "E0123")]
 LL | |
@@ -399,13 +405,13 @@ LL | | struct ErrorAttribute {}
    = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
 
 error: `#[warn_(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:574:1
+  --> $DIR/diagnostic-derive.rs:587:1
    |
 LL | #[warn_(no_crate_example, code = "E0123")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: diagnostic slug not specified
-  --> $DIR/diagnostic-derive.rs:574:1
+  --> $DIR/diagnostic-derive.rs:587:1
    |
 LL | / #[warn_(no_crate_example, code = "E0123")]
 LL | |
@@ -417,13 +423,13 @@ LL | | struct WarnAttribute {}
    = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
 
 error: `#[lint(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:581:1
+  --> $DIR/diagnostic-derive.rs:594:1
    |
 LL | #[lint(no_crate_example, code = "E0123")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: diagnostic slug not specified
-  --> $DIR/diagnostic-derive.rs:581:1
+  --> $DIR/diagnostic-derive.rs:594:1
    |
 LL | / #[lint(no_crate_example, code = "E0123")]
 LL | |
@@ -435,19 +441,19 @@ LL | | struct LintAttributeOnSessionDiag {}
    = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
 
 error: `#[lint(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:588:1
+  --> $DIR/diagnostic-derive.rs:601:1
    |
 LL | #[lint(no_crate_example, code = "E0123")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `#[lint(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:588:1
+  --> $DIR/diagnostic-derive.rs:601:1
    |
 LL | #[lint(no_crate_example, code = "E0123")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: diagnostic slug not specified
-  --> $DIR/diagnostic-derive.rs:588:1
+  --> $DIR/diagnostic-derive.rs:601:1
    |
 LL | / #[lint(no_crate_example, code = "E0123")]
 LL | |
@@ -460,19 +466,19 @@ LL | | struct LintAttributeOnLintDiag {}
    = help: specify the slug as the first argument to the attribute, such as `#[diag(compiletest_example)]`
 
 error: specified multiple times
-  --> $DIR/diagnostic-derive.rs:598:53
+  --> $DIR/diagnostic-derive.rs:611:53
    |
 LL |     #[suggestion(no_crate_suggestion, code = "...", code = ",,,")]
    |                                                     ^^^^^^^^^^^^
    |
 note: previously specified here
-  --> $DIR/diagnostic-derive.rs:598:39
+  --> $DIR/diagnostic-derive.rs:611:39
    |
 LL |     #[suggestion(no_crate_suggestion, code = "...", code = ",,,")]
    |                                       ^^^^^^^^^^^^
 
 error: wrong types for suggestion
-  --> $DIR/diagnostic-derive.rs:607:24
+  --> $DIR/diagnostic-derive.rs:620:24
    |
 LL |     suggestion: (Span, usize),
    |                        ^^^^^
@@ -480,7 +486,7 @@ LL |     suggestion: (Span, usize),
    = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`
 
 error: wrong types for suggestion
-  --> $DIR/diagnostic-derive.rs:615:17
+  --> $DIR/diagnostic-derive.rs:628:17
    |
 LL |     suggestion: (Span,),
    |                 ^^^^^^^
@@ -488,13 +494,13 @@ LL |     suggestion: (Span,),
    = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`
 
 error: suggestion without `code = "..."`
-  --> $DIR/diagnostic-derive.rs:622:5
+  --> $DIR/diagnostic-derive.rs:635:5
    |
 LL |     #[suggestion(no_crate_suggestion)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `#[multipart_suggestion(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:629:1
+  --> $DIR/diagnostic-derive.rs:642:1
    |
 LL | #[multipart_suggestion(no_crate_suggestion)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -502,7 +508,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)]
    = help: consider creating a `Subdiagnostic` instead
 
 error: `#[multipart_suggestion(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:632:1
+  --> $DIR/diagnostic-derive.rs:645:1
    |
 LL | #[multipart_suggestion()]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -510,7 +516,7 @@ LL | #[multipart_suggestion()]
    = help: consider creating a `Subdiagnostic` instead
 
 error: `#[multipart_suggestion(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:636:5
+  --> $DIR/diagnostic-derive.rs:649:5
    |
 LL |     #[multipart_suggestion(no_crate_suggestion)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -518,7 +524,7 @@ LL |     #[multipart_suggestion(no_crate_suggestion)]
    = help: consider creating a `Subdiagnostic` instead
 
 error: `#[suggestion(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:644:1
+  --> $DIR/diagnostic-derive.rs:657:1
    |
 LL | #[suggestion(no_crate_suggestion, code = "...")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -526,7 +532,7 @@ LL | #[suggestion(no_crate_suggestion, code = "...")]
    = help: `#[label]` and `#[suggestion]` can only be applied to fields
 
 error: `#[label]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:653:1
+  --> $DIR/diagnostic-derive.rs:666:1
    |
 LL | #[label]
    | ^^^^^^^^
@@ -534,7 +540,7 @@ LL | #[label]
    = help: `#[label]` and `#[suggestion]` can only be applied to fields
 
 error: `#[subdiagnostic(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:687:5
+  --> $DIR/diagnostic-derive.rs:700:5
    |
 LL |     #[subdiagnostic(bad)]
    |     ^^^^^^^^^^^^^^^^^^^^^
@@ -542,13 +548,13 @@ LL |     #[subdiagnostic(bad)]
    = help: `eager` is the only supported nested attribute for `subdiagnostic`
 
 error: `#[subdiagnostic = ...]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:695:5
+  --> $DIR/diagnostic-derive.rs:708:5
    |
 LL |     #[subdiagnostic = "bad"]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `#[subdiagnostic(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:703:5
+  --> $DIR/diagnostic-derive.rs:716:5
    |
 LL |     #[subdiagnostic(bad, bad)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -556,7 +562,7 @@ 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:711:5
+  --> $DIR/diagnostic-derive.rs:724:5
    |
 LL |     #[subdiagnostic("bad")]
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -564,7 +570,7 @@ LL |     #[subdiagnostic("bad")]
    = help: `eager` is the only supported nested attribute for `subdiagnostic`
 
 error: `#[subdiagnostic(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:719:5
+  --> $DIR/diagnostic-derive.rs:732:5
    |
 LL |     #[subdiagnostic(eager)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -572,25 +578,25 @@ LL |     #[subdiagnostic(eager)]
    = help: eager subdiagnostics are not supported on lints
 
 error: expected at least one string literal for `code(...)`
-  --> $DIR/diagnostic-derive.rs:777:18
+  --> $DIR/diagnostic-derive.rs:790:18
    |
 LL |     #[suggestion(code())]
    |                  ^^^^^^
 
 error: `code(...)` must contain only string literals
-  --> $DIR/diagnostic-derive.rs:785:23
+  --> $DIR/diagnostic-derive.rs:798:23
    |
 LL |     #[suggestion(code(foo))]
    |                       ^^^
 
 error: `code = "..."`/`code(...)` must contain only string literals
-  --> $DIR/diagnostic-derive.rs:793:18
+  --> $DIR/diagnostic-derive.rs:806:18
    |
 LL |     #[suggestion(code = 3)]
    |                  ^^^^^^^^
 
 error: `#[suggestion(...)]` is not a valid attribute
-  --> $DIR/diagnostic-derive.rs:808:5
+  --> $DIR/diagnostic-derive.rs:821:5
    |
 LL |     #[suggestion(no_crate_suggestion, code = "")]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -612,43 +618,43 @@ LL |     #[nonsense]
    |       ^^^^^^^^
 
 error: cannot find attribute `error` in this scope
-  --> $DIR/diagnostic-derive.rs:567:3
+  --> $DIR/diagnostic-derive.rs:580:3
    |
 LL | #[error(no_crate_example, code = "E0123")]
    |   ^^^^^
 
 error: cannot find attribute `warn_` in this scope
-  --> $DIR/diagnostic-derive.rs:574:3
+  --> $DIR/diagnostic-derive.rs:587:3
    |
 LL | #[warn_(no_crate_example, code = "E0123")]
    |   ^^^^^ help: a built-in attribute with a similar name exists: `warn`
 
 error: cannot find attribute `lint` in this scope
-  --> $DIR/diagnostic-derive.rs:581:3
+  --> $DIR/diagnostic-derive.rs:594:3
    |
 LL | #[lint(no_crate_example, code = "E0123")]
    |   ^^^^ help: a built-in attribute with a similar name exists: `link`
 
 error: cannot find attribute `lint` in this scope
-  --> $DIR/diagnostic-derive.rs:588:3
+  --> $DIR/diagnostic-derive.rs:601:3
    |
 LL | #[lint(no_crate_example, code = "E0123")]
    |   ^^^^ help: a built-in attribute with a similar name exists: `link`
 
 error: cannot find attribute `multipart_suggestion` in this scope
-  --> $DIR/diagnostic-derive.rs:629:3
+  --> $DIR/diagnostic-derive.rs:642:3
    |
 LL | #[multipart_suggestion(no_crate_suggestion)]
    |   ^^^^^^^^^^^^^^^^^^^^
 
 error: cannot find attribute `multipart_suggestion` in this scope
-  --> $DIR/diagnostic-derive.rs:632:3
+  --> $DIR/diagnostic-derive.rs:645:3
    |
 LL | #[multipart_suggestion()]
    |   ^^^^^^^^^^^^^^^^^^^^
 
 error: cannot find attribute `multipart_suggestion` in this scope
-  --> $DIR/diagnostic-derive.rs:636:7
+  --> $DIR/diagnostic-derive.rs:649:7
    |
 LL |     #[multipart_suggestion(no_crate_suggestion)]
    |       ^^^^^^^^^^^^^^^^^^^^
@@ -670,7 +676,7 @@ note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg`
   --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC
    = note: this error originates in the derive macro `Diagnostic` which comes from the expansion of the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 84 previous errors
+error: aborting due to 85 previous errors
 
 Some errors have detailed explanations: E0277, E0425.
 For more information about an error, try `rustc --explain E0277`.