about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-05-12 10:22:07 +0000
committerbors <bors@rust-lang.org>2022-05-12 10:22:07 +0000
commit18bd2dd5cda08b09ace6e37c1a0312e9b2bb4beb (patch)
treec419def06148595c6765e3cf04ae84b45fe43c09
parent4f8e2e3ad9fce35dc356ee1e87170814e4112d76 (diff)
parent47582471c61e15f9e409b45e11f2f15e61a88e29 (diff)
downloadrust-18bd2dd5cda08b09ace6e37c1a0312e9b2bb4beb.tar.gz
rust-18bd2dd5cda08b09ace6e37c1a0312e9b2bb4beb.zip
Auto merge of #96853 - davidtwco:diagnostic-translation-unit-and-more-porting, r=oli-obk
diagnostics: port more diagnostics to derive + support for `()` fields

- Extend diagnostic derive so that spanless subdiagnostics (e.g. some uses of `help`/`note`) can be applied via attributes to fields of type `()` (currently spanless subdiagnostics are applied via attributes on the diagnostic struct itself). A consequence of this is that `Option<()>` fields can be used to represent optional spanless subdiagnostics, which are sometimes useful (e.g. for a `help` that should only show on nightly builds).
- Simplify the "explicit generic args with impl trait" diagnostic struct (from #96760) using support for `Option<()>` spanless subdiagnostics.
- Change `DiagnosticBuilder::set_arg`, used to provide context for Fluent messages, so that it takes anything that implements `IntoDiagnosticArg`, rather than `DiagnosticArgValue` - this improves the ergonomics of manual implementations of `SessionDiagnostic` which are translatable.
- Port "the type parameter `T` must be explicitly specified", "manual implementations of `X` are experimental", "could not resolve substs on overridden impl" diagnostics to diagnostic structs.
- When testing macros from `rustc_macros` in `ui-fulldeps` tests, sometimes paths from the compiler source tree can be shown in error messages - these need to be normalized in `compiletest`.

r? `@oli-obk`
cc `@pvdrz`
-rw-r--r--compiler/rustc_error_messages/locales/en-US/typeck.ftl31
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs4
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs4
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs55
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs2
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs54
-rw-r--r--compiler/rustc_macros/src/lib.rs1
-rw-r--r--compiler/rustc_typeck/src/astconv/errors.rs77
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs14
-rw-r--r--compiler/rustc_typeck/src/errors.rs86
-rw-r--r--compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs3
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs24
-rw-r--r--src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr23
-rw-r--r--src/tools/compiletest/src/runtest.rs19
14 files changed, 254 insertions, 143 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/typeck.ftl b/compiler/rustc_error_messages/locales/en-US/typeck.ftl
index 9195d7a2b8f..95b348ec613 100644
--- a/compiler/rustc_error_messages/locales/en-US/typeck.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/typeck.ftl
@@ -99,6 +99,33 @@ 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
+    .help = add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable
 
-typeck-explicit-generic-args-with-impl-trait-feature =
-    add `#![feature(explicit_generic_args_with_impl_trait)]` to the crate attributes to enable
+typeck-missing-type-params =
+    the type {$parameterCount ->
+        [one] parameter
+        *[other] parameters
+    } {$parameters} must be explicitly specified
+    .label = type {$parameterCount ->
+        [one] parameter
+        *[other] parameters
+    } {$parameters} must be specified for this
+    .suggestion = set the type {$parameterCount ->
+        [one] parameter
+        *[other] parameters
+    } to the desired {$parameterCount ->
+        [one] type
+        *[other] types
+    }
+    .no-suggestion-label = missing {$parameterCount ->
+        [one] reference
+        *[other] references
+    } to {$parameters}
+    .note = because of the default `Self` reference, type parameters must be specified on object types
+
+typeck-manual-implementation =
+    manual implementations of `{$trait_name}` are experimental
+    .label = manual implementations of `{$trait_name}` are experimental
+    .help = add `#![feature(unboxed_closures)]` to the crate attributes to enable
+
+typeck-substs-on-overridden-impl = could not resolve substs on overridden impl
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 83e6a751394..909ed566f64 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -821,9 +821,9 @@ impl Diagnostic {
     pub fn set_arg(
         &mut self,
         name: impl Into<Cow<'static, str>>,
-        arg: DiagnosticArgValue<'static>,
+        arg: impl IntoDiagnosticArg,
     ) -> &mut Self {
-        self.args.push((name.into(), arg));
+        self.args.push((name.into(), arg.into_diagnostic_arg()));
         self
     }
 
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 96b730c2baa..53ad6e5a0ed 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -1,4 +1,4 @@
-use crate::diagnostic::DiagnosticArgValue;
+use crate::diagnostic::IntoDiagnosticArg;
 use crate::{Diagnostic, DiagnosticId, DiagnosticMessage, DiagnosticStyledString, ErrorGuaranteed};
 use crate::{Handler, Level, MultiSpan, StashKey};
 use rustc_lint_defs::Applicability;
@@ -528,7 +528,7 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     forward!(pub fn set_arg(
         &mut self,
         name: impl Into<Cow<'static, str>>,
-        arg: DiagnosticArgValue<'static>,
+        arg: impl IntoDiagnosticArg,
     ) -> &mut Self);
 
     forward!(pub fn subdiagnostic(
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index 83fc7bcde8a..dac3e986e7a 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;
@@ -113,7 +113,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
                         quote! {
                             #diag.set_arg(
                                 stringify!(#ident),
-                                #field_binding.into_diagnostic_arg()
+                                #field_binding
                             );
                         }
                     } else {
@@ -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/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 65b1328682f..ae5b9dbd9ba 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -349,7 +349,7 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
         let generated = quote! {
             #diag.set_arg(
                 stringify!(#ident),
-                #binding.into_diagnostic_arg()
+                #binding
             );
         };
 
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"]
diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs
index 38cc74a5e37..8fe89c66389 100644
--- a/compiler/rustc_typeck/src/astconv/errors.rs
+++ b/compiler/rustc_typeck/src/astconv/errors.rs
@@ -1,4 +1,5 @@
 use crate::astconv::AstConv;
+use crate::errors::{ManualImplementation, MissingTypeParams};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
 use rustc_hir as hir;
@@ -24,65 +25,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         if missing_type_params.is_empty() {
             return;
         }
-        let display =
-            missing_type_params.iter().map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", ");
-        let mut err = struct_span_err!(
-            self.tcx().sess,
+
+        self.tcx().sess.emit_err(MissingTypeParams {
             span,
-            E0393,
-            "the type parameter{} {} must be explicitly specified",
-            pluralize!(missing_type_params.len()),
-            display,
-        );
-        err.span_label(
-            self.tcx().def_span(def_id),
-            &format!(
-                "type parameter{} {} must be specified for this",
-                pluralize!(missing_type_params.len()),
-                display,
-            ),
-        );
-        let mut suggested = false;
-        if let (Ok(snippet), true) = (
-            self.tcx().sess.source_map().span_to_snippet(span),
-            // Don't suggest setting the type params if there are some already: the order is
-            // tricky to get right and the user will already know what the syntax is.
+            def_span: self.tcx().def_span(def_id),
+            missing_type_params,
             empty_generic_args,
-        ) {
-            if snippet.ends_with('>') {
-                // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion
-                // we would have to preserve the right order. For now, as clearly the user is
-                // aware of the syntax, we do nothing.
-            } else {
-                // The user wrote `Iterator`, so we don't have a type we can suggest, but at
-                // least we can clue them to the correct syntax `Iterator<Type>`.
-                err.span_suggestion(
-                    span,
-                    &format!(
-                        "set the type parameter{plural} to the desired type{plural}",
-                        plural = pluralize!(missing_type_params.len()),
-                    ),
-                    format!("{}<{}>", snippet, missing_type_params.join(", ")),
-                    Applicability::HasPlaceholders,
-                );
-                suggested = true;
-            }
-        }
-        if !suggested {
-            err.span_label(
-                span,
-                format!(
-                    "missing reference{} to {}",
-                    pluralize!(missing_type_params.len()),
-                    display,
-                ),
-            );
-        }
-        err.note(
-            "because of the default `Self` reference, type parameters must be \
-                  specified on object types",
-        );
-        err.emit();
+        });
     }
 
     /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
@@ -172,19 +121,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
         if is_impl {
             let trait_name = self.tcx().def_path_str(trait_def_id);
-            struct_span_err!(
-                self.tcx().sess,
-                span,
-                E0183,
-                "manual implementations of `{}` are experimental",
-                trait_name,
-            )
-            .span_label(
-                span,
-                format!("manual implementations of `{}` are experimental", trait_name),
-            )
-            .help("add `#![feature(unboxed_closures)]` to the crate attributes to enable")
-            .emit();
+            self.tcx().sess.emit_err(ManualImplementation { span, trait_name });
         }
     }
 
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs
index 38c29d3874c..dc4bc8fb55a 100644
--- a/compiler/rustc_typeck/src/astconv/generics.rs
+++ b/compiler/rustc_typeck/src/astconv/generics.rs
@@ -3,10 +3,7 @@ use crate::astconv::{
     AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
     GenericArgCountResult, GenericArgPosition,
 };
-use crate::errors::{
-    AssocTypeBindingNotAllowed, ExplicitGenericArgsWithImplTrait,
-    ExplicitGenericArgsWithImplTraitFeature,
-};
+use crate::errors::{AssocTypeBindingNotAllowed, ExplicitGenericArgsWithImplTrait};
 use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
 use rustc_ast::ast::ParamKindOrd;
 use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan};
