+++ /dev/null
-// 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
-// satisfy 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) }