about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJana Dönszelmann <jana@donsz.nl>2025-03-05 14:35:54 +0100
committerJana Dönszelmann <jana@donsz.nl>2025-06-17 23:19:31 +0200
commit672452d573e3f499a8edc4308400a6d116974938 (patch)
tree0ea1d7e9f4949e268394b974c765221575e95732
parentee976bbbcaf85390a00f03dedacd035e7e274e8e (diff)
downloadrust-672452d573e3f499a8edc4308400a6d116974938.tar.gz
rust-672452d573e3f499a8edc4308400a6d116974938.zip
use consistent attr errors in all attribute parsers
-rw-r--r--compiler/rustc_attr_parsing/messages.ftl5
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/confusables.rs12
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/deprecation.rs15
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/inline.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/stability.rs39
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/transparency.rs13
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs73
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs74
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs15
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs2
-rw-r--r--tests/ui/attributes/rustc_confusables.rs4
-rw-r--r--tests/ui/attributes/rustc_confusables.stderr31
-rw-r--r--tests/ui/deprecation/deprecation-sanity.rs10
-rw-r--r--tests/ui/deprecation/deprecation-sanity.stderr97
-rw-r--r--tests/ui/error-codes/E0534.stderr2
-rw-r--r--tests/ui/force-inlining/invalid.stderr6
-rw-r--r--tests/ui/invalid/invalid-inline.stderr4
-rw-r--r--tests/ui/issues/issue-43988.stderr4
-rw-r--r--tests/ui/span/E0535.stderr2
-rw-r--r--tests/ui/stability-attribute/stability-attribute-sanity-2.rs2
-rw-r--r--tests/ui/stability-attribute/stability-attribute-sanity-2.stderr10
-rw-r--r--tests/ui/stability-attribute/stability-attribute-sanity.rs8
-rw-r--r--tests/ui/stability-attribute/stability-attribute-sanity.stderr32
23 files changed, 308 insertions, 154 deletions
diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl
index 2687bfdc716..70de83f2f74 100644
--- a/compiler/rustc_attr_parsing/messages.ftl
+++ b/compiler/rustc_attr_parsing/messages.ftl
@@ -27,8 +27,6 @@ attr_parsing_ill_formed_attribute_input = {$num_suggestions ->
         [1] attribute must be of the form {$suggestions}
         *[other] valid forms for the attribute are {$suggestions}
     }
-attr_parsing_incorrect_meta_item = expected a quoted string literal
-attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes
 
 attr_parsing_incorrect_repr_format_align_one_arg =
     incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
@@ -85,9 +83,6 @@ attr_parsing_missing_note =
 attr_parsing_missing_since =
     missing 'since'
 
-attr_parsing_multiple_item =
-    multiple '{$item}' items
-
 attr_parsing_multiple_stability_levels =
     multiple stability levels
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
index 0be9d690c32..f4505cbc0e1 100644
--- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
@@ -19,9 +19,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
         template!(List: r#""name1", "name2", ..."#),
         |this, cx, args| {
             let Some(list) = args.list() else {
-                // FIXME(jdonszelmann): error when not a list? Bring validation code here.
-                //       NOTE: currently subsequent attributes are silently ignored using
-                //       tcx.get_attr().
+                cx.expected_list(cx.attr_span);
                 return;
             };
 
@@ -33,13 +31,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
                 let span = param.span();
 
                 let Some(lit) = param.lit() else {
-                    cx.emit_err(session_diagnostics::IncorrectMetaItem {
-                        span,
-                        suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion {
-                            lo: span.shrink_to_lo(),
-                            hi: span.shrink_to_hi(),
-                        }),
-                    });
+                    cx.expected_string_literal(span);
                     continue;
                 };
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
index 692f587ba03..0983a153efa 100644
--- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
@@ -19,7 +19,7 @@ fn get<S: Stage>(
     item: &Option<Symbol>,
 ) -> Option<Symbol> {
     if item.is_some() {
-        cx.emit_err(session_diagnostics::MultipleItem { span: param_span, item: name.to_string() });
+        cx.duplicate_key(param_span, name);
         return None;
     }
     if let Some(v) = arg.name_value() {
@@ -36,8 +36,7 @@ fn get<S: Stage>(
             None
         }
     } else {
-        // FIXME(jdonszelmann): suggestion?
-        cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None });
+        cx.expected_name_value(param_span, Some(name));
         None
     }
 }
