1// Copyright 2018 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
5package source
6
7import (
8	"context"
9	"fmt"
10	"go/ast"
11	"go/token"
12	"go/types"
13
14	"golang.org/x/mod/modfile"
15	"golang.org/x/tools/go/analysis"
16	"golang.org/x/tools/go/packages"
17	"golang.org/x/tools/internal/imports"
18	"golang.org/x/tools/internal/lsp/protocol"
19	"golang.org/x/tools/internal/packagesinternal"
20	"golang.org/x/tools/internal/span"
21)
22
23// Snapshot represents the current state for the given view.
24type Snapshot interface {
25	ID() uint64
26
27	// View returns the View associated with this snapshot.
28	View() View
29
30	// Config returns the configuration for the view.
31	Config(ctx context.Context) *packages.Config
32
33	// GetFile returns the file object for a given URI, initializing it
34	// if it is not already part of the view.
35	GetFile(uri span.URI) (FileHandle, error)
36
37	// IsOpen returns whether the editor currently has a file open.
38	IsOpen(uri span.URI) bool
39
40	// IsSaved returns whether the contents are saved on disk or not.
41	IsSaved(uri span.URI) bool
42
43	// Analyze runs the analyses for the given package at this snapshot.
44	Analyze(ctx context.Context, id string, analyzers []*analysis.Analyzer) ([]*Error, error)
45
46	// FindAnalysisError returns the analysis error represented by the diagnostic.
47	// This is used to get the SuggestedFixes associated with that error.
48	FindAnalysisError(ctx context.Context, pkgID, analyzerName, msg string, rng protocol.Range) (*Error, error)
49
50	// ModTidyHandle returns a ModTidyHandle for the given go.mod file handle.
51	// This function can have no data or error if there is no modfile detected.
52	ModTidyHandle(ctx context.Context, fh FileHandle) (ModTidyHandle, error)
53
54	// ModHandle returns a ModHandle for the passed in go.mod file handle.
55	// This function can have no data if there is no modfile detected.
56	ModHandle(ctx context.Context, fh FileHandle) ModHandle
57
58	// PackageHandles returns the PackageHandles for the packages that this file
59	// belongs to.
60	PackageHandles(ctx context.Context, fh FileHandle) ([]PackageHandle, error)
61
62	// GetActiveReverseDeps returns the active files belonging to the reverse
63	// dependencies of this file's package.
64	GetReverseDependencies(ctx context.Context, id string) ([]PackageHandle, error)
65
66	// CachedImportPaths returns all the imported packages loaded in this snapshot,
67	// indexed by their import path.
68	CachedImportPaths(ctx context.Context) (map[string]Package, error)
69
70	// KnownPackages returns all the packages loaded in this snapshot.
71	// Workspace packages may be parsed in ParseFull mode, whereas transitive
72	// dependencies will be in ParseExported mode.
73	KnownPackages(ctx context.Context) ([]PackageHandle, error)
74
75	// WorkspacePackages returns the PackageHandles for the snapshot's
76	// top-level packages.
77	WorkspacePackages(ctx context.Context) ([]PackageHandle, error)
78}
79
80// PackageHandle represents a handle to a specific version of a package.
81// It is uniquely defined by the file handles that make up the package.
82type PackageHandle interface {
83	// ID returns the ID of the package associated with the PackageHandle.
84	ID() string
85
86	// CompiledGoFiles returns the ParseGoHandles composing the package.
87	CompiledGoFiles() []ParseGoHandle
88
89	// Check returns the type-checked Package for the PackageHandle.
90	Check(ctx context.Context) (Package, error)
91
92	// Cached returns the Package for the PackageHandle if it has already been stored.
93	Cached() (Package, error)
94
95	// MissingDependencies reports any unresolved imports.
96	MissingDependencies() []string
97}
98
99// View represents a single workspace.
100// This is the level at which we maintain configuration like working directory
101// and build tags.
102type View interface {
103	// Session returns the session that created this view.
104	Session() Session
105
106	// Name returns the name this view was constructed with.
107	Name() string
108
109	// Folder returns the root folder for this view.
110	Folder() span.URI
111
112	// ModFiles returns the URIs of the go.mod files attached to the view associated with this snapshot.
113	ModFiles() (span.URI, span.URI)
114
115	// LookupBuiltin returns the go/ast.Object for the given name in the builtin package.
116	LookupBuiltin(ctx context.Context, name string) (*ast.Object, error)
117
118	// BackgroundContext returns a context used for all background processing
119	// on behalf of this view.
120	BackgroundContext() context.Context
121
122	// Shutdown closes this view, and detaches it from it's session.
123	Shutdown(ctx context.Context)
124
125	// Ignore returns true if this file should be ignored by this view.
126	Ignore(span.URI) bool
127
128	// RunProcessEnvFunc runs fn with the process env for this snapshot's view.
129	// Note: the process env contains cached module and filesystem state.
130	RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error
131
132	// Options returns a copy of the Options for this view.
133	Options() Options
134
135	// SetOptions sets the options of this view to new values.
136	// Calling this may cause the view to be invalidated and a replacement view
137	// added to the session. If so the new view will be returned, otherwise the
138	// original one will be.
139	SetOptions(context.Context, Options) (View, error)
140
141	// Snapshot returns the current snapshot for the view.
142	Snapshot() Snapshot
143
144	// Rebuild rebuilds the current view, replacing the original view in its session.
145	Rebuild(ctx context.Context) (Snapshot, error)
146
147	// InvalidBuildConfiguration returns true if there is some error in the
148	// user's workspace. In particular, if they are both outside of a module
149	// and their GOPATH.
150	ValidBuildConfiguration() bool
151}
152
153// Session represents a single connection from a client.
154// This is the level at which things like open files are maintained on behalf
155// of the client.
156// A session may have many active views at any given time.
157type Session interface {
158	// NewView creates a new View and returns it.
159	NewView(ctx context.Context, name string, folder span.URI, options Options) (View, Snapshot, error)
160
161	// Cache returns the cache that created this session.
162	Cache() Cache
163
164	// View returns a view with a matching name, if the session has one.
165	View(name string) View
166
167	// ViewOf returns a view corresponding to the given URI.
168	ViewOf(uri span.URI) (View, error)
169
170	// Views returns the set of active views built by this session.
171	Views() []View
172
173	// Shutdown the session and all views it has created.
174	Shutdown(ctx context.Context)
175
176	// A FileSystem prefers the contents from overlays, and falls back to the
177	// content from the underlying cache if no overlay is present.
178	FileSystem
179
180	// DidModifyFile reports a file modification to the session.
181	// It returns the resulting snapshots, a guaranteed one per view.
182	DidModifyFiles(ctx context.Context, changes []FileModification) ([]Snapshot, error)
183
184	// Options returns a copy of the SessionOptions for this session.
185	Options() Options
186
187	// SetOptions sets the options of this session to new values.
188	SetOptions(Options)
189}
190
191// FileModification represents a modification to a file.
192type FileModification struct {
193	URI    span.URI
194	Action FileAction
195
196	// OnDisk is true if a watched file is changed on disk.
197	// If true, Version will be -1 and Text will be nil.
198	OnDisk bool
199
200	// Version will be -1 and Text will be nil when they are not supplied,
201	// specifically on textDocument/didClose and for on-disk changes.
202	Version float64
203	Text    []byte
204
205	// LanguageID is only sent from the language client on textDocument/didOpen.
206	LanguageID string
207}
208
209type FileAction int
210
211const (
212	Open = FileAction(iota)
213	Change
214	Close
215	Save
216	Create
217	Delete
218	UnknownFileAction
219)
220
221// Cache abstracts the core logic of dealing with the environment from the
222// higher level logic that processes the information to produce results.
223// The cache provides access to files and their contents, so the source
224// package does not directly access the file system.
225// A single cache is intended to be process wide, and is the primary point of
226// sharing between all consumers.
227// A cache may have many active sessions at any given time.
228type Cache interface {
229	// A FileSystem that reads file contents from external storage.
230	FileSystem
231
232	// FileSet returns the shared fileset used by all files in the system.
233	FileSet() *token.FileSet
234
235	// ParseGoHandle returns a ParseGoHandle for the given file handle.
236	ParseGoHandle(fh FileHandle, mode ParseMode) ParseGoHandle
237}
238
239// FileSystem is the interface to something that provides file contents.
240type FileSystem interface {
241	// GetFile returns a handle for the specified file.
242	GetFile(uri span.URI) FileHandle
243}
244
245// ParseGoHandle represents a handle to the AST for a file.
246type ParseGoHandle interface {
247	// File returns a file handle for which to get the AST.
248	File() FileHandle
249
250	// Mode returns the parse mode of this handle.
251	Mode() ParseMode
252
253	// Parse returns the parsed AST for the file.
254	// If the file is not available, returns nil and an error.
255	Parse(ctx context.Context) (file *ast.File, src []byte, m *protocol.ColumnMapper, parseErr error, err error)
256
257	// Cached returns the AST for this handle, if it has already been stored.
258	Cached() (file *ast.File, src []byte, m *protocol.ColumnMapper, parseErr error, err error)
259}
260
261// ModHandle represents a handle to the modfile for a go.mod.
262type ModHandle interface {
263	// File returns a file handle for which to get the modfile.
264	File() FileHandle
265
266	// Parse returns the parsed modfile and a mapper for the go.mod file.
267	// If the file is not available, returns nil and an error.
268	Parse(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, error)
269
270	// Upgrades returns the parsed modfile, a mapper, and any dependency upgrades
271	// for the go.mod file. Note that this will only work if the go.mod is the view's go.mod.
272	// If the file is not available, returns nil and an error.
273	Upgrades(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, map[string]string, error)
274
275	// Why returns the parsed modfile, a mapper, and any explanations why a dependency should be
276	// in the go.mod file. Note that this will only work if the go.mod is the view's go.mod.
277	// If the file is not available, returns nil and an error.
278	Why(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, map[string]string, error)
279}
280
281// ModTidyHandle represents a handle to the modfile for the view.
282// Specifically for the purpose of getting diagnostics by running "go mod tidy".
283type ModTidyHandle interface {
284	// File returns a file handle for which to get the modfile.
285	File() FileHandle
286
287	// Tidy returns the parsed modfile, a mapper, and "go mod tidy" errors
288	// for the go.mod file. If the file is not available, returns nil and an error.
289	Tidy(ctx context.Context) (*modfile.File, *protocol.ColumnMapper, map[string]*modfile.Require, []Error, error)
290}
291
292// ParseMode controls the content of the AST produced when parsing a source file.
293type ParseMode int
294
295const (
296	// ParseHeader specifies that the main package declaration and imports are needed.
297	// This is the mode used when attempting to examine the package graph structure.
298	ParseHeader = ParseMode(iota)
299
300	// ParseExported specifies that the public symbols are needed, but things like
301	// private symbols and function bodies are not.
302	// This mode is used for things where a package is being consumed only as a
303	// dependency.
304	ParseExported
305
306	// ParseFull specifies the full AST is needed.
307	// This is used for files of direct interest where the entire contents must
308	// be considered.
309	ParseFull
310)
311
312// FileHandle represents a handle to a specific version of a single file from
313// a specific file system.
314type FileHandle interface {
315	// FileSystem returns the file system this handle was acquired from.
316	FileSystem() FileSystem
317
318	// Identity returns the FileIdentity for the file.
319	Identity() FileIdentity
320
321	// Read reads the contents of a file and returns it along with its hash value.
322	// If the file is not available, returns a nil slice and an error.
323	Read(ctx context.Context) ([]byte, string, error)
324}
325
326// FileIdentity uniquely identifies a file at a version from a FileSystem.
327type FileIdentity struct {
328	URI span.URI
329
330	// Version is the version of the file, as specified by the client.
331	Version float64
332
333	// Identifier represents a unique identifier for the file.
334	// It could be a file's modification time or its SHA1 hash if it is not on disk.
335	Identifier string
336
337	// Kind is the file's kind.
338	Kind FileKind
339}
340
341func (fileID FileIdentity) String() string {
342	// Version is not part of the FileIdentity string,
343	// as it can remain change even if the file does not.
344	return fmt.Sprintf("%s%s%s", fileID.URI, fileID.Identifier, fileID.Kind)
345}
346
347// FileKind describes the kind of the file in question.
348// It can be one of Go, mod, or sum.
349type FileKind int
350
351const (
352	Go = FileKind(iota)
353	Mod
354	Sum
355	UnknownKind
356)
357
358// Package represents a Go package that has been type-checked. It maintains
359// only the relevant fields of a *go/packages.Package.
360type Package interface {
361	ID() string
362	PkgPath() string
363	CompiledGoFiles() []ParseGoHandle
364	File(uri span.URI) (ParseGoHandle, error)
365	GetSyntax() []*ast.File
366	GetErrors() []*Error
367	GetTypes() *types.Package
368	GetTypesInfo() *types.Info
369	GetTypesSizes() types.Sizes
370	IsIllTyped() bool
371	ForTest() string
372	GetImport(pkgPath string) (Package, error)
373	Imports() []Package
374	Module() *packagesinternal.Module
375}
376
377type Error struct {
378	URI            span.URI
379	Range          protocol.Range
380	Kind           ErrorKind
381	Message        string
382	Category       string // only used by analysis errors so far
383	SuggestedFixes []SuggestedFix
384	Related        []RelatedInformation
385}
386
387type ErrorKind int
388
389const (
390	UnknownError = ErrorKind(iota)
391	ListError
392	ParseError
393	TypeError
394	Analysis
395)
396
397func (e *Error) Error() string {
398	return fmt.Sprintf("%s:%s: %s", e.URI, e.Range, e.Message)
399}
400