Skip to content

Commit 49f55a9

Browse files
dandua98stamblerre
authored andcommitted
internal/lsp: move package selection to before type checking
This change moves package selection to before type checking so we don't unnecessarily type-check both variants of a package. As a result, exec time and memory usage for features making calls to GetParsedFile are cut by half since we only type check either the narrowest or the widest package. Change-Id: Ifd076f8c38e33de2bd3509fe17feafccd59d7419 Reviewed-on: https://go-review.googlesource.com/c/tools/+/257240 Run-TryBot: Danish Dua <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Trust: Danish Dua <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]> (cherry picked from commit 8d73f17) Reviewed-on: https://go-review.googlesource.com/c/tools/+/257601 Trust: Rebecca Stambler <[email protected]> Run-TryBot: Rebecca Stambler <[email protected]> Reviewed-by: Danish Dua <[email protected]>
1 parent eb44a2b commit 49f55a9

File tree

6 files changed

+88
-73
lines changed

6 files changed

+88
-73
lines changed

internal/lsp/cache/snapshot.go

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,54 @@ func hashUnsavedOverlays(files map[span.URI]source.VersionedFileHandle) string {
287287
func (s *snapshot) PackagesForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode) ([]source.Package, error) {
288288
ctx = event.Label(ctx, tag.URI.Of(uri))
289289

290+
phs, err := s.packageHandlesForFile(ctx, uri, mode)
291+
if err != nil {
292+
return nil, err
293+
}
294+
var pkgs []source.Package
295+
for _, ph := range phs {
296+
pkg, err := ph.check(ctx, s)
297+
if err != nil {
298+
return nil, err
299+
}
300+
pkgs = append(pkgs, pkg)
301+
}
302+
return pkgs, nil
303+
}
304+
305+
func (s *snapshot) PackageForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode, pkgPolicy source.PackageFilter) (source.Package, error) {
306+
ctx = event.Label(ctx, tag.URI.Of(uri))
307+
308+
phs, err := s.packageHandlesForFile(ctx, uri, mode)
309+
if err != nil {
310+
return nil, err
311+
}
312+
313+
if len(phs) < 1 {
314+
return nil, errors.Errorf("no packages")
315+
}
316+
317+
ph := phs[0]
318+
for _, handle := range phs[1:] {
319+
switch pkgPolicy {
320+
case source.WidestPackage:
321+
if ph == nil || len(handle.CompiledGoFiles()) > len(ph.CompiledGoFiles()) {
322+
ph = handle
323+
}
324+
case source.NarrowestPackage:
325+
if ph == nil || len(handle.CompiledGoFiles()) < len(ph.CompiledGoFiles()) {
326+
ph = handle
327+
}
328+
}
329+
}
330+
if ph == nil {
331+
return nil, errors.Errorf("no packages in input")
332+
}
333+
334+
return ph.check(ctx, s)
335+
}
336+
337+
func (s *snapshot) packageHandlesForFile(ctx context.Context, uri span.URI, mode source.TypecheckMode) ([]*packageHandle, error) {
290338
// Check if we should reload metadata for the file. We don't invalidate IDs
291339
// (though we should), so the IDs will be a better source of truth than the
292340
// metadata. If there are no IDs for the file, then we should also reload.
@@ -310,7 +358,7 @@ func (s *snapshot) PackagesForFile(ctx context.Context, uri span.URI, mode sourc
310358
}
311359
}
312360
// Get the list of IDs from the snapshot again, in case it has changed.
313-
var pkgs []source.Package
361+
var phs []*packageHandle
314362
for _, id := range s.getIDsForURI(uri) {
315363
var parseModes []source.ParseMode
316364
switch mode {
@@ -327,22 +375,15 @@ func (s *snapshot) PackagesForFile(ctx context.Context, uri span.URI, mode sourc
327375
}
328376

329377
for _, parseMode := range parseModes {
330-
pkg, err := s.checkedPackage(ctx, id, parseMode)
378+
ph, err := s.buildPackageHandle(ctx, id, parseMode)
331379
if err != nil {
332380
return nil, err
333381
}
334-
pkgs = append(pkgs, pkg)
382+
phs = append(phs, ph)
335383
}
336384
}
337-
return pkgs, nil
338-
}
339385