@@ -99,15 +98,15 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
                         suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?);
                     }
                     _ => {
-                        cx.emit_err(session_diagnostics::UnknownMetaItem {
-                            span: param_span,
-                            item: param.path().to_string(),
-                            expected: if features.deprecated_suggestion() {
+                        cx.unknown_key(
+                            param_span,
+                            param.path().to_string(),
+                            if features.deprecated_suggestion() {
                                 &["since", "note", "suggestion"]
                             } else {
                                 &["since", "note"]
                             },
-                        });
+                        );
                         return None;
                     }
                 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs
index da56656e3ed..75ae1d6a6c1 100644
--- a/compiler/rustc_attr_parsing/src/attributes/inline.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs
@@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
                     return None;
                 };
 
-                match l.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
+                match l.meta_item().and_then(|i| i.path().word_sym()) {
                     Some(sym::always) => {
                         Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span))
                     }
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index fba725ae164..65de13972a0 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -6,7 +6,7 @@ use rustc_attr_data_structures::{
 };
 use rustc_errors::ErrorGuaranteed;
 use rustc_feature::{AttributeTemplate, template};
-use rustc_span::{Span, Symbol, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
 
 use super::util::parse_version;
 use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser};
@@ -217,12 +217,10 @@ fn insert_value_into_option_or_error<S: Stage>(
     cx: &AcceptContext<'_, '_, S>,
     param: &MetaItemParser<'_>,
     item: &mut Option<Symbol>,
+    name: Ident,
 ) -> Option<()> {
     if item.is_some() {
-        cx.emit_err(session_diagnostics::MultipleItem {
-            span: param.span(),
-            item: param.path().to_string(),
-        });
+        cx.duplicate_key(name.span, name.name);
         None
     } else if let Some(v) = param.args().name_value()
         && let Some(s) = v.value_as_str()
@@ -230,10 +228,7 @@ fn insert_value_into_option_or_error<S: Stage>(
         *item = Some(s);
         Some(())
     } else {
-        cx.emit_err(session_diagnostics::IncorrectMetaItem {
-            span: param.span(),
-            suggestion: None,
-        });
+        cx.expected_name_value(param.span(), Some(name.name));
         None
     }
 }
@@ -259,9 +254,14 @@ pub(crate) fn parse_stability<S: Stage>(
             return None;
         };
 
