mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
ast: Fix PE regression for future.keywords.not negation inside every (#8781)
Fixing regression where `future.keywords.not` negated expressions nested
inside an `every` body would fail to plug variables for partial
evaluation.
E.g.
```rego
package test
import future.keywords.not
p if {
x = input.foo
every y in [1, 2] {
not f(x, y)
}
}
```
would fail to plug `x`:
```rego
every __local0__1, __local1__1 in [1, 2] {
not data.test.f(x, __local1__)
}
x1 = input.foo
```
instead of the expected:
```rego
every __local0__1, __local1__1 in [1, 2] {
not data.test.f(input.foo, __local1__1)
}
x1 = input.foo
```
Signed-off-by: Johan Fylling <johan.dev@fylling.se>
This commit is contained in:
+18
-12
@@ -4210,18 +4210,7 @@ func (e *evalEvery) save(iter unifyIterator) error {
|
||||
func (e *evalEvery) plug(expr *ast.Expr) *ast.Expr {
|
||||
cpy := expr.Copy()
|
||||
every := cpy.Terms.(*ast.Every)
|
||||
for i := range every.Body {
|
||||
switch t := every.Body[i].Terms.(type) {
|
||||
case *ast.Term:
|
||||
every.Body[i].Terms = e.e.bindings.PlugNamespaced(t, e.e.caller.bindings)
|
||||
case []*ast.Term:
|
||||
for j := 1; j < len(t); j++ { // don't plug operator, t[0]
|
||||
t[j] = e.e.bindings.PlugNamespaced(t[j], e.e.caller.bindings)
|
||||
}
|
||||
case *ast.Every:
|
||||
every.Body[i] = e.plug(every.Body[i])
|
||||
}
|
||||
}
|
||||
e.plugBody(every.Body)
|
||||
|
||||
every.Key = e.e.bindings.PlugNamespaced(every.Key, e.e.caller.bindings)
|
||||
every.Value = e.e.bindings.PlugNamespaced(every.Value, e.e.caller.bindings)
|
||||
@@ -4230,6 +4219,23 @@ func (e *evalEvery) plug(expr *ast.Expr) *ast.Expr {
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (e *evalEvery) plugBody(body ast.Body) {
|
||||
for i := range body {
|
||||
switch t := body[i].Terms.(type) {
|
||||
case *ast.Term:
|
||||
body[i].Terms = e.e.bindings.PlugNamespaced(t, e.e.caller.bindings)
|
||||
case []*ast.Term:
|
||||
for j := 1; j < len(t); j++ { // don't plug operator, t[0]
|
||||
t[j] = e.e.bindings.PlugNamespaced(t[j], e.e.caller.bindings)
|
||||
}
|
||||
case *ast.Every:
|
||||
body[i] = e.plug(body[i])
|
||||
case *ast.Not:
|
||||
e.plugBody(t.Body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type evalNot struct {
|
||||
e *eval
|
||||
not *ast.Not
|
||||
|
||||
@@ -5898,6 +5898,98 @@ func TestTopDownPartialEvalNegation(t *testing.T) {
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
// Nesting inside every-body
|
||||
{
|
||||
note: "every+negation: implicit body, concrete-bound outer var",
|
||||
notBodyOnly: true,
|
||||
query: "data.test.p",
|
||||
modules: []string{`package test
|
||||
p if {
|
||||
z = 42
|
||||
every y in input.xs {
|
||||
not f(z, y)
|
||||
}
|
||||
}
|
||||
f(_, _) := true`},
|
||||
wantQueries: []string{
|
||||
`every __local0__1, __local1__1 in input.xs {
|
||||
not data.test.f(42, __local1__1)
|
||||
}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
note: "every+negation: implicit body, unknown-bound outer var",
|
||||
query: "data.test.p",
|
||||
modules: []string{`package test
|
||||
p if {
|
||||
x = input.foo
|
||||
every y in [1, 2] {
|
||||
not f(x, y)
|
||||
}
|
||||
}
|
||||
f(_, _) := false`},
|
||||
wantQueries: []string{
|
||||
`every __local0__1, __local1__1 in [1, 2] {
|
||||
not data.test.f(input.foo, __local1__1)
|
||||
}
|
||||
x1 = input.foo`,
|
||||
},
|
||||
},
|
||||
{
|
||||
note: "every+negation: explicit body, concrete-bound outer var",
|
||||
notBodyOnly: true,
|
||||
query: "data.test.p",
|
||||
modules: []string{`package test
|
||||
p if {
|
||||
z = 42
|
||||
every y in input.xs {
|
||||
not { f(z, y) }
|
||||
}
|
||||
}
|
||||
f(_, _) := true`},
|
||||
wantQueries: []string{
|
||||
`every __local0__1, __local1__1 in input.xs {
|
||||
not { data.test.f(42, __local1__1) }
|
||||
}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
note: "every+negation: explicit body, unknown-bound outer var",
|
||||
notBodyOnly: true,
|
||||
query: "data.test.p",
|
||||
modules: []string{`package test
|
||||
p if {
|
||||
x = input.foo
|
||||
every y in [1, 2] {
|
||||
not { f(x, y) }
|
||||
}
|
||||
}
|
||||
f(_, _) := false`},
|
||||
wantQueries: []string{
|
||||
`every __local0__1, __local1__1 in [1, 2] {
|
||||
not { data.test.f(input.foo, __local1__1) }
|
||||
}
|
||||
x1 = input.foo`,
|
||||
},
|
||||
},
|
||||
{
|
||||
note: "every+negation: implicit body, comparison expression",
|
||||
query: "data.test.p",
|
||||
modules: []string{`package test
|
||||
p if {
|
||||
v = input.threshold
|
||||
every y in [1, 2] {
|
||||
not v == y
|
||||
}
|
||||
}`},
|
||||
wantQueries: []string{
|
||||
`every __local0__1, __local1__1 in [1, 2] {
|
||||
not input.threshold = __local1__1
|
||||
}
|
||||
v1 = input.threshold`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := t.Context()
|
||||
|
||||
Reference in New Issue
Block a user