@@ -639,11 +636,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 })
                 .collect::<Vec<_>>();
 
-            let mut err = tcx.sess.create_err(ExplicitGenericArgsWithImplTrait { spans });
-            if tcx.sess.is_nightly_build() {
-                err.subdiagnostic(ExplicitGenericArgsWithImplTraitFeature);
-            }
-            err.emit();
+            tcx.sess.emit_err(ExplicitGenericArgsWithImplTrait {
+                spans,
+                is_nightly_build: tcx.sess.is_nightly_build().then_some(()),
+            });
         }
 
         impl_trait
diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs
index 81d4c9135ef..cd3813ca4f5 100644
--- a/compiler/rustc_typeck/src/errors.rs
+++ b/compiler/rustc_typeck/src/errors.rs
@@ -1,7 +1,10 @@
 //! Errors emitted by typeck.
-use rustc_errors::Applicability;
+use rustc_errors::{
+    error_code, Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
+};
 use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
 use rustc_middle::ty::Ty;
+use rustc_session::{parse::ParseSess, SessionDiagnostic};
 use rustc_span::{symbol::Ident, Span, Symbol};
 
 #[derive(SessionDiagnostic)]
@@ -247,8 +250,83 @@ pub struct ExplicitGenericArgsWithImplTrait {
     #[primary_span]
     #[label]
     pub spans: Vec<Span>,
+    #[help]
+    pub is_nightly_build: Option<()>,
 }
 
