Repository: taiki-e/cargo-hack (language: rust)

Issue:
Handling of optional dependencies changed, on nightly-2022-01-20 or later
UPDATE: fixed in 0.5.11.

---

After namespaced and weak dependency features were stabilized (https://github.com/rust-lang/cargo/pull/10269), the value returned by the cargo metadata seems to have changed, and our tests are broken.

https://github.com/taiki-e/cargo-hack/runs/4876584559?check_suite_focus=true


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b4595216..ddfcf749 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,8 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com
 
 ## [Unreleased]
 
+- Fix breakage on nightly-2022-01-20 or later. ([#146](https://github.com/taiki-e/cargo-hack/pull/146))
+
 ## [0.5.10] - 2022-01-05
 
 - Fix handling of combined short flags. ([#143](https://github.com/taiki-e/cargo-hack/pull/143))
diff --git a/src/cargo.rs b/src/cargo.rs
index 0783bb25..500ba49a 100644
--- a/src/cargo.rs
+++ b/src/cargo.rs
@@ -8,11 +8,8 @@ pub(crate) fn minor_version(mut cmd: ProcessBuilder<'_>) -> Result<u32> {
     let output = cmd.read()?;
 
     // Find the release line in the verbose version output.
-    let release = output
-        .lines()
-        .find(|line| line.starts_with("release: "))
-        .map(|line| &line["release: ".len()..])
-        .ok_or_else(|| {
+    let release =
+        output.lines().find_map(|line| line.strip_prefix("release: ")).ok_or_else(|| {
             format_err!("could not find rustc release from output of {}: {}", cmd, output)
         })?;
 
diff --git a/src/cli.rs b/src/cli.rs
index 2672f608..a16cca20 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -118,15 +118,15 @@ impl Args {
         }
         let rest = raw_args.collect::<Result<Vec<_>>>()?;
 
-        let mut cargo_args = Vec::new();
+        let mut cargo_args = vec![];
         let mut subcommand: Option<String> = None;
 
         let mut manifest_path = None;
         let mut color = None;
 
-        let mut package = Vec::new();
-        let mut exclude = Vec::new();
-        let mut features = Vec::new();
+        let mut package = vec![];
+        let mut exclude = vec![];
+        let mut features = vec![];
 
         let mut workspace = false;
         let mut no_dev_deps = false;
@@ -142,14 +142,14 @@ impl Args {
         let mut version_step = None;
 
         let mut optional_deps = None;
-        let mut include_features = Vec::new();
+        let mut include_features = vec![];
         let mut include_deps_features = false;
 
-        let mut exclude_features = Vec::new();
+        let mut exclude_features = vec![];
         let mut exclude_no_default_features = false;
         let mut exclude_all_features = false;
 
-        let mut group_features: Vec<String> = Vec::new();
+        let mut group_features: Vec<String> = vec![];
         let mut depth = None;
 
         let mut verbose = 0;
@@ -211,12 +211,13 @@ impl Args {
             }
 
             match arg {
+                Long("color") => parse_opt!(color, true),
+                Long("target") => parse_opt!(target, true),
+
                 Long("manifest-path") => parse_opt!(manifest_path, false),
                 Long("depth") => parse_opt!(depth, false),
-                Long("color") => parse_opt!(color, true),
                 Long("version-range") => parse_opt!(version_range, false),
                 Long("version-step") => parse_opt!(version_step, false),
-                Long("target") => parse_opt!(target, true),
 
                 Short('p') | Long("package") => package.push(parser.value()?.parse()?),
                 Long("exclude") => exclude.push(parser.value()?.parse()?),
@@ -272,10 +273,10 @@ impl Args {
 
                 // detect similar arg
                 Long("each-features") => {
-                    similar_arg(&arg, subcommand.as_deref(), "--each-feature", None)?
+                    similar_arg(&arg, subcommand.as_deref(), "--each-feature", None)?;
                 }
                 Long("features-powerset") => {
-                    similar_arg(&arg, subcommand.as_deref(), "--feature-powerset", None)?
+                    similar_arg(&arg, subcommand.as_deref(), "--feature-powerset", None)?;
                 }
 
                 // propagated
@@ -532,10 +533,10 @@ impl Args {
         term::verbose::set(verbose != 0);
         // If `-vv` is passed, propagate `-v` to cargo.
         if verbose > 1 {
-            cargo_args.push(format!("-{}", "v".repeat(verbose - 1)))
+            cargo_args.push(format!("-{}", "v".repeat(verbose - 1)));
         }
 
-        Ok(Args {
+        Ok(Self {
             leading_args: cargo_args,
             trailing_args: rest,
 
@@ -581,7 +582,7 @@ fn has_z_flag(args: &[String], name: &str) -> bool {
         if arg == "-Z" {
             arg = iter.next().unwrap();
         } else if let Some(a) = arg.strip_prefix("-Z") {
-            arg = a
+            arg = a;
         } else {
             continue;
         }
diff --git a/src/features.rs b/src/features.rs
index 98ab0fab..9243a3f3 100644
--- a/src/features.rs
+++ b/src/features.rs
@@ -5,10 +5,11 @@ use std::{
 
 use crate::{metadata::Metadata, PackageId};
 
+#[derive(Debug)]
 pub(crate) struct Features {
     features: Vec<Feature>,
-    /// [package features len, package features + optional deps len]
-    len: [usize; 2],
+    optional_deps_start: usize,
+    deps_features_start: usize,
 }
 
 impl Features {
@@ -17,14 +18,19 @@ impl Features {
         let node = &metadata.resolve.nodes[id];
 
         let mut features = Vec::with_capacity(package.features.len());
+        let mut optional_deps = vec![];
 
-        for name in package.features.keys() {
-            features.push(name.into());
-        }
         for name in package.optional_deps() {
-            features.push(name.into());
+            optional_deps.push(name);
+        }
+        for name in package.features.keys() {
+            if !optional_deps.contains(&&**name) {
+                features.push(name.into());
+            }
         }
-        let len = [package.features.len(), features.len()];
+        let optional_deps_start = features.len();
+        features.extend(optional_deps.into_iter().map(Into::into));
+        let deps_features_start = features.len();
 
         // TODO: Unpublished dependencies are not included in `node.deps`.
         for dep in node.deps.iter().filter(|dep| {
@@ -42,19 +48,19 @@ impl Features {
             // TODO: Optional deps of `dep_package`.
         }
 
-        Self { features, len }
+        Self { features, optional_deps_start, deps_features_start }
     }
 
     pub(crate) fn normal(&self) -> &[Feature] {
-        &self.features[..self.len[0]]
+        &self.features[..self.optional_deps_start]
     }
 
     pub(crate) fn optional_deps(&self) -> &[Feature] {
-        &self.features[self.len[0]..self.len[1]]
+        &self.features[self.optional_deps_start..self.deps_features_start]
     }
 
     pub(crate) fn deps_features(&self) -> &[Feature] {
-        &self.features[self.len[1]..]
+        &self.features[self.deps_features_start..]
     }
 
     pub(crate) fn contains(&self, name: &str) -> bool {
@@ -66,13 +72,11 @@ impl Features {
 #[derive(Debug)]
 pub(crate) enum Feature {
     /// A feature of the current crate.
-    #[allow(dead_code)] // false positive that fixed in Rust 1.44
     Normal {
         /// Feature name. It is considered indivisible.
         name: String,
     },
     /// Grouped features.
-    #[allow(dead_code)] // false positive that fixed in Rust 1.44
     Group {
         /// Feature name concatenated with `,`.
         name: String,
@@ -80,12 +84,11 @@ pub(crate) enum Feature {
         list: Vec<String>,
     },
     /// A feature of a dependency.
-    #[allow(dead_code)] // false positive that fixed in Rust 1.44
     Path {
         /// Feature path separated with `/`.
         name: String,
         /// Index of `/`.
-        slash: usize,
+        _slash: usize,
     },
 }
 
@@ -96,7 +99,7 @@ impl Feature {
     }
 
     pub(crate) fn path(parent: &str, name: &str) -> Self {
-        Self::Path { name: format!("{}/{}", parent, name), slash: parent.len() }
+        Self::Path { name: format!("{}/{}", parent, name), _slash: parent.len() }
     }
 
     pub(crate) fn name(&self) -> &str {
diff --git a/src/main.rs b/src/main.rs
index a2b77f70..a572c522 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,11 +1,6 @@
 #![forbid(unsafe_code)]
 #![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
-#![warn(
-    clippy::default_trait_access,
-    clippy::disallowed_methods,
-    clippy::disallowed_types,
-    clippy::wildcard_imports
-)]
+#![warn(clippy::default_trait_access, clippy::wildcard_imports)]
 
 #[macro_use]
 mod term;
@@ -150,27 +145,26 @@ fn determine_kind<'a>(cx: &'a Context, id: &PackageId, progress: &mut Progress)
     }
 
     let package = cx.packages(id);
+    let pkg_features = cx.pkg_features(id);
     let filter = |&f: &&Feature| {
         !cx.exclude_features.iter().any(|s| f == s)
             && !cx.group_features.iter().any(|g| g.matches(f.name()))
     };
     let features = if cx.include_features.is_empty() {
-        let feature_list = cx.pkg_features(id);
-
         cx.exclude_features.iter().for_each(|d| {
-            if !feature_list.contains(d) {
+            if !pkg_features.contains(d) {
                 warn!("specified feature `{}` not found in package `{}`", d, package.name);
             }
         });
 
-        let mut features: Vec<_> = feature_list.normal().iter().filter(filter).collect();
+        let mut features: Vec<_> = pkg_features.normal().iter().filter(filter).collect();
 
         if let Some(opt_deps) = &cx.optional_deps {
             if opt_deps.len() == 1 && opt_deps[0].is_empty() {
                 // --optional-deps=
             } else {
                 for d in opt_deps {
-                    if !feature_list.optional_deps().iter().any(|f| f == d) {
+                    if !pkg_features.optional_deps().iter().any(|f| f == d) {
                         warn!(
                             "specified optional dependency `{}` not found in package `{}`",
                             d, package.name
@@ -179,13 +173,13 @@ fn determine_kind<'a>(cx: &'a Context, id: &PackageId, progress: &mut Progress)
                 }
             }
 
-            features.extend(feature_list.optional_deps().iter().filter(|f| {
+            features.extend(pkg_features.optional_deps().iter().filter(|f| {
                 filter(f) && (opt_deps.is_empty() || opt_deps.iter().any(|x| *f == x))
             }));
         }
 
         if cx.include_deps_features {
-            features.extend(feature_list.deps_features().iter().filter(filter));
+            features.extend(pkg_features.deps_features().iter().filter(filter));
         }
 
         if !cx.group_features.is_empty() {
@@ -198,21 +192,27 @@ fn determine_kind<'a>(cx: &'a Context, id: &PackageId, progress: &mut Progress)
     };
 
     if cx.each_feature {
-        if (package.features.is_empty() || !cx.include_features.is_empty()) && features.is_empty() {
+        if (pkg_features.normal().is_empty() && pkg_features.optional_deps().is_empty()
+            || !cx.include_features.is_empty())
+            && features.is_empty()
+        {
             progress.total += 1;
             Kind::Normal
         } else {
             progress.total += features.len()
                 + !cx.exclude_no_default_features as usize
                 + (!cx.exclude_all_features
-                    && package.features.len() + package.optional_deps().count() > 1)
+                    && pkg_features.normal().len() + pkg_features.optional_deps().len() > 1)
                     as usize;
             Kind::Each { features }
         }
     } else if cx.feature_powerset {
         let features = features::feature_powerset(features, cx.depth, &package.features);
 
-        if (package.features.is_empty() || !cx.include_features.is_empty()) && features.is_empty() {
+        if (pkg_features.normal().is_empty() && pkg_features.optional_deps().is_empty()
+            || !cx.include_features.is_empty())
+            && features.is_empty()
+        {
             progress.total += 1;
             Kind::Normal
         } else {
@@ -220,7 +220,7 @@ fn determine_kind<'a>(cx: &'a Context, id: &PackageId, progress: &mut Progress)
             progress.total += features.len() - 1
                 + !cx.exclude_no_default_features as usize
                 + (!cx.exclude_all_features
-                    && package.features.len() + package.optional_deps().count() > 1)
+                    && pkg_features.normal().len() + pkg_features.optional_deps().len() > 1)
                     as usize;
             Kind::Powerset { features }
         }
@@ -355,8 +355,10 @@ fn exec_actual(
         _ => unreachable!(),
     }
 
-    let pkg = cx.packages(id);
-    if !cx.exclude_all_features && pkg.features.len() + pkg.optional_deps().count() > 1 {
+    let pkg_features = cx.pkg_features(id);
+    if !cx.exclude_all_features
+        && pkg_features.normal().len() + pkg_features.optional_deps().len() > 1
+    {
         // run with all features
         // https://github.com/taiki-e/cargo-hack/issues/42
         line.arg("--all-features");
diff --git a/src/term.rs b/src/term.rs
index 3c9ff801..53a414af 100644
--- a/src/term.rs
+++ b/src/term.rs
@@ -17,9 +17,9 @@ enum Coloring {
 }
 
 impl Coloring {
-    const AUTO: u8 = Coloring::Auto as _;
-    const ALWAYS: u8 = Coloring::Always as _;
-    const NEVER: u8 = Coloring::Never as _;
+    const AUTO: u8 = Self::Auto as _;
+    const ALWAYS: u8 = Self::Always as _;
+    const NEVER: u8 = Self::Never as _;
 }
 
 impl FromStr for Coloring {


Test patch:
diff --git a/tests/auxiliary/mod.rs b/tests/auxiliary/mod.rs
index d3fb8a36..bac48a59 100644
--- a/tests/auxiliary/mod.rs
+++ b/tests/auxiliary/mod.rs
@@ -1,11 +1,11 @@
 use std::{
-    env,
+    env::{self, consts::EXE_SUFFIX},
     ffi::OsStr,
     path::{Path, PathBuf},
     process::{Command, ExitStatus},
 };
 
-use anyhow::Result;
+use anyhow::{Context as _, Result};
 use easy_ext::ext;
 use fs_err as fs;
 use once_cell::sync::Lazy;
@@ -25,7 +25,7 @@ pub fn cargo_bin_exe() -> Command {
 
 fn test_toolchain() -> String {
     if let Some(toolchain) = test_version() {
-        format!("+1.{} ", toolchain)
+        format!("+1.{toolchain} ")
     } else {
         String::new()
     }
@@ -37,7 +37,7 @@ fn test_version() -> Option<u32> {
             env::var_os("CARGO_HACK_TEST_TOOLCHAIN")?.to_string_lossy().parse().unwrap();
         // Install toolchain first to avoid toolchain installation conflicts.
         let _ = Command::new("rustup")
-            .args(&["toolchain", "install", &format!("1.{}", toolchain), "--no-self-update"])
+            .args(&["toolchain", "install", &format!("1.{toolchain}"), "--no-self-update"])
             .output();
         Some(toolchain)
     });
@@ -58,7 +58,7 @@ pub fn cargo_hack<O: AsRef<OsStr>>(args: impl AsRef<[O]>) -> Command {
     cmd.arg("hack");
     if let Some(toolchain) = test_version() {
         if !args.iter().any(|a| a.as_ref().to_str().unwrap().starts_with("--version-range")) {
-            cmd.arg(format!("--version-range=1.{0}..1.{0}", toolchain));
+            cmd.arg(format!("--version-range=1.{toolchain}..1.{toolchain}"));
         }
     }
     cmd.args(args);
@@ -76,10 +76,8 @@ impl Command {
             _ => {}
         }
         let (_test_project, cur_dir) = test_project(test_model).unwrap();
-        let output = self
-            .current_dir(cur_dir)
-            .output()
-            .unwrap_or_else(|e| panic!("could not execute process: {}", e));
+        let output =
+            self.current_dir(cur_dir).output().context("could not execute process").unwrap();
         AssertOutput(Some(AssertOutputInner {
             stdout: String::from_utf8_lossy(&output.stdout).into_owned(),
             stderr: String::from_utf8_lossy(&output.stderr).into_owned(),
@@ -140,9 +138,7 @@ struct AssertOutputInner {
 
 #[track_caller]
 fn line_separated(lines: &str, f: impl FnMut(&str)) {
-    let lines = if lines.contains("cargo +")
-        || lines.contains(&format!("cargo{} +", env::consts::EXE_SUFFIX))
-    {
+    let lines = if lines.contains("cargo +") || lines.contains(&format!("cargo{EXE_SUFFIX} +")) {
         lines.to_string()
     } else {
         lines.replace("cargo ", &format!("cargo {}", test_toolchain()))
@@ -158,9 +154,8 @@ impl AssertOutput {
             line_separated(pats.as_ref(), |pat| {
                 if !output.stderr.contains(pat) {
                     panic!(
-                        "assertion failed: `self.stderr.contains(..)`:\n\nEXPECTED:\n{0}\n{1}\n{0}\n\nACTUAL:\n{0}\n{2}\n{0}\n",
+                        "assertion failed: `self.stderr.contains(..)`:\n\nEXPECTED:\n{0}\n{pat}\n{0}\n\nACTUAL:\n{0}\n{1}\n{0}\n",
                         "-".repeat(60),
-                        pat,
                         output.stderr
                     );
                 }
@@ -176,9 +171,8 @@ impl AssertOutput {
             line_separated(pats.as_ref(), |pat| {
                 if output.stderr.contains(pat) {
                     panic!(
-                        "assertion failed: `!self.stderr.contains(..)`:\n\nEXPECTED:\n{0}\n{1}\n{0}\n\nACTUAL:\n{0}\n{2}\n{0}\n",
+                        "assertion failed: `!self.stderr.contains(..)`:\n\nEXPECTED:\n{0}\n{pat}\n{0}\n\nACTUAL:\n{0}\n{1}\n{0}\n",
                         "-".repeat(60),
-                        pat,
                         output.stderr
                     );
                 }
@@ -194,9 +188,8 @@ impl AssertOutput {
             line_separated(pats.as_ref(), |pat| {
                 if !output.stdout.contains(pat) {
                     panic!(
-                        "assertion failed: `self.stdout.contains(..)`:\n\nEXPECTED:\n{0}\n{1}\n{0}\n\nACTUAL:\n{0}\n{2}\n{0}\n",
+                        "assertion failed: `self.stdout.contains(..)`:\n\nEXPECTED:\n{0}\n{pat}\n{0}\n\nACTUAL:\n{0}\n{1}\n{0}\n",
                         "-".repeat(60),
-                        pat,
                         output.stdout
                     );
                 }
@@ -212,9 +205,8 @@ impl AssertOutput {
             line_separated(pats.as_ref(), |pat| {
                 if output.stdout.contains(pat) {
                     panic!(
-                        "assertion failed: `!self.stdout.contains(..)`:\n\nEXPECTED:\n{0}\n{1}\n{0}\n\nACTUAL:\n{0}\n{2}\n{0}\n",
+                        "assertion failed: `!self.stdout.contains(..)`:\n\nEXPECTED:\n{0}\n{pat}\n{0}\n\nACTUAL:\n{0}\n{1}\n{0}\n",
                         "-".repeat(60),
-                        pat,
                         output.stdout
                     );
                 }
@@ -224,7 +216,7 @@ impl AssertOutput {
     }
 }
 
-pub fn target_triple() -> String {
+pub static TARGET_TRIPLE: Lazy<String> = Lazy::new(|| {
     let triple = env::consts::ARCH.to_string();
     if cfg!(target_os = "macos") {
         triple + "-apple-darwin"
@@ -243,7 +235,7 @@ pub fn target_triple() -> String {
     } else {
         unreachable!()
     }
-}
+});
 
 fn test_project(model: &str) -> Result<(TempDir, PathBuf)> {
     let tmpdir = tempfile::tempdir()?;
diff --git a/tests/fixtures/default_feature_behavior/has_default/src/lib.rs b/tests/fixtures/default_feature_behavior/has_default/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/default_feature_behavior/no_default/src/lib.rs b/tests/fixtures/default_feature_behavior/no_default/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/keep_going/src/lib.rs b/tests/fixtures/keep_going/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/optional_deps/src/lib.rs b/tests/fixtures/optional_deps/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/package_collision/member1/src/lib.rs b/tests/fixtures/package_collision/member1/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/package_collision/member2/src/lib.rs b/tests/fixtures/package_collision/member2/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/powerset_deduplication/member1/src/lib.rs b/tests/fixtures/powerset_deduplication/member1/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/powerset_deduplication/src/lib.rs b/tests/fixtures/powerset_deduplication/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/real/member1/src/lib.rs b/tests/fixtures/real/member1/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/real/member2/src/lib.rs b/tests/fixtures/real/member2/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/real/member3/src/lib.rs b/tests/fixtures/real/member3/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/real/src/lib.rs b/tests/fixtures/real/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/virtual/dir/not_find_manifest/src/lib.rs b/tests/fixtures/virtual/dir/not_find_manifest/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/virtual/member1/src/lib.rs b/tests/fixtures/virtual/member1/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/fixtures/virtual/member2/src/lib.rs b/tests/fixtures/virtual/member2/src/lib.rs
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/test.rs b/tests/test.rs
index a9c67fda..83430911 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -7,7 +7,7 @@ use std::{
     path::MAIN_SEPARATOR,
 };
 
-use auxiliary::{cargo_bin_exe, cargo_hack, has_stable_toolchain, target_triple, CommandExt};
+use auxiliary::{cargo_bin_exe, cargo_hack, has_stable_toolchain, CommandExt, TARGET_TRIPLE};
 
 #[test]
 fn failures() {
@@ -61,7 +61,7 @@ fn removed_flags() {
     ] {
         cargo_hack(["check", flag])
             .assert_failure("real")
-            .stderr_contains(format!("{} was removed, use {} instead", flag, alt));
+            .stderr_contains(format!("{flag} was removed, use {alt} instead"));
     }
 }
 
@@ -283,14 +283,13 @@ fn no_dev_deps_failure() {
     {
         cargo_hack(["check", "--no-dev-deps", flag])
             .assert_failure("real")
-            .stderr_contains(format!("--no-dev-deps may not be used together with {}", flag));
+            .stderr_contains(format!("--no-dev-deps may not be used together with {flag}"));
     }
 
     // with subcommands requires dev-deps
     for subcommand in &["test", "bench"] {
         cargo_hack([subcommand, "--no-dev-deps"]).assert_failure("real").stderr_contains(format!(
-            "--no-dev-deps may not be used together with {} subcommand",
-            subcommand
+            "--no-dev-deps may not be used together with {subcommand} subcommand"
         ));
     }
 }
@@ -303,13 +302,13 @@ fn remove_dev_deps_failure() {
     {
         cargo_hack(["check", "--remove-dev-deps", flag])
             .assert_failure("real")
-            .stderr_contains(format!("--remove-dev-deps may not be used together with {}", flag));
+            .stderr_contains(format!("--remove-dev-deps may not be used together with {flag}"));
     }
 
     // with subcommands requires dev-deps
     for subcommand in &["test", "bench"] {
         cargo_hack([subcommand, "--remove-dev-deps"]).assert_failure("real").stderr_contains(
-            format!("--remove-dev-deps may not be used together with {} subcommand", subcommand),
+            format!("--remove-dev-deps may not be used together with {subcommand} subcommand"),
         );
     }
 }
@@ -1084,10 +1083,14 @@ fn optional_deps() {
 
     cargo_hack(["check", "--each-feature"])
         .assert_success2("optional_deps", Some(31))
-        .stderr_contains("running `cargo check` on optional_deps (1/1)")
+        .stderr_contains(
+            "
+            running `cargo check --no-default-features` on optional_deps (1/2)
+            running `cargo check --no-default-features --all-features` on optional_deps (2/2)
+            ",
+        )
         .stderr_not_contains(
             "
-            --no-default-features
             --features real
             --features renemed
             ",
@@ -1128,7 +1131,12 @@ fn optional_deps() {
 
     cargo_hack(["check", "--each-feature", "--optional-deps="])
         .assert_success2("optional_deps", Some(31))
-        .stderr_contains("running `cargo check` on optional_deps (1/1)");
+        .stderr_contains(
+            "
+            running `cargo check --no-default-features` on optional_deps (1/2)
+            running `cargo check --no-default-features --all-features` on optional_deps (2/2)
+            ",
+        );
 }
 
 #[test]
@@ -1192,9 +1200,9 @@ fn short_flag() {
         .assert_success("virtual")
         .stderr_contains(format!(
             "
-            cargo{1} check -v --manifest-path member1{0}Cargo.toml` (1/1)
+            cargo{EXE_SUFFIX} check -v --manifest-path member1{0}Cargo.toml` (1/1)
             ",
-            MAIN_SEPARATOR, EXE_SUFFIX
+            MAIN_SEPARATOR
         ))
         .stderr_not_contains("member2");
 
@@ -1208,28 +1216,28 @@ fn short_flag() {
 fn verbose() {
     cargo_hack(["check", "--verbose"]).assert_success("virtual").stderr_contains(format!(
         "
-        cargo{1} check --manifest-path member1{0}Cargo.toml` (1/3)
-        cargo{1} check --manifest-path member2{0}Cargo.toml` (2/3)
-        cargo{1} check --manifest-path dir{0}not_find_manifest{0}Cargo.toml` (3/3)
+        cargo{EXE_SUFFIX} check --manifest-path member1{0}Cargo.toml` (1/3)
+        cargo{EXE_SUFFIX} check --manifest-path member2{0}Cargo.toml` (2/3)
+        cargo{EXE_SUFFIX} check --manifest-path dir{0}not_find_manifest{0}Cargo.toml` (3/3)
         ",
-        MAIN_SEPARATOR, EXE_SUFFIX
+        MAIN_SEPARATOR,
     ));
 
     // If `-vv` is passed, propagate `-v` to cargo.
     cargo_hack(["check", "-vv", "-p", "member1"]).assert_success("virtual").stderr_contains(
         format!(
             "
-            cargo{1} check -v --manifest-path member1{0}Cargo.toml` (1/1)
+            cargo{EXE_SUFFIX} check -v --manifest-path member1{0}Cargo.toml` (1/1)
             ",
-            MAIN_SEPARATOR, EXE_SUFFIX
+            MAIN_SEPARATOR,
         ),
     );
     cargo_hack(["check", "-vvv", "-p", "member1"]).assert_success("virtual").stderr_contains(
         format!(
             "
-            cargo{1} check -vv --manifest-path member1{0}Cargo.toml` (1/1)
+            cargo{EXE_SUFFIX} check -vv --manifest-path member1{0}Cargo.toml` (1/1)
             ",
-            MAIN_SEPARATOR, EXE_SUFFIX
+            MAIN_SEPARATOR,
         ),
     );
 }
@@ -1259,9 +1267,9 @@ fn propagate() {
         .stderr_contains("`cargo check --color auto`");
 
     // --target
-    cargo_hack(["check", "--target", &target_triple()])
+    cargo_hack(["check", "--target", &TARGET_TRIPLE])
         .assert_success("real")
-        .stderr_contains(format!("`cargo check --target {}`", target_triple()));
+        .stderr_contains(format!("`cargo check --target {}`", *TARGET_TRIPLE));
 
     // --verbose does not be propagated
     cargo_hack(["check", "--verbose"]).assert_success("real").stderr_not_contains("--verbose");
@@ -1301,14 +1309,14 @@ fn version_range() {
         ",
     );
 
-    cargo_hack(["check", "--version-range", "1.36..1.37", "--target", &target_triple()])
+    cargo_hack(["check", "--version-range", "1.36..1.37", "--target", &TARGET_TRIPLE])
         .assert_success("real")
         .stderr_contains(format!(
             "
             running `cargo +1.36 check --target {0}` on real (1/2)
             running `cargo +1.37 check --target {0}` on real (2/2)
             ",
-            target_triple()
+            *TARGET_TRIPLE
         ));
 
     if cfg!(target_os = "linux") {
@@ -1384,9 +1392,8 @@ fn keep_going() {
             failed to run 2 commands
             failed commands:
             keep_going:
-            cargo{0} check --manifest-path Cargo.toml --no-default-features`
-            cargo{0} check --manifest-path Cargo.toml --no-default-features --features a`
+            cargo{EXE_SUFFIX} check --manifest-path Cargo.toml --no-default-features`
+            cargo{EXE_SUFFIX} check --manifest-path Cargo.toml --no-default-features --features a`
             ",
-            EXE_SUFFIX
         ));
 }