Skip to content

Commit c784b0a

Browse files
xStromtaiki-e
authored andcommitted
Fix infinite recursion with cyclical features.
1 parent bc7d958 commit c784b0a

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com
1212

1313
## [Unreleased]
1414

15+
- Fix infinite recursion when cyclical features interact with `--mutually-exclusive-features`. ([#276](https://github.com/taiki-e/cargo-hack/pull/276), thanks @xStrom)
16+
1517
## [0.6.36] - 2025-03-20
1618

1719
- Fix a regression introduced in 0.6.34 that caused a stack overflow. ([#272](https://github.com/taiki-e/cargo-hack/pull/272))

src/features.rs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,13 @@ impl Feature {
152152
}
153153

154154
pub(crate) fn matches_recursive(&self, s: &str, map: &BTreeMap<String, Vec<String>>) -> bool {
155-
fn rec(
155+
fn rec<'a>(
156156
group: &Feature,
157-
map: &BTreeMap<String, Vec<String>>,
158-
cur: &str,
159-
root: &str,
157+
map: &'a BTreeMap<String, Vec<String>>,
158+
cur: &'a str,
159+
traversed: &mut BTreeSet<&'a str>,
160160
) -> bool {
161+
traversed.insert(cur);
161162
if let Some(v) = map.get(cur) {
162163
for cur in v {
163164
let fname = if let Some(slash_idx) = cur.find('/') {
@@ -169,14 +170,16 @@ impl Feature {
169170
// Could be 'dep:something', which is fine because it's not a feature.
170171
cur
171172
};
172-
if fname != root && (group.matches(fname) || rec(group, map, fname, root)) {
173+
if !traversed.contains(fname)
174+
&& (group.matches(fname) || rec(group, map, fname, traversed))
175+
{
173176
return true;
174177
}
175178
}
176179
}
177180
false
178181
}
179-
self.matches(s) || rec(self, map, s, s)
182+
self.matches(s) || rec(self, map, s, &mut BTreeSet::new())
180183
}
181184
}
182185

@@ -424,6 +427,33 @@ mod tests {
424427
vec!["b", "async-std"]
425428
]);
426429

430+
let map = map![
431+
("actual", v!["alias"]),
432+
("alias", v!["dep:actual", "actual/feat"]),
433+
("entry", v!["alias"]),
434+
("dummy_a", v![]),
435+
("dummy_b", v![])
436+
];
437+
let list = v!["actual", "alias", "entry", "dummy_a", "dummy_b"];
438+
let mutually_exclusive_features = [Feature::group(["dummy_a", "dummy_b"])];
439+
let filtered = feature_powerset(&list, None, &[], &mutually_exclusive_features, &map);
440+
assert_eq!(filtered, vec![
441+
vec!["actual"],
442+
vec!["alias"],
443+
vec!["entry"],
444+
vec!["actual", "entry"],
445+
vec!["dummy_a"],
446+
vec!["actual", "dummy_a"],
447+
vec!["alias", "dummy_a"],
448+
vec!["entry", "dummy_a"],
449+
vec!["actual", "entry", "dummy_a"],
450+
vec!["dummy_b"],
451+
vec!["actual", "dummy_b"],
452+
vec!["alias", "dummy_b"],
453+
vec!["entry", "dummy_b"],
454+
vec!["actual", "entry", "dummy_b"]
455+
]);
456+
427457
let map = map![("a", v![]), ("b", v!["a"]), ("c", v![]), ("d", v!["b"])];
428458
let list = v!["a", "b", "c", "d"];
429459
let mutually_exclusive_features = [Feature::group(["a", "c"])];

0 commit comments

Comments
 (0)