706 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			706 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2014 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.
 | |
| 
 | |
| // Package satisfy inspects the type-checked ASTs of Go packages and
 | |
| // reports the set of discovered type constraints of the form (lhs, rhs
 | |
| // Type) where lhs is a non-trivial interface, rhs satisfies this
 | |
| // interface, and this fact is necessary for the package to be
 | |
| // well-typed.
 | |
| //
 | |
| // THIS PACKAGE IS EXPERIMENTAL AND MAY CHANGE AT ANY TIME.
 | |
| //
 | |
| // It is provided only for the gorename tool.  Ideally this
 | |
| // functionality will become part of the type-checker in due course,
 | |
| // since it is computing it anyway, and it is robust for ill-typed
 | |
| // inputs, which this package is not.
 | |
| //
 | |
| package satisfy // import "golang.org/x/tools/refactor/satisfy"
 | |
| 
 | |
| // NOTES:
 | |
| //
 | |
| // We don't care about numeric conversions, so we don't descend into
 | |
| // types or constant expressions.  This is unsound because
 | |
| // constant expressions can contain arbitrary statements, e.g.
 | |
| //   const x = len([1]func(){func() {
 | |
| //     ...
 | |
| //   }})
 | |
| //
 | |
| // TODO(adonovan): make this robust against ill-typed input.
 | |
| // Or move it into the type-checker.
 | |
| //
 | |
| // Assignability conversions are possible in the following places:
 | |
| // - in assignments y = x, y := x, var y = x.
 | |
| // - from call argument types to formal parameter types
 | |
| // - in append and delete calls
 | |
| // - from return operands to result parameter types
 | |
| // - in composite literal T{k:v}, from k and v to T's field/element/key type
 | |
| // - in map[key] from key to the map's key type
 | |
| // - in comparisons x==y and switch x { case y: }.
 | |
| // - in explicit conversions T(x)
 | |
| // - in sends ch <- x, from x to the channel element type
 | |
| // - in type assertions x.(T) and switch x.(type) { case T: }
 | |
| //
 | |
| // The results of this pass provide information equivalent to the
 | |
| // ssa.MakeInterface and ssa.ChangeInterface instructions.
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"go/token"
 | |
| 	"go/types"
 | |
| 
 | |
| 	"golang.org/x/tools/go/ast/astutil"
 | |
| 	"golang.org/x/tools/go/types/typeutil"
 | |
| )
 | |
| 
 | |
| // A Constraint records the fact that the RHS type does and must
 | |
| // satisify the LHS type, which is an interface.
 | |
| // The names are suggestive of an assignment statement LHS = RHS.
 | |
| type Constraint struct {
 | |
| 	LHS, RHS types.Type
 | |
| }
 | |
| 
 | |
| // A Finder inspects the type-checked ASTs of Go packages and
 | |
| // accumulates the set of type constraints (x, y) such that x is
 | |
| // assignable to y, y is an interface, and both x and y have methods.
 | |
| //
 | |
| // In other words, it returns the subset of the "implements" relation
 | |
| // that is checked during compilation of a package.  Refactoring tools
 | |
| // will need to preserve at least this part of the relation to ensure
 | |
| // continued compilation.
 | |
| //
 | |
| type Finder struct {
 | |
| 	Result    map[Constraint]bool
 | |
| 	msetcache typeutil.MethodSetCache
 | |
| 
 | |
| 	// per-Find state
 | |
| 	info *types.Info
 | |
| 	sig  *types.Signature
 | |
| }
 | |
| 
 | |
| // Find inspects a single package, populating Result with its pairs of
 | |
| // constrained types.
 | |
| //
 | |
| // The result is non-canonical and thus may contain duplicates (but this
 | |
| // tends to preserves names of interface types better).
 | |
| //
 | |
| // The package must be free of type errors, and
 | |
| // info.{Defs,Uses,Selections,Types} must have been populated by the
 | |
| // type-checker.
 | |
| //
 | |
