// 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 http://codereview.appspot.com/4433066 `, } 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 }) // Rewrite // reflect.Typeof -> reflect.TypeOf, walk(f, func(n interface{}) { sel, ok := n.(*ast.SelectorExpr) if !ok { return } if isTopName(sel.X, "reflect") && sel.Sel.Name == "Typeof" { sel.Sel.Name = "TypeOf" fixed = true } if isTopName(sel.X, "reflect") && sel.Sel.Name == "NewValue" { sel.Sel.Name = "ValueOf" 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 }