github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/source/references.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package source
     6  
     7  import (
     8  	"context"
     9  	"go/ast"
    10  	"go/token"
    11  	"go/types"
    12  	"sort"
    13  
    14  	"github.com/april1989/origin-go-tools/internal/event"
    15  	"github.com/april1989/origin-go-tools/internal/lsp/protocol"
    16  	"github.com/april1989/origin-go-tools/internal/span"
    17  	"golang.org/x/xerrors"
    18  )
    19  
    20  // ReferenceInfo holds information about reference to an identifier in Go source.
    21  type ReferenceInfo struct {
    22  	Name string
    23  	mappedRange
    24  	ident         *ast.Ident
    25  	obj           types.Object
    26  	pkg           Package
    27  	isDeclaration bool
    28  }
    29  
    30  // References returns a list of references for a given identifier within the packages
    31  // containing i.File. Declarations appear first in the result.
    32  func References(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Position, includeDeclaration bool) ([]*ReferenceInfo, error) {
    33  	ctx, done := event.Start(ctx, "source.References")
    34  	defer done()
    35  
    36  	qualifiedObjs, err := qualifiedObjsAtProtocolPos(ctx, s, f, pp)
    37  	// Don't return references for builtin types.
    38  	if xerrors.Is(err, errBuiltin) {
    39  		return nil, nil
    40  	}
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	refs, err := references(ctx, s, qualifiedObjs, includeDeclaration)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	toSort := refs
    49  	if includeDeclaration {
    50  		toSort = refs[1:]
    51  	}
    52  	sort.Slice(toSort, func(i, j int) bool {
    53  		x := span.CompareURI(toSort[i].URI(), toSort[j].URI())
    54  		if x == 0 {
    55  			return toSort[i].ident.Pos() < toSort[j].ident.Pos()
    56  		}
    57  		return x < 0
    58  	})
    59  	return refs, nil
    60  }
    61  
    62  // references is a helper function to avoid recomputing qualifiedObjsAtProtocolPos.
    63  func references(ctx context.Context, snapshot Snapshot, qos []qualifiedObject, includeDeclaration bool) ([]*ReferenceInfo, error) {
    64  	var (
    65  		references []*ReferenceInfo
    66  		seen       = make(map[token.Position]bool)
    67  	)
    68  
    69  	// Make sure declaration is the first item in the response.
    70  	if includeDeclaration {
    71  		filename := snapshot.FileSet().Position(qos[0].obj.Pos()).Filename
    72  		pgf, err := qos[0].pkg.File(span.URIFromPath(filename))
    73  		if err != nil {
    74  			return nil, err
    75  		}
    76  		ident, err := findIdentifier(ctx, snapshot, qos[0].pkg, pgf.File, qos[0].obj.Pos())
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  		references = append(references, &ReferenceInfo{
    81  			mappedRange:   ident.mappedRange,
    82  			Name:          qos[0].obj.Name(),
    83  			ident:         ident.ident,
    84  			obj:           qos[0].obj,
    85  			pkg:           ident.pkg,
    86  			isDeclaration: true,
    87  		})
    88  	}
    89  	for _, qo := range qos {
    90  		var searchPkgs []Package
    91  
    92  		// Only search dependents if the object is exported.
    93  		if qo.obj.Exported() {
    94  			reverseDeps, err := snapshot.GetReverseDependencies(ctx, qo.pkg.ID())
    95  			if err != nil {
    96  				return nil, err
    97  			}
    98  			searchPkgs = append(searchPkgs, reverseDeps...)
    99  		}
   100  		// Add the package in which the identifier is declared.
   101  		searchPkgs = append(searchPkgs, qo.pkg)
   102  		for _, pkg := range searchPkgs {
   103  			for ident, obj := range pkg.GetTypesInfo().Uses {
   104  				if obj != qo.obj {
   105  					continue
   106  				}
   107  				pos := snapshot.FileSet().Position(ident.Pos())
   108  				if seen[pos] {
   109  					continue
   110  				}
   111  				seen[pos] = true
   112  				rng, err := posToMappedRange(snapshot, pkg, ident.Pos(), ident.End())
   113  				if err != nil {
   114  					return nil, err
   115  				}
   116  				references = append(references, &ReferenceInfo{
   117  					Name:        ident.Name,
   118  					ident:       ident,
   119  					pkg:         pkg,
   120  					obj:         obj,
   121  					mappedRange: rng,
   122  				})
   123  			}
   124  		}
   125  	}
   126  	return references, nil
   127  }