about summary refs log tree commit diff
path: root/compiler/rustc_macros/src
diff options
context:
space:
mode:
authorDavid Wood <david.wood@huawei.com>2022-05-07 06:02:11 +0100
committerDavid Wood <david.wood@huawei.com>2022-05-12 07:21:51 +0100
commit7b7061dd898b5a9c06477941e2d0840e27b9c67b (patch)
tree9a1134b917032ed2345f64f1ba21daf59266d223 /compiler/rustc_macros/src
parent1d2ea98cff1fde1d8b9e83a0eb639b8ec2cb82d8 (diff)
downloadrust-7b7061dd898b5a9c06477941e2d0840e27b9c67b.tar.gz
rust-7b7061dd898b5a9c06477941e2d0840e27b9c67b.zip
macros: spanless subdiagnostics from `()` fields
Type attributes could previously be used to support spanless
subdiagnostics but these couldn't easily be made optional in the same
way that spanned subdiagnostics could by using a field attribute on a
field with an `Option<Span>` type. Spanless subdiagnostics can now be
specified on fields with `()` type or `Option<()>` type.

Signed-off-by: David Wood <david.wood@huawei.com>
Diffstat (limited to 'compiler/rustc_macros/src')
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs53
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs54
-rw-r--r--compiler/rustc_macros/src/lib.rs1
3 files changed, 75 insertions, 33 deletions
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index 83fc7bcde8a..6788ce37eae 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -5,10 +5,10 @@ use crate::diagnostics::error::{
     SessionDiagnosticDeriveError,
 };
 use crate::diagnostics::utils::{
-    report_error_if_not_applied_to_span, type_matches_path, Applicability, FieldInfo, FieldInnerTy,
-    HasFieldMap, SetOnce,
+    report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
+    Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
 };
-use proc_macro2::TokenStream;
+use proc_macro2::{Ident, TokenStream};
 use quote::{format_ident, quote};
 use std::collections::HashMap;
 use std::str::FromStr;
@@ -388,7 +388,8 @@ impl SessionDiagnosticDeriveBuilder {
     ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
         let diag = &self.diag;
 
-        let name = attr.path.segments.last().unwrap().ident.to_string();
+        let ident = &attr.path.segments.last().unwrap().ident;
+        let name = ident.to_string();
         let name = name.as_str();
 
         let meta = attr.parse_meta()?;
@@ -405,9 +406,18 @@ impl SessionDiagnosticDeriveBuilder {
                         #diag.set_span(#binding);
                     })
                 }
-                "label" | "note" | "help" => {
+                "label" => {
                     report_error_if_not_applied_to_span(attr, &info)?;
-                    Ok(self.add_subdiagnostic(binding, name, name))
+                    Ok(self.add_spanned_subdiagnostic(binding, ident, name))
+                }
+                "note" | "help" => {
+                    if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
+                        Ok(self.add_spanned_subdiagnostic(binding, ident, name))
+                    } else if type_is_unit(&info.ty) {
+                        Ok(self.add_subdiagnostic(ident, name))
+                    } else {
+                        report_type_error(attr, "`Span` or `()`")?;
+                    }
                 }
                 "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }),
                 _ => throw_invalid_attr!(attr, &meta, |diag| {
@@ -416,9 +426,18 @@ impl SessionDiagnosticDeriveBuilder {
                 }),
             },
             Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
