github.com/v2fly/tools@v0.100.0/internal/lsp/source/view.go (about)

     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  
     5  package source
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"fmt"
    11  	"go/ast"
    12  	"go/scanner"
    13  	"go/token"
    14  	"go/types"
    15  	"io"
    16  	"strings"
    17  
    18  	"github.com/v2fly/tools/go/analysis"
    19  	"github.com/v2fly/tools/internal/gocommand"
    20  	"github.com/v2fly/tools/internal/imports"
    21  	"github.com/v2fly/tools/internal/lsp/protocol"
    22  	"github.com/v2fly/tools/internal/span"
    23  	"golang.org/x/mod/modfile"
    24  	"golang.org/x/mod/module"
    25  	errors "golang.org/x/xerrors"
    26  )
    27  
    28  // Snapshot represents the current state for the given view.
    29  type Snapshot interface {
    30  	ID() uint64
    31  
    32  	// View returns the View associated with this snapshot.
    33  	View() View
    34  
    35  	// BackgroundContext returns a context used for all background processing
    36  	// on behalf of this snapshot.
    37  	BackgroundContext() context.Context
    38  
    39  	// Fileset returns the Fileset used to parse all the Go files in this snapshot.
    40  	FileSet() *token.FileSet
    41  
    42  	// ValidBuildConfiguration returns true if there is some error in the
    43  	// user's workspace. In particular, if they are both outside of a module
    44  	// and their GOPATH.
    45  	ValidBuildConfiguration() bool
    46  
    47  	// WriteEnv writes the view-specific environment to the io.Writer.
    48  	WriteEnv(ctx context.Context, w io.Writer) error
    49  
    50  	// FindFile returns the FileHandle for the given URI, if it is already
    51  	// in the given snapshot.
    52  	FindFile(uri span.URI) VersionedFileHandle
    53  
    54  	// GetVersionedFile returns the VersionedFileHandle for a given URI,
    55  	// initializing it if it is not already part of the snapshot.
    56  	GetVersionedFile(ctx context.Context, uri span.URI) (VersionedFileHandle, error)
    57  
    58  	// GetFile returns the FileHandle for a given URI, initializing it if it is
    59  	// not already part of the snapshot.
    60  	GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
    61  
    62  	// AwaitInitialized waits until the snapshot's view is initialized.
    63  	AwaitInitialized(ctx context.Context)
    64  
    65  	// IsOpen returns whether the editor currently has a file open.
    66  	IsOpen(uri span.URI) bool
    67  
    68  	// IgnoredFile reports if a file would be ignored by a `go list` of the whole
    69  	// workspace.
    70  	IgnoredFile(uri span.URI) bool
    71  
    72  	// ParseGo returns the parsed AST for the file.
    73  	// If the file is not available, returns nil and an error.
    74  	ParseGo(ctx context.Context, fh FileHandle, mode ParseMode) (*ParsedGoFile, error)
    75  
    76  	// PosToField is a cache of *ast.Fields by token.Pos. This allows us
    77  	// to quickly find corresponding *ast.Field node given a *types.Var.
    78  	// We must refer to the AST to render type aliases properly when
    79  	// formatting signatures and other types.
    80  	PosToField(ctx context.Context, pgf *ParsedGoFile) (map[token.Pos]*ast.Field, error)
    81  
    82  	// PosToDecl maps certain objects' positions to their surrounding
    83  	// ast.Decl. This mapping is used when building the documentation
    84  	// string for the objects.
    85  	PosToDecl(ctx context.Context, pgf *ParsedGoFile) (map[token.Pos]ast.Decl, error)
    86  
    87  	// DiagnosePackage returns basic diagnostics, including list, parse, and type errors
    88  	// for pkg, grouped by file.
    89  	DiagnosePackage(ctx context.Context, pkg Package) (map[span.URI][]*Diagnostic, error)
    90  
    91  	// Analyze runs the analyses for the given package at this snapshot.
    92  	Analyze(ctx context.Context, pkgID string, analyzers []*Analyzer) ([]*Diagnostic, error)
    93  
    94  	// RunGoCommandPiped runs the given `go` command, writing its output
    95  	// to stdout and stderr. Verb, Args, and WorkingDir must be specified.
    96  	RunGoCommandPiped(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation, stdout, stderr io.Writer) error
    97  
    98  	// RunGoCommandDirect runs the given `go` command. Verb, Args, and
    99  	// WorkingDir must be specified.
   100  	RunGoCommandDirect(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, error)
   101  
   102  	// RunGoCommands runs a series of `go` commands that updates the go.mod
   103  	// and go.sum file for wd, and returns their updated contents.
   104  	RunGoCommands(ctx context.Context, allowNetwork bool, wd string, run func(invoke func(...string) (*bytes.Buffer, error)) error) (bool, []byte, []byte, error)
   105  
   106  	// RunProcessEnvFunc runs fn with the process env for this snapshot's view.
   107  	// Note: the process env contains cached module and filesystem state.
   108  	RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error
   109  
   110  	// ModFiles are the go.mod files enclosed in the snapshot's view and known
   111  	// to the snapshot.
   112  	ModFiles() []span.URI
   113  
   114  	// ParseMod is used to parse go.mod files.
   115  	ParseMod(ctx context.Context, fh FileHandle) (*ParsedModule, error)
   116  
   117  	// ModWhy returns the results of `go mod why` for the module specified by
   118  	// the given go.mod file.
   119  	ModWhy(ctx context.Context, fh FileHandle) (map[string]string, error)
   120  
   121  	// ModTidy returns the results of `go mod tidy` for the module specified by
   122  	// the given go.mod file.
   123  	ModTidy(ctx context.Context, pm *ParsedModule) (*TidiedModule, error)
   124  
   125  	// GoModForFile returns the URI of the go.mod file for the given URI.
   126  	GoModForFile(uri span.URI) span.URI
   127  
   128  	// BuiltinFile returns information about the special builtin package.
   129  	BuiltinFile(ctx context.Context) (*ParsedGoFile, error)
   130  
   131  	// IsBuiltin reports whether uri is part of the builtin package.
   132  	IsBuiltin(ctx context.Context, uri span.URI) bool
   133  
   134  	// PackagesForFile returns the packages that this file belongs to, checked
   135  	// in mode.
   136  	PackagesForFile(ctx context.Context, uri span.URI, mode TypecheckMode) ([]Package, error)
   137  
   138  	// PackageForFile returns a single package that this file belongs to,
   139  	// checked in mode and filtered by the package policy.
   140  	PackageForFile(ctx context.Context, uri span.URI, mode TypecheckMode, selectPackage PackageFilter) (Package, error)
   141  
   142  	// GetActiveReverseDeps returns the active files belonging to the reverse
   143  	// dependencies of this file's package, checked in TypecheckWorkspace mode.
   144  	GetReverseDependencies(ctx context.Context, id string) ([]Package, error)
   145  
   146  	// CachedImportPaths returns all the imported packages loaded in this
   147  	// snapshot, indexed by their import path and checked in TypecheckWorkspace
   148  	// mode.
   149  	CachedImportPaths(ctx context.Context) (map[string]Package, error)
   150  
   151  	// KnownPackages returns all the packages loaded in this snapshot, checked
   152  	// in TypecheckWorkspace mode.
   153  	KnownPackages(ctx context.Context) ([]Package, error)
   154  
   155  	// WorkspacePackages returns the snapshot's top-level packages.
   156  	WorkspacePackages(ctx context.Context) ([]Package, error)
   157  
   158  	// GetCriticalError returns any critical errors in the workspace.
   159  	GetCriticalError(ctx context.Context) *CriticalError
   160  }
   161  
   162  // PackageFilter sets how a package is filtered out from a set of packages
   163  // containing a given file.
   164  type PackageFilter int
   165  
   166  const (
   167  	// NarrowestPackage picks the "narrowest" package for a given file.
   168  	// By "narrowest" package, we mean the package with the fewest number of
   169  	// files that includes the given file. This solves the problem of test
   170  	// variants, as the test will have more files than the non-test package.
   171  	NarrowestPackage PackageFilter = iota
   172  
   173  	// WidestPackage returns the Package containing the most files.
   174  	// This is useful for something like diagnostics, where we'd prefer to
   175  	// offer diagnostics for as many files as possible.
   176  	WidestPackage
   177  )
   178  
   179  // InvocationFlags represents the settings of a particular go command invocation.
   180  // It is a mode, plus a set of flag bits.
   181  type InvocationFlags int
   182  
   183  const (
   184  	// Normal is appropriate for commands that might be run by a user and don't
   185  	// deliberately modify go.mod files, e.g. `go test`.
   186  	Normal InvocationFlags = iota
   187  	// UpdateUserModFile is for commands that intend to update the user's real
   188  	// go.mod file, e.g. `go mod tidy` in response to a user's request to tidy.
   189  	UpdateUserModFile
   190  	// WriteTemporaryModFile is for commands that need information from a
   191  	// modified version of the user's go.mod file, e.g. `go mod tidy` used to
   192  	// generate diagnostics.
   193  	WriteTemporaryModFile
   194  	// LoadWorkspace is for packages.Load, and other operations that should
   195  	// consider the whole workspace at once.
   196  	LoadWorkspace
   197  
   198  	// AllowNetwork is a flag bit that indicates the invocation should be
   199  	// allowed to access the network.
   200  	AllowNetwork InvocationFlags = 1 << 10
   201  )
   202  
   203  func (m InvocationFlags) Mode() InvocationFlags {
   204  	return m & (AllowNetwork - 1)
   205  }
   206  
   207  func (m InvocationFlags) AllowNetwork() bool {
   208  	return m&AllowNetwork != 0
   209  }
   210  
   211  // View represents a single workspace.
   212  // This is the level at which we maintain configuration like working directory
   213  // and build tags.
   214  type View interface {
   215  	// Name returns the name this view was constructed with.
   216  	Name() string
   217  
   218  	// Folder returns the folder with which this view was created.
   219  	Folder() span.URI
   220  
   221  	// TempWorkspace returns the folder this view uses for its temporary
   222  	// workspace module.
   223  	TempWorkspace() span.URI
   224  
   225  	// Shutdown closes this view, and detaches it from its session.
   226  	Shutdown(ctx context.Context)
   227  
   228  	// Options returns a copy of the Options for this view.
   229  	Options() *Options
   230  
   231  	// SetOptions sets the options of this view to new values.
   232  	// Calling this may cause the view to be invalidated and a replacement view
   233  	// added to the session. If so the new view will be returned, otherwise the
   234  	// original one will be.
   235  	SetOptions(context.Context, *Options) (View, error)
   236  
   237  	// Snapshot returns the current snapshot for the view.
   238  	Snapshot(ctx context.Context) (Snapshot, func())
   239  
   240  	// Rebuild rebuilds the current view, replacing the original view in its session.
   241  	Rebuild(ctx context.Context) (Snapshot, func(), error)
   242  
   243  	// IsGoPrivatePath reports whether target is a private import path, as identified
   244  	// by the GOPRIVATE environment variable.
   245  	IsGoPrivatePath(path string) bool
   246  
   247  	// ModuleUpgrades returns known module upgrades.
   248  	ModuleUpgrades() map[string]string
   249  
   250  	// RegisterModuleUpgrades registers that upgrades exist for the given modules.
   251  	RegisterModuleUpgrades(upgrades map[string]string)
   252  }
   253  
   254  // A FileSource maps uris to FileHandles. This abstraction exists both for
   255  // testability, and so that algorithms can be run equally on session and
   256  // snapshot files.
   257  type FileSource interface {
   258  	// GetFile returns the FileHandle for a given URI.
   259  	GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
   260  }
   261  
   262  // A ParsedGoFile contains the results of parsing a Go file.
   263  type ParsedGoFile struct {
   264  	URI  span.URI
   265  	Mode ParseMode
   266  	File *ast.File
   267  	Tok  *token.File
   268  	// Source code used to build the AST. It may be different from the
   269  	// actual content of the file if we have fixed the AST.
   270  	Src      []byte
   271  	Mapper   *protocol.ColumnMapper
   272  	ParseErr scanner.ErrorList
   273  }
   274  
   275  // A ParsedModule contains the results of parsing a go.mod file.
   276  type ParsedModule struct {
   277  	URI         span.URI
   278  	File        *modfile.File
   279  	Mapper      *protocol.ColumnMapper
   280  	ParseErrors []*Diagnostic
   281  }
   282  
   283  // A TidiedModule contains the results of running `go mod tidy` on a module.
   284  type TidiedModule struct {
   285  	// Diagnostics representing changes made by `go mod tidy`.
   286  	Diagnostics []*Diagnostic
   287  	// The bytes of the go.mod file after it was tidied.
   288  	TidiedContent []byte
   289  }
   290  
   291  // Session represents a single connection from a client.
   292  // This is the level at which things like open files are maintained on behalf
   293  // of the client.
   294  // A session may have many active views at any given time.
   295  type Session interface {
   296  	// ID returns the unique identifier for this session on this server.
   297  	ID() string
   298  	// NewView creates a new View, returning it and its first snapshot. If a
   299  	// non-empty tempWorkspace directory is provided, the View will record a copy
   300  	// of its gopls workspace module in that directory, so that client tooling
   301  	// can execute in the same main module.
   302  	NewView(ctx context.Context, name string, folder, tempWorkspace span.URI, options *Options) (View, Snapshot, func(), error)
   303  
   304  	// Cache returns the cache that created this session, for debugging only.
   305  	Cache() interface{}
   306  
   307  	// View returns a view with a matching name, if the session has one.
   308  	View(name string) View
   309  
   310  	// ViewOf returns a view corresponding to the given URI.
   311  	ViewOf(uri span.URI) (View, error)
   312  
   313  	// Views returns the set of active views built by this session.
   314  	Views() []View
   315  
   316  	// Shutdown the session and all views it has created.
   317  	Shutdown(ctx context.Context)
   318  
   319  	// GetFile returns a handle for the specified file.
   320  	GetFile(ctx context.Context, uri span.URI) (FileHandle, error)
   321  
   322  	// DidModifyFile reports a file modification to the session. It returns
   323  	// the new snapshots after the modifications have been applied, paired with
   324  	// the affected file URIs for those snapshots.
   325  	DidModifyFiles(ctx context.Context, changes []FileModification) (map[Snapshot][]span.URI, []func(), error)
   326  
   327  	// ExpandModificationsToDirectories returns the set of changes with the
   328  	// directory changes removed and expanded to include all of the files in
   329  	// the directory.
   330  	ExpandModificationsToDirectories(ctx context.Context, changes []FileModification) []FileModification
   331  
   332  	// Overlays returns a slice of file overlays for the session.
   333  	Overlays() []Overlay
   334  
   335  	// Options returns a copy of the SessionOptions for this session.
   336  	Options() *Options
   337  
   338  	// SetOptions sets the options of this session to new values.
   339  	SetOptions(*Options)
   340  
   341  	// FileWatchingGlobPatterns returns glob patterns to watch every directory
   342  	// known by the view. For views within a module, this is the module root,
   343  	// any directory in the module root, and any replace targets.
   344  	FileWatchingGlobPatterns(ctx context.Context) map[string]struct{}
   345  }
   346  
   347  // Overlay is the type for a file held in memory on a session.
   348  type Overlay interface {
   349  	VersionedFileHandle
   350  }
   351  
   352  // FileModification represents a modification to a file.
   353  type FileModification struct {
   354  	URI    span.URI
   355  	Action FileAction
   356  
   357  	// OnDisk is true if a watched file is changed on disk.
   358  	// If true, Version will be -1 and Text will be nil.
   359  	OnDisk bool
   360  
   361  	// Version will be -1 and Text will be nil when they are not supplied,
   362  	// specifically on textDocument/didClose and for on-disk changes.
   363  	Version int32
   364  	Text    []byte
   365  
   366  	// LanguageID is only sent from the language client on textDocument/didOpen.
   367  	LanguageID string
   368  }
   369  
   370  type FileAction int
   371  
   372  const (
   373  	UnknownFileAction = FileAction(iota)
   374  	Open
   375  	Change
   376  	Close
   377  	Save
   378  	Create
   379  	Delete
   380  	InvalidateMetadata
   381  )
   382  
   383  func (a FileAction) String() string {
   384  	switch a {
   385  	case Open:
   386  		return "Open"
   387  	case Change:
   388  		return "Change"
   389  	case Close:
   390  		return "Close"
   391  	case Save:
   392  		return "Save"
   393  	case Create:
   394  		return "Create"
   395  	case Delete:
   396  		return "Delete"
   397  	case InvalidateMetadata:
   398  		return "InvalidateMetadata"
   399  	default:
   400  		return "Unknown"
   401  	}
   402  }
   403  
   404  var ErrTmpModfileUnsupported = errors.New("-modfile is unsupported for this Go version")
   405  var ErrNoModOnDisk = errors.New("go.mod file is not on disk")
   406  
   407  func IsNonFatalGoModError(err error) bool {
   408  	return err == ErrTmpModfileUnsupported || err == ErrNoModOnDisk
   409  }
   410  
   411  // ParseMode controls the content of the AST produced when parsing a source file.
   412  type ParseMode int
   413  
   414  const (
   415  	// ParseHeader specifies that the main package declaration and imports are needed.
   416  	// This is the mode used when attempting to examine the package graph structure.
   417  	ParseHeader ParseMode = iota
   418  
   419  	// ParseExported specifies that the public symbols are needed, but things like
   420  	// private symbols and function bodies are not.
   421  	// This mode is used for things where a package is being consumed only as a
   422  	// dependency.
   423  	ParseExported
   424  
   425  	// ParseFull specifies the full AST is needed.
   426  	// This is used for files of direct interest where the entire contents must
   427  	// be considered.
   428  	ParseFull
   429  )
   430  
   431  // TypecheckMode controls what kind of parsing should be done (see ParseMode)
   432  // while type checking a package.
   433  type TypecheckMode int
   434  
   435  const (
   436  	// Invalid default value.
   437  	TypecheckUnknown TypecheckMode = iota
   438  	// TypecheckFull means to use ParseFull.
   439  	TypecheckFull
   440  	// TypecheckWorkspace means to use ParseFull for workspace packages, and
   441  	// ParseExported for others.
   442  	TypecheckWorkspace
   443  	// TypecheckAll means ParseFull for workspace packages, and both Full and
   444  	// Exported for others. Only valid for some functions.
   445  	TypecheckAll
   446  )
   447  
   448  type VersionedFileHandle interface {
   449  	FileHandle
   450  	Version() int32
   451  	Session() string
   452  
   453  	// LSPIdentity returns the version identity of a file.
   454  	VersionedFileIdentity() VersionedFileIdentity
   455  }
   456  
   457  type VersionedFileIdentity struct {
   458  	URI span.URI
   459  
   460  	// SessionID is the ID of the LSP session.
   461  	SessionID string
   462  
   463  	// Version is the version of the file, as specified by the client. It should
   464  	// only be set in combination with SessionID.
   465  	Version int32
   466  }
   467  
   468  // FileHandle represents a handle to a specific version of a single file.
   469  type FileHandle interface {
   470  	URI() span.URI
   471  	Kind() FileKind
   472  
   473  	// FileIdentity returns a FileIdentity for the file, even if there was an
   474  	// error reading it.
   475  	FileIdentity() FileIdentity
   476  	// Read reads the contents of a file.
   477  	// If the file is not available, returns a nil slice and an error.
   478  	Read() ([]byte, error)
   479  	// Saved reports whether the file has the same content on disk.
   480  	Saved() bool
   481  }
   482  
   483  // FileIdentity uniquely identifies a file at a version from a FileSystem.
   484  type FileIdentity struct {
   485  	URI span.URI
   486  
   487  	// Identifier represents a unique identifier for the file's content.
   488  	Hash string
   489  
   490  	// Kind is the file's kind.
   491  	Kind FileKind
   492  }
   493  
   494  func (id FileIdentity) String() string {
   495  	return fmt.Sprintf("%s%s%s", id.URI, id.Hash, id.Kind)
   496  }
   497  
   498  // FileKind describes the kind of the file in question.
   499  // It can be one of Go, mod, or sum.
   500  type FileKind int
   501  
   502  const (
   503  	// UnknownKind is a file type we don't know about.
   504  	UnknownKind = FileKind(iota)
   505  
   506  	// Go is a normal go source file.
   507  	Go
   508  	// Mod is a go.mod file.
   509  	Mod
   510  	// Sum is a go.sum file.
   511  	Sum
   512  )
   513  
   514  // Analyzer represents a go/analysis analyzer with some boolean properties
   515  // that let the user know how to use the analyzer.
   516  type Analyzer struct {
   517  	Analyzer *analysis.Analyzer
   518  
   519  	// Enabled reports whether the analyzer is enabled. This value can be
   520  	// configured per-analysis in user settings. For staticcheck analyzers,
   521  	// the value of the Staticcheck setting overrides this field.
   522  	Enabled bool
   523  
   524  	// Fix is the name of the suggested fix name used to invoke the suggested
   525  	// fixes for the analyzer. It is non-empty if we expect this analyzer to
   526  	// provide its fix separately from its diagnostics. That is, we should apply
   527  	// the analyzer's suggested fixes through a Command, not a TextEdit.
   528  	Fix string
   529  
   530  	// ActionKind is the kind of code action this analyzer produces. If
   531  	// unspecified the type defaults to quickfix.
   532  	ActionKind []protocol.CodeActionKind
   533  }
   534  
   535  func (a Analyzer) IsEnabled(view View) bool {
   536  	// Staticcheck analyzers can only be enabled when staticcheck is on.
   537  	if _, ok := view.Options().StaticcheckAnalyzers[a.Analyzer.Name]; ok {
   538  		if !view.Options().Staticcheck {
   539  			return false
   540  		}
   541  	}
   542  	if enabled, ok := view.Options().Analyses[a.Analyzer.Name]; ok {
   543  		return enabled
   544  	}
   545  	return a.Enabled
   546  }
   547  
   548  // Package represents a Go package that has been type-checked. It maintains
   549  // only the relevant fields of a *go/packages.Package.
   550  type Package interface {
   551  	ID() string
   552  	Name() string
   553  	PkgPath() string
   554  	CompiledGoFiles() []*ParsedGoFile
   555  	File(uri span.URI) (*ParsedGoFile, error)
   556  	GetSyntax() []*ast.File
   557  	GetTypes() *types.Package
   558  	GetTypesInfo() *types.Info
   559  	GetTypesSizes() types.Sizes
   560  	IsIllTyped() bool
   561  	ForTest() string
   562  	GetImport(pkgPath string) (Package, error)
   563  	MissingDependencies() []string
   564  	Imports() []Package
   565  	Version() *module.Version
   566  	HasListOrParseErrors() bool
   567  	HasTypeErrors() bool
   568  }
   569  
   570  type CriticalError struct {
   571  	// MainError is the primary error. Must be non-nil.
   572  	MainError error
   573  	// DiagList contains any supplemental (structured) diagnostics.
   574  	DiagList []*Diagnostic
   575  }
   576  
   577  // An Diagnostic corresponds to an LSP Diagnostic.
   578  // https://microsoft.github.io/language-server-protocol/specification#diagnostic
   579  type Diagnostic struct {
   580  	URI      span.URI
   581  	Range    protocol.Range
   582  	Severity protocol.DiagnosticSeverity
   583  	Code     string
   584  	CodeHref string
   585  
   586  	// Source is a human-readable description of the source of the error.
   587  	// Diagnostics generated by an analysis.Analyzer set it to Analyzer.Name.
   588  	Source DiagnosticSource
   589  
   590  	Message string
   591  
   592  	Tags    []protocol.DiagnosticTag
   593  	Related []RelatedInformation
   594  
   595  	// Fields below are used internally to generate quick fixes. They aren't
   596  	// part of the LSP spec and don't leave the server.
   597  	SuggestedFixes []SuggestedFix
   598  	Analyzer       *Analyzer
   599  }
   600  
   601  type DiagnosticSource string
   602  
   603  const (
   604  	UnknownError             DiagnosticSource = "<Unknown source>"
   605  	ListError                DiagnosticSource = "go list"
   606  	ParseError               DiagnosticSource = "syntax"
   607  	TypeError                DiagnosticSource = "compiler"
   608  	ModTidyError             DiagnosticSource = "go mod tidy"
   609  	OptimizationDetailsError DiagnosticSource = "optimizer details"
   610  	UpgradeNotification      DiagnosticSource = "upgrade available"
   611  )
   612  
   613  func AnalyzerErrorKind(name string) DiagnosticSource {
   614  	return DiagnosticSource(name)
   615  }
   616  
   617  var (
   618  	PackagesLoadError = errors.New("packages.Load error")
   619  )
   620  
   621  // WorkspaceModuleVersion is the nonexistent pseudoversion suffix used in the
   622  // construction of the workspace module. It is exported so that we can make
   623  // sure not to show this version to end users in error messages, to avoid
   624  // confusion.
   625  // The major version is not included, as that depends on the module path.
   626  //
   627  // If workspace module A is dependent on workspace module B, we need our
   628  // nonexistant version to be greater than the version A mentions.
   629  // Otherwise, the go command will try to update to that version. Use a very
   630  // high minor version to make that more likely.
   631  const workspaceModuleVersion = ".9999999.0-goplsworkspace"
   632  
   633  func IsWorkspaceModuleVersion(version string) bool {
   634  	return strings.HasSuffix(version, workspaceModuleVersion)
   635  }
   636  
   637  func WorkspaceModuleVersion(majorVersion string) string {
   638  	// Use the highest compatible major version to avoid unwanted upgrades.
   639  	// See the comment on workspaceModuleVersion.
   640  	if majorVersion == "v0" {
   641  		majorVersion = "v1"
   642  	}
   643  	return majorVersion + workspaceModuleVersion
   644  }