108 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			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"
 | |
| 
 | |
| 	"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
 | |
| }
 |