about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-06-24 15:51:03 -0400
committerGitHub <noreply@github.com>2024-06-24 15:51:03 -0400
commit9ce2a070b3fbb053644c8f0ae569957ae08034e4 (patch)
tree93fefed36bcbce497e0b3e828186816f0cfd388e
parent49bdf460a22ee85e27b311f3cde099cfa592f865 (diff)
parent1852141219b39b2a6bb13ad273c96371d58a41e0 (diff)
downloadrust-9ce2a070b3fbb053644c8f0ae569957ae08034e4.tar.gz
rust-9ce2a070b3fbb053644c8f0ae569957ae08034e4.zip
Rollup merge of #126682 - Zalathar:coverage-attr, r=lcnr
coverage: Overhaul validation of the `#[coverage(..)]` attribute

This PR makes sweeping changes to how the (currently-unstable) coverage attribute is validated:
- Multiple coverage attributes on the same item/expression are now treated as an error.
- The attribute must always be `#[coverage(off)]` or `#[coverage(on)]`, and the error messages for this are more consistent.
  -  A trailing comma is still allowed after off/on, since that's part of the normal attribute syntax.
- Some places that silently ignored a coverage attribute now produce an error instead.
  - These cases were all clearly bugs.
- Some places that ignored a coverage attribute (with a warning) now produce an error instead.
  - These were originally added as lints, but I don't think it makes much sense to knowingly allow new attributes to be used in meaningless places.
  - Some of these errors might soon disappear, if it's easy to extend recursive coverage attributes to things like modules and impl blocks.

---

One of the goals of this PR is to lay a more solid foundation for making the coverage attribute recursive, so that it applies to all nested functions/closures instead of just the one it is directly attached to.

Fixes #126658.

This PR incorporates #126659, which adds more tests for validation of the coverage attribute.

`@rustbot` label +A-code-coverage
-rw-r--r--compiler/rustc_codegen_ssa/messages.ftl2
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs11
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs7
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs26
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs13
-rw-r--r--compiler/rustc_passes/messages.ftl15
-rw-r--r--compiler/rustc_passes/src/check_attr.rs42
-rw-r--r--compiler/rustc_passes/src/errors.rs16
-rw-r--r--tests/ui/coverage-attr/bad-syntax.rs39
-rw-r--r--tests/ui/coverage-attr/bad-syntax.stderr137
-rw-r--r--tests/ui/coverage-attr/name-value.rs51
-rw-r--r--tests/ui/coverage-attr/name-value.stderr194
-rw-r--r--tests/ui/coverage-attr/no-coverage.rs30
-rw-r--r--tests/ui/coverage-attr/no-coverage.stderr103
-rw-r--r--tests/ui/coverage-attr/subword.rs8
-rw-r--r--tests/ui/coverage-attr/subword.stderr36
-rw-r--r--tests/ui/coverage-attr/word-only.rs45
-rw-r--r--tests/ui/coverage-attr/word-only.stderr270
18 files changed, 687 insertions, 358 deletions
diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl
index 1a851ad04a1..000fe2e3ce0 100644
--- a/compiler/rustc_codegen_ssa/messages.ftl
+++ b/compiler/rustc_codegen_ssa/messages.ftl
@@ -27,8 +27,6 @@ codegen_ssa_create_temp_dir = couldn't create a temp dir: {$error}
 
 codegen_ssa_error_creating_remark_dir = failed to create remark directory: {$error}
 
-codegen_ssa_expected_coverage_symbol = expected `coverage(off)` or `coverage(on)`
-
 codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
 
 codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 15955170e87..fb71cdaa8ff 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -15,11 +15,7 @@ use rustc_span::{sym, Span};
 use rustc_target::spec::{abi, SanitizerSet};
 
 use crate::errors;
-use crate::target_features::from_target_feature;
-use crate::{
-    errors::{ExpectedCoverageSymbol, ExpectedUsedSymbol},
-    target_features::check_target_feature_trait_unsafe,
-};
+use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature};
 
 fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
     use rustc_middle::mir::mono::Linkage::*;
@@ -139,7 +135,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         // coverage on a smaller scope within an excluded larger scope.
                     }
                     Some(_) | None => {
-                        tcx.dcx().emit_err(ExpectedCoverageSymbol { span: attr.span });
+                        tcx.dcx()
+                            .span_delayed_bug(attr.span, "unexpected value of coverage attribute");
                     }
                 }
             }
