Skip to content

Commit 5916dc2

Browse files
committed
Add type checks for 42 in ["a"] as invalid
1 parent 89ddc14 commit 5916dc2

File tree

7 files changed

+38
-19
lines changed

7 files changed

+38
-19
lines changed

checker/checker.go

+25-3
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ type varScope struct {
7373
type info struct {
7474
method bool
7575
fn *ast.Function
76+
77+
// elem is element type of array or map.
78+
// Arrays created with type []any, but
79+
// we would like to detect expressions
80+
// like `42 in ["a"]` as invalid.
81+
elem reflect.Type
7682
}
7783

7884
func (v *checker) visit(node ast.Node) (reflect.Type, info) {
@@ -227,7 +233,7 @@ func (v *checker) UnaryNode(node *ast.UnaryNode) (reflect.Type, info) {
227233

228234
func (v *checker) BinaryNode(node *ast.BinaryNode) (reflect.Type, info) {
229235
l, _ := v.visit(node.Left)
230-
r, _ := v.visit(node.Right)
236+
r, ri := v.visit(node.Right)
231237

232238
l = deref(l)
233239
r = deref(r)
@@ -351,6 +357,9 @@ func (v *checker) BinaryNode(node *ast.BinaryNode) (reflect.Type, info) {
351357
if !isComparable(l, r.Elem()) {
352358
return v.error(node, "cannot use %v as type %v in array", l, r.Elem())
353359
}
360+
if !isComparable(l, ri.elem) {
361+
return v.error(node, "cannot use %v as type %v in array", l, ri.elem)
362+
}
354363
return boolType, info{}
355364
}
356365
if isAny(l) && anyOf(r, isString, isArray, isMap) {
@@ -1067,8 +1076,21 @@ func (v *checker) ConditionalNode(node *ast.ConditionalNode) (reflect.Type, info
10671076
}
10681077

10691078
func (v *checker) ArrayNode(node *ast.ArrayNode) (reflect.Type, info) {
1070-
for _, node := range node.Nodes {
1071-
v.visit(node)
1079+
var prev reflect.Type
1080+
allElementsAreSameType := true
1081+
for i, node := range node.Nodes {
1082+
curr, _ := v.visit(node)
1083+
if i > 0 {
1084+
if curr == nil || prev == nil {
1085+
allElementsAreSameType = false
1086+
} else if curr.Kind() != prev.Kind() {
1087+
allElementsAreSameType = false
1088+
}
1089+
}
1090+
prev = curr
1091+
}
1092+
if allElementsAreSameType && prev != nil {
1093+
return arrayType, info{elem: prev}
10721094
}
10731095
return arrayType, info{}
10741096
}

checker/checker_test.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import (
77
"strings"
88
"testing"
99

10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
1013
"github.com/antonmedv/expr"
1114
"github.com/antonmedv/expr/ast"
1215
"github.com/antonmedv/expr/checker"
1316
"github.com/antonmedv/expr/conf"
1417
"github.com/antonmedv/expr/parser"
1518
"github.com/antonmedv/expr/test/mock"
16-
"github.com/stretchr/testify/assert"
17-
"github.com/stretchr/testify/require"
1819
)
1920

2021
func TestCheck(t *testing.T) {
@@ -547,6 +548,11 @@ map(1..9, #unknown)
547548
unknown pointer #unknown (1:11)
548549
| map(1..9, #unknown)
549550
| ..........^
551+
552+
42 in ["a", "b", "c"]
553+
cannot use int as type string in array (1:4)
554+
| 42 in ["a", "b", "c"]
555+
| ...^
550556
`
551557

552558
func TestCheck_error(t *testing.T) {

expr_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import (
77
"testing"
88
"time"
99

10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
1013
"github.com/antonmedv/expr"
1114
"github.com/antonmedv/expr/ast"
1215
"github.com/antonmedv/expr/file"
1316
"github.com/antonmedv/expr/test/mock"
14-
"github.com/stretchr/testify/assert"
15-
"github.com/stretchr/testify/require"
1617
)
1718

1819
func ExampleEval() {
@@ -1997,4 +1998,4 @@ func TestIssue432(t *testing.T) {
19971998
out, err := expr.Run(program, env)
19981999
assert.NoError(t, err)
19992000
assert.Equal(t, float64(10), out)
2000-
}
2001+
}

test/fuzz/fuzz_corpus.txt

-3
Original file line numberDiff line numberDiff line change
@@ -2711,7 +2711,6 @@ add == score != true
27112711
add == score and i32 >= 1
27122712
add == score or 0.5 < 1
27132713
add in [div]
2714-
add not in [f64]
27152714
add not in sort(array)
27162715
add(1, 1) ** i
27172716
add(1, 1) ^ i
@@ -3113,7 +3112,6 @@ array == map(array, add)
31133112
array == nil != nil
31143113
array == nil != true ? 0.5 : 0.5
31153114
array == nil && (false || ok)
3116-
array in [i]
31173115
array[-1]
31183116
array[-i32]
31193117
array[-i64]
@@ -6102,7 +6100,6 @@ foo == foo ? i : score
61026100
foo == last(list)
61036101
foo == nil == nil
61046102
foo == nil || ok
6105-
foo in [half]
61066103
foo in filter(list, false)
61076104
foo in list
61086105
foo in list ? nil : i32

test/fuzz/fuzz_expr_seed_corpus.zip

13 Bytes
Binary file not shown.

testdata/examples.txt

-3
Original file line numberDiff line numberDiff line change
@@ -2711,7 +2711,6 @@ add == score != true
27112711
add == score and i32 >= 1
27122712
add == score or 0.5 < 1
27132713
add in [div]
2714-
add not in [f64]
27152714
add not in sort(array)
27162715
add(1, 1) ** i
27172716
add(1, 1) ^ i
@@ -3113,7 +3112,6 @@ array == map(array, add)
31133112
array == nil != nil
31143113
array == nil != true ? 0.5 : 0.5
31153114
array == nil && (false || ok)
3116-
array in [i]
31173115
array[-1]
31183116
array[-i32]
31193117
array[-i64]
@@ -6102,7 +6100,6 @@ foo == foo ? i : score
61026100
foo == last(list)
61036101
foo == nil == nil
61046102
foo == nil || ok
6105-
foo in [half]
61066103
foo in filter(list, false)
61076104
foo in list
61086105
foo in list ? nil : i32

vm/runtime/runtime.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -237,11 +237,7 @@ func In(needle any, array any) bool {
237237
if needle == nil {
238238
value = v.MapIndex(reflect.Zero(v.Type().Key()))
239239
} else {
240-
n := reflect.ValueOf(needle)
241-
if !n.IsValid() {
242-
panic(fmt.Sprintf("cannot use %T as index to %T", needle, array))
243-
}
244-
value = v.MapIndex(n)
240+
value = v.MapIndex(reflect.ValueOf(needle))
245241
}
246242
if value.IsValid() {
247243
return true

0 commit comments

Comments
 (0)