| // 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" |
| |
| "golang.org/x/tools/go/types" |
| "golang.org/x/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 |
| } |