350 lines
6.4 KiB
Go
350 lines
6.4 KiB
Go
package checker
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/antonmedv/expr/ast"
|
|
)
|
|
|
|
var (
|
|
nilType = reflect.TypeOf(nil)
|
|
boolType = reflect.TypeOf(true)
|
|
integerType = reflect.TypeOf(int(0))
|
|
floatType = reflect.TypeOf(float64(0))
|
|
stringType = reflect.TypeOf("")
|
|
arrayType = reflect.TypeOf([]interface{}{})
|
|
mapType = reflect.TypeOf(map[string]interface{}{})
|
|
interfaceType = reflect.TypeOf(new(interface{})).Elem()
|
|
)
|
|
|
|
func typeWeight(t reflect.Type) int {
|
|
switch t.Kind() {
|
|
case reflect.Uint:
|
|
return 1
|
|
case reflect.Uint8:
|
|
return 2
|
|
case reflect.Uint16:
|
|
return 3
|
|
case reflect.Uint32:
|
|
return 4
|
|
case reflect.Uint64:
|
|
return 5
|
|
case reflect.Int:
|
|
return 6
|
|
case reflect.Int8:
|
|
return 7
|
|
case reflect.Int16:
|
|
return 8
|
|
case reflect.Int32:
|
|
return 9
|
|
case reflect.Int64:
|
|
return 10
|
|
case reflect.Float32:
|
|
return 11
|
|
case reflect.Float64:
|
|
return 12
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
func combined(a, b reflect.Type) reflect.Type {
|
|
if typeWeight(a) > typeWeight(b) {
|
|
return a
|
|
} else {
|
|
return b
|
|
}
|
|
}
|
|
|
|
func dereference(t reflect.Type) reflect.Type {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
if t.Kind() == reflect.Ptr {
|
|
t = dereference(t.Elem())
|
|
}
|
|
return t
|
|
}
|
|
|
|
func isComparable(l, r reflect.Type) bool {
|
|
l = dereference(l)
|
|
r = dereference(r)
|
|
|
|
if l == nil || r == nil { // It is possible to compare with nil.
|
|
return true
|
|
}
|
|
if l.Kind() == r.Kind() {
|
|
return true
|
|
}
|
|
if isInterface(l) || isInterface(r) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isInterface(t reflect.Type) bool {
|
|
t = dereference(t)
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Interface:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isInteger(t reflect.Type) bool {
|
|
t = dereference(t)
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
fallthrough
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
return true
|
|
case reflect.Interface:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isFloat(t reflect.Type) bool {
|
|
t = dereference(t)
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Float32, reflect.Float64:
|
|
return true
|
|
case reflect.Interface:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isNumber(t reflect.Type) bool {
|
|
return isInteger(t) || isFloat(t)
|
|
}
|
|
|
|
func isBool(t reflect.Type) bool {
|
|
t = dereference(t)
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Bool:
|
|
return true
|
|
case reflect.Interface:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isString(t reflect.Type) bool {
|
|
t = dereference(t)
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.String:
|
|
return true
|
|
case reflect.Interface:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isArray(t reflect.Type) bool {
|
|
t = dereference(t)
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Slice, reflect.Array:
|
|
return true
|
|
case reflect.Interface:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isMap(t reflect.Type) bool {
|
|
t = dereference(t)
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Map:
|
|
return true
|
|
case reflect.Interface:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isStruct(t reflect.Type) bool {
|
|
t = dereference(t)
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Struct:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isFunc(t reflect.Type) bool {
|
|
t = dereference(t)
|
|
if t != nil {
|
|
switch t.Kind() {
|
|
case reflect.Func:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func fieldType(ntype reflect.Type, name string) (reflect.Type, bool) {
|
|
ntype = dereference(ntype)
|
|
if ntype != nil {
|
|
switch ntype.Kind() {
|
|
case reflect.Interface:
|
|
return interfaceType, true
|
|
case reflect.Struct:
|
|
// First check all struct's fields.
|
|
for i := 0; i < ntype.NumField(); i++ {
|
|
f := ntype.Field(i)
|
|
if f.Name == name {
|
|
return f.Type, true
|
|
}
|
|
}
|
|
|
|
// Second check fields of embedded structs.
|
|
for i := 0; i < ntype.NumField(); i++ {
|
|
f := ntype.Field(i)
|
|
if f.Anonymous {
|
|
if t, ok := fieldType(f.Type, name); ok {
|
|
return t, true
|
|
}
|
|
}
|
|
}
|
|
case reflect.Map:
|
|
return ntype.Elem(), true
|
|
}
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
func methodType(t reflect.Type, name string) (reflect.Type, bool, bool) {
|
|
if t != nil {
|
|
// First, check methods defined on type itself,
|
|
// independent of which type it is.
|
|
if m, ok := t.MethodByName(name); ok {
|
|
if t.Kind() == reflect.Interface {
|
|
// In case of interface type method will not have a receiver,
|
|
// and to prevent checker decreasing numbers of in arguments
|
|
// return method type as not method (second argument is false).
|
|
return m.Type, false, true
|
|
} else {
|
|
return m.Type, true, true
|
|
}
|
|
}
|
|
|
|
d := t
|
|
if t.Kind() == reflect.Ptr {
|
|
d = t.Elem()
|
|
}
|
|
|
|
switch d.Kind() {
|
|
case reflect.Interface:
|
|
return interfaceType, false, true
|
|
case reflect.Struct:
|
|
// First, check all struct's fields.
|
|
for i := 0; i < d.NumField(); i++ {
|
|
f := d.Field(i)
|
|
if !f.Anonymous && f.Name == name {
|
|
return f.Type, false, true
|
|
}
|
|
}
|
|
|
|
// Second, check fields of embedded structs.
|
|
for i := 0; i < d.NumField(); i++ {
|
|
f := d.Field(i)
|
|
if f.Anonymous {
|
|
if t, method, ok := methodType(f.Type, name); ok {
|
|
return t, method, true
|
|
}
|
|
}
|
|
}
|
|
|
|
case reflect.Map:
|
|
return d.Elem(), false, true
|
|
}
|
|
}
|
|
return nil, false, false
|
|
}
|
|
|
|
func indexType(ntype reflect.Type) (reflect.Type, bool) {
|
|
ntype = dereference(ntype)
|
|
if ntype == nil {
|
|
return nil, false
|
|
}
|
|
|
|
switch ntype.Kind() {
|
|
case reflect.Interface:
|
|
return interfaceType, true
|
|
case reflect.Map, reflect.Array, reflect.Slice:
|
|
return ntype.Elem(), true
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
func isFuncType(ntype reflect.Type) (reflect.Type, bool) {
|
|
ntype = dereference(ntype)
|
|
if ntype == nil {
|
|
return nil, false
|
|
}
|
|
|
|
switch ntype.Kind() {
|
|
case reflect.Interface:
|
|
return interfaceType, true
|
|
case reflect.Func:
|
|
return ntype, true
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
func isIntegerOrArithmeticOperation(node ast.Node) bool {
|
|
switch n := node.(type) {
|
|
case *ast.IntegerNode:
|
|
return true
|
|
case *ast.UnaryNode:
|
|
switch n.Operator {
|
|
case "+", "-":
|
|
return true
|
|
}
|
|
case *ast.BinaryNode:
|
|
switch n.Operator {
|
|
case "+", "/", "-", "*":
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func setTypeForIntegers(node ast.Node, t reflect.Type) {
|
|
switch n := node.(type) {
|
|
case *ast.IntegerNode:
|
|
n.SetType(t)
|
|
case *ast.UnaryNode:
|
|
switch n.Operator {
|
|
case "+", "-":
|
|
setTypeForIntegers(n.Node, t)
|
|
}
|
|
case *ast.BinaryNode:
|
|
switch n.Operator {
|
|
case "+", "/", "-", "*":
|
|
setTypeForIntegers(n.Left, t)
|
|
setTypeForIntegers(n.Right, t)
|
|
}
|
|
}
|
|
}
|