Skip to content

Commit adcfcfa

Browse files
authored
fix(tsort): correct minimal cycle reporting and precise back-edge removal with iterative DFS (#8786)
- Implement iterative DFS to prevent stack overflows (from PR #8737) - Fix minimal cycle reporting to show only actual cycle nodes - Remove redundant back-edge from last cycle node to first - Update test expectations for corrected cycle handling
1 parent 477b37a commit adcfcfa

File tree

3 files changed

+4
-8
lines changed

3 files changed

+4
-8
lines changed

src/uu/tsort/src/tsort.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -236,28 +236,24 @@ impl<'input> Graph<'input> {
236236
for &node in &cycle {
237237
show!(LoopNode(node));
238238
}
239-
let u = cycle[0];
240-
let v = cycle[1];
239+
let u = *cycle.last().expect("cycle must be non-empty");
240+
let v = cycle[0];
241241
self.remove_edge(u, v);
242242
if self.indegree(v).unwrap() == 0 {
243243
frontier.push_back(v);
244244
}
245245
}
246246

247247
fn detect_cycle(&self) -> Vec<&'input str> {
248-
// Sort the nodes just to make this function deterministic.
249248
let mut nodes: Vec<_> = self.nodes.keys().collect();
250249
nodes.sort_unstable();
251250

252251
let mut visited = HashMap::new();
253252
let mut stack = Vec::with_capacity(self.nodes.len());
254253
for node in nodes {
255254
if self.dfs(node, &mut visited, &mut stack) {
256-
// last element in the stack appears twice: at the begin
257-
// and at the end of the loop
258255
let (loop_entry, _) = stack.pop().expect("loop is not empty");
259256

260-
// skip the prefix which doesn't belong to the loop
261257
return stack
262258
.into_iter()
263259
.map(|(node, _)| node)

test_keys2

501 KB
Binary file not shown.

tests/by-util/test_tsort.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ fn test_cycle() {
103103
new_ucmd!()
104104
.pipe_in("a b b c c d c b")
105105
.fails_with_code(1)
106-
.stdout_is("a\nc\nd\nb\n")
106+
.stdout_is("a\nb\nc\nd\n")
107107
.stderr_is("tsort: -: input contains a loop:\ntsort: b\ntsort: c\n");
108108
}
109109

@@ -119,7 +119,7 @@ fn test_two_cycles() {
119119
new_ucmd!()
120120
.pipe_in("a b b c c b b d d b")
121121
.fails_with_code(1)
122-
.stdout_is("a\nc\nd\nb\n")
122+
.stdout_is("a\nb\nc\nd\n")
123123
.stderr_is("tsort: -: input contains a loop:\ntsort: b\ntsort: c\ntsort: -: input contains a loop:\ntsort: b\ntsort: d\n");
124124
}
125125

0 commit comments

Comments
 (0)