Skip to content

Commit c04954a

Browse files
committed
fix: assure Action::Cancel doesn't run into unreachable code.
1 parent ed79aa7 commit c04954a

File tree

4 files changed

+94
-21
lines changed

4 files changed

+94
-21
lines changed

gix-dir/src/walk/function.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ pub fn walk(
8383
&mut out,
8484
&mut state,
8585
)?;
86-
assert_eq!(state.on_hold.len(), 0, "BUG: must be fully consumed");
8786
gix_trace::debug!(statistics = ?out);
8887
Ok(out)
8988
}

gix-dir/src/walk/readdir.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ pub(super) fn recursive(
6868
recursive(false, current, current_bstr, info, ctx, opts, delegate, out, state)?;
6969
prevent_collapse |= subdir_prevent_collapse;
7070
if action != Action::Continue {
71-
break;
71+
return Ok((action, prevent_collapse));
7272
}
7373
} else if !state.held_for_directory_collapse(current_bstr.as_bstr(), info, &opts) {
7474
let action = emit_entry(Cow::Borrowed(current_bstr.as_bstr()), info, None, opts, out, delegate);

gix-dir/tests/walk/mod.rs

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
use gix_dir::walk;
1+
use gix_dir::{walk, EntryRef};
22
use pretty_assertions::assert_eq;
33

44
use crate::walk_utils::{
55
collect, collect_filtered, entry, entry_dirstat, entry_nokind, entry_nomatch, entryps, entryps_dirstat, fixture,
6-
fixture_in, options, options_emit_all, try_collect, try_collect_filtered_opts, EntryExt, Options,
6+
fixture_in, options, options_emit_all, try_collect, try_collect_filtered_opts, try_collect_filtered_opts_collect,
7+
EntryExt, Options,
78
};
9+
use gix_dir::entry;
810
use gix_dir::entry::Kind::*;
911
use gix_dir::entry::PathspecMatch::*;
1012
use gix_dir::entry::Status::*;
@@ -1823,7 +1825,7 @@ fn root_may_not_go_through_dot_git() -> crate::Result {
18231825
#[test]
18241826
fn root_enters_directory_with_dot_git_in_reconfigured_worktree_tracked() -> crate::Result {
18251827
let root = fixture("nonstandard-worktree");
1826-
let (out, entries) = try_collect_filtered_opts(
1828+
let (out, entries) = try_collect_filtered_opts_collect(
18271829
&root,
18281830
|keep, ctx| {
18291831
walk(
@@ -1857,7 +1859,7 @@ fn root_enters_directory_with_dot_git_in_reconfigured_worktree_tracked() -> crat
18571859
"everything is tracked, so it won't try to detect git repositories anyway"
18581860
);
18591861

1860-
let (out, entries) = try_collect_filtered_opts(
1862+
let (out, entries) = try_collect_filtered_opts_collect(
18611863
&root,
18621864
|keep, ctx| {
18631865
walk(
@@ -1891,7 +1893,7 @@ fn root_enters_directory_with_dot_git_in_reconfigured_worktree_tracked() -> crat
18911893
#[test]
18921894
fn root_enters_directory_with_dot_git_in_reconfigured_worktree_untracked() -> crate::Result {
18931895
let root = fixture("nonstandard-worktree-untracked");
1894-
let (_out, entries) = try_collect_filtered_opts(
1896+
let (_out, entries) = try_collect_filtered_opts_collect(
18951897
&root,
18961898
|keep, ctx| {
18971899
walk(
@@ -2091,6 +2093,69 @@ fn root_can_be_pruned_early_with_pathspec() -> crate::Result {
20912093
Ok(())
20922094
}
20932095

2096+
#[test]
2097+
fn cancel_with_collection_does_not_fail() -> crate::Result {
2098+
struct CancelDelegate {
2099+
emits_left_until_cancel: usize,
2100+
}
2101+
2102+
impl gix_dir::walk::Delegate for CancelDelegate {
2103+
fn emit(&mut self, _entry: EntryRef<'_>, _collapsed_directory_status: Option<entry::Status>) -> walk::Action {
2104+
if self.emits_left_until_cancel == 0 {
2105+
walk::Action::Cancel
2106+
} else {
2107+
self.emits_left_until_cancel -= 1;
2108+
dbg!(self.emits_left_until_cancel);
2109+
walk::Action::Continue
2110+
}
2111+
}
2112+
}
2113+
2114+
for (idx, fixture_name) in [
2115+
"nonstandard-worktree",
2116+
"nonstandard-worktree-untracked",
2117+
"dir-with-file",
2118+
"expendable-and-precious",
2119+
"subdir-untracked-and-ignored",
2120+
"empty-and-untracked-dir",
2121+
"complex-empty",
2122+
"type-mismatch-icase-clash-file-is-dir",
2123+
]
2124+
.into_iter()
2125+
.enumerate()
2126+
{
2127+
let root = fixture(fixture_name);
2128+
let mut dlg = CancelDelegate {
2129+
emits_left_until_cancel: idx,
2130+
};
2131+
let _out = try_collect_filtered_opts(
2132+
&root,
2133+
|keep, ctx| {
2134+
walk(
2135+
&root,
2136+
&root,
2137+
ctx,
2138+
walk::Options {
2139+
emit_untracked: CollapseDirectory,
2140+
emit_ignored: Some(CollapseDirectory),
2141+
emit_empty_directories: true,
2142+
emit_tracked: true,
2143+
for_deletion: Some(Default::default()),
2144+
emit_pruned: true,
2145+
..options()
2146+
},
2147+
keep,
2148+
)
2149+
},
2150+
None::<&str>,
2151+
&mut dlg,
2152+
Options::default(),
2153+
)?;
2154+
// Note that this also doesn't trigger an error - the caller has to deal with that.
2155+
}
2156+
Ok(())
2157+
}
2158+
20942159
#[test]
20952160
fn file_root_is_shown_if_pathspec_matches_exactly() -> crate::Result {
20962161
let root = fixture("dir-with-file");
@@ -2751,7 +2816,7 @@ fn partial_checkout_cone_and_non_one() -> crate::Result {
27512816
#[test]
27522817
fn type_mismatch() {
27532818
let root = fixture("type-mismatch");
2754-
let (out, entries) = try_collect_filtered_opts(
2819+
let (out, entries) = try_collect_filtered_opts_collect(
27552820
&root,
27562821
|keep, ctx| {
27572822
walk(
@@ -2794,7 +2859,7 @@ fn type_mismatch() {
27942859
The typechange is visible only when there is an entry in the index, of course"
27952860
);
27962861

2797-
let (out, entries) = try_collect_filtered_opts(
2862+
let (out, entries) = try_collect_filtered_opts_collect(
27982863
&root,
27992864
|keep, ctx| {
28002865
walk(
@@ -2839,7 +2904,7 @@ fn type_mismatch() {
28392904
#[test]
28402905
fn type_mismatch_ignore_case() {
28412906
let root = fixture("type-mismatch-icase");
2842-
let (out, entries) = try_collect_filtered_opts(
2907+
let (out, entries) = try_collect_filtered_opts_collect(
28432908
&root,
28442909
|keep, ctx| {
28452910
walk(
@@ -2879,7 +2944,7 @@ fn type_mismatch_ignore_case() {
28792944
"this is the same as in the non-icase version, which means that icase lookup works"
28802945
);
28812946

2882-
let (out, entries) = try_collect_filtered_opts(
2947+
let (out, entries) = try_collect_filtered_opts_collect(
28832948
&root,
28842949
|keep, ctx| {
28852950
walk(
@@ -2923,7 +2988,7 @@ fn type_mismatch_ignore_case() {
29232988
#[test]
29242989
fn type_mismatch_ignore_case_clash_dir_is_file() {
29252990
let root = fixture("type-mismatch-icase-clash-dir-is-file");
2926-
let (out, entries) = try_collect_filtered_opts(
2991+
let (out, entries) = try_collect_filtered_opts_collect(
29272992
&root,
29282993
|keep, ctx| {
29292994
walk(
@@ -2964,7 +3029,7 @@ fn type_mismatch_ignore_case_clash_dir_is_file() {
29643029
#[test]
29653030
fn type_mismatch_ignore_case_clash_file_is_dir() {
29663031
let root = fixture("type-mismatch-icase-clash-file-is-dir");
2967-
let (out, entries) = try_collect_filtered_opts(
3032+
let (out, entries) = try_collect_filtered_opts_collect(
29683033
&root,
29693034
|keep, ctx| {
29703035
walk(

gix-dir/tests/walk_utils/mod.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,15 +169,27 @@ pub fn try_collect_filtered(
169169
cb: impl FnOnce(&mut dyn walk::Delegate, walk::Context) -> Result<walk::Outcome, walk::Error>,
170170
patterns: impl IntoIterator<Item = impl AsRef<BStr>>,
171171
) -> Result<(walk::Outcome, Entries), walk::Error> {
172-
try_collect_filtered_opts(worktree_root, cb, patterns, Default::default())
172+
try_collect_filtered_opts_collect(worktree_root, cb, patterns, Default::default())
173+
}
174+
175+
pub fn try_collect_filtered_opts_collect(
176+
worktree_root: &Path,
177+
cb: impl FnOnce(&mut dyn walk::Delegate, walk::Context) -> Result<walk::Outcome, walk::Error>,
178+
patterns: impl IntoIterator<Item = impl AsRef<BStr>>,
179+
options: Options<'_>,
180+
) -> Result<(walk::Outcome, Entries), walk::Error> {
181+
let mut dlg = gix_dir::walk::delegate::Collect::default();
182+
let outcome = try_collect_filtered_opts(worktree_root, cb, patterns, &mut dlg, options)?;
183+
Ok((outcome, dlg.into_entries_by_path()))
173184
}
174185

175186
pub fn try_collect_filtered_opts(
176187
worktree_root: &Path,
177188
cb: impl FnOnce(&mut dyn walk::Delegate, walk::Context) -> Result<walk::Outcome, walk::Error>,
178189
patterns: impl IntoIterator<Item = impl AsRef<BStr>>,
190+
delegate: &mut dyn gix_dir::walk::Delegate,
179191
Options { fresh_index, git_dir }: Options<'_>,
180-
) -> Result<(walk::Outcome, Entries), walk::Error> {
192+
) -> Result<walk::Outcome, walk::Error> {
181193
let git_dir = worktree_root.join(git_dir.unwrap_or(".git"));
182194
let mut index = std::fs::read(git_dir.join("index")).ok().map_or_else(
183195
|| gix_index::State::new(gix_index::hash::Kind::Sha1),
@@ -229,10 +241,9 @@ pub fn try_collect_filtered_opts(
229241

230242
let cwd = gix_fs::current_dir(false).expect("valid cwd");
231243
let git_dir_realpath = gix_path::realpath_opts(&git_dir, &cwd, gix_path::realpath::MAX_SYMLINKS).unwrap();
232-
let mut dlg = gix_dir::walk::delegate::Collect::default();
233244
let lookup = index.prepare_icase_backing();
234-
let outcome = cb(
235-
&mut dlg,
245+
cb(
246+
delegate,
236247
walk::Context {
237248
git_dir_realpath: &git_dir_realpath,
238249
current_dir: &cwd,
@@ -243,9 +254,7 @@ pub fn try_collect_filtered_opts(
243254
excludes: Some(&mut stack),
244255
objects: &gix_object::find::Never,
245256
},
246-
)?;
247-
248-
Ok((outcome, dlg.into_entries_by_path()))
257+
)
249258
}
250259

251260
pub struct Options<'a> {

0 commit comments

Comments
 (0)