@@ -174,7 +171,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
                     }
                     Some(_) => {
-                        tcx.dcx().emit_err(ExpectedUsedSymbol { span: attr.span });
+                        tcx.dcx().emit_err(errors::ExpectedUsedSymbol { span: attr.span });
                     }
                     None => {
                         // Unfortunately, unconditionally using `llvm.used` causes
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index e6ba31c5165..e9d31db9254 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -565,13 +565,6 @@ pub struct UnknownArchiveKind<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(codegen_ssa_expected_coverage_symbol)]
-pub struct ExpectedCoverageSymbol {
-    #[primary_span]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(codegen_ssa_expected_used_symbol)]
 pub struct ExpectedUsedSymbol {
     #[primary_span]
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 93104402df9..c53bf965139 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -105,6 +105,9 @@ pub struct AttributeTemplate {
     pub word: bool,
     /// If `Some`, the attribute is allowed to take a list of items like `#[allow(..)]`.
     pub list: Option<&'static str>,
+    /// If non-empty, the attribute is allowed to take a list containing exactly
+    /// one of the listed words, like `#[coverage(off)]`.
+    pub one_of: &'static [Symbol],
     /// If `Some`, the attribute is allowed to be a name/value pair where the
     /// value is a string, like `#[must_use = "reason"]`.
     pub name_value_str: Option<&'static str>,
@@ -165,19 +168,20 @@ pub enum AttributeDuplicates {
 /// E.g., `template!(Word, List: "description")` means that the attribute
 /// supports forms `#[attr]` and `#[attr(description)]`.
 macro_rules! template {
-    (Word) => { template!(@ true, None, None) };
-    (List: $descr: expr) => { template!(@ false, Some($descr), None) };
-    (NameValueStr: $descr: expr) => { template!(@ false, None, Some($descr)) };
-    (Word, List: $descr: expr) => { template!(@ true, Some($descr), None) };
-    (Word, NameValueStr: $descr: expr) => { template!(@ true, None, Some($descr)) };
+    (Word) => { template!(@ true, None, &[], None) };
+    (List: $descr: expr) => { template!(@ false, Some($descr), &[], None) };
+    (OneOf: $one_of: expr) => { template!(@ false, None, $one_of, None) };
+    (NameValueStr: $descr: expr) => { template!(@ false, None, &[], Some($descr)) };
+    (Word, List: $descr: expr) => { template!(@ true, Some($descr), &[], None) };
+    (Word, NameValueStr: $descr: expr) => { template!(@ true, None, &[], Some($descr)) };
     (List: $descr1: expr, NameValueStr: $descr2: expr) => {
-        template!(@ false, Some($descr1), Some($descr2))
+        template!(@ false, Some($descr1), &[], Some($descr2))
     };
     (Word, List: $descr1: expr, NameValueStr: $descr2: expr) => {
-        template!(@ true, Some($descr1), Some($descr2))
+        template!(@ true, Some($descr1), &[], Some($descr2))
     };
-    (@ $word: expr, $list: expr, $name_value_str: expr) => { AttributeTemplate {
-        word: $word, list: $list, name_value_str: $name_value_str
+    (@ $word: expr, $list: expr, $one_of: expr, $name_value_str: expr) => { AttributeTemplate {
+        word: $word, list: $list, one_of: $one_of, name_value_str: $name_value_str
     } };
 }
 
@@ -478,8 +482,8 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         EncodeCrossCrate::No, experimental!(no_sanitize)
     ),
     gated!(
-        coverage, Normal, template!(Word, List: "on|off"),
-        WarnFollowing, EncodeCrossCrate::No,
+        coverage, Normal, template!(OneOf: &[sym::off, sym::on]),
+        ErrorPreceding, EncodeCrossCrate::No,
         coverage_attribute, experimental!(coverage)
     ),
 
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index bcb1131cc19..3d5e6371f4c 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -4,8 +4,10 @@ use crate::{errors, parse_in};
 
 use rustc_ast::token::Delimiter;
 use rustc_ast::tokenstream::DelimSpan;
-use rustc_ast::MetaItemKind;
-use rustc_ast::{self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem, Safety};
+use rustc_ast::{
+    self as ast, AttrArgs, AttrArgsEq, Attribute, DelimArgs, MetaItem, MetaItemKind,
+    NestedMetaItem, Safety,
+};
 use rustc_errors::{Applicability, FatalError, PResult};
 use rustc_feature::{
     AttributeSafety, AttributeTemplate, BuiltinAttribute, Features, BUILTIN_ATTRIBUTE_MAP,
@@ -184,9 +186,13 @@ pub(super) fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim
 
 /// Checks that the given meta-item is compatible with this `AttributeTemplate`.
 fn is_attr_template_compatible(template: &AttributeTemplate, meta: &ast::MetaItemKind) -> bool {
+    let is_one_allowed_subword = |items: &[NestedMetaItem]| match items {
+        [item] => item.is_word() && template.one_of.iter().any(|&word| item.has_name(word)),
+        _ => false,
+    };
     match meta {
         MetaItemKind::Word => template.word,
-        MetaItemKind::List(..) => template.list.is_some(),
+        MetaItemKind::List(items) => template.list.is_some() || is_one_allowed_subword(items),
         MetaItemKind::NameValue(lit) if lit.kind.is_str() => template.name_value_str.is_some(),
         MetaItemKind::NameValue(..) => false,
     }
@@ -230,6 +236,7 @@ fn emit_malformed_attribute(
     if let Some(descr) = template.list {
         suggestions.push(format!("#{inner}[{name}({descr})]"));
     }
+    suggestions.extend(template.one_of.iter().map(|&word| format!("#{inner}[{name}({word})]")));
     if let Some(descr) = template.name_value_str {
         suggestions.push(format!("#{inner}[{name} = \"{descr}\"]"));
     }
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 9a830b0f49b..5a560325ab9 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -103,18 +103,9 @@ passes_continue_labeled_block =
     .label = labeled blocks cannot be `continue`'d
     .block_label = labeled block the `continue` points to
 
-passes_coverage_fn_defn =
-    `#[coverage]` may only be applied to function definitions
-
-passes_coverage_ignored_function_prototype =
-    `#[coverage]` is ignored on function prototypes
-
-passes_coverage_not_coverable =
-    `#[coverage]` must be applied to coverable code
-    .label = not coverable code
-
-passes_coverage_propagate =
-    `#[coverage]` does not propagate into items and must be applied to the contained functions directly
+passes_coverage_not_fn_or_closure =
+    attribute should be applied to a function definition or closure
+    .label = not a function or closure
 
 passes_dead_codes =
     { $multiple ->
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 2ed5bba85c6..d33f12a973f 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -122,7 +122,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     self.check_diagnostic_on_unimplemented(attr.span, hir_id, target)
                 }
                 [sym::inline] => self.check_inline(hir_id, attr, span, target),
-                [sym::coverage] => self.check_coverage(hir_id, attr, span, target),
+                [sym::coverage] => self.check_coverage(attr, span, target),
                 [sym::non_exhaustive] => self.check_non_exhaustive(hir_id, attr, span, target),
                 [sym::marker] => self.check_marker(hir_id, attr, span, target),
                 [sym::target_feature] => {
@@ -369,47 +369,15 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
-    /// Checks if a `#[coverage]` is applied directly to a function
-    fn check_coverage(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
+    /// Checks that `#[coverage(..)]` is applied to a function or closure.
+    fn check_coverage(&self, attr: &Attribute, span: Span, target: Target) -> bool {
         match target {
-            // #[coverage] on function is fine
+            // #[coverage(..)] on function is fine
             Target::Fn
             | Target::Closure
             | Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => true,
-
-            // function prototypes can't be covered
-            Target::Method(MethodKind::Trait { body: false }) | Target::ForeignFn => {
-                self.tcx.emit_node_span_lint(
-                    UNUSED_ATTRIBUTES,
-                    hir_id,
-                    attr.span,
-                    errors::IgnoredCoverageFnProto,
-                );
-                true
-            }
-
-            Target::Mod | Target::ForeignMod | Target::Impl | Target::Trait => {
-                self.tcx.emit_node_span_lint(
-                    UNUSED_ATTRIBUTES,
-                    hir_id,
-                    attr.span,
-                    errors::IgnoredCoveragePropagate,
-                );
-                true
-            }
-
-            Target::Expression | Target::Statement | Target::Arm => {
-                self.tcx.emit_node_span_lint(
-                    UNUSED_ATTRIBUTES,
-                    hir_id,
-                    attr.span,
-                    errors::IgnoredCoverageFnDefn,
-                );
-                true
-            }
-
             _ => {
-                self.dcx().emit_err(errors::IgnoredCoverageNotCoverable {
+                self.dcx().emit_err(errors::CoverageNotFnOrClosure {
                     attr_span: attr.span,
                     defn_span: span,
                 });
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index f0596568092..7734dba3670 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -60,21 +60,9 @@ pub struct InlineNotFnOrClosure {
     pub defn_span: Span,
 }
 
-#[derive(LintDiagnostic)]
-#[diag(passes_coverage_ignored_function_prototype)]
-pub struct IgnoredCoverageFnProto;
-
-#[derive(LintDiagnostic)]
-#[diag(passes_coverage_propagate)]
-pub struct IgnoredCoveragePropagate;
-
-#[derive(LintDiagnostic)]
-#[diag(passes_coverage_fn_defn)]
-pub struct IgnoredCoverageFnDefn;
-
 #[derive(Diagnostic)]
-#[diag(passes_coverage_not_coverable, code = E0788)]
-pub struct IgnoredCoverageNotCoverable {
+#[diag(passes_coverage_not_fn_or_closure, code = E0788)]
+pub struct CoverageNotFnOrClosure {
     #[primary_span]
     pub attr_span: Span,
     #[label]
diff --git a/tests/ui/coverage-attr/bad-syntax.rs b/tests/ui/coverage-attr/bad-syntax.rs
index 127179877e5..c8c92de8c38 100644
--- a/tests/ui/coverage-attr/bad-syntax.rs
+++ b/tests/ui/coverage-attr/bad-syntax.rs
@@ -1,58 +1,45 @@
 #![feature(coverage_attribute)]
+//@ edition: 2021
 
 // Tests the error messages produced (or not produced) by various unusual
 // uses of the `#[coverage(..)]` attribute.
 
-// FIXME(#126658): Multiple coverage attributes with the same value are useless,
-// and should probably produce a diagnostic.
-#[coverage(off)]
+#[coverage(off)] //~ ERROR multiple `coverage` attributes
 #[coverage(off)]
 fn multiple_consistent() {}
 
-// FIXME(#126658): When there are multiple inconsistent coverage attributes,
-// it's unclear which one will prevail.
-#[coverage(off)]
+#[coverage(off)] //~ ERROR multiple `coverage` attributes
 #[coverage(on)]
 fn multiple_inconsistent() {}
 
-#[coverage] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage] //~ ERROR malformed `coverage` attribute input
 fn bare_word() {}
 
-// FIXME(#126658): This shows as multiple different errors, one of which suggests
-// writing bare `#[coverage]`, which is not allowed.
-#[coverage = true]
-//~^ ERROR expected `coverage(off)` or `coverage(on)`
-//~| ERROR malformed `coverage` attribute input
-//~| HELP the following are the possible correct uses
-//~| SUGGESTION #[coverage(on|off)]
+#[coverage = true] //~ ERROR malformed `coverage` attribute input
 fn key_value() {}
 
-#[coverage()] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage()] //~ ERROR malformed `coverage` attribute input
 fn list_empty() {}
 
-#[coverage(off, off)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage(off, off)] //~ ERROR malformed `coverage` attribute input
 fn list_consistent() {}
 
-#[coverage(off, on)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage(off, on)] //~ ERROR malformed `coverage` attribute input
 fn list_inconsistent() {}
 
-#[coverage(bogus)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage(bogus)] //~ ERROR malformed `coverage` attribute input
 fn bogus_word() {}
 
-#[coverage(bogus, off)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage(bogus, off)] //~ ERROR malformed `coverage` attribute input
 fn bogus_word_before() {}
 
-#[coverage(off, bogus)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage(off, bogus)] //~ ERROR malformed `coverage` attribute input
 fn bogus_word_after() {}
 
-#[coverage(off,)]
+#[coverage(off,)] // (OK!)
 fn comma_after() {}
 
-// FIXME(#126658): This shows as multiple different errors.
-#[coverage(,off)]
-//~^ ERROR expected identifier, found `,`
-//~| HELP remove this comma
-//~| ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage(,off)] //~ ERROR expected identifier, found `,`
 fn comma_before() {}
 
 fn main() {}
diff --git a/tests/ui/coverage-attr/bad-syntax.stderr b/tests/ui/coverage-attr/bad-syntax.stderr
index f6181d12a94..a5868fcf19c 100644
--- a/tests/ui/coverage-attr/bad-syntax.stderr
+++ b/tests/ui/coverage-attr/bad-syntax.stderr
@@ -1,78 +1,139 @@
 error: malformed `coverage` attribute input
-  --> $DIR/bad-syntax.rs:23:1
-   |
-LL | #[coverage = true]
-   | ^^^^^^^^^^^^^^^^^^
-   |
-help: the following are the possible correct uses
-   |
-LL | #[coverage(on|off)]
+  --> $DIR/bad-syntax.rs:15:1
    |
 LL | #[coverage]
+   | ^^^^^^^^^^^
    |
-
-error: expected identifier, found `,`
-  --> $DIR/bad-syntax.rs:52:12
+help: the following are the possible correct uses
    |
-LL | #[coverage(,off)]
-   |            ^
-   |            |
-   |            expected identifier
-   |            help: remove this comma
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
+error: malformed `coverage` attribute input
   --> $DIR/bad-syntax.rs:18:1
    |
-LL | #[coverage]
-   | ^^^^^^^^^^^
-
-error: expected `coverage(off)` or `coverage(on)`
-  --> $DIR/bad-syntax.rs:23:1
-   |
 LL | #[coverage = true]
    | ^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
-  --> $DIR/bad-syntax.rs:30:1
+error: malformed `coverage` attribute input
+  --> $DIR/bad-syntax.rs:21:1
    |
 LL | #[coverage()]
    | ^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
-  --> $DIR/bad-syntax.rs:33:1
+error: malformed `coverage` attribute input
+  --> $DIR/bad-syntax.rs:24:1
    |
 LL | #[coverage(off, off)]
    | ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
-  --> $DIR/bad-syntax.rs:36:1
+error: malformed `coverage` attribute input
+  --> $DIR/bad-syntax.rs:27:1
    |
 LL | #[coverage(off, on)]
    | ^^^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
-  --> $DIR/bad-syntax.rs:39:1
+error: malformed `coverage` attribute input
+  --> $DIR/bad-syntax.rs:30:1
    |
 LL | #[coverage(bogus)]
    | ^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
-  --> $DIR/bad-syntax.rs:42:1
+error: malformed `coverage` attribute input
+  --> $DIR/bad-syntax.rs:33:1
    |
 LL | #[coverage(bogus, off)]
    | ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
-  --> $DIR/bad-syntax.rs:45:1
+error: malformed `coverage` attribute input
+  --> $DIR/bad-syntax.rs:36:1
    |
 LL | #[coverage(off, bogus)]
    | ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
-  --> $DIR/bad-syntax.rs:52:1
+error: expected identifier, found `,`
+  --> $DIR/bad-syntax.rs:42:12
    |
 LL | #[coverage(,off)]
-   | ^^^^^^^^^^^^^^^^^
+   |            ^
+   |            |
+   |            expected identifier
+   |            help: remove this comma
+
+error: multiple `coverage` attributes
+  --> $DIR/bad-syntax.rs:7:1
+   |
+LL | #[coverage(off)]
+   | ^^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/bad-syntax.rs:8:1
+   |
+LL | #[coverage(off)]
+   | ^^^^^^^^^^^^^^^^
+
+error: multiple `coverage` attributes
+  --> $DIR/bad-syntax.rs:11:1
+   |
+LL | #[coverage(off)]
+   | ^^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/bad-syntax.rs:12:1
+   |
+LL | #[coverage(on)]
+   | ^^^^^^^^^^^^^^^
 
 error: aborting due to 11 previous errors
 
diff --git a/tests/ui/coverage-attr/name-value.rs b/tests/ui/coverage-attr/name-value.rs
index 24c329780c5..cfd78a03e43 100644
--- a/tests/ui/coverage-attr/name-value.rs
+++ b/tests/ui/coverage-attr/name-value.rs
@@ -8,57 +8,62 @@
 // and in places that cannot have a coverage attribute, to demonstrate the
 // interaction between multiple errors.
 
-// FIXME(#126658): The error messages for using this syntax are inconsistent
-// with the error message in other cases. They also sometimes appear together
-// with other errors, and they suggest using the incorrect `#[coverage]` syntax.
-
-#[coverage = "off"] //~ ERROR malformed `coverage` attribute input
+#[coverage = "off"]
+//~^ ERROR malformed `coverage` attribute input
+//~| ERROR attribute should be applied to a function definition or closure
 mod my_mod {}
 
 mod my_mod_inner {
-    #![coverage = "off"] //~ ERROR malformed `coverage` attribute input
+    #![coverage = "off"]
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
 }
 
 #[coverage = "off"]
-//~^ ERROR `#[coverage]` must be applied to coverable code
-//~| ERROR malformed `coverage` attribute input
+//~^ ERROR malformed `coverage` attribute input
+//~| ERROR attribute should be applied to a function definition or closure
 struct MyStruct;
 
-#[coverage = "off"] //~ ERROR malformed `coverage` attribute input
+#[coverage = "off"]
+//~^ ERROR malformed `coverage` attribute input
+//~| ERROR attribute should be applied to a function definition or closure
 impl MyStruct {
     #[coverage = "off"]
-    //~^ ERROR `#[coverage]` must be applied to coverable code
-    //~| ERROR malformed `coverage` attribute input
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
     const X: u32 = 7;
 }
 
-#[coverage = "off"] //~ ERROR malformed `coverage` attribute input
+#[coverage = "off"]
+//~^ ERROR malformed `coverage` attribute input
+//~| ERROR attribute should be applied to a function definition or closure
 trait MyTrait {
     #[coverage = "off"]
-    //~^ ERROR `#[coverage]` must be applied to coverable code
-    //~| ERROR malformed `coverage` attribute input
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
     const X: u32;
 
     #[coverage = "off"]
-    //~^ ERROR `#[coverage]` must be applied to coverable code
-    //~| ERROR malformed `coverage` attribute input
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
     type T;
 }
 
-#[coverage = "off"] //~ ERROR malformed `coverage` attribute input
+#[coverage = "off"]
+//~^ ERROR malformed `coverage` attribute input
+//~| ERROR attribute should be applied to a function definition or closure
 impl MyTrait for MyStruct {
     #[coverage = "off"]
-    //~^ ERROR `#[coverage]` must be applied to coverable code
-    //~| ERROR malformed `coverage` attribute input
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
     const X: u32 = 8;
 
     #[coverage = "off"]
-    //~^ ERROR `#[coverage]` must be applied to coverable code
-    //~| ERROR malformed `coverage` attribute input
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
     type T = ();
 }
 
 #[coverage = "off"]
-//~^ ERROR expected `coverage(off)` or `coverage(on)`
-//~| ERROR malformed `coverage` attribute input
+//~^ ERROR malformed `coverage` attribute input
 fn main() {}
diff --git a/tests/ui/coverage-attr/name-value.stderr b/tests/ui/coverage-attr/name-value.stderr
index 90bc3a3b53b..caac687c94d 100644
--- a/tests/ui/coverage-attr/name-value.stderr
+++ b/tests/ui/coverage-attr/name-value.stderr
@@ -1,28 +1,28 @@
 error: malformed `coverage` attribute input
-  --> $DIR/name-value.rs:15:1
+  --> $DIR/name-value.rs:11:1
    |
 LL | #[coverage = "off"]
    | ^^^^^^^^^^^^^^^^^^^
    |
 help: the following are the possible correct uses
    |
-LL | #[coverage(on|off)]
-   | ~~~~~~~~~~~~~~~~~~~
-LL | #[coverage]
-   | ~~~~~~~~~~~
+LL | #[coverage(off)]
+   |
+LL | #[coverage(on)]
+   |
 
 error: malformed `coverage` attribute input
-  --> $DIR/name-value.rs:19:5
+  --> $DIR/name-value.rs:17:5
    |
 LL |     #![coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^^
    |
 help: the following are the possible correct uses
    |
-LL |     #![coverage(on|off)]
-   |     ~~~~~~~~~~~~~~~~~~~~
-LL |     #![coverage]
-   |     ~~~~~~~~~~~~
+LL |     #![coverage(off)]
+   |
+LL |     #![coverage(on)]
+   |
 
 error: malformed `coverage` attribute input
   --> $DIR/name-value.rs:22:1
@@ -32,22 +32,22 @@ LL | #[coverage = "off"]
    |
 help: the following are the possible correct uses
    |
-LL | #[coverage(on|off)]
+LL | #[coverage(off)]
    |
-LL | #[coverage]
+LL | #[coverage(on)]
    |
 
 error: malformed `coverage` attribute input
-  --> $DIR/name-value.rs:29:5
+  --> $DIR/name-value.rs:31:5
    |
 LL |     #[coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^
    |
 help: the following are the possible correct uses
    |
-LL |     #[coverage(on|off)]
+LL |     #[coverage(off)]
    |
-LL |     #[coverage]
+LL |     #[coverage(on)]
    |
 
 error: malformed `coverage` attribute input
@@ -58,162 +58,220 @@ LL | #[coverage = "off"]
    |
 help: the following are the possible correct uses
    |
-LL | #[coverage(on|off)]
-   | ~~~~~~~~~~~~~~~~~~~
-LL | #[coverage]
-   | ~~~~~~~~~~~
+LL | #[coverage(off)]
+   |
+LL | #[coverage(on)]
+   |
 
 error: malformed `coverage` attribute input
-  --> $DIR/name-value.rs:37:5
+  --> $DIR/name-value.rs:41:5
    |
 LL |     #[coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^
    |
 help: the following are the possible correct uses
    |
-LL |     #[coverage(on|off)]
+LL |     #[coverage(off)]
    |
-LL |     #[coverage]
+LL |     #[coverage(on)]
    |
 
 error: malformed `coverage` attribute input
-  --> $DIR/name-value.rs:42:5
+  --> $DIR/name-value.rs:46:5
    |
 LL |     #[coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^
    |
 help: the following are the possible correct uses
    |
-LL |     #[coverage(on|off)]
+LL |     #[coverage(off)]
    |
-LL |     #[coverage]
+LL |     #[coverage(on)]
    |
 
 error: malformed `coverage` attribute input
-  --> $DIR/name-value.rs:35:1
+  --> $DIR/name-value.rs:37:1
    |
 LL | #[coverage = "off"]
    | ^^^^^^^^^^^^^^^^^^^
    |
 help: the following are the possible correct uses
    |
-LL | #[coverage(on|off)]
-   | ~~~~~~~~~~~~~~~~~~~
-LL | #[coverage]
-   | ~~~~~~~~~~~
+LL | #[coverage(off)]
+   |
+LL | #[coverage(on)]
+   |
 
 error: malformed `coverage` attribute input
-  --> $DIR/name-value.rs:50:5
+  --> $DIR/name-value.rs:56:5
    |
 LL |     #[coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^
    |
 help: the following are the possible correct uses
    |
-LL |     #[coverage(on|off)]
+LL |     #[coverage(off)]
    |
-LL |     #[coverage]
+LL |     #[coverage(on)]
    |
 
 error: malformed `coverage` attribute input
-  --> $DIR/name-value.rs:55:5
+  --> $DIR/name-value.rs:61:5
    |
 LL |     #[coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^
    |
 help: the following are the possible correct uses
    |
-LL |     #[coverage(on|off)]
+LL |     #[coverage(off)]
    |
-LL |     #[coverage]
+LL |     #[coverage(on)]
    |
 
 error: malformed `coverage` attribute input
-  --> $DIR/name-value.rs:48:1
+  --> $DIR/name-value.rs:52:1
    |
 LL | #[coverage = "off"]
    | ^^^^^^^^^^^^^^^^^^^
    |
 help: the following are the possible correct uses
    |
-LL | #[coverage(on|off)]
-   | ~~~~~~~~~~~~~~~~~~~
-LL | #[coverage]
-   | ~~~~~~~~~~~
+LL | #[coverage(off)]
+   |
+LL | #[coverage(on)]
+   |
 
 error: malformed `coverage` attribute input
-  --> $DIR/name-value.rs:61:1
+  --> $DIR/name-value.rs:67:1
    |
 LL | #[coverage = "off"]
    | ^^^^^^^^^^^^^^^^^^^
    |
 help: the following are the possible correct uses
    |
-LL | #[coverage(on|off)]
+LL | #[coverage(off)]
    |
-LL | #[coverage]
+LL | #[coverage(on)]
+   |
+
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/name-value.rs:11:1
    |
+LL | #[coverage = "off"]
+   | ^^^^^^^^^^^^^^^^^^^
+...
+LL | mod my_mod {}
+   | ------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/name-value.rs:17:5
+   |
+LL | / mod my_mod_inner {
+LL | |     #![coverage = "off"]
+   | |     ^^^^^^^^^^^^^^^^^^^^
+LL | |
+LL | |
+LL | | }
+   | |_- not a function or closure
+
+error[E0788]: attribute should be applied to a function definition or closure
   --> $DIR/name-value.rs:22:1
    |
 LL | #[coverage = "off"]
    | ^^^^^^^^^^^^^^^^^^^
 ...
 LL | struct MyStruct;
-   | ---------------- not coverable code
+   | ---------------- not a function or closure
+
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/name-value.rs:27:1
+   |
+LL |   #[coverage = "off"]
+   |   ^^^^^^^^^^^^^^^^^^^
+...
+LL | / impl MyStruct {
+LL | |     #[coverage = "off"]
+LL | |
+LL | |
+LL | |     const X: u32 = 7;
+LL | | }
+   | |_- not a function or closure
+
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/name-value.rs:37:1
+   |
+LL |   #[coverage = "off"]
+   |   ^^^^^^^^^^^^^^^^^^^
+...
+LL | / trait MyTrait {
+LL | |     #[coverage = "off"]
+LL | |
+LL | |
+...  |
+LL | |     type T;
+LL | | }
+   | |_- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/name-value.rs:37:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/name-value.rs:52:1
+   |
+LL |   #[coverage = "off"]
+   |   ^^^^^^^^^^^^^^^^^^^
+...
+LL | / impl MyTrait for MyStruct {
+LL | |     #[coverage = "off"]
+LL | |
+LL | |
+...  |
+LL | |     type T = ();
+LL | | }
+   | |_- not a function or closure
+
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/name-value.rs:41:5
    |
 LL |     #[coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^
 ...
 LL |     const X: u32;
-   |     ------------- not coverable code
+   |     ------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/name-value.rs:42:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/name-value.rs:46:5
    |
 LL |     #[coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^
 ...
 LL |     type T;
-   |     ------- not coverable code
+   |     ------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/name-value.rs:29:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/name-value.rs:31:5
    |
 LL |     #[coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^
 ...
 LL |     const X: u32 = 7;
-   |     ----------------- not coverable code
+   |     ----------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/name-value.rs:50:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/name-value.rs:56:5
    |
 LL |     #[coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^
 ...
 LL |     const X: u32 = 8;
-   |     ----------------- not coverable code
+   |     ----------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/name-value.rs:55:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/name-value.rs:61:5
    |
 LL |     #[coverage = "off"]
    |     ^^^^^^^^^^^^^^^^^^^
 ...
 LL |     type T = ();
-   |     ------------ not coverable code
-
-error: expected `coverage(off)` or `coverage(on)`
-  --> $DIR/name-value.rs:61:1
-   |
-LL | #[coverage = "off"]
-   | ^^^^^^^^^^^^^^^^^^^
+   |     ------------ not a function or closure
 
-error: aborting due to 19 previous errors
+error: aborting due to 23 previous errors
 
 For more information about this error, try `rustc --explain E0788`.
diff --git a/tests/ui/coverage-attr/no-coverage.rs b/tests/ui/coverage-attr/no-coverage.rs
index 907d25d333e..5290fccca61 100644
--- a/tests/ui/coverage-attr/no-coverage.rs
+++ b/tests/ui/coverage-attr/no-coverage.rs
@@ -2,54 +2,48 @@
 #![feature(coverage_attribute)]
 #![feature(impl_trait_in_assoc_type)]
 #![warn(unused_attributes)]
-#![coverage(off)]
-//~^ WARN: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
+#![coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
 
-#[coverage(off)]
-//~^ WARN: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
+#[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
 trait Trait {
-    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
     const X: u32;
 
-    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
     type T;
 
     type U;
 }
 
-#[coverage(off)]
-//~^ WARN: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
+#[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
 impl Trait for () {
     const X: u32 = 0;
 
-    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
     type T = Self;
 
-    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
     type U = impl Trait; //~ ERROR unconstrained opaque type
 }
 
 extern "C" {
-    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
     static X: u32;
 
-    #[coverage(off)] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
     type T;
 }
 
 #[coverage(off)]
 fn main() {
-    #[coverage(off)]
-    //~^ WARN `#[coverage]` may only be applied to function definitions
+    #[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
     let _ = ();
 
     match () {
-        #[coverage(off)]
-        //~^ WARN `#[coverage]` may only be applied to function definitions
+        #[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
         () => (),
     }
 
-    #[coverage(off)]
-    //~^ WARN `#[coverage]` may only be applied to function definitions
+    #[coverage(off)] //~ ERROR attribute should be applied to a function definition or closure
     return ();
 }
diff --git a/tests/ui/coverage-attr/no-coverage.stderr b/tests/ui/coverage-attr/no-coverage.stderr
index a87b0fb49f0..c5e3b0922cb 100644
--- a/tests/ui/coverage-attr/no-coverage.stderr
+++ b/tests/ui/coverage-attr/no-coverage.stderr
@@ -1,101 +1,116 @@
-warning: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
-  --> $DIR/no-coverage.rs:8:1
-   |
-LL | #[coverage(off)]
-   | ^^^^^^^^^^^^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/no-coverage.rs:4:9
-   |
-LL | #![warn(unused_attributes)]
-   |         ^^^^^^^^^^^^^^^^^
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:7:1
+   |
+LL |   #[coverage(off)]
+   |   ^^^^^^^^^^^^^^^^
+LL | / trait Trait {
+LL | |     #[coverage(off)]
+LL | |     const X: u32;
+...  |
+LL | |     type U;
+LL | | }
+   | |_- not a function or closure
 
-warning: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
-  --> $DIR/no-coverage.rs:20:1
-   |
-LL | #[coverage(off)]
-   | ^^^^^^^^^^^^^^^^
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:18:1
+   |
+LL |   #[coverage(off)]
+   |   ^^^^^^^^^^^^^^^^
+LL | / impl Trait for () {
+LL | |     const X: u32 = 0;
+LL | |
+LL | |     #[coverage(off)]
+...  |
+LL | |     type U = impl Trait;
+LL | | }
+   | |_- not a function or closure
 
-warning: `#[coverage]` may only be applied to function definitions
-  --> $DIR/no-coverage.rs:42:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:39:5
    |
 LL |     #[coverage(off)]
    |     ^^^^^^^^^^^^^^^^
+LL |     let _ = ();
+   |     ----------- not a function or closure
 
-warning: `#[coverage]` may only be applied to function definitions
-  --> $DIR/no-coverage.rs:47:9
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:43:9
    |
 LL |         #[coverage(off)]
    |         ^^^^^^^^^^^^^^^^
+LL |         () => (),
+   |         -------- not a function or closure
 
-warning: `#[coverage]` may only be applied to function definitions
-  --> $DIR/no-coverage.rs:52:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:47:5
    |
 LL |     #[coverage(off)]
    |     ^^^^^^^^^^^^^^^^
+LL |     return ();
+   |     --------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/no-coverage.rs:11:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:9:5
    |
 LL |     #[coverage(off)]
    |     ^^^^^^^^^^^^^^^^
 LL |     const X: u32;
-   |     ------------- not coverable code
+   |     ------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/no-coverage.rs:14:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:12:5
    |
 LL |     #[coverage(off)]
    |     ^^^^^^^^^^^^^^^^
 LL |     type T;
-   |     ------- not coverable code
+   |     ------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/no-coverage.rs:25:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:22:5
    |
 LL |     #[coverage(off)]
    |     ^^^^^^^^^^^^^^^^
 LL |     type T = Self;
-   |     -------------- not coverable code
+   |     -------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/no-coverage.rs:28:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:25:5
    |
 LL |     #[coverage(off)]
    |     ^^^^^^^^^^^^^^^^
 LL |     type U = impl Trait;
-   |     -------------------- not coverable code
+   |     -------------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/no-coverage.rs:33:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:30:5
    |
 LL |     #[coverage(off)]
    |     ^^^^^^^^^^^^^^^^
 LL |     static X: u32;
-   |     -------------- not coverable code
+   |     -------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/no-coverage.rs:36:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/no-coverage.rs:33:5
    |
 LL |     #[coverage(off)]
    |     ^^^^^^^^^^^^^^^^
 LL |     type T;
-   |     ------- not coverable code
+   |     ------- not a function or closure
 
-warning: `#[coverage]` does not propagate into items and must be applied to the contained functions directly
+error[E0788]: attribute should be applied to a function definition or closure
   --> $DIR/no-coverage.rs:5:1
    |
 LL | #![coverage(off)]
-   | ^^^^^^^^^^^^^^^^^
+   | ^^^^^^^^^^^^^^^^^ not a function or closure
 
 error: unconstrained opaque type
-  --> $DIR/no-coverage.rs:29:14
+  --> $DIR/no-coverage.rs:26:14
    |
 LL |     type U = impl Trait;
    |              ^^^^^^^^^^
    |
    = note: `U` must be used in combination with a concrete type within the same impl
 
-error: aborting due to 7 previous errors; 6 warnings emitted
+error: aborting due to 13 previous errors
 
 For more information about this error, try `rustc --explain E0788`.
diff --git a/tests/ui/coverage-attr/subword.rs b/tests/ui/coverage-attr/subword.rs
index 98b8c25113c..16582240b69 100644
--- a/tests/ui/coverage-attr/subword.rs
+++ b/tests/ui/coverage-attr/subword.rs
@@ -4,16 +4,16 @@
 // Check that yes/no in `#[coverage(yes)]` and `#[coverage(no)]` must be bare
 // words, not part of a more complicated substructure.
 
-#[coverage(yes(milord))] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage(yes(milord))] //~ ERROR malformed `coverage` attribute input
 fn yes_list() {}
 
-#[coverage(no(milord))] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage(no(milord))] //~ ERROR malformed `coverage` attribute input
 fn no_list() {}
 
-#[coverage(yes = "milord")] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage(yes = "milord")] //~ ERROR malformed `coverage` attribute input
 fn yes_key() {}
 
-#[coverage(no = "milord")] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage(no = "milord")] //~ ERROR malformed `coverage` attribute input
 fn no_key() {}
 
 fn main() {}
diff --git a/tests/ui/coverage-attr/subword.stderr b/tests/ui/coverage-attr/subword.stderr
index 561573b8ada..3a106898f8b 100644
--- a/tests/ui/coverage-attr/subword.stderr
+++ b/tests/ui/coverage-attr/subword.stderr
@@ -1,26 +1,54 @@
-error: expected `coverage(off)` or `coverage(on)`
+error: malformed `coverage` attribute input
   --> $DIR/subword.rs:7:1
    |
 LL | #[coverage(yes(milord))]
    | ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
+error: malformed `coverage` attribute input
   --> $DIR/subword.rs:10:1
    |
 LL | #[coverage(no(milord))]
    | ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
+error: malformed `coverage` attribute input
   --> $DIR/subword.rs:13:1
    |
 LL | #[coverage(yes = "milord")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
-error: expected `coverage(off)` or `coverage(on)`
+error: malformed `coverage` attribute input
   --> $DIR/subword.rs:16:1
    |
 LL | #[coverage(no = "milord")]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   | ~~~~~~~~~~~~~~~~
+LL | #[coverage(on)]
+   | ~~~~~~~~~~~~~~~
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/coverage-attr/word-only.rs b/tests/ui/coverage-attr/word-only.rs
index 5c723b1b6b6..0a61d1e709f 100644
--- a/tests/ui/coverage-attr/word-only.rs
+++ b/tests/ui/coverage-attr/word-only.rs
@@ -8,47 +8,62 @@
 // and in places that cannot have a coverage attribute, to demonstrate the
 // interaction between multiple errors.
 
-// FIXME(#126658): The error messages for using this syntax give the impression
-// that it is legal, even though it should never be legal.
-
-// FIXME(#126658): This is silently allowed, but should not be.
 #[coverage]
+//~^ ERROR malformed `coverage` attribute input
+//~| ERROR attribute should be applied to a function definition or closure
 mod my_mod {}
 
-// FIXME(#126658): This is silently allowed, but should not be.
 mod my_mod_inner {
     #![coverage]
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
 }
 
-#[coverage] //~ ERROR `#[coverage]` must be applied to coverable code
+#[coverage]
+//~^ ERROR malformed `coverage` attribute input
+//~| ERROR attribute should be applied to a function definition or closure
 struct MyStruct;
 
-// FIXME(#126658): This is silently allowed, but should not be.
 #[coverage]
+//~^ ERROR malformed `coverage` attribute input
+//~| ERROR attribute should be applied to a function definition or closure
 impl MyStruct {
-    #[coverage] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage]
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
     const X: u32 = 7;
 }
 
-// FIXME(#126658): This is silently allowed, but should not be.
 #[coverage]
+//~^ ERROR malformed `coverage` attribute input
+//~| ERROR attribute should be applied to a function definition or closure
 trait MyTrait {
-    #[coverage] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage]
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
     const X: u32;
 
-    #[coverage] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage]
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
     type T;
 }
 
-// FIXME(#126658): This is silently allowed, but should not be.
 #[coverage]
+//~^ ERROR malformed `coverage` attribute input
+//~| ERROR attribute should be applied to a function definition or closure
 impl MyTrait for MyStruct {
-    #[coverage] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage]
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
     const X: u32 = 8;
 
-    #[coverage] //~ ERROR `#[coverage]` must be applied to coverable code
+    #[coverage]
+    //~^ ERROR malformed `coverage` attribute input
+    //~| ERROR attribute should be applied to a function definition or closure
     type T = ();
 }
 
-#[coverage] //~ ERROR expected `coverage(off)` or `coverage(on)`
+#[coverage]
+//~^ ERROR malformed `coverage` attribute input
 fn main() {}
diff --git a/tests/ui/coverage-attr/word-only.stderr b/tests/ui/coverage-attr/word-only.stderr
index bcafc23bc8d..18b5fed7484 100644
--- a/tests/ui/coverage-attr/word-only.stderr
+++ b/tests/ui/coverage-attr/word-only.stderr
@@ -1,57 +1,277 @@
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/word-only.rs:23:1
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:11:1
    |
 LL | #[coverage]
    | ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   |
+LL | #[coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:17:5
+   |
+LL |     #![coverage]
+   |     ^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL |     #![coverage(off)]
+   |
+LL |     #![coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:22:1
+   |
+LL | #[coverage]
+   | ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   |
+LL | #[coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:31:5
+   |
+LL |     #[coverage]
+   |     ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL |     #[coverage(off)]
+   |
+LL |     #[coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:27:1
+   |
+LL | #[coverage]
+   | ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   |
+LL | #[coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:41:5
+   |
+LL |     #[coverage]
+   |     ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL |     #[coverage(off)]
+   |
+LL |     #[coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:46:5
+   |
+LL |     #[coverage]
+   |     ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL |     #[coverage(off)]
+   |
+LL |     #[coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:37:1
+   |
+LL | #[coverage]
+   | ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   |
+LL | #[coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:56:5
+   |
+LL |     #[coverage]
+   |     ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL |     #[coverage(off)]
+   |
+LL |     #[coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:61:5
+   |
+LL |     #[coverage]
+   |     ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL |     #[coverage(off)]
+   |
+LL |     #[coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:52:1
+   |
+LL | #[coverage]
+   | ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   |
+LL | #[coverage(on)]
+   |
+
+error: malformed `coverage` attribute input
+  --> $DIR/word-only.rs:67:1
+   |
+LL | #[coverage]
+   | ^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(off)]
+   |
+LL | #[coverage(on)]
+   |
+
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:11:1
+   |
+LL | #[coverage]
+   | ^^^^^^^^^^^
+...
+LL | mod my_mod {}
+   | ------------- not a function or closure
+
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:17:5
+   |
+LL | / mod my_mod_inner {
+LL | |     #![coverage]
+   | |     ^^^^^^^^^^^^
+LL | |
+LL | |
+LL | | }
+   | |_- not a function or closure
+
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:22:1
+   |
+LL | #[coverage]
+   | ^^^^^^^^^^^
+...
 LL | struct MyStruct;
-   | ---------------- not coverable code
+   | ---------------- not a function or closure
+
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:27:1
+   |
+LL |   #[coverage]
+   |   ^^^^^^^^^^^
+...
+LL | / impl MyStruct {
+LL | |     #[coverage]
+LL | |
+LL | |
+LL | |     const X: u32 = 7;
+LL | | }
+   | |_- not a function or closure
+
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:37:1
+   |
+LL |   #[coverage]
+   |   ^^^^^^^^^^^
+...
+LL | / trait MyTrait {
+LL | |     #[coverage]
+LL | |
+LL | |
+...  |
+LL | |     type T;
+LL | | }
+   | |_- not a function or closure
+
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:52:1
+   |
+LL |   #[coverage]
+   |   ^^^^^^^^^^^
+...
+LL | / impl MyTrait for MyStruct {
+LL | |     #[coverage]
+LL | |
+LL | |
+...  |
+LL | |     type T = ();
+LL | | }
+   | |_- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/word-only.rs:36:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:41:5
    |
 LL |     #[coverage]
    |     ^^^^^^^^^^^
+...
 LL |     const X: u32;
-   |     ------------- not coverable code
+   |     ------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/word-only.rs:39:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:46:5
    |
 LL |     #[coverage]
    |     ^^^^^^^^^^^
+...
 LL |     type T;
-   |     ------- not coverable code
+   |     ------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/word-only.rs:29:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:31:5
    |
 LL |     #[coverage]
    |     ^^^^^^^^^^^
+...
 LL |     const X: u32 = 7;
-   |     ----------------- not coverable code
+   |     ----------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/word-only.rs:46:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:56:5
    |
 LL |     #[coverage]
    |     ^^^^^^^^^^^
+...
 LL |     const X: u32 = 8;
-   |     ----------------- not coverable code
+   |     ----------------- not a function or closure
 
-error[E0788]: `#[coverage]` must be applied to coverable code
-  --> $DIR/word-only.rs:49:5
+error[E0788]: attribute should be applied to a function definition or closure
+  --> $DIR/word-only.rs:61:5
    |
 LL |     #[coverage]
    |     ^^^^^^^^^^^
+...
 LL |     type T = ();
-   |     ------------ not coverable code
-
-error: expected `coverage(off)` or `coverage(on)`
-  --> $DIR/word-only.rs:53:1
-   |
-LL | #[coverage]
-   | ^^^^^^^^^^^
+   |     ------------ not a function or closure
 
-error: aborting due to 7 previous errors
+error: aborting due to 23 previous errors
 
 For more information about this error, try `rustc --explain E0788`.