1
0
mirror of https://github.com/golang/go synced 2024-10-01 08:38:47 -06:00
go/oracle/referrers.go
Alan Donovan 95bd0c4fdf go.tools/go/types: add (*PkgName).ImportedPackage method.
It returns the value formerly returned by Pkg(), i.e. the imported package.
Pkg() now returns the package enclosing the import statement,
which is consistent with all other Objects.

Fixes golang/go#8628.

LGTM=gri
R=gri
CC=golang-codereviews
https://golang.org/cl/136090043
2014-09-02 18:12:08 -04:00

108 lines
2.7 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 (
"fmt"
"go/ast"
"go/token"
"sort"
"code.google.com/p/go.tools/go/types"
"code.google.com/p/go.tools/oracle/serial"
)
// Referrers reports all identifiers that resolve to the same object
// as the queried identifier, within any package in the analysis scope.
//
func referrers(o *Oracle, qpos *QueryPos) (queryResult, error) {
id, _ := qpos.path[0].(*ast.Ident)
if id == nil {
return nil, fmt.Errorf("no identifier here")
}
obj := qpos.info.ObjectOf(id)
if obj == nil {
// Happens for y in "switch y := x.(type)", but I think that's all.
return nil, fmt.Errorf("no object for identifier")
}
// Iterate over all go/types' Uses facts for the entire program.
var refs []*ast.Ident
for _, info := range o.typeInfo {
for id2, obj2 := range info.Uses {
if sameObj(obj, obj2) {
refs = append(refs, id2)
}
}
}
sort.Sort(byNamePos(refs))
return &referrersResult{
query: id,
obj: obj,
refs: refs,
}, nil
}
// same reports whether x and y are identical, or both are PkgNames
// that import the same Package.
//
func sameObj(x, y types.Object) bool {
if x == y {
return true
}
if x, ok := x.(*types.PkgName); ok {
if y, ok := y.(*types.PkgName); ok {
return x.Imported() == y.Imported()
}
}
return false
}
// -------- utils --------
type byNamePos []*ast.Ident
func (p byNamePos) Len() int { return len(p) }
func (p byNamePos) Less(i, j int) bool { return p[i].NamePos < p[j].NamePos }
func (p byNamePos) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
type referrersResult struct {
query *ast.Ident // identifier of query
obj types.Object // object it denotes
refs []*ast.Ident // set of all other references to it
}
func (r *referrersResult) display(printf printfFunc) {
if r.query.Pos() != r.obj.Pos() {
printf(r.query, "reference to %s", r.obj.Name())
}
// TODO(adonovan): pretty-print object using same logic as
// (*describeValueResult).display.
printf(r.obj, "defined here as %s", r.obj)
for _, ref := range r.refs {
if r.query != ref {
printf(ref, "referenced here")
}
}
}
// TODO(adonovan): encode extent, not just Pos info, in Serial form.
func (r *referrersResult) toSerial(res *serial.Result, fset *token.FileSet) {
referrers := &serial.Referrers{
Pos: fset.Position(r.query.Pos()).String(),
Desc: r.obj.String(),
}
if pos := r.obj.Pos(); pos != token.NoPos { // Package objects have no Pos()
referrers.ObjPos = fset.Position(pos).String()
}
for _, ref := range r.refs {
referrers.Refs = append(referrers.Refs, fset.Position(ref.NamePos).String())
}
res.Referrers = referrers
}