| func (f *Finder) Find(info *types.Info, files []*ast.File) {
 | |
| 	if f.Result == nil {
 | |
| 		f.Result = make(map[Constraint]bool)
 | |
| 	}
 | |
| 
 | |
| 	f.info = info
 | |
| 	for _, file := range files {
 | |
| 		for _, d := range file.Decls {
 | |
| 			switch d := d.(type) {
 | |
| 			case *ast.GenDecl:
 | |
| 				if d.Tok == token.VAR { // ignore consts
 | |
| 					for _, spec := range d.Specs {
 | |
| 						f.valueSpec(spec.(*ast.ValueSpec))
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 			case *ast.FuncDecl:
 | |
| 				if d.Body != nil {
 | |
| 					f.sig = f.info.Defs[d.Name].Type().(*types.Signature)
 | |
| 					f.stmt(d.Body)
 | |
| 					f.sig = nil
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	f.info = nil
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	tInvalid     = types.Typ[types.Invalid]
 | |
| 	tUntypedBool = types.Typ[types.UntypedBool]
 | |
| 	tUntypedNil  = types.Typ[types.UntypedNil]
 | |
| )
 | |
| 
 | |
| // exprN visits an expression in a multi-value context.
 | |
| func (f *Finder) exprN(e ast.Expr) types.Type {
 | |
| 	typ := f.info.Types[e].Type.(*types.Tuple)
 | |
| 	switch e := e.(type) {
 | |
| 	case *ast.ParenExpr:
 | |
| 		return f.exprN(e.X)
 | |
| 
 | |
| 	case *ast.CallExpr:
 | |
| 		// x, err := f(args)
 | |
| 		sig := f.expr(e.Fun).Underlying().(*types.Signature)
 | |
| 		f.call(sig, e.Args)
 | |
| 
 | |
| 	case *ast.IndexExpr:
 | |
| 		// y, ok := x[i]
 | |
| 		x := f.expr(e.X)
 | |
| 		f.assign(f.expr(e.Index), x.Underlying().(*types.Map).Key())
 | |
| 
 | |
| 	case *ast.TypeAssertExpr:
 | |
| 		// y, ok := x.(T)
 | |
| 		f.typeAssert(f.expr(e.X), typ.At(0).Type())
 | |
| 
 | |
| 	case *ast.UnaryExpr: // must be receive <-
 | |
| 		// y, ok := <-x
 | |
| 		f.expr(e.X)
 | |
| 
 | |
| 	default:
 | |
| 		panic(e)
 | |
| 	}
 | |
| 	return typ
 | |
| }
 | |
| 
 | |
| func (f *Finder) call(sig *types.Signature, args []ast.Expr) {
 | |
| 	if len(args) == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	// Ellipsis call?  e.g. f(x, y, z...)
 | |
| 	if _, ok := args[len(args)-1].(*ast.Ellipsis); ok {
 | |
| 		for i, arg := range args {
 | |
| 			// The final arg is a slice, and so is the final param.
 | |
| 			f.assign(sig.Params().At(i).Type(), f.expr(arg))
 | |
| 		}
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	var argtypes []types.Type
 | |
| 
 | |
| 	// Gather the effective actual parameter types.
 | |
| 	if tuple, ok := f.info.Types[args[0]].Type.(*types.Tuple); ok {
 | |
| 		// f(g()) call where g has multiple results?
 | |
| 		f.expr(args[0])
 | |
| 		// unpack the tuple
 | |
| 		for i := 0; i < tuple.Len(); i++ {
 | |
| 			argtypes = append(argtypes, tuple.At(i).Type())
 | |
| 		}
 | |
| 	} else {
 | |
| 		for _, arg := range args {
 | |
| 			argtypes = append(argtypes, f.expr(arg))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Assign the actuals to the formals.
 | |
| 	if !sig.Variadic() {
 | |
| 		for i, argtype := range argtypes {
 | |
| 			f.assign(sig.Params().At(i).Type(), argtype)
 | |
| 		}
 | |
| 	} else {
 | |
| 		// The first n-1 parameters are assigned normally.
 | |
| 		nnormals := sig.Params().Len() - 1
 | |
| 		for i, argtype := range argtypes[:nnormals] {
 | |
| 			f.assign(sig.Params().At(i).Type(), argtype)
 | |
| 		}
 | |
| 		// Remaining args are assigned to elements of varargs slice.
 | |
| 		tElem := sig.Params().At(nnormals).Type().(*types.Slice).Elem()
 | |
| 		for i := nnormals; i < len(argtypes); i++ {
 | |
| 			f.assign(tElem, argtypes[i])
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (f *Finder) builtin(obj *types.Builtin, sig *types.Signature, args []ast.Expr, T types.Type) types.Type {
 | |
| 	switch obj.Name() {
 | |
| 	case "make", "new":
 | |
| 		// skip the type operand
 | |
| 		for _, arg := range args[1:] {
 | |
| 			f.expr(arg)
 | |
| 		}
 | |
| 
 | |
| 	case "append":
 | |
| 		s := f.expr(args[0])
 | |
| 		if _, ok := args[len(args)-1].(*ast.Ellipsis); ok && len(args) == 2 {
 | |
| 			// append(x, y...)   including append([]byte, "foo"...)
 | |
| 			f.expr(args[1])
 | |
| 		} else {
 | |
| 			// append(x, y, z)
 | |
| 			tElem := s.Underlying().(*types.Slice).Elem()
 | |
| 			for _, arg := range args[1:] {
 | |
| 				f.assign(tElem, f.expr(arg))
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	case "delete":
 | |
| 		m := f.expr(args[0])
 | |
| 		k := f.expr(args[1])
 | |
| 		f.assign(m.Underlying().(*types.Map).Key(), k)
 | |
| 
 | |
| 	default:
 | |
| 		// ordinary call
 | |
| 		f.call(sig, args)
 | |
| 	}
 | |
| 
 | |
| 	return T
 | |
| }
 | |
| 
 | |
| func (f *Finder) extract(tuple types.Type, i int) types.Type {
 | |
| 	if tuple, ok := tuple.(*types.Tuple); ok && i < tuple.Len() {
 | |
| 		return tuple.At(i).Type()
 | |
| 	}
 | |
| 	return tInvalid
 | |
| }
 | |
| 
 | |
| func (f *Finder) valueSpec(spec *ast.ValueSpec) {
 | |
| 	var T types.Type
 | |
| 	if spec.Type != nil {
 | |
| 		T = f.info.Types[spec.Type].Type
 | |
| 	}
 | |
| 	switch len(spec.Values) {
 | |
| 	case len(spec.Names): // e.g. var x, y = f(), g()
 | |
| 		for _, value := range spec.Values {
 | |
| 			v := f.expr(value)
 | |
| 			if T != nil {
 | |
| 				f.assign(T, v)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	case 1: // e.g. var x, y = f()
 | |
| 		tuple := f.exprN(spec.Values[0])
 | |
| 		for i := range spec.Names {
 | |
| 			if T != nil {
 | |
| 				f.assign(T, f.extract(tuple, i))
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // assign records pairs of distinct types that are related by
 | |
| // assignability, where the left-hand side is an interface and both
 | |
| // sides have methods.
 | |
| //
 | |
| // It should be called for all assignability checks, type assertions,
 | |
| // explicit conversions and comparisons between two types, unless the
 | |
| // types are uninteresting (e.g. lhs is a concrete type, or the empty
 | |
| // interface; rhs has no methods).
 | |
| //
 | |
| func (f *Finder) assign(lhs, rhs types.Type) {
 | |
| 	if types.Identical(lhs, rhs) {
 | |
| 		return
 | |
| 	}
 | |
| 	if !isInterface(lhs) {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if f.msetcache.MethodSet(lhs).Len() == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 	if f.msetcache.MethodSet(rhs).Len() == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 	// record the pair
 | |
| 	f.Result[Constraint{lhs, rhs}] = true
 | |
| }
 | |
| 
 | |
| // typeAssert must be called for each type assertion x.(T) where x has
 | |
| // interface type I.
 | |
| func (f *Finder) typeAssert(I, T types.Type) {
 | |
| 	// Type assertions are slightly subtle, because they are allowed
 | |
| 	// to be "impossible", e.g.
 | |
| 	//
 | |
| 	// 	var x interface{f()}
 | |
| 	//	_ = x.(interface{f()int}) // legal
 | |
| 	//
 | |
| 	// (In hindsight, the language spec should probably not have
 | |
| 	// allowed this, but it's too late to fix now.)
 | |
| 	//
 | |
| 	// This means that a type assert from I to T isn't exactly a
 | |
| 	// constraint that T is assignable to I, but for a refactoring
 | |
| 	// tool it is a conditional constraint that, if T is assignable
 | |
| 	// to I before a refactoring, it should remain so after.
 | |
| 
 | |
| 	if types.AssignableTo(T, I) {
 | |
| 		f.assign(I, T)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // compare must be called for each comparison x==y.
 | |
| func (f *Finder) compare(x, y types.Type) {
 | |
| 	if types.AssignableTo(x, y) {
 | |
| 		f.assign(y, x)
 | |
| 	} else if types.AssignableTo(y, x) {
 | |
| 		f.assign(x, y)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // expr visits a true expression (not a type or defining ident)
 | |
| // and returns its type.
 | |
| func (f *Finder) expr(e ast.Expr) types.Type {
 | |
| 	tv := f.info.Types[e]
 | |
| 	if tv.Value != nil {
 | |
| 		return tv.Type // prune the descent for constants
 | |
| 	}
 | |
| 
 | |
| 	// tv.Type may be nil for an ast.Ident.
 | |
| 
 | |
| 	switch e := e.(type) {
 | |
| 	case *ast.BadExpr, *ast.BasicLit:
 | |
| 		// no-op
 | |
| 
 | |
| 	case *ast.Ident:
 | |
| 		// (referring idents only)
 | |
| 		if obj, ok := f.info.Uses[e]; ok {
 | |
| 			return obj.Type()
 | |
| 		}
 | |
| 		if e.Name == "_" { // e.g. "for _ = range x"
 | |
| 			return tInvalid
 | |
| 		}
 | |
| 		panic("undefined ident: " + e.Name)
 | |
| 
 | |
| 	case *ast.Ellipsis:
 | |
| 		if e.Elt != nil {
 | |
| 			f.expr(e.Elt)
 | |
| 		}
 | |
| 
 | |
| 	case *ast.FuncLit:
 | |
| 		saved := f.sig
 | |
| 		f.sig = tv.Type.(*types.Signature)
 | |
| 		f.stmt(e.Body)
 | |
| 		f.sig = saved
 | |
| 
 | |
| 	case *ast.CompositeLit:
 | |
| 		switch T := deref(tv.Type).Underlying().(type) {
 | |
| 		case *types.Struct:
 | |
| 			for i, elem := range e.Elts {
 | |
| 				if kv, ok := elem.(*ast.KeyValueExpr); ok {
 | |
| 					f.assign(f.info.Uses[kv.Key.(*ast.Ident)].Type(), f.expr(kv.Value))
 | |
| 				} else {
 | |
| 					f.assign(T.Field(i).Type(), f.expr(elem))
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 		case *types.Map:
 | |
| 			for _, elem := range e.Elts {
 | |
| 				elem := elem.(*ast.KeyValueExpr)
 | |
| 				f.assign(T.Key(), f.expr(elem.Key))
 | |
| 				f.assign(T.Elem(), f.expr(elem.Value))
 | |
| 			}
 | |
| 
 | |
| 		case *types.Array, *types.Slice:
 | |
| 			tElem := T.(interface {
 | |
| 				Elem() types.Type
 | |
| 			}).Elem()
 | |
| 			for _, elem := range e.Elts {
 | |
| 				if kv, ok := elem.(*ast.KeyValueExpr); ok {
 | |
| 					// ignore the key
 | |
| 					f.assign(tElem, f.expr(kv.Value))
 | |
| 				} else {
 | |
| 					f.assign(tElem, f.expr(elem))
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 		default:
 | |
| 			panic("unexpected composite literal type: " + tv.Type.String())
 | |
| 		}
 | |
| 
 | |
| 	case *ast.ParenExpr:
 | |
| 		f.expr(e.X)
 | |
| 
 | |
| 	case *ast.SelectorExpr:
 | |
| 		if _, ok := f.info.Selections[e]; ok {
 | |
| 			f.expr(e.X) // selection
 | |
| 		} else {
 | |
| 			return f.info.Uses[e.Sel].Type() // qualified identifier
 | |
| 		}
 | |
| 
 | |
| 	case *ast.IndexExpr:
 | |
| 		x := f.expr(e.X)
 | |
| 		i := f.expr(e.Index)
 | |
| 		if ux, ok := x.Underlying().(*types.Map); ok {
 | |
| 			f.assign(ux.Key(), i)
 | |
| 		}
 | |
| 
 | |
| 	case *ast.SliceExpr:
 | |
| 		f.expr(e.X)
 | |
| 		if e.Low != nil {
 | |
| 			f.expr(e.Low)
 | |
| 		}
 | |
| 		if e.High != nil {
 | |
| 			f.expr(e.High)
 | |
| 		}
 | |
| 		if e.Max != nil {
 | |
| 			f.expr(e.Max)
 | |
| 		}
 | |
| 
 | |
| 	case *ast.TypeAssertExpr:
 | |
| 		x := f.expr(e.X)
 | |
| 		f.typeAssert(x, f.info.Types[e.Type].Type)
 | |
| 
 | |
| 	case *ast.CallExpr:
 | |
| 		if tvFun := f.info.Types[e.Fun]; tvFun.IsType() {
 | |
| 			// conversion
 | |
| 			arg0 := f.expr(e.Args[0])
 | |
| 			f.assign(tvFun.Type, arg0)
 | |
| 		} else {
 | |
| 			// function call
 | |
| 			if id, ok := unparen(e.Fun).(*ast.Ident); ok {
 | |
| 				if obj, ok := f.info.Uses[id].(*types.Builtin); ok {
 | |
| 					sig := f.info.Types[id].Type.(*types.Signature)
 | |
| 					return f.builtin(obj, sig, e.Args, tv.Type)
 | |
| 				}
 | |
| 			}
 | |
| 			// ordinary call
 | |
| 			f.call(f.expr(e.Fun).Underlying().(*types.Signature), e.Args)
 | |
| 		}
 | |
| 
 | |
| 	case *ast.StarExpr:
 | |
| 		f.expr(e.X)
 | |
| 
 | |
| 	case *ast.UnaryExpr:
 | |
| 		f.expr(e.X)
 | |
| 
 | |
| 	case *ast.BinaryExpr:
 | |
| 		x := f.expr(e.X)
 | |
| 		y := f.expr(e.Y)
 | |
| 		if e.Op == token.EQL || e.Op == token.NEQ {
 | |
| 			f.compare(x, y)
 | |
| 		}
 | |
| 
 | |
| 	case *ast.KeyValueExpr:
 | |
| 		f.expr(e.Key)
 | |
| 		f.expr(e.Value)
 | |
| 
 | |
| 	case *ast.ArrayType,
 | |
| 		*ast.StructType,
 | |
| 		*ast.FuncType,
 | |
| 		*ast.InterfaceType,
 | |
| 		*ast.MapType,
 | |
| 		*ast.ChanType:
 | |
| 		panic(e)
 | |
| 	}
 | |
| 
 | |
| 	if tv.Type == nil {
 | |
| 		panic(fmt.Sprintf("no type for %T", e))
 | |
| 	}
 | |
| 
 | |
| 	return tv.Type
 | |
| }
 | |
| 
 | |
| func (f *Finder) stmt(s ast.Stmt) {
 | |
| 	switch s := s.(type) {
 | |
| 	case *ast.BadStmt,
 | |
| 		*ast.EmptyStmt,
 | |
| 		*ast.BranchStmt:
 | |
| 		// no-op
 | |
| 
 | |
| 	case *ast.DeclStmt:
 | |
| 		d := s.Decl.(*ast.GenDecl)
 | |
| 		if d.Tok == token.VAR { // ignore consts
 | |
| 			for _, spec := range d.Specs {
 | |
| 				f.valueSpec(spec.(*ast.ValueSpec))
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	case *ast.LabeledStmt:
 | |
| 		f.stmt(s.Stmt)
 | |
| 
 | |
| 	case *ast.ExprStmt:
 | |
| 		f.expr(s.X)
 | |
| 
 | |
| 	case *ast.SendStmt:
 | |
| 		ch := f.expr(s.Chan)
 | |
| 		val := f.expr(s.Value)
 | |
| 		f.assign(ch.Underlying().(*types.Chan).Elem(), val)
 | |
| 
 | |
| 	case *ast.IncDecStmt:
 | |
| 		f.expr(s.X)
 | |
| 
 | |
| 	case *ast.AssignStmt:
 | |
| 		switch s.Tok {
 | |
| 		case token.ASSIGN, token.DEFINE:
 | |
| 			// y := x   or   y = x
 | |
| 			var rhsTuple types.Type
 | |
| 			if len(s.Lhs) != len(s.Rhs) {
 | |
| 				rhsTuple = f.exprN(s.Rhs[0])
 | |
| 			}
 | |
| 			for i := range s.Lhs {
 | |
| 				var lhs, rhs types.Type
 | |
| 				if rhsTuple == nil {
 | |
| 					rhs = f.expr(s.Rhs[i]) // 1:1 assignment
 | |
| 				} else {
 | |
| 					rhs = f.extract(rhsTuple, i) // n:1 assignment
 | |
| 				}
 | |
| 
 | |
| 				if id, ok := s.Lhs[i].(*ast.Ident); ok {
 | |
| 					if id.Name != "_" {
 | |
| 						if obj, ok := f.info.Defs[id]; ok {
 | |
| 							lhs = obj.Type() // definition
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				if lhs == nil {
 | |
| 					lhs = f.expr(s.Lhs[i]) // assignment
 | |
| 				}
 | |
| 				f.assign(lhs, rhs)
 | |
| 			}
 | |
| 
 | |
| 		default:
 | |
| 			// y op= x
 | |
| 			f.expr(s.Lhs[0])
 | |
| 			f.expr(s.Rhs[0])
 | |
| 		}
 | |
| 
 | |
| 	case *ast.GoStmt:
 | |
| 		f.expr(s.Call)
 | |
| 
 | |
| 	case *ast.DeferStmt:
 | |
| 		f.expr(s.Call)
 | |
| 
 | |
| 	case *ast.ReturnStmt:
 | |
| 		formals := f.sig.Results()
 | |
| 		switch len(s.Results) {
 | |
| 		case formals.Len(): // 1:1
 | |
| 			for i, result := range s.Results {
 | |
| 				f.assign(formals.At(i).Type(), f.expr(result))
 | |
| 			}
 | |
| 
 | |
| 		case 1: // n:1
 | |
| 			tuple := f.exprN(s.Results[0])
 | |
| 			for i := 0; i < formals.Len(); i++ {
 | |
| 				f.assign(formals.At(i).Type(), f.extract(tuple, i))
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	case *ast.SelectStmt:
 | |
| 		f.stmt(s.Body)
 | |
| 
 | |
| 	case *ast.BlockStmt:
 | |
| 		for _, s := range s.List {
 | |
| 			f.stmt(s)
 | |
| 		}
 | |
| 
 | |
| 	case *ast.IfStmt:
 | |
| 		if s.Init != nil {
 | |
| 			f.stmt(s.Init)
 | |
| 		}
 | |
| 		f.expr(s.Cond)
 | |
| 		f.stmt(s.Body)
 | |
| 		if s.Else != nil {
 | |
| 			f.stmt(s.Else)
 | |
| 		}
 | |
| 
 | |
| 	case *ast.SwitchStmt:
 | |
| 		if s.Init != nil {
 | |
| 			f.stmt(s.Init)
 | |
| 		}
 | |
| 		var tag types.Type = tUntypedBool
 | |
| 		if s.Tag != nil {
 | |
| 			tag = f.expr(s.Tag)
 | |
| 		}
 | |
| 		for _, cc := range s.Body.List {
 | |
| 			cc := cc.(*ast.CaseClause)
 | |
| 			for _, cond := range cc.List {
 | |
| 				f.compare(tag, f.info.Types[cond].Type)
 | |
| 			}
 | |
| 			for _, s := range cc.Body {
 | |
| 				f.stmt(s)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	case *ast.TypeSwitchStmt:
 | |
| 		if s.Init != nil {
 | |
| 			f.stmt(s.Init)
 | |
| 		}
 | |
| 		var I types.Type
 | |
| 		switch ass := s.Assign.(type) {
 | |
| 		case *ast.ExprStmt: // x.(type)
 | |
| 			I = f.expr(unparen(ass.X).(*ast.TypeAssertExpr).X)
 | |
| 		case *ast.AssignStmt: // y := x.(type)
 | |
| 			I = f.expr(unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
 | |
| 		}
 | |
| 		for _, cc := range s.Body.List {
 | |
| 			cc := cc.(*ast.CaseClause)
 | |
| 			for _, cond := range cc.List {
 | |
| 				tCase := f.info.Types[cond].Type
 | |
| 				if tCase != tUntypedNil {
 | |
| 					f.typeAssert(I, tCase)
 | |
| 				}
 | |
| 			}
 | |
| 			for _, s := range cc.Body {
 | |
| 				f.stmt(s)
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 	case *ast.CommClause:
 | |
| 		if s.Comm != nil {
 | |
| 			f.stmt(s.Comm)
 | |
| 		}
 | |
| 		for _, s := range s.Body {
 | |
| 			f.stmt(s)
 | |
| 		}
 | |
| 
 | |
| 	case *ast.ForStmt:
 | |
| 		if s.Init != nil {
 | |
| 			f.stmt(s.Init)
 | |
| 		}
 | |
| 		if s.Cond != nil {
 | |
| 			f.expr(s.Cond)
 | |
| 		}
 | |
| 		if s.Post != nil {
 | |
| 			f.stmt(s.Post)
 | |
| 		}
 | |
| 		f.stmt(s.Body)
 | |
| 
 | |
| 	case *ast.RangeStmt:
 | |
| 		x := f.expr(s.X)
 | |
| 		// No conversions are involved when Tok==DEFINE.
 | |
| 		if s.Tok == token.ASSIGN {
 | |
| 			if s.Key != nil {
 | |
| 				k := f.expr(s.Key)
 | |
| 				var xelem types.Type
 | |
| 				// keys of array, *array, slice, string aren't interesting
 | |
| 				switch ux := x.Underlying().(type) {
 | |
| 				case *types.Chan:
 | |
| 					xelem = ux.Elem()
 | |
| 				case *types.Map:
 | |
| 					xelem = ux.Key()
 | |
| 				}
 | |
| 				if xelem != nil {
 | |
| 					f.assign(xelem, k)
 | |
| 				}
 | |
| 			}
 | |
| 			if s.Value != nil {
 | |
| 				val := f.expr(s.Value)
 | |
| 				var xelem types.Type
 | |
| 				// values of strings aren't interesting
 | |
| 				switch ux := x.Underlying().(type) {
 | |
| 				case *types.Array:
 | |
| 					xelem = ux.Elem()
 | |
| 				case *types.Chan:
 | |
| 					xelem = ux.Elem()
 | |
| 				case *types.Map:
 | |
| 					xelem = ux.Elem()
 | |
| 				case *types.Pointer: // *array
 | |
| 					xelem = deref(ux).(*types.Array).Elem()
 | |
| 				case *types.Slice:
 | |
| 					xelem = ux.Elem()
 | |
| 				}
 | |
| 				if xelem != nil {
 | |
| 					f.assign(xelem, val)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		f.stmt(s.Body)
 | |
| 
 | |
| 	default:
 | |
| 		panic(s)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // -- Plundered from golang.org/x/tools/go/ssa -----------------
 | |
| 
 | |
| // deref returns a pointer's element type; otherwise it returns typ.
 | |
| func deref(typ types.Type) types.Type {
 | |
| 	if p, ok := typ.Underlying().(*types.Pointer); ok {
 | |
| 		return p.Elem()
 | |
| 	}
 | |
| 	return typ
 | |
| }
 | |
| 
 | |
| func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
 | |
| 
 | |
| func isInterface(T types.Type) bool { return types.IsInterface(T) }
 |