about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_trait_selection/messages.ftl3
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs147
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs67
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr305
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs8
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr54
6 files changed, 523 insertions, 61 deletions
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index d753aa8618e..41db8059cbe 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -55,3 +55,6 @@ trait_selection_trait_has_no_impls = this trait has no implementations, consider
 
 trait_selection_ty_alias_overflow = in case this is a recursive type alias, consider using a struct, enum, or union instead
 trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated}
+
+trait_selection_unknown_format_parameter_for_on_unimplemented_attr = there is no parameter `{$argument_name}` on trait `{$trait_name}`
+    .help = expect either a generic argument name or {"`{Self}`"} as format argument
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index feeeaa51f81..ca485864050 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -321,7 +321,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 }
 
 #[derive(Clone, Debug)]
-pub struct OnUnimplementedFormatString(Symbol, Span);
+pub struct OnUnimplementedFormatString {
+    symbol: Symbol,
+    span: Span,
+    is_diagnostic_namespace_variant: bool,
+}
 
 #[derive(Debug)]
 pub struct OnUnimplementedDirective {
@@ -401,6 +405,14 @@ impl IgnoredDiagnosticOption {
     }
 }
 
+#[derive(LintDiagnostic)]
+#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
+#[help]
+pub struct UnknownFormatParameterForOnUnimplementedAttr {
+    argument_name: Symbol,
+    trait_name: Symbol,
+}
+
 impl<'tcx> OnUnimplementedDirective {
     fn parse(
         tcx: TyCtxt<'tcx>,
@@ -414,8 +426,14 @@ impl<'tcx> OnUnimplementedDirective {
         let mut item_iter = items.iter();
 
         let parse_value = |value_str, value_span| {
-            OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span, value_span)
-                .map(Some)
+            OnUnimplementedFormatString::try_parse(
+                tcx,
+                item_def_id,
+                value_str,
+                value_span,
+                is_diagnostic_namespace_variant,
+            )
+            .map(Some)
         };
 
         let condition = if is_root {
@@ -552,15 +570,15 @@ impl<'tcx> OnUnimplementedDirective {
                         IgnoredDiagnosticOption::maybe_emit_warning(
                             tcx,
                             item_def_id,
-                            directive.message.as_ref().map(|f| f.1),
-                            aggr.message.as_ref().map(|f| f.1),
+                            directive.message.as_ref().map(|f| f.span),
+                            aggr.message.as_ref().map(|f| f.span),
                             "message",
                         );
                         IgnoredDiagnosticOption::maybe_emit_warning(
                             tcx,
                             item_def_id,
-                            directive.label.as_ref().map(|f| f.1),
-                            aggr.label.as_ref().map(|f| f.1),
+                            directive.label.as_ref().map(|f| f.span),
+                            aggr.label.as_ref().map(|f| f.span),
                             "label",
                         );
                         IgnoredDiagnosticOption::maybe_emit_warning(
@@ -573,8 +591,8 @@ impl<'tcx> OnUnimplementedDirective {
                         IgnoredDiagnosticOption::maybe_emit_warning(
                             tcx,
                             item_def_id,
-                            directive.parent_label.as_ref().map(|f| f.1),
-                            aggr.parent_label.as_ref().map(|f| f.1),
+                            directive.parent_label.as_ref().map(|f| f.span),
+                            aggr.parent_label.as_ref().map(|f| f.span),
                             "parent_label",
                         );
                         IgnoredDiagnosticOption::maybe_emit_warning(
@@ -634,7 +652,7 @@ impl<'tcx> OnUnimplementedDirective {
                         item_def_id,
                         value,
                         attr.span,
-                        attr.span,
+                        is_diagnostic_namespace_variant,
                     )?),
                     notes: Vec::new(),
                     parent_label: None,
@@ -712,7 +730,12 @@ impl<'tcx> OnUnimplementedDirective {
                             // `with_no_visible_paths` is also used when generating the options,
                             // so we need to match it here.
                             ty::print::with_no_visible_paths!(
-                                OnUnimplementedFormatString(v, cfg.span).format(
+                                OnUnimplementedFormatString {
+                                    symbol: v,
+                                    span: cfg.span,
+                                    is_diagnostic_namespace_variant: false
+                                }
+                                .format(
                                     tcx,
                                     trait_ref,
                                     &options_map
@@ -760,20 +783,19 @@ impl<'tcx> OnUnimplementedFormatString {
         tcx: TyCtxt<'tcx>,
         item_def_id: DefId,
         from: Symbol,
-        err_sp: Span,
         value_span: Span,
+        is_diagnostic_namespace_variant: bool,
     ) -> Result<Self, ErrorGuaranteed> {
-        let result = OnUnimplementedFormatString(from, value_span);
-        result.verify(tcx, item_def_id, err_sp)?;
+        let result = OnUnimplementedFormatString {
+            symbol: from,
+            span: value_span,
+            is_diagnostic_namespace_variant,
+        };
+        result.verify(tcx, item_def_id)?;
         Ok(result)
     }
 
-    fn verify(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        item_def_id: DefId,
-        span: Span,
-    ) -> Result<(), ErrorGuaranteed> {
+    fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
         let trait_def_id = if tcx.is_trait(item_def_id) {
             item_def_id
         } else {
@@ -782,7 +804,7 @@ impl<'tcx> OnUnimplementedFormatString {
         };
         let trait_name = tcx.item_name(trait_def_id);
         let generics = tcx.generics_of(item_def_id);
-        let s = self.0.as_str();
+        let s = self.symbol.as_str();
         let parser = Parser::new(s, None, None, false, ParseMode::Format);
         let mut result = Ok(());
         for token in parser {
@@ -792,24 +814,40 @@ impl<'tcx> OnUnimplementedFormatString {
                     Position::ArgumentNamed(s) => {
                         match Symbol::intern(s) {
                             // `{ThisTraitsName}` is allowed
-                            s if s == trait_name => (),
-                            s if ALLOWED_FORMAT_SYMBOLS.contains(&s) => (),
+                            s if s == trait_name && !self.is_diagnostic_namespace_variant => (),
+                            s if ALLOWED_FORMAT_SYMBOLS.contains(&s)
+                                && !self.is_diagnostic_namespace_variant =>
+                            {
+                                ()
+                            }
                             // So is `{A}` if A is a type parameter
                             s if generics.params.iter().any(|param| param.name == s) => (),
                             s => {
-                                result = Err(struct_span_err!(
-                                    tcx.sess,
-                                    span,
-                                    E0230,
-                                    "there is no parameter `{}` on {}",
-                                    s,
-                                    if trait_def_id == item_def_id {
-                                        format!("trait `{trait_name}`")
-                                    } else {
-                                        "impl".to_string()
-                                    }
-                                )
-                                .emit());
+                                if self.is_diagnostic_namespace_variant {
+                                    tcx.emit_spanned_lint(
+                                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                                        tcx.local_def_id_to_hir_id(item_def_id.expect_local()),
+                                        self.span,
+                                        UnknownFormatParameterForOnUnimplementedAttr {
+                                            argument_name: s,
+                                            trait_name,
+                                        },
+                                    );
+                                } else {
+                                    result = Err(struct_span_err!(
+                                        tcx.sess,
+                                        self.span,
+                                        E0230,
+                                        "there is no parameter `{}` on {}",
+                                        s,
+                                        if trait_def_id == item_def_id {
+                                            format!("trait `{trait_name}`")
+                                        } else {
+                                            "impl".to_string()
+                                        }
+                                    )
+                                    .emit());
+                                }
                             }
                         }
                     }
@@ -817,7 +855,7 @@ impl<'tcx> OnUnimplementedFormatString {
                     Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
                         let reported = struct_span_err!(
                             tcx.sess,
-                            span,
+                            self.span,
                             E0231,
                             "only named substitution parameters are allowed"
                         )
@@ -856,37 +894,42 @@ impl<'tcx> OnUnimplementedFormatString {
             .collect::<FxHashMap<Symbol, String>>();
         let empty_string = String::new();
 
-        let s = self.0.as_str();
+        let s = self.symbol.as_str();
         let parser = Parser::new(s, None, None, false, ParseMode::Format);
         let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
         parser
             .map(|p| match p {
-                Piece::String(s) => s,
+                Piece::String(s) => s.to_owned(),
                 Piece::NextArgument(a) => match a.position {
-                    Position::ArgumentNamed(s) => {
-                        let s = Symbol::intern(s);
+                    Position::ArgumentNamed(arg) => {
+                        let s = Symbol::intern(arg);
                         match generic_map.get(&s) {
-                            Some(val) => val,
-                            None if s == name => &trait_str,
+                            Some(val) => val.to_string(),
+                            None if self.is_diagnostic_namespace_variant => {
+                                format!("{{{arg}}}")
+                            }
+                            None if s == name => trait_str.clone(),
                             None => {
                                 if let Some(val) = options.get(&s) {
-                                    val
+                                    val.clone()
                                 } else if s == sym::from_desugaring {
                                     // don't break messages using these two arguments incorrectly
-                                    &empty_string
-                                } else if s == sym::ItemContext {
-                                    item_context
+                                    String::new()
+                                } else if s == sym::ItemContext
+                                    && !self.is_diagnostic_namespace_variant
+                                {
+                                    item_context.clone()
                                 } else if s == sym::integral {
-                                    "{integral}"
+                                    String::from("{integral}")
                                 } else if s == sym::integer_ {
-                                    "{integer}"
+                                    String::from("{integer}")
                                 } else if s == sym::float {
-                                    "{float}"
+                                    String::from("{float}")
                                 } else {
                                     bug!(
                                         "broken on_unimplemented {:?} for {:?}: \
                                       no argument matching {:?}",
-                                        self.0,
+                                        self.symbol,
                                         trait_ref,
                                         s
                                     )
@@ -894,7 +937,7 @@ impl<'tcx> OnUnimplementedFormatString {
                             }
                         }
                     }
-                    _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
+                    _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol),
                 },
             })
             .collect()
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs
new file mode 100644
index 00000000000..eb985c062f3
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs
@@ -0,0 +1,67 @@
+#![feature(diagnostic_namespace)]
+
+#[diagnostic::on_unimplemented(
+    on(_Self = "&str"),
+    //~^WARN malformed `on_unimplemented` attribute
+    //~|WARN malformed `on_unimplemented` attribute
+    message = "trait has `{Self}` and `{T}` as params",
+    label = "trait has `{Self}` and `{T}` as params",
+    note  = "trait has `{Self}` and `{T}` as params",
+    parent_label = "in this scope",
+    //~^WARN malformed `on_unimplemented` attribute
+    //~|WARN malformed `on_unimplemented` attribute
+    append_const_msg
+    //~^WARN malformed `on_unimplemented` attribute
+    //~|WARN malformed `on_unimplemented` attribute
+)]
+trait Foo<T> {}
+
+#[diagnostic::on_unimplemented = "Message"]
+//~^WARN malformed `on_unimplemented` attribute
+//~|WARN malformed `on_unimplemented` attribute
+trait Bar {}
+
+#[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")]
+//~^WARN #[diagnostic::on_unimplemented]` can only be applied to trait definitions
+impl Bar for i32 {}
+
+// cannot use special rustc_on_unimplement symbols
+// in the format string
+#[diagnostic::on_unimplemented(
+    message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+    //~^WARN there is no parameter `from_desugaring` on trait `Baz`
+    //~|WARN there is no parameter `from_desugaring` on trait `Baz`
+    //~|WARN there is no parameter `direct` on trait `Baz`
+    //~|WARN there is no parameter `direct` on trait `Baz`
+    //~|WARN there is no parameter `cause` on trait `Baz`
+    //~|WARN there is no parameter `cause` on trait `Baz`
+    //~|WARN there is no parameter `integral` on trait `Baz`
+    //~|WARN there is no parameter `integral` on trait `Baz`
+    //~|WARN there is no parameter `integer` on trait `Baz`
+    //~|WARN there is no parameter `integer` on trait `Baz`
+    label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+    //~^WARN there is no parameter `float` on trait `Baz`
+    //~|WARN there is no parameter `float` on trait `Baz`
+    //~|WARN there is no parameter `_Self` on trait `Baz`
+    //~|WARN there is no parameter `_Self` on trait `Baz`
+    //~|WARN there is no parameter `crate_local` on trait `Baz`
+    //~|WARN there is no parameter `crate_local` on trait `Baz`
+    //~|WARN there is no parameter `Trait` on trait `Baz`
+    //~|WARN there is no parameter `Trait` on trait `Baz`
+    //~|WARN there is no parameter `ItemContext` on trait `Baz`
+    //~|WARN there is no parameter `ItemContext` on trait `Baz`
+)]
+trait Baz {}
+
+fn takes_foo(_: impl Foo<i32>) {}
+fn takes_bar(_: impl Bar) {}
+fn takes_baz(_: impl Baz) {}
+
+fn main() {
+    takes_foo(());
+    //~^ERROR trait has `()` and `i32` as params
+    takes_bar(());
+    //~^ERROR the trait bound `(): Bar` is not satisfied
+    takes_baz(());
+    //~^ERROR {from_desugaring}{direct}{cause}{integral}{integer}
+}
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr
new file mode 100644
index 00000000000..75a701f0b5f
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr
@@ -0,0 +1,305 @@
+warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:24:1
+   |
+LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:4:5
+   |
+LL |     on(_Self = "&str"),
+   |     ^^^^^^^^^^^^^^^^^^ invalid option found here
+   |
+   = help: only `message`, `note` and `label` are allowed as options
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:10:5
+   |
+LL |     parent_label = "in this scope",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
+   |
+   = help: only `message`, `note` and `label` are allowed as options
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:13:5
+   |
+LL |     append_const_msg
+   |     ^^^^^^^^^^^^^^^^ invalid option found here
+   |
+   = help: only `message`, `note` and `label` are allowed as options
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:19:32
+   |
+LL | #[diagnostic::on_unimplemented = "Message"]
+   |                                ^^^^^^^^^^^ invalid option found here
+   |
+   = help: only `message`, `note` and `label` are allowed as options
+
+warning: there is no parameter `from_desugaring` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
+   |
+LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+warning: there is no parameter `direct` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
+   |
+LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+warning: there is no parameter `cause` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
+   |
+LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+warning: there is no parameter `integral` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
+   |
+LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+warning: there is no parameter `integer` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
+   |
+LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+warning: there is no parameter `float` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
+   |
+LL |     label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+warning: there is no parameter `_Self` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
+   |
+LL |     label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+warning: there is no parameter `crate_local` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
+   |
+LL |     label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+warning: there is no parameter `Trait` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
+   |
+LL |     label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+warning: there is no parameter `ItemContext` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
+   |
+LL |     label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:4:5
+   |
+LL |     on(_Self = "&str"),
+   |     ^^^^^^^^^^^^^^^^^^ invalid option found here
+   |
+   = help: only `message`, `note` and `label` are allowed as options
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:10:5
+   |
+LL |     parent_label = "in this scope",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
+   |
+   = help: only `message`, `note` and `label` are allowed as options
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:13:5
+   |
+LL |     append_const_msg
+   |     ^^^^^^^^^^^^^^^^ invalid option found here
+   |
+   = help: only `message`, `note` and `label` are allowed as options
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0277]: trait has `()` and `i32` as params
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:61:15
+   |
+LL |     takes_foo(());
+   |     --------- ^^ trait has `()` and `i32` as params
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Foo<i32>` is not implemented for `()`
+   = note: trait has `()` and `i32` as params
+help: this trait has no implementations, consider adding one
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:17:1
+   |
+LL | trait Foo<T> {}
+   | ^^^^^^^^^^^^
+note: required by a bound in `takes_foo`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:56:22
+   |
+LL | fn takes_foo(_: impl Foo<i32>) {}
+   |                      ^^^^^^^^ required by this bound in `takes_foo`
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:19:32
+   |
+LL | #[diagnostic::on_unimplemented = "Message"]
+   |                                ^^^^^^^^^^^ invalid option found here
+   |
+   = help: only `message`, `note` and `label` are allowed as options
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0277]: the trait bound `(): Bar` is not satisfied
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:63:15
+   |
+LL |     takes_bar(());
+   |     --------- ^^ the trait `Bar` is not implemented for `()`
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Bar` is implemented for `i32`
+note: required by a bound in `takes_bar`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:57:22
+   |
+LL | fn takes_bar(_: impl Bar) {}
+   |                      ^^^ required by this bound in `takes_bar`
+
+warning: there is no parameter `from_desugaring` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
+   |
+LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: there is no parameter `direct` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
+   |
+LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: there is no parameter `cause` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
+   |
+LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: there is no parameter `integral` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
+   |
+LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: there is no parameter `integer` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:31:5
+   |
+LL |     message = "{from_desugaring}{direct}{cause}{integral}{integer}",
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: there is no parameter `float` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
+   |
+LL |     label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: there is no parameter `_Self` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
+   |
+LL |     label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: there is no parameter `crate_local` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
+   |
+LL |     label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: there is no parameter `Trait` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
+   |
+LL |     label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+warning: there is no parameter `ItemContext` on trait `Baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:42:5
+   |
+LL |     label = "{float}{_Self}{crate_local}{Trait}{ItemContext}"
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer}
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:65:15
+   |
+LL |     takes_baz(());
+   |     --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext}
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Baz` is not implemented for `()`
+help: this trait has no implementations, consider adding one
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:54:1
+   |
+LL | trait Baz {}
+   | ^^^^^^^^^
+note: required by a bound in `takes_baz`
+  --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:58:22
+   |
+LL | fn takes_baz(_: impl Baz) {}
+   |                      ^^^ required by this bound in `takes_baz`
+
+error: aborting due to 3 previous errors; 29 warnings emitted
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs
index 346d8373f73..12fe988170a 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs
@@ -28,10 +28,16 @@ trait Doom {}
 //~|WARN missing options for `on_unimplemented` attribute
 trait Whatever {}
 
+#[diagnostic::on_unimplemented(message = "{DoesNotExist}")]
+//~^WARN there is no parameter `DoesNotExist` on trait `Test`
+//~|WARN there is no parameter `DoesNotExist` on trait `Test`
+trait Test {}
+
 fn take_foo(_: impl Foo) {}
 fn take_baz(_: impl Baz) {}
 fn take_boom(_: impl Boom) {}
 fn take_whatever(_: impl Whatever) {}
+fn take_test(_: impl Test) {}
 
 fn main() {
     take_foo(1_i32);
@@ -42,4 +48,6 @@ fn main() {
     //~^ERROR Boom
     take_whatever(1_i32);
     //~^ERROR the trait bound `i32: Whatever` is not satisfied
+    take_test(());
+    //~^ERROR {DoesNotExist}
 }
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr
index 162ddd79fbb..11263580b15 100644
--- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr
@@ -46,6 +46,14 @@ LL | #[diagnostic::on_unimplemented]
    |
    = help: at least one of the `message`, `note` and `label` options are expected
 
+warning: there is no parameter `DoesNotExist` on trait `Test`
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:32
+   |
+LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")]
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+
 warning: malformed `on_unimplemented` attribute
   --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32
    |
@@ -56,7 +64,7 @@ LL | #[diagnostic::on_unimplemented(unsupported = "foo")]
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0277]: the trait bound `i32: Foo` is not satisfied
-  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:37:14
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:43:14
    |
 LL |     take_foo(1_i32);
    |     -------- ^^^^^ the trait `Foo` is not implemented for `i32`
@@ -69,7 +77,7 @@ help: this trait has no implementations, consider adding one
 LL | trait Foo {}
    | ^^^^^^^^^
 note: required by a bound in `take_foo`
-  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:21
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:36:21
    |
 LL | fn take_foo(_: impl Foo) {}
    |                     ^^^ required by this bound in `take_foo`
@@ -84,7 +92,7 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")]
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0277]: Boom
-  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:39:14
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:45:14
    |
 LL |     take_baz(1_i32);
    |     -------- ^^^^^ the trait `Baz` is not implemented for `i32`
