diff --git a/CHANGELOG.md b/CHANGELOG.md index c88f3d8..f5b00d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com ## [Unreleased] +- Fix `--mutually-exclusive-features` interacting with optional dependencies. ([#261](https://github.com/taiki-e/cargo-hack/pull/261), thanks @xStrom) + ## [0.6.33] - 2024-11-02 - Allow using `--exclude` without also specifying `--workspace`. ([#258](https://github.com/taiki-e/cargo-hack/pull/258), thanks @xStrom) diff --git a/src/features.rs b/src/features.rs index 358ce01..c5e3d1c 100644 --- a/src/features.rs +++ b/src/features.rs @@ -160,7 +160,16 @@ impl Feature { ) -> bool { if let Some(v) = map.get(cur) { for cur in v { - if cur != root && (group.matches(cur) || rec(group, map, cur, root)) { + let fname = if let Some(slash_idx) = cur.find('/') { + // The fname may still have a '?' suffix, which is fine. + // Because in that case it doesn't activate that dependency, so it can be ignored. + let (fname, _) = cur.split_at(slash_idx); + fname + } else { + // Could be 'dep:something', which is fine because it's not a feature. + cur + }; + if fname != root && (group.matches(fname) || rec(group, map, fname, root)) { return true; } } @@ -396,6 +405,25 @@ mod tests { vec!["b", "async-std"] ]); + let map = map![ + ("tokio", v![]), + ("async-std", v![]), + ("a", v!["tokio/full"]), + ("b", v!["async-std?/alloc"]) + ]; + let list = v!["a", "b", "tokio", "async-std"]; + let mutually_exclusive_features = [Feature::group(["tokio", "async-std"])]; + let filtered = feature_powerset(&list, None, &[], &mutually_exclusive_features, &map); + assert_eq!(filtered, vec![ + vec!["a"], + vec!["b"], + vec!["a", "b"], + vec!["tokio"], + vec!["b", "tokio"], + vec!["async-std"], + vec!["b", "async-std"] + ]); + let map = map![("a", v![]), ("b", v!["a"]), ("c", v![]), ("d", v!["b"])]; let list = v!["a", "b", "c", "d"]; let mutually_exclusive_features = [Feature::group(["a", "c"])];