aboutsummaryrefslogtreecommitdiff
path: root/internal/cond
diff options
context:
space:
mode:
Diffstat (limited to 'internal/cond')
-rw-r--r--internal/cond/eval.go13
-rw-r--r--internal/cond/eval_test.go24
2 files changed, 30 insertions, 7 deletions
diff --git a/internal/cond/eval.go b/internal/cond/eval.go
index c9f7f75..31c54e3 100644
--- a/internal/cond/eval.go
+++ b/internal/cond/eval.go
@@ -24,7 +24,11 @@ type Facts interface {
// -1 when it contains none.
ContentContains(opt Options, keywords []string) (int, error)
Duplicate(dirs []string) (original string, ok bool, err error)
- Matched() bool // an earlier rule matched this file
+ // Matched reports whether an earlier rule matched this file, and whether
+ // an earlier rule could not be decided (its condition was unknown): with
+ // no match and an undecided rule, (matched) is unknown (plan 11 review
+ // L6).
+ Matched() (matched, undecided bool)
}
// Result is the outcome of evaluating a Cond against one file's Facts.
@@ -154,6 +158,9 @@ func (c *Cond) eval(n *node, f Facts, ctx *evalCtx, negated bool) (tri, []string
}
return yes, []string{"not " + label}
default:
+ if m, undecided := f.Matched(); n.kind == kMatched && !m && undecided {
+ return unknown, nil
+ }
ok, reason, warn, caps := c.evalLeaf(n, f)
ctx.warn(warn)
if warn != "" && n.kind == kContent {
@@ -258,7 +265,7 @@ func (c *Cond) evalLeaf(n *node, f Facts) (ok bool, reason, warn string, caps []
return false, "", "", nil
case kMatched:
- if f.Matched() {
+ if m, _ := f.Matched(); m {
return true, "matched", "", nil
}
return false, "", "", nil
@@ -348,7 +355,7 @@ func (c *Cond) explain(n *node, f Facts) *Trace {
default:
ok, _, warn, _ := c.evalLeaf(n, f)
t := &Trace{Label: n.label, Value: ok, Err: warn}
- if warn != "" && n.kind == kContent {
+ if m, undecided := f.Matched(); (warn != "" && n.kind == kContent) || (n.kind == kMatched && !m && undecided) {
t.set(unknown)
}
return t
diff --git a/internal/cond/eval_test.go b/internal/cond/eval_test.go
index 7d796a1..9352b82 100644
--- a/internal/cond/eval_test.go
+++ b/internal/cond/eval_test.go
@@ -21,6 +21,7 @@ type fake struct {
size int64
age time.Duration
matched bool
+ matchedUnknown bool
dupOrig string
dupOK bool
contentCalls int
@@ -33,10 +34,10 @@ func (f *fake) Rel() string {
}
return f.name
}
-func (f *fake) Size() int64 { return f.size }
-func (f *fake) ModTime() time.Time { return now.Add(-f.age) }
-func (f *fake) Now() time.Time { return now }
-func (f *fake) Matched() bool { return f.matched }
+func (f *fake) Size() int64 { return f.size }
+func (f *fake) ModTime() time.Time { return now.Add(-f.age) }
+func (f *fake) Now() time.Time { return now }
+func (f *fake) Matched() (bool, bool) { return f.matched, f.matchedUnknown }
func (f *fake) ContentContains(opt Options, keywords []string) (int, error) {
f.contentCalls++
if f.rawErr != nil {
@@ -284,3 +285,18 @@ func TestUnreadableContentIsUnknown(t *testing.T) {
}
}
}
+
+// TestMatchedIsUnknownAfterAnUnknownRule: when no earlier rule matched but
+// one could not be decided, (matched) is unknown, so a later
+// (not (matched)) does not act on a file krino could not read (plan 11
+// review L6).
+func TestMatchedIsUnknownAfterAnUnknownRule(t *testing.T) {
+ f := &fake{name: "a.docx", matchedUnknown: true}
+ if r := eval(t, `(not (matched))`, Options{}, f); r.Match || !r.Unreadable {
+ t.Errorf("(not (matched)): Match %v Unreadable %v; want false, true", r.Match, r.Unreadable)
+ }
+ f = &fake{name: "a.docx", matched: true, matchedUnknown: true}
+ if r := eval(t, `(matched)`, Options{}, f); !r.Match {
+ t.Errorf("(matched) after a rule that did match: %+v, want true", r)
+ }
+}