Skip to content

Commit 7a6108e

Browse files
committed
internal/lsp: don't use ast.NewPackage to build builtin
ast.NewPackage mutates the input files, making it difficult to avoid races with our caching model. I had avoided a race resulting from cache handle cancellation, just to run into another race in multi-session servers. But there's no reason to use ast.NewPackage when we only have a single file. We can just interrogate the file scope wherever needed. Fixes golang/go#45868 Change-Id: I521475b51ee3b1c3e408916affecafbc629b0191 Reviewed-on: https://go-review.googlesource.com/c/tools/+/315629 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]>
1 parent edbe9be commit 7a6108e

File tree

7 files changed

+38
-70
lines changed

7 files changed

+38
-70
lines changed

internal/lsp/cache/load.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,10 @@ func (s *snapshot) load(ctx context.Context, allowNetwork bool, scopes ...interf
148148
}
149149
// Special case for the builtin package, as it has no dependencies.
150150
if pkg.PkgPath == "builtin" {
151-
if err := s.buildBuiltinPackage(ctx, pkg.GoFiles); err != nil {
152-
return err
151+
if len(pkg.GoFiles) != 1 {
152+
return errors.Errorf("only expected 1 file for builtin, got %v", len(pkg.GoFiles))
153153
}
154+
s.setBuiltin(pkg.GoFiles[0])
154155
continue
155156
}
156157
// Skip test main packages.

internal/lsp/cache/snapshot.go

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,6 @@ type snapshot struct {
5050
// the cache generation that contains the data for this snapshot.
5151
generation *memoize.Generation
5252

53-
// builtin pins the AST and package for builtin.go in memory.
54-
builtin *builtinPackageData
55-
5653
// The snapshot's initialization state is controlled by the fields below.
5754
//
5855
// initializeOnce guards snapshot initialization. Each snapshot is
@@ -64,9 +61,12 @@ type snapshot struct {
6461
// to avoid too many go/packages calls.
6562
initializedErr *source.CriticalError
6663

67-
// mu guards all of the maps in the snapshot.
64+
// mu guards all of the maps in the snapshot, as well as the builtin URI.
6865
mu sync.Mutex
6966

67+
// builtin pins the AST and package for builtin.go in memory.
68+
builtin span.URI
69+
7070
// ids maps file URIs to package IDs.
7171
// It may be invalidated on calls to go/packages.
7272
ids map[span.URI][]packageID
@@ -1745,60 +1745,37 @@ func extractMagicComments(f *ast.File) []string {
17451745
return results
17461746
}
17471747

1748-
func (s *snapshot) BuiltinPackage(ctx context.Context) (*source.BuiltinPackage, error) {
1748+
func (s *snapshot) BuiltinFile(ctx context.Context) (*source.ParsedGoFile, error) {
17491749
s.AwaitInitialized(ctx)
17501750

1751-
if s.builtin == nil {
1751+
s.mu.Lock()
1752+
builtin := s.builtin
1753+
s.mu.Unlock()
1754+
1755+
if builtin == "" {
17521756
return nil, errors.Errorf("no builtin package for view %s", s.view.name)
17531757
}
1754-
return s.builtin.parsed, s.builtin.err
1755-
}
17561758

1757-
func (s *snapshot) IsBuiltin(ctx context.Context, uri span.URI) bool {
1758-
builtin, err := s.BuiltinPackage(ctx)
1759+
fh, err := s.GetFile(ctx, builtin)
17591760
if err != nil {
1760-
event.Error(ctx, "checking for builtin", err)
1761-
return false
1761+
return nil, err
17621762
}
1763+
return s.ParseGo(ctx, fh, source.ParseFull)
1764+
}
1765+
1766+
func (s *snapshot) IsBuiltin(ctx context.Context, uri span.URI) bool {
1767+
s.mu.Lock()
1768+
defer s.mu.Unlock()
17631769
// We should always get the builtin URI in a canonical form, so use simple
17641770
// string comparison here. span.CompareURI is too expensive.
1765-
return builtin.ParsedFile.URI == uri
1771+
return uri == s.builtin
17661772
}
17671773

1768-
func (s *snapshot) buildBuiltinPackage(ctx context.Context, goFiles []string) error {
1769-
if len(goFiles) != 1 {
1770-
return errors.Errorf("only expected 1 file, got %v", len(goFiles))
1771-
}
1772-
uri := span.URIFromPath(goFiles[0])
1773-
1774-
// Get the FileHandle through the cache to avoid adding it to the snapshot
1775-
// and to get the file content from disk.
1776-
fh, err := s.view.session.cache.getFile(ctx, uri)
1777-
if err != nil {
1778-
return err
1779-
}
1780-
s.builtin = buildBuiltinPackage(ctx, fh, s)
1781-
return nil
1782-
}
1774+
func (s *snapshot) setBuiltin(path string) {
1775+
s.mu.Lock()
1776+
defer s.mu.Unlock()
17831777

1784-
func buildBuiltinPackage(ctx context.Context, fh *fileHandle, snapshot *snapshot) *builtinPackageData {
1785-
pgh := snapshot.parseGoHandle(ctx, fh, source.ParseFull)
1786-
pgf, _, err := snapshot.parseGo(ctx, pgh)
1787-
if err != nil {
1788-
return &builtinPackageData{err: err}
1789-
}
1790-
pkg, err := ast.NewPackage(snapshot.view.session.cache.fset, map[string]*ast.File{
1791-
pgf.URI.Filename(): pgf.File,
1792-
}, nil, nil)
1793-
if err != nil {
1794-
return &builtinPackageData{err: err}
1795-
}
1796-
return &builtinPackageData{
1797-
parsed: &source.BuiltinPackage{
1798-
ParsedFile: pgf,
1799-
Package: pkg,
1800-
},
1801-
}
1778+
s.builtin = span.URIFromPath(path)
18021779
}
18031780

18041781
// BuildGoplsMod generates a go.mod file for all modules in the workspace. It

internal/lsp/cache/view.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,6 @@ type builtinPackageHandle struct {
151151
handle *memoize.Handle
152152
}
153153

154-
type builtinPackageData struct {
155-
parsed *source.BuiltinPackage
156-
err error
157-
}
158-
159154
// fileBase holds the common functionality for all files.
160155
// It is intended to be embedded in the file implementations
161156
type fileBase struct {

internal/lsp/source/completion/builtin.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ import (
1414
// argument. It attempts to use the AST hints from builtin.go where
1515
// possible.
1616
func (c *completer) builtinArgKind(ctx context.Context, obj types.Object, call *ast.CallExpr) objKind {
17-
builtin, err := c.snapshot.BuiltinPackage(ctx)
17+
builtin, err := c.snapshot.BuiltinFile(ctx)
1818
if err != nil {
1919
return 0
2020
}
2121
exprIdx := exprAtPos(c.pos, call.Args)
2222

23-
builtinObj := builtin.Package.Scope.Lookup(obj.Name())
23+
builtinObj := builtin.File.Scope.Lookup(obj.Name())
2424
if builtinObj == nil {
2525
return 0
2626
}

internal/lsp/source/identifier.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,11 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, file *a
179179

180180
// Handle builtins separately.
181181
if result.Declaration.obj.Parent() == types.Universe {
182-
builtin, err := snapshot.BuiltinPackage(ctx)
182+
builtin, err := snapshot.BuiltinFile(ctx)
183183
if err != nil {
184184
return nil, err
185185
}
186-
builtinObj := builtin.Package.Scope.Lookup(result.Name)
186+
builtinObj := builtin.File.Scope.Lookup(result.Name)
187187
if builtinObj == nil {
188188
return nil, fmt.Errorf("no builtin object for %s", result.Name)
189189
}
@@ -195,7 +195,7 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, file *a
195195

196196
// The builtin package isn't in the dependency graph, so the usual
197197
// utilities won't work here.
198-
rng := NewMappedRange(snapshot.FileSet(), builtin.ParsedFile.Mapper, decl.Pos(), decl.Pos()+token.Pos(len(result.Name)))
198+
rng := NewMappedRange(snapshot.FileSet(), builtin.Mapper, decl.Pos(), decl.Pos()+token.Pos(len(result.Name)))
199199
result.Declaration.MappedRange = append(result.Declaration.MappedRange, rng)
200200
return result, nil
201201
}
@@ -204,14 +204,14 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, file *a
204204
// that this is the builtin Error.
205205
if obj := result.Declaration.obj; obj.Parent() == nil && obj.Pkg() == nil && obj.Name() == "Error" {
206206
if _, ok := obj.Type().(*types.Signature); ok {
207-
builtin, err := snapshot.BuiltinPackage(ctx)
207+
builtin, err := snapshot.BuiltinFile(ctx)
208208
if err != nil {
209209
return nil, err
210210
}
211211
// Look up "error" and then navigate to its only method.
212212
// The Error method does not appear in the builtin package's scope.log.Pri
213213
const errorName = "error"
214-
builtinObj := builtin.Package.Scope.Lookup(errorName)
214+
builtinObj := builtin.File.Scope.Lookup(errorName)
215215
if builtinObj == nil {
216216
return nil, fmt.Errorf("no builtin object for %s", errorName)
217217
}
@@ -236,7 +236,7 @@ func findIdentifier(ctx context.Context, snapshot Snapshot, pkg Package, file *a
236236
}
237237
name := method.Names[0].Name
238238
result.Declaration.node = method
239-
rng := NewMappedRange(snapshot.FileSet(), builtin.ParsedFile.Mapper, method.Pos(), method.Pos()+token.Pos(len(name)))
239+
rng := NewMappedRange(snapshot.FileSet(), builtin.Mapper, method.Pos(), method.Pos()+token.Pos(len(name)))
240240
result.Declaration.MappedRange = append(result.Declaration.MappedRange, rng)
241241
return result, nil
242242
}

internal/lsp/source/types_format.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ func (s *signature) Params() []string {
8181
// NewBuiltinSignature returns signature for the builtin object with a given
8282
// name, if a builtin object with the name exists.
8383
func NewBuiltinSignature(ctx context.Context, s Snapshot, name string) (*signature, error) {
84-
builtin, err := s.BuiltinPackage(ctx)
84+
builtin, err := s.BuiltinFile(ctx)
8585
if err != nil {
8686
return nil, err
8787
}
88-
obj := builtin.Package.Scope.Lookup(name)
88+
obj := builtin.File.Scope.Lookup(name)
8989
if obj == nil {
9090
return nil, fmt.Errorf("no builtin object for %s", name)
9191
}

internal/lsp/source/view.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ type Snapshot interface {
125125
// GoModForFile returns the URI of the go.mod file for the given URI.
126126
GoModForFile(uri span.URI) span.URI
127127

128-
// BuiltinPackage returns information about the special builtin package.
129-
BuiltinPackage(ctx context.Context) (*BuiltinPackage, error)
128+
// BuiltinFile returns information about the special builtin package.
129+
BuiltinFile(ctx context.Context) (*ParsedGoFile, error)
130130

131131
// IsBuiltin reports whether uri is part of the builtin package.
132132
IsBuiltin(ctx context.Context, uri span.URI) bool
@@ -259,11 +259,6 @@ type FileSource interface {
259259
GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
260260
}
261261

262-
type BuiltinPackage struct {
263-
Package *ast.Package
264-
ParsedFile *ParsedGoFile
265-
}
266-
267262
// A ParsedGoFile contains the results of parsing a Go file.
268263
type ParsedGoFile struct {
269264
URI span.URI

0 commit comments

Comments
 (0)