-                "label" | "note" | "help" => {
+                "label" => {
                     report_error_if_not_applied_to_span(attr, &info)?;
-                    Ok(self.add_subdiagnostic(binding, name, &s.value()))
+                    Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value()))
+                }
+                "note" | "help" => {
+                    if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
+                        Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value()))
+                    } else if type_is_unit(&info.ty) {
+                        Ok(self.add_subdiagnostic(ident, &s.value()))
+                    } else {
+                        report_type_error(attr, "`Span` or `()`")?;
+                    }
                 }
                 _ => throw_invalid_attr!(attr, &meta, |diag| {
                     diag.help("only `label`, `note` and `help` are valid field attributes")
@@ -510,12 +529,12 @@ impl SessionDiagnosticDeriveBuilder {
         }
     }
 
-    /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
-    /// `fluent_attr_identifier`.
-    fn add_subdiagnostic(
+    /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug
+    /// and `fluent_attr_identifier`.
+    fn add_spanned_subdiagnostic(
         &self,
         field_binding: TokenStream,
-        kind: &str,
+        kind: &Ident,
         fluent_attr_identifier: &str,
     ) -> TokenStream {
         let diag = &self.diag;
@@ -531,6 +550,16 @@ impl SessionDiagnosticDeriveBuilder {
         }
     }
 
+    /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug
+    /// and `fluent_attr_identifier`.
+    fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: &str) -> TokenStream {
+        let diag = &self.diag;
+        let slug = self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or("missing-slug");
+        quote! {
+            #diag.#kind(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier));
+        }
+    }
+
     fn span_and_applicability_of_ty(
         &self,
         info: FieldInfo<'_>,
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index aba861fc6aa..af5a30880e0 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -4,7 +4,7 @@ use proc_macro2::TokenStream;
 use quote::{format_ident, quote, ToTokens};
 use std::collections::BTreeSet;
 use std::str::FromStr;
-use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
+use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple, Visibility};
 use synstructure::BindingInfo;
 
 /// Checks whether the type name of `ty` matches `name`.
@@ -25,7 +25,35 @@ pub(crate) fn type_matches_path(ty: &Type, name: &[&str]) -> bool {
     }
 }
 
-/// Reports an error if the field's type is not `Applicability`.
+/// Checks whether the type `ty` is `()`.
+pub(crate) fn type_is_unit(ty: &Type) -> bool {
+    if let Type::Tuple(TypeTuple { elems, .. }) = ty { elems.is_empty() } else { false }
+}
+
+/// Reports a type error for field with `attr`.
+pub(crate) fn report_type_error(
+    attr: &Attribute,
+    ty_name: &str,
+) -> Result<!, SessionDiagnosticDeriveError> {
+    let name = attr.path.segments.last().unwrap().ident.to_string();
+    let meta = attr.parse_meta()?;
+
+    throw_span_err!(
+        attr.span().unwrap(),
+        &format!(
+            "the `#[{}{}]` attribute can only be applied to fields of type {}",
+            name,
+            match meta {
+                Meta::Path(_) => "",
+                Meta::NameValue(_) => " = ...",
+                Meta::List(_) => "(...)",
+            },
+            ty_name
+        )
+    );
+}
+
+/// Reports an error if the field's type does not match `path`.
 fn report_error_if_not_applied_to_ty(
     attr: &Attribute,
     info: &FieldInfo<'_>,
@@ -33,23 +61,7 @@ fn report_error_if_not_applied_to_ty(
     ty_name: &str,
 ) -> Result<(), SessionDiagnosticDeriveError> {
     if !type_matches_path(&info.ty, path) {
-        let name = attr.path.segments.last().unwrap().ident.to_string();
-        let name = name.as_str();
-        let meta = attr.parse_meta()?;
-
-        throw_span_err!(
-            attr.span().unwrap(),
-            &format!(
-                "the `#[{}{}]` attribute can only be applied to fields of type `{}`",
-                name,
-                match meta {
-                    Meta::Path(_) => "",
-                    Meta::NameValue(_) => " = ...",
-                    Meta::List(_) => "(...)",
-                },
-                ty_name
-            )
-        );
+        report_type_error(attr, ty_name)?;
     }
 
     Ok(())
@@ -64,7 +76,7 @@ pub(crate) fn report_error_if_not_applied_to_applicability(
         attr,
         info,
         &["rustc_errors", "Applicability"],
-        "Applicability",
+        "`Applicability`",
     )
 }
 
@@ -73,7 +85,7 @@ pub(crate) fn report_error_if_not_applied_to_span(
     attr: &Attribute,
     info: &FieldInfo<'_>,
 ) -> Result<(), SessionDiagnosticDeriveError> {
-    report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span")
+    report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`")
 }
 
 /// Inner type of a field and type of wrapper.
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index b01e01414e8..0baebdb7130 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -1,5 +1,6 @@
 #![feature(allow_internal_unstable)]
 #![feature(let_else)]
+#![feature(never_type)]
 #![feature(proc_macro_diagnostic)]
 #![allow(rustc::default_hash_types)]
 #![recursion_limit = "128"]