mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
planner: forget variables from comprehensions (#3327)
Before, variables introduced in a comprehension, like the `x` in
{ x | xs[x] }
would have been recorded in `p.vars`, and have an effect outside of
the scope of this comprehension, leading to wrong plans.
Now, we'll push a new frame for learning about variables we've not
known before, and reset to that state after planning the
comprehensions, thereby forgetting them.
Fixes #3325.
Signed-off-by: Stephan Renatus <stephan.renatus@gmail.com>
This commit is contained in:
@@ -1360,6 +1360,10 @@ func (p *Planner) planObjectComprehension(oc *ast.ObjectComprehension, iter plan
|
||||
|
||||
func (p *Planner) planComprehension(body ast.Body, closureIter planiter, target ir.Local, iter planiter) error {
|
||||
|
||||
// Variables that have been introduced in this comprehension have
|
||||
// no effect on other parts of the policy, so they'll be dropped
|
||||
// below.
|
||||
p.vars.Push(map[ast.Var]ir.Local{})
|
||||
prev := p.curr
|
||||
p.curr = &ir.Block{}
|
||||
ploc := p.loc
|
||||
@@ -1373,6 +1377,7 @@ func (p *Planner) planComprehension(body ast.Body, closureIter planiter, target
|
||||
block := p.curr
|
||||
p.curr = prev
|
||||
p.loc = ploc
|
||||
p.vars.Pop()
|
||||
|
||||
p.appendStmt(&ir.BlockStmt{
|
||||
Blocks: []*ir.Block{
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package planner
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/internal/ir"
|
||||
)
|
||||
|
||||
func TestVarStackPushPop(t *testing.T) {
|
||||
p := New()
|
||||
vs := newVarstack()
|
||||
vs.Push(map[ast.Var]ir.Local{})
|
||||
loc0, loc1 := p.newLocal(), p.newLocal()
|
||||
vs.Put(ast.Var("x"), loc0)
|
||||
vs.Push(map[ast.Var]ir.Local{})
|
||||
loc, ok := vs.Get(ast.Var("x"))
|
||||
if exp, act := true, ok; exp != act {
|
||||
t.Errorf("Get(x): expected %v, got %v", exp, act)
|
||||
}
|
||||
if exp, act := loc0, loc; exp != act {
|
||||
t.Errorf("Get(x) expected %v, got %v", exp, act)
|
||||
}
|
||||
vs.Put(ast.Var("y"), loc1)
|
||||
pop := vs.Pop()
|
||||
if exp, act := 1, len(pop); exp != act {
|
||||
t.Errorf("Pop(): expected len %v, got %v", exp, act)
|
||||
}
|
||||
|
||||
// x still there
|
||||
loc, ok = vs.Get(ast.Var("x"))
|
||||
if exp, act := true, ok; exp != act {
|
||||
t.Errorf("Get(x): expected %v, got %v", exp, act)
|
||||
}
|
||||
if exp, act := loc0, loc; exp != act {
|
||||
t.Errorf("Get(x) expected %v, got %v", exp, act)
|
||||
}
|
||||
|
||||
// y not there
|
||||
_, ok = vs.Get(ast.Var("y"))
|
||||
if exp, act := false, ok; exp != act {
|
||||
t.Errorf("Get(y): expected %v, got %v", exp, act)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
cases:
|
||||
- data:
|
||||
modules:
|
||||
- |
|
||||
package test
|
||||
xs := {"a", "b", "c"}
|
||||
p = x {
|
||||
y := { x | xs[x] }
|
||||
z := { x | xs[x] }
|
||||
count(y) == count(z)
|
||||
x := count(y)
|
||||
}
|
||||
note: comprehensions/var bindings have no effect outside
|
||||
query: data.test.p = x
|
||||
want_result:
|
||||
- x: 3
|
||||
Reference in New Issue
Block a user