diff --git a/src/cargo/core/resolver/dep_cache.rs b/src/cargo/core/resolver/dep_cache.rs index 4fd27538555..54b0ce97ff2 100644 --- a/src/cargo/core/resolver/dep_cache.rs +++ b/src/cargo/core/resolver/dep_cache.rs @@ -455,7 +455,12 @@ impl Requirements<'_> { .iter() .any(|dep| dep.name_in_toml() == package && dep.is_optional()) { - self.require_feature(package)?; + // This optional dependency may not have an implicit feature of + // the same name if the `dep:` syntax is used to avoid creating + // that implicit feature. + if self.summary.features().contains_key(&package) { + self.require_feature(package)?; + } } self.deps.entry(package).or_default().insert(feat); Ok(()) diff --git a/src/cargo/core/resolver/features.rs b/src/cargo/core/resolver/features.rs index e2f2bd5c50c..6b79722caed 100644 --- a/src/cargo/core/resolver/features.rs +++ b/src/cargo/core/resolver/features.rs @@ -717,7 +717,15 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { // The old behavior before weak dependencies were // added is to also enables a feature of the same // name. - self.activate_rec(pkg_id, fk, dep_name)?; + // + // Don't enable if the implicit optional dependency + // feature wasn't created due to `dep:` hiding. + // See rust-lang/cargo#10788 and rust-lang/cargo#12130 + let summary = self.resolve.summary(pkg_id); + let feature_map = summary.features(); + if feature_map.contains_key(&dep_name) { + self.activate_rec(pkg_id, fk, dep_name)?; + } } } // Activate the feature on the dependency. diff --git a/tests/testsuite/features_namespaced.rs b/tests/testsuite/features_namespaced.rs index a6047231beb..f24186c1591 100644 --- a/tests/testsuite/features_namespaced.rs +++ b/tests/testsuite/features_namespaced.rs @@ -1216,3 +1216,81 @@ Caused by: ) .run(); } + +#[cargo_test] +fn dep_feature_when_hidden() { + // Checks for behavior with dep:bar and bar/feat syntax when there is no + // `bar` feature. + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = { path = "bar", optional = true } + + [features] + f1 = ["dep:bar"] + f2 = ["bar/bar_feat"] + "#, + ) + .file("src/lib.rs", "") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.1.0" + + [features] + bar_feat = [] + "#, + ) + .file("bar/src/lib.rs", "") + .build(); + + p.cargo("tree -f") + .arg("{p} features={f}") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) features=", + ) + .with_stderr("") + .run(); + + p.cargo("tree -F f1 -f") + .arg("{p} features={f}") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) features=f1 +└── bar v0.1.0 ([ROOT]/foo/bar) features= +", + ) + .with_stderr("") + .run(); + + p.cargo("tree -F f2 -f") + .arg("{p} features={f}") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) features=f2 +└── bar v0.1.0 ([ROOT]/foo/bar) features=bar_feat +", + ) + .with_stderr("") + .run(); + + p.cargo("tree --all-features -f") + .arg("{p} features={f}") + .with_stdout( + "\ +foo v0.1.0 ([ROOT]/foo) features=f1,f2 +└── bar v0.1.0 ([ROOT]/foo/bar) features=bar_feat +", + ) + .with_stderr("") + .run(); +}