github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/oracle/implements14.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build !go1.5
     6  
     7  package oracle
     8  
     9  import (
    10  	"fmt"
    11  	"go/ast"
    12  	"go/token"
    13  	"reflect"
    14  	"sort"
    15  	"strings"
    16  
    17  	"golang.org/x/tools/go/loader"
    18  	"golang.org/x/tools/go/types"
    19  	"golang.org/x/tools/go/types/typeutil"
    20  	"golang.org/x/tools/oracle/serial"
    21  	"golang.org/x/tools/refactor/importgraph"
    22  )
    23  
    24  // Implements displays the "implements" relation as it pertains to the
    25  // selected type.
    26  // If the selection is a method, 'implements' displays
    27  // the corresponding methods of the types that would have been reported
    28  // by an implements query on the receiver type.
    29  //
    30  func implements(q *Query) error {
    31  	lconf := loader.Config{Build: q.Build}
    32  	allowErrors(&lconf)
    33  
    34  	qpkg, err := importQueryPackage(q.Pos, &lconf)
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	// Set the packages to search.
    40  	if len(q.Scope) > 0 {
    41  		// Inspect all packages in the analysis scope, if specified.
    42  		if err := setPTAScope(&lconf, q.Scope); err != nil {
    43  			return err
    44  		}
    45  	} else {
    46  		// Otherwise inspect the forward and reverse
    47  		// transitive closure of the selected package.
    48  		// (In theory even this is incomplete.)
    49  		_, rev, _ := importgraph.Build(q.Build)
    50  		for path := range rev.Search(qpkg) {
    51  			lconf.ImportWithTests(path)
    52  		}
    53  
    54  		// TODO(adonovan): for completeness, we should also
    55  		// type-check and inspect function bodies in all
    56  		// imported packages.  This would be expensive, but we
    57  		// could optimize by skipping functions that do not
    58  		// contain type declarations.  This would require
    59  		// changing the loader's TypeCheckFuncBodies hook to
    60  		// provide the []*ast.File.
    61  	}
    62  
    63  	// Load/parse/type-check the program.
    64  	lprog, err := lconf.Load()
    65  	if err != nil {
    66  		return err
    67  	}
    68  	q.Fset = lprog.Fset
    69  
    70  	qpos, err := parseQueryPos(lprog, q.Pos, false)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	// Find the selected type.
    76  	path, action := findInterestingNode(qpos.info, qpos.path)
    77  
    78  	var method *types.Func
    79  	var T types.Type // selected type (receiver if method != nil)
    80  
    81  	switch action {
    82  	case actionExpr:
    83  		// method?
    84  		if id, ok := path[0].(*ast.Ident); ok {
    85  			if obj, ok := qpos.info.ObjectOf(id).(*types.Func); ok {
    86  				recv := obj.Type().(*types.Signature).Recv()
    87  				if recv == nil {
    88  					return fmt.Errorf("this function is not a method")
    89  				}
    90  				method = obj
    91  				T = recv.Type()
    92  			}
    93  		}
    94  	case actionType:
    95  		T = qpos.info.TypeOf(path[0].(ast.Expr))
    96  	}
    97  	if T == nil {
    98  		return fmt.Errorf("no type or method here")
    99  	}
   100  
   101  	// Find all named types, even local types (which can have
   102  	// methods via promotion) and the built-in "error".
   103  	var allNamed []types.Type
   104  	for _, info := range lprog.AllPackages {
   105  		for _, obj := range info.Defs {
   106  			if obj, ok := obj.(*types.TypeName); ok {
   107  				allNamed = append(allNamed, obj.Type())
   108  			}
   109  		}
   110  	}
   111  	allNamed = append(allNamed, types.Universe.Lookup("error").Type())
   112  
   113  	var msets typeutil.MethodSetCache
   114  
   115  	// Test each named type.
   116  	var to, from, fromPtr []types.Type
   117  	for _, U := range allNamed {
   118  		if isInterface(T) {
   119  			if msets.MethodSet(T).Len() == 0 {
   120  				continue // empty interface
   121  			}
   122  			if isInterface(U) {
   123  				if msets.MethodSet(U).Len() == 0 {
   124  					continue // empty interface
   125  				}
   126  
   127  				// T interface, U interface
   128  				if !types.Identical(T, U) {
   129  					if types.AssignableTo(U, T) {
   130  						to = append(to, U)
   131  					}
   132  					if types.AssignableTo(T, U) {
   133  						from = append(from, U)
   134  					}
   135  				}
   136  			} else {
   137  				// T interface, U concrete
   138  				if types.AssignableTo(U, T) {
   139  					to = append(to, U)
   140  				} else if pU := types.NewPointer(U); types.AssignableTo(pU, T) {
   141  					to = append(to, pU)
   142  				}
   143  			}
   144  		} else if isInterface(U) {
   145  			if msets.MethodSet(U).Len() == 0 {
   146  				continue // empty interface
   147  			}
   148  
   149  			// T concrete, U interface
   150  			if types.AssignableTo(T, U) {
   151  				from = append(from, U)
   152  			} else if pT := types.NewPointer(T); types.AssignableTo(pT, U) {
   153  				fromPtr = append(fromPtr, U)
   154  			}
   155  		}
   156  	}
   157  
   158  	var pos interface{} = qpos
   159  	if nt, ok := deref(T).(*types.Named); ok {
   160  		pos = nt.Obj()
   161  	}
   162  
   163  	// Sort types (arbitrarily) to ensure test determinism.
   164  	sort.Sort(typesByString(to))
   165  	sort.Sort(typesByString(from))
   166  	sort.Sort(typesByString(fromPtr))
   167  
   168  	var toMethod, fromMethod, fromPtrMethod []*types.Selection // contain nils
   169  	if method != nil {
   170  		for _, t := range to {
   171  			toMethod = append(toMethod,
   172  				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
   173  		}
   174  		for _, t := range from {
   175  			fromMethod = append(fromMethod,
   176  				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
   177  		}
   178  		for _, t := range fromPtr {
   179  			fromPtrMethod = append(fromPtrMethod,
   180  				types.NewMethodSet(t).Lookup(method.Pkg(), method.Name()))
   181  		}
   182  	}
   183  
   184  	q.result = &implementsResult{
   185  		qpos, T, pos, to, from, fromPtr, method, toMethod, fromMethod, fromPtrMethod,
   186  	}
   187  	return nil
   188  }
   189  
   190  type implementsResult struct {
   191  	qpos *queryPos
   192  
   193  	t       types.Type   // queried type (not necessarily named)
   194  	pos     interface{}  // pos of t (*types.Name or *QueryPos)
   195  	to      []types.Type // named or ptr-to-named types assignable to interface T
   196  	from    []types.Type // named interfaces assignable from T
   197  	fromPtr []types.Type // named interfaces assignable only from *T
   198  
   199  	// if a method was queried:
   200  	method        *types.Func        // queried method
   201  	toMethod      []*types.Selection // method of type to[i], if any
   202  	fromMethod    []*types.Selection // method of type from[i], if any
   203  	fromPtrMethod []*types.Selection // method of type fromPtrMethod[i], if any
   204  }
   205  
   206  func (r *implementsResult) display(printf printfFunc) {
   207  	relation := "is implemented by"
   208  
   209  	meth := func(sel *types.Selection) {
   210  		if sel != nil {
   211  			printf(sel.Obj(), "\t%s method (%s).%s",
   212  				relation, r.qpos.typeString(sel.Recv()), sel.Obj().Name())
   213  		}
   214  	}
   215  
   216  	if isInterface(r.t) {
   217  		if types.NewMethodSet(r.t).Len() == 0 { // TODO(adonovan): cache mset
   218  			printf(r.pos, "empty interface type %s", r.qpos.typeString(r.t))
   219  			return
   220  		}
   221  
   222  		if r.method == nil {
   223  			printf(r.pos, "interface type %s", r.qpos.typeString(r.t))
   224  		} else {
   225  			printf(r.method, "abstract method %s", r.qpos.objectString(r.method))
   226  		}
   227  
   228  		// Show concrete types (or methods) first; use two passes.
   229  		for i, sub := range r.to {
   230  			if !isInterface(sub) {
   231  				if r.method == nil {
   232  					printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s",
   233  						relation, typeKind(sub), r.qpos.typeString(sub))
   234  				} else {
   235  					meth(r.toMethod[i])
   236  				}
   237  			}
   238  		}
   239  		for i, sub := range r.to {
   240  			if isInterface(sub) {
   241  				if r.method == nil {
   242  					printf(sub.(*types.Named).Obj(), "\t%s %s type %s",
   243  						relation, typeKind(sub), r.qpos.typeString(sub))
   244  				} else {
   245  					meth(r.toMethod[i])
   246  				}
   247  			}
   248  		}
   249  
   250  		relation = "implements"
   251  		for i, super := range r.from {
   252  			if r.method == nil {
   253  				printf(super.(*types.Named).Obj(), "\t%s %s",
   254  					relation, r.qpos.typeString(super))
   255  			} else {
   256  				meth(r.fromMethod[i])
   257  			}
   258  		}
   259  	} else {
   260  		relation = "implements"
   261  
   262  		if r.from != nil {
   263  			if r.method == nil {
   264  				printf(r.pos, "%s type %s",
   265  					typeKind(r.t), r.qpos.typeString(r.t))
   266  			} else {
   267  				printf(r.method, "concrete method %s",
   268  					r.qpos.objectString(r.method))
   269  			}
   270  			for i, super := range r.from {
   271  				if r.method == nil {
   272  					printf(super.(*types.Named).Obj(), "\t%s %s",
   273  						relation, r.qpos.typeString(super))
   274  				} else {
   275  					meth(r.fromMethod[i])
   276  				}
   277  			}
   278  		}
   279  		if r.fromPtr != nil {
   280  			if r.method == nil {
   281  				printf(r.pos, "pointer type *%s", r.qpos.typeString(r.t))
   282  			} else {
   283  				// TODO(adonovan): de-dup (C).f and (*C).f implementing (I).f.
   284  				printf(r.method, "concrete method %s",
   285  					r.qpos.objectString(r.method))
   286  			}
   287  
   288  			for i, psuper := range r.fromPtr {
   289  				if r.method == nil {
   290  					printf(psuper.(*types.Named).Obj(), "\t%s %s",
   291  						relation, r.qpos.typeString(psuper))
   292  				} else {
   293  					meth(r.fromPtrMethod[i])
   294  				}
   295  			}
   296  		} else if r.from == nil {
   297  			printf(r.pos, "%s type %s implements only interface{}",
   298  				typeKind(r.t), r.qpos.typeString(r.t))
   299  		}
   300  	}
   301  }
   302  
   303  func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) {
   304  	res.Implements = &serial.Implements{
   305  		T:                       makeImplementsType(r.t, fset),
   306  		AssignableTo:            makeImplementsTypes(r.to, fset),
   307  		AssignableFrom:          makeImplementsTypes(r.from, fset),
   308  		AssignableFromPtr:       makeImplementsTypes(r.fromPtr, fset),
   309  		AssignableToMethod:      methodsToSerial(r.qpos.info.Pkg, r.toMethod, fset),
   310  		AssignableFromMethod:    methodsToSerial(r.qpos.info.Pkg, r.fromMethod, fset),
   311  		AssignableFromPtrMethod: methodsToSerial(r.qpos.info.Pkg, r.fromPtrMethod, fset),
   312  	}
   313  	if r.method != nil {
   314  		res.Implements.Method = &serial.DescribeMethod{
   315  			Name: r.qpos.objectString(r.method),
   316  			Pos:  fset.Position(r.method.Pos()).String(),
   317  		}
   318  	}
   319  }
   320  
   321  func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.ImplementsType {
   322  	var r []serial.ImplementsType
   323  	for _, t := range tt {
   324  		r = append(r, makeImplementsType(t, fset))
   325  	}
   326  	return r
   327  }
   328  
   329  func makeImplementsType(T types.Type, fset *token.FileSet) serial.ImplementsType {
   330  	var pos token.Pos
   331  	if nt, ok := deref(T).(*types.Named); ok { // implementsResult.t may be non-named
   332  		pos = nt.Obj().Pos()
   333  	}
   334  	return serial.ImplementsType{
   335  		Name: T.String(),
   336  		Pos:  fset.Position(pos).String(),
   337  		Kind: typeKind(T),
   338  	}
   339  }
   340  
   341  // typeKind returns a string describing the underlying kind of type,
   342  // e.g. "slice", "array", "struct".
   343  func typeKind(T types.Type) string {
   344  	s := reflect.TypeOf(T.Underlying()).String()
   345  	return strings.ToLower(strings.TrimPrefix(s, "*types."))
   346  }
   347  
   348  func isInterface(T types.Type) bool { return types.IsInterface(T) }
   349  
   350  type typesByString []types.Type
   351  
   352  func (p typesByString) Len() int           { return len(p) }
   353  func (p typesByString) Less(i, j int) bool { return p[i].String() < p[j].String() }
   354  func (p typesByString) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }