From 3584c1dd0cb501916c8bc6fd452864b261068beb Mon Sep 17 00:00:00 2001
From: Rich Kadel <richkadel@google.com>
Date: Mon, 3 May 2021 08:47:43 -0700
Subject: [PATCH] Disallows `#![feature(no_coverage)]` on stable and beta

using allow_internal_unstable (as recommended)

Fixes: #84836

```shell
$ ./build/x86_64-unknown-linux-gnu/stage1/bin/rustc     src/test/run-make-fulldeps/coverage/no_cov_crate.rs
error[E0554]: `#![feature]` may not be used on the dev release channel
 --> src/test/run-make-fulldeps/coverage/no_cov_crate.rs:2:1
  |
2 | #![feature(no_coverage)]
  | ^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0554`.
```
---
 .../src/deriving/cmp/eq.rs                    | 14 ++--------
 compiler/rustc_feature/src/builtin_attrs.rs   |  8 +-----
 compiler/rustc_typeck/src/collect.rs          | 28 +------------------
 library/core/src/cmp.rs                       |  5 ++--
 library/core/src/lib.rs                       |  1 +
 .../expected_show_coverage.no_cov_func.txt    | 19 -------------
 .../run-make-fulldeps/coverage/no_cov_func.rs | 18 ------------
 .../feature-gates/feature-gate-no_coverage.rs | 13 ++++++---
 .../feature-gate-no_coverage.stderr           |  3 +-
 9 files changed, 18 insertions(+), 91 deletions(-)
 delete mode 100644 src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt
 delete mode 100644 src/test/run-make-fulldeps/coverage/no_cov_func.rs

diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index 5a4e7fd9d07b4..54ab88dc3ffc9 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -15,20 +15,12 @@ pub fn expand_deriving_eq(
     item: &Annotatable,
     push: &mut dyn FnMut(Annotatable),
 ) {
+    let span = cx.with_def_site_ctxt(span);
     let inline = cx.meta_word(span, sym::inline);
-    let no_coverage_ident =
-        rustc_ast::attr::mk_nested_word_item(Ident::new(sym::no_coverage, span));
-    let no_coverage_feature =
-        rustc_ast::attr::mk_list_item(Ident::new(sym::feature, span), vec![no_coverage_ident]);
-    let no_coverage = cx.meta_word(span, sym::no_coverage);
     let hidden = rustc_ast::attr::mk_nested_word_item(Ident::new(sym::hidden, span));
     let doc = rustc_ast::attr::mk_list_item(Ident::new(sym::doc, span), vec![hidden]);
-    let attrs = vec![
-        cx.attribute(inline),
-        cx.attribute(no_coverage_feature),
-        cx.attribute(no_coverage),
-        cx.attribute(doc),
-    ];
+    let no_coverage = cx.meta_word(span, sym::no_coverage);
+    let attrs = vec![cx.attribute(inline), cx.attribute(doc), cx.attribute(no_coverage)];
     let trait_def = TraitDef {
         span,
         attributes: Vec::new(),
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 5474fea9c7857..a8719be84c2a4 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -273,13 +273,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(List: "address, memory, thread"),
         experimental!(no_sanitize)
     ),
-    ungated!(
-        // Not exclusively gated at the crate level (though crate-level is
-        // supported). The feature can alternatively be enabled on individual
-        // functions.
-        no_coverage, AssumedUsed,
-        template!(Word),
-    ),
+    gated!(no_coverage, AssumedUsed, template!(Word), experimental!(no_coverage)),
 
     // FIXME: #14408 assume docs are used since rustdoc looks at them.
     ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 190c9d35934f9..0528f8812f920 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -2661,8 +2661,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
     let mut inline_span = None;
     let mut link_ordinal_span = None;
     let mut no_sanitize_span = None;
-    let mut no_coverage_feature_enabled = false;
-    let mut no_coverage_attr = None;
     for attr in attrs.iter() {
         if tcx.sess.check_name(attr, sym::cold) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
@@ -2726,15 +2724,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
         } else if tcx.sess.check_name(attr, sym::no_mangle) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
-        } else if attr.has_name(sym::feature) {
-            if let Some(list) = attr.meta_item_list() {
-                if list.iter().any(|nested_meta_item| nested_meta_item.has_name(sym::no_coverage)) {
-                    tcx.sess.mark_attr_used(attr);
-                    no_coverage_feature_enabled = true;
-                }
-            }
         } else if tcx.sess.check_name(attr, sym::no_coverage) {
-            no_coverage_attr = Some(attr);
+            codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
         } else if tcx.sess.check_name(attr, sym::rustc_std_internal_symbol) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
         } else if tcx.sess.check_name(attr, sym::used) {
@@ -2945,23 +2936,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
         }
     }
 
