about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs8
-rw-r--r--compiler/rustc_middle/src/mir/coverage.rs28
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs4
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs9
-rw-r--r--compiler/rustc_middle/src/query/mod.rs4
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters.rs18
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs182
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs20
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters/union_find.rs28
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs32
-rw-r--r--compiler/rustc_mir_transform/src/coverage/query.rs12
-rw-r--r--library/alloc/src/vec/mod.rs18
-rw-r--r--src/doc/rustc-dev-guide/src/conventions.md28
-rw-r--r--src/doc/rustc-dev-guide/src/tests/directives.md3
-rw-r--r--src/tools/compiletest/src/common.rs11
-rw-r--r--src/tools/compiletest/src/directive-list.rs1
-rw-r--r--src/tools/compiletest/src/header/needs.rs8
-rw-r--r--src/tools/tidy/src/ext_tool_checks.rs141
-rw-r--r--src/tools/tidy/src/issues.txt1
-rw-r--r--src/tools/tidy/src/ui_tests.rs2
-rw-r--r--tests/assembly/powerpc64-struct-abi.rs8
-rw-r--r--tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff4
-rw-r--r--tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff12
-rw-r--r--tests/mir-opt/coverage/instrument_coverage.rs19
-rw-r--r--tests/ui/abi/homogenous-floats-target-feature-mixup.rs3
-rw-r--r--tests/ui/abi/segfault-no-out-of-stack.rs3
-rw-r--r--tests/ui/abi/stack-probes-lto.rs2
-rw-r--r--tests/ui/abi/stack-probes.rs3
-rw-r--r--tests/ui/alloc-error/default-alloc-error-hook.rs3
-rw-r--r--tests/ui/array-slice-vec/bounds-check-no-overflow.rs2
-rw-r--r--tests/ui/array-slice-vec/box-of-array-of-drop-1.rs4
-rw-r--r--tests/ui/array-slice-vec/box-of-array-of-drop-2.rs4
-rw-r--r--tests/ui/array-slice-vec/dst-raw-slice.rs2
-rw-r--r--tests/ui/array-slice-vec/nested-vec-3.rs4
-rw-r--r--tests/ui/array-slice-vec/slice-panic-1.rs3
-rw-r--r--tests/ui/array-slice-vec/slice-panic-2.rs3
-rw-r--r--tests/ui/array-slice-vec/vec-overrun.rs2
-rw-r--r--tests/ui/backtrace/backtrace.rs3
-rw-r--r--tests/ui/backtrace/std-backtrace.rs3
-rw-r--r--tests/ui/binop/binop-fail-3.rs2
-rw-r--r--tests/ui/binop/binop-panic.rs2
-rw-r--r--tests/ui/borrowck/borrowck-local-borrow.rs2
-rw-r--r--tests/ui/borrowck/issue-28934.rs2
-rw-r--r--tests/ui/box/unit/unwind-unique.rs2
-rw-r--r--tests/ui/cleanup-rvalue-temp-during-incomplete-alloc.rs2
-rw-r--r--tests/ui/closures/diverging-closure.rs2
-rw-r--r--tests/ui/command/command-argv0.rs5
-rw-r--r--tests/ui/command/command-current-dir.rs3
-rw-r--r--tests/ui/command/command-exec.rs8
-rw-r--r--tests/ui/command/command-pre-exec.rs10
-rw-r--r--tests/ui/command/command-setgroups.rs5
-rw-r--r--tests/ui/command/command-uid-gid.rs3
-rw-r--r--tests/ui/command/issue-10626.rs3
-rw-r--r--tests/ui/consts/issue-29798.rs2
-rw-r--r--tests/ui/coroutine/coroutine-resume-after-panic.rs2
-rw-r--r--tests/ui/drop/drop-trait-enum.rs2
-rw-r--r--tests/ui/drop/terminate-in-initializer.rs2
-rw-r--r--tests/ui/expr/if/expr-if-panic-fn.rs2
-rw-r--r--tests/ui/expr/if/expr-if-panic.rs2
-rw-r--r--tests/ui/expr/if/if-check-panic.rs2
-rw-r--r--tests/ui/expr/if/if-cond-bot.rs2
-rw-r--r--tests/ui/extern/issue-18576.rs2
-rw-r--r--tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs2
-rw-r--r--tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs2
-rw-r--r--tests/ui/fn/expr-fn-panic.rs2
-rw-r--r--tests/ui/hashmap/hashmap-capacity-overflow.rs2
-rw-r--r--tests/ui/imports/glob-use-std.rs2
-rw-r--r--tests/ui/intrinsics/panic-uninitialized-zeroed.rs9
-rw-r--r--tests/ui/issues/issue-12920.rs2
-rw-r--r--tests/ui/issues/issue-13202.rs2
-rw-r--r--tests/ui/issues/issue-20971.rs2
-rw-r--r--tests/ui/issues/issue-2190-1.rs14
-rw-r--r--tests/ui/issues/issue-23354-2.rs2
-rw-r--r--tests/ui/issues/issue-23354.rs2
-rw-r--r--tests/ui/issues/issue-2470-bounds-check-overflow.rs2
-rw-r--r--tests/ui/issues/issue-25089.rs2
-rw-r--r--tests/ui/issues/issue-26655.rs2
-rw-r--r--tests/ui/issues/issue-2761.rs2
-rw-r--r--tests/ui/issues/issue-29485.rs2
-rw-r--r--tests/ui/issues/issue-30018-panic.rs2
-rw-r--r--tests/ui/issues/issue-3029.rs2
-rw-r--r--tests/ui/issues/issue-30380.rs2
-rw-r--r--tests/ui/issues/issue-33770.rs3
-rw-r--r--tests/ui/issues/issue-38763.rs2
-rw-r--r--tests/ui/issues/issue-44216-add-system-time.rs2
-rw-r--r--tests/ui/issues/issue-44216-sub-instant.rs2
-rw-r--r--tests/ui/issues/issue-44216-sub-system-time.rs2
-rw-r--r--tests/ui/lifetimes/tail-expr-lock-poisoning.rs3
-rw-r--r--tests/ui/loops/for-each-loop-panic.rs2
-rw-r--r--tests/ui/macros/assert-as-macro.rs2
-rw-r--r--tests/ui/macros/assert-eq-macro-msg.rs2
-rw-r--r--tests/ui/macros/assert-eq-macro-panic.rs2
-rw-r--r--tests/ui/macros/assert-macro-explicit.rs2
-rw-r--r--tests/ui/macros/assert-macro-fmt.rs2
-rw-r--r--tests/ui/macros/assert-macro-owned.rs2
-rw-r--r--tests/ui/macros/assert-macro-static.rs2
-rw-r--r--tests/ui/macros/assert-matches-macro-msg.rs2
-rw-r--r--tests/ui/macros/assert-ne-macro-msg.rs2
-rw-r--r--tests/ui/macros/assert-ne-macro-panic.rs2
-rw-r--r--tests/ui/macros/die-macro-2.rs2
-rw-r--r--tests/ui/macros/die-macro-expr.rs2
-rw-r--r--tests/ui/macros/die-macro-pure.rs2
-rw-r--r--tests/ui/macros/unimplemented-macro-panic.rs2
-rw-r--r--tests/ui/macros/unreachable-arg.rs2
-rw-r--r--tests/ui/macros/unreachable-fmt-msg.rs2
-rw-r--r--tests/ui/macros/unreachable-format-arg.rs2
-rw-r--r--tests/ui/macros/unreachable-format-args.rs2
-rw-r--r--tests/ui/macros/unreachable-macro-panic.rs2
-rw-r--r--tests/ui/macros/unreachable-static-msg.rs2
-rw-r--r--tests/ui/macros/unreachable.rs2
-rw-r--r--tests/ui/match/expr-match-panic-fn.rs2
-rw-r--r--tests/ui/match/expr-match-panic.rs2
-rw-r--r--tests/ui/match/match-bot-panic.rs2
-rw-r--r--tests/ui/match/match-disc-bot.rs2
-rw-r--r--tests/ui/match/match-wildcards.rs2
-rw-r--r--tests/ui/meta/revision-ok.rs2
-rw-r--r--tests/ui/mir/mir_codegen_calls_converging_drops.rs2
-rw-r--r--tests/ui/mir/mir_codegen_calls_converging_drops_2.rs2
-rw-r--r--tests/ui/mir/mir_codegen_calls_diverging.rs2
-rw-r--r--tests/ui/mir/mir_dynamic_drops_1.rs2
-rw-r--r--tests/ui/mir/mir_dynamic_drops_2.rs2
-rw-r--r--tests/ui/mir/mir_dynamic_drops_3.rs2
-rw-r--r--tests/ui/mir/mir_indexing_oob_1.rs2
-rw-r--r--tests/ui/mir/mir_indexing_oob_2.rs2
-rw-r--r--tests/ui/mir/mir_indexing_oob_3.rs2
-rw-r--r--tests/ui/never_type/return-never-coerce.rs2
-rw-r--r--tests/ui/nll/issue-51345-2.rs2
-rw-r--r--tests/ui/numbers-arithmetic/divide-by-zero.rs2
-rw-r--r--tests/ui/numbers-arithmetic/int-abs-overflow.rs2
-rw-r--r--tests/ui/numbers-arithmetic/issue-8460.rs2
-rw-r--r--tests/ui/numbers-arithmetic/mod-zero.rs2
-rw-r--r--tests/ui/numbers-arithmetic/overflowing-add.rs2
-rw-r--r--tests/ui/numbers-arithmetic/overflowing-mul.rs2
-rw-r--r--tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs2
-rw-r--r--tests/ui/numbers-arithmetic/overflowing-pow-signed.rs2
-rw-r--r--tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs2
-rw-r--r--tests/ui/numbers-arithmetic/overflowing-sub.rs2
-rw-r--r--tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs6
-rw-r--r--tests/ui/panic-runtime/abort.rs6
-rw-r--r--tests/ui/panic-runtime/lto-abort.rs6
-rw-r--r--tests/ui/panic-runtime/lto-unwind.rs7
-rw-r--r--tests/ui/panic-runtime/unwind-interleaved.rs2
-rw-r--r--tests/ui/panic-runtime/unwind-rec.rs2
-rw-r--r--tests/ui/panic-runtime/unwind-rec2.rs2
-rw-r--r--tests/ui/panic-runtime/unwind-unique.rs2
-rw-r--r--tests/ui/panics/abort-on-panic.rs3
-rw-r--r--tests/ui/panics/args-panic.rs2
-rw-r--r--tests/ui/panics/doublepanic.rs2
-rw-r--r--tests/ui/panics/explicit-panic-msg.rs2
-rw-r--r--tests/ui/panics/explicit-panic.rs2
-rw-r--r--tests/ui/panics/fmt-panic.rs2
-rw-r--r--tests/ui/panics/issue-47429-short-backtraces.rs6
-rw-r--r--tests/ui/panics/main-panic.rs2
-rw-r--r--tests/ui/panics/panic-arg.rs2
-rw-r--r--tests/ui/panics/panic-handler-chain-update-hook.rs2
-rw-r--r--tests/ui/panics/panic-handler-flail-wildly.rs2
-rw-r--r--tests/ui/panics/panic-handler-set-twice.rs2
-rw-r--r--tests/ui/panics/panic-in-dtor-drops-fields.rs2
-rw-r--r--tests/ui/panics/panic-macro-any-wrapped.rs2
-rw-r--r--tests/ui/panics/panic-macro-any.rs2
-rw-r--r--tests/ui/panics/panic-macro-explicit.rs2
-rw-r--r--tests/ui/panics/panic-macro-fmt.rs2
-rw-r--r--tests/ui/panics/panic-macro-owned.rs2
-rw-r--r--tests/ui/panics/panic-macro-static.rs2
-rw-r--r--tests/ui/panics/panic-main.rs2
-rw-r--r--tests/ui/panics/panic-parens.rs2
-rw-r--r--tests/ui/panics/panic-recover-propagate.rs2
-rw-r--r--tests/ui/panics/panic-set-handler.rs2
-rw-r--r--tests/ui/panics/panic-set-unset-handler.rs2
-rw-r--r--tests/ui/panics/panic-take-handler-nop.rs2
-rw-r--r--tests/ui/panics/panic.rs2
-rw-r--r--tests/ui/panics/result-get-panic.rs2
-rw-r--r--tests/ui/panics/runtime-switch.rs4
-rw-r--r--tests/ui/panics/test-panic.rs2
-rw-r--r--tests/ui/panics/test-should-panic-no-message.rs2
-rw-r--r--tests/ui/panics/while-body-panics.rs2
-rw-r--r--tests/ui/panics/while-panic.rs2
-rw-r--r--tests/ui/print-stdout-eprint-stderr.rs3
-rw-r--r--tests/ui/process/core-run-destroy.rs5
-rw-r--r--tests/ui/process/env-args-reverse-iterator.rs3
-rw-r--r--tests/ui/process/fds-are-cloexec.rs5
-rw-r--r--tests/ui/process/inherit-env.rs3
-rw-r--r--tests/ui/process/issue-13304.rs6
-rw-r--r--tests/ui/process/issue-14456.rs6
-rw-r--r--tests/ui/process/issue-14940.rs3
-rw-r--r--tests/ui/process/issue-16272.rs3
-rw-r--r--tests/ui/process/issue-20091.rs6
-rw-r--r--tests/ui/process/issue-30490.rs3
-rw-r--r--tests/ui/process/multi-panic.rs3
-rw-r--r--tests/ui/process/no-stdio.rs3
-rw-r--r--tests/ui/process/println-with-broken-pipe.rs2
-rw-r--r--tests/ui/process/process-envs.rs3
-rw-r--r--tests/ui/process/process-exit.rs6
-rw-r--r--tests/ui/process/process-panic-after-fork.rs5
-rw-r--r--tests/ui/process/process-remove-from-env.rs3
-rw-r--r--tests/ui/process/process-sigpipe.rs2
-rw-r--r--tests/ui/process/process-spawn-nonexistent.rs3
-rw-r--r--tests/ui/process/process-spawn-with-unicode-params.rs3
-rw-r--r--tests/ui/process/process-status-inherits-stdin.rs3
-rw-r--r--tests/ui/process/signal-exit-status.rs5
-rw-r--r--tests/ui/process/sigpipe-should-be-ignored.rs7
-rw-r--r--tests/ui/process/tls-exit-status.rs2
-rw-r--r--tests/ui/process/try-wait.rs6
-rw-r--r--tests/ui/reachable/issue-948.rs2
-rw-r--r--tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs2
-rw-r--r--tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs2
-rw-r--r--tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs2
-rw-r--r--tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs2
-rw-r--r--tests/ui/runtime/atomic-print.rs4
-rw-r--r--tests/ui/runtime/backtrace-debuginfo.rs3
-rw-r--r--tests/ui/runtime/out-of-stack.rs3
-rw-r--r--tests/ui/runtime/rt-explody-panic-payloads.rs3
-rw-r--r--tests/ui/runtime/running-with-no-runtime.rs3
-rw-r--r--tests/ui/sepcomp/sepcomp-unwind.rs2
-rw-r--r--tests/ui/simd/target-feature-mixup.rs3
-rw-r--r--tests/ui/std/thread-sleep-ms.rs11
-rw-r--r--tests/ui/stdio-is-blocking.rs3
-rw-r--r--tests/ui/str/str-overrun.rs2
-rw-r--r--tests/ui/structs-enums/unit-like-struct-drop-run.rs2
-rw-r--r--tests/ui/structs/rhs-type.rs2
-rw-r--r--tests/ui/suggestions/import-visible-path-39175.fixed16
-rw-r--r--tests/ui/suggestions/import-visible-path-39175.rs (renamed from tests/ui/issues/issue-39175.rs)7
-rw-r--r--tests/ui/suggestions/import-visible-path-39175.stderr (renamed from tests/ui/issues/issue-39175.stderr)6
-rw-r--r--tests/ui/test-attrs/terse.rs2
-rw-r--r--tests/ui/test-attrs/test-panic-abort-disabled.rs5
-rw-r--r--tests/ui/test-attrs/test-panic-abort-nocapture.rs6
-rw-r--r--tests/ui/test-attrs/test-panic-abort.rs6
-rw-r--r--tests/ui/test-attrs/test-thread-capture.rs2
-rw-r--r--tests/ui/test-attrs/test-thread-nocapture.rs2
-rw-r--r--tests/ui/threads-sendsync/eprint-on-tls-drop.rs2
-rw-r--r--tests/ui/threads-sendsync/issue-24313.rs2
-rw-r--r--tests/ui/wait-forked-but-failed-child.rs3
233 files changed, 595 insertions, 608 deletions
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
index 5428d776f41..460a4664615 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs
@@ -51,7 +51,7 @@ pub(crate) fn prepare_covfun_record<'tcx>(
     is_used: bool,
 ) -> Option<CovfunRecord<'tcx>> {
     let fn_cov_info = tcx.instance_mir(instance.def).function_coverage_info.as_deref()?;
-    let ids_info = tcx.coverage_ids_info(instance.def);
+    let ids_info = tcx.coverage_ids_info(instance.def)?;
 
     let expressions = prepare_expressions(fn_cov_info, ids_info, is_used);
 
diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
index 7311cd9d230..021108cd51c 100644
--- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs
@@ -8,7 +8,6 @@ use rustc_codegen_ssa::traits::{
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_middle::mir::coverage::CoverageKind;
 use rustc_middle::ty::Instance;
-use rustc_middle::ty::layout::HasTyCtxt;
 use tracing::{debug, instrument};
 
 use crate::builder::Builder;
@@ -147,6 +146,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
             debug!("function has a coverage statement but no coverage info");
             return;
         };
+        let Some(ids_info) = bx.tcx.coverage_ids_info(instance.def) else {
+            debug!("function has a coverage statement but no IDs info");
+            return;
+        };
 
         // Mark the instance as used in this CGU, for coverage purposes.
         // This includes functions that were not partitioned into this CGU,
@@ -162,8 +165,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
                 // be smaller than the number originally inserted by the instrumentor,
                 // if some high-numbered counters were removed by MIR optimizations.
                 // If so, LLVM's profiler runtime will use fewer physical counters.
-                let num_counters =
-                    bx.tcx().coverage_ids_info(instance.def).num_counters_after_mir_opts();
+                let num_counters = ids_info.num_counters_after_mir_opts();
                 assert!(
                     num_counters as usize <= function_coverage_info.num_counters,
                     "num_counters disagreement: query says {num_counters} but function info only has {}",
diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs
index 65f51ae9d39..46534697e1d 100644
--- a/compiler/rustc_middle/src/mir/coverage.rs
+++ b/compiler/rustc_middle/src/mir/coverage.rs
@@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter};
 
 use rustc_index::IndexVec;
 use rustc_index::bit_set::DenseBitSet;