-#[derive(SessionSubdiagnostic)]
-#[help(slug = "typeck-explicit-generic-args-with-impl-trait-feature")]
-pub struct ExplicitGenericArgsWithImplTraitFeature;
+pub struct MissingTypeParams {
+    pub span: Span,
+    pub def_span: Span,
+    pub missing_type_params: Vec<String>,
+    pub empty_generic_args: bool,
+}
+
+// Manual implementation of `SessionDiagnostic` to be able to call `span_to_snippet`.
+impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
+    fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        static SLUG: &'static str = "typeck-missing-type-params";
+        let mut err = sess.span_diagnostic.struct_span_err_with_code(
+            self.span,
+            DiagnosticMessage::fluent(SLUG),
+            error_code!(E0393),
+        );
+        err.set_arg("parameterCount", self.missing_type_params.len());
+        err.set_arg(
+            "parameters",
+            self.missing_type_params
+                .iter()
+                .map(|n| format!("`{}`", n))
+                .collect::<Vec<_>>()
+                .join(", "),
+        );
+
+        err.span_label(self.def_span, DiagnosticMessage::fluent_attr(SLUG, "label"));
+
+        let mut suggested = false;
+        if let (Ok(snippet), true) = (
+            sess.source_map().span_to_snippet(self.span),
+            // Don't suggest setting the type params if there are some already: the order is
+            // tricky to get right and the user will already know what the syntax is.
+            self.empty_generic_args,
+        ) {
+            if snippet.ends_with('>') {
+                // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion
+                // we would have to preserve the right order. For now, as clearly the user is
+                // aware of the syntax, we do nothing.
+            } else {
+                // The user wrote `Iterator`, so we don't have a type we can suggest, but at
+                // least we can clue them to the correct syntax `Iterator<Type>`.
+                err.span_suggestion(
+                    self.span,
+                    DiagnosticMessage::fluent_attr(SLUG, "suggestion"),
+                    format!("{}<{}>", snippet, self.missing_type_params.join(", ")),
+                    Applicability::HasPlaceholders,
+                );
+                suggested = true;
+            }
+        }
+        if !suggested {
+            err.span_label(self.span, DiagnosticMessage::fluent_attr(SLUG, "no-suggestion-label"));
+        }
+
+        err.note(DiagnosticMessage::fluent_attr(SLUG, "note"));
+        err
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0183", slug = "typeck-manual-implementation")]
+#[help]
+pub struct ManualImplementation {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub trait_name: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(slug = "typeck-substs-on-overridden-impl")]
+pub struct SubstsOnOverriddenImpl {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
index 22db15f4d4e..bb97d00be32 100644
--- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
@@ -66,6 +66,7 @@
 //! on traits with methods can.
 
 use crate::constrained_generic_params as cgp;
+use crate::errors::SubstsOnOverriddenImpl;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -165,7 +166,7 @@ fn get_impl_substs<'tcx>(
     let outlives_env = OutlivesEnvironment::new(ty::ParamEnv::empty());
     infcx.resolve_regions_and_report_errors(impl1_def_id, &outlives_env, RegionckMode::default());
     let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else {
-        tcx.sess.struct_span_err(span, "could not resolve substs on overridden impl").emit();
+        tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
         return None;
     };
     Some((impl1_substs, impl2_substs))
diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
index c63410fa35b..1cdc5d18c28 100644
--- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
+++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs
@@ -327,7 +327,7 @@ struct ErrorWithDefaultLabelAttr<'a> {
 }
 
 #[derive(SessionDiagnostic)]