-    if let Some(no_coverage_attr) = no_coverage_attr {
-        if tcx.sess.features_untracked().no_coverage || no_coverage_feature_enabled {
-            codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE
-        } else {
-            let mut err = feature_err(
-                &tcx.sess.parse_sess,
-                sym::no_coverage,
-                no_coverage_attr.span,
-                "the `#[no_coverage]` attribute is an experimental feature",
-            );
-            if tcx.sess.parse_sess.unstable_features.is_nightly_build() {
-                err.help("or, alternatively, add `#[feature(no_coverage)]` to the function");
-            }
-            err.emit();
-        }
-    }
-
     codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
         if !attr.has_name(sym::inline) {
             return ia;
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index 0a3e5789e8bed..f8b16b6f9275c 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -274,8 +274,7 @@ pub trait Eq: PartialEq<Self> {
     //
     // This should never be implemented by hand.
     #[doc(hidden)]
-    #[cfg_attr(not(bootstrap), feature(no_coverage))]
-    #[cfg_attr(not(bootstrap), no_coverage)]
+    #[cfg_attr(not(bootstrap), no_coverage)] // rust-lang/rust#84605
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn assert_receiver_is_total_eq(&self) {}
@@ -284,7 +283,7 @@ pub trait Eq: PartialEq<Self> {
 /// Derive macro generating an impl of the trait `Eq`.
 #[rustc_builtin_macro]
 #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
-#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match)]
+#[allow_internal_unstable(core_intrinsics, derive_eq, structural_match, no_coverage)]
 pub macro Eq($item:item) {
     /* compiler built-in */
 }
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 0e2c140c367a9..d1329b8f22116 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -166,6 +166,7 @@
 #![feature(const_caller_location)]
 #![feature(slice_ptr_get)]
 #![feature(no_niche)] // rust-lang/rust#68303
+#![cfg_attr(not(bootstrap), feature(no_coverage))] // rust-lang/rust#84605
 #![feature(int_error_matching)]
 #![deny(unsafe_op_in_unsafe_fn)]
 
diff --git a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt b/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt
deleted file mode 100644
index 16eaf7c858c19..0000000000000
--- a/src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.no_cov_func.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-    1|       |// Enables `no_coverage` on individual functions
-    2|       |
-    3|       |#[feature(no_coverage)]
-    4|       |#[no_coverage]
-    5|       |fn do_not_add_coverage_1() {
-    6|       |    println!("called but not covered");
-    7|       |}
-    8|       |
-    9|       |#[no_coverage]
-   10|       |#[feature(no_coverage)]
-   11|       |fn do_not_add_coverage_2() {
-   12|       |    println!("called but not covered");
-   13|       |}
-   14|       |
-   15|      1|fn main() {
-   16|      1|    do_not_add_coverage_1();
-   17|      1|    do_not_add_coverage_2();
-   18|      1|}
-
diff --git a/src/test/run-make-fulldeps/coverage/no_cov_func.rs b/src/test/run-make-fulldeps/coverage/no_cov_func.rs
deleted file mode 100644
index e19a2c4a87200..0000000000000
--- a/src/test/run-make-fulldeps/coverage/no_cov_func.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-// Enables `no_coverage` on individual functions
-
-#[feature(no_coverage)]
-#[no_coverage]
-fn do_not_add_coverage_1() {
-    println!("called but not covered");
-}
-
-#[no_coverage]
-#[feature(no_coverage)]
-fn do_not_add_coverage_2() {
-    println!("called but not covered");
-}
-
-fn main() {
-    do_not_add_coverage_1();
-    do_not_add_coverage_2();
-}
diff --git a/src/test/ui/feature-gates/feature-gate-no_coverage.rs b/src/test/ui/feature-gates/feature-gate-no_coverage.rs
index c6b79f9a43171..fd4c6f76059aa 100644
--- a/src/test/ui/feature-gates/feature-gate-no_coverage.rs
+++ b/src/test/ui/feature-gates/feature-gate-no_coverage.rs
@@ -1,8 +1,13 @@
 #![crate_type = "lib"]
 
-#[no_coverage]
-#[feature(no_coverage)] // does not have to be enabled before `#[no_coverage]`
-fn no_coverage_is_enabled_on_this_function() {}
+#[derive(PartialEq, Eq)] // ensure deriving `Eq` does not enable `feature(no_coverage)`
+struct Foo {
+    a: u8,
+    b: u32,
+}
 
 #[no_coverage] //~ ERROR the `#[no_coverage]` attribute is an experimental feature
-fn requires_feature_no_coverage() {}
+fn requires_feature_no_coverage() -> bool {
+    let bar = Foo { a: 0, b: 0 };
+    bar == Foo { a: 0, b: 0 }
+}
diff --git a/src/test/ui/feature-gates/feature-gate-no_coverage.stderr b/src/test/ui/feature-gates/feature-gate-no_coverage.stderr
index 04627be4aaf65..f7167e0b771c0 100644
--- a/src/test/ui/feature-gates/feature-gate-no_coverage.stderr
+++ b/src/test/ui/feature-gates/feature-gate-no_coverage.stderr
@@ -1,12 +1,11 @@
 error[E0658]: the `#[no_coverage]` attribute is an experimental feature
-  --> $DIR/feature-gate-no_coverage.rs:7:1
+  --> $DIR/feature-gate-no_coverage.rs:9:1
    |
 LL | #[no_coverage]
    | ^^^^^^^^^^^^^^
    |
    = note: see issue #84605 <https://github.com/rust-lang/rust/issues/84605> for more information
    = help: add `#![feature(no_coverage)]` to the crate attributes to enable
-   = help: or, alternatively, add `#[feature(no_coverage)]` to the function
 
 error: aborting due to previous error