Skip to content

Commit f2cd9ef

Browse files
committed
gopls/internal/lsp/source: reduce usage of TypecheckWorkspace
Fix calls to TypeCheck using TypecheckWorkspace where we really want TypecheckFull. Also, use NarrowestPackage where it suffices. In approximate order of appearance: - Code actions, semantic tokens, code lens, and document highlights are all scoped to a file; the narrowest package for that file should suffice. - When completing at a position, we need the full package to find enclosing context. Furthermore, that file is open, and so will be fully type-checked by other operations. - Ditto for suggested fixes, inlay hints, and signature help. The current behavior leads to incorrect or missing functionality when outside the workspace. I did not add comprehensive tests demonstrating this in all cases, but added one for signature help. For golang/go#57987 Change-Id: I8270d0f0a0787e36bd4103378176d150426d37f2 Reviewed-on: https://go-review.googlesource.com/c/tools/+/463375 Reviewed-by: Alan Donovan <[email protected]> TryBot-Result: Gopher Robot <[email protected]> gopls-CI: kokoro <[email protected]> Run-TryBot: Robert Findley <[email protected]>
1 parent f10e7d5 commit f2cd9ef

File tree

11 files changed

+89
-25
lines changed

11 files changed

+89
-25
lines changed

gopls/internal/lsp/code_action.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
158158

159159
// Type-check the package and also run analysis,
160160
// then combine their diagnostics.
161-
pkg, _, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.TypecheckFull, source.WidestPackage)
161+
pkg, _, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.TypecheckFull, source.NarrowestPackage)
162162
if err != nil {
163163
return nil, err
164164
}

gopls/internal/lsp/regtest/wrappers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ func (e *Env) RegexpRange(name, re string) protocol.Range {
131131
// RegexpSearch returns the starting position of the first match for re in the
132132
// buffer specified by name, calling t.Fatal on any error. It first searches
133133
// for the position in open buffers, then in workspace files.
134+
//
135+
// TODO(rfindley): RegexpSearch should return a protocol.Location (but that is
136+
// a large change).
134137
func (e *Env) RegexpSearch(name, re string) protocol.Position {
135138
e.T.Helper()
136139
pos, err := e.Editor.RegexpSearch(name, re)

gopls/internal/lsp/semantic.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func (s *Server) computeSemanticTokens(ctx context.Context, td protocol.TextDocu
9393
if kind != source.Go {
9494
return nil, nil
9595
}
96-
pkg, pgf, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.TypecheckFull, source.WidestPackage)
96+
pkg, pgf, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.TypecheckFull, source.NarrowestPackage)
9797
if err != nil {
9898
return nil, err
9999
}

gopls/internal/lsp/source/code_lens.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func TestsAndBenchmarks(ctx context.Context, snapshot Snapshot, fh FileHandle) (
100100
if !strings.HasSuffix(fh.URI().Filename(), "_test.go") {
101101
return out, nil
102102
}
103-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckWorkspace, WidestPackage)
103+
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckFull, NarrowestPackage)
104104
if err != nil {
105105
return out, err
106106
}

gopls/internal/lsp/source/completion/completion.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ func Completion(ctx context.Context, snapshot source.Snapshot, fh source.FileHan
430430

431431
startTime := time.Now()
432432

433-
pkg, pgf, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.TypecheckWorkspace, source.NarrowestPackage)
433+
pkg, pgf, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.TypecheckFull, source.NarrowestPackage)
434434
if err != nil || pgf.File.Package == token.NoPos {
435435
// If we can't parse this file or find position for the package
436436
// keyword, it may be missing a package declaration. Try offering

gopls/internal/lsp/source/fix.go

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,16 @@ var suggestedFixes = map[string]SuggestedFixFunc{
5353
// singleFile calls analyzers that expect inputs for a single file
5454
func singleFile(sf singleFileFixFunc) SuggestedFixFunc {
5555
return func(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) (*token.FileSet, *analysis.SuggestedFix, error) {
56-
fset, rng, src, file, pkg, info, err := getAllSuggestedFixInputs(ctx, snapshot, fh, pRng)
56+
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckFull, NarrowestPackage)
5757
if err != nil {
5858
return nil, nil, err
5959
}
60-
fix, err := sf(fset, rng, src, file, pkg, info)
61-
return fset, fix, err
60+
rng, err := pgf.RangeToTokenRange(pRng)
61+
if err != nil {
62+
return nil, nil, err
63+
}
64+
fix, err := sf(pkg.FileSet(), rng, pgf.Src, pgf.File, pkg.GetTypes(), pkg.GetTypesInfo())
65+
return pkg.FileSet(), fix, err
6266
}
6367
}
6468

@@ -130,17 +134,3 @@ func ApplyFix(ctx context.Context, fix string, snapshot Snapshot, fh FileHandle,
130134
}
131135
return edits, nil
132136
}
133-
134-
// getAllSuggestedFixInputs is a helper function to collect all possible needed
135-
// inputs for an AppliesFunc or SuggestedFixFunc.
136-
func getAllSuggestedFixInputs(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) (*token.FileSet, safetoken.Range, []byte, *ast.File, *types.Package, *types.Info, error) {
137-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckWorkspace, NarrowestPackage)
138-
if err != nil {
139-
return nil, safetoken.Range{}, nil, nil, nil, nil, fmt.Errorf("getting file for Identifier: %w", err)
140-
}
141-
rng, err := pgf.RangeToTokenRange(pRng)
142-
if err != nil {
143-
return nil, safetoken.Range{}, nil, nil, nil, nil, err
144-
}
145-
return pkg.FileSet(), rng, pgf.Src, pgf.File, pkg.GetTypes(), pkg.GetTypesInfo(), nil
146-
}

