1
0
mirror of https://github.com/golang/go synced 2024-10-01 11:28:34 -06:00
go/oracle/implements.go
Alan Donovan 37f76edde8 go.tools/oracle: support -format=xml (for Eclipse)
This CL is mostly a renaming s/json/serial/, abstracting the
oracle package away from any particular data syntax.  (The
encoding/* machinery is very clean; clearly I should have
structured it this way from the outset.)

Supporting XML then becomes a one-liner in cmd/oracle/main.go.

Also: call MarshalIndent(), not Marshall() then Indent().

R=crawshaw
CC=golang-dev
https://golang.org/cl/13858046
2013-09-24 15:08:14 -04:00

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
}