Skip to content

Commit

Permalink
gopls/internal/lsp: support more cases of definition on linkname dire…
Browse files Browse the repository at this point in the history
…ctive.

Handle the case where //go:linkname localname importpath.name
is used in a package with no transitive dependency, forwards or
reverse, to importpath.

Updates golang/go#57312

Change-Id: I0033b166558275931a371a967caa6044a1b089f3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/469695
Run-TryBot: Alan Donovan <adonovan@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Alan Donovan <adonovan@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
  • Loading branch information
vikblom authored and gopherbot committed Feb 22, 2023
1 parent e85b533 commit 25d2519
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 41 deletions.
53 changes: 12 additions & 41 deletions gopls/internal/lsp/source/linkname.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,31 +92,23 @@ func findLinknameOnLine(pgf *ParsedGoFile, line int) (string, int) {
// findLinkname searches dependencies of packages containing fh for an object
// with linker name matching the given package path and name.
func findLinkname(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position, pkgPath PackagePath, name string) ([]protocol.Location, error) {
metas, err := snapshot.MetadataForFile(ctx, fh.URI())
// Typically the linkname refers to a forward dependency
// or a reverse dependency, but in general it may refer
// to any package in the workspace.
var pkgMeta *Metadata
metas, err := snapshot.AllMetadata(ctx)
if err != nil {
return nil, err
}
if len(metas) == 0 {
return nil, fmt.Errorf("no package found for file %q", fh.URI())
metas = RemoveIntermediateTestVariants(metas)
for _, meta := range metas {
if meta.PkgPath == pkgPath {
pkgMeta = meta
break
}
}

// Find package starting from narrowest package metadata.
pkgMeta := findPackageInDeps(snapshot, metas[0], pkgPath)
if pkgMeta == nil {
// Fall back to searching reverse dependencies.
reverse, err := snapshot.ReverseDependencies(ctx, metas[0].ID, true /* transitive */)
if err != nil {
return nil, err
}
for _, dep := range reverse {
if dep.PkgPath == pkgPath {
pkgMeta = dep
break
}
}
if pkgMeta == nil {
return nil, fmt.Errorf("cannot find package %q", pkgPath)
}
return nil, fmt.Errorf("cannot find package %q", pkgPath)
}

// When found, type check the desired package (snapshot.TypeCheck in TypecheckFull mode),
Expand All @@ -142,24 +134,3 @@ func findLinkname(ctx context.Context, snapshot Snapshot, fh FileHandle, pos pro
}
return []protocol.Location{loc}, nil
}

// findPackageInDeps returns the dependency of meta of the specified package path, if any.
func findPackageInDeps(snapshot Snapshot, meta *Metadata, pkgPath PackagePath) *Metadata {
seen := make(map[*Metadata]bool)
var visit func(*Metadata) *Metadata
visit = func(meta *Metadata) *Metadata {
if !seen[meta] {
seen[meta] = true
if meta.PkgPath == pkgPath {
return meta
}
for _, id := range meta.DepsByPkgPath {
if m := visit(snapshot.Metadata(id)); m != nil {
return m
}
}
}
return nil
}
return visit(meta)
}
39 changes: 39 additions & 0 deletions gopls/internal/regtest/misc/definition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,45 @@ func TestGoToLinknameDefinitionInReverseDep(t *testing.T) {
})
}

// The linkname directive connects two packages not related in the import graph.
const linknameDefinitionDisconnected = `
-- go.mod --
module mod.com
-- a/a.go --
package a
import (
_ "unsafe"
)
//go:linkname foo mod.com/b.bar
func foo() string
-- b/b.go --
package b
func bar() string {
return "bar as foo"
}`

func TestGoToLinknameDefinitionDisconnected(t *testing.T) {
Run(t, linknameDefinitionDisconnected, func(t *testing.T, env *Env) {
env.OpenFile("a/a.go")

// Jump from directives 2nd arg.
start := env.RegexpSearch("a/a.go", `b.bar`)
loc := env.GoToDefinition(start)
name := env.Sandbox.Workdir.URIToPath(loc.URI)
if want := "b/b.go"; name != want {
t.Errorf("GoToDefinition: got file %q, want %q", name, want)
}
if want := env.RegexpSearch("b/b.go", `bar`); loc != want {
t.Errorf("GoToDefinition: got position %v, want %v", loc, want)
}
})
}

const stdlibDefinition = `
-- go.mod --
module mod.com
Expand Down

0 comments on commit 25d2519

Please sign in to comment.