diff options
Diffstat (limited to 'src/cmd/gofix/reflect.go')
-rw-r--r-- | src/cmd/gofix/reflect.go | 843 |
1 files changed, 843 insertions, 0 deletions
diff --git a/src/cmd/gofix/reflect.go b/src/cmd/gofix/reflect.go new file mode 100644 index 000000000..74ddb398f --- /dev/null +++ b/src/cmd/gofix/reflect.go @@ -0,0 +1,843 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO(rsc): Once there is better support for writing +// multi-package commands, this should really be in +// its own package, and then we can drop all the "reflect" +// prefixes on the global variables and functions. + +package main + +import ( + "go/ast" + "go/token" + "strings" +) + +var reflectFix = fix{ + "reflect", + reflectFn, + `Adapt code to new reflect API. + +http://codereview.appspot.com/4281055 +`, +} + +func init() { + register(reflectFix) +} + +// The reflect API change dropped the concrete types *reflect.ArrayType etc. +// Any type assertions prior to method calls can be deleted: +// x.(*reflect.ArrayType).Len() -> x.Len() +// +// Any type checks can be replaced by assignment and check of Kind: +// x, y := z.(*reflect.ArrayType) +// -> +// x := z +// y := x.Kind() == reflect.Array +// +// If z is an ordinary variable name and x is not subsequently assigned to, +// references to x can be replaced by z and the assignment deleted. +// We only bother if x and z are the same name. +// If y is not subsequently assigned to and neither is x, references to +// y can be replaced by its expression. We only bother when there is +// just one use or when the use appears in an if clause. +// +// Not all type checks result in a single Kind check. The rewrite of the type check for +// reflect.ArrayOrSliceType checks x.Kind() against reflect.Array and reflect.Slice. +// The rewrite for *reflect.IntType checks againt Int, Int8, Int16, Int32, Int64. +// The rewrite for *reflect.UintType adds Uintptr. +// +// A type switch turns into an assignment and a switch on Kind: +// switch x := y.(type) { +// case reflect.ArrayOrSliceType: +// ... +// case *reflect.ChanType: +// ... +// case *reflect.IntType: +// ... +// } +// -> +// switch x := y; x.Kind() { +// case reflect.Array, reflect.Slice: +// ... +// case reflect.Chan: +// ... +// case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: +// ... +// } +// +// The same simplification applies: we drop x := x if x is not assigned +// to in the switch cases. +// +// Because the type check assignment includes a type assertion in its +// syntax and the rewrite traversal is bottom up, we must do a pass to +// rewrite the type check assignments and then a separate pass to +// rewrite the type assertions. +// +// The same process applies to the API changes for reflect.Value. +// +// For both cases, but especially Value, the code needs to be aware +// of the type of a receiver when rewriting a method call. For example, +// x.(*reflect.ArrayValue).Elem(i) becomes x.Index(i) while +// x.(*reflect.MapValue).Elem(v) becomes x.MapIndex(v). +// In general, reflectFn needs to know the type of the receiver expression. +// In most cases (and in all the cases in the Go source tree), the toy +// type checker in typecheck.go provides enough information for gofix +// to make the rewrite. If gofix misses a rewrite, the code that is left over +// will not compile, so it will be noticed immediately. + +func reflectFn(f *ast.File) bool { + if !imports(f, "reflect") { + return false + } + + fixed := false + + // Rewrite names in method calls. + // Needs basic type information (see above). + typeof := typecheck(reflectTypeConfig, f) + walk(f, func(n interface{}) { + switch n := n.(type) { + case *ast.SelectorExpr: + typ := typeof[n.X] + if m := reflectRewriteMethod[typ]; m != nil { + if replace := m[n.Sel.Name]; replace != "" { + n.Sel.Name = replace + fixed = true + return + } + } + + // For all reflect Values, replace SetValue with Set. + if isReflectValue[typ] && n.Sel.Name == "SetValue" { + n.Sel.Name = "Set" + fixed = true + return + } + + // Replace reflect.MakeZero with reflect.Zero. + if isPkgDot(n, "reflect", "MakeZero") { + n.Sel.Name = "Zero" + fixed = true + return + } + } + }) + + // Replace PtrValue's PointTo(x) with Set(x.Addr()). + walk(f, func(n interface{}) { + call, ok := n.(*ast.CallExpr) + if !ok || len(call.Args) != 1 { + return + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok || sel.Sel.Name != "PointTo" { + return + } + typ := typeof[sel.X] + if typ != "*reflect.PtrValue" { + return + } + sel.Sel.Name = "Set" + if !isTopName(call.Args[0], "nil") { + call.Args[0] = &ast.SelectorExpr{ + X: call.Args[0], + Sel: ast.NewIdent("Addr()"), + } + } + fixed = true + }) + + // Fix type switches. + walk(f, func(n interface{}) { + if reflectFixSwitch(n) { + fixed = true + } + }) + + // Fix type assertion checks (multiple assignment statements). + // Have to work on the statement context (statement list or if statement) + // so that we can insert an extra statement occasionally. + // Ignoring for and switch because they don't come up in + // typical code. + walk(f, func(n interface{}) { + switch n := n.(type) { + case *[]ast.Stmt: + // v is the replacement statement list. + var v []ast.Stmt + insert := func(x ast.Stmt) { + v = append(v, x) + } + for i, x := range *n { + // Tentatively append to v; if we rewrite x + // we'll have to update the entry, so remember + // the index. + j := len(v) + v = append(v, x) + if reflectFixTypecheck(&x, insert, (*n)[i+1:]) { + // reflectFixTypecheck may have overwritten x. + // Update the entry we appended just before the call. + v[j] = x + fixed = true + } + } + *n = v + case *ast.IfStmt: + x := &ast.ExprStmt{n.Cond} + if reflectFixTypecheck(&n.Init, nil, []ast.Stmt{x, n.Body, n.Else}) { + n.Cond = x.X + fixed = true + } + } + }) + + // Warn about any typecheck statements that we missed. + walk(f, reflectWarnTypecheckStmt) + + // Now that those are gone, fix remaining type assertions. + // Delayed because the type checks have + // type assertions as part of their syntax. + walk(f, func(n interface{}) { + if reflectFixAssert(n) { + fixed = true + } + }) + + // Now that the type assertions are gone, rewrite remaining + // references to specific reflect types to use the general ones. + walk(f, func(n interface{}) { + ptr, ok := n.(*ast.Expr) + if !ok { + return + } + nn := *ptr + typ := reflectType(nn) + if typ == "" { + return + } + if strings.HasSuffix(typ, "Type") { + *ptr = newPkgDot(nn.Pos(), "reflect", "Type") + } else { + *ptr = newPkgDot(nn.Pos(), "reflect", "Value") + } + fixed = true + }) + + // Rewrite v.Set(nil) to v.Set(reflect.MakeZero(v.Type())). + walk(f, func(n interface{}) { + call, ok := n.(*ast.CallExpr) + if !ok || len(call.Args) != 1 || !isTopName(call.Args[0], "nil") { + return + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok || !isReflectValue[typeof[sel.X]] || sel.Sel.Name != "Set" { + return + } + call.Args[0] = &ast.CallExpr{ + Fun: newPkgDot(call.Args[0].Pos(), "reflect", "Zero"), + Args: []ast.Expr{ + &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: sel.X, + Sel: &ast.Ident{Name: "Type"}, + }, + }, + }, + } + fixed = true + }) + + // Rewrite v != nil to v.IsValid(). + // Rewrite nil used as reflect.Value (in function argument or return) to reflect.Value{}. + walk(f, func(n interface{}) { + ptr, ok := n.(*ast.Expr) + if !ok { + return + } + if isTopName(*ptr, "nil") && isReflectValue[typeof[*ptr]] { + *ptr = ast.NewIdent("reflect.Value{}") + fixed = true + return + } + nn, ok := (*ptr).(*ast.BinaryExpr) + if !ok || (nn.Op != token.EQL && nn.Op != token.NEQ) || !isTopName(nn.Y, "nil") || !isReflectValue[typeof[nn.X]] { + return + } + var call ast.Expr = &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: nn.X, + Sel: &ast.Ident{Name: "IsValid"}, + }, + } + if nn.Op == token.EQL { + call = &ast.UnaryExpr{Op: token.NOT, X: call} + } + *ptr = call + fixed = true + }) + + return fixed +} + +// reflectFixSwitch rewrites *n (if n is an *ast.Stmt) corresponding +// to a type switch. +func reflectFixSwitch(n interface{}) bool { + ptr, ok := n.(*ast.Stmt) + if !ok { + return false + } + n = *ptr + + ts, ok := n.(*ast.TypeSwitchStmt) + if !ok { + return false + } + + // Are any switch cases referring to reflect types? + // (That is, is this an old reflect type switch?) + for _, cas := range ts.Body.List { + for _, typ := range cas.(*ast.CaseClause).List { + if reflectType(typ) != "" { + goto haveReflect + } + } + } + return false + +haveReflect: + // Now we know it's an old reflect type switch. Prepare the new version, + // but don't replace or edit the original until we're sure of success. + + // Figure out the initializer statement, if any, and the receiver for the Kind call. + var init ast.Stmt + var rcvr ast.Expr + + init = ts.Init + switch n := ts.Assign.(type) { + default: + warn(ts.Pos(), "unexpected form in type switch") + return false + + case *ast.AssignStmt: + as := n + ta := as.Rhs[0].(*ast.TypeAssertExpr) + x := isIdent(as.Lhs[0]) + z := isIdent(ta.X) + + if isBlank(x) || x != nil && z != nil && x.Name == z.Name && !assignsTo(x, ts.Body.List) { + // Can drop the variable creation. + rcvr = ta.X + } else { + // Need to use initialization statement. + if init != nil { + warn(ts.Pos(), "cannot rewrite reflect type switch with initializing statement") + return false + } + init = &ast.AssignStmt{ + Lhs: []ast.Expr{as.Lhs[0]}, + TokPos: as.TokPos, + Tok: token.DEFINE, + Rhs: []ast.Expr{ta.X}, + } + rcvr = as.Lhs[0] + } + + case *ast.ExprStmt: + rcvr = n.X.(*ast.TypeAssertExpr).X + } + + // Prepare rewritten type switch (see large comment above for form). + sw := &ast.SwitchStmt{ + Switch: ts.Switch, + Init: init, + Tag: &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: rcvr, + Sel: &ast.Ident{ + NamePos: rcvr.End(), + Name: "Kind", + Obj: nil, + }, + }, + Lparen: rcvr.End(), + Rparen: rcvr.End(), + }, + Body: &ast.BlockStmt{ + Lbrace: ts.Body.Lbrace, + List: nil, // to be filled in + Rbrace: ts.Body.Rbrace, + }, + } + + // Translate cases. + for _, tcas := range ts.Body.List { + tcas := tcas.(*ast.CaseClause) + cas := &ast.CaseClause{ + Case: tcas.Case, + Colon: tcas.Colon, + Body: tcas.Body, + } + for _, t := range tcas.List { + if isTopName(t, "nil") { + cas.List = append(cas.List, newPkgDot(t.Pos(), "reflect", "Invalid")) + continue + } + + typ := reflectType(t) + if typ == "" { + warn(t.Pos(), "cannot rewrite reflect type switch case with non-reflect type %s", gofmt(t)) + cas.List = append(cas.List, t) + continue + } + + for _, k := range reflectKind[typ] { + cas.List = append(cas.List, newPkgDot(t.Pos(), "reflect", k)) + } + } + sw.Body.List = append(sw.Body.List, cas) + } + + // Everything worked. Rewrite AST. + *ptr = sw + return true +} + +// Rewrite x, y = z.(T) into +// x = z +// y = x.Kind() == K +// as described in the long comment above. +// +// If insert != nil, it can be called to insert a statement after *ptr in its block. +// If insert == nil, insertion is not possible. +// At most one call to insert is allowed. +// +// Scope gives the statements for which a declaration +// in *ptr would be in scope. +// +// The result is true of the statement was rewritten. +// +func reflectFixTypecheck(ptr *ast.Stmt, insert func(ast.Stmt), scope []ast.Stmt) bool { + st := *ptr + as, ok := st.(*ast.AssignStmt) + if !ok || len(as.Lhs) != 2 || len(as.Rhs) != 1 { + return false + } + + ta, ok := as.Rhs[0].(*ast.TypeAssertExpr) + if !ok { + return false + } + typ := reflectType(ta.Type) + if typ == "" { + return false + } + + // Have x, y := z.(t). + x := isIdent(as.Lhs[0]) + y := isIdent(as.Lhs[1]) + z := isIdent(ta.X) + + // First step is x := z, unless it's x := x and the resulting x is never reassigned. + // rcvr is the x in x.Kind(). + var rcvr ast.Expr + if isBlank(x) || + as.Tok == token.DEFINE && x != nil && z != nil && x.Name == z.Name && !assignsTo(x, scope) { + // Can drop the statement. + // If we need to insert a statement later, now we have a slot. + *ptr = &ast.EmptyStmt{} + insert = func(x ast.Stmt) { *ptr = x } + rcvr = ta.X + } else { + *ptr = &ast.AssignStmt{ + Lhs: []ast.Expr{as.Lhs[0]}, + TokPos: as.TokPos, + Tok: as.Tok, + Rhs: []ast.Expr{ta.X}, + } + rcvr = as.Lhs[0] + } + + // Prepare x.Kind() == T expression appropriate to t. + // If x is not a simple identifier, warn that we might be + // reevaluating x. + if x == nil { + warn(as.Pos(), "rewrite reevaluates expr with possible side effects: %s", gofmt(as.Lhs[0])) + } + yExpr, yNotExpr := reflectKindEq(rcvr, reflectKind[typ]) + + // Second step is y := x.Kind() == T, unless it's only used once + // or we have no way to insert that statement. + var yStmt *ast.AssignStmt + if as.Tok == token.DEFINE && countUses(y, scope) <= 1 || insert == nil { + // Can drop the statement and use the expression directly. + rewriteUses(y, + func(token.Pos) ast.Expr { return yExpr }, + func(token.Pos) ast.Expr { return yNotExpr }, + scope) + } else { + yStmt = &ast.AssignStmt{ + Lhs: []ast.Expr{as.Lhs[1]}, + TokPos: as.End(), + Tok: as.Tok, + Rhs: []ast.Expr{yExpr}, + } + insert(yStmt) + } + return true +} + +// reflectKindEq returns the expression z.Kind() == kinds[0] || z.Kind() == kinds[1] || ... +// and its negation. +// The qualifier "reflect." is inserted before each kinds[i] expression. +func reflectKindEq(z ast.Expr, kinds []string) (ast.Expr, ast.Expr) { + n := len(kinds) + if n == 1 { + y := &ast.BinaryExpr{ + X: &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: z, + Sel: ast.NewIdent("Kind"), + }, + }, + Op: token.EQL, + Y: newPkgDot(token.NoPos, "reflect", kinds[0]), + } + ynot := &ast.BinaryExpr{ + X: &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: z, + Sel: ast.NewIdent("Kind"), + }, + }, + Op: token.NEQ, + Y: newPkgDot(token.NoPos, "reflect", kinds[0]), + } + return y, ynot + } + + x, xnot := reflectKindEq(z, kinds[0:n-1]) + y, ynot := reflectKindEq(z, kinds[n-1:]) + + or := &ast.BinaryExpr{ + X: x, + Op: token.LOR, + Y: y, + } + andnot := &ast.BinaryExpr{ + X: xnot, + Op: token.LAND, + Y: ynot, + } + return or, andnot +} + +// if x represents a known old reflect type/value like *reflect.PtrType or reflect.ArrayOrSliceValue, +// reflectType returns the string form of that type. +func reflectType(x ast.Expr) string { + ptr, ok := x.(*ast.StarExpr) + if ok { + x = ptr.X + } + + sel, ok := x.(*ast.SelectorExpr) + if !ok || !isName(sel.X, "reflect") { + return "" + } + + var s = "reflect." + if ptr != nil { + s = "*reflect." + } + s += sel.Sel.Name + + if reflectKind[s] != nil { + return s + } + return "" +} + +// reflectWarnTypecheckStmt warns about statements +// of the form x, y = z.(T) for any old reflect type T. +// The last pass should have gotten them all, and if it didn't, +// the next pass is going to turn them into x, y = z. +func reflectWarnTypecheckStmt(n interface{}) { + as, ok := n.(*ast.AssignStmt) + if !ok || len(as.Lhs) != 2 || len(as.Rhs) != 1 { + return + } + ta, ok := as.Rhs[0].(*ast.TypeAssertExpr) + if !ok || reflectType(ta.Type) == "" { + return + } + warn(n.(ast.Node).Pos(), "unfixed reflect type check") +} + +// reflectFixAssert rewrites x.(T) to x for any old reflect type T. +func reflectFixAssert(n interface{}) bool { + ptr, ok := n.(*ast.Expr) + if ok { + ta, ok := (*ptr).(*ast.TypeAssertExpr) + if ok && reflectType(ta.Type) != "" { + *ptr = ta.X + return true + } + } + return false +} + +// Tables describing the transformations. + +// Description of old reflect API for partial type checking. +// We pretend the Elem method is on Type and Value instead +// of enumerating all the types it is actually on. +// Also, we pretend that ArrayType etc embeds Type for the +// purposes of describing the API. (In fact they embed commonType, +// which implements Type.) +var reflectTypeConfig = &TypeConfig{ + Type: map[string]*Type{ + "reflect.ArrayOrSliceType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.ArrayOrSliceValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.ArrayType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.ArrayValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.BoolType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.BoolValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.ChanType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.ChanValue": &Type{ + Method: map[string]string{ + "Recv": "func() (reflect.Value, bool)", + "TryRecv": "func() (reflect.Value, bool)", + }, + Embed: []string{"reflect.Value"}, + }, + "reflect.ComplexType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.ComplexValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.FloatType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.FloatValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.FuncType": &Type{ + Method: map[string]string{ + "In": "func(int) reflect.Type", + "Out": "func(int) reflect.Type", + }, + Embed: []string{"reflect.Type"}, + }, + "reflect.FuncValue": &Type{ + Method: map[string]string{ + "Call": "func([]reflect.Value) []reflect.Value", + }, + }, + "reflect.IntType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.IntValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.InterfaceType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.InterfaceValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.MapType": &Type{ + Method: map[string]string{ + "Key": "func() reflect.Type", + }, + Embed: []string{"reflect.Type"}, + }, + "reflect.MapValue": &Type{ + Method: map[string]string{ + "Keys": "func() []reflect.Value", + }, + Embed: []string{"reflect.Value"}, + }, + "reflect.Method": &Type{ + Field: map[string]string{ + "Type": "*reflect.FuncType", + "Func": "*reflect.FuncValue", + }, + }, + "reflect.PtrType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.PtrValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.SliceType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.SliceValue": &Type{ + Method: map[string]string{ + "Slice": "func(int, int) *reflect.SliceValue", + }, + Embed: []string{"reflect.Value"}, + }, + "reflect.StringType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.StringValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.StructField": &Type{ + Field: map[string]string{ + "Type": "reflect.Type", + }, + }, + "reflect.StructType": &Type{ + Method: map[string]string{ + "Field": "func() reflect.StructField", + "FieldByIndex": "func() reflect.StructField", + "FieldByName": "func() reflect.StructField,bool", + "FieldByNameFunc": "func() reflect.StructField,bool", + }, + Embed: []string{"reflect.Type"}, + }, + "reflect.StructValue": &Type{ + Method: map[string]string{ + "Field": "func() reflect.Value", + "FieldByIndex": "func() reflect.Value", + "FieldByName": "func() reflect.Value", + "FieldByNameFunc": "func() reflect.Value", + }, + Embed: []string{"reflect.Value"}, + }, + "reflect.Type": &Type{ + Method: map[string]string{ + "Elem": "func() reflect.Type", + "Method": "func() reflect.Method", + }, + }, + "reflect.UintType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.UintValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.UnsafePointerType": &Type{Embed: []string{"reflect.Type"}}, + "reflect.UnsafePointerValue": &Type{Embed: []string{"reflect.Value"}}, + "reflect.Value": &Type{ + Method: map[string]string{ + "Addr": "func() *reflect.PtrValue", + "Elem": "func() reflect.Value", + "Method": "func() *reflect.FuncValue", + "SetValue": "func(reflect.Value)", + }, + }, + }, + Func: map[string]string{ + "reflect.Append": "*reflect.SliceValue", + "reflect.AppendSlice": "*reflect.SliceValue", + "reflect.Indirect": "reflect.Value", + "reflect.MakeSlice": "*reflect.SliceValue", + "reflect.MakeChan": "*reflect.ChanValue", + "reflect.MakeMap": "*reflect.MapValue", + "reflect.MakeZero": "reflect.Value", + "reflect.NewValue": "reflect.Value", + "reflect.PtrTo": "*reflect.PtrType", + "reflect.Typeof": "reflect.Type", + }, +} + +var reflectRewriteMethod = map[string]map[string]string{ + // The type API didn't change much. + "*reflect.ChanType": {"Dir": "ChanDir"}, + "*reflect.FuncType": {"DotDotDot": "IsVariadic"}, + + // The value API has longer names to disambiguate + // methods with different signatures. + "reflect.ArrayOrSliceValue": { // interface, not pointer + "Elem": "Index", + }, + "*reflect.ArrayValue": { + "Elem": "Index", + }, + "*reflect.BoolValue": { + "Get": "Bool", + "Set": "SetBool", + }, + "*reflect.ChanValue": { + "Get": "Pointer", + }, + "*reflect.ComplexValue": { + "Get": "Complex", + "Set": "SetComplex", + "Overflow": "OverflowComplex", + }, + "*reflect.FloatValue": { + "Get": "Float", + "Set": "SetFloat", + "Overflow": "OverflowFloat", + }, + "*reflect.FuncValue": { + "Get": "Pointer", + }, + "*reflect.IntValue": { + "Get": "Int", + "Set": "SetInt", + "Overflow": "OverflowInt", + }, + "*reflect.InterfaceValue": { + "Get": "InterfaceData", + }, + "*reflect.MapValue": { + "Elem": "MapIndex", + "Get": "Pointer", + "Keys": "MapKeys", + "SetElem": "SetMapIndex", + }, + "*reflect.PtrValue": { + "Get": "Pointer", + }, + "*reflect.SliceValue": { + "Elem": "Index", + "Get": "Pointer", + }, + "*reflect.StringValue": { + "Get": "String", + "Set": "SetString", + }, + "*reflect.UintValue": { + "Get": "Uint", + "Set": "SetUint", + "Overflow": "OverflowUint", + }, + "*reflect.UnsafePointerValue": { + "Get": "Pointer", + "Set": "SetPointer", + }, +} + +var reflectKind = map[string][]string{ + "reflect.ArrayOrSliceType": {"Array", "Slice"}, // interface, not pointer + "*reflect.ArrayType": {"Array"}, + "*reflect.BoolType": {"Bool"}, + "*reflect.ChanType": {"Chan"}, + "*reflect.ComplexType": {"Complex64", "Complex128"}, + "*reflect.FloatType": {"Float32", "Float64"}, + "*reflect.FuncType": {"Func"}, + "*reflect.IntType": {"Int", "Int8", "Int16", "Int32", "Int64"}, + "*reflect.InterfaceType": {"Interface"}, + "*reflect.MapType": {"Map"}, + "*reflect.PtrType": {"Ptr"}, + "*reflect.SliceType": {"Slice"}, + "*reflect.StringType": {"String"}, + "*reflect.StructType": {"Struct"}, + "*reflect.UintType": {"Uint", "Uint8", "Uint16", "Uint32", "Uint64", "Uintptr"}, + "*reflect.UnsafePointerType": {"UnsafePointer"}, + + "reflect.ArrayOrSliceValue": {"Array", "Slice"}, // interface, not pointer + "*reflect.ArrayValue": {"Array"}, + "*reflect.BoolValue": {"Bool"}, + "*reflect.ChanValue": {"Chan"}, + "*reflect.ComplexValue": {"Complex64", "Complex128"}, + "*reflect.FloatValue": {"Float32", "Float64"}, + "*reflect.FuncValue": {"Func"}, + "*reflect.IntValue": {"Int", "Int8", "Int16", "Int32", "Int64"}, + "*reflect.InterfaceValue": {"Interface"}, + "*reflect.MapValue": {"Map"}, + "*reflect.PtrValue": {"Ptr"}, + "*reflect.SliceValue": {"Slice"}, + "*reflect.StringValue": {"String"}, + "*reflect.StructValue": {"Struct"}, + "*reflect.UintValue": {"Uint", "Uint8", "Uint16", "Uint32", "Uint64", "Uintptr"}, + "*reflect.UnsafePointerValue": {"UnsafePointer"}, +} + +var isReflectValue = map[string]bool{ + "reflect.ArrayOrSliceValue": true, // interface, not pointer + "*reflect.ArrayValue": true, + "*reflect.BoolValue": true, + "*reflect.ChanValue": true, + "*reflect.ComplexValue": true, + "*reflect.FloatValue": true, + "*reflect.FuncValue": true, + "*reflect.IntValue": true, + "*reflect.InterfaceValue": true, + "*reflect.MapValue": true, + "*reflect.PtrValue": true, + "*reflect.SliceValue": true, + "*reflect.StringValue": true, + "*reflect.StructValue": true, + "*reflect.UintValue": true, + "*reflect.UnsafePointerValue": true, + "reflect.Value": true, // interface, not pointer +} |