Skip to content

Commit df2cccd

Browse files
authored
Fix nil key check in map (#351)
1 parent 5613693 commit df2cccd

File tree

3 files changed

+55
-6
lines changed

3 files changed

+55
-6
lines changed

checker/checker.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ func (v *visitor) MemberNode(node *ast.MemberNode) (reflect.Type, info) {
417417
return anyType, info{}
418418

419419
case reflect.Map:
420-
if !prop.AssignableTo(base.Key()) && !isAny(prop) {
420+
if prop != nil && !prop.AssignableTo(base.Key()) && !isAny(prop) {
421421
return v.error(node.Property, "cannot use %v to get an element from %v", prop, base)
422422
}
423423
t, c := deref(base.Elem())

expr_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -1772,6 +1772,45 @@ func TestRun_NilCoalescingOperator(t *testing.T) {
17721772
})
17731773
}
17741774

1775+
func TestEval_nil_in_maps(t *testing.T) {
1776+
env := map[string]interface{}{
1777+
"m": map[interface{}]interface{}{nil: "bar"},
1778+
"empty": map[interface{}]interface{}{},
1779+
}
1780+
t.Run("nil key exists", func(t *testing.T) {
1781+
p, err := expr.Compile(`m[nil]`, expr.Env(env))
1782+
assert.NoError(t, err)
1783+
1784+
out, err := expr.Run(p, env)
1785+
assert.NoError(t, err)
1786+
assert.Equal(t, "bar", out)
1787+
})
1788+
t.Run("no nil key", func(t *testing.T) {
1789+
p, err := expr.Compile(`empty[nil]`, expr.Env(env))
1790+
assert.NoError(t, err)
1791+
1792+
out, err := expr.Run(p, env)
1793+
assert.NoError(t, err)
1794+
assert.Equal(t, nil, out)
1795+
})
1796+
t.Run("nil in m", func(t *testing.T) {
1797+
p, err := expr.Compile(`nil in m`, expr.Env(env))
1798+
assert.NoError(t, err)
1799+
1800+
out, err := expr.Run(p, env)
1801+
assert.NoError(t, err)
1802+
assert.Equal(t, true, out)
1803+
})
1804+
t.Run("nil in empty", func(t *testing.T) {
1805+
p, err := expr.Compile(`nil in empty`, expr.Env(env))
1806+
assert.NoError(t, err)
1807+
1808+
out, err := expr.Run(p, env)
1809+
assert.NoError(t, err)
1810+
assert.Equal(t, false, out)
1811+
})
1812+
}
1813+
17751814
// Mock types
17761815

17771816
type mockEnv struct {

vm/runtime/runtime.go

+15-5
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,12 @@ func Fetch(from, i interface{}) interface{} {
4848
}
4949

5050
case reflect.Map:
51-
value := v.MapIndex(reflect.ValueOf(i))
51+
var value reflect.Value
52+
if i == nil {
53+
value = v.MapIndex(reflect.Zero(v.Type().Key()))
54+
} else {
55+
value = v.MapIndex(reflect.ValueOf(i))
56+
}
5257
if value.IsValid() {
5358
return value.Interface()
5459
} else {
@@ -221,11 +226,16 @@ func In(needle interface{}, array interface{}) bool {
221226
return false
222227

223228
case reflect.Map:
224-
n := reflect.ValueOf(needle)
225-
if !n.IsValid() {
226-
panic(fmt.Sprintf("cannot use %T as index to %T", needle, array))
229+
var value reflect.Value
230+
if needle == nil {
231+
value = v.MapIndex(reflect.Zero(v.Type().Key()))
232+
} else {
233+
n := reflect.ValueOf(needle)
234+
if !n.IsValid() {
235+
panic(fmt.Sprintf("cannot use %T as index to %T", needle, array))
236+
}
237+
value = v.MapIndex(n)
227238
}
228-
value := v.MapIndex(n)
229239
if value.IsValid() {
230240
return true
231241
}

0 commit comments

Comments
 (0)