diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-15 00:27:43 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-15 00:27:43 +0200 |
| commit | 59015f1f91d640502fdfc34019cf0154b5206bb8 (patch) | |
| tree | 30e3ea85407b5ece06c92f3e971cf0cdfac8656b /internal/engine | |
| parent | fc6eda20555ea33f1a8260610056b7079c8e8fd9 (diff) | |
| download | krino-59015f1f91d640502fdfc34019cf0154b5206bb8.tar.gz krino-59015f1f91d640502fdfc34019cf0154b5206bb8.zip | |
explain walks the directory for a duplicate test in an exclude too
Diffstat (limited to 'internal/engine')
| -rw-r--r-- | internal/engine/exclude_test.go | 29 | ||||
| -rw-r--r-- | internal/engine/match.go | 18 |
2 files changed, 44 insertions, 3 deletions
diff --git a/internal/engine/exclude_test.go b/internal/engine/exclude_test.go index 5a758bb..9aafd7f 100644 --- a/internal/engine/exclude_test.go +++ b/internal/engine/exclude_test.go @@ -483,3 +483,32 @@ func TestPartlyReadableDocumentIsNotCached(t *testing.T) { } } } + +// TestExplainAgreesWithMatchOnADuplicateExclude: a (duplicate) test inside +// an exclude needs the directory's other files, as one inside a rule does, +// so explain sets aside exactly the files Match does (plan 11 review M3). +func TestExplainAgreesWithMatchOnADuplicateExclude(t *testing.T) { + h, dl := excludeTree(t, map[string]string{"a.txt": "same", "b.txt": "same", "c.txt": "other"}) + main := writeConfig(t, h, `(include "dl")`, map[string]string{"dl": ` +(path "~/dl") +(exclude (not (duplicate))) +(rule "all" (move "Out")) +`}) + e, errs := Load(main) + if len(errs) > 0 { + t.Fatal(errs) + } + r, err := e.Match(context.Background(), e.Dirs[0]) + if err != nil { + t.Fatal(err) + } + for _, fm := range append(append([]FileMatch{}, r.Matched...), r.Unmatched...) { + x, err := e.Explain(context.Background(), filepath.Join(dl, fm.File.Rel)) + if err != nil { + t.Fatal(err) + } + if x.Excluded != fm.Excluded { + t.Errorf("%s: explain sets it aside as %q, Match as %q", fm.File.Rel, x.Excluded, fm.Excluded) + } + } +} diff --git a/internal/engine/match.go b/internal/engine/match.go index 6b98d73..c252596 100644 --- a/internal/engine/match.go +++ b/internal/engine/match.go @@ -280,10 +280,11 @@ func (e *Engine) Explain(ctx context.Context, path string) (*Explanation, error) excl := e.excludeDirs(d) skip := explainSkip(d, sf, excl, now) - // The directory is walked only for a duplicate test, the one thing that - // needs its other files (triage 21). + // The directory is walked only for a duplicate test - in a rule or in an + // exclude - the one thing that needs its other files (triage 21, plan 11 + // review M3). files := []scan.File{sf} - if len(d.DupScopes) > 0 { + if len(d.DupScopes) > 0 || excludesUseDuplicate(d) { files = e.filesForExplain(d, sf, excl, now) } run := newMatchRun(e, d, ctx, now, files) @@ -332,6 +333,17 @@ func (e *Engine) Explain(ctx context.Context, path string) (*Explanation, error) return &Explanation{Dir: d, File: sf, Skip: skip, Excludes: excludes, Excluded: excluded, Rules: rules, NoDelete: noDel}, nil } +// excludesUseDuplicate reports whether any of d's excludes has a +// (duplicate) test. +func excludesUseDuplicate(d *Dir) bool { + for _, x := range d.Excludes { + if len(x.Cond.DupDirs) > 0 { + return true + } + } + return false +} + // cacheFingerprint identifies what a cached keyword answer of d depends on // besides the file: the extractor (its version and tools), normalisation // and its Unicode tables, the Go release, and d's max-read, which caps what |
