104 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			104 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2013 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 oracle
 | |
| 
 | |
| import (
 | |
| 	"go/token"
 | |
| 
 | |
| 	"code.google.com/p/go.tools/go/types"
 | |
| 	"code.google.com/p/go.tools/oracle/serial"
 | |
| )
 | |
| 
 | |
| // Implements displays the 'implements" relation among all
 | |
| // package-level named types in the package containing the query
 | |
| // position.
 | |
| //
 | |
| // TODO(adonovan): more features:
 | |
| // - should we include pairs of types belonging to
 | |
| //   different packages in the 'implements' relation?
 | |
| // - should we restrict the query to the type declaration identified
 | |
| //   by the query position, if any, and use all types in the package
 | |
| //   otherwise?
 | |
| // - should we show types that are local to functions?
 | |
| //   They can only have methods via promotion.
 | |
| // - abbreviate the set of concrete types implementing the empty
 | |
| //   interface.
 | |
| // - should we scan the instruction stream for MakeInterface
 | |
| //   instructions and report which concrete->interface conversions
 | |
| //   actually occur, with examples?  (NB: this is not a conservative
 | |
| //   answer due to ChangeInterface, i.e. subtyping among interfaces.)
 | |
| //
 | |
| func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
 | |
| 	pkg := qpos.info.Pkg
 | |
| 
 | |
| 	// Compute set of named interface/concrete types at package level.
 | |
| 	var interfaces, concretes []*types.Named
 | |
| 	scope := pkg.Scope()
 | |
| 	for _, name := range scope.Names() {
 | |
| 		mem := scope.Lookup(name)
 | |
| 		if t, ok := mem.(*types.TypeName); ok {
 | |
| 			nt := t.Type().(*types.Named)
 | |
| 			if _, ok := nt.Underlying().(*types.Interface); ok {
 | |
| 				interfaces = append(interfaces, nt)
 | |
| 			} else {
 | |
| 				concretes = append(concretes, nt)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// For each interface, show the concrete types that implement it.
 | |
| 	var facts []implementsFact
 | |
| 	for _, iface := range interfaces {
 | |
| 		fact := implementsFact{iface: iface}
 | |
| 		for _, conc := range concretes {
 | |
| 			if types.IsAssignableTo(conc, iface) {
 | |
| 				fact.conc = conc
 | |
| 			} else if ptr := types.NewPointer(conc); types.IsAssignableTo(ptr, iface) {
 | |
| 				fact.conc = ptr
 | |
| 			} else {
 | |
| 				continue
 | |
| 			}
 | |
| 			facts = append(facts, fact)
 | |
| 		}
 | |
| 	}
 | |
| 	// TODO(adonovan): sort facts to ensure test nondeterminism.
 | |
| 
 | |
| 	return &implementsResult{o.prog.Fset, facts}, nil
 | |
| }
 | |
| 
 | |
| type implementsFact struct {
 | |
| 	iface *types.Named
 | |
| 	conc  types.Type // Named or Pointer(Named)
 | |
| }
 | |
| 
 | |
| type implementsResult struct {
 | |
| 	fset  *token.FileSet
 | |
| 	facts []implementsFact // facts are grouped by interface
 | |
| }
 | |
| 
 | |
| func (r *implementsResult) display(printf printfFunc) {
 | |
| 	var prevIface *types.Named
 | |
| 	for _, fact := range r.facts {
 | |
| 		if fact.iface != prevIface {
 | |
| 			printf(fact.iface.Obj(), "\tInterface %s:", fact.iface)
 | |
| 			prevIface = fact.iface
 | |
| 		}
 | |
| 		printf(deref(fact.conc).(*types.Named).Obj(), "\t\t%s", fact.conc)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (r *implementsResult) toSerial(res *serial.Result, fset *token.FileSet) {
 | |
| 	var facts []*serial.Implements
 | |
| 	for _, fact := range r.facts {
 | |
| 		facts = append(facts, &serial.Implements{
 | |
| 			I:    fact.iface.String(),
 | |
| 			IPos: fset.Position(fact.iface.Obj().Pos()).String(),
 | |
| 			C:    fact.conc.String(),
 | |
| 			CPos: fset.Position(deref(fact.conc).(*types.Named).Obj().Pos()).String(),
 | |
| 		})
 | |
| 	}
 | |
| 	res.Implements = facts
 | |
| }
 |