about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2022-05-07 15:23:46 +0200
committerGitHub <noreply@github.com>2022-05-07 15:23:46 +0200
commita6441973914de920db875f6968cd9e080ae87a6f (patch)
tree6951fef91a8c952feaade6d021611c9b0b62fc7e
parent3346d11f4e8521894377636f145bfc68fad10bea (diff)
parentaf47257c0dfc5b38468417d36a465a613c675d6e (diff)
downloadrust-a6441973914de920db875f6968cd9e080ae87a6f.tar.gz
rust-a6441973914de920db875f6968cd9e080ae87a6f.zip
Rollup merge of #96760 - davidtwco:diagnostic-translation-vec, r=oli-obk
diagnostics: port more diagnostics to derive + add support for `Vec` fields

- Port "unconstrained opaque type" diagnostic to using the derive.
- Allow `Vec` fields in diagnostic derive - enables support for diagnostics that have multiple primary spans, or have subdiagnostics repeated at multiple locations. `Vec<..>` fields in the diagnostic derive become loops in the generated code.
- Add `create_{err,warning}` - there wasn't a way to create a diagnostic from a struct and not emit it straight away.
- Port "explicit generic args w/ impl trait" diagnostic to using the derive.

r? `````@oli-obk`````
cc `````@pvdrz`````
-rw-r--r--compiler/rustc_error_messages/locales/en-US/typeck.ftl11
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs45
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs18
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs61
-rw-r--r--compiler/rustc_session/src/parse.rs18
-rw-r--r--compiler/rustc_session/src/session.rs12
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs28
-rw-r--r--compiler/rustc_typeck/src/collect/type_of.rs12
-rw-r--r--compiler/rustc_typeck/src/errors.rs22
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs8
10 files changed, 165 insertions, 70 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/typeck.ftl b/compiler/rustc_error_messages/locales/en-US/typeck.ftl
index 6a3235fc772..aef18fcafaa 100644
--- a/compiler/rustc_error_messages/locales/en-US/typeck.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/typeck.ftl
@@ -90,3 +90,14 @@ typeck-add-return-type-missing-here = a return type might be missing here
 typeck-expected-default-return-type = expected `()` because of default return type
 
 typeck-expected-return-type = expected `{$expected}` because of return type
