about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs21
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs21
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs67
3 files changed, 66 insertions, 43 deletions
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index e405462bbb8..12bcd939bd6 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -322,11 +322,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
                 let generated_code = self
                     .generate_inner_field_code(
                         attr,
-                        FieldInfo {
-                            binding: binding_info,
-                            ty: inner_ty.inner_type().unwrap_or(&field.ty),
-                            span: &field.span(),
-                        },
+                        FieldInfo { binding: binding_info, ty: inner_ty, span: &field.span() },
                         binding,
                     )
                     .unwrap_or_else(|v| v.to_compile_error());
@@ -418,9 +414,9 @@ 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, &["rustc_span", "Span"]) {
+                if type_matches_path(info.ty.inner_type(), &["rustc_span", "Span"]) {
                     Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug))
-                } else if type_is_unit(info.ty) {
+                } else if type_is_unit(info.ty.inner_type()) {
                     Ok(self.add_subdiagnostic(&fn_ident, slug))
                 } else {
                     report_type_error(attr, "`Span` or `()`")?
@@ -432,6 +428,15 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
                 code_field,
                 code_init,
             } => {
+                if let FieldInnerTy::Vec(_) = info.ty {
+                    throw_invalid_attr!(attr, &meta, |diag| {
+                        diag
+                        .note("`#[suggestion(...)]` applied to `Vec` field is ambiguous")
+                        .help("to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]`")
+                        .help("to show a variable set of suggestions, use a `Vec` of `Subdiagnostic`s annotated with `#[suggestion(...)]`")
+                    });
+                }
+
                 let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?;
 
                 if let Some((static_applicability, span)) = static_applicability {
@@ -489,7 +494,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
         &self,
         info: FieldInfo<'_>,
     ) -> Result<(TokenStream, SpannedOption<TokenStream>), DiagnosticDeriveError> {
-        match &info.ty {
+        match &info.ty.inner_type() {
             // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
             ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
                 let binding = &info.binding.binding;
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index baffd3cec9c..906e4c0b0e1 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -247,11 +247,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
                     return quote! {};
                 }
 
-                let info = FieldInfo {
-                    binding,
-                    ty: inner_ty.inner_type().unwrap_or(&ast.ty),
-                    span: &ast.span(),
-                };
+                let info = FieldInfo { binding, ty: inner_ty, span: &ast.span() };
 
                 let generated = self
                     .generate_field_code_inner(kind_stats, attr, info, inner_ty.will_iterate())
@@ -312,6 +308,21 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
                     let binding = info.binding.binding.clone();
                     // FIXME(#100717): support `Option<Span>` on `primary_span` like in the
                     // diagnostic derive
+                    if !matches!(info.ty, FieldInnerTy::Plain(_)) {
+                        throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
+                            let diag = diag.note("there must be exactly one primary span");
+
+                            if kind_stats.has_normal_suggestion {
+                                diag.help(
+                                    "to create a suggestion with multiple spans, \
+                                     use `#[multipart_suggestion]` instead",
+                                )
+                            } else {
+                                diag
+                            }
+                        });
+                    }
+
                     self.span_field.set_once(binding, span);
                 }
 
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index 6f52a3de1b1..27b8f676f3f 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -80,7 +80,7 @@ fn report_error_if_not_applied_to_ty(
     path: &[&str],
     ty_name: &str,
 ) -> Result<(), DiagnosticDeriveError> {
-    if !type_matches_path(info.ty, path) {
+    if !type_matches_path(info.ty.inner_type(), path) {
         report_type_error(attr, ty_name)?;
     }
 
@@ -105,8 +105,8 @@ pub(crate) fn report_error_if_not_applied_to_span(
     attr: &Attribute,
     info: &FieldInfo<'_>,
 ) -> Result<(), DiagnosticDeriveError> {
-    if !type_matches_path(info.ty, &["rustc_span", "Span"])
-        && !type_matches_path(info.ty, &["rustc_errors", "MultiSpan"])
+    if !type_matches_path(info.ty.inner_type(), &["rustc_span", "Span"])
+        && !type_matches_path(info.ty.inner_type(), &["rustc_errors", "MultiSpan"])
     {
         report_type_error(attr, "`Span` or `MultiSpan`")?;
     }
@@ -115,44 +115,50 @@ pub(crate) fn report_error_if_not_applied_to_span(
 }
 
 /// Inner type of a field and type of wrapper.
+#[derive(Copy, Clone)]
 pub(crate) enum FieldInnerTy<'ty> {
     /// Field is wrapped in a `Option<$inner>`.
     Option(&'ty Type),
     /// Field is wrapped in a `Vec<$inner>`.
     Vec(&'ty Type),
     /// Field isn't wrapped in an outer type.
-    None,
+    Plain(&'ty Type),
 }
 
 impl<'ty> FieldInnerTy<'ty> {
     /// Returns inner type for a field, if there is one.
     ///
-    /// - If `ty` is an `Option`, returns `FieldInnerTy::Option { inner: (inner type) }`.
-    /// - If `ty` is a `Vec`, returns `FieldInnerTy::Vec { inner: (inner type) }`.
-    /// - Otherwise returns `None`.
+    /// - If `ty` is an `Option<Inner>`, returns `FieldInnerTy::Option(Inner)`.
+    /// - If `ty` is a `Vec<Inner>`, returns `FieldInnerTy::Vec(Inner)`.
+    /// - Otherwise returns `FieldInnerTy::Plain(ty)`.
     pub(crate) fn from_type(ty: &'ty Type) -> Self {
-        let variant: &dyn Fn(&'ty Type) -> FieldInnerTy<'ty> =
-            if type_matches_path(ty, &["std", "option", "Option"]) {
-                &FieldInnerTy::Option
-            } else if type_matches_path(ty, &["std", "vec", "Vec"]) {
-                &FieldInnerTy::Vec
-            } else {
-                return FieldInnerTy::None;
+        fn single_generic_type(ty: &Type) -> &Type {
+            let Type::Path(ty_path) = ty else {
+                panic!("expected path type");
             };
 
-        if let Type::Path(ty_path) = ty {
             let path = &ty_path.path;
             let ty = path.segments.iter().last().unwrap();
-            if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments {
-                if bracketed.args.len() == 1 {
-                    if let syn::GenericArgument::Type(ty) = &bracketed.args[0] {
-                        return variant(ty);
-                    }
-                }
-            }
+            let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments else {
+                panic!("expected bracketed generic arguments");
+            };
+
+            assert_eq!(bracketed.args.len(), 1);
+
+            let syn::GenericArgument::Type(ty) = &bracketed.args[0] else {
+                panic!("expected generic parameter to be a type generic");
+            };
+
+            ty
         }
 
-        unreachable!();
+        if type_matches_path(ty, &["std", "option", "Option"]) {
+            FieldInnerTy::Option(single_generic_type(ty))
+        } else if type_matches_path(ty, &["std", "vec", "Vec"]) {
+            FieldInnerTy::Vec(single_generic_type(ty))
+        } else {
+            FieldInnerTy::Plain(ty)
+        }
     }
 
     /// Returns `true` if `FieldInnerTy::with` will result in iteration for this inner type (i.e.
@@ -160,15 +166,16 @@ impl<'ty> FieldInnerTy<'ty> {
     pub(crate) fn will_iterate(&self) -> bool {
         match self {
             FieldInnerTy::Vec(..) => true,
-            FieldInnerTy::Option(..) | FieldInnerTy::None => false,
+            FieldInnerTy::Option(..) | FieldInnerTy::Plain(_) => false,
         }
     }
 
-    /// Returns `Option` containing inner type if there is one.
-    pub(crate) fn inner_type(&self) -> Option<&'ty Type> {
+    /// Returns the inner type.
+    pub(crate) fn inner_type(&self) -> &'ty Type {
         match self {
-            FieldInnerTy::Option(inner) | FieldInnerTy::Vec(inner) => Some(inner),
-            FieldInnerTy::None => None,
+            FieldInnerTy::Option(inner) | FieldInnerTy::Vec(inner) | FieldInnerTy::Plain(inner) => {
+                inner
+            }
         }
     }
 
@@ -185,7 +192,7 @@ impl<'ty> FieldInnerTy<'ty> {
                     #inner
                 }
             },
-            FieldInnerTy::None => quote! { #inner },
+            FieldInnerTy::Plain(..) => quote! { #inner },
         }
     }
 }
@@ -194,7 +201,7 @@ impl<'ty> FieldInnerTy<'ty> {
 /// `generate_*` methods from walking the attributes themselves.
 pub(crate) struct FieldInfo<'a> {
     pub(crate) binding: &'a BindingInfo<'a>,
-    pub(crate) ty: &'a Type,
+    pub(crate) ty: FieldInnerTy<'a>,
     pub(crate) span: &'a proc_macro2::Span,
 }