From 21c09286383a3aca7701771f2c0801e29f470d20 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 5 Jun 2024 16:00:39 -0500 Subject: [PATCH] fix(fix): Ensure optional dep is available for dep-features --- src/cargo/ops/fix.rs | 39 ++++++++++++++++++++++++++++++++++++--- tests/testsuite/fix.rs | 8 ++++---- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs index 0e45e164418..df354b36e4b 100644 --- a/src/cargo/ops/fix.rs +++ b/src/cargo/ops/fix.rs @@ -285,7 +285,7 @@ fn migrate_manifests(ws: &Workspace<'_>, pkgs: &[&Package]) -> CargoResult<()> { fixes += rename_dep_fields_2024(workspace, "dependencies"); } - fixes += add_feature_for_unused_deps(pkg, root); + fixes += add_feature_for_unused_deps(pkg, root, ws.gctx()); fixes += rename_table(root, "project", "package"); if let Some(target) = root.get_mut("lib").and_then(|t| t.as_table_like_mut()) { fixes += rename_target_fields_2024(target); @@ -435,7 +435,11 @@ fn rename_table(parent: &mut dyn toml_edit::TableLike, old: &str, new: &str) -> 1 } -fn add_feature_for_unused_deps(pkg: &Package, parent: &mut dyn toml_edit::TableLike) -> usize { +fn add_feature_for_unused_deps( + pkg: &Package, + parent: &mut dyn toml_edit::TableLike, + gctx: &GlobalContext, +) -> usize { let manifest = pkg.manifest(); let activated_opt_deps = manifest @@ -461,14 +465,43 @@ fn add_feature_for_unused_deps(pkg: &Package, parent: &mut dyn toml_edit::TableL .or_insert(toml_edit::table()) .as_table_like_mut() { + let activate_dep = format!("dep:{dep_name_in_toml}"); + let strong_dep_feature_prefix = format!("{dep_name_in_toml}/"); features .entry(dep_name_in_toml.as_str()) .or_insert_with(|| { fixes += 1; toml_edit::Item::Value(toml_edit::Value::Array( - toml_edit::Array::from_iter(&[format!("dep:{}", dep_name_in_toml)]), + toml_edit::Array::from_iter([&activate_dep]), )) }); + // Ensure `dep:dep_name` is present for `dep_name/feature_name` since `dep:` is the + // only way to guarantee an optional dependency is available for use. + // + // The way we avoid implicitly creating features in Edition2024 is we remove the + // dependency from `resolved_toml` if there is no `dep:` syntax as that is the only + // syntax that suppresses the creation of the implicit feature. + for (feature_name, activations) in features.iter_mut() { + let Some(activations) = activations.as_array_mut() else { + let _ = gctx.shell().warn(format_args!("skipping fix of feature `{feature_name}` in package `{}`: unsupported feature schema", pkg.name())); + continue; + }; + if activations + .iter() + .any(|a| a.as_str().map(|a| a == activate_dep).unwrap_or(false)) + { + continue; + } + let Some(activate_dep_pos) = activations.iter().position(|a| { + a.as_str() + .map(|a| a.starts_with(&strong_dep_feature_prefix)) + .unwrap_or(false) + }) else { + continue; + }; + fixes += 1; + activations.insert(activate_dep_pos, &activate_dep); + } } } } diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs index 0c3d7793ade..a86acb80ea8 100644 --- a/tests/testsuite/fix.rs +++ b/tests/testsuite/fix.rs @@ -2705,7 +2705,7 @@ unrelated-dep-feature = ["unrelated-feature/a", "unrelated-feature/b"] .with_stderr( "\ [MIGRATING] Cargo.toml from 2021 edition to 2024 -[FIXED] Cargo.toml (1 fix) +[FIXED] Cargo.toml (4 fixes) [UPDATING] `dummy-registry` index [LOCKING] 5 packages to latest compatible versions [CHECKING] foo v0.1.0 ([CWD]) @@ -2730,11 +2730,11 @@ renamed-feature = { version = "0.1.0", optional = true } unrelated-feature = { version = "0.1.0", optional = true } [features] -dep-feature = ["dep-feature/a", "dep-feature/b"] +dep-feature = [ "dep:dep-feature","dep-feature/a", "dep-feature/b"] dep-and-dep-feature = ["dep:dep-and-dep-feature", "dep-and-dep-feature/a", "dep-and-dep-feature/b"] -renamed = ["renamed-feature/a", "renamed-feature/b"] +renamed = [ "dep:renamed-feature","renamed-feature/a", "renamed-feature/b"] unrelated-feature = [] -unrelated-dep-feature = ["unrelated-feature/a", "unrelated-feature/b"] +unrelated-dep-feature = [ "dep:unrelated-feature","unrelated-feature/a", "unrelated-feature/b"] renamed-feature = ["dep:renamed-feature"] "#]],