Skip to content

Commit

Permalink
Update types.Unknown with attribute trails (#776)
Browse files Browse the repository at this point in the history
* Support for multi-mapped attribute trails

Additional test cases provided for unknown string formatting
as well as for handling unknown field selections across numeric
types.

This change also revealed an issues with how custom attribute
factory qualifiers were not being tracked correctly during
state-tracking, nor was the type information correctly collected
for array index operations.
  • Loading branch information
TristonianJones authored Jul 18, 2023
1 parent 5045f58 commit bad352c
Show file tree
Hide file tree
Showing 16 changed files with 759 additions and 165 deletions.
12 changes: 6 additions & 6 deletions cel/cel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1438,21 +1438,21 @@ func TestPartialVars(t *testing.T) {
interpreter.NewAttributePattern("x"),
interpreter.NewAttributePattern("y"),
},
out: types.Unknown{1},
out: types.NewUnknown(1, types.NewAttributeTrail("x")),
},
{
in: map[string]any{"x": "10"},
unk: []*interpreter.AttributePattern{
interpreter.NewAttributePattern("y"),
},
out: types.Unknown{4},
out: types.NewUnknown(4, types.NewAttributeTrail("y")),
},
{
in: map[string]any{"y": 10},
unk: []*interpreter.AttributePattern{
interpreter.NewAttributePattern("x"),
},
out: types.Unknown{1},
out: types.NewUnknown(1, types.NewAttributeTrail("x")),
},
{
in: map[string]any{"x": "10", "y": 10},
Expand All @@ -1468,19 +1468,19 @@ func TestPartialVars(t *testing.T) {
in: map[string]any{"y": 10},
unk: []*interpreter.AttributePattern{},
out: types.NewErr("no such attribute: x"),
partialOut: types.Unknown{1},
partialOut: types.NewUnknown(1, types.NewAttributeTrail("x")),
},
{
in: map[string]any{"x": "10"},
unk: []*interpreter.AttributePattern{},
out: types.NewErr("no such attribute: y"),
partialOut: types.Unknown{4},
partialOut: types.NewUnknown(4, types.NewAttributeTrail("y")),
},
{
in: map[string]any{},
unk: []*interpreter.AttributePattern{},
out: types.NewErr("no such attribute: x"),
partialOut: types.Unknown{1},
partialOut: types.NewUnknown(1, types.NewAttributeTrail("x")),
},
}
for i, tst := range tests {
Expand Down
24 changes: 12 additions & 12 deletions cel/decls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ var dispatchTests = []struct {
},
{
expr: "max(unk, unk)",
out: types.Unknown{42},
out: types.NewUnknown(42, nil),
},
{
expr: "max(unk, unk, unk)",
out: types.Unknown{42},
out: types.NewUnknown(42, nil),
},
}

Expand Down Expand Up @@ -395,11 +395,11 @@ func TestUnaryBinding(t *testing.T) {
if err != nil {
t.Fatalf("Program() failed: %v", err)
}
out, _, err := prg.Eval(map[string]any{"x": types.Unknown{1}})
out, _, err := prg.Eval(map[string]any{"x": types.NewUnknown(1, nil)})
if err != nil {
t.Fatalf("prg.Eval(x=unk) failed: %v", err)
}
if !reflect.DeepEqual(out, types.Unknown{1}) {
if !types.NewUnknown(1, nil).Contains(out.(*types.Unknown)) {
t.Errorf("prg.Eval(x=unk) returned %v, wanted unknown{1}", out)
}
}
Expand Down Expand Up @@ -439,14 +439,14 @@ func TestBinaryBinding(t *testing.T) {
if err != nil {
t.Fatalf("Program() failed: %v", err)
}
out, _, err := prg.Eval(map[string]any{"x": types.Unknown{1}, "y": 1})
out, _, err := prg.Eval(map[string]any{"x": types.NewUnknown(1, nil), "y": 1})
if err != nil {
t.Fatalf("prg.Eval(x=unk) failed: %v", err)
}
if !reflect.DeepEqual(out, types.IntOne) {
t.Errorf("prg.Eval(x=unk, y=1) returned %v, wanted 1", out)
}
out, _, err = prg.Eval(map[string]any{"x": 2, "y": types.Unknown{2}})
out, _, err = prg.Eval(map[string]any{"x": 2, "y": types.NewUnknown(2, nil)})
if err != nil {
t.Fatalf("prg.Eval(x=2, y=unk) failed: %v", err)
}
Expand Down Expand Up @@ -790,10 +790,10 @@ func testParse(t testing.TB, env *Env, expr string, want any) {
if err != nil {
t.Fatalf("env.Program() failed: %v", err)
}
out, _, err := prg.Eval(map[string]any{"err": types.NewErr("error argument"), "unk": types.Unknown{42}})
out, _, err := prg.Eval(map[string]any{"err": types.NewErr("error argument"), "unk": types.NewUnknown(42, nil)})
switch want := want.(type) {
case types.Unknown:
if !reflect.DeepEqual(want, out.(types.Unknown)) {
case *types.Unknown:
if !want.Contains(out.(*types.Unknown)) {
t.Errorf("prg.Eval() got %v, wanted %v", out, want)
}
case ref.Val:
Expand All @@ -817,10 +817,10 @@ func testCompile(t testing.TB, env *Env, expr string, want any) {
if err != nil {
t.Fatalf("env.Program() failed: %v", err)
}
out, _, err := prg.Eval(map[string]any{"err": types.NewErr("error argument"), "unk": types.Unknown{42}})
out, _, err := prg.Eval(map[string]any{"err": types.NewErr("error argument"), "unk": types.NewUnknown(42, nil)})
switch want := want.(type) {
case types.Unknown:
if !reflect.DeepEqual(want, out.(types.Unknown)) {
case *types.Unknown:
if !want.Contains(out.(*types.Unknown)) {
t.Errorf("prg.Eval() got %v, wanted %v", out, want)
}
case ref.Val:
Expand Down
6 changes: 3 additions & 3 deletions common/decls/decls.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,17 +275,17 @@ func (f *FunctionDecl) Bindings() ([]*functions.Overload, error) {
// to return an unknown set, or to produce a new error for a missing function signature.
func MaybeNoSuchOverload(funcName string, args ...ref.Val) ref.Val {
argTypes := make([]string, len(args))
var unk types.Unknown
var unk *types.Unknown = nil
for i, arg := range args {
if types.IsError(arg) {
return arg
}
if types.IsUnknown(arg) {
unk = append(unk, arg.(types.Unknown)...)
unk = types.MergeUnknowns(arg.(*types.Unknown), unk)
}
argTypes[i] = arg.Type().TypeName()
}
if len(unk) != 0 {
if unk != nil {
return unk
}
signature := strings.Join(argTypes, ", ")
Expand Down
2 changes: 1 addition & 1 deletion common/decls/decls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func TestFunctionVariableArgBindings(t *testing.T) {
if !types.IsError(celErr) || !strings.Contains(celErr.(*types.Err).String(), "no such overload") {
t.Errorf("binding.Binary(bytes, string) got %v, wanted no such overload", celErr)
}
celUnk := binding.Binary(types.Bytes("hi"), types.Unknown{1})
celUnk := binding.Binary(types.Bytes("hi"), types.NewUnknown(1, types.NewAttributeTrail("x")))
if !types.IsUnknown(celUnk) {
t.Errorf("binding.Binary(bytes, unk) got %v, wanted unknown{1}", celUnk)
}
Expand Down
Loading

0 comments on commit bad352c

Please sign in to comment.