+
+typeck-unconstrained-opaque-type = unconstrained opaque type
+    .note = `{$name}` must be used in combination with a concrete type within the same module
+
+typeck-explicit-generic-args-with-impl-trait =
+    cannot provide explicit generic arguments when `impl Trait` is used in argument position
+    .label = explicit generic argument not allowed
+    .note = see issue #83701 <https://github.com/rust-lang/rust/issues/83701> for more information
+
+typeck-explicit-generic-args-with-impl-trait-feature =
+    add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index f49166433fa..83fc7bcde8a 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -5,8 +5,8 @@ use crate::diagnostics::error::{
     SessionDiagnosticDeriveError,
 };
 use crate::diagnostics::utils::{
-    option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, Applicability,
-    FieldInfo, HasFieldMap, SetOnce,
+    report_error_if_not_applied_to_span, type_matches_path, Applicability, FieldInfo, FieldInnerTy,
+    HasFieldMap, SetOnce,
 };
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote};
@@ -353,35 +353,40 @@ impl SessionDiagnosticDeriveBuilder {
         info: FieldInfo<'_>,
     ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
         let field_binding = &info.binding.binding;
-        let option_ty = option_inner_ty(&info.ty);
-        let generated_code = self.generate_non_option_field_code(
+
+        let inner_ty = FieldInnerTy::from_type(&info.ty);
+        let name = attr.path.segments.last().unwrap().ident.to_string();
+        let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
+            // `primary_span` can accept a `Vec<Span>` so don't destructure that.
+            ("primary_span", FieldInnerTy::Vec(_)) => (quote! { #field_binding.clone() }, false),
+            _ => (quote! { *#field_binding }, true),
+        };
+
+        let generated_code = self.generate_inner_field_code(
             attr,
             FieldInfo {
                 vis: info.vis,
                 binding: info.binding,
-                ty: option_ty.unwrap_or(&info.ty),
+                ty: inner_ty.inner_type().unwrap_or(&info.ty),
                 span: info.span,
             },
+            binding,
         )?;
 
-        if option_ty.is_none() {
-            Ok(quote! { #generated_code })
+        if needs_destructure {
+            Ok(inner_ty.with(field_binding, generated_code))
         } else {
-            Ok(quote! {
-                if let Some(#field_binding) = #field_binding {
-                    #generated_code
-                }
-            })
+            Ok(generated_code)
         }
     }
 
-    fn generate_non_option_field_code(
+    fn generate_inner_field_code(
         &mut self,
         attr: &Attribute,
         info: FieldInfo<'_>,
+        binding: TokenStream,
     ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
         let diag = &self.diag;
-        let field_binding = &info.binding.binding;
 
         let name = attr.path.segments.last().unwrap().ident.to_string();
         let name = name.as_str();
@@ -397,14 +402,14 @@ impl SessionDiagnosticDeriveBuilder {
                 "primary_span" => {
                     report_error_if_not_applied_to_span(attr, &info)?;
                     Ok(quote! {
-                        #diag.set_span(*#field_binding);
+                        #diag.set_span(#binding);
                     })
                 }
                 "label" | "note" | "help" => {
                     report_error_if_not_applied_to_span(attr, &info)?;
-                    Ok(self.add_subdiagnostic(field_binding, name, name))
+                    Ok(self.add_subdiagnostic(binding, name, name))
                 }
-                "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(*#field_binding); }),
+                "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }),
                 _ => throw_invalid_attr!(attr, &meta, |diag| {
                     diag
                         .help("only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes")
@@ -413,7 +418,7 @@ impl SessionDiagnosticDeriveBuilder {
             Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
                 "label" | "note" | "help" => {
                     report_error_if_not_applied_to_span(attr, &info)?;
-                    Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
+                    Ok(self.add_subdiagnostic(binding, name, &s.value()))
                 }
                 _ => throw_invalid_attr!(attr, &meta, |diag| {
                     diag.help("only `label`, `note` and `help` are valid field attributes")
@@ -509,7 +514,7 @@ impl SessionDiagnosticDeriveBuilder {
     /// `fluent_attr_identifier`.
     fn add_subdiagnostic(
         &self,
-        field_binding: &proc_macro2::Ident,
+        field_binding: TokenStream,
         kind: &str,
         fluent_attr_identifier: &str,
     ) -> TokenStream {
@@ -520,7 +525,7 @@ impl SessionDiagnosticDeriveBuilder {
         let fn_name = format_ident!("span_{}", kind);
         quote! {
             #diag.#fn_name(
-                *#field_binding,
+                #field_binding,
                 rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)
             );
         }
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 961b42f424f..65b1328682f 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -5,8 +5,8 @@ use crate::diagnostics::error::{
     SessionDiagnosticDeriveError,
 };
 use crate::diagnostics::utils::{
-    option_inner_ty, report_error_if_not_applied_to_applicability,
-    report_error_if_not_applied_to_span, Applicability, FieldInfo, HasFieldMap, SetOnce,
+    report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span,
+    Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
 };
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote};
@@ -301,11 +301,11 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
     ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
         let ast = binding.ast();
 
-        let option_ty = option_inner_ty(&ast.ty);
+        let inner_ty = FieldInnerTy::from_type(&ast.ty);
         let info = FieldInfo {
             vis: &ast.vis,
             binding: binding,
-            ty: option_ty.unwrap_or(&ast.ty),
+            ty: inner_ty.inner_type().unwrap_or(&ast.ty),
             span: &ast.span(),
         };
 
@@ -353,15 +353,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
             );
         };
 
-        if option_ty.is_none() {
-            Ok(quote! { #generated })
-        } else {
-            Ok(quote! {
-                if let Some(#binding) = #binding {
-                    #generated
-                }
-            })
-        }
+        Ok(inner_ty.with(binding, generated))
     }
 
     fn into_tokens(&mut self) -> Result<TokenStream, SessionDiagnosticDeriveError> {
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index 1f36af0a20b..aba861fc6aa 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -1,7 +1,7 @@
 use crate::diagnostics::error::{span_err, throw_span_err, SessionDiagnosticDeriveError};
 use proc_macro::Span;
 use proc_macro2::TokenStream;
-use quote::{format_ident, quote};
+use quote::{format_ident, quote, ToTokens};
 use std::collections::BTreeSet;
 use std::str::FromStr;
 use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
@@ -76,22 +76,71 @@ pub(crate) fn report_error_if_not_applied_to_span(
     report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span")
 }
 
-/// If `ty` is an Option, returns `Some(inner type)`, otherwise returns `None`.
-pub(crate) fn option_inner_ty(ty: &Type) -> Option<&Type> {
-    if type_matches_path(ty, &["std", "option", "Option"]) {
+/// Inner type of a field and type of wrapper.
+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,
+}
+
+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`.
+    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;
+            };
+
         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 Some(ty);
+                        return variant(ty);
                     }
                 }
             }
         }
+
+        unreachable!();
+    }
+
+    /// Returns `Option` containing inner type if there is one.
+    pub(crate) fn inner_type(&self) -> Option<&'ty Type> {
+        match self {
+            FieldInnerTy::Option(inner) | FieldInnerTy::Vec(inner) => Some(inner),
+            FieldInnerTy::None => None,
+        }
+    }
+
+    /// Surrounds `inner` with destructured wrapper type, exposing inner type as `binding`.
+    pub(crate) fn with(&self, binding: impl ToTokens, inner: impl ToTokens) -> TokenStream {
+        match self {
+            FieldInnerTy::Option(..) => quote! {
+                if let Some(#binding) = #binding {
+                    #inner
+                }
+            },
+            FieldInnerTy::Vec(..) => quote! {
+                for #binding in #binding {
+                    #inner
+                }
+            },
+            FieldInnerTy::None => quote! { #inner },
+        }
     }
-    None
 }
 
 /// Field information passed to the builder. Deliberately omits attrs to discourage the
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index e933fe1cb24..6fb87e15a33 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -289,12 +289,26 @@ impl ParseSess {
         self.proc_macro_quoted_spans.lock().clone()
     }
 
+    pub fn create_err<'a>(
+        &'a self,
+        err: impl SessionDiagnostic<'a>,
+    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        err.into_diagnostic(self)
+    }
+
     pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
-        err.into_diagnostic(self).emit()
+        self.create_err(err).emit()
+    }
+
+    pub fn create_warning<'a>(
+        &'a self,
+        warning: impl SessionDiagnostic<'a, ()>,
+    ) -> DiagnosticBuilder<'a, ()> {
+        warning.into_diagnostic(self)
     }
 
     pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) {
-        warning.into_diagnostic(self).emit()
+        self.create_warning(warning).emit()
     }
 
     pub fn struct_err(
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index e8279f6fed2..b2c23cda6aa 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -413,9 +413,21 @@ impl Session {
     pub fn err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
         self.diagnostic().err(msg)
     }
+    pub fn create_err<'a>(
+        &'a self,
+        err: impl SessionDiagnostic<'a>,
+    ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        self.parse_sess.create_err(err)
+    }
     pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
         self.parse_sess.emit_err(err)
     }
+    pub fn create_warning<'a>(
+        &'a self,
+        err: impl SessionDiagnostic<'a, ()>,
+    ) -> DiagnosticBuilder<'a, ()> {
+        self.parse_sess.create_warning(err)
+    }
     pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) {
         self.parse_sess.emit_warning(warning)
     }
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs
index 794e711b6c8..38c29d3874c 100644
--- a/compiler/rustc_typeck/src/astconv/generics.rs
+++ b/compiler/rustc_typeck/src/astconv/generics.rs
@@ -3,7 +3,10 @@ use crate::astconv::{
     AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
     GenericArgCountResult, GenericArgPosition,
 };
-use crate::errors::AssocTypeBindingNotAllowed;
+use crate::errors::{
+    AssocTypeBindingNotAllowed, ExplicitGenericArgsWithImplTrait,
+    ExplicitGenericArgsWithImplTraitFeature,
+};
 use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
 use rustc_ast::ast::ParamKindOrd;
 use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan};
@@ -636,29 +639,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 })
                 .collect::<Vec<_>>();
 
-            let mut err = struct_span_err! {
-                tcx.sess,
-                spans.clone(),
-                E0632,
-                "cannot provide explicit generic arguments when `impl Trait` is \
-                used in argument position"
-            };
-
-            for span in spans {
-                err.span_label(span, "explicit generic argument not allowed");
-            }
-
-            err.note(
-                "see issue #83701 <https://github.com/rust-lang/rust/issues/83701> \
-                 for more information",
-            );
+            let mut err = tcx.sess.create_err(ExplicitGenericArgsWithImplTrait { spans });
             if tcx.sess.is_nightly_build() {
-                err.help(
-                    "add `#![feature(explicit_generic_args_with_impl_trait)]` \
-                     to the crate attributes to enable",
-                );
+                err.subdiagnostic(ExplicitGenericArgsWithImplTraitFeature);
             }
-
             err.emit();
         }
 
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs
index 5330d4d74cb..4b6f80ce57a 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_typeck/src/collect/type_of.rs
@@ -14,6 +14,7 @@ use rustc_span::{Span, DUMMY_SP};
 
 use super::ItemCtxt;
 use super::{bad_placeholder, is_suggestable_infer_ty};
+use crate::errors::UnconstrainedOpaqueType;
 
 /// Computes the relevant generic parameter for a potential generic const argument.
 ///
@@ -682,13 +683,10 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
     match locator.found {
         Some(hidden) => hidden.ty,
         None => {
-            let span = tcx.def_span(def_id);
-            let name = tcx.item_name(tcx.local_parent(def_id).to_def_id());
-            let label = format!(
-                "`{}` must be used in combination with a concrete type within the same module",
-                name
-            );
-            tcx.sess.struct_span_err(span, "unconstrained opaque type").note(&label).emit();
+            tcx.sess.emit_err(UnconstrainedOpaqueType {
+                span: tcx.def_span(def_id),
+                name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
+            });
             tcx.ty_error()
         }
     }
diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs
index 3d2f93537e4..a3e7108caae 100644
--- a/compiler/rustc_typeck/src/errors.rs
+++ b/compiler/rustc_typeck/src/errors.rs
@@ -228,3 +228,25 @@ pub enum ExpectedReturnTypeLabel<'tcx> {
         expected: Ty<'tcx>,
     },
 }
+
+#[derive(SessionDiagnostic)]
+#[error(slug = "typeck-unconstrained-opaque-type")]
+#[note]
+pub struct UnconstrainedOpaqueType {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0632", slug = "typeck-explicit-generic-args-with-impl-trait")]
+#[note]
+pub struct ExplicitGenericArgsWithImplTrait {
+    #[primary_span]
+    #[label]
+    pub spans: Vec<Span>,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[help(slug = "typeck-explicit-generic-args-with-impl-trait-feature")]
+pub struct ExplicitGenericArgsWithImplTraitFeature;
diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
index efbf78ac87d..c63410fa35b 100644
--- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
+++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
@@ -474,3 +474,11 @@ struct Subdiagnostic {
     #[subdiagnostic]
     note: Note,
 }
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct VecField {
+    #[primary_span]
+    #[label]
+    spans: Vec<Span>,
+}