blob: 76b2e1fdba5c32d1e97f88095d0eee6d19ebf7db [file] [log] [blame]
// 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
}