// 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
}