-        match param.path().word_sym() {
-            Some(sym::feature) => insert_value_into_option_or_error(cx, &param, &mut feature)?,
-            Some(sym::since) => insert_value_into_option_or_error(cx, &param, &mut since)?,
+        let word = param.path().word();
+        match word.map(|i| i.name) {
+            Some(sym::feature) => {
+                insert_value_into_option_or_error(cx, &param, &mut feature, word.unwrap())?
+            }
+            Some(sym::since) => {
+                insert_value_into_option_or_error(cx, &param, &mut since, word.unwrap())?
+            }
             _ => {
                 cx.emit_err(session_diagnostics::UnknownMetaItem {
                     span: param_span,
@@ -328,11 +328,16 @@ pub(crate) fn parse_unstability<S: Stage>(
             return None;
         };
 
-        match param.path().word_sym() {
-            Some(sym::feature) => insert_value_into_option_or_error(cx, &param, &mut feature)?,
-            Some(sym::reason) => insert_value_into_option_or_error(cx, &param, &mut reason)?,
+        let word = param.path().word();
+        match word.map(|i| i.name) {
+            Some(sym::feature) => {
+                insert_value_into_option_or_error(cx, &param, &mut feature, word.unwrap())?
+            }
+            Some(sym::reason) => {
+                insert_value_into_option_or_error(cx, &param, &mut reason, word.unwrap())?
+            }
             Some(sym::issue) => {
-                insert_value_into_option_or_error(cx, &param, &mut issue)?;
+                insert_value_into_option_or_error(cx, &param, &mut issue, word.unwrap())?;
 
                 // These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item
                 // is a name/value pair string literal.
@@ -362,7 +367,7 @@ pub(crate) fn parse_unstability<S: Stage>(
                 is_soft = true;
             }
             Some(sym::implied_by) => {
-                insert_value_into_option_or_error(cx, &param, &mut implied_by)?
+                insert_value_into_option_or_error(cx, &param, &mut implied_by, word.unwrap())?
             }
             Some(sym::old_name) => insert_value_into_option_or_error(cx, &param, &mut old_name)?,
             _ => {
diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
index b0f5dac7fe2..ce5ceb9139a 100644
--- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
@@ -22,12 +22,19 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
         template!(NameValueStr: "transparent|semitransparent|opaque");
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
-        match args.name_value().and_then(|nv| nv.value_as_str()) {
+        let Some(nv) = args.name_value() else {
+            cx.expected_name_value(cx.attr_span, None);
+            return None;
+        };
+        match nv.value_as_str() {
             Some(sym::transparent) => Some(Transparency::Transparent),
             Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque),
             Some(sym::opaque) => Some(Transparency::Opaque),
-            Some(other) => {
-                cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`"));
+            Some(_) => {
+                cx.expected_specific_argument_strings(
+                    nv.value_span,
+                    vec!["transparent", "semitransparent", "opaque"],
+                );
                 None
             }
             None => None,
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index ca95c67b418..d6dfa2c7477 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -27,7 +27,7 @@ use crate::attributes::stability::{
 use crate::attributes::transparency::TransparencyParser;
 use crate::attributes::{AttributeParser as _, Combine, Single};
 use crate::parser::{ArgParser, MetaItemParser};
-use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason};
+use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};
 
 macro_rules! group_type {
     ($stage: ty) => {
@@ -191,8 +191,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         (self.emit_lint)(AttributeLint { id, span, kind: lint });
     }
 
+    pub(crate) fn unknown_key(
+        &self,
+        span: Span,
+        found: String,
+        options: &'static [&'static str],
+    ) -> ErrorGuaranteed {
+        self.emit_err(UnknownMetaItem { span, item: found, expected: options })
+    }
+
     pub(crate) fn expected_string_literal(&self, span: Span) -> ErrorGuaranteed {
-        // 539?
         self.emit_err(AttributeParseError {
             span,
             attr_span: self.attr_span,
@@ -202,12 +210,40 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         })
     }
 
-    // pub(crate) fn expected_any_arguments(&self, span: Span) -> ErrorGuaranteed {
-    //
-    // }
+    pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed {
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedList,
+        })
+    }
+
+    /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for
+    /// a nicer error message talking about the specific name that was found lacking a value.
+    pub(crate) fn expected_name_value(&self, span: Span, name: Option<Symbol>) -> ErrorGuaranteed {
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedNameValue(name),
+        })
+    }
+
+    /// emit an error that a `name = value` pair was found where that name was already seen.
+    pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed {
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::DuplicateKey(key),
+        })
+    }
 
     pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed {
-        // E534?
         self.emit_err(AttributeParseError {
             span,
             attr_span: self.attr_span,
@@ -220,15 +256,34 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
     pub(crate) fn expected_specific_argument(
         &self,
         span: Span,
-        options: Vec<&'static str>,
+        possibilities: Vec<&'static str>,
+    ) -> ErrorGuaranteed {
+        self.emit_err(AttributeParseError {
+            span,
+            attr_span: self.attr_span,
+            template: self.template.clone(),
+            attribute: self.attr_path.clone(),
+            reason: AttributeParseErrorReason::ExpectedSpecificArgument {
+                possibilities,
+                strings: false,
+            },
+        })
+    }
+
+    pub(crate) fn expected_specific_argument_strings(
+        &self,
+        span: Span,
+        possibilities: Vec<&'static str>,
     ) -> ErrorGuaranteed {
-        // E535?
         self.emit_err(AttributeParseError {
             span,
             attr_span: self.attr_span,
             template: self.template.clone(),
             attribute: self.attr_path.clone(),
-            reason: AttributeParseErrorReason::ExpectedSpecificArgument(options),
+            reason: AttributeParseErrorReason::ExpectedSpecificArgument {
+                possibilities,
+                strings: true,
+            },
         })
     }
 }
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 08cd8b5df2d..3d865c1d14e 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -36,37 +36,6 @@ pub(crate) struct InvalidPredicate {
     pub predicate: String,
 }
 
-#[derive(Diagnostic)]
-#[diag(attr_parsing_multiple_item, code = E0538)]
-pub(crate) struct MultipleItem {
-    #[primary_span]
-    pub span: Span,
-
-    pub item: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(attr_parsing_incorrect_meta_item, code = E0539)]
-pub(crate) struct IncorrectMetaItem {
-    #[primary_span]
-    pub span: Span,
-
-    #[subdiagnostic]
-    pub suggestion: Option<IncorrectMetaItemSuggestion>,
-}
-
-#[derive(Subdiagnostic)]
-#[multipart_suggestion(
-    attr_parsing_incorrect_meta_item_suggestion,
-    applicability = "maybe-incorrect"
-)]
-pub(crate) struct IncorrectMetaItemSuggestion {
-    #[suggestion_part(code = "\"")]
-    pub lo: Span,
-    #[suggestion_part(code = "\"")]
-    pub hi: Span,
-}
-
 /// Error code: E0541
 pub(crate) struct UnknownMetaItem<'a> {
     pub span: Span,
@@ -506,7 +475,10 @@ pub(crate) struct UnrecognizedReprHint {
 pub(crate) enum AttributeParseErrorReason {
     ExpectedStringLiteral,
     ExpectedSingleArgument,
-    ExpectedSpecificArgument(Vec<&'static str>),
+    ExpectedList,
+    ExpectedNameValue(Option<Symbol>),
+    DuplicateKey(Symbol),
+    ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool },
 }
 
 pub(crate) struct AttributeParseError {
@@ -531,24 +503,46 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
             AttributeParseErrorReason::ExpectedSingleArgument => {
                 diag.span_note(self.span, "expected a single argument here");
             }
-            AttributeParseErrorReason::ExpectedSpecificArgument(possibilities) => {
+            AttributeParseErrorReason::ExpectedList => {
+                diag.span_note(self.span, "expected this to be a list");
+            }
+            AttributeParseErrorReason::DuplicateKey(key) => {
+                diag.span_note(self.span, format!("found `{key}` used as a key more than once"));
+                diag.code(E0538);
+            }
+            AttributeParseErrorReason::ExpectedNameValue(None) => {
+                diag.span_note(
+                    self.span,
+                    format!("expected this to be of the form `{name} = \"...\"`"),
+                );
+            }
+            AttributeParseErrorReason::ExpectedNameValue(Some(name)) => {
+                diag.span_note(
+                    self.span,
+                    format!("expected this to be of the form `{name} = \"...\"`"),
+                );
+            }
+            AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => {
+                let quote = if strings { '"' } else { '`' };
                 match possibilities.as_slice() {
                     &[] => {}
                     &[x] => {
-                        diag.span_note(self.span, format!("the only valid argument here is `{x}`"));
-                    }
-                    [first, second] => {
                         diag.span_note(
                             self.span,
-                            format!("valid arguments are `{first}` or `{second}`"),
+                            format!("the only valid argument here is {quote}{x}{quote}"),
                         );
                     }
+                    [first, second] => {
+                        diag.span_note(self.span, format!("valid arguments are {quote}{first}{quote} or {quote}{second}{quote}"));
+                    }
                     [first @ .., second_to_last, last] => {
                         let mut res = String::new();
                         for i in first {
-                            res.push_str(&format!("`{i}`, "));
+                            res.push_str(&format!("{quote}{i}{quote}, "));
                         }
-                        res.push_str(&format!("`{second_to_last}` or `{last}`"));
+                        res.push_str(&format!(
+                            "{quote}{second_to_last}{quote} or {quote}{last}{quote}"
+                        ));
 
                         diag.span_note(self.span, format!("valid arguments are {res}"));
                     }
@@ -562,7 +556,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
             if suggestions.len() == 1 {
                 "must be of the form"
             } else {
-                "the following are possible correct uses"
+                "try changing it to one of the following valid forms of the attribute"
             },
             suggestions,
             Applicability::HasPlaceholders,
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index bac02bdf983..5387b2a7f81 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -209,13 +209,6 @@ pub(crate) struct OutOfRangeInteger {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_ssa_expected_one_argument, code = E0534)]
-pub(crate) struct ExpectedOneArgument {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_ssa_expected_one_argument, code = E0722)]
 pub(crate) struct ExpectedOneArgumentOptimize {
     #[primary_span]
@@ -223,14 +216,6 @@ pub(crate) struct ExpectedOneArgumentOptimize {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_ssa_invalid_argument, code = E0535)]
-#[help]
-pub(crate) struct InvalidArgument {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_ssa_invalid_argument, code = E0722)]
 pub(crate) struct InvalidArgumentOptimize {
     #[primary_span]
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 1c12e8c189d..2296ec10432 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -283,7 +283,7 @@ fn emit_malformed_attribute(
     template: AttributeTemplate,
 ) {
     // attrs with new parsers are locally validated so excluded here
-    if matches!(name, sym::inline | sym::rustc_force_inline) {
+    if matches!(name, sym::inline | sym::rustc_force_inline | sym::rustc_confusables) {
         return;
     }
 
diff --git a/tests/ui/attributes/rustc_confusables.rs b/tests/ui/attributes/rustc_confusables.rs
index 93d9a7d572c..a8095936cff 100644
--- a/tests/ui/attributes/rustc_confusables.rs
+++ b/tests/ui/attributes/rustc_confusables.rs
@@ -37,8 +37,8 @@ impl Bar {
     fn qux() {}
 
     #[rustc_confusables(invalid_meta_item)]
-    //~^ ERROR expected a quoted string literal
-    //~| HELP consider surrounding this with quotes
+    //~^ ERROR malformed `rustc_confusables` attribute input [E0539]
+    //~| HELP must be of the form
     fn quux() {}
 }
 
diff --git a/tests/ui/attributes/rustc_confusables.stderr b/tests/ui/attributes/rustc_confusables.stderr
index aba384ff8ac..be16da2f873 100644
--- a/tests/ui/attributes/rustc_confusables.stderr
+++ b/tests/ui/attributes/rustc_confusables.stderr
@@ -1,25 +1,32 @@
-error: malformed `rustc_confusables` attribute input
-  --> $DIR/rustc_confusables.rs:34:5
-   |
-LL |     #[rustc_confusables]
-   |     ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]`
-
 error: expected at least one confusable name
   --> $DIR/rustc_confusables.rs:30:5
    |
 LL |     #[rustc_confusables()]
    |     ^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0539]: expected a quoted string literal
-  --> $DIR/rustc_confusables.rs:39:25
+error[E0539]: malformed `rustc_confusables` attribute input
+  --> $DIR/rustc_confusables.rs:34:5
+   |
+LL |     #[rustc_confusables]
+   |     ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]`
+   |
+note: expected this to be a list
+  --> $DIR/rustc_confusables.rs:34:5
+   |
+LL |     #[rustc_confusables]
+   |     ^^^^^^^^^^^^^^^^^^^^
+
+error[E0539]: malformed `rustc_confusables` attribute input
+  --> $DIR/rustc_confusables.rs:39:5
    |
 LL |     #[rustc_confusables(invalid_meta_item)]
-   |                         ^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]`
    |
-help: consider surrounding this with quotes
+note: expected a string literal here
+  --> $DIR/rustc_confusables.rs:39:25
    |
-LL |     #[rustc_confusables("invalid_meta_item")]
-   |                         +                 +
+LL |     #[rustc_confusables(invalid_meta_item)]
+   |                         ^^^^^^^^^^^^^^^^^
 
 error: attribute should be applied to an inherent method
   --> $DIR/rustc_confusables.rs:45:1
diff --git a/tests/ui/deprecation/deprecation-sanity.rs b/tests/ui/deprecation/deprecation-sanity.rs
index d5b149b18ed..e40b017378a 100644
--- a/tests/ui/deprecation/deprecation-sanity.rs
+++ b/tests/ui/deprecation/deprecation-sanity.rs
@@ -4,16 +4,16 @@ mod bogus_attribute_types_1 {
     #[deprecated(since = "a", note = "a", reason)] //~ ERROR unknown meta item 'reason'
     fn f1() { }
 
-    #[deprecated(since = "a", note)] //~ ERROR expected a quoted string literal
+    #[deprecated(since = "a", note)] //~ ERROR malformed `deprecated` attribute input [E0539]
     fn f2() { }
 
-    #[deprecated(since, note = "a")] //~ ERROR expected a quoted string literal
+    #[deprecated(since, note = "a")] //~ ERROR malformed `deprecated` attribute input [E0539]
     fn f3() { }
 
-    #[deprecated(since = "a", note(b))] //~ ERROR expected a quoted string literal
+    #[deprecated(since = "a", note(b))] //~ ERROR malformed `deprecated` attribute input [E0539]
     fn f5() { }
 
-    #[deprecated(since(b), note = "a")] //~ ERROR expected a quoted string literal
+    #[deprecated(since(b), note = "a")] //~ ERROR malformed `deprecated` attribute input [E0539]
     fn f6() { }
 
     #[deprecated(note = b"test")] //~ ERROR literal in `deprecated` value must be a string
@@ -27,7 +27,7 @@ mod bogus_attribute_types_1 {
 #[deprecated(since = "a", note = "b")] //~ ERROR multiple `deprecated` attributes
 fn multiple1() { }
 
-#[deprecated(since = "a", since = "b", note = "c")] //~ ERROR multiple 'since' items
+#[deprecated(since = "a", since = "b", note = "c")] //~ ERROR malformed `deprecated` attribute input [E0538]
 fn f1() { }
 
 struct X;
diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr
index 53047d40cb2..8854ebb70d2 100644
--- a/tests/ui/deprecation/deprecation-sanity.stderr
+++ b/tests/ui/deprecation/deprecation-sanity.stderr
@@ -4,29 +4,97 @@ error[E0541]: unknown meta item 'reason'
 LL |     #[deprecated(since = "a", note = "a", reason)]
    |                                           ^^^^^^ expected one of `since`, `note`
 
-error[E0539]: expected a quoted string literal
+error[E0539]: malformed `deprecated` attribute input
+  --> $DIR/deprecation-sanity.rs:7:5
+   |
+LL |     #[deprecated(since = "a", note)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: expected this to be of the form `note = "..."`
   --> $DIR/deprecation-sanity.rs:7:31
    |
 LL |     #[deprecated(since = "a", note)]
    |                               ^^^^
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL -     #[deprecated(since = "a", note)]
+LL +     #[deprecated = "reason"]
+   |
+LL -     #[deprecated(since = "a", note)]
+LL +     #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+   |
+LL -     #[deprecated(since = "a", note)]
+LL +     #[deprecated]
+   |
 
-error[E0539]: expected a quoted string literal
+error[E0539]: malformed `deprecated` attribute input
+  --> $DIR/deprecation-sanity.rs:10:5
+   |
+LL |     #[deprecated(since, note = "a")]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: expected this to be of the form `since = "..."`
   --> $DIR/deprecation-sanity.rs:10:18
    |
 LL |     #[deprecated(since, note = "a")]
    |                  ^^^^^
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL -     #[deprecated(since, note = "a")]
+LL +     #[deprecated = "reason"]
+   |
+LL -     #[deprecated(since, note = "a")]
+LL +     #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+   |
+LL -     #[deprecated(since, note = "a")]
+LL +     #[deprecated]
+   |
 
-error[E0539]: expected a quoted string literal
+error[E0539]: malformed `deprecated` attribute input
+  --> $DIR/deprecation-sanity.rs:13:5
+   |
+LL |     #[deprecated(since = "a", note(b))]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: expected this to be of the form `note = "..."`
   --> $DIR/deprecation-sanity.rs:13:31
    |
 LL |     #[deprecated(since = "a", note(b))]
    |                               ^^^^^^^
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL -     #[deprecated(since = "a", note(b))]
+LL +     #[deprecated = "reason"]
+   |
+LL -     #[deprecated(since = "a", note(b))]
+LL +     #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+   |
+LL -     #[deprecated(since = "a", note(b))]
+LL +     #[deprecated]
+   |
 
-error[E0539]: expected a quoted string literal
+error[E0539]: malformed `deprecated` attribute input
+  --> $DIR/deprecation-sanity.rs:16:5
+   |
+LL |     #[deprecated(since(b), note = "a")]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: expected this to be of the form `since = "..."`
   --> $DIR/deprecation-sanity.rs:16:18
    |
 LL |     #[deprecated(since(b), note = "a")]
    |                  ^^^^^^^^
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL -     #[deprecated(since(b), note = "a")]
+LL +     #[deprecated = "reason"]
+   |
+LL -     #[deprecated(since(b), note = "a")]
+LL +     #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+   |
+LL -     #[deprecated(since(b), note = "a")]
+LL +     #[deprecated]
+   |
 
 error[E0565]: literal in `deprecated` value must be a string
   --> $DIR/deprecation-sanity.rs:19:25
@@ -54,11 +122,28 @@ note: attribute also specified here
 LL | #[deprecated(since = "a", note = "b")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error[E0538]: multiple 'since' items
+error[E0538]: malformed `deprecated` attribute input
+  --> $DIR/deprecation-sanity.rs:30:1
+   |
+LL | #[deprecated(since = "a", since = "b", note = "c")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: found `since` used as a key more than once
   --> $DIR/deprecation-sanity.rs:30:27
    |
 LL | #[deprecated(since = "a", since = "b", note = "c")]
-   |                           ^^^^^^^^^^^
+   |                           ^^^^^
+help: try changing it to one of the following valid forms of the attribute
+   |
+LL - #[deprecated(since = "a", since = "b", note = "c")]
+LL + #[deprecated = "reason"]
+   |
+LL - #[deprecated(since = "a", since = "b", note = "c")]
+LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
+   |
+LL - #[deprecated(since = "a", since = "b", note = "c")]
+LL + #[deprecated]
+   |
 
 error: this `#[deprecated]` annotation has no effect
   --> $DIR/deprecation-sanity.rs:35:1
diff --git a/tests/ui/error-codes/E0534.stderr b/tests/ui/error-codes/E0534.stderr
index f63d80c220c..de97ef18b73 100644
--- a/tests/ui/error-codes/E0534.stderr
+++ b/tests/ui/error-codes/E0534.stderr
@@ -9,7 +9,7 @@ note: expected a single argument here
    |
 LL | #[inline()]
    |         ^^
-help: the following are possible correct uses
+help: try changing it to one of the following valid forms of the attribute
    |
 LL | #[inline(always|never)]
    |          ++++++++++++
diff --git a/tests/ui/force-inlining/invalid.stderr b/tests/ui/force-inlining/invalid.stderr
index 26259a4d61f..a12f633b3f2 100644
--- a/tests/ui/force-inlining/invalid.stderr
+++ b/tests/ui/force-inlining/invalid.stderr
@@ -15,7 +15,7 @@ note: expected a single argument here
    |
 LL | #[rustc_force_inline(bar, baz)]
    |                     ^^^^^^^^^^
-help: the following are possible correct uses
+help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[rustc_force_inline(bar, baz)]
 LL + #[rustc_force_inline = "reason"]
@@ -38,7 +38,7 @@ note: expected a string literal here
    |
 LL | #[rustc_force_inline(2)]
    |                      ^
-help: the following are possible correct uses
+help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[rustc_force_inline(2)]
 LL + #[rustc_force_inline = "reason"]
@@ -61,7 +61,7 @@ note: expected a string literal here
    |
 LL | #[rustc_force_inline = 2]
    |                        ^
-help: the following are possible correct uses
+help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[rustc_force_inline = 2]
 LL + #[rustc_force_inline = "reason"]
diff --git a/tests/ui/invalid/invalid-inline.stderr b/tests/ui/invalid/invalid-inline.stderr
index 965da7e7567..8a230351d32 100644
--- a/tests/ui/invalid/invalid-inline.stderr
+++ b/tests/ui/invalid/invalid-inline.stderr
@@ -9,7 +9,7 @@ note: expected a single argument here
    |
 LL | #[inline(please,no)]
    |         ^^^^^^^^^^^
-help: the following are possible correct uses
+help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[inline(please,no)]
 LL + #[inline(always|never)]
@@ -29,7 +29,7 @@ note: expected a single argument here
    |
 LL | #[inline()]
    |         ^^
-help: the following are possible correct uses
+help: try changing it to one of the following valid forms of the attribute
    |
 LL | #[inline(always|never)]
    |          ++++++++++++
diff --git a/tests/ui/issues/issue-43988.stderr b/tests/ui/issues/issue-43988.stderr
index 74c3e81cb1c..8e731db6f2e 100644
--- a/tests/ui/issues/issue-43988.stderr
+++ b/tests/ui/issues/issue-43988.stderr
@@ -21,7 +21,7 @@ note: valid arguments are `always` or `never`
    |
 LL |     #[inline(XYZ)]
    |              ^^^
-help: the following are possible correct uses
+help: try changing it to one of the following valid forms of the attribute
    |
 LL -     #[inline(XYZ)]
 LL +     #[inline(always|never)]
@@ -57,7 +57,7 @@ note: valid arguments are `always` or `never`
    |
 LL |     #[inline(ABC)]
    |              ^^^
-help: the following are possible correct uses
+help: try changing it to one of the following valid forms of the attribute
    |
 LL -     #[inline(ABC)]
 LL +     #[inline(always|never)]
diff --git a/tests/ui/span/E0535.stderr b/tests/ui/span/E0535.stderr
index 54dcdd89579..fb0b72c0001 100644
--- a/tests/ui/span/E0535.stderr
+++ b/tests/ui/span/E0535.stderr
@@ -9,7 +9,7 @@ note: valid arguments are `always` or `never`
    |
 LL | #[inline(unknown)]
    |          ^^^^^^^
-help: the following are possible correct uses
+help: try changing it to one of the following valid forms of the attribute
    |
 LL - #[inline(unknown)]
 LL + #[inline(always|never)]
diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-2.rs b/tests/ui/stability-attribute/stability-attribute-sanity-2.rs
index de3ea4eaca9..92e300d33d6 100644
--- a/tests/ui/stability-attribute/stability-attribute-sanity-2.rs
+++ b/tests/ui/stability-attribute/stability-attribute-sanity-2.rs
@@ -4,7 +4,7 @@
 
 #![stable(feature = "stable_test_feature", since = "1.0.0")]
 
-#[stable(feature = "a", feature = "b", since = "1.0.0")] //~ ERROR multiple 'feature' items
+#[stable(feature = "a", feature = "b", since = "1.0.0")] //~ ERROR malformed `stable` attribute input [E0538]
 fn f1() { }
 
 #[stable(feature = "a", sinse = "1.0.0")] //~ ERROR unknown meta item 'sinse'
diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr b/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr
index 8dbcc6c97ef..412af87bad6 100644
--- a/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr
+++ b/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr
@@ -1,8 +1,14 @@
-error[E0538]: multiple 'feature' items
+error[E0538]: malformed `stable` attribute input
+  --> $DIR/stability-attribute-sanity-2.rs:7:1
+   |
+LL | #[stable(feature = "a", feature = "b", since = "1.0.0")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]`
+   |
+note: found `feature` used as a key more than once
   --> $DIR/stability-attribute-sanity-2.rs:7:25
    |
 LL | #[stable(feature = "a", feature = "b", since = "1.0.0")]
-   |                         ^^^^^^^^^^^^^
+   |                         ^^^^^^^
 
 error[E0541]: unknown meta item 'sinse'
   --> $DIR/stability-attribute-sanity-2.rs:10:25
diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.rs b/tests/ui/stability-attribute/stability-attribute-sanity.rs
index f46e35e1a72..c4c86e12d26 100644
--- a/tests/ui/stability-attribute/stability-attribute-sanity.rs
+++ b/tests/ui/stability-attribute/stability-attribute-sanity.rs
@@ -8,16 +8,16 @@ mod bogus_attribute_types_1 {
     #[stable(feature = "a", since = "4.4.4", reason)] //~ ERROR unknown meta item 'reason' [E0541]
     fn f1() { }
 
-    #[stable(feature = "a", since)] //~ ERROR expected a quoted string literal [E0539]
+    #[stable(feature = "a", since)] //~ ERROR malformed `stable` attribute input [E0539]
     fn f2() { }
 
-    #[stable(feature, since = "3.3.3")] //~ ERROR expected a quoted string literal [E0539]
+    #[stable(feature, since = "3.3.3")] //~ ERROR malformed `stable` attribute input [E0539]
     fn f3() { }
 
-    #[stable(feature = "a", since(b))] //~ ERROR expected a quoted string literal [E0539]
+    #[stable(feature = "a", since(b))] //~ ERROR malformed `stable` attribute input [E0539]
     fn f5() { }
 
-    #[stable(feature(b), since = "3.3.3")] //~ ERROR expected a quoted string literal [E0539]
+    #[stable(feature(b), since = "3.3.3")] //~ ERROR malformed `stable` attribute input [E0539]
     fn f6() { }
 }
 
diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.stderr b/tests/ui/stability-attribute/stability-attribute-sanity.stderr
index 2e2b5b509c8..86284b0c1b8 100644
--- a/tests/ui/stability-attribute/stability-attribute-sanity.stderr
+++ b/tests/ui/stability-attribute/stability-attribute-sanity.stderr
@@ -4,25 +4,49 @@ error[E0541]: unknown meta item 'reason'
 LL |     #[stable(feature = "a", since = "4.4.4", reason)]
    |                                              ^^^^^^ expected one of `feature`, `since`
 
-error[E0539]: expected a quoted string literal
+error[E0539]: malformed `stable` attribute input
+  --> $DIR/stability-attribute-sanity.rs:11:5
+   |
+LL |     #[stable(feature = "a", since)]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]`
+   |
+note: expected this to be of the form `since = "..."`
   --> $DIR/stability-attribute-sanity.rs:11:29
    |
 LL |     #[stable(feature = "a", since)]
    |                             ^^^^^
 
-error[E0539]: expected a quoted string literal
+error[E0539]: malformed `stable` attribute input
+  --> $DIR/stability-attribute-sanity.rs:14:5
+   |
+LL |     #[stable(feature, since = "3.3.3")]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]`
+   |
+note: expected this to be of the form `feature = "..."`
   --> $DIR/stability-attribute-sanity.rs:14:14
    |
 LL |     #[stable(feature, since = "3.3.3")]
    |              ^^^^^^^
 
-error[E0539]: expected a quoted string literal
+error[E0539]: malformed `stable` attribute input
+  --> $DIR/stability-attribute-sanity.rs:17:5
+   |
+LL |     #[stable(feature = "a", since(b))]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]`
+   |
+note: expected this to be of the form `since = "..."`
   --> $DIR/stability-attribute-sanity.rs:17:29
    |
 LL |     #[stable(feature = "a", since(b))]
    |                             ^^^^^^^^
 
-error[E0539]: expected a quoted string literal
+error[E0539]: malformed `stable` attribute input
+  --> $DIR/stability-attribute-sanity.rs:20:5
+   |
+LL |     #[stable(feature(b), since = "3.3.3")]
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]`
+   |
+note: expected this to be of the form `feature = "..."`
   --> $DIR/stability-attribute-sanity.rs:20:14
    |
 LL |     #[stable(feature(b), since = "3.3.3")]