gopls/internal/lsp/source/highlight.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func Highlight(ctx context.Context, snapshot Snapshot, fh FileHandle, position p
2323

2424
// We always want fully parsed files for highlight, regardless
2525
// of whether the file belongs to a workspace package.
26-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckFull, WidestPackage)
26+
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckFull, NarrowestPackage)
2727
if err != nil {
2828
return nil, fmt.Errorf("getting package for Highlight: %w", err)
2929
}

gopls/internal/lsp/source/inlay_hint.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func InlayHint(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng proto
8282
ctx, done := event.Start(ctx, "source.InlayHint")
8383
defer done()
8484

85-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckWorkspace, NarrowestPackage)
85+
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckFull, NarrowestPackage)
8686
if err != nil {
8787
return nil, fmt.Errorf("getting file for InlayHint: %w", err)
8888
}

gopls/internal/lsp/source/signature_help.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ func SignatureHelp(ctx context.Context, snapshot Snapshot, fh FileHandle, positi
2020
ctx, done := event.Start(ctx, "source.SignatureHelp")
2121
defer done()
2222

23-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckWorkspace, NarrowestPackage)
23+
// We need full type-checking here, as we must type-check function bodies in
24+
// order to provide signature help at the requested position.
25+
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckFull, NarrowestPackage)
2426
if err != nil {
2527
return nil, 0, fmt.Errorf("getting file for SignatureHelp: %w", err)
2628
}

gopls/internal/lsp/source/stub.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
)
2727

2828
func stubSuggestedFixFunc(ctx context.Context, snapshot Snapshot, fh FileHandle, rng protocol.Range) (*token.FileSet, *analysis.SuggestedFix, error) {
29-
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckWorkspace, NarrowestPackage)
29+
pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), TypecheckFull, NarrowestPackage)
3030
if err != nil {
3131
return nil, nil, fmt.Errorf("GetTypedFile: %w", err)
3232
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2023 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 misc
6+
7+
import (
8+
"testing"
9+
10+
"github.com/google/go-cmp/cmp"
11+
"golang.org/x/tools/gopls/internal/lsp/protocol"
12+
. "golang.org/x/tools/gopls/internal/lsp/regtest"
13+
)
14+
15+
func TestSignatureHelpInNonWorkspacePackage(t *testing.T) {
16+
const files = `
17+
-- a/go.mod --
18+
module a.com
19+
20+
go 1.18
21+
-- a/a/a.go --
22+
package a
23+
24+
func DoSomething(int) {}
25+
26+
func _() {
27+
DoSomething()
28+
}
29+
-- b/go.mod --
30+
module b.com
31+
go 1.18
32+
33+
require a.com v1.0.0
34+
35+
replace a.com => ../a
36+
-- b/b/b.go --
37+
package b
38+
39+
import "a.com/a"
40+
41+
func _() {
42+
a.DoSomething()
43+
}
44+
`
45+
46+
WithOptions(
47+
WorkspaceFolders("a"),
48+
).Run(t, files, func(t *testing.T, env *Env) {
49+
env.OpenFile("a/a/a.go")
50+
env.OpenFile("b/b/b.go")
51+
signatureHelp := func(filename string) *protocol.SignatureHelp {
52+
pos := env.RegexpSearch(filename, `DoSomething\(()\)`)
53+
var params protocol.SignatureHelpParams
54+
params.Position = pos
55+
params.TextDocument.URI = env.Sandbox.Workdir.URI(filename)
56+
help, err := env.Editor.Server.SignatureHelp(env.Ctx, &params)
57+
if err != nil {
58+
t.Fatal(err)
59+
}
60+
return help
61+
}
62+
ahelp := signatureHelp("a/a/a.go")
63+
bhelp := signatureHelp("b/b/b.go")
64+
65+
if diff := cmp.Diff(ahelp, bhelp); diff != "" {
66+
t.Fatal(diff)
67+
}
68+
})
69+
}

0 commit comments

Comments
 (0)