Skip to content

Commit

Permalink
internal/lsp: find references to package
Browse files Browse the repository at this point in the history
Update References to detect if the package is referenced and a regtest to test within and external package references.

Updates golang/go#41567

Change-Id: I607a47bf15f1c9f8236336f795fcef081db49d6a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/408714
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Dylan Le <dungtuanle@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
  • Loading branch information
Dylan Le committed Jun 21, 2022
1 parent a1303c8 commit 59bd4fa
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 0 deletions.
88 changes: 88 additions & 0 deletions gopls/internal/regtest/misc/references_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
package misc

import (
"fmt"
"strings"
"testing"

. "golang.org/x/tools/internal/lsp/regtest"
Expand Down Expand Up @@ -81,3 +83,89 @@ func _() {
}
})
}

func TestPackageReferences(t *testing.T) {
tests := []struct {
packageName string
wantRefCount int
wantFiles []string
}{
{
"lib1",
3,
[]string{
"main.go",
"lib1/a.go",
"lib1/b.go",
},
},
{
"lib2",
2,
[]string{
"main.go",
"lib2/a.go",
},
},
}

const files = `
-- go.mod --
module mod.com
go 1.18
-- lib1/a.go --
package lib1
const A = 1
-- lib1/b.go --
package lib1
const B = 1
-- lib2/a.go --
package lib2
const C = 1
-- main.go --
package main
import (
"mod.com/lib1"
"mod.com/lib2"
)
func main() {
println("Hello")
}
`
Run(t, files, func(t *testing.T, env *Env) {
for _, test := range tests {
f := fmt.Sprintf("%s/a.go", test.packageName)
env.OpenFile(f)
pos := env.RegexpSearch(f, test.packageName)
refs := env.References(fmt.Sprintf("%s/a.go", test.packageName), pos)
if len(refs) != test.wantRefCount {
t.Fatalf("got %v reference(s), want %d", len(refs), test.wantRefCount)
}
var refURIs []string
for _, ref := range refs {
refURIs = append(refURIs, string(ref.URI))
}
for _, base := range test.wantFiles {
hasBase := false
for _, ref := range refURIs {
if strings.HasSuffix(ref, base) {
hasBase = true
break
}
}
if !hasBase {
t.Fatalf("got [%v], want reference ends with \"%v\"", strings.Join(refURIs, ","), base)
}
}
}
})
}
60 changes: 60 additions & 0 deletions internal/lsp/source/references.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import (
"errors"
"fmt"
"go/ast"

"go/token"
"go/types"
"sort"
"strconv"

"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/safetoken"
"golang.org/x/tools/internal/span"
)

Expand All @@ -34,6 +37,63 @@ func References(ctx context.Context, s Snapshot, f FileHandle, pp protocol.Posit
ctx, done := event.Start(ctx, "source.References")
defer done()

// Find position of the package name declaration
pgf, err := s.ParseGo(ctx, f, ParseFull)
if err != nil {
return nil, err
}

cursorOffset, err := pgf.Mapper.Offset(pp)
if err != nil {
return nil, err
}

packageNameStart, err := safetoken.Offset(pgf.Tok, pgf.File.Name.Pos())
if err != nil {
return nil, err
}

packageNameEnd, err := safetoken.Offset(pgf.Tok, pgf.File.Name.End())
if err != nil {
return nil, err
}

if packageNameStart <= cursorOffset && cursorOffset < packageNameEnd {
renamingPkg, err := s.PackageForFile(ctx, f.URI(), TypecheckAll, NarrowestPackage)
if err != nil {
return nil, err
}

// Find external references to the package.
rdeps, err := s.GetReverseDependencies(ctx, renamingPkg.ID())
if err != nil {
return nil, err
}
var refs []*ReferenceInfo
for _, dep := range rdeps {
for _, f := range dep.CompiledGoFiles() {
for _, imp := range f.File.Imports {
if path, err := strconv.Unquote(imp.Path.Value); err == nil && path == renamingPkg.PkgPath() {
refs = append(refs, &ReferenceInfo{
Name: pgf.File.Name.Name,
MappedRange: NewMappedRange(s.FileSet(), f.Mapper, imp.Pos(), imp.End()),
})
}
}
}
}

// Find internal references to the package within the package itself
for _, f := range renamingPkg.CompiledGoFiles() {
refs = append(refs, &ReferenceInfo{
Name: pgf.File.Name.Name,
MappedRange: NewMappedRange(s.FileSet(), f.Mapper, f.File.Name.Pos(), f.File.Name.End()),
})
}

return refs, nil
}

qualifiedObjs, err := qualifiedObjsAtProtocolPos(ctx, s, f.URI(), pp)
// Don't return references for builtin types.
if errors.Is(err, errBuiltin) {
Expand Down

0 comments on commit 59bd4fa

Please sign in to comment.