Skip to content

Commit b3b25ab

Browse files
committed
Auto merge of #8395 - ehuss:fix-opt-dep-order, r=Eh2406
Fix order-dependent feature resolution. There is a situation where if you have `pkg/feature` syntax, and `pkg` is an optional dependency, but also a dev-dependency, and the dev-dependency appears before the (optional) normal dependency in the summary, then the optional dependency would not get activated. This is because the feature code used `find` to get the first entry. Fixes #8394
2 parents 442de29 + ab73b2c commit b3b25ab

File tree

2 files changed

+81
-6
lines changed

2 files changed

+81
-6
lines changed

src/cargo/core/resolver/dep_cache.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -402,15 +402,13 @@ impl Requirements<'_> {
402402
// If `package` is indeed an optional dependency then we activate the
403403
// feature named `package`, but otherwise if `package` is a required
404404
// dependency then there's no feature associated with it.
405-
if let Some(dep) = self
405+
if self
406406
.summary
407407
.dependencies()
408408
.iter()
409-
.find(|p| p.name_in_toml() == package)
409+
.any(|dep| dep.name_in_toml() == package && dep.is_optional())
410410
{
411-
if dep.is_optional() {
412-
self.used.insert(package);
413-
}
411+
self.used.insert(package);
414412
}
415413
self.deps
416414
.entry(package)

tests/testsuite/features.rs

+78-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Tests for `[features]` table.
22
33
use cargo_test_support::paths::CargoPathExt;
4-
use cargo_test_support::registry::Package;
4+
use cargo_test_support::registry::{Dependency, Package};
55
use cargo_test_support::{basic_manifest, project};
66

77
#[cargo_test]
@@ -2113,3 +2113,80 @@ fn slash_optional_enables() {
21132113

21142114
p.cargo("check --features dep/feat").run();
21152115
}
2116+
2117+
#[cargo_test]
2118+
fn registry_summary_order_doesnt_matter() {
2119+
// Checks for an issue where the resolver depended on the order of entries
2120+
// in the registry summary. If there was a non-optional dev-dependency
2121+
// that appeared before an optional normal dependency, then the resolver
2122+
// would not activate the optional dependency with a pkg/featname feature
2123+
// syntax.
2124+
Package::new("dep", "0.1.0")
2125+
.feature("feat1", &[])
2126+
.file(
2127+
"src/lib.rs",
2128+
r#"
2129+
#[cfg(feature="feat1")]
2130+
pub fn work() {
2131+
println!("it works");
2132+
}
2133+
"#,
2134+
)
2135+
.publish();
2136+
Package::new("bar", "0.1.0")
2137+
.feature("bar_feat", &["dep/feat1"])
2138+
.add_dep(Dependency::new("dep", "0.1.0").dev())
2139+
.add_dep(Dependency::new("dep", "0.1.0").optional(true))
2140+
.file(
2141+
"src/lib.rs",
2142+
r#"
2143+
// This will fail to compile without `dep` optional dep activated.
2144+
extern crate dep;
2145+
2146+
pub fn doit() {
2147+
dep::work();
2148+
}
2149+
"#,
2150+
)
2151+
.publish();
2152+
2153+
let p = project()
2154+
.file(
2155+
"Cargo.toml",
2156+
r#"
2157+
[package]
2158+
name = "foo"
2159+
version = "0.1.0"
2160+
edition = "2018"
2161+
2162+
[dependencies]
2163+
bar = { version="0.1", features = ["bar_feat"] }
2164+
"#,
2165+
)
2166+
.file(
2167+
"src/main.rs",
2168+
r#"
2169+
fn main() {
2170+
bar::doit();
2171+
}
2172+
"#,
2173+
)
2174+
.build();
2175+
2176+
p.cargo("run")
2177+
.with_stderr(
2178+
"\
2179+
[UPDATING] [..]
2180+
[DOWNLOADING] crates ...
2181+
[DOWNLOADED] [..]
2182+
[DOWNLOADED] [..]
2183+
[COMPILING] dep v0.1.0
2184+
[COMPILING] bar v0.1.0
2185+
[COMPILING] foo v0.1.0 [..]
2186+
[FINISHED] [..]
2187+
[RUNNING] `target/debug/foo[EXE]`
2188+
",
2189+
)
2190+
.with_stdout("it works")
2191+
.run();
2192+
}

0 commit comments

Comments
 (0)