diff --git a/build/generate-extended-cases/exceptions.yaml b/build/generate-extended-cases/exceptions.yaml index a5e9f77149..8c3413b8c6 100644 --- a/build/generate-extended-cases/exceptions.yaml +++ b/build/generate-extended-cases/exceptions.yaml @@ -1,9 +1 @@ # Exception Format is : -"negation/not-body: negated call with vars, call succeeds": "No planner support" -"negation/not-body: negated call with vars, call fails": "No planner support" -"negation/not-body: negated call with vars, undefined operands": "No planner support" -"negation/not-body: negated call with vars, inside comprehension, call succeeds": "No planner support" -"negation/not-body: negated call with vars, inside comprehension, undefined operands": "No planner support" -"negation/not-body: negated call with vars, inside every, call succeeds": "No planner support" -"negation/not-body: negated call with vars, inside every, undefined operands": "No planner support" -"negation/not-body: negated and non-negated unification on same var, calls, rearranged": "No planner support" diff --git a/build/generate-extended-cases/extended_cases.go b/build/generate-extended-cases/extended_cases.go index b2e4cdf243..af958c20d2 100644 --- a/build/generate-extended-cases/extended_cases.go +++ b/build/generate-extended-cases/extended_cases.go @@ -179,10 +179,15 @@ func LoadIrExtendedTestCasesFiltered(filters ...Filters) ([]ExtendedSet, error) continue } + // TODO: drop once future.keywords.not is enabled by default + caps := ast.CapabilitiesForThisVersion() + caps.FutureKeywords = append(caps.FutureKeywords, "not") + opts := []func(*rego.Rego){ rego.Target(pluginName), rego.Query(tc.Query), rego.SetRegoVersion(ast.RegoV1), + rego.Capabilities(caps), } for i := range tc.Modules { opts = append(opts, rego.Module(fmt.Sprintf("module-%d.rego", i), tc.Modules[i])) diff --git a/internal/planner/planner.go b/internal/planner/planner.go index 445b5d7c2a..c1aeddc1fa 100644 --- a/internal/planner/planner.go +++ b/internal/planner/planner.go @@ -637,7 +637,7 @@ func (p *Planner) planQuery(q ast.Body, index int, iter planiter) error { func (p *Planner) planExpr(e *ast.Expr, iter planiter) error { switch { - case e.Negated: + case e.IsNegated(): return p.planNot(e, iter) case len(e.With) > 0: @@ -661,8 +661,16 @@ func (p *Planner) planNot(e *ast.Expr, iter planiter) error { prev := p.curr p.curr = not.Block - if err := p.planExpr(e.Complement(), func() error { return nil }); err != nil { - return err + if n, ok := e.Terms.(*ast.Not); ok { + for _, be := range n.Body { + if err := p.planExpr(be, func() error { return nil }); err != nil { + return err + } + } + } else { + if err := p.planExpr(e.Complement(), func() error { return nil }); err != nil { + return err + } } p.curr = prev diff --git a/internal/planner/planner_test.go b/internal/planner/planner_test.go index 1bd92705ba..44e00f490e 100644 --- a/internal/planner/planner_test.go +++ b/internal/planner/planner_test.go @@ -66,6 +66,15 @@ func TestPlannerHelloWorld(t *testing.T) { note: "negation", queries: []string{"not input.x.y = 1"}, }, + { + note: "negation, not-body", + queries: []string{"data.test.p = x"}, + modules: []string{` + package test + import future.keywords.not + p if { not input.x.y = 1 } + `}, + }, { note: "not and known vars", // https://github.com/open-policy-agent/opa/issues/3279 queries: []string{`x = "foo"; not data.tenants[x]`}, @@ -396,7 +405,16 @@ q = 2`, modules := make([]*ast.Module, len(tc.modules)) for i := range modules { file := fmt.Sprintf("module-%d.rego", i) - opts := ast.ParserOptions{AllFutureKeywords: true} + + // TODO: drop once future.keywords.not is enabled by default + caps := ast.CapabilitiesForThisVersion() + caps.FutureKeywords = append(caps.FutureKeywords, "not") + + opts := ast.ParserOptions{ + AllFutureKeywords: true, + Capabilities: caps, + } + m, err := ast.ParseModuleWithOpts(file, tc.modules[i], opts) if err != nil { t.Fatal(err) diff --git a/internal/wasm/sdk/test/e2e/exceptions.yaml b/internal/wasm/sdk/test/e2e/exceptions.yaml index b81458f51c..d8eb5d5283 100644 --- a/internal/wasm/sdk/test/e2e/exceptions.yaml +++ b/internal/wasm/sdk/test/e2e/exceptions.yaml @@ -1,11 +1,3 @@ # Exception Format is : "data/toplevel integer": "https://github.com/open-policy-agent/opa/issues/3711" "data/nested integer": "https://github.com/open-policy-agent/opa/issues/3711" -"negation/not-body: negated call with vars, call succeeds": "No planner support" -"negation/not-body: negated call with vars, call fails": "No planner support" -"negation/not-body: negated call with vars, undefined operands": "No planner support" -"negation/not-body: negated call with vars, inside comprehension, call succeeds": "No planner support" -"negation/not-body: negated call with vars, inside comprehension, undefined operands": "No planner support" -"negation/not-body: negated call with vars, inside every, call succeeds": "No planner support" -"negation/not-body: negated call with vars, inside every, undefined operands": "No planner support" -"negation/not-body: negated and non-negated unification on same var, calls, rearranged": "No planner support" diff --git a/internal/wasm/sdk/test/e2e/external_test.go b/internal/wasm/sdk/test/e2e/external_test.go index 00d6e20347..995b1cde97 100644 --- a/internal/wasm/sdk/test/e2e/external_test.go +++ b/internal/wasm/sdk/test/e2e/external_test.go @@ -74,9 +74,13 @@ func TestWasmE2E(t *testing.T) { t.Setenv(k, v) } + caps := ast.CapabilitiesForThisVersion() + caps.FutureKeywords = append(caps.FutureKeywords, "not") + opts := []func(*rego.Rego){ rego.Query(tc.Query), rego.SetRegoVersion(regoVersion), + rego.Capabilities(caps), } for i := range tc.Modules { opts = append(opts, rego.Module(fmt.Sprintf("module-%d.rego", i), tc.Modules[i]))