-//~^ ERROR no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
+//~^ ERROR the trait bound `Hello: IntoDiagnosticArg` is not satisfied
 #[error(code = "E0123", slug = "foo")]
 struct ArgFieldWithoutSkip {
     #[primary_span]
@@ -482,3 +482,25 @@ struct VecField {
     #[label]
     spans: Vec<Span>,
 }
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct UnitField {
+    #[primary_span]
+    spans: Span,
+    #[help]
+    foo: (),
+    #[help = "a"]
+    bar: (),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct OptUnitField {
+    #[primary_span]
+    spans: Span,
+    #[help]
+    foo: Option<()>,
+    #[help = "a"]
+    bar: Option<()>,
+}
diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
index b1738b60bc0..2583363120a 100644
--- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
+++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr
@@ -349,17 +349,26 @@ error: cannot find attribute `nonsense` in this scope
 LL |     #[nonsense]
    |       ^^^^^^^^
 
-error[E0599]: no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
+error[E0277]: the trait bound `Hello: IntoDiagnosticArg` is not satisfied
   --> $DIR/diagnostic-derive.rs:329:10
    |
-LL | struct Hello {}
-   | ------------ method `into_diagnostic_arg` not found for this
-...
 LL | #[derive(SessionDiagnostic)]
-   |          ^^^^^^^^^^^^^^^^^ method not found in `Hello`
-   |
+   |          ^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello`
+   |
+   = help: the following other types implement trait `IntoDiagnosticArg`:
+             &'a str
+             Ident
+             String
+             Symbol
+             rustc_middle::ty::Ty<'tcx>
+             usize
+note: required by a bound in `DiagnosticBuilder::<'a, G>::set_arg`
+  --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:531:19
+   |
+LL |         arg: impl IntoDiagnosticArg,
+   |                   ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg`
    = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to 43 previous errors
 
-For more information about this error, try `rustc --explain E0599`.
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 6d94fe3ebb9..a59a0584d5e 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -3494,22 +3494,21 @@ impl<'test> TestCx<'test> {
         normalize_path(parent_dir, "$DIR");
 
         // Paths into the libstd/libcore
-        let src_dir = self
-            .config
-            .src_base
-            .parent()
-            .unwrap()
-            .parent()
-            .unwrap()
-            .parent()
-            .unwrap()
-            .join("library");
+        let base_dir = self.config.src_base.parent().unwrap().parent().unwrap().parent().unwrap();
+        let src_dir = base_dir.join("library");
         normalize_path(&src_dir, "$SRC_DIR");
 
+        // `ui-fulldeps` tests can show paths to the compiler source when testing macros from
+        // `rustc_macros`
+        // eg. /home/user/rust/compiler
+        let compiler_src_dir = base_dir.join("compiler");
+        normalize_path(&compiler_src_dir, "$COMPILER_DIR");
+
         if let Some(virtual_rust_source_base_dir) =
             option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(PathBuf::from)
         {
             normalize_path(&virtual_rust_source_base_dir.join("library"), "$SRC_DIR");
+            normalize_path(&virtual_rust_source_base_dir.join("compiler"), "$COMPILER_DIR");
         }
 
         // Paths into the build directory