340-
func (s *snapshot) checkedPackage(ctx context.Context, id packageID, mode source.ParseMode) (*pkg, error) {
341-
ph, err := s.buildPackageHandle(ctx, id, mode)
342-
if err != nil {
343-
return nil, err
344-
}
345-
return ph.check(ctx, s)
386+
return phs, nil
346387
}
347388

348389
func (s *snapshot) GetReverseDependencies(ctx context.Context, id string) ([]source.Package, error) {
@@ -366,6 +407,14 @@ func (s *snapshot) GetReverseDependencies(ctx context.Context, id string) ([]sou
366407
return pkgs, nil
367408
}
368409

410+
func (s *snapshot) checkedPackage(ctx context.Context, id packageID, mode source.ParseMode) (*pkg, error) {
411+
ph, err := s.buildPackageHandle(ctx, id, mode)
412+
if err != nil {
413+
return nil, err
414+
}
415+
return ph.check(ctx, s)
416+
}
417+
369418
// transitiveReverseDependencies populates the uris map with file URIs
370419
// belonging to the provided package and its transitive reverse dependencies.
371420
func (s *snapshot) transitiveReverseDependencies(id packageID, ids map[packageID]struct{}) {

internal/lsp/code_action.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,7 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
123123
if ctx.Err() != nil {
124124
return nil, ctx.Err()
125125
}
126-
pkgs, err := snapshot.PackagesForFile(ctx, fh.URI(), source.TypecheckFull)
127-
if err != nil {
128-
return nil, err
129-
}
130-
pkg, err := source.WidestPackage(pkgs)
126+
pkg, err := snapshot.PackageForFile(ctx, fh.URI(), source.TypecheckFull, source.WidestPackage)
131127
if err != nil {
132128
return nil, err
133129
}

internal/lsp/link.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,7 @@ func modLinks(ctx context.Context, snapshot source.Snapshot, fh source.FileHandl
100100
func goLinks(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.DocumentLink, error) {
101101
view := snapshot.View()
102102
// We don't actually need type information, so any typecheck mode is fine.
103-
pkgs, err := snapshot.PackagesForFile(ctx, fh.URI(), source.TypecheckWorkspace)
104-
if err != nil {
105-
return nil, err
106-
}
107-
pkg, err := source.WidestPackage(pkgs)
103+
pkg, err := snapshot.PackageForFile(ctx, fh.URI(), source.TypecheckWorkspace, source.WidestPackage)
108104
if err != nil {
109105
return nil, err
110106
}

internal/lsp/source/completion/completion.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ func Completion(ctx context.Context, snapshot source.Snapshot, fh source.FileHan
404404
// present but no package name exists.
405405
items, surrounding, innerErr := packageClauseCompletions(ctx, snapshot, fh, protoPos)
406406
if innerErr != nil {
407-
// return the error for getParsedFile since it's more relevant in this situation.
407+
// return the error for GetParsedFile since it's more relevant in this situation.
408408
return nil, nil, errors.Errorf("getting file for Completion: %w (package completions: %v)", err, innerErr)
409409

410410
}

internal/lsp/source/util.go

Lines changed: 4 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -75,64 +75,17 @@ func (s MappedRange) URI() span.URI {
7575
}
7676

7777
// GetParsedFile is a convenience function that extracts the Package and
78-
// ParsedGoFile for a File in a Snapshot. selectPackage is typically
79-
// Narrowest/WidestPackageHandle below.
80-
func GetParsedFile(ctx context.Context, snapshot Snapshot, fh FileHandle, selectPackage PackagePolicy) (Package, *ParsedGoFile, error) {
81-
phs, err := snapshot.PackagesForFile(ctx, fh.URI(), TypecheckWorkspace)
82-
if err != nil {
83-
return nil, nil, err
84-
}
85-
pkg, err := selectPackage(phs)
78+
// ParsedGoFile for a file in a Snapshot. pkgPolicy is one of NarrowestPackage/
79+
// WidestPackage.
80+
func GetParsedFile(ctx context.Context, snapshot Snapshot, fh FileHandle, pkgPolicy PackageFilter) (Package, *ParsedGoFile, error) {
81+
pkg, err := snapshot.PackageForFile(ctx, fh.URI(), TypecheckWorkspace, pkgPolicy)
8682
if err != nil {
8783
return nil, nil, err
8884
}
8985
pgh, err := pkg.File(fh.URI())
9086
return pkg, pgh, err
9187
}
9288

93-
type PackagePolicy func([]Package) (Package, error)
94-
95-
// NarrowestPackage picks the "narrowest" package for a given file.
96-
//
97-
// By "narrowest" package, we mean the package with the fewest number of files
98-
// that includes the given file. This solves the problem of test variants,
99-
// as the test will have more files than the non-test package.
100-
func NarrowestPackage(pkgs []Package) (Package, error) {
101-
if len(pkgs) < 1 {
102-
return nil, errors.Errorf("no packages")
103-
}
104-
result := pkgs[0]
105-
for _, handle := range pkgs[1:] {
106-
if result == nil || len(handle.CompiledGoFiles()) < len(result.CompiledGoFiles()) {
107-
result = handle
108-
}
109-
}
110-
if result == nil {
111-
return nil, errors.Errorf("no packages in input")
112-
}
113-
return result, nil
114-
}
115-
116-
// WidestPackage returns the Package containing the most files.
117-
//
118-
// This is useful for something like diagnostics, where we'd prefer to offer diagnostics
119-
// for as many files as possible.
120-
func WidestPackage(pkgs []Package) (Package, error) {
121-
if len(pkgs) < 1 {
122-
return nil, errors.Errorf("no packages")
123-
}
124-
result := pkgs[0]
125-
for _, handle := range pkgs[1:] {
126-
if result == nil || len(handle.CompiledGoFiles()) > len(result.CompiledGoFiles()) {
127-
result = handle
128-
}
129-
}
130-
if result == nil {
131-
return nil, errors.Errorf("no packages in input")
132-
}
133-
return result, nil
134-
}
135-
13689
func IsGenerated(ctx context.Context, snapshot Snapshot, uri span.URI) bool {
13790
fh, err := snapshot.GetFile(ctx, uri)
13891
if err != nil {

internal/lsp/source/view.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ type Snapshot interface {
106106
// in mode.
107107
PackagesForFile(ctx context.Context, uri span.URI, mode TypecheckMode) ([]Package, error)
108108

109+
// PackageForFile returns a single package that this file belongs to,
110+
// checked in mode and filtered by the package policy.
111+
PackageForFile(ctx context.Context, uri span.URI, mode TypecheckMode, selectPackage PackageFilter) (Package, error)
112+
109113
// GetActiveReverseDeps returns the active files belonging to the reverse
110114
// dependencies of this file's package, checked in TypecheckWorkspace mode.
111115
GetReverseDependencies(ctx context.Context, id string) ([]Package, error)
@@ -127,6 +131,23 @@ type Snapshot interface {
127131
WorkspaceDirectories(ctx context.Context) []span.URI
128132
}
129133

134+
// PackageFilter sets how a package is filtered out from a set of packages
135+
// containing a given file.
136+
type PackageFilter int
137+
138+
const (
139+
// NarrowestPackage picks the "narrowest" package for a given file.
140+
// By "narrowest" package, we mean the package with the fewest number of
141+
// files that includes the given file. This solves the problem of test
142+
// variants, as the test will have more files than the non-test package.
143+
NarrowestPackage PackageFilter = iota
144+
145+
// WidestPackage returns the Package containing the most files.
146+
// This is useful for something like diagnostics, where we'd prefer to
147+
// offer diagnostics for as many files as possible.
148+
WidestPackage
149+
)
150+
130151
// View represents a single workspace.
131152
// This is the level at which we maintain configuration like working directory
132153
// and build tags.

0 commit comments

Comments
 (0)