proc/eval: support for simple builtin functions
supported: len, cap, imag, real, complex
This commit is contained in:
parent
48e13a9045
commit
141fc4ed21
@ -41,6 +41,7 @@ func main() {
|
|||||||
i3 := 3
|
i3 := 3
|
||||||
p1 := &i1
|
p1 := &i1
|
||||||
s1 := []string{"one", "two", "three", "four", "five"}
|
s1 := []string{"one", "two", "three", "four", "five"}
|
||||||
|
s3 := make([]int, 0, 6)
|
||||||
a1 := [5]string{"one", "two", "three", "four", "five"}
|
a1 := [5]string{"one", "two", "three", "four", "five"}
|
||||||
c1 := cstruct{&bstruct{astruct{1, 2}}, []*astruct{&astruct{1, 2}, &astruct{2, 3}, &astruct{4, 5}}}
|
c1 := cstruct{&bstruct{astruct{1, 2}}, []*astruct{&astruct{1, 2}, &astruct{2, 3}, &astruct{4, 5}}}
|
||||||
s2 := []astruct{{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}}
|
s2 := []astruct{{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14}, {15, 16}}
|
||||||
@ -109,11 +110,14 @@ func main() {
|
|||||||
var errnil error = nil
|
var errnil error = nil
|
||||||
var iface1 interface{} = c1.sa[0]
|
var iface1 interface{} = c1.sa[0]
|
||||||
var ifacenil interface{} = nil
|
var ifacenil interface{} = nil
|
||||||
|
arr1 := [4]int{0, 1, 2, 3}
|
||||||
|
parr := &arr1
|
||||||
|
cpx1 := complex(1, 2)
|
||||||
|
|
||||||
var amb1 = 1
|
var amb1 = 1
|
||||||
runtime.Breakpoint()
|
runtime.Breakpoint()
|
||||||
for amb1 := 0; amb1 < 10; amb1++ {
|
for amb1 := 0; amb1 < 10; amb1++ {
|
||||||
fmt.Println(amb1)
|
fmt.Println(amb1)
|
||||||
}
|
}
|
||||||
fmt.Println(i1, i2, i3, p1, amb1, s1, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, ifacenil)
|
fmt.Println(i1, i2, i3, p1, amb1, s1, s3, a1, p2, p3, s2, as1, str1, f1, fn1, fn2, nilslice, nilptr, ch1, chnil, m1, mnil, m2, m3, up1, i4, i5, i6, err1, err2, errnil, iface1, ifacenil, arr1, parr, cpx1)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -104,6 +104,8 @@ func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var TypeNotFoundErr = errors.New("no type entry found")
|
||||||
|
|
||||||
// SeekToType moves the reader to the type specified by the entry,
|
// SeekToType moves the reader to the type specified by the entry,
|
||||||
// optionally resolving typedefs and pointer types. If the reader is set
|
// optionally resolving typedefs and pointer types. If the reader is set
|
||||||
// to a struct type the NextMemberVariable call can be used to walk all member data.
|
// to a struct type the NextMemberVariable call can be used to walk all member data.
|
||||||
@ -138,7 +140,7 @@ func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resol
|
|||||||
reader.Seek(offset)
|
reader.Seek(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("no type entry found")
|
return nil, TypeNotFoundErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// SeekToTypeNamed moves the reader to the type specified by the name.
|
// SeekToTypeNamed moves the reader to the type specified by the name.
|
||||||
@ -168,7 +170,7 @@ func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("no type entry found")
|
return nil, TypeNotFoundErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds the entry for 'name'.
|
// Finds the entry for 'name'.
|
||||||
|
|||||||
257
proc/eval.go
257
proc/eval.go
@ -5,6 +5,7 @@ import (
|
|||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/derekparker/delve/dwarf/reader"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
@ -31,12 +32,16 @@ func (scope *EvalScope) EvalExpression(expr string) (*Variable, error) {
|
|||||||
func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
|
func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
|
||||||
switch node := t.(type) {
|
switch node := t.(type) {
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
if fnnode, ok := node.Fun.(*ast.Ident); ok && len(node.Args) == 2 && (fnnode.Name == "complex64" || fnnode.Name == "complex128") {
|
if len(node.Args) == 1 {
|
||||||
// implement the special case type casts complex64(f1, f2) and complex128(f1, f2)
|
v, err := scope.evalTypeCast(node)
|
||||||
return scope.evalComplexCast(fnnode.Name, node)
|
if err == nil {
|
||||||
|
return v, nil
|
||||||
}
|
}
|
||||||
// this must be a type cast because we do not support function calls
|
if err != reader.TypeNotFoundErr {
|
||||||
return scope.evalTypeCast(node)
|
return v, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scope.evalBuiltinCall(node)
|
||||||
|
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
return scope.evalIdent(node)
|
return scope.evalIdent(node)
|
||||||
@ -99,56 +104,8 @@ func exprToString(t ast.Expr) string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eval expressions: complex64(<float const>, <float const>) and complex128(<float const>, <float const>)
|
|
||||||
func (scope *EvalScope) evalComplexCast(typename string, node *ast.CallExpr) (*Variable, error) {
|
|
||||||
realev, err := scope.evalAST(node.Args[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
imagev, err := scope.evalAST(node.Args[1])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sz := 128
|
|
||||||
ftypename := "float64"
|
|
||||||
if typename == "complex64" {
|
|
||||||
sz = 64
|
|
||||||
ftypename = "float32"
|
|
||||||
}
|
|
||||||
|
|
||||||
realev.loadValue()
|
|
||||||
imagev.loadValue()
|
|
||||||
|
|
||||||
if realev.Unreadable != nil {
|
|
||||||
return nil, realev.Unreadable
|
|
||||||
}
|
|
||||||
|
|
||||||
if imagev.Unreadable != nil {
|
|
||||||
return nil, imagev.Unreadable
|
|
||||||
}
|
|
||||||
|
|
||||||
if realev.Value == nil || ((realev.Value.Kind() != constant.Int) && (realev.Value.Kind() != constant.Float)) {
|
|
||||||
return nil, fmt.Errorf("can not convert \"%s\" to %s", exprToString(node.Args[0]), ftypename)
|
|
||||||
}
|
|
||||||
|
|
||||||
if imagev.Value == nil || ((imagev.Value.Kind() != constant.Int) && (imagev.Value.Kind() != constant.Float)) {
|
|
||||||
return nil, fmt.Errorf("can not convert \"%s\" to %s", exprToString(node.Args[1]), ftypename)
|
|
||||||
}
|
|
||||||
|
|
||||||
typ := &dwarf.ComplexType{dwarf.BasicType{dwarf.CommonType{ByteSize: int64(sz / 8), Name: typename}, int64(sz), 0}}
|
|
||||||
|
|
||||||
r := newVariable("", 0, typ, scope.Thread)
|
|
||||||
r.Value = constant.BinaryOp(realev.Value, token.ADD, constant.MakeImag(imagev.Value))
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Eval type cast expressions
|
// Eval type cast expressions
|
||||||
func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
|
func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
|
||||||
if len(node.Args) != 1 {
|
|
||||||
return nil, fmt.Errorf("wrong number of arguments for a type cast")
|
|
||||||
}
|
|
||||||
|
|
||||||
argv, err := scope.evalAST(node.Args[0])
|
argv, err := scope.evalAST(node.Args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -265,6 +222,200 @@ func convertInt(n uint64, signed bool, size int64) uint64 {
|
|||||||
return uint64(binary.BigEndian.Uint64(buf))
|
return uint64(binary.BigEndian.Uint64(buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (scope *EvalScope) evalBuiltinCall(node *ast.CallExpr) (*Variable, error) {
|
||||||
|
fnnode, ok := node.Fun.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("function calls are not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]*Variable, len(node.Args))
|
||||||
|
|
||||||
|
for i := range node.Args {
|
||||||
|
v, err := scope.evalAST(node.Args[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
args[i] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fnnode.Name {
|
||||||
|
case "cap":
|
||||||
|
return capBuiltin(args, node.Args)
|
||||||
|
case "len":
|
||||||
|
return lenBuiltin(args, node.Args)
|
||||||
|
case "complex":
|
||||||
|
return complexBuiltin(args, node.Args)
|
||||||
|
case "imag":
|
||||||
|
return imagBuiltin(args, node.Args)
|
||||||
|
case "real":
|
||||||
|
return realBuiltin(args, node.Args)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("function calls are not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func capBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, fmt.Errorf("wrong number of arguments to cap: %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := args[0]
|
||||||
|
invalidArgErr := fmt.Errorf("invalid argument %s (type %s) for cap", exprToString(nodeargs[0]), arg.TypeString())
|
||||||
|
|
||||||
|
switch arg.Kind {
|
||||||
|
case reflect.Ptr:
|
||||||
|
arg = arg.maybeDereference()
|
||||||
|
if arg.Kind != reflect.Array {
|
||||||
|
return nil, invalidArgErr
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case reflect.Array:
|
||||||
|
return newConstant(constant.MakeInt64(arg.Len), arg.thread), nil
|
||||||
|
case reflect.Slice:
|
||||||
|
return newConstant(constant.MakeInt64(arg.Cap), arg.thread), nil
|
||||||
|
case reflect.Chan:
|
||||||
|
arg.loadValue()
|
||||||
|
if arg.Unreadable != nil {
|
||||||
|
return nil, arg.Unreadable
|
||||||
|
}
|
||||||
|
if arg.base == 0 {
|
||||||
|
return newConstant(constant.MakeInt64(0), arg.thread), nil
|
||||||
|
}
|
||||||
|
return newConstant(arg.Children[1].Value, arg.thread), nil
|
||||||
|
default:
|
||||||
|
return nil, invalidArgErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func lenBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, fmt.Errorf("wrong number of arguments to len: %d", len(args))
|
||||||
|
}
|
||||||
|
arg := args[0]
|
||||||
|
invalidArgErr := fmt.Errorf("invalid argument %s (type %s) for len", exprToString(nodeargs[0]), arg.TypeString())
|
||||||
|
|
||||||
|
switch arg.Kind {
|
||||||
|
case reflect.Ptr:
|
||||||
|
arg = arg.maybeDereference()
|
||||||
|
if arg.Kind != reflect.Array {
|
||||||
|
return nil, invalidArgErr
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case reflect.Array, reflect.Slice, reflect.String:
|
||||||
|
if arg.Unreadable != nil {
|
||||||
|
return nil, arg.Unreadable
|
||||||
|
}
|
||||||
|
return newConstant(constant.MakeInt64(arg.Len), arg.thread), nil
|
||||||
|
case reflect.Chan:
|
||||||
|
arg.loadValue()
|
||||||
|
if arg.Unreadable != nil {
|
||||||
|
return nil, arg.Unreadable
|
||||||
|
}
|
||||||
|
if arg.base == 0 {
|
||||||
|
return newConstant(constant.MakeInt64(0), arg.thread), nil
|
||||||
|
}
|
||||||
|
return newConstant(arg.Children[0].Value, arg.thread), nil
|
||||||
|
case reflect.Map:
|
||||||
|
it := arg.mapIterator()
|
||||||
|
if arg.Unreadable != nil {
|
||||||
|
return nil, arg.Unreadable
|
||||||
|
}
|
||||||
|
if it == nil {
|
||||||
|
return newConstant(constant.MakeInt64(0), arg.thread), nil
|
||||||
|
}
|
||||||
|
return newConstant(constant.MakeInt64(arg.Len), arg.thread), nil
|
||||||
|
default:
|
||||||
|
return nil, invalidArgErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func complexBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, fmt.Errorf("wrong number of arguments to complex: %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
realev := args[0]
|
||||||
|
imagev := args[1]
|
||||||
|
|
||||||
|
realev.loadValue()
|
||||||
|
imagev.loadValue()
|
||||||
|
|
||||||
|
if realev.Unreadable != nil {
|
||||||
|
return nil, realev.Unreadable
|
||||||
|
}
|
||||||
|
|
||||||
|
if imagev.Unreadable != nil {
|
||||||
|
return nil, imagev.Unreadable
|
||||||
|
}
|
||||||
|
|
||||||
|
if realev.Value == nil || ((realev.Value.Kind() != constant.Int) && (realev.Value.Kind() != constant.Float)) {
|
||||||
|
return nil, fmt.Errorf("invalid argument 1 %s (type %s) to complex", exprToString(nodeargs[0]), realev.TypeString())
|
||||||
|
}
|
||||||
|
|
||||||
|
if imagev.Value == nil || ((imagev.Value.Kind() != constant.Int) && (imagev.Value.Kind() != constant.Float)) {
|
||||||
|
return nil, fmt.Errorf("invalid argument 2 %s (type %s) to complex", exprToString(nodeargs[1]), imagev.TypeString())
|
||||||
|
}
|
||||||
|
|
||||||
|
sz := int64(0)
|
||||||
|
if realev.RealType != nil {
|
||||||
|
sz = realev.RealType.(*dwarf.FloatType).Size()
|
||||||
|
}
|
||||||
|
if imagev.RealType != nil {
|
||||||
|
isz := imagev.RealType.(*dwarf.FloatType).Size()
|
||||||
|
if isz > sz {
|
||||||
|
sz = isz
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sz == 0 {
|
||||||
|
sz = 128
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := &dwarf.ComplexType{dwarf.BasicType{dwarf.CommonType{ByteSize: int64(sz / 8), Name: fmt.Sprintf("complex%d", sz)}, sz, 0}}
|
||||||
|
|
||||||
|
r := newVariable("", 0, typ, realev.thread)
|
||||||
|
r.Value = constant.BinaryOp(realev.Value, token.ADD, constant.MakeImag(imagev.Value))
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func imagBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, fmt.Errorf("wrong number of arguments to imag: %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := args[0]
|
||||||
|
arg.loadValue()
|
||||||
|
|
||||||
|
if arg.Unreadable != nil {
|
||||||
|
return nil, arg.Unreadable
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg.Kind != reflect.Complex64 && arg.Kind != reflect.Complex128 {
|
||||||
|
return nil, fmt.Errorf("invalid argument %s (type %s) to imag", exprToString(nodeargs[0]), arg.TypeString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return newConstant(constant.Imag(arg.Value), arg.thread), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func realBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
return nil, fmt.Errorf("wrong number of arguments to real: %d", len(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
arg := args[0]
|
||||||
|
arg.loadValue()
|
||||||
|
|
||||||
|
if arg.Unreadable != nil {
|
||||||
|
return nil, arg.Unreadable
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg.Value == nil || ((arg.Value.Kind() != constant.Int) && (arg.Value.Kind() != constant.Float) && (arg.Value.Kind() != constant.Complex)) {
|
||||||
|
return nil, fmt.Errorf("invalid argument %s (type %s) to real", exprToString(nodeargs[0]), arg.TypeString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return newConstant(constant.Real(arg.Value), arg.thread), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Evaluates identifier expressions
|
// Evaluates identifier expressions
|
||||||
func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
|
func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
|
||||||
switch node.Name {
|
switch node.Name {
|
||||||
|
|||||||
@ -344,7 +344,7 @@ func TestComplexSetting(t *testing.T) {
|
|||||||
h("3.2i", "(0 + 3.2i)")
|
h("3.2i", "(0 + 3.2i)")
|
||||||
h("1.1", "(1.1 + 0i)")
|
h("1.1", "(1.1 + 0i)")
|
||||||
h("1 + 3.3i", "(1 + 3.3i)")
|
h("1 + 3.3i", "(1 + 3.3i)")
|
||||||
h("complex128(1.2, 3.4)", "(1.2 + 3.4i)")
|
h("complex(1.2, 3.4)", "(1.2 + 3.4i)")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,6 +462,28 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
{"c1.pb.a == *(c1.sa[1])", false, "false", "", "", nil},
|
{"c1.pb.a == *(c1.sa[1])", false, "false", "", "", nil},
|
||||||
{"c1.pb.a != *(c1.sa[1])", false, "true", "", "", nil},
|
{"c1.pb.a != *(c1.sa[1])", false, "true", "", "", nil},
|
||||||
|
|
||||||
|
// builtins
|
||||||
|
{"cap(parr)", false, "4", "", "", nil},
|
||||||
|
{"len(parr)", false, "4", "", "", nil},
|
||||||
|
{"cap(p1)", false, "", "", "", fmt.Errorf("invalid argument p1 (type *int) for cap")},
|
||||||
|
{"len(p1)", false, "", "", "", fmt.Errorf("invalid argument p1 (type *int) for len")},
|
||||||
|
{"cap(a1)", false, "5", "", "", nil},
|
||||||
|
{"len(a1)", false, "5", "", "", nil},
|
||||||
|
{"cap(s3)", false, "6", "", "", nil},
|
||||||
|
{"len(s3)", false, "0", "", "", nil},
|
||||||
|
{"cap(nilslice)", false, "0", "", "", nil},
|
||||||
|
{"len(nilslice)", false, "0", "", "", nil},
|
||||||
|
{"cap(ch1)", false, "2", "", "", nil},
|
||||||
|
{"len(ch1)", false, "0", "", "", nil},
|
||||||
|
{"cap(chnil)", false, "0", "", "", nil},
|
||||||
|
{"len(chnil)", false, "0", "", "", nil},
|
||||||
|
{"len(m1)", false, "41", "", "", nil},
|
||||||
|
{"len(mnil)", false, "0", "", "", nil},
|
||||||
|
{"imag(cpx1)", false, "2", "", "", nil},
|
||||||
|
{"real(cpx1)", false, "1", "", "", nil},
|
||||||
|
{"imag(3i)", false, "3", "", "", nil},
|
||||||
|
{"real(4)", false, "4", "", "", nil},
|
||||||
|
|
||||||
// nil
|
// nil
|
||||||
{"nil", false, "nil", "", "", nil},
|
{"nil", false, "nil", "", "", nil},
|
||||||
{"nil+1", false, "", "", "", fmt.Errorf("operator + can not be applied to \"nil\"")},
|
{"nil+1", false, "", "", "", fmt.Errorf("operator + can not be applied to \"nil\"")},
|
||||||
@ -504,7 +526,7 @@ func TestEvalExpression(t *testing.T) {
|
|||||||
{"i2 << i3", false, "", "", "int", fmt.Errorf("shift count type int, must be unsigned integer")},
|
{"i2 << i3", false, "", "", "int", fmt.Errorf("shift count type int, must be unsigned integer")},
|
||||||
{"*(i2 + i3)", false, "", "", "", fmt.Errorf("expression \"(i2 + i3)\" (int) can not be dereferenced")},
|
{"*(i2 + i3)", false, "", "", "", fmt.Errorf("expression \"(i2 + i3)\" (int) can not be dereferenced")},
|
||||||
{"i2.member", false, "", "", "", fmt.Errorf("i2 (type int) is not a struct")},
|
{"i2.member", false, "", "", "", fmt.Errorf("i2 (type int) is not a struct")},
|
||||||
{"fmt.Println(\"hello\")", false, "", "", "", fmt.Errorf("no type entry found")},
|
{"fmt.Println(\"hello\")", false, "", "", "", fmt.Errorf("function calls are not supported")},
|
||||||
{"*nil", false, "", "", "", fmt.Errorf("nil can not be dereferenced")},
|
{"*nil", false, "", "", "", fmt.Errorf("nil can not be dereferenced")},
|
||||||
{"!nil", false, "", "", "", fmt.Errorf("operator ! can not be applied to \"nil\"")},
|
{"!nil", false, "", "", "", fmt.Errorf("operator ! can not be applied to \"nil\"")},
|
||||||
{"&nil", false, "", "", "", fmt.Errorf("can not take address of \"nil\"")},
|
{"&nil", false, "", "", "", fmt.Errorf("can not take address of \"nil\"")},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user