@@ -97,7 +105,7 @@ help: this trait has no implementations, consider adding one
 LL | trait Baz {}
    | ^^^^^^^^^
 note: required by a bound in `take_baz`
-  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:32:21
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:37:21
    |
 LL | fn take_baz(_: impl Baz) {}
    |                     ^^^ required by this bound in `take_baz`
@@ -112,7 +120,7 @@ LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0277]: Boom
-  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:41:15
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:47:15
    |
 LL |     take_boom(1_i32);
    |     --------- ^^^^^ the trait `Boom` is not implemented for `i32`
@@ -125,7 +133,7 @@ help: this trait has no implementations, consider adding one
 LL | trait Boom {}
    | ^^^^^^^^^^
 note: required by a bound in `take_boom`
-  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:22
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:38:22
    |
 LL | fn take_boom(_: impl Boom) {}
    |                      ^^^^ required by this bound in `take_boom`
@@ -140,7 +148,7 @@ LL | #[diagnostic::on_unimplemented]
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0277]: the trait bound `i32: Whatever` is not satisfied
-  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:43:19
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:49:19
    |
 LL |     take_whatever(1_i32);
    |     ------------- ^^^^^ the trait `Whatever` is not implemented for `i32`
@@ -153,11 +161,39 @@ help: this trait has no implementations, consider adding one
 LL | trait Whatever {}
    | ^^^^^^^^^^^^^^
 note: required by a bound in `take_whatever`
-  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:34:26
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:39:26
    |
 LL | fn take_whatever(_: impl Whatever) {}
    |                          ^^^^^^^^ required by this bound in `take_whatever`
 
-error: aborting due to 4 previous errors; 10 warnings emitted
+warning: there is no parameter `DoesNotExist` on trait `Test`
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:32
+   |
+LL | #[diagnostic::on_unimplemented(message = "{DoesNotExist}")]
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: expect either a generic argument name or `{Self}` as format argument
+   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+
+error[E0277]: {DoesNotExist}
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:51:15
+   |
+LL |     take_test(());
+   |     --------- ^^ the trait `Test` is not implemented for `()`
+   |     |
+   |     required by a bound introduced by this call
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:34:1
+   |
+LL | trait Test {}
+   | ^^^^^^^^^^
+note: required by a bound in `take_test`
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:40:22
+   |
+LL | fn take_test(_: impl Test) {}
+   |                      ^^^^ required by this bound in `take_test`
+
+error: aborting due to 5 previous errors; 12 warnings emitted
 
 For more information about this error, try `rustc --explain E0277`.