about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_interface/src/tests.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs7
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mappings.rs33
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs8
-rw-r--r--compiler/rustc_session/src/config.rs9
-rw-r--r--compiler/rustc_session/src/options.rs4
-rw-r--r--compiler/rustc_session/src/session.rs5
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt6
-rw-r--r--tests/coverage/attr/nested.cov-map100
-rw-r--r--tests/coverage/attr/nested.coverage111
-rw-r--r--tests/coverage/attr/nested.rs110
-rw-r--r--tests/coverage/attr/off-on-sandwich.cov-map32
-rw-r--r--tests/coverage/attr/off-on-sandwich.coverage58
-rw-r--r--tests/coverage/attr/off-on-sandwich.rs57
-rw-r--r--tests/coverage/branch/no-mir-spans.cov-map52
-rw-r--r--tests/coverage/branch/no-mir-spans.coverage77
-rw-r--r--tests/coverage/branch/no-mir-spans.rs62
-rw-r--r--tests/run-make/error-found-staticlib-instead-crate/Makefile5
-rw-r--r--tests/run-make/error-found-staticlib-instead-crate/rmake.rs11
-rw-r--r--tests/run-make/native-link-modifier-verbatim-linker/Makefile15
-rw-r--r--tests/run-make/native-link-modifier-verbatim-linker/rmake.rs41
-rw-r--r--tests/run-make/native-link-modifier-verbatim-rustc/Makefile12
-rw-r--r--tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs47
-rw-r--r--tests/run-make/output-filename-conflicts-with-directory/Makefile7
-rw-r--r--tests/run-make/output-filename-conflicts-with-directory/rmake.rs14
-rw-r--r--tests/run-make/output-filename-overwrites-input/Makefile14
-rw-r--r--tests/run-make/output-filename-overwrites-input/rmake.rs21
-rw-r--r--tests/run-make/used/Makefile7
-rw-r--r--tests/run-make/used/rmake.rs15
-rw-r--r--tests/ui/coverage-attr/bad-syntax.rs58
-rw-r--r--tests/ui/coverage-attr/bad-syntax.stderr78
-rw-r--r--tests/ui/coverage-attr/no-coverage.rs (renamed from tests/ui/lint/no-coverage.rs)0
-rw-r--r--tests/ui/coverage-attr/no-coverage.stderr (renamed from tests/ui/lint/no-coverage.stderr)0
-rw-r--r--tests/ui/instrument-coverage/coverage-options.bad.stderr2
34 files changed, 992 insertions, 88 deletions
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 6538995926a..fa3e44e7744 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -761,7 +761,7 @@ fn test_unstable_options_tracking_hash() {
         })
     );
     tracked!(codegen_backend, Some("abc".to_string()));
-    tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc });
+    tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc, no_mir_spans: true });
     tracked!(crate_attr, vec!["abc".to_string()]);
     tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
     tracked!(debug_info_for_profiling, true);
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index b0f8a047b82..2fc466c0e7e 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -52,7 +52,7 @@ impl AllocBytes for Box<[u8]> {
     }
 
     fn zeroed(size: Size, _align: Align) -> Option<Self> {
-        let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize()).ok()?;
+        let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes().try_into().ok()?).ok()?;
         // SAFETY: the box was zero-allocated, which is a valid initial value for Box<[u8]>
         let bytes = unsafe { bytes.assume_init() };
         Some(bytes)
