| // 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/json" |
| ) |
| |
| // 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) (queryResult, error) { |
| pkg := o.queryPkgInfo.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) toJSON(res *json.Result, fset *token.FileSet) { |
| var facts []*json.Implements |
| for _, fact := range r.facts { |
| facts = append(facts, &json.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 |
| } |