mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
@@ -535,6 +535,14 @@ func (v *checker) MemberNode(node *ast.MemberNode) Nature {
|
||||
}
|
||||
}
|
||||
|
||||
// Not found.
|
||||
|
||||
if name, ok := node.Property.(*ast.StringNode); ok {
|
||||
if node.Method {
|
||||
return v.error(node, "type %v has no method %v", base, name.Value)
|
||||
}
|
||||
return v.error(node, "type %v has no field %v", base, name.Value)
|
||||
}
|
||||
return v.error(node, "type %v[%v] is undefined", base, prop)
|
||||
}
|
||||
|
||||
|
||||
@@ -1055,7 +1055,7 @@ func TestCheck_builtin_without_call(t *testing.T) {
|
||||
err string
|
||||
}{
|
||||
{`len + 1`, "invalid operation: + (mismatched types func(...interface {}) (interface {}, error) and int) (1:5)\n | len + 1\n | ....^"},
|
||||
{`string.A`, "type func(interface {}) string[string] is undefined (1:8)\n | string.A\n | .......^"},
|
||||
{`string.A`, "type func(interface {}) string has no field A (1:8)\n | string.A\n | .......^"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -74,7 +74,7 @@ func (n Nature) Elem() Nature {
|
||||
func (n Nature) AssignableTo(nt Nature) bool {
|
||||
if n.Nil {
|
||||
// Untyped nil is assignable to any interface, but implements only the empty interface.
|
||||
if nt.Type != nil && nt.Type.Kind() == reflect.Interface {
|
||||
if isAny(nt) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -84,6 +84,13 @@ func (n Nature) AssignableTo(nt Nature) bool {
|
||||
return n.Type.AssignableTo(nt.Type)
|
||||
}
|
||||
|
||||
func (n Nature) NumMethods() int {
|
||||
if n.Type == nil {
|
||||
return 0
|
||||
}
|
||||
return n.Type.NumMethod()
|
||||
}
|
||||
|
||||
func (n Nature) MethodByName(name string) (Nature, bool) {
|
||||
if n.Type == nil {
|
||||
return unknown, false
|
||||
|
||||
@@ -6,6 +6,10 @@ import (
|
||||
"github.com/expr-lang/expr/internal/deref"
|
||||
)
|
||||
|
||||
func isAny(nt Nature) bool {
|
||||
return nt.Kind() == reflect.Interface && nt.NumMethods() == 0
|
||||
}
|
||||
|
||||
func fieldName(field reflect.StructField) string {
|
||||
if taggedName := field.Tag.Get("expr"); taggedName != "" {
|
||||
return taggedName
|
||||
|
||||
+5
-1
@@ -74,12 +74,16 @@ func isUnknown(nt Nature) bool {
|
||||
switch {
|
||||
case nt.Type == nil && !nt.Nil:
|
||||
return true
|
||||
case nt.Kind() == reflect.Interface:
|
||||
case isAny(nt):
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isAny(nt Nature) bool {
|
||||
return nt.Kind() == reflect.Interface && nt.NumMethods() == 0
|
||||
}
|
||||
|
||||
func isInteger(nt Nature) bool {
|
||||
switch nt.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package interface_method_test
|
||||
package interface_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -0,0 +1,40 @@
|
||||
package interface_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/expr-lang/expr"
|
||||
"github.com/expr-lang/expr/internal/testify/assert"
|
||||
)
|
||||
|
||||
type StoreInterface interface {
|
||||
Get(string) int
|
||||
}
|
||||
|
||||
type StoreImpt struct{}
|
||||
|
||||
func (f StoreImpt) Get(s string) int {
|
||||
return 42
|
||||
}
|
||||
|
||||
func (f StoreImpt) Set(s string, i int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type Env struct {
|
||||
Store StoreInterface `expr:"store"`
|
||||
}
|
||||
|
||||
func TestInterfaceHide(t *testing.T) {
|
||||
var env Env
|
||||
p, err := expr.Compile(`store.Get("foo")`, expr.Env(env))
|
||||
assert.NoError(t, err)
|
||||
|
||||
out, err := expr.Run(p, Env{Store: StoreImpt{}})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 42, out)
|
||||
|
||||
_, err = expr.Compile(`store.Set("foo", 100)`, expr.Env(env))
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "type interface_test.StoreInterface has no method Set")
|
||||
}
|
||||
Reference in New Issue
Block a user