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