Fix resolve error with cyclic dev-dependency features.

There is a convoluted situation where a cyclic dev-dependency attempts
to enable a feature on its parent. It would result in a confusing error
saying a package didn't have a feature.

This check was intended only for CLI features, not features passed
through the dependency graph.
This commit is contained in:
Eric Huss 2021-11-19 18:10:10 -08:00
parent d7ab4a6598
commit 2eee644c68
2 changed files with 116 additions and 4 deletions

View file

@ -280,10 +280,12 @@ pub fn resolve_features<'b>(
// dep_name/feat_name` where `dep_name` does not exist. All other
// validation is done either in `build_requirements` or
// `build_feature_map`.
for dep_name in reqs.deps.keys() {
if !valid_dep_names.contains(dep_name) {
let e = RequirementError::MissingDependency(*dep_name);
return Err(e.into_activate_error(parent, s));
if parent.is_none() {
for dep_name in reqs.deps.keys() {
if !valid_dep_names.contains(dep_name) {
let e = RequirementError::MissingDependency(*dep_name);
return Err(e.into_activate_error(parent, s));
}
}
}

View file

@ -1937,3 +1937,113 @@ foo v1.0.0 ([ROOT]/foo)
)
.run();
}
#[cargo_test]
fn dev_dep_cycle_with_feature_nested() {
// Checks for an issue where a cyclic dev dependency tries to activate a
// feature on its parent that tries to activate the feature back on the
// dev-dependency.
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "1.0.0"
[dev-dependencies]
bar = { path = "bar" }
[features]
a = ["bar/feat1"]
b = ["a"]
"#,
)
.file("src/lib.rs", "")
.file(
"bar/Cargo.toml",
r#"
[package]
name = "bar"
version = "1.0.0"
[dependencies]
foo = { path = ".." }
[features]
feat1 = ["foo/b"]
"#,
)
.file("bar/src/lib.rs", "")
.build();
p.cargo("tree -e features")
.with_stdout(
"\
foo v1.0.0 ([ROOT]/foo)
[dev-dependencies]
bar feature \"default\"
bar v1.0.0 ([ROOT]/foo/bar)
foo feature \"default\" (command-line)
foo v1.0.0 ([ROOT]/foo) (*)
",
)
.run();
p.cargo("tree -e features --features a -i foo")
.with_stdout(
"\
foo v1.0.0 ([ROOT]/foo)
foo feature \"a\" (command-line)
foo feature \"b\"
bar feature \"feat1\"
foo feature \"a\" (command-line) (*)
foo feature \"b\" (*)
foo feature \"default\" (command-line)
bar v1.0.0 ([ROOT]/foo/bar)
bar feature \"default\"
[dev-dependencies]
foo v1.0.0 ([ROOT]/foo) (*)
bar feature \"feat1\" (*)
",
)
.run();
p.cargo("tree -e features --features b -i foo")
.with_stdout(
"\
foo v1.0.0 ([ROOT]/foo)
foo feature \"a\"
foo feature \"b\" (command-line)
bar feature \"feat1\"
foo feature \"a\" (*)
foo feature \"b\" (command-line) (*)
foo feature \"default\" (command-line)
bar v1.0.0 ([ROOT]/foo/bar)
bar feature \"default\"
[dev-dependencies]
foo v1.0.0 ([ROOT]/foo) (*)
bar feature \"feat1\" (*)
",
)
.run();
p.cargo("tree -e features --features bar/feat1 -i foo")
.with_stdout(
"\
foo v1.0.0 ([ROOT]/foo)
foo feature \"a\"
foo feature \"b\"
bar feature \"feat1\" (command-line)
foo feature \"a\" (*)
foo feature \"b\" (*)
foo feature \"default\" (command-line)
bar v1.0.0 ([ROOT]/foo/bar)
bar feature \"default\"
[dev-dependencies]
foo v1.0.0 ([ROOT]/foo) (*)
bar feature \"feat1\" (command-line) (*)
",
)
.run();
}