@@ -323,7 +323,10 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
     /// first call this function and then call write_scalar to fill in the right data.
     pub fn uninit(size: Size, align: Align) -> Self {
         match Self::uninit_inner(size, align, || {
-            panic!("Allocation::uninit called with panic_on_fail had allocation failure");
+            panic!(
+                "interpreter ran out of memory: cannot create allocation of {} bytes",
+                size.bytes()
+            );
         }) {
             Ok(x) => x,
             Err(x) => x,
diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs
index 0e209757100..759bb7c1f9d 100644
--- a/compiler/rustc_mir_transform/src/coverage/mappings.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs
@@ -5,6 +5,7 @@ use rustc_index::bit_set::BitSet;
 use rustc_index::IndexVec;
 use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind};
 use rustc_middle::mir::{self, BasicBlock, StatementKind};
+use rustc_middle::ty::TyCtxt;
 use rustc_span::Span;
 
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
@@ -63,31 +64,35 @@ pub(super) struct ExtractedMappings {
 
 /// Extracts coverage-relevant spans from MIR, and associates them with
 /// their corresponding BCBs.
-pub(super) fn extract_all_mapping_info_from_mir(
-    mir_body: &mir::Body<'_>,
+pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mir_body: &mir::Body<'tcx>,
     hir_info: &ExtractedHirInfo,
     basic_coverage_blocks: &CoverageGraph,
 ) -> ExtractedMappings {
-    if hir_info.is_async_fn {
+    let mut code_mappings = vec![];
+    let mut branch_pairs = vec![];
+    let mut mcdc_bitmap_bytes = 0;
+    let mut mcdc_branches = vec![];
+    let mut mcdc_decisions = vec![];
+
+    if hir_info.is_async_fn || tcx.sess.coverage_no_mir_spans() {
         // An async function desugars into a function that returns a future,
         // with the user code wrapped in a closure. Any spans in the desugared
         // outer function will be unhelpful, so just keep the signature span
         // and ignore all of the spans in the MIR body.
-        let mut mappings = ExtractedMappings::default();
+        //
+        // When debugging flag `-Zcoverage-options=no-mir-spans` is set, we need
+        // to give the same treatment to _all_ functions, because `llvm-cov`
+        // seems to ignore functions that don't have any ordinary code spans.
         if let Some(span) = hir_info.fn_sig_span_extended {
-            mappings.code_mappings.push(CodeMapping { span, bcb: START_BCB });
+            code_mappings.push(CodeMapping { span, bcb: START_BCB });
         }
-        return mappings;
+    } else {
+        // Extract coverage spans from MIR statements/terminators as normal.
+        extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings);
     }
 
-    let mut code_mappings = vec![];
-    let mut branch_pairs = vec![];
-    let mut mcdc_bitmap_bytes = 0;
-    let mut mcdc_branches = vec![];
-    let mut mcdc_decisions = vec![];
-
-    extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings);
-
     branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks));
 
     extract_mcdc_mappings(
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 419e39bc386..4a64d21f3d1 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -71,8 +71,12 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
 
     ////////////////////////////////////////////////////
     // Extract coverage spans and other mapping info from MIR.
-    let extracted_mappings =
-        mappings::extract_all_mapping_info_from_mir(mir_body, &hir_info, &basic_coverage_blocks);
+    let extracted_mappings = mappings::extract_all_mapping_info_from_mir(
+        tcx,
+        mir_body,
+        &hir_info,
+        &basic_coverage_blocks,
+    );
 
     ////////////////////////////////////////////////////
     // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index a622f1b577d..5f9c3a14d60 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -149,7 +149,14 @@ pub enum InstrumentCoverage {
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
 pub struct CoverageOptions {
     pub level: CoverageLevel,
-    // Other boolean or enum-valued options might be added here.
+
+    /// `-Z coverage-options=no-mir-spans`: Don't extract block coverage spans
+    /// from MIR statements/terminators, making it easier to inspect/debug
+    /// branch and MC/DC coverage mappings.
+    ///
+    /// For internal debugging only. If other code changes would make it hard
+    /// to keep supporting this flag, remove it.
+    pub no_mir_spans: bool,
 }
 
 /// Controls whether branch coverage or MC/DC coverage is enabled.
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index fd4a3a9e6ce..145af50117c 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -395,7 +395,8 @@ mod desc {
     pub const parse_optimization_fuel: &str = "crate=integer";
     pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
     pub const parse_instrument_coverage: &str = parse_bool;
-    pub const parse_coverage_options: &str = "`block` | `branch` | `condition` | `mcdc`";
+    pub const parse_coverage_options: &str =
+        "`block` | `branch` | `condition` | `mcdc` | `no-mir-spans`";
     pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`";
     pub const parse_unpretty: &str = "`string` or `string=string`";
     pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number";
@@ -963,6 +964,7 @@ mod parse {
                 "branch" => slot.level = CoverageLevel::Branch,
                 "condition" => slot.level = CoverageLevel::Condition,
                 "mcdc" => slot.level = CoverageLevel::Mcdc,
+                "no-mir-spans" => slot.no_mir_spans = true,
                 _ => return false,
             }
         }
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 87bbfcf07c8..20e50ef1b4a 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -363,6 +363,11 @@ impl Session {
             && self.opts.unstable_opts.coverage_options.level >= CoverageLevel::Mcdc
     }
 
+    /// True if `-Zcoverage-options=no-mir-spans` was passed.
+    pub fn coverage_no_mir_spans(&self) -> bool {
+        self.opts.unstable_opts.coverage_options.no_mir_spans
+    }
+
     pub fn is_sanitizer_cfi_enabled(&self) -> bool {
         self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
     }
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index b031557bf4c..1596257747f 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -35,7 +35,6 @@ run-make/emit-shared-files/Makefile
 run-make/emit-stack-sizes/Makefile
 run-make/emit-to-stdout/Makefile
 run-make/env-dep-info/Makefile
-run-make/error-found-staticlib-instead-crate/Makefile
 run-make/error-writing-dependencies/Makefile
 run-make/export-executable-symbols/Makefile
 run-make/extern-diff-internal-name/Makefile
@@ -130,8 +129,6 @@ run-make/missing-crate-dependency/Makefile
 run-make/mixing-libs/Makefile
 run-make/msvc-opt-minsize/Makefile
 run-make/native-link-modifier-bundle/Makefile
-run-make/native-link-modifier-verbatim-linker/Makefile
-run-make/native-link-modifier-verbatim-rustc/Makefile
 run-make/native-link-modifier-whole-archive/Makefile
 run-make/no-alloc-shim/Makefile
 run-make/no-builtins-attribute/Makefile
@@ -139,8 +136,6 @@ run-make/no-duplicate-libs/Makefile
 run-make/obey-crate-type-flag/Makefile
 run-make/optimization-remarks-dir-pgo/Makefile
 run-make/optimization-remarks-dir/Makefile
-run-make/output-filename-conflicts-with-directory/Makefile
-run-make/output-filename-overwrites-input/Makefile
 run-make/output-type-permutations/Makefile
 run-make/override-aliased-flags/Makefile
 run-make/overwrite-input/Makefile
@@ -225,7 +220,6 @@ run-make/unknown-mod-stdin/Makefile
 run-make/unstable-flag-required/Makefile
 run-make/use-suggestions-rust-2018/Makefile
 run-make/used-cdylib-macos/Makefile
-run-make/used/Makefile
 run-make/volatile-intrinsics/Makefile
 run-make/wasm-exceptions-nostd/Makefile
 run-make/wasm-override-linker/Makefile
diff --git a/tests/coverage/attr/nested.cov-map b/tests/coverage/attr/nested.cov-map
new file mode 100644
index 00000000000..a613bb7f8cd
--- /dev/null
+++ b/tests/coverage/attr/nested.cov-map
@@ -0,0 +1,100 @@
+Function name: <<<nested::MyOuter as nested::MyTrait>::trait_method::MyMiddle as nested::MyTrait>::trait_method::MyInner as nested::MyTrait>::trait_method (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 39, 15, 02, 16]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 57, 21) to (start + 2, 22)
+
+Function name: <<<nested::MyOuter>::outer_method::MyMiddle>::middle_method::MyInner>::inner_method (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 23, 15, 02, 16]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 35, 21) to (start + 2, 22)
+
+Function name: <<nested::MyOuter as nested::MyTrait>::trait_method::MyMiddle as nested::MyTrait>::trait_method (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 36, 0d, 08, 0e]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 54, 13) to (start + 8, 14)
+
+Function name: <<nested::MyOuter>::outer_method::MyMiddle>::middle_method (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 20, 0d, 08, 0e]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 32, 13) to (start + 8, 14)
+
+Function name: nested::closure_expr
+Raw bytes (14): 0x[01, 01, 00, 02, 01, 44, 01, 01, 0f, 01, 0b, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 68, 1) to (start + 1, 15)
+- Code(Counter(0)) at (prev + 11, 5) to (start + 1, 2)
+
+Function name: nested::closure_expr::{closure#0}::{closure#0} (unused)
+Raw bytes (14): 0x[01, 01, 00, 02, 00, 47, 1a, 01, 17, 00, 04, 0d, 01, 0a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Zero) at (prev + 71, 26) to (start + 1, 23)
+- Code(Zero) at (prev + 4, 13) to (start + 1, 10)
+
+Function name: nested::closure_expr::{closure#0}::{closure#0}::{closure#0} (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 48, 1d, 02, 0e]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 72, 29) to (start + 2, 14)
+
+Function name: nested::closure_tail
+Raw bytes (14): 0x[01, 01, 00, 02, 01, 53, 01, 01, 0f, 01, 11, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 83, 1) to (start + 1, 15)
+- Code(Counter(0)) at (prev + 17, 5) to (start + 1, 2)
+
+Function name: nested::closure_tail::{closure#0}::{closure#0} (unused)
+Raw bytes (14): 0x[01, 01, 00, 02, 00, 58, 14, 01, 1f, 00, 06, 15, 01, 12]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Zero) at (prev + 88, 20) to (start + 1, 31)
+- Code(Zero) at (prev + 6, 21) to (start + 1, 18)
+
+Function name: nested::closure_tail::{closure#0}::{closure#0}::{closure#0} (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 5a, 1c, 02, 1a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 90, 28) to (start + 2, 26)
+
+Function name: nested::outer_fn::middle_fn (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 11, 05, 05, 06]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 17, 5) to (start + 5, 6)
+
+Function name: nested::outer_fn::middle_fn::inner_fn (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 12, 09, 02, 0a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 18, 9) to (start + 2, 10)
+
diff --git a/tests/coverage/attr/nested.coverage b/tests/coverage/attr/nested.coverage
new file mode 100644
index 00000000000..13129572aec
--- /dev/null
+++ b/tests/coverage/attr/nested.coverage
@@ -0,0 +1,111 @@
+   LL|       |#![feature(coverage_attribute, stmt_expr_attributes)]
+   LL|       |//@ edition: 2021
+   LL|       |
+   LL|       |// Demonstrates the interaction between #[coverage(off)] and various kinds of
+   LL|       |// nested function.
+   LL|       |
+   LL|       |// FIXME(#126625): Coverage attributes should apply recursively to nested functions.
+   LL|       |// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`,
+   LL|       |// its lines can still be marked with misleading execution counts from its enclosing
+   LL|       |// function.
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn do_stuff() {}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn outer_fn() {
+   LL|      0|    fn middle_fn() {
+   LL|      0|        fn inner_fn() {
+   LL|      0|            do_stuff();
+   LL|      0|        }
+   LL|      0|        do_stuff();
+   LL|      0|    }
+   LL|       |    do_stuff();
+   LL|       |}
+   LL|       |
+   LL|       |struct MyOuter;
+   LL|       |impl MyOuter {
+   LL|       |    #[coverage(off)]
+   LL|       |    fn outer_method(&self) {
+   LL|       |        struct MyMiddle;
+   LL|       |        impl MyMiddle {
+   LL|      0|            fn middle_method(&self) {
+   LL|      0|                struct MyInner;
+   LL|      0|                impl MyInner {
+   LL|      0|                    fn inner_method(&self) {
+   LL|      0|                        do_stuff();
+   LL|      0|                    }
+   LL|      0|                }
+   LL|      0|                do_stuff();
+   LL|      0|            }
+   LL|       |        }
+   LL|       |        do_stuff();
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|       |trait MyTrait {
+   LL|       |    fn trait_method(&self);
+   LL|       |}
+   LL|       |impl MyTrait for MyOuter {
+   LL|       |    #[coverage(off)]
+   LL|       |    fn trait_method(&self) {
+   LL|       |        struct MyMiddle;
+   LL|       |        impl MyTrait for MyMiddle {
+   LL|      0|            fn trait_method(&self) {
+   LL|      0|                struct MyInner;
+   LL|      0|                impl MyTrait for MyInner {
+   LL|      0|                    fn trait_method(&self) {
+   LL|      0|                        do_stuff();
+   LL|      0|                    }
+   LL|      0|                }
+   LL|      0|                do_stuff();
+   LL|      0|            }
+   LL|       |        }
+   LL|       |        do_stuff();
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|      1|fn closure_expr() {
+   LL|      1|    let _outer = #[coverage(off)]
+   LL|       |    || {
+   LL|      0|        let _middle = || {
+   LL|      0|            let _inner = || {
+   LL|      0|                do_stuff();
+   LL|      0|            };
+   LL|      0|            do_stuff();
+   LL|      0|        };
+   LL|       |        do_stuff();
+   LL|       |    };
+   LL|      1|    do_stuff();
+   LL|      1|}
+   LL|       |
+   LL|       |// This syntax is allowed, even without #![feature(stmt_expr_attributes)].
+   LL|      1|fn closure_tail() {
+   LL|      1|    let _outer = {
+   LL|       |        #[coverage(off)]
+   LL|       |        || {
+   LL|       |            let _middle = {
+   LL|      0|                || {
+   LL|      0|                    let _inner = {
+   LL|      0|                        || {
+   LL|      0|                            do_stuff();
+   LL|      0|                        }
+   LL|       |                    };
+   LL|      0|                    do_stuff();
+   LL|      0|                }
+   LL|       |            };
+   LL|       |            do_stuff();
+   LL|       |        }
+   LL|       |    };
+   LL|      1|    do_stuff();
+   LL|      1|}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    outer_fn();
+   LL|       |    MyOuter.outer_method();
+   LL|       |    MyOuter.trait_method();
+   LL|       |    closure_expr();
+   LL|       |    closure_tail();
+   LL|       |}
+
diff --git a/tests/coverage/attr/nested.rs b/tests/coverage/attr/nested.rs
new file mode 100644
index 00000000000..c7ff835f44f
--- /dev/null
+++ b/tests/coverage/attr/nested.rs
@@ -0,0 +1,110 @@
+#![feature(coverage_attribute, stmt_expr_attributes)]
+//@ edition: 2021
+
+// Demonstrates the interaction between #[coverage(off)] and various kinds of
+// nested function.
+
+// FIXME(#126625): Coverage attributes should apply recursively to nested functions.
+// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`,
+// its lines can still be marked with misleading execution counts from its enclosing
+// function.
+
+#[coverage(off)]
+fn do_stuff() {}
+
+#[coverage(off)]
+fn outer_fn() {
+    fn middle_fn() {
+        fn inner_fn() {
+            do_stuff();
+        }
+        do_stuff();
+    }
+    do_stuff();
+}
+
+struct MyOuter;
+impl MyOuter {
+    #[coverage(off)]
+    fn outer_method(&self) {
+        struct MyMiddle;
+        impl MyMiddle {
+            fn middle_method(&self) {
+                struct MyInner;
+                impl MyInner {
+                    fn inner_method(&self) {
+                        do_stuff();
+                    }
+                }
+                do_stuff();
+            }
+        }
+        do_stuff();
+    }
+}
+
+trait MyTrait {
+    fn trait_method(&self);
+}
+impl MyTrait for MyOuter {
+    #[coverage(off)]
+    fn trait_method(&self) {
+        struct MyMiddle;
+        impl MyTrait for MyMiddle {
+            fn trait_method(&self) {
+                struct MyInner;
+                impl MyTrait for MyInner {
+                    fn trait_method(&self) {
+                        do_stuff();
+                    }
+                }
+                do_stuff();
+            }
+        }
+        do_stuff();
+    }
+}
+
+fn closure_expr() {
+    let _outer = #[coverage(off)]
+    || {
+        let _middle = || {
+            let _inner = || {
+                do_stuff();
+            };
+            do_stuff();
+        };
+        do_stuff();
+    };
+    do_stuff();
+}
+
+// This syntax is allowed, even without #![feature(stmt_expr_attributes)].
+fn closure_tail() {
+    let _outer = {
+        #[coverage(off)]
+        || {
+            let _middle = {
+                || {
+                    let _inner = {
+                        || {
+                            do_stuff();
+                        }
+                    };
+                    do_stuff();
+                }
+            };
+            do_stuff();
+        }
+    };
+    do_stuff();
+}
+
+#[coverage(off)]
+fn main() {
+    outer_fn();
+    MyOuter.outer_method();
+    MyOuter.trait_method();
+    closure_expr();
+    closure_tail();
+}
diff --git a/tests/coverage/attr/off-on-sandwich.cov-map b/tests/coverage/attr/off-on-sandwich.cov-map
new file mode 100644
index 00000000000..72b96420cb5
--- /dev/null
+++ b/tests/coverage/attr/off-on-sandwich.cov-map
@@ -0,0 +1,32 @@
+Function name: off_on_sandwich::dense_a::dense_b
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 14, 05, 07, 06]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 20, 5) to (start + 7, 6)
+
+Function name: off_on_sandwich::sparse_a::sparse_b
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 22, 05, 10, 06]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 34, 5) to (start + 16, 6)
+
+Function name: off_on_sandwich::sparse_a::sparse_b::sparse_c
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 26, 09, 0b, 0a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 38, 9) to (start + 11, 10)
+
+Function name: off_on_sandwich::sparse_a::sparse_b::sparse_c::sparse_d
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 29, 0d, 07, 0e]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 41, 13) to (start + 7, 14)
+
diff --git a/tests/coverage/attr/off-on-sandwich.coverage b/tests/coverage/attr/off-on-sandwich.coverage
new file mode 100644
index 00000000000..e831b0e926e
--- /dev/null
+++ b/tests/coverage/attr/off-on-sandwich.coverage
@@ -0,0 +1,58 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |//@ edition: 2021
+   LL|       |
+   LL|       |// Demonstrates the interaction of `#[coverage(off)]` and `#[coverage(on)]`
+   LL|       |// in nested functions.
+   LL|       |
+   LL|       |// FIXME(#126625): Coverage attributes should apply recursively to nested functions.
+   LL|       |// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`,
+   LL|       |// its lines can still be marked with misleading execution counts from its enclosing
+   LL|       |// function.
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn do_stuff() {}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn dense_a() {
+   LL|       |    dense_b();
+   LL|       |    dense_b();
+   LL|       |    #[coverage(on)]
+   LL|      2|    fn dense_b() {
+   LL|      2|        dense_c();
+   LL|      2|        dense_c();
+   LL|      2|        #[coverage(off)]
+   LL|      2|        fn dense_c() {
+   LL|      2|            do_stuff();
+   LL|      2|        }
+   LL|      2|    }
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn sparse_a() {
+   LL|       |    sparse_b();
+   LL|       |    sparse_b();
+   LL|      2|    fn sparse_b() {
+   LL|      2|        sparse_c();
+   LL|      2|        sparse_c();
+   LL|      2|        #[coverage(on)]
+   LL|      4|        fn sparse_c() {
+   LL|      4|            sparse_d();
+   LL|      4|            sparse_d();
+   LL|      8|            fn sparse_d() {
+   LL|      8|                sparse_e();
+   LL|      8|                sparse_e();
+   LL|      8|                #[coverage(off)]
+   LL|      8|                fn sparse_e() {
+   LL|      8|                    do_stuff();
+   LL|      8|                }
+   LL|      8|            }
+   LL|      4|        }
+   LL|      2|    }
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    dense_a();
+   LL|       |    sparse_a();
+   LL|       |}
+
diff --git a/tests/coverage/attr/off-on-sandwich.rs b/tests/coverage/attr/off-on-sandwich.rs
new file mode 100644
index 00000000000..6b21b180223
--- /dev/null
+++ b/tests/coverage/attr/off-on-sandwich.rs
@@ -0,0 +1,57 @@
+#![feature(coverage_attribute)]
+//@ edition: 2021
+
+// Demonstrates the interaction of `#[coverage(off)]` and `#[coverage(on)]`
+// in nested functions.
+
+// FIXME(#126625): Coverage attributes should apply recursively to nested functions.
+// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`,
+// its lines can still be marked with misleading execution counts from its enclosing
+// function.
+
+#[coverage(off)]
+fn do_stuff() {}
+
+#[coverage(off)]
+fn dense_a() {
+    dense_b();
+    dense_b();
+    #[coverage(on)]
+    fn dense_b() {
+        dense_c();
+        dense_c();
+        #[coverage(off)]
+        fn dense_c() {
+            do_stuff();
+        }
+    }
+}
+
+#[coverage(off)]
+fn sparse_a() {
+    sparse_b();
+    sparse_b();
+    fn sparse_b() {
+        sparse_c();
+        sparse_c();
+        #[coverage(on)]
+        fn sparse_c() {
+            sparse_d();
+            sparse_d();
+            fn sparse_d() {
+                sparse_e();
+                sparse_e();
+                #[coverage(off)]
+                fn sparse_e() {
+                    do_stuff();
+                }
+            }
+        }
+    }
+}
+
+#[coverage(off)]
+fn main() {
+    dense_a();
+    sparse_a();
+}
diff --git a/tests/coverage/branch/no-mir-spans.cov-map b/tests/coverage/branch/no-mir-spans.cov-map
new file mode 100644
index 00000000000..cb19211913f
--- /dev/null
+++ b/tests/coverage/branch/no-mir-spans.cov-map
@@ -0,0 +1,52 @@
+Function name: no_mir_spans::while_cond
+Raw bytes (16): 0x[01, 01, 00, 02, 01, 10, 01, 00, 11, 20, 05, 09, 04, 0b, 00, 10]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 16, 1) to (start + 0, 17)
+- Branch { true: Counter(1), false: Counter(2) } at (prev + 4, 11) to (start + 0, 16)
+    true  = c1
+    false = c2
+
+Function name: no_mir_spans::while_cond_not
+Raw bytes (16): 0x[01, 01, 00, 02, 01, 19, 01, 00, 15, 20, 09, 05, 04, 0b, 00, 14]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 25, 1) to (start + 0, 21)
+- Branch { true: Counter(2), false: Counter(1) } at (prev + 4, 11) to (start + 0, 20)
+    true  = c2
+    false = c1
+
+Function name: no_mir_spans::while_op_and
+Raw bytes (25): 0x[01, 01, 01, 09, 0d, 03, 01, 22, 01, 00, 13, 20, 09, 05, 05, 0b, 00, 10, 20, 02, 0d, 00, 14, 00, 19]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 1
+- expression 0 operands: lhs = Counter(2), rhs = Counter(3)
+Number of file 0 mappings: 3
+- Code(Counter(0)) at (prev + 34, 1) to (start + 0, 19)
+- Branch { true: Counter(2), false: Counter(1) } at (prev + 5, 11) to (start + 0, 16)
+    true  = c2
+    false = c1
+- Branch { true: Expression(0, Sub), false: Counter(3) } at (prev + 0, 20) to (start + 0, 25)
+    true  = (c2 - c3)
+    false = c3
+
+Function name: no_mir_spans::while_op_or
+Raw bytes (25): 0x[01, 01, 01, 09, 0d, 03, 01, 2d, 01, 00, 12, 20, 05, 09, 05, 0b, 00, 10, 20, 0d, 02, 00, 14, 00, 19]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 1
+- expression 0 operands: lhs = Counter(2), rhs = Counter(3)
+Number of file 0 mappings: 3
+- Code(Counter(0)) at (prev + 45, 1) to (start + 0, 18)
+- Branch { true: Counter(1), false: Counter(2) } at (prev + 5, 11) to (start + 0, 16)
+    true  = c1
+    false = c2
+- Branch { true: Counter(3), false: Expression(0, Sub) } at (prev + 0, 20) to (start + 0, 25)
+    true  = c3
+    false = (c2 - c3)
+
diff --git a/tests/coverage/branch/no-mir-spans.coverage b/tests/coverage/branch/no-mir-spans.coverage
new file mode 100644
index 00000000000..2cae98ed3ff
--- /dev/null
+++ b/tests/coverage/branch/no-mir-spans.coverage
@@ -0,0 +1,77 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |//@ edition: 2021
+   LL|       |//@ compile-flags: -Zcoverage-options=branch,no-mir-spans
+   LL|       |//@ llvm-cov-flags: --show-branches=count
+   LL|       |
+   LL|       |// Tests the behaviour of the `-Zcoverage-options=no-mir-spans` debugging flag.
+   LL|       |// The actual code below is just some non-trivial code copied from another test
+   LL|       |// (`while.rs`), and has no particular significance.
+   LL|       |
+   LL|       |macro_rules! no_merge {
+   LL|       |    () => {
+   LL|       |        for _ in 0..1 {}
+   LL|       |    };
+   LL|       |}
+   LL|       |
+   LL|      1|fn while_cond() {
+   LL|       |    no_merge!();
+   LL|       |
+   LL|       |    let mut a = 8;
+   LL|       |    while a > 0 {
+  ------------------
+  |  Branch (LL:11): [True: 8, False: 1]
+  ------------------
+   LL|       |        a -= 1;
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|      1|fn while_cond_not() {
+   LL|       |    no_merge!();
+   LL|       |
+   LL|       |    let mut a = 8;
+   LL|       |    while !(a == 0) {
+  ------------------
+  |  Branch (LL:11): [True: 8, False: 1]
+  ------------------
+   LL|       |        a -= 1;
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|      1|fn while_op_and() {
+   LL|       |    no_merge!();
+   LL|       |
+   LL|       |    let mut a = 8;
+   LL|       |    let mut b = 4;
+   LL|       |    while a > 0 && b > 0 {
+  ------------------
+  |  Branch (LL:11): [True: 5, False: 0]
+  |  Branch (LL:20): [True: 4, False: 1]
+  ------------------
+   LL|       |        a -= 1;
+   LL|       |        b -= 1;
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|      1|fn while_op_or() {
+   LL|       |    no_merge!();
+   LL|       |
+   LL|       |    let mut a = 4;
+   LL|       |    let mut b = 8;
+   LL|       |    while a > 0 || b > 0 {
+  ------------------
+  |  Branch (LL:11): [True: 4, False: 5]
+  |  Branch (LL:20): [True: 4, False: 1]
+  ------------------
+   LL|       |        a -= 1;
+   LL|       |        b -= 1;
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    while_cond();
+   LL|       |    while_cond_not();
+   LL|       |    while_op_and();
+   LL|       |    while_op_or();
+   LL|       |}
+
diff --git a/tests/coverage/branch/no-mir-spans.rs b/tests/coverage/branch/no-mir-spans.rs
new file mode 100644
index 00000000000..acb268f2d45
--- /dev/null
+++ b/tests/coverage/branch/no-mir-spans.rs
@@ -0,0 +1,62 @@
+#![feature(coverage_attribute)]
+//@ edition: 2021
+//@ compile-flags: -Zcoverage-options=branch,no-mir-spans
+//@ llvm-cov-flags: --show-branches=count
+
+// Tests the behaviour of the `-Zcoverage-options=no-mir-spans` debugging flag.
+// The actual code below is just some non-trivial code copied from another test
+// (`while.rs`), and has no particular significance.
+
+macro_rules! no_merge {
+    () => {
+        for _ in 0..1 {}
+    };
+}
+
+fn while_cond() {
+    no_merge!();
+
+    let mut a = 8;
+    while a > 0 {
+        a -= 1;
+    }
+}
+
+fn while_cond_not() {
+    no_merge!();
+
+    let mut a = 8;
+    while !(a == 0) {
+        a -= 1;
+    }
+}
+
+fn while_op_and() {
+    no_merge!();
+
+    let mut a = 8;
+    let mut b = 4;
+    while a > 0 && b > 0 {
+        a -= 1;
+        b -= 1;
+    }
+}
+
+fn while_op_or() {
+    no_merge!();
+
+    let mut a = 4;
+    let mut b = 8;
+    while a > 0 || b > 0 {
+        a -= 1;
+        b -= 1;
+    }
+}
+
+#[coverage(off)]
+fn main() {
+    while_cond();
+    while_cond_not();
+    while_op_and();
+    while_op_or();
+}
diff --git a/tests/run-make/error-found-staticlib-instead-crate/Makefile b/tests/run-make/error-found-staticlib-instead-crate/Makefile
deleted file mode 100644
index 0eae41d720c..00000000000
--- a/tests/run-make/error-found-staticlib-instead-crate/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-include ../tools.mk
-
-all:
-	$(RUSTC) foo.rs --crate-type staticlib
-	$(RUSTC) bar.rs 2>&1 | $(CGREP) "found staticlib"
diff --git a/tests/run-make/error-found-staticlib-instead-crate/rmake.rs b/tests/run-make/error-found-staticlib-instead-crate/rmake.rs
new file mode 100644
index 00000000000..8c707092b7e
--- /dev/null
+++ b/tests/run-make/error-found-staticlib-instead-crate/rmake.rs
@@ -0,0 +1,11 @@
+// When rustc is looking for a crate but is given a staticlib instead,
+// the error message should be helpful and indicate precisely the cause
+// of the compilation failure.
+// See https://github.com/rust-lang/rust/pull/21978
+
+use run_make_support::rustc;
+
+fn main() {
+    rustc().input("foo.rs").crate_type("staticlib").run();
+    rustc().input("bar.rs").run_fail().assert_stderr_contains("found staticlib");
+}
diff --git a/tests/run-make/native-link-modifier-verbatim-linker/Makefile b/tests/run-make/native-link-modifier-verbatim-linker/Makefile
deleted file mode 100644
index 47ed2a14291..00000000000
--- a/tests/run-make/native-link-modifier-verbatim-linker/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-# ignore-cross-compile
-# ignore-apple
-
-include ../tools.mk
-
-all:
-	# Verbatim allows specify precise name.
-	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_some_strange_name.ext
-	$(RUSTC) main.rs -l static:+verbatim=local_some_strange_name.ext
-
-	# With verbatim any other name cannot be used (local).
-	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/liblocal_native_dep.a
-	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_native_dep.a
-	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_native_dep.lib
-	$(RUSTC) main.rs -l static:+verbatim=local_native_dep 2>&1 | $(CGREP) "local_native_dep"
diff --git a/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs b/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs
new file mode 100644
index 00000000000..6868cb368cc
--- /dev/null
+++ b/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs
@@ -0,0 +1,41 @@
+// `verbatim` is a native link modifier that forces rustc to only accept libraries with
+// a specified name. This test checks that this modifier works as intended.
+// This test is the same as native-link-modifier-rustc, but without rlibs.
+// See https://github.com/rust-lang/rust/issues/99425
+
+//@ ignore-apple
+// Reason: linking fails due to the unusual ".ext" staticlib name.
+
+use run_make_support::rustc;
+
+fn main() {
+    // Verbatim allows for the specification of a precise name
+    // - in this case, the unconventional ".ext" extension.
+    rustc()
+        .input("local_native_dep.rs")
+        .crate_type("staticlib")
+        .output("local_some_strange_name.ext")
+        .run();
+    rustc().input("main.rs").arg("-lstatic:+verbatim=local_some_strange_name.ext").run();
+
+    // This section voluntarily avoids using static_lib_name helpers to be verbatim.
+    // With verbatim, even these common library names are refused
+    // - it wants local_native_dep without
+    // any file extensions.
+    rustc()
+        .input("local_native_dep.rs")
+        .crate_type("staticlib")
+        .output("liblocal_native_dep.a")
+        .run();
+    rustc().input("local_native_dep.rs").crate_type("staticlib").output("local_native_dep.a").run();
+    rustc()
+        .input("local_native_dep.rs")
+        .crate_type("staticlib")
+        .output("local_native_dep.lib")
+        .run();
+    rustc()
+        .input("main.rs")
+        .arg("-lstatic:+verbatim=local_native_dep")
+        .run_fail()
+        .assert_stderr_contains("local_native_dep");
+}
diff --git a/tests/run-make/native-link-modifier-verbatim-rustc/Makefile b/tests/run-make/native-link-modifier-verbatim-rustc/Makefile
deleted file mode 100644
index dfd6ec50fc0..00000000000
--- a/tests/run-make/native-link-modifier-verbatim-rustc/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-include ../tools.mk
-
-all:
-	# Verbatim allows specify precise name.
-	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_some_strange_name.ext
-	$(RUSTC) rust_dep.rs -l static:+verbatim=upstream_some_strange_name.ext --crate-type rlib
-
-	# With verbatim any other name cannot be used (upstream).
-	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/libupstream_native_dep.a
-	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_native_dep.a
-	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_native_dep.lib
-	$(RUSTC) rust_dep.rs -l static:+verbatim=upstream_native_dep --crate-type rlib 2>&1 | $(CGREP) "upstream_native_dep"
diff --git a/tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs b/tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs
new file mode 100644
index 00000000000..703b8a80ef3
--- /dev/null
+++ b/tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs
@@ -0,0 +1,47 @@
+// `verbatim` is a native link modifier that forces rustc to only accept libraries with
+// a specified name. This test checks that this modifier works as intended.
+// This test is the same as native-link-modifier-linker, but with rlibs.
+// See https://github.com/rust-lang/rust/issues/99425
+
+use run_make_support::rustc;
+
+fn main() {
+    // Verbatim allows for the specification of a precise name
+    // - in this case, the unconventional ".ext" extension.
+    rustc()
+        .input("upstream_native_dep.rs")
+        .crate_type("staticlib")
+        .output("upstream_some_strange_name.ext")
+        .run();
+    rustc()
+        .input("rust_dep.rs")
+        .crate_type("rlib")
+        .arg("-lstatic:+verbatim=upstream_some_strange_name.ext")
+        .run();
+
+    // This section voluntarily avoids using static_lib_name helpers to be verbatim.
+    // With verbatim, even these common library names are refused
+    // - it wants upstream_native_dep without
+    // any file extensions.
+    rustc()
+        .input("upstream_native_dep.rs")
+        .crate_type("staticlib")
+        .output("libupstream_native_dep.a")
+        .run();
+    rustc()
+        .input("upstream_native_dep.rs")
+        .crate_type("staticlib")
+        .output("upstream_native_dep.a")
+        .run();
+    rustc()
+        .input("upstream_native_dep.rs")
+        .crate_type("staticlib")
+        .output("upstream_native_dep.lib")
+        .run();
+    rustc()
+        .input("rust_dep.rs")
+        .crate_type("rlib")
+        .arg("-lstatic:+verbatim=upstream_native_dep")
+        .run_fail()
+        .assert_stderr_contains("upstream_native_dep");
+}
diff --git a/tests/run-make/output-filename-conflicts-with-directory/Makefile b/tests/run-make/output-filename-conflicts-with-directory/Makefile
deleted file mode 100644
index 45221356cd9..00000000000
--- a/tests/run-make/output-filename-conflicts-with-directory/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-include ../tools.mk
-
-all:
-	cp foo.rs $(TMPDIR)/foo.rs
-	mkdir $(TMPDIR)/foo
-	$(RUSTC) $(TMPDIR)/foo.rs -o $(TMPDIR)/foo 2>&1 \
-		| $(CGREP) -e "the generated executable for the input file \".*foo\.rs\" conflicts with the existing directory \".*foo\""
diff --git a/tests/run-make/output-filename-conflicts-with-directory/rmake.rs b/tests/run-make/output-filename-conflicts-with-directory/rmake.rs
new file mode 100644
index 00000000000..4b5c9e8d118
--- /dev/null
+++ b/tests/run-make/output-filename-conflicts-with-directory/rmake.rs
@@ -0,0 +1,14 @@
+// ignore-tidy-linelength
+// When the compiled executable would conflict with a directory, a
+// rustc error should be displayed instead of a verbose and
+// potentially-confusing linker error.
+// See https://github.com/rust-lang/rust/pull/47203
+
+use run_make_support::{fs_wrapper, rustc};
+
+fn main() {
+    fs_wrapper::create_dir("foo");
+    rustc().input("foo.rs").output("foo").run_fail().assert_stderr_contains(
+        r#"the generated executable for the input file "foo.rs" conflicts with the existing directory "foo""#,
+    );
+}
diff --git a/tests/run-make/output-filename-overwrites-input/Makefile b/tests/run-make/output-filename-overwrites-input/Makefile
deleted file mode 100644
index fe5d231382d..00000000000
--- a/tests/run-make/output-filename-overwrites-input/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-# ignore-cross-compile
-include ../tools.mk
-
-all:
-	cp foo.rs $(TMPDIR)/foo
-	$(RUSTC) $(TMPDIR)/foo -o $(TMPDIR)/foo 2>&1 \
-		| $(CGREP) -e "the input file \".*foo\" would be overwritten by the generated executable"
-	cp bar.rs $(TMPDIR)/bar.rlib
-	$(RUSTC) $(TMPDIR)/bar.rlib -o $(TMPDIR)/bar.rlib 2>&1 \
-		| $(CGREP) -e "the input file \".*bar.rlib\" would be overwritten by the generated executable"
-	$(RUSTC) foo.rs 2>&1 && $(RUSTC) -Z ls=root $(TMPDIR)/foo 2>&1
-	cp foo.rs $(TMPDIR)/foo.rs
-	$(RUSTC) $(TMPDIR)/foo.rs -o $(TMPDIR)/foo.rs 2>&1 \
-		| $(CGREP) -e "the input file \".*foo.rs\" would be overwritten by the generated executable"
diff --git a/tests/run-make/output-filename-overwrites-input/rmake.rs b/tests/run-make/output-filename-overwrites-input/rmake.rs
new file mode 100644
index 00000000000..c6055e818a1
--- /dev/null
+++ b/tests/run-make/output-filename-overwrites-input/rmake.rs
@@ -0,0 +1,21 @@
+// If rustc is invoked on a file that would be overwritten by the
+// compilation, the compilation should fail, to avoid accidental loss.
+// See https://github.com/rust-lang/rust/pull/46814
+
+//@ ignore-cross-compile
+
+use run_make_support::{fs_wrapper, rustc};
+
+fn main() {
+    fs_wrapper::copy("foo.rs", "foo");
+    rustc().input("foo").output("foo").run_fail().assert_stderr_contains(
+        r#"the input file "foo" would be overwritten by the generated executable"#,
+    );
+    fs_wrapper::copy("bar.rs", "bar.rlib");
+    rustc().input("bar.rlib").output("bar.rlib").run_fail().assert_stderr_contains(
+        r#"the input file "bar.rlib" would be overwritten by the generated executable"#,
+    );
+    rustc().input("foo.rs").output("foo.rs").run_fail().assert_stderr_contains(
+        r#"the input file "foo.rs" would be overwritten by the generated executable"#,
+    );
+}
diff --git a/tests/run-make/used/Makefile b/tests/run-make/used/Makefile
deleted file mode 100644
index e80eb9e4020..00000000000
--- a/tests/run-make/used/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-include ../tools.mk
-
-# ignore-windows-msvc
-
-all:
-	$(RUSTC) -C opt-level=3 --emit=obj used.rs
-	nm $(TMPDIR)/used.o | $(CGREP) FOO
diff --git a/tests/run-make/used/rmake.rs b/tests/run-make/used/rmake.rs
new file mode 100644
index 00000000000..56ef5c6b9cc
--- /dev/null
+++ b/tests/run-make/used/rmake.rs
@@ -0,0 +1,15 @@
+// This test ensures that the compiler is keeping static variables, even if not referenced
+// by another part of the program, in the output object file.
+//
+// It comes from #39987 which implements this RFC for the #[used] attribute:
+// https://rust-lang.github.io/rfcs/2386-used.html
+
+//@ ignore-msvc
+
+use run_make_support::{cmd, rustc};
+
+fn main() {
+    rustc().opt_level("3").emit("obj").input("used.rs").run();
+
+    cmd("nm").arg("used.o").run().assert_stdout_contains("FOO");
+}
diff --git a/tests/ui/coverage-attr/bad-syntax.rs b/tests/ui/coverage-attr/bad-syntax.rs
new file mode 100644
index 00000000000..8783714992b
--- /dev/null
+++ b/tests/ui/coverage-attr/bad-syntax.rs
@@ -0,0 +1,58 @@
+#![feature(coverage_attribute)]
+
+// Tests the error messages produced (or not produced) by various unusual
+// uses of the `#[coverage(..)]` attribute.
+
+// FIXME(#84605): Multiple coverage attributes with the same value are useless,
+// and should probably produce a diagnostic.
+#[coverage(off)]
+#[coverage(off)]
+fn multiple_consistent() {}
+
+// FIXME(#84605): When there are multiple inconsistent coverage attributes,
+// it's unclear which one will prevail.
+#[coverage(off)]
+#[coverage(on)]
+fn multiple_inconsistent() {}
+
+#[coverage] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn bare_word() {}
+
+// FIXME(#84605): 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)]
+fn key_value() {}
+
+#[coverage()] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn list_empty() {}
+
+#[coverage(off, off)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn list_consistent() {}
+
+#[coverage(off, on)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn list_inconsistent() {}
+
+#[coverage(bogus)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn bogus_word() {}
+
+#[coverage(bogus, off)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn bogus_word_before() {}
+
+#[coverage(off, bogus)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn bogus_word_after() {}
+
+#[coverage(off,)]
+fn comma_after() {}
+
+// FIXME(#84605): This shows as multiple different errors.
+#[coverage(,off)]
+//~^ ERROR expected identifier, found `,`
+//~| HELP remove this comma
+//~| ERROR expected `coverage(off)` or `coverage(on)`
+fn comma_before() {}
+
+fn main() {}
diff --git a/tests/ui/coverage-attr/bad-syntax.stderr b/tests/ui/coverage-attr/bad-syntax.stderr
new file mode 100644
index 00000000000..f6181d12a94
--- /dev/null
+++ b/tests/ui/coverage-attr/bad-syntax.stderr
@@ -0,0 +1,78 @@
+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)]
+   |
+LL | #[coverage]
+   |
+
+error: expected identifier, found `,`
+  --> $DIR/bad-syntax.rs:52:12
+   |
+LL | #[coverage(,off)]
+   |            ^
+   |            |
+   |            expected identifier
+   |            help: remove this comma
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:18:1
+   |
+LL | #[coverage]
+   | ^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:23:1
+   |
+LL | #[coverage = true]
+   | ^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:30:1
+   |
+LL | #[coverage()]
+   | ^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:33:1
+   |
+LL | #[coverage(off, off)]
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:36:1
+   |
+LL | #[coverage(off, on)]
+   | ^^^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:39:1
+   |
+LL | #[coverage(bogus)]
+   | ^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:42:1
+   |
+LL | #[coverage(bogus, off)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:45:1
+   |
+LL | #[coverage(off, bogus)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:52:1
+   |
+LL | #[coverage(,off)]
+   | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 11 previous errors
+
diff --git a/tests/ui/lint/no-coverage.rs b/tests/ui/coverage-attr/no-coverage.rs
index 907d25d333e..907d25d333e 100644
--- a/tests/ui/lint/no-coverage.rs
+++ b/tests/ui/coverage-attr/no-coverage.rs
diff --git a/tests/ui/lint/no-coverage.stderr b/tests/ui/coverage-attr/no-coverage.stderr
index a87b0fb49f0..a87b0fb49f0 100644
--- a/tests/ui/lint/no-coverage.stderr
+++ b/tests/ui/coverage-attr/no-coverage.stderr
diff --git a/tests/ui/instrument-coverage/coverage-options.bad.stderr b/tests/ui/instrument-coverage/coverage-options.bad.stderr
index 4a272cf97fb..1a6b30dc832 100644
--- a/tests/ui/instrument-coverage/coverage-options.bad.stderr
+++ b/tests/ui/instrument-coverage/coverage-options.bad.stderr
@@ -1,2 +1,2 @@
-error: incorrect value `bad` for unstable option `coverage-options` - `block` | `branch` | `condition` | `mcdc` was expected
+error: incorrect value `bad` for unstable option `coverage-options` - `block` | `branch` | `condition` | `mcdc` | `no-mir-spans` was expected