-use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
+use rustc_macros::{HashStable, TyDecodable, TyEncodable};
 use rustc_span::Span;
 
 rustc_index::newtype_index! {
@@ -72,7 +72,7 @@ impl ConditionId {
 /// Enum that can hold a constant zero value, the ID of an physical coverage
 /// counter, or the ID of a coverage-counter expression.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub enum CovTerm {
     Zero,
     Counter(CounterId),
@@ -89,7 +89,7 @@ impl Debug for CovTerm {
     }
 }
 
-#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
 pub enum CoverageKind {
     /// Marks a span that might otherwise not be represented in MIR, so that
     /// coverage instrumentation can associate it with its enclosing block/BCB.
@@ -151,7 +151,7 @@ impl Debug for CoverageKind {
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
-#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable)]
 pub enum Op {
     Subtract,
     Add,
@@ -168,7 +168,7 @@ impl Op {
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub struct Expression {
     pub lhs: CovTerm,
     pub op: Op,
@@ -176,7 +176,7 @@ pub struct Expression {
 }
 
 #[derive(Clone, Debug)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub enum MappingKind {
     /// Associates a normal region of code with a counter/expression/zero.
     Code(CovTerm),
@@ -208,7 +208,7 @@ impl MappingKind {
 }
 
 #[derive(Clone, Debug)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub struct Mapping {
     pub kind: MappingKind,
     pub span: Span,
@@ -218,7 +218,7 @@ pub struct Mapping {
 /// to be used in conjunction with the individual coverage statements injected
 /// into the function's basic blocks.
 #[derive(Clone, Debug)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub struct FunctionCoverageInfo {
     pub function_source_hash: u64,
     pub body_span: Span,
@@ -238,7 +238,7 @@ pub struct FunctionCoverageInfo {
 /// ("Hi" indicates that this is "high-level" information collected at the
 /// THIR/MIR boundary, before the MIR-based coverage instrumentation pass.)
 #[derive(Clone, Debug)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub struct CoverageInfoHi {
     /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was
     /// injected into the MIR body. This makes it possible to allocate per-ID
@@ -252,7 +252,7 @@ pub struct CoverageInfoHi {
 }
 
 #[derive(Clone, Debug)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub struct BranchSpan {
     pub span: Span,
     pub true_marker: BlockMarkerId,
@@ -260,7 +260,7 @@ pub struct BranchSpan {
 }
 
 #[derive(Copy, Clone, Debug)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub struct ConditionInfo {
     pub condition_id: ConditionId,
     pub true_next_id: Option<ConditionId>,
@@ -268,7 +268,7 @@ pub struct ConditionInfo {
 }
 
 #[derive(Clone, Debug)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub struct MCDCBranchSpan {
     pub span: Span,
     pub condition_info: ConditionInfo,
@@ -277,14 +277,14 @@ pub struct MCDCBranchSpan {
 }
 
 #[derive(Copy, Clone, Debug)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub struct DecisionInfo {
     pub bitmap_idx: u32,
     pub num_conditions: u16,
 }
 
 #[derive(Clone, Debug)]
-#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(TyEncodable, TyDecodable, Hash, HashStable)]
 pub struct MCDCDecisionSpan {
     pub span: Span,
     pub end_markers: Vec<BlockMarkerId>,
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index bbb8bdce4a0..0f3fca434ee 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -358,6 +358,8 @@ pub struct Body<'tcx> {
     ///
     /// Only present if coverage is enabled and this function is eligible.
     /// Boxed to limit space overhead in non-coverage builds.
+    #[type_foldable(identity)]
+    #[type_visitable(ignore)]
     pub coverage_info_hi: Option<Box<coverage::CoverageInfoHi>>,
 
     /// Per-function coverage information added by the `InstrumentCoverage`
@@ -366,6 +368,8 @@ pub struct Body<'tcx> {
     ///
     /// If `-Cinstrument-coverage` is not active, or if an individual function
     /// is not eligible for coverage, then this should always be `None`.
+    #[type_foldable(identity)]
+    #[type_visitable(ignore)]
     pub function_coverage_info: Option<Box<coverage::FunctionCoverageInfo>>,
 }
 
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 0c17a2e0fe5..29ae2e1bd6b 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -417,7 +417,14 @@ pub enum StatementKind<'tcx> {
     ///
     /// Interpreters and codegen backends that don't support coverage instrumentation
     /// can usually treat this as a no-op.
-    Coverage(CoverageKind),
+    Coverage(
+        // Coverage statements are unlikely to ever contain type information in
+        // the foreseeable future, so excluding them from TypeFoldable/TypeVisitable
+        // avoids some unhelpful derive boilerplate.
+        #[type_foldable(identity)]
+        #[type_visitable(ignore)]
+        CoverageKind,
+    ),
 
     /// Denotes a call to an intrinsic that does not require an unwind path and always returns.
     /// This avoids adding a new block and a terminator for simple intrinsics.
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 05ded71dbeb..17e1fe35bba 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -618,7 +618,9 @@ rustc_queries! {
     /// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass
     /// (for compiler option `-Cinstrument-coverage`), after MIR optimizations
     /// have had a chance to potentially remove some of them.
-    query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> &'tcx mir::coverage::CoverageIdsInfo {
+    ///
+    /// Returns `None` for functions that were not instrumented.
+    query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> Option<&'tcx mir::coverage::CoverageIdsInfo> {
         desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) }
         arena_cache
     }
diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs
index 8d397f63cc7..50ebde3292e 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters.rs
@@ -11,7 +11,9 @@ use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId,
 
 use crate::coverage::counters::balanced_flow::BalancedFlowGraph;
 use crate::coverage::counters::iter_nodes::IterNodes;
-use crate::coverage::counters::node_flow::{CounterTerm, MergedNodeFlowGraph, NodeCounters};
+use crate::coverage::counters::node_flow::{
+    CounterTerm, NodeCounters, make_node_counters, node_flow_data_for_balanced_graph,
+};
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
 
 mod balanced_flow;
@@ -27,12 +29,12 @@ pub(super) fn make_bcb_counters(
 ) -> CoverageCounters {
     // Create the derived graphs that are necessary for subsequent steps.
     let balanced_graph = BalancedFlowGraph::for_graph(graph, |n| !graph[n].is_out_summable);
-    let merged_graph = MergedNodeFlowGraph::for_balanced_graph(&balanced_graph);
+    let node_flow_data = node_flow_data_for_balanced_graph(&balanced_graph);
 
     // Use those graphs to determine which nodes get physical counters, and how
     // to compute the execution counts of other nodes from those counters.
-    let nodes = make_node_counter_priority_list(graph, balanced_graph);
-    let node_counters = merged_graph.make_node_counters(&nodes);
+    let priority_list = make_node_flow_priority_list(graph, balanced_graph);
+    let node_counters = make_node_counters(&node_flow_data, &priority_list);
 
     // Convert the counters into a form suitable for embedding into MIR.
     transcribe_counters(&node_counters, bcb_needs_counter)
@@ -40,7 +42,7 @@ pub(super) fn make_bcb_counters(
 
 /// Arranges the nodes in `balanced_graph` into a list, such that earlier nodes
 /// take priority in being given a counter expression instead of a physical counter.
-fn make_node_counter_priority_list(
+fn make_node_flow_priority_list(
     graph: &CoverageGraph,
     balanced_graph: BalancedFlowGraph<&CoverageGraph>,
 ) -> Vec<BasicCoverageBlock> {
@@ -81,11 +83,11 @@ fn transcribe_counters(
     let mut new = CoverageCounters::with_num_bcbs(bcb_needs_counter.domain_size());
 
     for bcb in bcb_needs_counter.iter() {
-        // Our counter-creation algorithm doesn't guarantee that a counter
-        // expression starts or ends with a positive term, so partition the
+        // Our counter-creation algorithm doesn't guarantee that a node's list
+        // of terms starts or ends with a positive term, so partition the
         // counters into "positive" and "negative" lists for easier handling.
         let (mut pos, mut neg): (Vec<_>, Vec<_>) =
-            old.counter_expr(bcb).iter().partition_map(|&CounterTerm { node, op }| match op {
+            old.counter_terms[bcb].iter().partition_map(|&CounterTerm { node, op }| match op {
                 Op::Add => Either::Left(node),
                 Op::Subtract => Either::Right(node),
             });
diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs
index 610498c6c0e..3647c889937 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs
@@ -8,18 +8,17 @@
 
 use rustc_data_structures::graph;
 use rustc_index::bit_set::DenseBitSet;
-use rustc_index::{Idx, IndexVec};
+use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_middle::mir::coverage::Op;
-use smallvec::SmallVec;
 
 use crate::coverage::counters::iter_nodes::IterNodes;
-use crate::coverage::counters::union_find::{FrozenUnionFind, UnionFind};
+use crate::coverage::counters::union_find::UnionFind;
 
 #[cfg(test)]
 mod tests;
 
-/// View of some underlying graph, in which each node's successors have been
-/// merged into a single "supernode".
+/// Data representing a view of some underlying graph, in which each node's
+/// successors have been merged into a single "supernode".
 ///
 /// The resulting supernodes have no obvious meaning on their own.
 /// However, merging successor nodes means that a node's out-edges can all
@@ -30,10 +29,10 @@ mod tests;
 /// in the merged graph, it becomes possible to analyze the original node flows
 /// using techniques for analyzing edge flows.
 #[derive(Debug)]
-pub(crate) struct MergedNodeFlowGraph<Node: Idx> {
+pub(crate) struct NodeFlowData<Node: Idx> {
     /// Maps each node to the supernode that contains it, indicated by some
     /// arbitrary "root" node that is part of that supernode.
-    supernodes: FrozenUnionFind<Node>,
+    supernodes: IndexVec<Node, Node>,
     /// For each node, stores the single supernode that all of its successors
     /// have been merged into.
     ///
@@ -42,84 +41,71 @@ pub(crate) struct MergedNodeFlowGraph<Node: Idx> {
     succ_supernodes: IndexVec<Node, Node>,
 }
 
-impl<Node: Idx> MergedNodeFlowGraph<Node> {
-    /// Creates a "merged" view of an underlying graph.
-    ///
-    /// The given graph is assumed to have [“balanced flow”](balanced-flow),
-    /// though it does not necessarily have to be a `BalancedFlowGraph`.
-    ///
-    /// [balanced-flow]: `crate::coverage::counters::balanced_flow::BalancedFlowGraph`.
-    pub(crate) fn for_balanced_graph<G>(graph: G) -> Self
-    where
-        G: graph::DirectedGraph<Node = Node> + graph::Successors,
-    {
-        let mut supernodes = UnionFind::<G::Node>::new(graph.num_nodes());
-
-        // For each node, merge its successors into a single supernode, and
-        // arbitrarily choose one of those successors to represent all of them.
-        let successors = graph
-            .iter_nodes()
-            .map(|node| {
-                graph
-                    .successors(node)
-                    .reduce(|a, b| supernodes.unify(a, b))
-                    .expect("each node in a balanced graph must have at least one out-edge")
-            })
-            .collect::<IndexVec<G::Node, G::Node>>();
-
-        // Now that unification is complete, freeze the supernode forest,
-        // and resolve each arbitrarily-chosen successor to its canonical root.
-        // (This avoids having to explicitly resolve them later.)
-        let supernodes = supernodes.freeze();
-        let succ_supernodes = successors.into_iter().map(|succ| supernodes.find(succ)).collect();
-
-        Self { supernodes, succ_supernodes }
-    }
-
-    fn num_nodes(&self) -> usize {
-        self.succ_supernodes.len()
-    }
+/// Creates a "merged" view of an underlying graph.
+///
+/// The given graph is assumed to have [“balanced flow”](balanced-flow),
+/// though it does not necessarily have to be a `BalancedFlowGraph`.
+///
+/// [balanced-flow]: `crate::coverage::counters::balanced_flow::BalancedFlowGraph`.
+pub(crate) fn node_flow_data_for_balanced_graph<G>(graph: G) -> NodeFlowData<G::Node>
+where
+    G: graph::Successors,
+{
+    let mut supernodes = UnionFind::<G::Node>::new(graph.num_nodes());
+
+    // For each node, merge its successors into a single supernode, and
+    // arbitrarily choose one of those successors to represent all of them.
+    let successors = graph
+        .iter_nodes()
+        .map(|node| {
+            graph
+                .successors(node)
+                .reduce(|a, b| supernodes.unify(a, b))
+                .expect("each node in a balanced graph must have at least one out-edge")
+        })
+        .collect::<IndexVec<G::Node, G::Node>>();
+
+    // Now that unification is complete, take a snapshot of the supernode forest,
+    // and resolve each arbitrarily-chosen successor to its canonical root.
+    // (This avoids having to explicitly resolve them later.)
+    let supernodes = supernodes.snapshot();
+    let succ_supernodes = successors.into_iter().map(|succ| supernodes[succ]).collect();
+
+    NodeFlowData { supernodes, succ_supernodes }
+}
 
-    fn is_supernode(&self, node: Node) -> bool {
-        self.supernodes.find(node) == node
+/// Uses the graph information in `node_flow_data`, together with a given
+/// permutation of all nodes in the graph, to create physical counters and
+/// counter expressions for each node in the underlying graph.
+///
+/// The given list must contain exactly one copy of each node in the
+/// underlying balanced-flow graph. The order of nodes is used as a hint to
+/// influence counter allocation:
+/// - Earlier nodes are more likely to receive counter expressions.
+/// - Later nodes are more likely to receive physical counters.
+pub(crate) fn make_node_counters<Node: Idx>(
+    node_flow_data: &NodeFlowData<Node>,
+    priority_list: &[Node],
+) -> NodeCounters<Node> {
+    let mut builder = SpantreeBuilder::new(node_flow_data);
+
+    for &node in priority_list {
+        builder.visit_node(node);
     }
 
-    /// Using the information in this merged graph, together with a given
-    /// permutation of all nodes in the graph, to create physical counters and
-    /// counter expressions for each node in the underlying graph.
-    ///
-    /// The given list must contain exactly one copy of each node in the
-    /// underlying balanced-flow graph. The order of nodes is used as a hint to
-    /// influence counter allocation:
-    /// - Earlier nodes are more likely to receive counter expressions.
-    /// - Later nodes are more likely to receive physical counters.
-    pub(crate) fn make_node_counters(&self, all_nodes_permutation: &[Node]) -> NodeCounters<Node> {
-        let mut builder = SpantreeBuilder::new(self);
-
-        for &node in all_nodes_permutation {
-            builder.visit_node(node);
-        }
-
-        NodeCounters { counter_exprs: builder.finish() }
-    }
+    NodeCounters { counter_terms: builder.finish() }
 }
 
 /// End result of allocating physical counters and counter expressions for the
 /// nodes of a graph.
 #[derive(Debug)]
 pub(crate) struct NodeCounters<Node: Idx> {
-    counter_exprs: IndexVec<Node, CounterExprVec<Node>>,
-}
-
-impl<Node: Idx> NodeCounters<Node> {
     /// For the given node, returns the finished list of terms that represent
     /// its physical counter or counter expression. Always non-empty.
     ///
-    /// If a node was given a physical counter, its "expression" will contain
+    /// If a node was given a physical counter, the term list will contain
     /// that counter as its sole element.
-    pub(crate) fn counter_expr(&self, this: Node) -> &[CounterTerm<Node>] {
-        self.counter_exprs[this].as_slice()
-    }
+    pub(crate) counter_terms: IndexVec<Node, Vec<CounterTerm<Node>>>,
 }
 
 #[derive(Debug)]
@@ -146,12 +132,11 @@ pub(crate) struct CounterTerm<Node> {
     pub(crate) node: Node,
 }
 
-/// Stores the list of counter terms that make up a node's counter expression.
-type CounterExprVec<Node> = SmallVec<[CounterTerm<Node>; 2]>;
-
 #[derive(Debug)]
 struct SpantreeBuilder<'a, Node: Idx> {
-    graph: &'a MergedNodeFlowGraph<Node>,
+    supernodes: &'a IndexSlice<Node, Node>,
+    succ_supernodes: &'a IndexSlice<Node, Node>,
+
     is_unvisited: DenseBitSet<Node>,
     /// Links supernodes to each other, gradually forming a spanning tree of
     /// the merged-flow graph.
@@ -163,26 +148,32 @@ struct SpantreeBuilder<'a, Node: Idx> {
     yank_buffer: Vec<Node>,
     /// An in-progress counter expression for each node. Each expression is
     /// initially empty, and will be filled in as relevant nodes are visited.
-    counter_exprs: IndexVec<Node, CounterExprVec<Node>>,
+    counter_terms: IndexVec<Node, Vec<CounterTerm<Node>>>,
 }
 
 impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
-    fn new(graph: &'a MergedNodeFlowGraph<Node>) -> Self {
-        let num_nodes = graph.num_nodes();
+    fn new(node_flow_data: &'a NodeFlowData<Node>) -> Self {
+        let NodeFlowData { supernodes, succ_supernodes } = node_flow_data;
+        let num_nodes = supernodes.len();
         Self {
-            graph,
+            supernodes,
+            succ_supernodes,
             is_unvisited: DenseBitSet::new_filled(num_nodes),
             span_edges: IndexVec::from_fn_n(|_| None, num_nodes),
             yank_buffer: vec![],
-            counter_exprs: IndexVec::from_fn_n(|_| SmallVec::new(), num_nodes),
+            counter_terms: IndexVec::from_fn_n(|_| vec![], num_nodes),
         }
     }
 
+    fn is_supernode(&self, node: Node) -> bool {
+        self.supernodes[node] == node
+    }
+
     /// Given a supernode, finds the supernode that is the "root" of its
     /// spantree component. Two nodes that have the same spantree root are
     /// connected in the spantree.
     fn spantree_root(&self, this: Node) -> Node {
-        debug_assert!(self.graph.is_supernode(this));
+        debug_assert!(self.is_supernode(this));
 
         match self.span_edges[this] {
             None => this,
@@ -193,7 +184,7 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
     /// Rotates edges in the spantree so that `this` is the root of its
     /// spantree component.
     fn yank_to_spantree_root(&mut self, this: Node) {
-        debug_assert!(self.graph.is_supernode(this));
+        debug_assert!(self.is_supernode(this));
 
         // The rotation is done iteratively, by first traversing from `this` to
         // its root and storing the path in a buffer, and then traversing the
@@ -235,12 +226,12 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
 
         // Get the supernode containing `this`, and make it the root of its
         // component of the spantree.
-        let this_supernode = self.graph.supernodes.find(this);
+        let this_supernode = self.supernodes[this];
         self.yank_to_spantree_root(this_supernode);
 
         // Get the supernode containing all of this's successors.
-        let succ_supernode = self.graph.succ_supernodes[this];
-        debug_assert!(self.graph.is_supernode(succ_supernode));
+        let succ_supernode = self.succ_supernodes[this];
+        debug_assert!(self.is_supernode(succ_supernode));
 
         // If two supernodes are already connected in the spantree, they will
         // have the same spantree root. (Each supernode is connected to itself.)
@@ -268,8 +259,8 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
             // `this_supernode`.
 
             // Instead of setting `this.measure = true` as in the original paper,
-            // we just add the node's ID to its own "expression".
-            self.counter_exprs[this].push(CounterTerm { node: this, op: Op::Add });
+            // we just add the node's ID to its own list of terms.
+            self.counter_terms[this].push(CounterTerm { node: this, op: Op::Add });
 
             // Walk the spantree from `this.successor` back to `this`. For each
             // spantree edge along the way, add this node's physical counter to
@@ -279,7 +270,7 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
                 let &SpantreeEdge { is_reversed, claiming_node, span_parent } =
                     self.span_edges[curr].as_ref().unwrap();
                 let op = if is_reversed { Op::Subtract } else { Op::Add };
-                self.counter_exprs[claiming_node].push(CounterTerm { node: this, op });
+                self.counter_terms[claiming_node].push(CounterTerm { node: this, op });
 
                 curr = span_parent;
             }
@@ -288,19 +279,20 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> {
 
     /// Asserts that all nodes have been visited, and returns the computed
     /// counter expressions (made up of physical counters) for each node.
-    fn finish(self) -> IndexVec<Node, CounterExprVec<Node>> {
-        let Self { graph, is_unvisited, span_edges, yank_buffer: _, counter_exprs } = self;
+    fn finish(self) -> IndexVec<Node, Vec<CounterTerm<Node>>> {
+        let Self { ref span_edges, ref is_unvisited, ref counter_terms, .. } = self;
         assert!(is_unvisited.is_empty(), "some nodes were never visited: {is_unvisited:?}");
         debug_assert!(
             span_edges
                 .iter_enumerated()
-                .all(|(node, span_edge)| { span_edge.is_some() <= graph.is_supernode(node) }),
+                .all(|(node, span_edge)| { span_edge.is_some() <= self.is_supernode(node) }),
             "only supernodes can have a span edge",
         );
         debug_assert!(
-            counter_exprs.iter().all(|expr| !expr.is_empty()),
-            "after visiting all nodes, every node should have a non-empty expression",
+            counter_terms.iter().all(|terms| !terms.is_empty()),
+            "after visiting all nodes, every node should have at least one term",
         );
-        counter_exprs
+
+        self.counter_terms
     }
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs
index 9e7f754523d..b509a14514b 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs
@@ -4,10 +4,12 @@ use rustc_data_structures::graph::vec_graph::VecGraph;
 use rustc_index::Idx;
 use rustc_middle::mir::coverage::Op;
 
-use super::{CounterTerm, MergedNodeFlowGraph, NodeCounters};
+use crate::coverage::counters::node_flow::{
+    CounterTerm, NodeCounters, NodeFlowData, make_node_counters, node_flow_data_for_balanced_graph,
+};
 
-fn merged_node_flow_graph<G: graph::Successors>(graph: G) -> MergedNodeFlowGraph<G::Node> {
-    MergedNodeFlowGraph::for_balanced_graph(graph)
+fn node_flow_data<G: graph::Successors>(graph: G) -> NodeFlowData<G::Node> {
+    node_flow_data_for_balanced_graph(graph)
 }
 
 fn make_graph<Node: Idx + Ord>(num_nodes: usize, edge_pairs: Vec<(Node, Node)>) -> VecGraph<Node> {
@@ -30,8 +32,8 @@ fn example_driver() {
         (4, 0),
     ]);
 
-    let merged = merged_node_flow_graph(&graph);
-    let counters = merged.make_node_counters(&[3, 1, 2, 0, 4]);
+    let node_flow_data = node_flow_data(&graph);
+    let counters = make_node_counters(&node_flow_data, &[3, 1, 2, 0, 4]);
 
     assert_eq!(format_counter_expressions(&counters), &[
         // (comment to force vertical formatting for clarity)
@@ -53,12 +55,12 @@ fn format_counter_expressions<Node: Idx>(counters: &NodeCounters<Node>) -> Vec<S
     };
 
     counters
-        .counter_exprs
+        .counter_terms
         .indices()
         .map(|node| {
-            let mut expr = counters.counter_expr(node).iter().collect::<Vec<_>>();
-            expr.sort_by_key(|item| item.node.index());
-            format!("[{node:?}]: {}", expr.into_iter().map(format_item).join(" "))
+            let mut terms = counters.counter_terms[node].iter().collect::<Vec<_>>();
+            terms.sort_by_key(|item| item.node.index());
+            format!("[{node:?}]: {}", terms.into_iter().map(format_item).join(" "))
         })
         .collect()
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs b/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs
index 2da4f5f5fce..a826a953fa6 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs
@@ -88,29 +88,9 @@ impl<Key: Idx> UnionFind<Key> {
         a
     }
 
-    /// Creates a snapshot of this disjoint-set forest that can no longer be
-    /// mutated, but can be queried without mutation.
-    pub(crate) fn freeze(&mut self) -> FrozenUnionFind<Key> {
-        // Just resolve each key to its actual root.
-        let roots = self.table.indices().map(|key| self.find(key)).collect();
-        FrozenUnionFind { roots }
-    }
-}
-
-/// Snapshot of a disjoint-set forest that can no longer be mutated, but can be
-/// queried in O(1) time without mutation.
-///
-/// This is really just a wrapper around a direct mapping from keys to roots,
-/// but with a [`Self::find`] method that resembles [`UnionFind::find`].
-#[derive(Debug)]
-pub(crate) struct FrozenUnionFind<Key: Idx> {
-    roots: IndexVec<Key, Key>,
-}
-
-impl<Key: Idx> FrozenUnionFind<Key> {
-    /// Returns the "root" key of the disjoint-set containing the given key.
-    /// If two keys have the same root, they belong to the same set.
-    pub(crate) fn find(&self, key: Key) -> Key {
-        self.roots[key]
+    /// Takes a "snapshot" of the current state of this disjoint-set forest, in
+    /// the form of a vector that directly maps each key to its current root.
+    pub(crate) fn snapshot(&mut self) -> IndexVec<Key, Key> {
+        self.table.indices().map(|key| self.find(key)).collect()
     }
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 19568735df7..b8aa76a7dbe 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -180,7 +180,12 @@ fn create_mappings(
     ));
 
     for (decision, branches) in mcdc_mappings {
-        let num_conditions = branches.len() as u16;
+        // FIXME(#134497): Previously it was possible for some of these branch
+        // conversions to fail, in which case the remaining branches in the
+        // decision would be degraded to plain `MappingKind::Branch`.
+        // The changes in #134497 made that failure impossible, because the
+        // fallible step was deferred to codegen. But the corresponding code
+        // in codegen wasn't updated to detect the need for a degrade step.
         let conditions = branches
             .into_iter()
             .map(
@@ -206,24 +211,13 @@ fn create_mappings(
             )
             .collect::<Vec<_>>();
 
-        if conditions.len() == num_conditions as usize {
-            // LLVM requires end index for counter mapping regions.
-            let kind = MappingKind::MCDCDecision(DecisionInfo {
-                bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32,
-                num_conditions,
-            });
-            let span = decision.span;
-            mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter()));
-        } else {
-            mappings.extend(conditions.into_iter().map(|mapping| {
-                let MappingKind::MCDCBranch { true_term, false_term, mcdc_params: _ } =
-                    mapping.kind
-                else {
-                    unreachable!("all mappings here are MCDCBranch as shown above");
-                };
-                Mapping { kind: MappingKind::Branch { true_term, false_term }, span: mapping.span }
-            }))
-        }
+        // LLVM requires end index for counter mapping regions.
+        let kind = MappingKind::MCDCDecision(DecisionInfo {
+            bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32,
+            num_conditions: u16::try_from(conditions.len()).unwrap(),
+        });
+        let span = decision.span;
+        mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter()));
     }
 
     mappings
diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs
index 3e7cf8541c2..5e7b46182dc 100644
--- a/compiler/rustc_mir_transform/src/coverage/query.rs
+++ b/compiler/rustc_mir_transform/src/coverage/query.rs
@@ -87,15 +87,9 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
 fn coverage_ids_info<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance_def: ty::InstanceKind<'tcx>,
-) -> CoverageIdsInfo {
+) -> Option<CoverageIdsInfo> {
     let mir_body = tcx.instance_mir(instance_def);
-
-    let Some(fn_cov_info) = mir_body.function_coverage_info.as_deref() else {
-        return CoverageIdsInfo {
-            counters_seen: DenseBitSet::new_empty(0),
-            zero_expressions: DenseBitSet::new_empty(0),
-        };
-    };
+    let fn_cov_info = mir_body.function_coverage_info.as_deref()?;
 
     let mut counters_seen = DenseBitSet::new_empty(fn_cov_info.num_counters);
     let mut expressions_seen = DenseBitSet::new_filled(fn_cov_info.expressions.len());
@@ -129,7 +123,7 @@ fn coverage_ids_info<'tcx>(
     let zero_expressions =
         identify_zero_expressions(fn_cov_info, &counters_seen, &expressions_seen);
 
-    CoverageIdsInfo { counters_seen, zero_expressions }
+    Some(CoverageIdsInfo { counters_seen, zero_expressions })
 }
 
 fn all_coverage_in_mir_body<'a, 'tcx>(
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index cd2afd7a473..88bd3f414ea 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -2511,9 +2511,9 @@ impl<T, A: Allocator> Vec<T, A> {
         }
     }
 
-    /// Removes and returns the last element in a vector if the predicate
+    /// Removes and returns the last element from a vector if the predicate
     /// returns `true`, or [`None`] if the predicate returns false or the vector
-    /// is empty.
+    /// is empty (the predicate will not be called in that case).
     ///
     /// # Examples
     ///
@@ -2528,12 +2528,9 @@ impl<T, A: Allocator> Vec<T, A> {
     /// assert_eq!(vec.pop_if(pred), None);
     /// ```
     #[unstable(feature = "vec_pop_if", issue = "122741")]
-    pub fn pop_if<F>(&mut self, f: F) -> Option<T>
-    where
-        F: FnOnce(&mut T) -> bool,
-    {
+    pub fn pop_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option<T> {
         let last = self.last_mut()?;
-        if f(last) { self.pop() } else { None }
+        if predicate(last) { self.pop() } else { None }
     }
 
     /// Moves all the elements of `other` into `self`, leaving `other` empty.
@@ -3016,10 +3013,9 @@ impl<T: Clone, A: Allocator> Vec<T, A> {
     /// Iterates over the slice `other`, clones each element, and then appends
     /// it to this `Vec`. The `other` slice is traversed in-order.
     ///
-    /// Note that this function is same as [`extend`] except that it is
-    /// specialized to work with slices instead. If and when Rust gets
-    /// specialization this function will likely be deprecated (but still
-    /// available).
+    /// Note that this function is the same as [`extend`],
+    /// except that it also works with slice elements that are Clone but not Copy.
+    /// If Rust gets specialization this function may be deprecated.
     ///
     /// # Examples
     ///
diff --git a/src/doc/rustc-dev-guide/src/conventions.md b/src/doc/rustc-dev-guide/src/conventions.md
index 37af8121cd1..4010e90821f 100644
--- a/src/doc/rustc-dev-guide/src/conventions.md
+++ b/src/doc/rustc-dev-guide/src/conventions.md
@@ -1,4 +1,4 @@
-This file offers some tips on the coding conventions for rustc.  This
+This file offers some tips on the coding conventions for rustc. This
 chapter covers [formatting](#formatting), [coding for correctness](#cc),
 [using crates from crates.io](#cio), and some tips on
 [structuring your PR for easy review](#er).
@@ -25,6 +25,7 @@ pass the <!-- date-check: nov 2022 --> `--edition=2021` argument yourself when c
 `rustfmt` directly.
 
 [fmt]: https://github.com/rust-dev-tools/fmt-rfcs
+
 [`rustfmt`]:https://github.com/rust-lang/rustfmt
 
 ## Formatting C++ code
@@ -40,6 +41,26 @@ When modifying that code, use this command to format it:
 This uses a pinned version of `clang-format`, to avoid relying on the local
 environment.
 
+## Formatting and linting Python code
+
+The Rust repository contains quite a lof of Python code. We try to keep
+it both linted and formatted by the [ruff][ruff] tool.
+
+When modifying Python code, use this command to format it:
+```sh
+./x test tidy --extra-checks=py:fmt --bless
+```
+
+and the following command to run lints:
+```sh
+./x test tidy --extra-checks=py:lint
+```
+
+This uses a pinned version of `ruff`, to avoid relying on the local
+environment.
+
+[ruff]: https://github.com/astral-sh/ruff
+
 <a id="copyright"></a>
 
 <!-- REUSE-IgnoreStart -->
@@ -84,7 +105,7 @@ Using `_` in a match is convenient, but it means that when new
 variants are added to the enum, they may not get handled correctly.
 Ask yourself: if a new variant were added to this enum, what's the
 chance that it would want to use the `_` code, versus having some
-other treatment?  Unless the answer is "low", then prefer an
+other treatment? Unless the answer is "low", then prefer an
 exhaustive match. (The same advice applies to `if let` and `while
 let`, which are effectively tests for a single variant.)
 
@@ -124,7 +145,7 @@ See the [crates.io dependencies][crates] section.
 # How to structure your PR
 
 How you prepare the commits in your PR can make a big difference for the
-reviewer.  Here are some tips.
+reviewer. Here are some tips.
 
 **Isolate "pure refactorings" into their own commit.** For example, if
 you rename a method, then put that rename into its own commit, along
@@ -165,4 +186,5 @@ to the compiler.
   crate-related, often the spelling is changed to `krate`.
 
 [tcx]: ./ty.md
+
 [crates]: ./crates-io.md
diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md
index 426a6ff1da5..7366e55d314 100644
--- a/src/doc/rustc-dev-guide/src/tests/directives.md
+++ b/src/doc/rustc-dev-guide/src/tests/directives.md
@@ -94,7 +94,7 @@ for more details.
 | Directive                         | Explanation                                                                                                              | Supported test suites                        | Possible values                                                                         |
 |-----------------------------------|--------------------------------------------------------------------------------------------------------------------------|----------------------------------------------|-----------------------------------------------------------------------------------------|
 | `check-run-results`               | Check run test binary `run-{pass,fail}` output snapshot                                                                  | `ui`, `crashes`, `incremental` if `run-pass` | N/A                                                                                     |
-| `error-pattern`                   | Check that output contains a specific string                                                                             | `ui`, `crashes`, `incremental` if `run-pass` | String                                                                                   |
+| `error-pattern`                   | Check that output contains a specific string                                                                             | `ui`, `crashes`, `incremental` if `run-pass` | String                                                                                  |
 | `regex-error-pattern`             | Check that output contains a regex pattern                                                                               | `ui`, `crashes`, `incremental` if `run-pass` | Regex                                                                                   |
 | `check-stdout`                    | Check `stdout` against `error-pattern`s from running test binary[^check_stdout]                                          | `ui`, `crashes`, `incremental`               | N/A                                                                                     |
 | `normalize-stderr-32bit`          | Normalize actual stderr (for 32-bit platforms) with a rule `"<raw>" -> "<normalized>"` before comparing against snapshot | `ui`, `incremental`                          | `"<RAW>" -> "<NORMALIZED>"`, `<RAW>`/`<NORMALIZED>` is regex capture and replace syntax |
@@ -176,6 +176,7 @@ settings:
 - `needs-rust-lld` — ignores if the rust lld support is not enabled (`rust.lld =
   true` in `config.toml`)
 - `needs-threads` — ignores if the target does not have threading support
+- `needs-subprocess`  — ignores if the target does not have subprocess support
 - `needs-symlink` — ignores if the target does not support symlinks. This can be
   the case on Windows if the developer did not enable privileged symlink
   permissions.
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index c6f3d7c0d10..00821fc9f9d 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -488,6 +488,17 @@ impl Config {
             git_merge_commit_email: &self.git_merge_commit_email,
         }
     }
+
+    pub fn has_subprocess_support(&self) -> bool {
+        // FIXME(#135928): compiletest is always a **host** tool. Building and running an
+        // capability detection executable against the **target** is not trivial. The short term
+        // solution here is to hard-code some targets to allow/deny, unfortunately.
+
+        let unsupported_target = self.target_cfg().env == "sgx"
+            || matches!(self.target_cfg().arch.as_str(), "wasm32" | "wasm64")
+            || self.target_cfg().os == "emscripten";
+        !unsupported_target
+    }
 }
 
 /// Known widths of `target_has_atomic`.
diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs
index 5784cd83119..acdb3cbdd45 100644
--- a/src/tools/compiletest/src/directive-list.rs
+++ b/src/tools/compiletest/src/directive-list.rs
@@ -152,6 +152,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "needs-sanitizer-support",
     "needs-sanitizer-thread",
     "needs-std-debug-assertions",
+    "needs-subprocess",
     "needs-symlink",
     "needs-target-has-atomic",
     "needs-threads",
diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs
index e19dcd992fc..12f0790fb10 100644
--- a/src/tools/compiletest/src/header/needs.rs
+++ b/src/tools/compiletest/src/header/needs.rs
@@ -95,6 +95,11 @@ pub(super) fn handle_needs(
             ignore_reason: "ignored on targets without threading support",
         },
         Need {
+            name: "needs-subprocess",
+            condition: config.has_subprocess_support(),
+            ignore_reason: "ignored on targets without subprocess support",
+        },
+        Need {
             name: "needs-unwind",
             condition: config.can_unwind(),
             ignore_reason: "ignored on targets without unwinding support",
@@ -351,6 +356,9 @@ fn find_dlltool(config: &Config) -> bool {
     dlltool_found
 }
 
+// FIXME(#135928): this is actually not quite right because this detection is run on the **host**.
+// This however still helps the case of windows -> windows local development in case symlinks are
+// not available.
 #[cfg(windows)]
 fn has_symlinks() -> bool {
     if std::env::var_os("CI").is_some() {
diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs
index 6269d91c7ec..2a4cff1d12e 100644
--- a/src/tools/tidy/src/ext_tool_checks.rs
+++ b/src/tools/tidy/src/ext_tool_checks.rs
@@ -89,74 +89,45 @@ fn check_impl(
 
     if python_lint {
         eprintln!("linting python files");
-        let mut cfg_args_ruff = cfg_args.clone();
-        let mut file_args_ruff = file_args.clone();
-
-        let mut cfg_path = root_path.to_owned();
-        cfg_path.extend(RUFF_CONFIG_PATH);
-        let mut cache_dir = outdir.to_owned();
-        cache_dir.extend(RUFF_CACHE_PATH);
-
-        cfg_args_ruff.extend([
-            "--config".as_ref(),
-            cfg_path.as_os_str(),
-            "--cache-dir".as_ref(),
-            cache_dir.as_os_str(),
-        ]);
-
-        if file_args_ruff.is_empty() {
-            file_args_ruff.push(root_path.as_os_str());
-        }
-
-        let mut args = merge_args(&cfg_args_ruff, &file_args_ruff);
-        args.insert(0, "check".as_ref());
-        let res = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
+        let py_path = py_path.as_ref().unwrap();
+        let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &["check".as_ref()]);
 
         if res.is_err() && show_diff {
             eprintln!("\npython linting failed! Printing diff suggestions:");
 
-            args.insert(1, "--diff".as_ref());
-            let _ = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
+            let _ = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &[
+                "check".as_ref(),
+                "--diff".as_ref(),
+            ]);
         }
         // Rethrow error
         let _ = res?;
     }
 
     if python_fmt {
-        let mut cfg_args_ruff = cfg_args.clone();
-        let mut file_args_ruff = file_args.clone();
-
+        let mut args: Vec<&OsStr> = vec!["format".as_ref()];
         if bless {
             eprintln!("formatting python files");
         } else {
             eprintln!("checking python file formatting");
-            cfg_args_ruff.push("--check".as_ref());
-        }
-
-        let mut cfg_path = root_path.to_owned();
-        cfg_path.extend(RUFF_CONFIG_PATH);
-        let mut cache_dir = outdir.to_owned();
-        cache_dir.extend(RUFF_CACHE_PATH);
-
-        cfg_args_ruff.extend(["--config".as_ref(), cfg_path.as_os_str()]);
-
-        if file_args_ruff.is_empty() {
-            file_args_ruff.push(root_path.as_os_str());
+            args.push("--check".as_ref());
         }
 
-        let mut args = merge_args(&cfg_args_ruff, &file_args_ruff);
-        args.insert(0, "format".as_ref());
-        let res = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
+        let py_path = py_path.as_ref().unwrap();
+        let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &args);
 
-        if res.is_err() && show_diff {
-            eprintln!("\npython formatting does not match! Printing diff:");
-
-            args.insert(0, "--diff".as_ref());
-            let _ = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
-        }
         if res.is_err() && !bless {
+            if show_diff {
+                eprintln!("\npython formatting does not match! Printing diff:");
+
+                let _ = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &[
+                    "format".as_ref(),
+                    "--diff".as_ref(),
+                ]);
+            }
             eprintln!("rerun tidy with `--extra-checks=py:fmt --bless` to reformat Python code");
         }
+
         // Rethrow error
         let _ = res?;
     }
@@ -247,6 +218,38 @@ fn check_impl(
     Ok(())
 }
 
+fn run_ruff(
+    root_path: &Path,
+    outdir: &Path,
+    py_path: &Path,
+    cfg_args: &[&OsStr],
+    file_args: &[&OsStr],
+    ruff_args: &[&OsStr],
+) -> Result<(), Error> {
+    let mut cfg_args_ruff = cfg_args.into_iter().copied().collect::<Vec<_>>();
+    let mut file_args_ruff = file_args.into_iter().copied().collect::<Vec<_>>();
+
+    let mut cfg_path = root_path.to_owned();
+    cfg_path.extend(RUFF_CONFIG_PATH);
+    let mut cache_dir = outdir.to_owned();
+    cache_dir.extend(RUFF_CACHE_PATH);
+
+    cfg_args_ruff.extend([
+        "--config".as_ref(),
+        cfg_path.as_os_str(),
+        "--cache-dir".as_ref(),
+        cache_dir.as_os_str(),
+    ]);
+
+    if file_args_ruff.is_empty() {
+        file_args_ruff.push(root_path.as_os_str());
+    }
+
+    let mut args: Vec<&OsStr> = ruff_args.into_iter().copied().collect();
+    args.extend(merge_args(&cfg_args_ruff, &file_args_ruff));
+    py_runner(py_path, true, None, "ruff", &args)
+}
+
 /// Helper to create `cfg1 cfg2 -- file1 file2` output
 fn merge_args<'a>(cfg_args: &[&'a OsStr], file_args: &[&'a OsStr]) -> Vec<&'a OsStr> {
     let mut args = cfg_args.to_owned();
@@ -321,8 +324,16 @@ fn get_or_create_venv(venv_path: &Path, src_reqs_path: &Path) -> Result<PathBuf,
 fn create_venv_at_path(path: &Path) -> Result<(), Error> {
     /// Preferred python versions in order. Newest to oldest then current
     /// development versions
-    const TRY_PY: &[&str] =
-        &["python3.11", "python3.10", "python3.9", "python3", "python", "python3.12", "python3.13"];
+    const TRY_PY: &[&str] = &[
+        "python3.13",
+        "python3.12",
+        "python3.11",
+        "python3.10",
+        "python3.9",
+        "python3",
+        "python",
+        "python3.14",
+    ];
 
     let mut sys_py = None;
     let mut found = Vec::new();
@@ -357,22 +368,40 @@ fn create_venv_at_path(path: &Path) -> Result<(), Error> {
         return Err(ret);
     };
 
-    eprintln!("creating virtual environment at '{}' using '{sys_py}'", path.display());
-    let out = Command::new(sys_py).args(["-m", "virtualenv"]).arg(path).output().unwrap();
+    // First try venv, which should be packaged in the Python3 standard library.
+    // If it is not available, try to create the virtual environment using the
+    // virtualenv package.
+    if try_create_venv(sys_py, path, "venv").is_ok() {
+        return Ok(());
+    }
+    try_create_venv(sys_py, path, "virtualenv")
+}
+
+fn try_create_venv(python: &str, path: &Path, module: &str) -> Result<(), Error> {
+    eprintln!(
+        "creating virtual environment at '{}' using '{python}' and '{module}'",
+        path.display()
+    );
+    let out = Command::new(python).args(["-m", module]).arg(path).output().unwrap();
 
     if out.status.success() {
         return Ok(());
     }
 
     let stderr = String::from_utf8_lossy(&out.stderr);
-    let err = if stderr.contains("No module named virtualenv") {
+    let err = if stderr.contains(&format!("No module named {module}")) {
         Error::Generic(format!(
-            "virtualenv not found: you may need to install it \
-                               (`{sys_py} -m pip install virtualenv`)"
+            r#"{module} not found: you may need to install it:
+`{python} -m pip install {module}`
+If you see an error about "externally managed environment" when running the above command,
+either install `{module}` using your system package manager
+(e.g. `sudo apt-get install {python}-{module}`) or create a virtual environment manually, install
+`{module}` in it and then activate it before running tidy.
+"#
         ))
     } else {
         Error::Generic(format!(
-            "failed to create venv at '{}' using {sys_py}: {stderr}",
+            "failed to create venv at '{}' using {python} -m {module}: {stderr}",
             path.display()
         ))
     };
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 5878cce5ffb..0e5a7458b68 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -2204,7 +2204,6 @@ ui/issues/issue-3895.rs
 ui/issues/issue-38954.rs
 ui/issues/issue-38987.rs
 ui/issues/issue-39089.rs
-ui/issues/issue-39175.rs
 ui/issues/issue-39211.rs
 ui/issues/issue-39367.rs
 ui/issues/issue-39548.rs
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 63ac447a772..5ef07911429 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -17,7 +17,7 @@ use ignore::Walk;
 const ENTRY_LIMIT: u32 = 901;
 // FIXME: The following limits should be reduced eventually.
 
-const ISSUES_ENTRY_LIMIT: u32 = 1664;
+const ISSUES_ENTRY_LIMIT: u32 = 1662;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
     "rs",     // test source files
diff --git a/tests/assembly/powerpc64-struct-abi.rs b/tests/assembly/powerpc64-struct-abi.rs
index 7052937acf6..db08a514819 100644
--- a/tests/assembly/powerpc64-struct-abi.rs
+++ b/tests/assembly/powerpc64-struct-abi.rs
@@ -50,9 +50,9 @@ struct ThreeU8s(u8, u8, u8);
 
 // CHECK-LABEL: read_large
 // aix: lwz [[REG1:.*]], 16(4)
-// aix-NEXT: lxvd2x 0, 0, 4
+// aix-NEXT: lxv{{d2x|w4x}} 0, 0, 4
 // aix-NEXT: stw [[REG1]], 16(3)
-// aix-NEXT: stxvd2x 0, 0, 3
+// aix-NEXT: stxv{{d2x|w4x}} 0, 0, 3
 // be: lwz [[REG1:.*]], 16(4)
 // be-NEXT: stw [[REG1]], 16(3)
 // be-NEXT: ld [[REG2:.*]], 8(4)
@@ -118,8 +118,8 @@ extern "C" fn read_small(x: &ThreeU8s) -> ThreeU8s {
 // aix-NEXT: std 4, 56(1)
 // aix-NEXT: stw [[REG1]], 16(6)
 // aix-NEXT: addi [[REG2:.*]], 1, 48
-// aix-NEXT: lxvd2x 0, 0, [[REG2]]
-// aix-NEXT: stxvd2x 0, 0, 6
+// aix-NEXT: lxv{{d2x|w4x}} 0, 0, [[REG2]]
+// aix-NEXT: stxv{{d2x|w4x}} 0, 0, 6
 // elf: std 3, 0(6)
 // be-NEXT: rldicl [[REG1:.*]], 5, 32, 32
 // elf-NEXT: std 4, 8(6)
diff --git a/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff
index 148ff86354b..a91d88984a8 100644
--- a/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff
+++ b/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff
@@ -4,8 +4,8 @@
   fn bar() -> bool {
       let mut _0: bool;
   
-+     coverage body span: $DIR/instrument_coverage.rs:19:18: 21:2 (#0)
-+     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:19:1: 21:2 (#0);
++     coverage body span: $DIR/instrument_coverage.rs:29:18: 31:2 (#0)
++     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:29:1: 31:2 (#0);
 + 
       bb0: {
 +         Coverage::CounterIncrement(0);
diff --git a/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff
index fa09cf0b83f..d7ea442518e 100644
--- a/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff
+++ b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff
@@ -7,13 +7,13 @@
       let mut _2: bool;
       let mut _3: !;
   
-+     coverage body span: $DIR/instrument_coverage.rs:10:11: 16:2 (#0)
++     coverage body span: $DIR/instrument_coverage.rs:14:11: 20:2 (#0)
 +     coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Subtract, rhs: Counter(0) };
-+     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:10:1: 10:11 (#0);
-+     coverage Code(Counter(1)) => $DIR/instrument_coverage.rs:12:12: 12:17 (#0);
-+     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:13:13: 13:18 (#0);
-+     coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:14:10: 14:10 (#0);
-+     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:16:2: 16:2 (#0);
++     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:14:1: 14:11 (#0);
++     coverage Code(Counter(1)) => $DIR/instrument_coverage.rs:16:12: 16:17 (#0);
++     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:17:13: 17:18 (#0);
++     coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:18:10: 18:10 (#0);
++     coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:20:2: 20:2 (#0);
 + 
       bb0: {
 +         Coverage::CounterIncrement(0);
diff --git a/tests/mir-opt/coverage/instrument_coverage.rs b/tests/mir-opt/coverage/instrument_coverage.rs
index beb88b607f9..c49786f9615 100644
--- a/tests/mir-opt/coverage/instrument_coverage.rs
+++ b/tests/mir-opt/coverage/instrument_coverage.rs
@@ -6,7 +6,11 @@
 //@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime
 
 // EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff
-// EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff
+// CHECK-LABEL: fn main()
+// CHECK: coverage body span:
+// CHECK: coverage Code(Counter({{[0-9]+}})) =>
+// CHECK: bb0:
+// CHECK: Coverage::CounterIncrement
 fn main() {
     loop {
         if bar() {
@@ -15,14 +19,13 @@ fn main() {
     }
 }
 
+// EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff
+// CHECK-LABEL: fn bar()
+// CHECK: coverage body span:
+// CHECK: coverage Code(Counter({{[0-9]+}})) =>
+// CHECK: bb0:
+// CHECK: Coverage::CounterIncrement
 #[inline(never)]
 fn bar() -> bool {
     true
 }
-
-// CHECK:     coverage ExpressionId({{[0-9]+}}) =>
-// CHECK-DAG: coverage Code(Counter({{[0-9]+}})) =>
-// CHECK-DAG: coverage Code(Expression({{[0-9]+}})) =>
-// CHECK:     bb0:
-// CHECK-DAG: Coverage::ExpressionUsed({{[0-9]+}})
-// CHECK-DAG: Coverage::CounterIncrement({{[0-9]+}})
diff --git a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs
index 4afb710b193..22b9b029a40 100644
--- a/tests/ui/abi/homogenous-floats-target-feature-mixup.rs
+++ b/tests/ui/abi/homogenous-floats-target-feature-mixup.rs
@@ -5,8 +5,7 @@
 // without #[repr(simd)]
 
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 #![feature(avx512_target_feature)]
 
diff --git a/tests/ui/abi/segfault-no-out-of-stack.rs b/tests/ui/abi/segfault-no-out-of-stack.rs
index 113c82c30e9..b5af13ebfb5 100644
--- a/tests/ui/abi/segfault-no-out-of-stack.rs
+++ b/tests/ui/abi/segfault-no-out-of-stack.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 can't run commands
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-fuchsia must translate zircon signal to SIGSEGV/SIGBUS, FIXME (#58590)
 
 #![feature(rustc_private)]
diff --git a/tests/ui/abi/stack-probes-lto.rs b/tests/ui/abi/stack-probes-lto.rs
index e6c26c5c4de..c6e5bea5f42 100644
--- a/tests/ui/abi/stack-probes-lto.rs
+++ b/tests/ui/abi/stack-probes-lto.rs
@@ -3,7 +3,7 @@
 //@[aarch64] only-aarch64
 //@[x32] only-x86
 //@[x64] only-x86_64
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-musl FIXME #31506
 //@ ignore-fuchsia no exception handler registered for segfault
 //@ compile-flags: -C lto
diff --git a/tests/ui/abi/stack-probes.rs b/tests/ui/abi/stack-probes.rs
index 1c0e50250d7..f0fbd80d2e7 100644
--- a/tests/ui/abi/stack-probes.rs
+++ b/tests/ui/abi/stack-probes.rs
@@ -3,8 +3,7 @@
 //@[aarch64] only-aarch64
 //@[x32] only-x86
 //@[x64] only-x86_64
-//@ ignore-emscripten no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-fuchsia no exception handler registered for segfault
 //@ ignore-nto Crash analysis impossible at SIGSEGV in QNX Neutrino
 //@ ignore-ios Stack probes are enabled, but the SIGSEGV handler isn't
diff --git a/tests/ui/alloc-error/default-alloc-error-hook.rs b/tests/ui/alloc-error/default-alloc-error-hook.rs
index 5f977460b8c..7fbc66ca5f4 100644
--- a/tests/ui/alloc-error/default-alloc-error-hook.rs
+++ b/tests/ui/alloc-error/default-alloc-error-hook.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::alloc::{Layout, handle_alloc_error};
 use std::env;
diff --git a/tests/ui/array-slice-vec/bounds-check-no-overflow.rs b/tests/ui/array-slice-vec/bounds-check-no-overflow.rs
index 4614df44084..c5ff805a853 100644
--- a/tests/ui/array-slice-vec/bounds-check-no-overflow.rs
+++ b/tests/ui/array-slice-vec/bounds-check-no-overflow.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:index out of bounds
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::mem::size_of;
 
diff --git a/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs b/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs
index d64df4f7e4d..c7c05946c4c 100644
--- a/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs
+++ b/tests/ui/array-slice-vec/box-of-array-of-drop-1.rs
@@ -1,12 +1,12 @@
 //@ run-pass
 //@ needs-unwind
+//@ needs-threads
+
 #![allow(overflowing_literals)]
 
 // Test that we cleanup a fixed size Box<[D; k]> properly when D has a
 // destructor.
 
-//@ ignore-emscripten no threads support
-
 use std::thread;
 use std::sync::atomic::{AtomicUsize, Ordering};
 
diff --git a/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs b/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs
index 5ca3d60ad1d..98175a26ec0 100644
--- a/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs
+++ b/tests/ui/array-slice-vec/box-of-array-of-drop-2.rs
@@ -1,12 +1,12 @@
 //@ run-pass
 //@ needs-unwind
+//@ needs-threads
+
 #![allow(overflowing_literals)]
 
 // Test that we cleanup dynamic sized Box<[D]> properly when D has a
 // destructor.
 
-//@ ignore-emscripten no threads support
-
 use std::thread;
 use std::sync::atomic::{AtomicUsize, Ordering};
 
diff --git a/tests/ui/array-slice-vec/dst-raw-slice.rs b/tests/ui/array-slice-vec/dst-raw-slice.rs
index f1281f4e302..ab9dedc139d 100644
--- a/tests/ui/array-slice-vec/dst-raw-slice.rs
+++ b/tests/ui/array-slice-vec/dst-raw-slice.rs
@@ -2,7 +2,7 @@
 
 //@ run-fail
 //@ error-pattern:index out of bounds
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #[allow(unconditional_panic)]
 fn main() {
diff --git a/tests/ui/array-slice-vec/nested-vec-3.rs b/tests/ui/array-slice-vec/nested-vec-3.rs
index ce61401aab4..51975743742 100644
--- a/tests/ui/array-slice-vec/nested-vec-3.rs
+++ b/tests/ui/array-slice-vec/nested-vec-3.rs
@@ -1,8 +1,8 @@
 //@ run-pass
 //@ needs-unwind
-#![allow(overflowing_literals)]
+//@ needs-threads
 
-//@ ignore-emscripten no threads support
+#![allow(overflowing_literals)]
 
 // Test that using the `vec!` macro nested within itself works when
 // the contents implement Drop and we hit a panic in the middle of
diff --git a/tests/ui/array-slice-vec/slice-panic-1.rs b/tests/ui/array-slice-vec/slice-panic-1.rs
index d4f584c1632..a745dff96af 100644
--- a/tests/ui/array-slice-vec/slice-panic-1.rs
+++ b/tests/ui/array-slice-vec/slice-panic-1.rs
@@ -1,7 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 // Test that if a slicing expr[..] fails, the correct cleanups happen.
 
diff --git a/tests/ui/array-slice-vec/slice-panic-2.rs b/tests/ui/array-slice-vec/slice-panic-2.rs
index b3d1dc45573..483a4cbe245 100644
--- a/tests/ui/array-slice-vec/slice-panic-2.rs
+++ b/tests/ui/array-slice-vec/slice-panic-2.rs
@@ -1,7 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 // Test that if a slicing expr[..] fails, the correct cleanups happen.
 
diff --git a/tests/ui/array-slice-vec/vec-overrun.rs b/tests/ui/array-slice-vec/vec-overrun.rs
index 10f8350869f..3b3e9215279 100644
--- a/tests/ui/array-slice-vec/vec-overrun.rs
+++ b/tests/ui/array-slice-vec/vec-overrun.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:index out of bounds: the len is 1 but the index is 2
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let v: Vec<isize> = vec![10];
diff --git a/tests/ui/backtrace/backtrace.rs b/tests/ui/backtrace/backtrace.rs
index 2579ff5203b..487473f4393 100644
--- a/tests/ui/backtrace/backtrace.rs
+++ b/tests/ui/backtrace/backtrace.rs
@@ -1,8 +1,7 @@
 //@ run-pass
 //@ ignore-android FIXME #17520
-//@ ignore-wasm32 spawning processes is not supported
+//@ needs-subprocess
 //@ ignore-openbsd no support for libbacktrace without filename
-//@ ignore-sgx no processes
 //@ ignore-msvc see #62897 and `backtrace-debuginfo.rs` test
 //@ ignore-fuchsia Backtraces not symbolized
 //@ compile-flags:-g
diff --git a/tests/ui/backtrace/std-backtrace.rs b/tests/ui/backtrace/std-backtrace.rs
index 57d953a8640..7ccbd46152b 100644
--- a/tests/ui/backtrace/std-backtrace.rs
+++ b/tests/ui/backtrace/std-backtrace.rs
@@ -1,8 +1,7 @@
 //@ run-pass
 //@ ignore-android FIXME #17520
-//@ ignore-wasm32 spawning processes is not supported
+//@ needs-subprocess
 //@ ignore-openbsd no support for libbacktrace without filename
-//@ ignore-sgx no processes
 //@ ignore-fuchsia Backtraces not symbolized
 //@ compile-flags:-g
 //@ compile-flags:-Cstrip=none
diff --git a/tests/ui/binop/binop-fail-3.rs b/tests/ui/binop/binop-fail-3.rs
index b1e70a1c596..4e8d7e92ab6 100644
--- a/tests/ui/binop/binop-fail-3.rs
+++ b/tests/ui/binop/binop-fail-3.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:quux
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn foo() -> ! {
     panic!("quux");
diff --git a/tests/ui/binop/binop-panic.rs b/tests/ui/binop/binop-panic.rs
index 8dbf62a922e..8173eb0d689 100644
--- a/tests/ui/binop/binop-panic.rs
+++ b/tests/ui/binop/binop-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:quux
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn my_err(s: String) -> ! {
     println!("{}", s);
diff --git a/tests/ui/borrowck/borrowck-local-borrow.rs b/tests/ui/borrowck/borrowck-local-borrow.rs
index de6ee5983c8..4d22503e37b 100644
--- a/tests/ui/borrowck/borrowck-local-borrow.rs
+++ b/tests/ui/borrowck/borrowck-local-borrow.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:panic 1
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let x = 2;
diff --git a/tests/ui/borrowck/issue-28934.rs b/tests/ui/borrowck/issue-28934.rs
index a3ac663c5b5..64559d4cf1d 100644
--- a/tests/ui/borrowck/issue-28934.rs
+++ b/tests/ui/borrowck/issue-28934.rs
@@ -3,7 +3,7 @@
 
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 struct Parser<'i: 't, 't>(&'i u8, &'t u8);
 
diff --git a/tests/ui/box/unit/unwind-unique.rs b/tests/ui/box/unit/unwind-unique.rs
index 512327c9af4..1da55c45ee9 100644
--- a/tests/ui/box/unit/unwind-unique.rs
+++ b/tests/ui/box/unit/unwind-unique.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 use std::thread;
 
diff --git a/tests/ui/cleanup-rvalue-temp-during-incomplete-alloc.rs b/tests/ui/cleanup-rvalue-temp-during-incomplete-alloc.rs
index 80c5a8fe099..4c59df24e4b 100644
--- a/tests/ui/cleanup-rvalue-temp-during-incomplete-alloc.rs
+++ b/tests/ui/cleanup-rvalue-temp-during-incomplete-alloc.rs
@@ -20,7 +20,7 @@
 // It's unclear how likely such a bug is to recur, but it seems like a
 // scenario worth testing.
 
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 use std::thread;
 
diff --git a/tests/ui/closures/diverging-closure.rs b/tests/ui/closures/diverging-closure.rs
index dda829d8af4..2c86f55cf25 100644
--- a/tests/ui/closures/diverging-closure.rs
+++ b/tests/ui/closures/diverging-closure.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:oops
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let func = || -> ! {
diff --git a/tests/ui/command/command-argv0.rs b/tests/ui/command/command-argv0.rs
index 35625c0b334..0907e18b30c 100644
--- a/tests/ui/command/command-argv0.rs
+++ b/tests/ui/command/command-argv0.rs
@@ -1,8 +1,7 @@
 //@ run-pass
 
-//@ ignore-windows - this is a unix-specific test
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ only-unix (this is a unix-specific test)
+//@ needs-subprocess
 use std::env;
 use std::os::unix::process::CommandExt;
 use std::process::Command;
diff --git a/tests/ui/command/command-current-dir.rs b/tests/ui/command/command-current-dir.rs
index 23269e41231..e264cbe4d70 100644
--- a/tests/ui/command/command-current-dir.rs
+++ b/tests/ui/command/command-current-dir.rs
@@ -1,7 +1,6 @@
 //@ run-pass
 //@ no-prefer-dynamic We move the binary around, so do not depend dynamically on libstd
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-fuchsia Needs directory creation privilege
 
 use std::env;
diff --git a/tests/ui/command/command-exec.rs b/tests/ui/command/command-exec.rs
index d2545b0b472..77336377e88 100644
--- a/tests/ui/command/command-exec.rs
+++ b/tests/ui/command/command-exec.rs
@@ -1,13 +1,9 @@
 //@ run-pass
 
-#![allow(stable_features)]
-//@ ignore-windows - this is a unix-specific test
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ only-unix (this is a unix-specific test)
+//@ needs-subprocess
 //@ ignore-fuchsia no execvp syscall provided
 
-#![feature(process_exec)]
-
 use std::env;
 use std::os::unix::process::CommandExt;
 use std::process::Command;
diff --git a/tests/ui/command/command-pre-exec.rs b/tests/ui/command/command-pre-exec.rs
index 7242dea2775..7299f357bd0 100644
--- a/tests/ui/command/command-pre-exec.rs
+++ b/tests/ui/command/command-pre-exec.rs
@@ -1,11 +1,9 @@
 //@ run-pass
-
-#![allow(stable_features)]
-//@ ignore-windows - this is a unix-specific test
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ only-unix (this is a unix-specific test)
+//@ needs-subprocess
 //@ ignore-fuchsia no execvp syscall
-#![feature(process_exec, rustc_private)]
+
+#![feature(rustc_private)]
 
 extern crate libc;
 
diff --git a/tests/ui/command/command-setgroups.rs b/tests/ui/command/command-setgroups.rs
index c940135d844..047f06729af 100644
--- a/tests/ui/command/command-setgroups.rs
+++ b/tests/ui/command/command-setgroups.rs
@@ -1,9 +1,8 @@
 //@ run-pass
-//@ ignore-windows - this is a unix-specific test
-//@ ignore-wasm32
-//@ ignore-sgx
+//@ only-unix (this is a unix-specific test)
 //@ ignore-musl - returns dummy result for _SC_NGROUPS_MAX
 //@ ignore-nto - does not have `/bin/id`, expects groups to be i32 (not u32)
+//@ needs-subprocess
 
 #![feature(rustc_private)]
 #![feature(setgroups)]
diff --git a/tests/ui/command/command-uid-gid.rs b/tests/ui/command/command-uid-gid.rs
index 7a70a0fbd76..f54a0f50708 100644
--- a/tests/ui/command/command-uid-gid.rs
+++ b/tests/ui/command/command-uid-gid.rs
@@ -1,8 +1,7 @@
 //@ run-pass
 //@ ignore-android
-//@ ignore-emscripten
-//@ ignore-sgx
 //@ ignore-fuchsia no '/bin/sh', '/bin/ls'
+//@ needs-subprocess
 
 #![feature(rustc_private)]
 
diff --git a/tests/ui/command/issue-10626.rs b/tests/ui/command/issue-10626.rs
index f8dbb011513..d2679ec9e29 100644
--- a/tests/ui/command/issue-10626.rs
+++ b/tests/ui/command/issue-10626.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 // Make sure that if a process doesn't have its stdio/stderr descriptors set up
 // that we don't die in a large ball of fire
diff --git a/tests/ui/consts/issue-29798.rs b/tests/ui/consts/issue-29798.rs
index bdabbad6491..f7470d7aac9 100644
--- a/tests/ui/consts/issue-29798.rs
+++ b/tests/ui/consts/issue-29798.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:index out of bounds: the len is 5 but the index is 5
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 const fn test(x: usize) -> i32 {
     [42;5][x]
diff --git a/tests/ui/coroutine/coroutine-resume-after-panic.rs b/tests/ui/coroutine/coroutine-resume-after-panic.rs
index 2745ebc6132..1aa547c2a7b 100644
--- a/tests/ui/coroutine/coroutine-resume-after-panic.rs
+++ b/tests/ui/coroutine/coroutine-resume-after-panic.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ needs-unwind
 //@ error-pattern:coroutine resumed after panicking
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 // Test that we get the correct message for resuming a panicked coroutine.
 
diff --git a/tests/ui/drop/drop-trait-enum.rs b/tests/ui/drop/drop-trait-enum.rs
index 91b5bcdf730..5a88d959ec6 100644
--- a/tests/ui/drop/drop-trait-enum.rs
+++ b/tests/ui/drop/drop-trait-enum.rs
@@ -2,7 +2,7 @@
 #![allow(dead_code)]
 #![allow(unused_assignments)]
 #![allow(unused_variables)]
-//@ ignore-emscripten no threads support
+//@ needs-threads
 //@ needs-unwind
 
 use std::thread;
diff --git a/tests/ui/drop/terminate-in-initializer.rs b/tests/ui/drop/terminate-in-initializer.rs
index 23169aaf65b..24ec39fe096 100644
--- a/tests/ui/drop/terminate-in-initializer.rs
+++ b/tests/ui/drop/terminate-in-initializer.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 // Issue #787
 // Don't try to clean up uninitialized locals
diff --git a/tests/ui/expr/if/expr-if-panic-fn.rs b/tests/ui/expr/if/expr-if-panic-fn.rs
index 4f3d7fd48e3..0b4742d4a89 100644
--- a/tests/ui/expr/if/expr-if-panic-fn.rs
+++ b/tests/ui/expr/if/expr-if-panic-fn.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn f() -> ! {
     panic!()
diff --git a/tests/ui/expr/if/expr-if-panic.rs b/tests/ui/expr/if/expr-if-panic.rs
index 0b43d1d6b00..2f35878e844 100644
--- a/tests/ui/expr/if/expr-if-panic.rs
+++ b/tests/ui/expr/if/expr-if-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let _x = if false {
diff --git a/tests/ui/expr/if/if-check-panic.rs b/tests/ui/expr/if/if-check-panic.rs
index 4b400deaca4..eb0413f42fc 100644
--- a/tests/ui/expr/if/if-check-panic.rs
+++ b/tests/ui/expr/if/if-check-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:Number is odd
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn even(x: usize) -> bool {
     if x < 2 {
diff --git a/tests/ui/expr/if/if-cond-bot.rs b/tests/ui/expr/if/if-cond-bot.rs
index ddb5559ffca..56bd5ec35f9 100644
--- a/tests/ui/expr/if/if-cond-bot.rs
+++ b/tests/ui/expr/if/if-cond-bot.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:quux
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn my_err(s: String) -> ! {
     println!("{}", s);
diff --git a/tests/ui/extern/issue-18576.rs b/tests/ui/extern/issue-18576.rs
index 0a98e85e484..6b41fe73631 100644
--- a/tests/ui/extern/issue-18576.rs
+++ b/tests/ui/extern/issue-18576.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:stop
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 // #18576
 // Make sure that calling an extern function pointer in an unreachable
diff --git a/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs b/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs
index e9471d207da..1cd52b70315 100644
--- a/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs
+++ b/tests/ui/extern/issue-64655-allow-unwind-when-calling-panic-directly.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 // rust-lang/rust#64655: with panic=unwind, a panic from a subroutine
 // should still run destructors as it unwinds the stack. However,
diff --git a/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs b/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs
index 9486b5f1178..a44eb3828d0 100644
--- a/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs
+++ b/tests/ui/extern/issue-64655-extern-rust-must-allow-unwind.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 // rust-lang/rust#64655: with panic=unwind, a panic from a subroutine
 // should still run destructors as it unwinds the stack. However,
diff --git a/tests/ui/fn/expr-fn-panic.rs b/tests/ui/fn/expr-fn-panic.rs
index 23946b7533d..d726aac2a4c 100644
--- a/tests/ui/fn/expr-fn-panic.rs
+++ b/tests/ui/fn/expr-fn-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn f() -> ! {
     panic!()
diff --git a/tests/ui/hashmap/hashmap-capacity-overflow.rs b/tests/ui/hashmap/hashmap-capacity-overflow.rs
index 91aebc3bbba..502dbe9fa93 100644
--- a/tests/ui/hashmap/hashmap-capacity-overflow.rs
+++ b/tests/ui/hashmap/hashmap-capacity-overflow.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:capacity overflow
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::collections::hash_map::HashMap;
 use std::mem::size_of;
diff --git a/tests/ui/imports/glob-use-std.rs b/tests/ui/imports/glob-use-std.rs
index b625543da81..d2af816e24f 100644
--- a/tests/ui/imports/glob-use-std.rs
+++ b/tests/ui/imports/glob-use-std.rs
@@ -2,7 +2,7 @@
 
 //@ run-fail
 //@ error-pattern:panic works
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::*;
 
diff --git a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs
index 67b9832d601..346a94c37dd 100644
--- a/tests/ui/intrinsics/panic-uninitialized-zeroed.rs
+++ b/tests/ui/intrinsics/panic-uninitialized-zeroed.rs
@@ -1,11 +1,10 @@
+// ignore-tidy-linelength
+//! This test checks panic emitted from `mem::{uninitialized,zeroed}`.
 //@ run-pass
 //@ revisions: default strict
 //@ [strict]compile-flags: -Zstrict-init-checks
-// ignore-tidy-linelength
-//@ ignore-wasm32 spawning processes is not supported
-//@ ignore-sgx no processes
-//
-// This test checks panic emitted from `mem::{uninitialized,zeroed}`.
+//@ needs-subprocess
+
 #![allow(deprecated, invalid_value)]
 #![feature(never_type)]
 
diff --git a/tests/ui/issues/issue-12920.rs b/tests/ui/issues/issue-12920.rs
index 7f453e499d4..f3b1b643c45 100644
--- a/tests/ui/issues/issue-12920.rs
+++ b/tests/ui/issues/issue-12920.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 pub fn main() {
     panic!();
diff --git a/tests/ui/issues/issue-13202.rs b/tests/ui/issues/issue-13202.rs
index 89205fc7fd1..99ffba3fba5 100644
--- a/tests/ui/issues/issue-13202.rs
+++ b/tests/ui/issues/issue-13202.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:bad input
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     Some("foo").unwrap_or(panic!("bad input")).to_string();
diff --git a/tests/ui/issues/issue-20971.rs b/tests/ui/issues/issue-20971.rs
index 377a3d9ea30..31dd9101919 100644
--- a/tests/ui/issues/issue-20971.rs
+++ b/tests/ui/issues/issue-20971.rs
@@ -2,7 +2,7 @@
 
 //@ run-fail
 //@ error-pattern:Hello, world!
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 pub trait Parser {
     type Input;
diff --git a/tests/ui/issues/issue-2190-1.rs b/tests/ui/issues/issue-2190-1.rs
index 8db4a84aac8..e4e4bf9dbbe 100644
--- a/tests/ui/issues/issue-2190-1.rs
+++ b/tests/ui/issues/issue-2190-1.rs
@@ -1,20 +1,16 @@
-//@ run-pass
-#![allow(unused_must_use)]
-#![allow(non_upper_case_globals)]
-
-//@ ignore-emscripten no threads
+//@ check-pass
 
 use std::thread::Builder;
 
-static generations: usize = 1024+256+128+49;
+static GENERATIONS: usize = 1024+256+128+49;
 
 fn spawn(mut f: Box<dyn FnMut() + 'static + Send>) {
-    Builder::new().stack_size(32 * 1024).spawn(move|| f());
+    Builder::new().stack_size(32 * 1024).spawn(move || f());
 }
 
 fn child_no(x: usize) -> Box<dyn FnMut() + 'static + Send> {
-    Box::new(move|| {
-        if x < generations {
+    Box::new(move || {
+        if x < GENERATIONS {
             spawn(child_no(x+1));
         }
     })
diff --git a/tests/ui/issues/issue-23354-2.rs b/tests/ui/issues/issue-23354-2.rs
index 90de1276cc6..a296477215e 100644
--- a/tests/ui/issues/issue-23354-2.rs
+++ b/tests/ui/issues/issue-23354-2.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:panic evaluated
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #[allow(unused_variables)]
 fn main() {
diff --git a/tests/ui/issues/issue-23354.rs b/tests/ui/issues/issue-23354.rs
index 31783842dac..eff5f3d2714 100644
--- a/tests/ui/issues/issue-23354.rs
+++ b/tests/ui/issues/issue-23354.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:panic evaluated
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #[allow(unused_variables)]
 fn main() {
diff --git a/tests/ui/issues/issue-2470-bounds-check-overflow.rs b/tests/ui/issues/issue-2470-bounds-check-overflow.rs
index 241bc8fda9c..4f300454dcb 100644
--- a/tests/ui/issues/issue-2470-bounds-check-overflow.rs
+++ b/tests/ui/issues/issue-2470-bounds-check-overflow.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:index out of bounds
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::mem;
 
diff --git a/tests/ui/issues/issue-25089.rs b/tests/ui/issues/issue-25089.rs
index ea9ab290904..929738c3e79 100644
--- a/tests/ui/issues/issue-25089.rs
+++ b/tests/ui/issues/issue-25089.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 use std::thread;
 
diff --git a/tests/ui/issues/issue-26655.rs b/tests/ui/issues/issue-26655.rs
index 7f1858fdb7d..416472b0b26 100644
--- a/tests/ui/issues/issue-26655.rs
+++ b/tests/ui/issues/issue-26655.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 // Check that the destructors of simple enums are run on unwinding
 
diff --git a/tests/ui/issues/issue-2761.rs b/tests/ui/issues/issue-2761.rs
index b44a24e09f2..6df7a5118b8 100644
--- a/tests/ui/issues/issue-2761.rs
+++ b/tests/ui/issues/issue-2761.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:custom message
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     assert!(false, "custom message");
diff --git a/tests/ui/issues/issue-29485.rs b/tests/ui/issues/issue-29485.rs
index 56865d0e5db..a44bcd49c6a 100644
--- a/tests/ui/issues/issue-29485.rs
+++ b/tests/ui/issues/issue-29485.rs
@@ -2,7 +2,7 @@
 #![allow(unused_attributes)]
 //@ aux-build:issue-29485.rs
 //@ needs-unwind
-//@ ignore-emscripten no threads
+//@ needs-threads
 
 #[feature(recover)]
 
diff --git a/tests/ui/issues/issue-30018-panic.rs b/tests/ui/issues/issue-30018-panic.rs
index f5482d7c741..591848b6f7b 100644
--- a/tests/ui/issues/issue-30018-panic.rs
+++ b/tests/ui/issues/issue-30018-panic.rs
@@ -5,7 +5,7 @@
 // SIGTRAP injected by the drop-flag consistency checking.
 
 //@ needs-unwind
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 struct Foo;
 
diff --git a/tests/ui/issues/issue-3029.rs b/tests/ui/issues/issue-3029.rs
index a070578969c..22d0906ccf7 100644
--- a/tests/ui/issues/issue-3029.rs
+++ b/tests/ui/issues/issue-3029.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:so long
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #![allow(unreachable_code)]
 
diff --git a/tests/ui/issues/issue-30380.rs b/tests/ui/issues/issue-30380.rs
index 534bb3423d0..49fce3d150c 100644
--- a/tests/ui/issues/issue-30380.rs
+++ b/tests/ui/issues/issue-30380.rs
@@ -3,7 +3,7 @@
 
 //@ run-fail
 //@ error-pattern:panicking destructors ftw!
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 struct Observer<'a>(&'a mut FilledOnDrop);
 
diff --git a/tests/ui/issues/issue-33770.rs b/tests/ui/issues/issue-33770.rs
index 0fa91ac91c4..814e8f37176 100644
--- a/tests/ui/issues/issue-33770.rs
+++ b/tests/ui/issues/issue-33770.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::process::{Command, Stdio};
 use std::env;
diff --git a/tests/ui/issues/issue-38763.rs b/tests/ui/issues/issue-38763.rs
index 05cd648dcfe..87c758db172 100644
--- a/tests/ui/issues/issue-38763.rs
+++ b/tests/ui/issues/issue-38763.rs
@@ -1,5 +1,5 @@
 //@ run-pass
-//@ ignore-emscripten
+//@ needs-threads
 
 #[repr(C)]
 pub struct Foo(i128);
diff --git a/tests/ui/issues/issue-44216-add-system-time.rs b/tests/ui/issues/issue-44216-add-system-time.rs
index 207f72fade8..3838d28e33d 100644
--- a/tests/ui/issues/issue-44216-add-system-time.rs
+++ b/tests/ui/issues/issue-44216-add-system-time.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:overflow
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::time::{Duration, SystemTime};
 
diff --git a/tests/ui/issues/issue-44216-sub-instant.rs b/tests/ui/issues/issue-44216-sub-instant.rs
index 2457d2aaa04..19cd12e685f 100644
--- a/tests/ui/issues/issue-44216-sub-instant.rs
+++ b/tests/ui/issues/issue-44216-sub-instant.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:overflow
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::time::{Instant, Duration};
 
diff --git a/tests/ui/issues/issue-44216-sub-system-time.rs b/tests/ui/issues/issue-44216-sub-system-time.rs
index 7e33f227933..bd4f66f3e16 100644
--- a/tests/ui/issues/issue-44216-sub-system-time.rs
+++ b/tests/ui/issues/issue-44216-sub-system-time.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:overflow
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::time::{Duration, SystemTime};
 
diff --git a/tests/ui/lifetimes/tail-expr-lock-poisoning.rs b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs
index 6af6655149b..d7c7b2e2414 100644
--- a/tests/ui/lifetimes/tail-expr-lock-poisoning.rs
+++ b/tests/ui/lifetimes/tail-expr-lock-poisoning.rs
@@ -1,5 +1,6 @@
 //@ revisions: edition2021 edition2024
-//@ ignore-wasm no panic or subprocess support
+//@ ignore-wasm no panic support
+//@ needs-subprocess
 //@ [edition2024] edition: 2024
 //@ run-pass
 //@ needs-unwind
diff --git a/tests/ui/loops/for-each-loop-panic.rs b/tests/ui/loops/for-each-loop-panic.rs
index 04784cac8f2..79cfca93e0e 100644
--- a/tests/ui/loops/for-each-loop-panic.rs
+++ b/tests/ui/loops/for-each-loop-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:moop
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     for _ in 0_usize..10_usize {
diff --git a/tests/ui/macros/assert-as-macro.rs b/tests/ui/macros/assert-as-macro.rs
index 391b056292f..c18a3d89fb6 100644
--- a/tests/ui/macros/assert-as-macro.rs
+++ b/tests/ui/macros/assert-as-macro.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:assertion failed: 1 == 2
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     assert!(1 == 2);
diff --git a/tests/ui/macros/assert-eq-macro-msg.rs b/tests/ui/macros/assert-eq-macro-msg.rs
index 39eeefeeef9..9ef7552cc7b 100644
--- a/tests/ui/macros/assert-eq-macro-msg.rs
+++ b/tests/ui/macros/assert-eq-macro-msg.rs
@@ -2,7 +2,7 @@
 //@ error-pattern:assertion `left == right` failed: 1 + 1 definitely should be 3
 //@ error-pattern:  left: 2
 //@ error-pattern: right: 3
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     assert_eq!(1 + 1, 3, "1 + 1 definitely should be 3");
diff --git a/tests/ui/macros/assert-eq-macro-panic.rs b/tests/ui/macros/assert-eq-macro-panic.rs
index 22c3a8a634f..3c46e2798d1 100644
--- a/tests/ui/macros/assert-eq-macro-panic.rs
+++ b/tests/ui/macros/assert-eq-macro-panic.rs
@@ -2,7 +2,7 @@
 //@ error-pattern:assertion `left == right` failed
 //@ error-pattern:  left: 14
 //@ error-pattern: right: 15
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     assert_eq!(14, 15);
diff --git a/tests/ui/macros/assert-macro-explicit.rs b/tests/ui/macros/assert-macro-explicit.rs
index 167581d2525..b14a182a876 100644
--- a/tests/ui/macros/assert-macro-explicit.rs
+++ b/tests/ui/macros/assert-macro-explicit.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:assertion failed: false
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     assert!(false);
diff --git a/tests/ui/macros/assert-macro-fmt.rs b/tests/ui/macros/assert-macro-fmt.rs
index 47554430379..3fd0f472d16 100644
--- a/tests/ui/macros/assert-macro-fmt.rs
+++ b/tests/ui/macros/assert-macro-fmt.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern: panicked
 //@ error-pattern: test-assert-fmt 42 rust
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     assert!(false, "test-assert-fmt {} {}", 42, "rust");
diff --git a/tests/ui/macros/assert-macro-owned.rs b/tests/ui/macros/assert-macro-owned.rs
index 46a59db1390..f9b6f5f5180 100644
--- a/tests/ui/macros/assert-macro-owned.rs
+++ b/tests/ui/macros/assert-macro-owned.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:panicked
 //@ error-pattern:test-assert-owned
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #![allow(non_fmt_panics)]
 
diff --git a/tests/ui/macros/assert-macro-static.rs b/tests/ui/macros/assert-macro-static.rs
index 7d9e345d516..8c91d7722fc 100644
--- a/tests/ui/macros/assert-macro-static.rs
+++ b/tests/ui/macros/assert-macro-static.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:panicked
 //@ error-pattern:test-assert-static
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     assert!(false, "test-assert-static");
diff --git a/tests/ui/macros/assert-matches-macro-msg.rs b/tests/ui/macros/assert-matches-macro-msg.rs
index efa4121da92..956bc9cf0f4 100644
--- a/tests/ui/macros/assert-matches-macro-msg.rs
+++ b/tests/ui/macros/assert-matches-macro-msg.rs
@@ -2,7 +2,7 @@
 //@ error-pattern:assertion `left matches right` failed: 1 + 1 definitely should be 3
 //@ error-pattern:  left: 2
 //@ error-pattern: right: 3
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #![feature(assert_matches)]
 
diff --git a/tests/ui/macros/assert-ne-macro-msg.rs b/tests/ui/macros/assert-ne-macro-msg.rs
index 0a578e1baf9..24bc4dbea47 100644
--- a/tests/ui/macros/assert-ne-macro-msg.rs
+++ b/tests/ui/macros/assert-ne-macro-msg.rs
@@ -2,7 +2,7 @@
 //@ error-pattern:assertion `left != right` failed: 1 + 1 definitely should not be 2
 //@ error-pattern:  left: 2
 //@ error-pattern: right: 2
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     assert_ne!(1 + 1, 2, "1 + 1 definitely should not be 2");
diff --git a/tests/ui/macros/assert-ne-macro-panic.rs b/tests/ui/macros/assert-ne-macro-panic.rs
index 9cf5f05e9f1..c349825baf3 100644
--- a/tests/ui/macros/assert-ne-macro-panic.rs
+++ b/tests/ui/macros/assert-ne-macro-panic.rs
@@ -2,7 +2,7 @@
 //@ error-pattern:assertion `left != right` failed
 //@ error-pattern:  left: 14
 //@ error-pattern: right: 14
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     assert_ne!(14, 14);
diff --git a/tests/ui/macros/die-macro-2.rs b/tests/ui/macros/die-macro-2.rs
index e5456bdfca0..d802f189ce1 100644
--- a/tests/ui/macros/die-macro-2.rs
+++ b/tests/ui/macros/die-macro-2.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:test
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     panic!("test");
diff --git a/tests/ui/macros/die-macro-expr.rs b/tests/ui/macros/die-macro-expr.rs
index fb92dd66e3d..f4fefb0ca37 100644
--- a/tests/ui/macros/die-macro-expr.rs
+++ b/tests/ui/macros/die-macro-expr.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:test
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let __isize: isize = panic!("test");
diff --git a/tests/ui/macros/die-macro-pure.rs b/tests/ui/macros/die-macro-pure.rs
index 484eed3d720..d84787705a1 100644
--- a/tests/ui/macros/die-macro-pure.rs
+++ b/tests/ui/macros/die-macro-pure.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:test
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn f() {
     panic!("test");
diff --git a/tests/ui/macros/unimplemented-macro-panic.rs b/tests/ui/macros/unimplemented-macro-panic.rs
index d3bff8ca10b..804bd61270e 100644
--- a/tests/ui/macros/unimplemented-macro-panic.rs
+++ b/tests/ui/macros/unimplemented-macro-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:not implemented
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     unimplemented!()
diff --git a/tests/ui/macros/unreachable-arg.rs b/tests/ui/macros/unreachable-arg.rs
index 1f0d0073486..702bd053ab0 100644
--- a/tests/ui/macros/unreachable-arg.rs
+++ b/tests/ui/macros/unreachable-arg.rs
@@ -1,4 +1,4 @@
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 //@ revisions: edition_2015 edition_2021
 //@ [edition_2015]edition:2015
diff --git a/tests/ui/macros/unreachable-fmt-msg.rs b/tests/ui/macros/unreachable-fmt-msg.rs
index b16394a1920..928e0a427ae 100644
--- a/tests/ui/macros/unreachable-fmt-msg.rs
+++ b/tests/ui/macros/unreachable-fmt-msg.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:internal error: entered unreachable code: 6 is not prime
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     unreachable!("{} is not {}", 6u32, "prime");
diff --git a/tests/ui/macros/unreachable-format-arg.rs b/tests/ui/macros/unreachable-format-arg.rs
index 449c6bca16b..e1fdab25a43 100644
--- a/tests/ui/macros/unreachable-format-arg.rs
+++ b/tests/ui/macros/unreachable-format-arg.rs
@@ -1,5 +1,5 @@
 //@ run-fail
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 //@ revisions: edition_2015 edition_2021
 //@ [edition_2015]edition:2015
diff --git a/tests/ui/macros/unreachable-format-args.rs b/tests/ui/macros/unreachable-format-args.rs
index 5f8a0e9cdff..856fc992685 100644
--- a/tests/ui/macros/unreachable-format-args.rs
+++ b/tests/ui/macros/unreachable-format-args.rs
@@ -1,4 +1,4 @@
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 //@ revisions: edition_2015 edition_2021
 //@ [edition_2015]edition:2015
diff --git a/tests/ui/macros/unreachable-macro-panic.rs b/tests/ui/macros/unreachable-macro-panic.rs
index 7909bcb7624..c5cea5551cf 100644
--- a/tests/ui/macros/unreachable-macro-panic.rs
+++ b/tests/ui/macros/unreachable-macro-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:internal error: entered unreachable code
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     unreachable!()
diff --git a/tests/ui/macros/unreachable-static-msg.rs b/tests/ui/macros/unreachable-static-msg.rs
index 3e917897da4..a85f8962e7d 100644
--- a/tests/ui/macros/unreachable-static-msg.rs
+++ b/tests/ui/macros/unreachable-static-msg.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:internal error: entered unreachable code: uhoh
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     unreachable!("uhoh")
diff --git a/tests/ui/macros/unreachable.rs b/tests/ui/macros/unreachable.rs
index 7909bcb7624..c5cea5551cf 100644
--- a/tests/ui/macros/unreachable.rs
+++ b/tests/ui/macros/unreachable.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:internal error: entered unreachable code
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     unreachable!()
diff --git a/tests/ui/match/expr-match-panic-fn.rs b/tests/ui/match/expr-match-panic-fn.rs
index 82991d20df8..a07c2c6af8f 100644
--- a/tests/ui/match/expr-match-panic-fn.rs
+++ b/tests/ui/match/expr-match-panic-fn.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn f() -> ! {
     panic!()
diff --git a/tests/ui/match/expr-match-panic.rs b/tests/ui/match/expr-match-panic.rs
index e332ba83b91..d15b0a9cd72 100644
--- a/tests/ui/match/expr-match-panic.rs
+++ b/tests/ui/match/expr-match-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let _x = match true {
diff --git a/tests/ui/match/match-bot-panic.rs b/tests/ui/match/match-bot-panic.rs
index a155b5fb3f2..7ec07ae290d 100644
--- a/tests/ui/match/match-bot-panic.rs
+++ b/tests/ui/match/match-bot-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #![allow(unreachable_code)]
 #![allow(unused_variables)]
diff --git a/tests/ui/match/match-disc-bot.rs b/tests/ui/match/match-disc-bot.rs
index fdb98a0accb..d65dc5aea07 100644
--- a/tests/ui/match/match-disc-bot.rs
+++ b/tests/ui/match/match-disc-bot.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:quux
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn f() -> ! {
     panic!("quux")
diff --git a/tests/ui/match/match-wildcards.rs b/tests/ui/match/match-wildcards.rs
index 4fddee6666e..1cc81a7c415 100644
--- a/tests/ui/match/match-wildcards.rs
+++ b/tests/ui/match/match-wildcards.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:squirrelcupcake
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn cmp() -> isize {
     match (Some('a'), None::<char>) {
diff --git a/tests/ui/meta/revision-ok.rs b/tests/ui/meta/revision-ok.rs
index c1387f7d18e..430d3d0a239 100644
--- a/tests/ui/meta/revision-ok.rs
+++ b/tests/ui/meta/revision-ok.rs
@@ -5,7 +5,7 @@
 //@ revisions: foo bar
 //@[foo] error-pattern:foo
 //@[bar] error-pattern:bar
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #[cfg(foo)]
 fn die() {
diff --git a/tests/ui/mir/mir_codegen_calls_converging_drops.rs b/tests/ui/mir/mir_codegen_calls_converging_drops.rs
index 5c3c8b999b2..8a6d3926de2 100644
--- a/tests/ui/mir/mir_codegen_calls_converging_drops.rs
+++ b/tests/ui/mir/mir_codegen_calls_converging_drops.rs
@@ -2,7 +2,7 @@
 //@ error-pattern:converging_fn called
 //@ error-pattern:0 dropped
 //@ error-pattern:exit
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 struct Droppable(u8);
 impl Drop for Droppable {
diff --git a/tests/ui/mir/mir_codegen_calls_converging_drops_2.rs b/tests/ui/mir/mir_codegen_calls_converging_drops_2.rs
index e3cb9a96de0..e89fbc58a0d 100644
--- a/tests/ui/mir/mir_codegen_calls_converging_drops_2.rs
+++ b/tests/ui/mir/mir_codegen_calls_converging_drops_2.rs
@@ -2,7 +2,7 @@
 //@ error-pattern:complex called
 //@ error-pattern:dropped
 //@ error-pattern:exit
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 struct Droppable;
 impl Drop for Droppable {
diff --git a/tests/ui/mir/mir_codegen_calls_diverging.rs b/tests/ui/mir/mir_codegen_calls_diverging.rs
index c62527f01d3..ce2d7140b07 100644
--- a/tests/ui/mir/mir_codegen_calls_diverging.rs
+++ b/tests/ui/mir/mir_codegen_calls_diverging.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:diverging_fn called
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn diverging_fn() -> ! {
     panic!("diverging_fn called")
diff --git a/tests/ui/mir/mir_dynamic_drops_1.rs b/tests/ui/mir/mir_dynamic_drops_1.rs
index ffb8cc26100..0b0a3960574 100644
--- a/tests/ui/mir/mir_dynamic_drops_1.rs
+++ b/tests/ui/mir/mir_dynamic_drops_1.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:drop 1
 //@ error-pattern:drop 2
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 /// Structure which will not allow to be dropped twice.
 struct Droppable<'a>(&'a mut bool, u32);
diff --git a/tests/ui/mir/mir_dynamic_drops_2.rs b/tests/ui/mir/mir_dynamic_drops_2.rs
index dc71f414673..f625f19be26 100644
--- a/tests/ui/mir/mir_dynamic_drops_2.rs
+++ b/tests/ui/mir/mir_dynamic_drops_2.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:drop 1
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 /// Structure which will not allow to be dropped twice.
 struct Droppable<'a>(&'a mut bool, u32);
diff --git a/tests/ui/mir/mir_dynamic_drops_3.rs b/tests/ui/mir/mir_dynamic_drops_3.rs
index fe84502ef1d..2a8af4ce1fb 100644
--- a/tests/ui/mir/mir_dynamic_drops_3.rs
+++ b/tests/ui/mir/mir_dynamic_drops_3.rs
@@ -4,7 +4,7 @@
 //@ error-pattern:drop 3
 //@ error-pattern:drop 2
 //@ error-pattern:drop 1
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 /// Structure which will not allow to be dropped twice.
 struct Droppable<'a>(&'a mut bool, u32);
diff --git a/tests/ui/mir/mir_indexing_oob_1.rs b/tests/ui/mir/mir_indexing_oob_1.rs
index 3afc2f0b32e..936484b0c1e 100644
--- a/tests/ui/mir/mir_indexing_oob_1.rs
+++ b/tests/ui/mir/mir_indexing_oob_1.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:index out of bounds: the len is 5 but the index is 10
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 const C: [u32; 5] = [0; 5];
 
diff --git a/tests/ui/mir/mir_indexing_oob_2.rs b/tests/ui/mir/mir_indexing_oob_2.rs
index 6e7c1c83536..310a7f8f2b7 100644
--- a/tests/ui/mir/mir_indexing_oob_2.rs
+++ b/tests/ui/mir/mir_indexing_oob_2.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:index out of bounds: the len is 5 but the index is 10
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 const C: &'static [u8; 5] = b"hello";
 
diff --git a/tests/ui/mir/mir_indexing_oob_3.rs b/tests/ui/mir/mir_indexing_oob_3.rs
index 4012864c6ce..44b3a2c5a6d 100644
--- a/tests/ui/mir/mir_indexing_oob_3.rs
+++ b/tests/ui/mir/mir_indexing_oob_3.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:index out of bounds: the len is 5 but the index is 10
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 const C: &'static [u8; 5] = b"hello";
 
diff --git a/tests/ui/never_type/return-never-coerce.rs b/tests/ui/never_type/return-never-coerce.rs
index 559b7d0e985..14f07dfeb76 100644
--- a/tests/ui/never_type/return-never-coerce.rs
+++ b/tests/ui/never_type/return-never-coerce.rs
@@ -2,7 +2,7 @@
 
 //@ run-fail
 //@ error-pattern:aah!
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn call_another_fn<T, F: FnOnce() -> T>(f: F) -> T {
     f()
diff --git a/tests/ui/nll/issue-51345-2.rs b/tests/ui/nll/issue-51345-2.rs
index f2501fdbab4..39871d56a9a 100644
--- a/tests/ui/nll/issue-51345-2.rs
+++ b/tests/ui/nll/issue-51345-2.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:thread 'main' panicked
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let mut vec = vec![];
diff --git a/tests/ui/numbers-arithmetic/divide-by-zero.rs b/tests/ui/numbers-arithmetic/divide-by-zero.rs
index 626daf9771d..a05abadf4bb 100644
--- a/tests/ui/numbers-arithmetic/divide-by-zero.rs
+++ b/tests/ui/numbers-arithmetic/divide-by-zero.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:attempt to divide by zero
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #[allow(unconditional_panic)]
 fn main() {
diff --git a/tests/ui/numbers-arithmetic/int-abs-overflow.rs b/tests/ui/numbers-arithmetic/int-abs-overflow.rs
index e9114138048..6397f62d065 100644
--- a/tests/ui/numbers-arithmetic/int-abs-overflow.rs
+++ b/tests/ui/numbers-arithmetic/int-abs-overflow.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ compile-flags: -C overflow-checks=on
-//@ ignore-emscripten no threads support
+//@ needs-threads
 //@ needs-unwind
 
 use std::thread;
diff --git a/tests/ui/numbers-arithmetic/issue-8460.rs b/tests/ui/numbers-arithmetic/issue-8460.rs
index 9d3044a7ca0..87867fdc93e 100644
--- a/tests/ui/numbers-arithmetic/issue-8460.rs
+++ b/tests/ui/numbers-arithmetic/issue-8460.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 #![allow(unused_must_use)]
-//@ ignore-emscripten no threads support
+//@ needs-threads
 //@ needs-unwind
 #![feature(rustc_attrs)]
 
diff --git a/tests/ui/numbers-arithmetic/mod-zero.rs b/tests/ui/numbers-arithmetic/mod-zero.rs
index f3cc7c9fc88..300bd765c40 100644
--- a/tests/ui/numbers-arithmetic/mod-zero.rs
+++ b/tests/ui/numbers-arithmetic/mod-zero.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:attempt to calculate the remainder with a divisor of zero
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #[allow(unconditional_panic)]
 fn main() {
diff --git a/tests/ui/numbers-arithmetic/overflowing-add.rs b/tests/ui/numbers-arithmetic/overflowing-add.rs
index 16583f6eb74..c1f498c802b 100644
--- a/tests/ui/numbers-arithmetic/overflowing-add.rs
+++ b/tests/ui/numbers-arithmetic/overflowing-add.rs
@@ -2,7 +2,7 @@
 //@ error-pattern:thread 'main' panicked
 //@ error-pattern:attempt to add with overflow
 //@ compile-flags: -C debug-assertions
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #![allow(arithmetic_overflow)]
 
diff --git a/tests/ui/numbers-arithmetic/overflowing-mul.rs b/tests/ui/numbers-arithmetic/overflowing-mul.rs
index 59575d2e86e..0eece536929 100644
--- a/tests/ui/numbers-arithmetic/overflowing-mul.rs
+++ b/tests/ui/numbers-arithmetic/overflowing-mul.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:thread 'main' panicked
 //@ error-pattern:attempt to multiply with overflow
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 //@ compile-flags: -C debug-assertions
 
 #![allow(arithmetic_overflow)]
diff --git a/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs b/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs
index 8aa0d04e500..c5059c002b4 100644
--- a/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs
+++ b/tests/ui/numbers-arithmetic/overflowing-neg-nonzero.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:attempt to negate with overflow
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 //@ compile-flags: -C debug-assertions
 #![allow(arithmetic_overflow)]
 
diff --git a/tests/ui/numbers-arithmetic/overflowing-pow-signed.rs b/tests/ui/numbers-arithmetic/overflowing-pow-signed.rs
index 69e22c2262a..28deb7cf6ba 100644
--- a/tests/ui/numbers-arithmetic/overflowing-pow-signed.rs
+++ b/tests/ui/numbers-arithmetic/overflowing-pow-signed.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:thread 'main' panicked
 //@ error-pattern:attempt to multiply with overflow
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 //@ compile-flags: -C debug-assertions
 
 fn main() {
diff --git a/tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs b/tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs
index f980033c480..dea9a4d5428 100644
--- a/tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs
+++ b/tests/ui/numbers-arithmetic/overflowing-pow-unsigned.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:thread 'main' panicked
 //@ error-pattern:attempt to multiply with overflow
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 //@ compile-flags: -C debug-assertions
 
 fn main() {
diff --git a/tests/ui/numbers-arithmetic/overflowing-sub.rs b/tests/ui/numbers-arithmetic/overflowing-sub.rs
index 44aadf6b3e7..88b1b693f63 100644
--- a/tests/ui/numbers-arithmetic/overflowing-sub.rs
+++ b/tests/ui/numbers-arithmetic/overflowing-sub.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:thread 'main' panicked
 //@ error-pattern:attempt to subtract with overflow
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 //@ compile-flags: -C debug-assertions
 
 #![allow(arithmetic_overflow)]
diff --git a/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs b/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs
index ce6644e6758..0566d2319df 100644
--- a/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs
+++ b/tests/ui/panic-runtime/abort-link-to-unwinding-crates.rs
@@ -1,10 +1,8 @@
 //@ run-pass
-#![allow(unused_variables)]
 //@ compile-flags:-C panic=abort
 //@ aux-build:exit-success-if-unwind.rs
 //@ no-prefer-dynamic
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 extern crate exit_success_if_unwind;
 
@@ -13,7 +11,7 @@ use std::process::Command;
 
 fn main() {
     let mut args = env::args_os();
-    let me = args.next().unwrap();
+    let _ = args.next().unwrap();
 
     if let Some(s) = args.next() {
         if &*s == "foo" {
diff --git a/tests/ui/panic-runtime/abort.rs b/tests/ui/panic-runtime/abort.rs
index caf0243ebdb..8cdfd018a92 100644
--- a/tests/ui/panic-runtime/abort.rs
+++ b/tests/ui/panic-runtime/abort.rs
@@ -1,9 +1,7 @@
 //@ run-pass
-#![allow(unused_variables)]
 //@ compile-flags:-C panic=abort
 //@ no-prefer-dynamic
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::env;
 use std::process::Command;
@@ -18,7 +16,7 @@ impl Drop for Bomb {
 
 fn main() {
     let mut args = env::args_os();
-    let me = args.next().unwrap();
+    let _ = args.next().unwrap();
 
     if let Some(s) = args.next() {
         if &*s == "foo" {
diff --git a/tests/ui/panic-runtime/lto-abort.rs b/tests/ui/panic-runtime/lto-abort.rs
index c66b6a60c73..cf15ae6435b 100644
--- a/tests/ui/panic-runtime/lto-abort.rs
+++ b/tests/ui/panic-runtime/lto-abort.rs
@@ -1,9 +1,7 @@
 //@ run-pass
-#![allow(unused_variables)]
 //@ compile-flags:-C lto -C panic=abort
 //@ no-prefer-dynamic
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::process::Command;
 use std::env;
@@ -18,7 +16,7 @@ impl Drop for Bomb {
 
 fn main() {
     let mut args = env::args_os();
-    let me = args.next().unwrap();
+    let _ = args.next().unwrap();
 
     if let Some(s) = args.next() {
         if &*s == "foo" {
diff --git a/tests/ui/panic-runtime/lto-unwind.rs b/tests/ui/panic-runtime/lto-unwind.rs
index 5eab2bd56ed..93275052f85 100644
--- a/tests/ui/panic-runtime/lto-unwind.rs
+++ b/tests/ui/panic-runtime/lto-unwind.rs
@@ -1,11 +1,8 @@
 //@ run-pass
-#![allow(unused_variables)]
-
 //@ compile-flags:-C lto -C panic=unwind
 //@ needs-unwind
 //@ no-prefer-dynamic
-//@ ignore-emscripten no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::process::Command;
 use std::env;
@@ -20,7 +17,7 @@ impl Drop for Bomb {
 
 fn main() {
     let mut args = env::args_os();
-    let me = args.next().unwrap();
+    let _ = args.next().unwrap();
 
     if let Some(s) = args.next() {
         if &*s == "foo" {
diff --git a/tests/ui/panic-runtime/unwind-interleaved.rs b/tests/ui/panic-runtime/unwind-interleaved.rs
index e5505cd893a..83eb6365097 100644
--- a/tests/ui/panic-runtime/unwind-interleaved.rs
+++ b/tests/ui/panic-runtime/unwind-interleaved.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn a() {}
 
diff --git a/tests/ui/panic-runtime/unwind-rec.rs b/tests/ui/panic-runtime/unwind-rec.rs
index d4b53c88768..a855a4de280 100644
--- a/tests/ui/panic-runtime/unwind-rec.rs
+++ b/tests/ui/panic-runtime/unwind-rec.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn build() -> Vec<isize> {
     panic!();
diff --git a/tests/ui/panic-runtime/unwind-rec2.rs b/tests/ui/panic-runtime/unwind-rec2.rs
index 6ac9a5a5805..ed02b117fff 100644
--- a/tests/ui/panic-runtime/unwind-rec2.rs
+++ b/tests/ui/panic-runtime/unwind-rec2.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn build1() -> Vec<isize> {
     vec![0, 0, 0, 0, 0, 0, 0]
diff --git a/tests/ui/panic-runtime/unwind-unique.rs b/tests/ui/panic-runtime/unwind-unique.rs
index a6cd59690ca..b57d81842c4 100644
--- a/tests/ui/panic-runtime/unwind-unique.rs
+++ b/tests/ui/panic-runtime/unwind-unique.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn failfn() {
     panic!();
diff --git a/tests/ui/panics/abort-on-panic.rs b/tests/ui/panics/abort-on-panic.rs
index feccefb2537..d3bf087bd3e 100644
--- a/tests/ui/panics/abort-on-panic.rs
+++ b/tests/ui/panics/abort-on-panic.rs
@@ -8,8 +8,7 @@
 // Since we mark some ABIs as "nounwind" to LLVM, we must make sure that
 // we never unwind through them.
 
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::io;
 use std::io::prelude::*;
diff --git a/tests/ui/panics/args-panic.rs b/tests/ui/panics/args-panic.rs
index 091ced9b479..9675c99dcb6 100644
--- a/tests/ui/panics/args-panic.rs
+++ b/tests/ui/panics/args-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:meep
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn f(_a: isize, _b: isize, _c: Box<isize>) {
     panic!("moop");
diff --git a/tests/ui/panics/doublepanic.rs b/tests/ui/panics/doublepanic.rs
index 51945ea708c..6fddde82a27 100644
--- a/tests/ui/panics/doublepanic.rs
+++ b/tests/ui/panics/doublepanic.rs
@@ -2,7 +2,7 @@
 
 //@ run-fail
 //@ error-pattern:One
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     panic!("One");
diff --git a/tests/ui/panics/explicit-panic-msg.rs b/tests/ui/panics/explicit-panic-msg.rs
index ef0c5b39f09..e5728ef1e20 100644
--- a/tests/ui/panics/explicit-panic-msg.rs
+++ b/tests/ui/panics/explicit-panic-msg.rs
@@ -4,7 +4,7 @@
 
 //@ run-fail
 //@ error-pattern:wooooo
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let mut a = 1;
diff --git a/tests/ui/panics/explicit-panic.rs b/tests/ui/panics/explicit-panic.rs
index 34e952ef68f..7c69289940e 100644
--- a/tests/ui/panics/explicit-panic.rs
+++ b/tests/ui/panics/explicit-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:explicit
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     panic!();
diff --git a/tests/ui/panics/fmt-panic.rs b/tests/ui/panics/fmt-panic.rs
index 032f65cb2e4..c6cb039684d 100644
--- a/tests/ui/panics/fmt-panic.rs
+++ b/tests/ui/panics/fmt-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:meh
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let str_var: String = "meh".to_string();
diff --git a/tests/ui/panics/issue-47429-short-backtraces.rs b/tests/ui/panics/issue-47429-short-backtraces.rs
index dff885af1b8..4a73ebe8712 100644
--- a/tests/ui/panics/issue-47429-short-backtraces.rs
+++ b/tests/ui/panics/issue-47429-short-backtraces.rs
@@ -17,10 +17,10 @@
 //@ ignore-msvc see #62897 and `backtrace-debuginfo.rs` test
 //@ ignore-android FIXME #17520
 //@ ignore-openbsd no support for libbacktrace without filename
-//@ ignore-wasm no panic or subprocess support
-//@ ignore-emscripten no panic or subprocess support
-//@ ignore-sgx no subprocess support
+//@ ignore-wasm no panic support
+//@ ignore-emscripten no panic support
 //@ ignore-fuchsia Backtraces not symbolized
+//@ needs-subprocess
 
 fn main() {
     panic!()
diff --git a/tests/ui/panics/main-panic.rs b/tests/ui/panics/main-panic.rs
index b69f1656ca4..0b3d5c3aaec 100644
--- a/tests/ui/panics/main-panic.rs
+++ b/tests/ui/panics/main-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:thread 'main' panicked at
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     panic!()
diff --git a/tests/ui/panics/panic-arg.rs b/tests/ui/panics/panic-arg.rs
index 10be6d5ff6c..037cfda3689 100644
--- a/tests/ui/panics/panic-arg.rs
+++ b/tests/ui/panics/panic-arg.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:woe
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn f(a: isize) {
     println!("{}", a);
diff --git a/tests/ui/panics/panic-handler-chain-update-hook.rs b/tests/ui/panics/panic-handler-chain-update-hook.rs
index 1f8fe30cfd8..662ea9e978f 100644
--- a/tests/ui/panics/panic-handler-chain-update-hook.rs
+++ b/tests/ui/panics/panic-handler-chain-update-hook.rs
@@ -2,7 +2,7 @@
 //@ needs-unwind
 #![allow(stable_features)]
 
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 #![feature(std_panic)]
 #![feature(panic_update_hook)]
diff --git a/tests/ui/panics/panic-handler-flail-wildly.rs b/tests/ui/panics/panic-handler-flail-wildly.rs
index 768c9d4c4c5..d42dfd68d9c 100644
--- a/tests/ui/panics/panic-handler-flail-wildly.rs
+++ b/tests/ui/panics/panic-handler-flail-wildly.rs
@@ -4,7 +4,7 @@
 #![allow(stable_features)]
 #![allow(unused_must_use)]
 
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 #![feature(std_panic)]
 
diff --git a/tests/ui/panics/panic-handler-set-twice.rs b/tests/ui/panics/panic-handler-set-twice.rs
index 902e48b6541..5f670d5f492 100644
--- a/tests/ui/panics/panic-handler-set-twice.rs
+++ b/tests/ui/panics/panic-handler-set-twice.rs
@@ -5,7 +5,7 @@
 
 #![feature(std_panic)]
 
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::panic;
diff --git a/tests/ui/panics/panic-in-dtor-drops-fields.rs b/tests/ui/panics/panic-in-dtor-drops-fields.rs
index 4d18dc0e059..38eb6d0acfb 100644
--- a/tests/ui/panics/panic-in-dtor-drops-fields.rs
+++ b/tests/ui/panics/panic-in-dtor-drops-fields.rs
@@ -3,7 +3,7 @@
 #![allow(dead_code)]
 #![allow(non_upper_case_globals)]
 
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 use std::thread;
 
diff --git a/tests/ui/panics/panic-macro-any-wrapped.rs b/tests/ui/panics/panic-macro-any-wrapped.rs
index 7c6790e35fd..e3a2dc6eed4 100644
--- a/tests/ui/panics/panic-macro-any-wrapped.rs
+++ b/tests/ui/panics/panic-macro-any-wrapped.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:panicked
 //@ error-pattern:Box<dyn Any>
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #![allow(non_fmt_panics)]
 
diff --git a/tests/ui/panics/panic-macro-any.rs b/tests/ui/panics/panic-macro-any.rs
index 75397333fa4..1392929b65c 100644
--- a/tests/ui/panics/panic-macro-any.rs
+++ b/tests/ui/panics/panic-macro-any.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:panicked
 //@ error-pattern:Box<dyn Any>
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #![allow(non_fmt_panics)]
 
diff --git a/tests/ui/panics/panic-macro-explicit.rs b/tests/ui/panics/panic-macro-explicit.rs
index 2c7b84d99fe..b9195e3b0fc 100644
--- a/tests/ui/panics/panic-macro-explicit.rs
+++ b/tests/ui/panics/panic-macro-explicit.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:panicked
 //@ error-pattern:explicit panic
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     panic!();
diff --git a/tests/ui/panics/panic-macro-fmt.rs b/tests/ui/panics/panic-macro-fmt.rs
index 1a63a06c75a..550cd5c1ee3 100644
--- a/tests/ui/panics/panic-macro-fmt.rs
+++ b/tests/ui/panics/panic-macro-fmt.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:panicked
 //@ error-pattern:test-fail-fmt 42 rust
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     panic!("test-fail-fmt {} {}", 42, "rust");
diff --git a/tests/ui/panics/panic-macro-owned.rs b/tests/ui/panics/panic-macro-owned.rs
index 1878f3d52ab..4df04c50bc3 100644
--- a/tests/ui/panics/panic-macro-owned.rs
+++ b/tests/ui/panics/panic-macro-owned.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:panicked
 //@ error-pattern:test-fail-owned
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     panic!("test-fail-owned");
diff --git a/tests/ui/panics/panic-macro-static.rs b/tests/ui/panics/panic-macro-static.rs
index 018166e60cf..1c6258ebed2 100644
--- a/tests/ui/panics/panic-macro-static.rs
+++ b/tests/ui/panics/panic-macro-static.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:panicked
 //@ error-pattern:test-fail-static
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     panic!("test-fail-static");
diff --git a/tests/ui/panics/panic-main.rs b/tests/ui/panics/panic-main.rs
index d71fca0754e..3876dbb37c3 100644
--- a/tests/ui/panics/panic-main.rs
+++ b/tests/ui/panics/panic-main.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:moop
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     panic!("moop");
diff --git a/tests/ui/panics/panic-parens.rs b/tests/ui/panics/panic-parens.rs
index 271d0363cab..5440bf18f25 100644
--- a/tests/ui/panics/panic-parens.rs
+++ b/tests/ui/panics/panic-parens.rs
@@ -3,7 +3,7 @@
 
 //@ run-fail
 //@ error-pattern:oops
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn bigpanic() {
     while (panic!("oops")) {
diff --git a/tests/ui/panics/panic-recover-propagate.rs b/tests/ui/panics/panic-recover-propagate.rs
index f8be86be19d..ef6ae4fd788 100644
--- a/tests/ui/panics/panic-recover-propagate.rs
+++ b/tests/ui/panics/panic-recover-propagate.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 use std::sync::atomic::{AtomicUsize, Ordering};
 use std::panic;
diff --git a/tests/ui/panics/panic-set-handler.rs b/tests/ui/panics/panic-set-handler.rs
index 39286ca865b..41e513e0bd6 100644
--- a/tests/ui/panics/panic-set-handler.rs
+++ b/tests/ui/panics/panic-set-handler.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:greetings from the panic handler
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::panic;
 
diff --git a/tests/ui/panics/panic-set-unset-handler.rs b/tests/ui/panics/panic-set-unset-handler.rs
index 02f1599338b..66d5003d0f1 100644
--- a/tests/ui/panics/panic-set-unset-handler.rs
+++ b/tests/ui/panics/panic-set-unset-handler.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:thread 'main' panicked
 //@ error-pattern:foobar
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::panic;
 
diff --git a/tests/ui/panics/panic-take-handler-nop.rs b/tests/ui/panics/panic-take-handler-nop.rs
index 89e1d234df1..f10582872df 100644
--- a/tests/ui/panics/panic-take-handler-nop.rs
+++ b/tests/ui/panics/panic-take-handler-nop.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:thread 'main' panicked
 //@ error-pattern:foobar
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::panic;
 
diff --git a/tests/ui/panics/panic.rs b/tests/ui/panics/panic.rs
index b9721ac8230..068f187524d 100644
--- a/tests/ui/panics/panic.rs
+++ b/tests/ui/panics/panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:1 == 2
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     assert!(1 == 2);
diff --git a/tests/ui/panics/result-get-panic.rs b/tests/ui/panics/result-get-panic.rs
index d7f6dfe8406..b8dc49f9aca 100644
--- a/tests/ui/panics/result-get-panic.rs
+++ b/tests/ui/panics/result-get-panic.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:called `Result::unwrap()` on an `Err` value
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::result::Result::Err;
 
diff --git a/tests/ui/panics/runtime-switch.rs b/tests/ui/panics/runtime-switch.rs
index ffd038f9535..9f0be8c7ddf 100644
--- a/tests/ui/panics/runtime-switch.rs
+++ b/tests/ui/panics/runtime-switch.rs
@@ -18,9 +18,9 @@
 //@ ignore-android FIXME #17520
 //@ ignore-openbsd no support for libbacktrace without filename
 //@ ignore-wasm no backtrace support
-//@ ignore-emscripten no panic or subprocess support
-//@ ignore-sgx no subprocess support
+//@ ignore-emscripten no panic support
 //@ ignore-fuchsia Backtrace not symbolized
+//@ needs-subprocess
 
 #![feature(panic_backtrace_config)]
 
diff --git a/tests/ui/panics/test-panic.rs b/tests/ui/panics/test-panic.rs
index 29a3c4e9c9f..c7af47524cf 100644
--- a/tests/ui/panics/test-panic.rs
+++ b/tests/ui/panics/test-panic.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ check-stdout
 //@ compile-flags: --test
-//@ ignore-emscripten
+//@ needs-subprocess
 
 #[test]
 fn test_foo() {
diff --git a/tests/ui/panics/test-should-panic-no-message.rs b/tests/ui/panics/test-should-panic-no-message.rs
index b6ed6b19dd0..05fc927d876 100644
--- a/tests/ui/panics/test-should-panic-no-message.rs
+++ b/tests/ui/panics/test-should-panic-no-message.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ compile-flags: --test
 //@ check-stdout
-//@ ignore-wasm32 no processes
+//@ needs-subprocess
 
 #[test]
 #[should_panic(expected = "foo")]
diff --git a/tests/ui/panics/while-body-panics.rs b/tests/ui/panics/while-body-panics.rs
index bddcd5d50ce..8459a8d63bf 100644
--- a/tests/ui/panics/while-body-panics.rs
+++ b/tests/ui/panics/while-body-panics.rs
@@ -2,7 +2,7 @@
 
 //@ run-fail
 //@ error-pattern:quux
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let _x: isize = {
diff --git a/tests/ui/panics/while-panic.rs b/tests/ui/panics/while-panic.rs
index 2961e8599c3..4c8431c71d1 100644
--- a/tests/ui/panics/while-panic.rs
+++ b/tests/ui/panics/while-panic.rs
@@ -2,7 +2,7 @@
 
 //@ run-fail
 //@ error-pattern:giraffe
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     panic!("{}", {
diff --git a/tests/ui/print-stdout-eprint-stderr.rs b/tests/ui/print-stdout-eprint-stderr.rs
index e84a9bebc49..4b356e2fe61 100644
--- a/tests/ui/print-stdout-eprint-stderr.rs
+++ b/tests/ui/print-stdout-eprint-stderr.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 spawning processes is not supported
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::{env, process};
 
diff --git a/tests/ui/process/core-run-destroy.rs b/tests/ui/process/core-run-destroy.rs
index 3f2ea0e8441..b4815c9dfbb 100644
--- a/tests/ui/process/core-run-destroy.rs
+++ b/tests/ui/process/core-run-destroy.rs
@@ -1,12 +1,11 @@
 //@ run-pass
 
 #![allow(unused_must_use)]
-#![allow(stable_features)]
 #![allow(deprecated)]
 #![allow(unused_imports)]
+
 //@ compile-flags:--test
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-vxworks no 'cat' and 'sleep'
 //@ ignore-fuchsia no 'cat'
 
diff --git a/tests/ui/process/env-args-reverse-iterator.rs b/tests/ui/process/env-args-reverse-iterator.rs
index 830e9535466..f0afeeb22eb 100644
--- a/tests/ui/process/env-args-reverse-iterator.rs
+++ b/tests/ui/process/env-args-reverse-iterator.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::env::args;
 use std::process::Command;
diff --git a/tests/ui/process/fds-are-cloexec.rs b/tests/ui/process/fds-are-cloexec.rs
index e7b000b2c49..f6678379dd6 100644
--- a/tests/ui/process/fds-are-cloexec.rs
+++ b/tests/ui/process/fds-are-cloexec.rs
@@ -1,9 +1,8 @@
 //@ run-pass
-//@ ignore-windows
+//@ only-unix
 //@ ignore-android
-//@ ignore-wasm32 no processes
+//@ needs-subprocess
 //@ ignore-haiku
-//@ ignore-sgx no processes
 
 #![feature(rustc_private)]
 
diff --git a/tests/ui/process/inherit-env.rs b/tests/ui/process/inherit-env.rs
index 0eb61fcdd53..09d5b76141e 100644
--- a/tests/ui/process/inherit-env.rs
+++ b/tests/ui/process/inherit-env.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no subprocess support
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::env;
 use std::process::Command;
diff --git a/tests/ui/process/issue-13304.rs b/tests/ui/process/issue-13304.rs
index 6dbf0caaaec..621c54300d3 100644
--- a/tests/ui/process/issue-13304.rs
+++ b/tests/ui/process/issue-13304.rs
@@ -1,7 +1,5 @@
 //@ run-pass
-#![allow(unused_mut)]
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::env;
 use std::io::prelude::*;
@@ -32,7 +30,7 @@ fn parent() {
 }
 
 fn child() {
-    let mut stdin = io::stdin();
+    let stdin = io::stdin();
     for line in stdin.lock().lines() {
         println!("{}", line.unwrap());
     }
diff --git a/tests/ui/process/issue-14456.rs b/tests/ui/process/issue-14456.rs
index fd6da8a5fc4..e67a9d8bad5 100644
--- a/tests/ui/process/issue-14456.rs
+++ b/tests/ui/process/issue-14456.rs
@@ -1,7 +1,5 @@
 //@ run-pass
-#![allow(unused_mut)]
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::env;
 use std::io::prelude::*;
@@ -20,7 +18,7 @@ fn main() {
 fn child() {
     writeln!(&mut io::stdout(), "foo").unwrap();
     writeln!(&mut io::stderr(), "bar").unwrap();
-    let mut stdin = io::stdin();
+    let stdin = io::stdin();
     let mut s = String::new();
     stdin.lock().read_line(&mut s).unwrap();
     assert_eq!(s.len(), 0);
diff --git a/tests/ui/process/issue-14940.rs b/tests/ui/process/issue-14940.rs
index 13fb18154a0..cfbc743250f 100644
--- a/tests/ui/process/issue-14940.rs
+++ b/tests/ui/process/issue-14940.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::env;
 use std::process::Command;
diff --git a/tests/ui/process/issue-16272.rs b/tests/ui/process/issue-16272.rs
index bf26769d494..72708554753 100644
--- a/tests/ui/process/issue-16272.rs
+++ b/tests/ui/process/issue-16272.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::process::Command;
 use std::env;
diff --git a/tests/ui/process/issue-20091.rs b/tests/ui/process/issue-20091.rs
index b6d94661b75..72bf4c0e71e 100644
--- a/tests/ui/process/issue-20091.rs
+++ b/tests/ui/process/issue-20091.rs
@@ -1,9 +1,5 @@
 //@ run-pass
-#![allow(stable_features)]
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
-
-#![feature(os)]
+//@ needs-subprocess
 
 #[cfg(unix)]
 fn main() {
diff --git a/tests/ui/process/issue-30490.rs b/tests/ui/process/issue-30490.rs
index 0d918bc3dd5..75b36e7c20b 100644
--- a/tests/ui/process/issue-30490.rs
+++ b/tests/ui/process/issue-30490.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-emscripten no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-fuchsia Child I/O swaps not privileged
 
 // Previously libstd would set stdio descriptors of a child process
diff --git a/tests/ui/process/multi-panic.rs b/tests/ui/process/multi-panic.rs
index ad47925a149..481fe75c731 100644
--- a/tests/ui/process/multi-panic.rs
+++ b/tests/ui/process/multi-panic.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ needs-unwind
 
 fn check_for_no_backtrace(test: std::process::Output) {
diff --git a/tests/ui/process/no-stdio.rs b/tests/ui/process/no-stdio.rs
index 8eebf6dbc7d..5cc7cacbb22 100644
--- a/tests/ui/process/no-stdio.rs
+++ b/tests/ui/process/no-stdio.rs
@@ -1,7 +1,6 @@
 //@ run-pass
 //@ ignore-android
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 #![feature(rustc_private)]
 
diff --git a/tests/ui/process/println-with-broken-pipe.rs b/tests/ui/process/println-with-broken-pipe.rs
index d88c6dcc12b..fbac9b6cd95 100644
--- a/tests/ui/process/println-with-broken-pipe.rs
+++ b/tests/ui/process/println-with-broken-pipe.rs
@@ -1,7 +1,7 @@
 //@ run-pass
 //@ check-run-results
+//@ needs-subprocess
 //@ ignore-windows
-//@ ignore-wasm32
 //@ ignore-fuchsia
 //@ ignore-horizon
 //@ ignore-android
diff --git a/tests/ui/process/process-envs.rs b/tests/ui/process/process-envs.rs
index 15285960d16..98052f1d3a5 100644
--- a/tests/ui/process/process-envs.rs
+++ b/tests/ui/process/process-envs.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-vxworks no 'env'
 //@ ignore-fuchsia no 'env'
 
diff --git a/tests/ui/process/process-exit.rs b/tests/ui/process/process-exit.rs
index a75a7306cbc..a1ed243b62b 100644
--- a/tests/ui/process/process-exit.rs
+++ b/tests/ui/process/process-exit.rs
@@ -1,10 +1,8 @@
 //@ run-pass
-#![allow(unused_imports)]
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::env;
-use std::process::{self, Command, Stdio};
+use std::process::{self, Command};
 
 fn main() {
     let args: Vec<String> = env::args().collect();
diff --git a/tests/ui/process/process-panic-after-fork.rs b/tests/ui/process/process-panic-after-fork.rs
index afb1b721182..6e0267e0a54 100644
--- a/tests/ui/process/process-panic-after-fork.rs
+++ b/tests/ui/process/process-panic-after-fork.rs
@@ -1,8 +1,7 @@
 //@ run-pass
 //@ no-prefer-dynamic
-//@ ignore-windows
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ only-unix
+//@ needs-subprocess
 //@ ignore-fuchsia no fork
 
 #![feature(rustc_private)]
diff --git a/tests/ui/process/process-remove-from-env.rs b/tests/ui/process/process-remove-from-env.rs
index 21fff4fd45d..c1a2b2daf5b 100644
--- a/tests/ui/process/process-remove-from-env.rs
+++ b/tests/ui/process/process-remove-from-env.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-vxworks no 'env'
 //@ ignore-fuchsia no 'env'
 
diff --git a/tests/ui/process/process-sigpipe.rs b/tests/ui/process/process-sigpipe.rs
index 9db130c26bd..453e53379fc 100644
--- a/tests/ui/process/process-sigpipe.rs
+++ b/tests/ui/process/process-sigpipe.rs
@@ -15,7 +15,7 @@
 
 //@ ignore-vxworks no 'sh'
 //@ ignore-fuchsia no 'sh'
-//@ ignore-emscripten No threads
+//@ needs-threads
 //@ only-unix SIGPIPE is a unix feature
 
 use std::process;
diff --git a/tests/ui/process/process-spawn-nonexistent.rs b/tests/ui/process/process-spawn-nonexistent.rs
index 1cd32866299..3db670624fb 100644
--- a/tests/ui/process/process-spawn-nonexistent.rs
+++ b/tests/ui/process/process-spawn-nonexistent.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-fuchsia ErrorKind not translated
 
 use std::io::ErrorKind;
diff --git a/tests/ui/process/process-spawn-with-unicode-params.rs b/tests/ui/process/process-spawn-with-unicode-params.rs
index 4d2ba49eeac..65f835c1345 100644
--- a/tests/ui/process/process-spawn-with-unicode-params.rs
+++ b/tests/ui/process/process-spawn-with-unicode-params.rs
@@ -7,8 +7,7 @@
 // non-ASCII characters.  The child process ensures all the strings are
 // intact.
 
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-fuchsia Filesystem manipulation privileged
 
 use std::io::prelude::*;
diff --git a/tests/ui/process/process-status-inherits-stdin.rs b/tests/ui/process/process-status-inherits-stdin.rs
index 39eef34c5f8..d5dd0e55fa3 100644
--- a/tests/ui/process/process-status-inherits-stdin.rs
+++ b/tests/ui/process/process-status-inherits-stdin.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::env;
 use std::io;
diff --git a/tests/ui/process/signal-exit-status.rs b/tests/ui/process/signal-exit-status.rs
index a6acea47636..33aa83abfc3 100644
--- a/tests/ui/process/signal-exit-status.rs
+++ b/tests/ui/process/signal-exit-status.rs
@@ -1,7 +1,6 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
-//@ ignore-windows
+//@ needs-subprocess
+//@ only-unix (`code()` returns `None` if terminated by a signal on Unix)
 //@ ignore-fuchsia code returned as ZX_TASK_RETCODE_EXCEPTION_KILL, FIXME (#58590)
 
 #![feature(core_intrinsics)]
diff --git a/tests/ui/process/sigpipe-should-be-ignored.rs b/tests/ui/process/sigpipe-should-be-ignored.rs
index 44785bee7f8..3dcf0117ae9 100644
--- a/tests/ui/process/sigpipe-should-be-ignored.rs
+++ b/tests/ui/process/sigpipe-should-be-ignored.rs
@@ -1,12 +1,9 @@
 //@ run-pass
+//@ needs-subprocess
 
-#![allow(unused_must_use)]
 // Be sure that when a SIGPIPE would have been received that the entire process
 // doesn't die in a ball of fire, but rather it's gracefully handled.
 
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
-
 use std::env;
 use std::io::prelude::*;
 use std::io;
@@ -14,7 +11,7 @@ use std::process::{Command, Stdio};
 
 fn test() {
     let _ = io::stdin().read_line(&mut String::new());
-    io::stdout().write(&[1]);
+    io::stdout().write(&[1]).unwrap();
     assert!(io::stdout().flush().is_err());
 }
 
diff --git a/tests/ui/process/tls-exit-status.rs b/tests/ui/process/tls-exit-status.rs
index cddcf369da0..6dd0d71ef35 100644
--- a/tests/ui/process/tls-exit-status.rs
+++ b/tests/ui/process/tls-exit-status.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:nonzero
 //@ exec-env:RUST_NEWRT=1
-//@ ignore-wasm32 no processes
+//@ needs-subprocess
 
 use std::env;
 
diff --git a/tests/ui/process/try-wait.rs b/tests/ui/process/try-wait.rs
index b6d026d802f..dcef43ad348 100644
--- a/tests/ui/process/try-wait.rs
+++ b/tests/ui/process/try-wait.rs
@@ -1,9 +1,5 @@
 //@ run-pass
-
-#![allow(stable_features)]
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
-#![feature(process_try_wait)]
+//@ needs-subprocess
 
 use std::env;
 use std::process::Command;
diff --git a/tests/ui/reachable/issue-948.rs b/tests/ui/reachable/issue-948.rs
index 8e239a1115e..6181e547acc 100644
--- a/tests/ui/reachable/issue-948.rs
+++ b/tests/ui/reachable/issue-948.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:beep boop
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #![allow(unused_variables)]
 
diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs
index fb6718e55b2..00a0ea04c0d 100644
--- a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs
+++ b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-box-dyn-error-err.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:returned Box<dyn Error> from main()
 //@ failure-status: 1
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::error::Error;
 use std::io;
diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs
index 91be3afbe22..3b80c2b49a5 100644
--- a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs
+++ b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-never.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:oh, dear
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() -> ! {
     panic!("oh, dear");
diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs
index f1d972b3c55..48605309965 100644
--- a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs
+++ b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-result-box-error_err.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern:returned Box<Error> from main()
 //@ failure-status: 1
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 use std::io::{Error, ErrorKind};
 
diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs
index acf3da2d55f..8f7b3da31bb 100644
--- a/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs
+++ b/tests/ui/rfcs/rfc-1937-termination-trait/termination-trait-for-str-err.rs
@@ -1,7 +1,7 @@
 //@ run-fail
 //@ error-pattern: An error message for you
 //@ failure-status: 1
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() -> Result<(), &'static str> {
     Err("An error message for you")
diff --git a/tests/ui/runtime/atomic-print.rs b/tests/ui/runtime/atomic-print.rs
index 73520589736..6b899d675a2 100644
--- a/tests/ui/runtime/atomic-print.rs
+++ b/tests/ui/runtime/atomic-print.rs
@@ -2,8 +2,8 @@
 
 #![allow(unused_must_use)]
 #![allow(deprecated)]
-//@ ignore-wasm32 no processes or threads
-//@ ignore-sgx no processes
+//@ needs-threads
+//@ needs-subprocess
 
 use std::{env, fmt, process, sync, thread};
 
diff --git a/tests/ui/runtime/backtrace-debuginfo.rs b/tests/ui/runtime/backtrace-debuginfo.rs
index da747ded44f..afc96d6bb5f 100644
--- a/tests/ui/runtime/backtrace-debuginfo.rs
+++ b/tests/ui/runtime/backtrace-debuginfo.rs
@@ -9,8 +9,7 @@
 //@ compile-flags:-g -Copt-level=0 -Cllvm-args=-enable-tail-merge=0
 //@ compile-flags:-Cforce-frame-pointers=yes
 //@ compile-flags:-Cstrip=none
-//@ ignore-wasm32 spawning processes is not supported
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-fuchsia Backtrace not symbolized, trace different line alignment
 
 // FIXME(#117097): backtrace (possibly unwinding mechanism) seems to be different on at least
diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs
index c5300635ad9..6be34afb560 100644
--- a/tests/ui/runtime/out-of-stack.rs
+++ b/tests/ui/runtime/out-of-stack.rs
@@ -3,8 +3,7 @@
 #![allow(unused_must_use)]
 #![allow(unconditional_recursion)]
 //@ ignore-android: FIXME (#20004)
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-fuchsia must translate zircon signal to SIGABRT, FIXME (#58590)
 //@ ignore-nto no stack overflow handler used (no alternate stack available)
 //@ ignore-ios stack overflow handlers aren't enabled
diff --git a/tests/ui/runtime/rt-explody-panic-payloads.rs b/tests/ui/runtime/rt-explody-panic-payloads.rs
index bd3624a8aee..c177fd260ed 100644
--- a/tests/ui/runtime/rt-explody-panic-payloads.rs
+++ b/tests/ui/runtime/rt-explody-panic-payloads.rs
@@ -1,7 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-//@ ignore-emscripten no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::env;
 use std::process::Command;
diff --git a/tests/ui/runtime/running-with-no-runtime.rs b/tests/ui/runtime/running-with-no-runtime.rs
index 5c219b6feda..7ac0dd912dc 100644
--- a/tests/ui/runtime/running-with-no-runtime.rs
+++ b/tests/ui/runtime/running-with-no-runtime.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 spawning processes is not supported
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 #![no_main]
 
diff --git a/tests/ui/sepcomp/sepcomp-unwind.rs b/tests/ui/sepcomp/sepcomp-unwind.rs
index 6a40b5ccc12..8c25278bb7e 100644
--- a/tests/ui/sepcomp/sepcomp-unwind.rs
+++ b/tests/ui/sepcomp/sepcomp-unwind.rs
@@ -2,7 +2,7 @@
 //@ needs-unwind
 #![allow(dead_code)]
 //@ compile-flags: -C codegen-units=3
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 // Test unwinding through multiple compilation units.
 
diff --git a/tests/ui/simd/target-feature-mixup.rs b/tests/ui/simd/target-feature-mixup.rs
index 62d87c3a6dc..2786251c795 100644
--- a/tests/ui/simd/target-feature-mixup.rs
+++ b/tests/ui/simd/target-feature-mixup.rs
@@ -3,8 +3,7 @@
 #![allow(stable_features)]
 #![allow(overflowing_literals)]
 
-//@ ignore-wasm32 no subprocess support
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-fuchsia must translate zircon signal to SIGILL, FIXME (#58590)
 
 #![feature(repr_simd, target_feature, cfg_target_feature)]
diff --git a/tests/ui/std/thread-sleep-ms.rs b/tests/ui/std/thread-sleep-ms.rs
index 0a3d0253a20..2d668b8265c 100644
--- a/tests/ui/std/thread-sleep-ms.rs
+++ b/tests/ui/std/thread-sleep-ms.rs
@@ -1,12 +1,9 @@
 //@ run-pass
-//@ ignore-sgx not supported
-//@ ignore-emscripten
-// FIXME: test hangs on emscripten
-#![allow(deprecated)]
-#![allow(unused_imports)]
+//@ needs-threads
+//@ ignore-emscripten (FIXME: test hangs on emscripten)
 
-use std::thread;
+#![allow(deprecated)]
 
 fn main() {
-    thread::sleep_ms(250);
+    std::thread::sleep_ms(250);
 }
diff --git a/tests/ui/stdio-is-blocking.rs b/tests/ui/stdio-is-blocking.rs
index dda100951dd..615530dcd47 100644
--- a/tests/ui/stdio-is-blocking.rs
+++ b/tests/ui/stdio-is-blocking.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::env;
 use std::io::prelude::*;
diff --git a/tests/ui/str/str-overrun.rs b/tests/ui/str/str-overrun.rs
index b8e245475da..6d62b837694 100644
--- a/tests/ui/str/str-overrun.rs
+++ b/tests/ui/str/str-overrun.rs
@@ -1,6 +1,6 @@
 //@ run-fail
 //@ error-pattern:index out of bounds: the len is 5 but the index is 5
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 fn main() {
     let s: String = "hello".to_string();
diff --git a/tests/ui/structs-enums/unit-like-struct-drop-run.rs b/tests/ui/structs-enums/unit-like-struct-drop-run.rs
index 02d14265f3e..3d00871837c 100644
--- a/tests/ui/structs-enums/unit-like-struct-drop-run.rs
+++ b/tests/ui/structs-enums/unit-like-struct-drop-run.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ needs-unwind
-//@ ignore-emscripten no threads support
+//@ needs-threads
 
 // Make sure the destructor is run for unit-like structs.
 
diff --git a/tests/ui/structs/rhs-type.rs b/tests/ui/structs/rhs-type.rs
index fde5c16a068..8ce924672cf 100644
--- a/tests/ui/structs/rhs-type.rs
+++ b/tests/ui/structs/rhs-type.rs
@@ -3,7 +3,7 @@
 
 //@ run-fail
 //@ error-pattern:bye
-//@ ignore-emscripten no processes
+//@ needs-subprocess
 
 #![allow(unreachable_code)]
 #![allow(unused_variables)]
diff --git a/tests/ui/suggestions/import-visible-path-39175.fixed b/tests/ui/suggestions/import-visible-path-39175.fixed
new file mode 100644
index 00000000000..1f2b5b8b0ce
--- /dev/null
+++ b/tests/ui/suggestions/import-visible-path-39175.fixed
@@ -0,0 +1,16 @@
+// This test ignores some platforms as the particular extension trait used
+// to demonstrate the issue is only available on unix. This is fine as
+// the fix to suggested paths is not platform-dependent and will apply on
+// these platforms also.
+
+//@ run-rustfix
+//@ only-unix (the diagnostics is influenced by `use std::os::unix::process::CommandExt;`)
+
+use std::os::unix::process::CommandExt;
+use std::process::Command;
+// use std::os::unix::process::CommandExt;
+
+fn main() {
+    let _ = Command::new("echo").arg("hello").exec();
+//~^ ERROR no method named `exec`
+}
diff --git a/tests/ui/issues/issue-39175.rs b/tests/ui/suggestions/import-visible-path-39175.rs
index 7b801317b71..a7e6134b2d9 100644
--- a/tests/ui/issues/issue-39175.rs
+++ b/tests/ui/suggestions/import-visible-path-39175.rs
@@ -3,14 +3,13 @@
 // the fix to suggested paths is not platform-dependent and will apply on
 // these platforms also.
 
-//@ ignore-windows
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ run-rustfix
+//@ only-unix (the diagnostics is influenced by `use std::os::unix::process::CommandExt;`)
 
 use std::process::Command;
 // use std::os::unix::process::CommandExt;
 
 fn main() {
-    Command::new("echo").arg("hello").exec();
+    let _ = Command::new("echo").arg("hello").exec();
 //~^ ERROR no method named `exec`
 }
diff --git a/tests/ui/issues/issue-39175.stderr b/tests/ui/suggestions/import-visible-path-39175.stderr
index bbe8badb652..0b558ca4b52 100644
--- a/tests/ui/issues/issue-39175.stderr
+++ b/tests/ui/suggestions/import-visible-path-39175.stderr
@@ -1,8 +1,8 @@
 error[E0599]: no method named `exec` found for mutable reference `&mut Command` in the current scope
-  --> $DIR/issue-39175.rs:14:39
+  --> $DIR/import-visible-path-39175.rs:13:47
    |
-LL |     Command::new("echo").arg("hello").exec();
-   |                                       ^^^^
+LL |     let _ = Command::new("echo").arg("hello").exec();
+   |                                               ^^^^
    |
    = help: items from traits can only be used if the trait is in scope
 help: there is a method `pre_exec` with a similar name, but with different arguments
diff --git a/tests/ui/test-attrs/terse.rs b/tests/ui/test-attrs/terse.rs
index 6c3f29ed10f..6e605f994f2 100644
--- a/tests/ui/test-attrs/terse.rs
+++ b/tests/ui/test-attrs/terse.rs
@@ -4,7 +4,7 @@
 //@ check-run-results
 //@ exec-env:RUST_BACKTRACE=0
 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
-//@ ignore-emscripten no threads support
+//@ needs-threads
 //@ needs-unwind
 
 #[test]
diff --git a/tests/ui/test-attrs/test-panic-abort-disabled.rs b/tests/ui/test-attrs/test-panic-abort-disabled.rs
index fbe3d7d5d18..05dd9395c2b 100644
--- a/tests/ui/test-attrs/test-panic-abort-disabled.rs
+++ b/tests/ui/test-attrs/test-panic-abort-disabled.rs
@@ -4,8 +4,9 @@
 //@ run-flags: --test-threads=1
 
 //@ needs-unwind
-//@ ignore-wasm no panic or subprocess support
-//@ ignore-emscripten no panic or subprocess support
+//@ ignore-wasm no panic support
+//@ ignore-emscripten no panic support
+//@ needs-subprocess
 
 #![cfg(test)]
 
diff --git a/tests/ui/test-attrs/test-panic-abort-nocapture.rs b/tests/ui/test-attrs/test-panic-abort-nocapture.rs
index 4377ae1ac3b..f7e15dbdbc3 100644
--- a/tests/ui/test-attrs/test-panic-abort-nocapture.rs
+++ b/tests/ui/test-attrs/test-panic-abort-nocapture.rs
@@ -7,9 +7,9 @@
 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
 
 //@ ignore-android #120567
-//@ ignore-wasm no panic or subprocess support
-//@ ignore-emscripten no panic or subprocess support
-//@ ignore-sgx no subprocess support
+//@ ignore-wasm no panic support
+//@ ignore-emscripten no panic support
+//@ needs-subprocess
 
 #![cfg(test)]
 
diff --git a/tests/ui/test-attrs/test-panic-abort.rs b/tests/ui/test-attrs/test-panic-abort.rs
index 3d203e059a4..951cf54346b 100644
--- a/tests/ui/test-attrs/test-panic-abort.rs
+++ b/tests/ui/test-attrs/test-panic-abort.rs
@@ -7,9 +7,9 @@
 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
 
 //@ ignore-android #120567
-//@ ignore-wasm no panic or subprocess support
-//@ ignore-emscripten no panic or subprocess support
-//@ ignore-sgx no subprocess support
+//@ ignore-wasm no panic support
+//@ ignore-emscripten no panic support
+//@ needs-subprocess
 
 #![cfg(test)]
 #![feature(test)]
diff --git a/tests/ui/test-attrs/test-thread-capture.rs b/tests/ui/test-attrs/test-thread-capture.rs
index c56f87f2dda..0a5b1e9816f 100644
--- a/tests/ui/test-attrs/test-thread-capture.rs
+++ b/tests/ui/test-attrs/test-thread-capture.rs
@@ -4,7 +4,7 @@
 //@ check-run-results
 //@ exec-env:RUST_BACKTRACE=0
 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
-//@ ignore-emscripten no threads support
+//@ needs-threads
 //@ needs-unwind
 
 #[test]
diff --git a/tests/ui/test-attrs/test-thread-nocapture.rs b/tests/ui/test-attrs/test-thread-nocapture.rs
index 5b82e9b2720..ce5db7bf1c3 100644
--- a/tests/ui/test-attrs/test-thread-nocapture.rs
+++ b/tests/ui/test-attrs/test-thread-nocapture.rs
@@ -4,7 +4,7 @@
 //@ check-run-results
 //@ exec-env:RUST_BACKTRACE=0
 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME"
-//@ ignore-emscripten no threads support
+//@ needs-threads
 //@ needs-unwind
 
 #[test]
diff --git a/tests/ui/threads-sendsync/eprint-on-tls-drop.rs b/tests/ui/threads-sendsync/eprint-on-tls-drop.rs
index 82abf21df3f..e85c7c83339 100644
--- a/tests/ui/threads-sendsync/eprint-on-tls-drop.rs
+++ b/tests/ui/threads-sendsync/eprint-on-tls-drop.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ needs-threads
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::cell::RefCell;
 use std::env;
diff --git a/tests/ui/threads-sendsync/issue-24313.rs b/tests/ui/threads-sendsync/issue-24313.rs
index 99c6c4a5e12..83ab5122e82 100644
--- a/tests/ui/threads-sendsync/issue-24313.rs
+++ b/tests/ui/threads-sendsync/issue-24313.rs
@@ -1,6 +1,6 @@
 //@ run-pass
 //@ needs-threads
-//@ ignore-sgx no processes
+//@ needs-subprocess
 
 use std::process::Command;
 use std::{env, thread};
diff --git a/tests/ui/wait-forked-but-failed-child.rs b/tests/ui/wait-forked-but-failed-child.rs
index dd6a7fa0e65..04f1c1a65d5 100644
--- a/tests/ui/wait-forked-but-failed-child.rs
+++ b/tests/ui/wait-forked-but-failed-child.rs
@@ -1,6 +1,5 @@
 //@ run-pass
-//@ ignore-wasm32 no processes
-//@ ignore-sgx no processes
+//@ needs-subprocess
 //@ ignore-vxworks no 'ps'
 //@ ignore-fuchsia no 'ps'
 //@ ignore-nto no 'ps'