1// Copyright 2019 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 "path/filepath" 11 "regexp" 12 "strings" 13 "sync" 14 "time" 15 16 "golang.org/x/tools/go/analysis" 17 "golang.org/x/tools/go/analysis/passes/asmdecl" 18 "golang.org/x/tools/go/analysis/passes/assign" 19 "golang.org/x/tools/go/analysis/passes/atomic" 20 "golang.org/x/tools/go/analysis/passes/atomicalign" 21 "golang.org/x/tools/go/analysis/passes/bools" 22 "golang.org/x/tools/go/analysis/passes/buildtag" 23 "golang.org/x/tools/go/analysis/passes/cgocall" 24 "golang.org/x/tools/go/analysis/passes/composite" 25 "golang.org/x/tools/go/analysis/passes/copylock" 26 "golang.org/x/tools/go/analysis/passes/deepequalerrors" 27 "golang.org/x/tools/go/analysis/passes/errorsas" 28 "golang.org/x/tools/go/analysis/passes/fieldalignment" 29 "golang.org/x/tools/go/analysis/passes/httpresponse" 30 "golang.org/x/tools/go/analysis/passes/ifaceassert" 31 "golang.org/x/tools/go/analysis/passes/loopclosure" 32 "golang.org/x/tools/go/analysis/passes/lostcancel" 33 "golang.org/x/tools/go/analysis/passes/nilfunc" 34 "golang.org/x/tools/go/analysis/passes/nilness" 35 "golang.org/x/tools/go/analysis/passes/printf" 36 "golang.org/x/tools/go/analysis/passes/shadow" 37 "golang.org/x/tools/go/analysis/passes/shift" 38 "golang.org/x/tools/go/analysis/passes/sortslice" 39 "golang.org/x/tools/go/analysis/passes/stdmethods" 40 "golang.org/x/tools/go/analysis/passes/stringintconv" 41 "golang.org/x/tools/go/analysis/passes/structtag" 42 "golang.org/x/tools/go/analysis/passes/testinggoroutine" 43 "golang.org/x/tools/go/analysis/passes/tests" 44 "golang.org/x/tools/go/analysis/passes/unmarshal" 45 "golang.org/x/tools/go/analysis/passes/unreachable" 46 "golang.org/x/tools/go/analysis/passes/unsafeptr" 47 "golang.org/x/tools/go/analysis/passes/unusedresult" 48 "golang.org/x/tools/go/analysis/passes/unusedwrite" 49 "golang.org/x/tools/internal/lsp/analysis/fillreturns" 50 "golang.org/x/tools/internal/lsp/analysis/fillstruct" 51 "golang.org/x/tools/internal/lsp/analysis/nonewvars" 52 "golang.org/x/tools/internal/lsp/analysis/noresultvalues" 53 "golang.org/x/tools/internal/lsp/analysis/simplifycompositelit" 54 "golang.org/x/tools/internal/lsp/analysis/simplifyrange" 55 "golang.org/x/tools/internal/lsp/analysis/simplifyslice" 56 "golang.org/x/tools/internal/lsp/analysis/undeclaredname" 57 "golang.org/x/tools/internal/lsp/analysis/unusedparams" 58 "golang.org/x/tools/internal/lsp/command" 59 "golang.org/x/tools/internal/lsp/diff" 60 "golang.org/x/tools/internal/lsp/diff/myers" 61 "golang.org/x/tools/internal/lsp/protocol" 62 errors "golang.org/x/xerrors" 63) 64 65var ( 66 optionsOnce sync.Once 67 defaultOptions *Options 68) 69 70// DefaultOptions is the options that are used for Gopls execution independent 71// of any externally provided configuration (LSP initialization, command 72// invokation, etc.). 73func DefaultOptions() *Options { 74 optionsOnce.Do(func() { 75 var commands []string 76 for _, c := range command.Commands { 77 commands = append(commands, c.ID()) 78 } 79 defaultOptions = &Options{ 80 ClientOptions: ClientOptions{ 81 InsertTextFormat: protocol.PlainTextTextFormat, 82 PreferredContentFormat: protocol.Markdown, 83 ConfigurationSupported: true, 84 DynamicConfigurationSupported: true, 85 DynamicWatchedFilesSupported: true, 86 LineFoldingOnly: false, 87 HierarchicalDocumentSymbolSupport: true, 88 }, 89 ServerOptions: ServerOptions{ 90 SupportedCodeActions: map[FileKind]map[protocol.CodeActionKind]bool{ 91 Go: { 92 protocol.SourceFixAll: true, 93 protocol.SourceOrganizeImports: true, 94 protocol.QuickFix: true, 95 protocol.RefactorRewrite: true, 96 protocol.RefactorExtract: true, 97 }, 98 Mod: { 99 protocol.SourceOrganizeImports: true, 100 protocol.QuickFix: true, 101 }, 102 Sum: {}, 103 Tmpl: {}, 104 }, 105 SupportedCommands: commands, 106 }, 107 UserOptions: UserOptions{ 108 BuildOptions: BuildOptions{ 109 ExpandWorkspaceToModule: true, 110 ExperimentalPackageCacheKey: true, 111 MemoryMode: ModeNormal, 112 }, 113 UIOptions: UIOptions{ 114 DiagnosticOptions: DiagnosticOptions{ 115 DiagnosticsDelay: 250 * time.Millisecond, 116 Annotations: map[Annotation]bool{ 117 Bounds: true, 118 Escape: true, 119 Inline: true, 120 Nil: true, 121 }, 122 }, 123 DocumentationOptions: DocumentationOptions{ 124 HoverKind: FullDocumentation, 125 LinkTarget: "pkg.go.dev", 126 LinksInHover: true, 127 }, 128 NavigationOptions: NavigationOptions{ 129 ImportShortcut: Both, 130 SymbolMatcher: SymbolFuzzy, 131 SymbolStyle: DynamicSymbols, 132 }, 133 CompletionOptions: CompletionOptions{ 134 Matcher: Fuzzy, 135 CompletionBudget: 100 * time.Millisecond, 136 ExperimentalPostfixCompletions: true, 137 }, 138 Codelenses: map[string]bool{ 139 string(command.Generate): true, 140 string(command.RegenerateCgo): true, 141 string(command.Tidy): true, 142 string(command.GCDetails): false, 143 string(command.UpgradeDependency): true, 144 string(command.Vendor): true, 145 }, 146 }, 147 }, 148 InternalOptions: InternalOptions{ 149 LiteralCompletions: true, 150 TempModfile: true, 151 CompleteUnimported: true, 152 CompletionDocumentation: true, 153 DeepCompletion: true, 154 }, 155 Hooks: Hooks{ 156 ComputeEdits: myers.ComputeEdits, 157 URLRegexp: urlRegexp(), 158 DefaultAnalyzers: defaultAnalyzers(), 159 TypeErrorAnalyzers: typeErrorAnalyzers(), 160 ConvenienceAnalyzers: convenienceAnalyzers(), 161 StaticcheckAnalyzers: map[string]*Analyzer{}, 162 GoDiff: true, 163 }, 164 } 165 }) 166 return defaultOptions 167} 168 169// Options holds various configuration that affects Gopls execution, organized 170// by the nature or origin of the settings. 171type Options struct { 172 ClientOptions 173 ServerOptions 174 UserOptions 175 InternalOptions 176 Hooks 177} 178 179// ClientOptions holds LSP-specific configuration that is provided by the 180// client. 181type ClientOptions struct { 182 InsertTextFormat protocol.InsertTextFormat 183 ConfigurationSupported bool 184 DynamicConfigurationSupported bool 185 DynamicWatchedFilesSupported bool 186 PreferredContentFormat protocol.MarkupKind 187 LineFoldingOnly bool 188 HierarchicalDocumentSymbolSupport bool 189 SemanticTypes []string 190 SemanticMods []string 191 RelatedInformationSupported bool 192 CompletionTags bool 193 CompletionDeprecated bool 194} 195 196// ServerOptions holds LSP-specific configuration that is provided by the 197// server. 198type ServerOptions struct { 199 SupportedCodeActions map[FileKind]map[protocol.CodeActionKind]bool 200 SupportedCommands []string 201} 202 203type BuildOptions struct { 204 // BuildFlags is the set of flags passed on to the build system when invoked. 205 // It is applied to queries like `go list`, which is used when discovering files. 206 // The most common use is to set `-tags`. 207 BuildFlags []string 208 209 // Env adds environment variables to external commands run by `gopls`, most notably `go list`. 210 Env map[string]string 211 212 // DirectoryFilters can be used to exclude unwanted directories from the 213 // workspace. By default, all directories are included. Filters are an 214 // operator, `+` to include and `-` to exclude, followed by a path prefix 215 // relative to the workspace folder. They are evaluated in order, and 216 // the last filter that applies to a path controls whether it is included. 217 // The path prefix can be empty, so an initial `-` excludes everything. 218 // 219 // Examples: 220 // 221 // Exclude node_modules: `-node_modules` 222 // 223 // Include only project_a: `-` (exclude everything), `+project_a` 224 // 225 // Include only project_a, but not node_modules inside it: `-`, `+project_a`, `-project_a/node_modules` 226 DirectoryFilters []string 227 228 // MemoryMode controls the tradeoff `gopls` makes between memory usage and 229 // correctness. 230 // 231 // Values other than `Normal` are untested and may break in surprising ways. 232 MemoryMode MemoryMode `status:"experimental"` 233 234 // ExpandWorkspaceToModule instructs `gopls` to adjust the scope of the 235 // workspace to find the best available module root. `gopls` first looks for 236 // a go.mod file in any parent directory of the workspace folder, expanding 237 // the scope to that directory if it exists. If no viable parent directory is 238 // found, gopls will check if there is exactly one child directory containing 239 // a go.mod file, narrowing the scope to that directory if it exists. 240 ExpandWorkspaceToModule bool `status:"experimental"` 241 242 // ExperimentalWorkspaceModule opts a user into the experimental support 243 // for multi-module workspaces. 244 ExperimentalWorkspaceModule bool `status:"experimental"` 245 246 // ExperimentalTemplateSupport opts into the experimental support 247 // for template files. 248 ExperimentalTemplateSupport bool `status:"experimental"` 249 250 // ExperimentalPackageCacheKey controls whether to use a coarser cache key 251 // for package type information to increase cache hits. This setting removes 252 // the user's environment, build flags, and working directory from the cache 253 // key, which should be a safe change as all relevant inputs into the type 254 // checking pass are already hashed into the key. This is temporarily guarded 255 // by an experiment because caching behavior is subtle and difficult to 256 // comprehensively test. 257 ExperimentalPackageCacheKey bool `status:"experimental"` 258 259 // AllowModfileModifications disables -mod=readonly, allowing imports from 260 // out-of-scope modules. This option will eventually be removed. 261 AllowModfileModifications bool `status:"experimental"` 262 263 // AllowImplicitNetworkAccess disables GOPROXY=off, allowing implicit module 264 // downloads rather than requiring user action. This option will eventually 265 // be removed. 266 AllowImplicitNetworkAccess bool `status:"experimental"` 267 268 // ExperimentalUseInvalidMetadata enables gopls to fall back on outdated 269 // package metadata to provide editor features if the go command fails to 270 // load packages for some reason (like an invalid go.mod file). This will 271 // eventually be the default behavior, and this setting will be removed. 272 ExperimentalUseInvalidMetadata bool `status:"experimental"` 273} 274 275type UIOptions struct { 276 DocumentationOptions 277 CompletionOptions 278 NavigationOptions 279 DiagnosticOptions 280 281 // Codelenses overrides the enabled/disabled state of code lenses. See the 282 // "Code Lenses" section of the 283 // [Settings page](https://github.com/golang/tools/blob/master/gopls/doc/settings.md) 284 // for the list of supported lenses. 285 // 286 // Example Usage: 287 // 288 // ```json5 289 // "gopls": { 290 // ... 291 // "codelens": { 292 // "generate": false, // Don't show the `go generate` lens. 293 // "gc_details": true // Show a code lens toggling the display of gc's choices. 294 // } 295 // ... 296 // } 297 // ``` 298 Codelenses map[string]bool 299 300 // SemanticTokens controls whether the LSP server will send 301 // semantic tokens to the client. 302 SemanticTokens bool `status:"experimental"` 303} 304 305type CompletionOptions struct { 306 // Placeholders enables placeholders for function parameters or struct 307 // fields in completion responses. 308 UsePlaceholders bool 309 310 // CompletionBudget is the soft latency goal for completion requests. Most 311 // requests finish in a couple milliseconds, but in some cases deep 312 // completions can take much longer. As we use up our budget we 313 // dynamically reduce the search scope to ensure we return timely 314 // results. Zero means unlimited. 315 CompletionBudget time.Duration `status:"debug"` 316 317 // Matcher sets the algorithm that is used when calculating completion 318 // candidates. 319 Matcher Matcher `status:"advanced"` 320 321 // ExperimentalPostfixCompletions enables artifical method snippets 322 // such as "someSlice.sort!". 323 ExperimentalPostfixCompletions bool `status:"experimental"` 324} 325 326type DocumentationOptions struct { 327 // HoverKind controls the information that appears in the hover text. 328 // SingleLine and Structured are intended for use only by authors of editor plugins. 329 HoverKind HoverKind 330 331 // LinkTarget controls where documentation links go. 332 // It might be one of: 333 // 334 // * `"godoc.org"` 335 // * `"pkg.go.dev"` 336 // 337 // If company chooses to use its own `godoc.org`, its address can be used as well. 338 LinkTarget string 339 340 // LinksInHover toggles the presence of links to documentation in hover. 341 LinksInHover bool 342} 343 344type FormattingOptions struct { 345 // Local is the equivalent of the `goimports -local` flag, which puts 346 // imports beginning with this string after third-party packages. It should 347 // be the prefix of the import path whose imports should be grouped 348 // separately. 349 Local string 350 351 // Gofumpt indicates if we should run gofumpt formatting. 352 Gofumpt bool 353} 354 355type DiagnosticOptions struct { 356 // Analyses specify analyses that the user would like to enable or disable. 357 // A map of the names of analysis passes that should be enabled/disabled. 358 // A full list of analyzers that gopls uses can be found 359 // [here](https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md). 360 // 361 // Example Usage: 362 // 363 // ```json5 364 // ... 365 // "analyses": { 366 // "unreachable": false, // Disable the unreachable analyzer. 367 // "unusedparams": true // Enable the unusedparams analyzer. 368 // } 369 // ... 370 // ``` 371 Analyses map[string]bool 372 373 // Staticcheck enables additional analyses from staticcheck.io. 374 Staticcheck bool `status:"experimental"` 375 376 // Annotations specifies the various kinds of optimization diagnostics 377 // that should be reported by the gc_details command. 378 Annotations map[Annotation]bool `status:"experimental"` 379 380 // DiagnosticsDelay controls the amount of time that gopls waits 381 // after the most recent file modification before computing deep diagnostics. 382 // Simple diagnostics (parsing and type-checking) are always run immediately 383 // on recently modified packages. 384 // 385 // This option must be set to a valid duration string, for example `"250ms"`. 386 DiagnosticsDelay time.Duration `status:"advanced"` 387 388 // ExperimentalWatchedFileDelay controls the amount of time that gopls waits 389 // for additional workspace/didChangeWatchedFiles notifications to arrive, 390 // before processing all such notifications in a single batch. This is 391 // intended for use by LSP clients that don't support their own batching of 392 // file system notifications. 393 // 394 // This option must be set to a valid duration string, for example `"100ms"`. 395 ExperimentalWatchedFileDelay time.Duration `status:"experimental"` 396} 397 398type NavigationOptions struct { 399 // ImportShortcut specifies whether import statements should link to 400 // documentation or go to definitions. 401 ImportShortcut ImportShortcut 402 403 // SymbolMatcher sets the algorithm that is used when finding workspace symbols. 404 SymbolMatcher SymbolMatcher `status:"advanced"` 405 406 // SymbolStyle controls how symbols are qualified in symbol responses. 407 // 408 // Example Usage: 409 // 410 // ```json5 411 // "gopls": { 412 // ... 413 // "symbolStyle": "dynamic", 414 // ... 415 // } 416 // ``` 417 SymbolStyle SymbolStyle `status:"advanced"` 418} 419 420// UserOptions holds custom Gopls configuration (not part of the LSP) that is 421// modified by the client. 422type UserOptions struct { 423 BuildOptions 424 UIOptions 425 FormattingOptions 426 427 // VerboseOutput enables additional debug logging. 428 VerboseOutput bool `status:"debug"` 429} 430 431// EnvSlice returns Env as a slice of k=v strings. 432func (u *UserOptions) EnvSlice() []string { 433 var result []string 434 for k, v := range u.Env { 435 result = append(result, fmt.Sprintf("%v=%v", k, v)) 436 } 437 return result 438} 439 440// SetEnvSlice sets Env from a slice of k=v strings. 441func (u *UserOptions) SetEnvSlice(env []string) { 442 u.Env = map[string]string{} 443 for _, kv := range env { 444 split := strings.SplitN(kv, "=", 2) 445 if len(split) != 2 { 446 continue 447 } 448 u.Env[split[0]] = split[1] 449 } 450} 451 452// Hooks contains configuration that is provided to the Gopls command by the 453// main package. 454type Hooks struct { 455 LicensesText string 456 GoDiff bool 457 ComputeEdits diff.ComputeEdits 458 URLRegexp *regexp.Regexp 459 GofumptFormat func(ctx context.Context, src []byte) ([]byte, error) 460 DefaultAnalyzers map[string]*Analyzer 461 TypeErrorAnalyzers map[string]*Analyzer 462 ConvenienceAnalyzers map[string]*Analyzer 463 StaticcheckAnalyzers map[string]*Analyzer 464} 465 466// InternalOptions contains settings that are not intended for use by the 467// average user. These may be settings used by tests or outdated settings that 468// will soon be deprecated. Some of these settings may not even be configurable 469// by the user. 470type InternalOptions struct { 471 // LiteralCompletions controls whether literal candidates such as 472 // "&someStruct{}" are offered. Tests disable this flag to simplify 473 // their expected values. 474 LiteralCompletions bool 475 476 // VerboseWorkDoneProgress controls whether the LSP server should send 477 // progress reports for all work done outside the scope of an RPC. 478 // Used by the regression tests. 479 VerboseWorkDoneProgress bool 480 481 // The following options were previously available to users, but they 482 // really shouldn't be configured by anyone other than "power users". 483 484 // CompletionDocumentation enables documentation with completion results. 485 CompletionDocumentation bool 486 487 // CompleteUnimported enables completion for packages that you do not 488 // currently import. 489 CompleteUnimported bool 490 491 // DeepCompletion enables the ability to return completions from deep 492 // inside relevant entities, rather than just the locally accessible ones. 493 // 494 // Consider this example: 495 // 496 // ```go 497 // package main 498 // 499 // import "fmt" 500 // 501 // type wrapString struct { 502 // str string 503 // } 504 // 505 // func main() { 506 // x := wrapString{"hello world"} 507 // fmt.Printf(<>) 508 // } 509 // ``` 510 // 511 // At the location of the `<>` in this program, deep completion would suggest the result `x.str`. 512 DeepCompletion bool 513 514 // TempModfile controls the use of the -modfile flag in Go 1.14. 515 TempModfile bool 516} 517 518type ImportShortcut string 519 520const ( 521 Both ImportShortcut = "Both" 522 Link ImportShortcut = "Link" 523 Definition ImportShortcut = "Definition" 524) 525 526func (s ImportShortcut) ShowLinks() bool { 527 return s == Both || s == Link 528} 529 530func (s ImportShortcut) ShowDefinition() bool { 531 return s == Both || s == Definition 532} 533 534type Matcher string 535 536const ( 537 Fuzzy Matcher = "Fuzzy" 538 CaseInsensitive Matcher = "CaseInsensitive" 539 CaseSensitive Matcher = "CaseSensitive" 540) 541 542type SymbolMatcher string 543 544const ( 545 SymbolFuzzy SymbolMatcher = "Fuzzy" 546 SymbolCaseInsensitive SymbolMatcher = "CaseInsensitive" 547 SymbolCaseSensitive SymbolMatcher = "CaseSensitive" 548) 549 550type SymbolStyle string 551 552const ( 553 // PackageQualifiedSymbols is package qualified symbols i.e. 554 // "pkg.Foo.Field". 555 PackageQualifiedSymbols SymbolStyle = "Package" 556 // FullyQualifiedSymbols is fully qualified symbols, i.e. 557 // "path/to/pkg.Foo.Field". 558 FullyQualifiedSymbols SymbolStyle = "Full" 559 // DynamicSymbols uses whichever qualifier results in the highest scoring 560 // match for the given symbol query. Here a "qualifier" is any "/" or "." 561 // delimited suffix of the fully qualified symbol. i.e. "to/pkg.Foo.Field" or 562 // just "Foo.Field". 563 DynamicSymbols SymbolStyle = "Dynamic" 564) 565 566type HoverKind string 567 568const ( 569 SingleLine HoverKind = "SingleLine" 570 NoDocumentation HoverKind = "NoDocumentation" 571 SynopsisDocumentation HoverKind = "SynopsisDocumentation" 572 FullDocumentation HoverKind = "FullDocumentation" 573 574 // Structured is an experimental setting that returns a structured hover format. 575 // This format separates the signature from the documentation, so that the client 576 // can do more manipulation of these fields. 577 // 578 // This should only be used by clients that support this behavior. 579 Structured HoverKind = "Structured" 580) 581 582type MemoryMode string 583 584const ( 585 ModeNormal MemoryMode = "Normal" 586 // In DegradeClosed mode, `gopls` will collect less information about 587 // packages without open files. As a result, features like Find 588 // References and Rename will miss results in such packages. 589 ModeDegradeClosed MemoryMode = "DegradeClosed" 590) 591 592type OptionResults []OptionResult 593 594type OptionResult struct { 595 Name string 596 Value interface{} 597 Error error 598 599 State OptionState 600 Replacement string 601} 602 603type OptionState int 604 605const ( 606 OptionHandled = OptionState(iota) 607 OptionDeprecated 608 OptionUnexpected 609) 610 611type LinkTarget string 612 613func SetOptions(options *Options, opts interface{}) OptionResults { 614 var results OptionResults 615 switch opts := opts.(type) { 616 case nil: 617 case map[string]interface{}: 618 // If the user's settings contains "allExperiments", set that first, 619 // and then let them override individual settings independently. 620 var enableExperiments bool 621 for name, value := range opts { 622 if b, ok := value.(bool); name == "allExperiments" && ok && b { 623 enableExperiments = true 624 options.EnableAllExperiments() 625 } 626 } 627 seen := map[string]struct{}{} 628 for name, value := range opts { 629 results = append(results, options.set(name, value, seen)) 630 } 631 // Finally, enable any experimental features that are specified in 632 // maps, which allows users to individually toggle them on or off. 633 if enableExperiments { 634 options.enableAllExperimentMaps() 635 } 636 default: 637 results = append(results, OptionResult{ 638 Value: opts, 639 Error: errors.Errorf("Invalid options type %T", opts), 640 }) 641 } 642 return results 643} 644 645func (o *Options) ForClientCapabilities(caps protocol.ClientCapabilities) { 646 // Check if the client supports snippets in completion items. 647 if c := caps.TextDocument.Completion; c.CompletionItem.SnippetSupport { 648 o.InsertTextFormat = protocol.SnippetTextFormat 649 } 650 // Check if the client supports configuration messages. 651 o.ConfigurationSupported = caps.Workspace.Configuration 652 o.DynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration 653 o.DynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration 654 655 // Check which types of content format are supported by this client. 656 if hover := caps.TextDocument.Hover; len(hover.ContentFormat) > 0 { 657 o.PreferredContentFormat = hover.ContentFormat[0] 658 } 659 // Check if the client supports only line folding. 660 fr := caps.TextDocument.FoldingRange 661 o.LineFoldingOnly = fr.LineFoldingOnly 662 // Check if the client supports hierarchical document symbols. 663 o.HierarchicalDocumentSymbolSupport = caps.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport 664 // Check if the client supports semantic tokens 665 o.SemanticTypes = caps.TextDocument.SemanticTokens.TokenTypes 666 o.SemanticMods = caps.TextDocument.SemanticTokens.TokenModifiers 667 // we don't need Requests, as we support full functionality 668 // we don't need Formats, as there is only one, for now 669 670 // Check if the client supports diagnostic related information. 671 o.RelatedInformationSupported = caps.TextDocument.PublishDiagnostics.RelatedInformation 672 // Check if the client completion support incliudes tags (preferred) or deprecation 673 if caps.TextDocument.Completion.CompletionItem.TagSupport.ValueSet != nil { 674 o.CompletionTags = true 675 } else if caps.TextDocument.Completion.CompletionItem.DeprecatedSupport { 676 o.CompletionDeprecated = true 677 } 678} 679 680func (o *Options) Clone() *Options { 681 result := &Options{ 682 ClientOptions: o.ClientOptions, 683 InternalOptions: o.InternalOptions, 684 Hooks: Hooks{ 685 GoDiff: o.Hooks.GoDiff, 686 ComputeEdits: o.Hooks.ComputeEdits, 687 GofumptFormat: o.GofumptFormat, 688 URLRegexp: o.URLRegexp, 689 }, 690 ServerOptions: o.ServerOptions, 691 UserOptions: o.UserOptions, 692 } 693 // Fully clone any slice or map fields. Only Hooks, ExperimentalOptions, 694 // and UserOptions can be modified. 695 copyStringMap := func(src map[string]bool) map[string]bool { 696 dst := make(map[string]bool) 697 for k, v := range src { 698 dst[k] = v 699 } 700 return dst 701 } 702 result.Analyses = copyStringMap(o.Analyses) 703 result.Codelenses = copyStringMap(o.Codelenses) 704 705 copySlice := func(src []string) []string { 706 dst := make([]string, len(src)) 707 copy(dst, src) 708 return dst 709 } 710 result.SetEnvSlice(o.EnvSlice()) 711 result.BuildFlags = copySlice(o.BuildFlags) 712 result.DirectoryFilters = copySlice(o.DirectoryFilters) 713 714 copyAnalyzerMap := func(src map[string]*Analyzer) map[string]*Analyzer { 715 dst := make(map[string]*Analyzer) 716 for k, v := range src { 717 dst[k] = v 718 } 719 return dst 720 } 721 result.DefaultAnalyzers = copyAnalyzerMap(o.DefaultAnalyzers) 722 result.TypeErrorAnalyzers = copyAnalyzerMap(o.TypeErrorAnalyzers) 723 result.ConvenienceAnalyzers = copyAnalyzerMap(o.ConvenienceAnalyzers) 724 result.StaticcheckAnalyzers = copyAnalyzerMap(o.StaticcheckAnalyzers) 725 return result 726} 727 728func (o *Options) AddStaticcheckAnalyzer(a *analysis.Analyzer, enabled bool, severity protocol.DiagnosticSeverity) { 729 o.StaticcheckAnalyzers[a.Name] = &Analyzer{ 730 Analyzer: a, 731 Enabled: enabled, 732 Severity: severity, 733 } 734} 735 736// EnableAllExperiments turns on all of the experimental "off-by-default" 737// features offered by gopls. Any experimental features specified in maps 738// should be enabled in enableAllExperimentMaps. 739func (o *Options) EnableAllExperiments() { 740 o.SemanticTokens = true 741 o.ExperimentalPostfixCompletions = true 742 o.ExperimentalTemplateSupport = true 743 o.ExperimentalUseInvalidMetadata = true 744 o.ExperimentalWatchedFileDelay = 50 * time.Millisecond 745} 746 747func (o *Options) enableAllExperimentMaps() { 748 if _, ok := o.Codelenses[string(command.GCDetails)]; !ok { 749 o.Codelenses[string(command.GCDetails)] = true 750 } 751 if _, ok := o.Analyses[unusedparams.Analyzer.Name]; !ok { 752 o.Analyses[unusedparams.Analyzer.Name] = true 753 } 754} 755 756func (o *Options) set(name string, value interface{}, seen map[string]struct{}) OptionResult { 757 // Flatten the name in case we get options with a hierarchy. 758 split := strings.Split(name, ".") 759 name = split[len(split)-1] 760 761 result := OptionResult{Name: name, Value: value} 762 if _, ok := seen[name]; ok { 763 result.errorf("duplicate configuration for %s", name) 764 } 765 seen[name] = struct{}{} 766 767 switch name { 768 case "env": 769 menv, ok := value.(map[string]interface{}) 770 if !ok { 771 result.errorf("invalid type %T, expect map", value) 772 break 773 } 774 if o.Env == nil { 775 o.Env = make(map[string]string) 776 } 777 for k, v := range menv { 778 o.Env[k] = fmt.Sprint(v) 779 } 780 781 case "buildFlags": 782 iflags, ok := value.([]interface{}) 783 if !ok { 784 result.errorf("invalid type %T, expect list", value) 785 break 786 } 787 flags := make([]string, 0, len(iflags)) 788 for _, flag := range iflags { 789 flags = append(flags, fmt.Sprintf("%s", flag)) 790 } 791 o.BuildFlags = flags 792 case "directoryFilters": 793 ifilters, ok := value.([]interface{}) 794 if !ok { 795 result.errorf("invalid type %T, expect list", value) 796 break 797 } 798 var filters []string 799 for _, ifilter := range ifilters { 800 filter := fmt.Sprint(ifilter) 801 if filter[0] != '+' && filter[0] != '-' { 802 result.errorf("invalid filter %q, must start with + or -", filter) 803 return result 804 } 805 filters = append(filters, strings.TrimRight(filepath.FromSlash(filter), "/")) 806 } 807 o.DirectoryFilters = filters 808 case "memoryMode": 809 if s, ok := result.asOneOf( 810 string(ModeNormal), 811 string(ModeDegradeClosed), 812 ); ok { 813 o.MemoryMode = MemoryMode(s) 814 } 815 case "completionDocumentation": 816 result.setBool(&o.CompletionDocumentation) 817 case "usePlaceholders": 818 result.setBool(&o.UsePlaceholders) 819 case "deepCompletion": 820 result.setBool(&o.DeepCompletion) 821 case "completeUnimported": 822 result.setBool(&o.CompleteUnimported) 823 case "completionBudget": 824 result.setDuration(&o.CompletionBudget) 825 case "matcher": 826 if s, ok := result.asOneOf( 827 string(Fuzzy), 828 string(CaseSensitive), 829 string(CaseInsensitive), 830 ); ok { 831 o.Matcher = Matcher(s) 832 } 833 834 case "symbolMatcher": 835 if s, ok := result.asOneOf( 836 string(SymbolFuzzy), 837 string(SymbolCaseInsensitive), 838 string(SymbolCaseSensitive), 839 ); ok { 840 o.SymbolMatcher = SymbolMatcher(s) 841 } 842 843 case "symbolStyle": 844 if s, ok := result.asOneOf( 845 string(FullyQualifiedSymbols), 846 string(PackageQualifiedSymbols), 847 string(DynamicSymbols), 848 ); ok { 849 o.SymbolStyle = SymbolStyle(s) 850 } 851 852 case "hoverKind": 853 if s, ok := result.asOneOf( 854 string(NoDocumentation), 855 string(SingleLine), 856 string(SynopsisDocumentation), 857 string(FullDocumentation), 858 string(Structured), 859 ); ok { 860 o.HoverKind = HoverKind(s) 861 } 862 863 case "linkTarget": 864 result.setString(&o.LinkTarget) 865 866 case "linksInHover": 867 result.setBool(&o.LinksInHover) 868 869 case "importShortcut": 870 if s, ok := result.asOneOf(string(Both), string(Link), string(Definition)); ok { 871 o.ImportShortcut = ImportShortcut(s) 872 } 873 874 case "analyses": 875 result.setBoolMap(&o.Analyses) 876 877 case "annotations": 878 result.setAnnotationMap(&o.Annotations) 879 880 case "codelenses", "codelens": 881 var lensOverrides map[string]bool 882 result.setBoolMap(&lensOverrides) 883 if result.Error == nil { 884 if o.Codelenses == nil { 885 o.Codelenses = make(map[string]bool) 886 } 887 for lens, enabled := range lensOverrides { 888 o.Codelenses[lens] = enabled 889 } 890 } 891 892 // codelens is deprecated, but still works for now. 893 // TODO(rstambler): Remove this for the gopls/v0.7.0 release. 894 if name == "codelens" { 895 result.State = OptionDeprecated 896 result.Replacement = "codelenses" 897 } 898 899 case "staticcheck": 900 result.setBool(&o.Staticcheck) 901 902 case "local": 903 result.setString(&o.Local) 904 905 case "verboseOutput": 906 result.setBool(&o.VerboseOutput) 907 908 case "verboseWorkDoneProgress": 909 result.setBool(&o.VerboseWorkDoneProgress) 910 911 case "tempModfile": 912 result.setBool(&o.TempModfile) 913 914 case "gofumpt": 915 result.setBool(&o.Gofumpt) 916 917 case "semanticTokens": 918 result.setBool(&o.SemanticTokens) 919 920 case "expandWorkspaceToModule": 921 result.setBool(&o.ExpandWorkspaceToModule) 922 923 case "experimentalPostfixCompletions": 924 result.setBool(&o.ExperimentalPostfixCompletions) 925 926 case "experimentalWorkspaceModule": 927 result.setBool(&o.ExperimentalWorkspaceModule) 928 929 case "experimentalTemplateSupport": 930 result.setBool(&o.ExperimentalTemplateSupport) 931 932 case "experimentalDiagnosticsDelay", "diagnosticsDelay": 933 if name == "experimentalDiagnosticsDelay" { 934 result.State = OptionDeprecated 935 result.Replacement = "diagnosticsDelay" 936 } 937 result.setDuration(&o.DiagnosticsDelay) 938 939 case "experimentalWatchedFileDelay": 940 result.setDuration(&o.ExperimentalWatchedFileDelay) 941 942 case "experimentalPackageCacheKey": 943 result.setBool(&o.ExperimentalPackageCacheKey) 944 945 case "allowModfileModifications": 946 result.setBool(&o.AllowModfileModifications) 947 948 case "allowImplicitNetworkAccess": 949 result.setBool(&o.AllowImplicitNetworkAccess) 950 951 case "experimentalUseInvalidMetadata": 952 result.setBool(&o.ExperimentalUseInvalidMetadata) 953 954 case "allExperiments": 955 // This setting should be handled before all of the other options are 956 // processed, so do nothing here. 957 958 // Replaced settings. 959 case "experimentalDisabledAnalyses": 960 result.State = OptionDeprecated 961 result.Replacement = "analyses" 962 963 case "disableDeepCompletion": 964 result.State = OptionDeprecated 965 result.Replacement = "deepCompletion" 966 967 case "disableFuzzyMatching": 968 result.State = OptionDeprecated 969 result.Replacement = "fuzzyMatching" 970 971 case "wantCompletionDocumentation": 972 result.State = OptionDeprecated 973 result.Replacement = "completionDocumentation" 974 975 case "wantUnimportedCompletions": 976 result.State = OptionDeprecated 977 result.Replacement = "completeUnimported" 978 979 case "fuzzyMatching": 980 result.State = OptionDeprecated 981 result.Replacement = "matcher" 982 983 case "caseSensitiveCompletion": 984 result.State = OptionDeprecated 985 result.Replacement = "matcher" 986 987 // Deprecated settings. 988 case "wantSuggestedFixes": 989 result.State = OptionDeprecated 990 991 case "noIncrementalSync": 992 result.State = OptionDeprecated 993 994 case "watchFileChanges": 995 result.State = OptionDeprecated 996 997 case "go-diff": 998 result.State = OptionDeprecated 999 1000 default: 1001 result.State = OptionUnexpected 1002 } 1003 return result 1004} 1005 1006func (r *OptionResult) errorf(msg string, values ...interface{}) { 1007 prefix := fmt.Sprintf("parsing setting %q: ", r.Name) 1008 r.Error = errors.Errorf(prefix+msg, values...) 1009} 1010 1011func (r *OptionResult) asBool() (bool, bool) { 1012 b, ok := r.Value.(bool) 1013 if !ok { 1014 r.errorf("invalid type %T, expect bool", r.Value) 1015 return false, false 1016 } 1017 return b, true 1018} 1019 1020func (r *OptionResult) setBool(b *bool) { 1021 if v, ok := r.asBool(); ok { 1022 *b = v 1023 } 1024} 1025 1026func (r *OptionResult) setDuration(d *time.Duration) { 1027 if v, ok := r.asString(); ok { 1028 parsed, err := time.ParseDuration(v) 1029 if err != nil { 1030 r.errorf("failed to parse duration %q: %v", v, err) 1031 return 1032 } 1033 *d = parsed 1034 } 1035} 1036 1037func (r *OptionResult) setBoolMap(bm *map[string]bool) { 1038 m := r.asBoolMap() 1039 *bm = m 1040} 1041 1042func (r *OptionResult) setAnnotationMap(bm *map[Annotation]bool) { 1043 all := r.asBoolMap() 1044 if all == nil { 1045 return 1046 } 1047 // Default to everything enabled by default. 1048 m := make(map[Annotation]bool) 1049 for k, enabled := range all { 1050 a, err := asOneOf( 1051 k, 1052 string(Nil), 1053 string(Escape), 1054 string(Inline), 1055 string(Bounds), 1056 ) 1057 if err != nil { 1058 // In case of an error, process any legacy values. 1059 switch k { 1060 case "noEscape": 1061 m[Escape] = false 1062 r.errorf(`"noEscape" is deprecated, set "Escape: false" instead`) 1063 case "noNilcheck": 1064 m[Nil] = false 1065 r.errorf(`"noNilcheck" is deprecated, set "Nil: false" instead`) 1066 case "noInline": 1067 m[Inline] = false 1068 r.errorf(`"noInline" is deprecated, set "Inline: false" instead`) 1069 case "noBounds": 1070 m[Bounds] = false 1071 r.errorf(`"noBounds" is deprecated, set "Bounds: false" instead`) 1072 default: 1073 r.errorf(err.Error()) 1074 } 1075 continue 1076 } 1077 m[Annotation(a)] = enabled 1078 } 1079 *bm = m 1080} 1081 1082func (r *OptionResult) asBoolMap() map[string]bool { 1083 all, ok := r.Value.(map[string]interface{}) 1084 if !ok { 1085 r.errorf("invalid type %T for map[string]bool option", r.Value) 1086 return nil 1087 } 1088 m := make(map[string]bool) 1089 for a, enabled := range all { 1090 if enabled, ok := enabled.(bool); ok { 1091 m[a] = enabled 1092 } else { 1093 r.errorf("invalid type %T for map key %q", enabled, a) 1094 return m 1095 } 1096 } 1097 return m 1098} 1099 1100func (r *OptionResult) asString() (string, bool) { 1101 b, ok := r.Value.(string) 1102 if !ok { 1103 r.errorf("invalid type %T, expect string", r.Value) 1104 return "", false 1105 } 1106 return b, true 1107} 1108 1109func (r *OptionResult) asOneOf(options ...string) (string, bool) { 1110 s, ok := r.asString() 1111 if !ok { 1112 return "", false 1113 } 1114 s, err := asOneOf(s, options...) 1115 if err != nil { 1116 r.errorf(err.Error()) 1117 } 1118 return s, err == nil 1119} 1120 1121func asOneOf(str string, options ...string) (string, error) { 1122 lower := strings.ToLower(str) 1123 for _, opt := range options { 1124 if strings.ToLower(opt) == lower { 1125 return opt, nil 1126 } 1127 } 1128 return "", fmt.Errorf("invalid option %q for enum", str) 1129} 1130 1131func (r *OptionResult) setString(s *string) { 1132 if v, ok := r.asString(); ok { 1133 *s = v 1134 } 1135} 1136 1137// EnabledAnalyzers returns all of the analyzers enabled for the given 1138// snapshot. 1139func EnabledAnalyzers(snapshot Snapshot) (analyzers []*Analyzer) { 1140 for _, a := range snapshot.View().Options().DefaultAnalyzers { 1141 if a.IsEnabled(snapshot.View()) { 1142 analyzers = append(analyzers, a) 1143 } 1144 } 1145 for _, a := range snapshot.View().Options().TypeErrorAnalyzers { 1146 if a.IsEnabled(snapshot.View()) { 1147 analyzers = append(analyzers, a) 1148 } 1149 } 1150 for _, a := range snapshot.View().Options().ConvenienceAnalyzers { 1151 if a.IsEnabled(snapshot.View()) { 1152 analyzers = append(analyzers, a) 1153 } 1154 } 1155 for _, a := range snapshot.View().Options().StaticcheckAnalyzers { 1156 if a.IsEnabled(snapshot.View()) { 1157 analyzers = append(analyzers, a) 1158 } 1159 } 1160 return analyzers 1161} 1162 1163func typeErrorAnalyzers() map[string]*Analyzer { 1164 return map[string]*Analyzer{ 1165 fillreturns.Analyzer.Name: { 1166 Analyzer: fillreturns.Analyzer, 1167 ActionKind: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, 1168 Enabled: true, 1169 }, 1170 nonewvars.Analyzer.Name: { 1171 Analyzer: nonewvars.Analyzer, 1172 Enabled: true, 1173 }, 1174 noresultvalues.Analyzer.Name: { 1175 Analyzer: noresultvalues.Analyzer, 1176 Enabled: true, 1177 }, 1178 undeclaredname.Analyzer.Name: { 1179 Analyzer: undeclaredname.Analyzer, 1180 Fix: UndeclaredName, 1181 Enabled: true, 1182 }, 1183 } 1184} 1185 1186func convenienceAnalyzers() map[string]*Analyzer { 1187 return map[string]*Analyzer{ 1188 fillstruct.Analyzer.Name: { 1189 Analyzer: fillstruct.Analyzer, 1190 Fix: FillStruct, 1191 Enabled: true, 1192 ActionKind: []protocol.CodeActionKind{protocol.RefactorRewrite}, 1193 }, 1194 } 1195} 1196 1197func defaultAnalyzers() map[string]*Analyzer { 1198 return map[string]*Analyzer{ 1199 // The traditional vet suite: 1200 asmdecl.Analyzer.Name: {Analyzer: asmdecl.Analyzer, Enabled: true}, 1201 assign.Analyzer.Name: {Analyzer: assign.Analyzer, Enabled: true}, 1202 atomic.Analyzer.Name: {Analyzer: atomic.Analyzer, Enabled: true}, 1203 bools.Analyzer.Name: {Analyzer: bools.Analyzer, Enabled: true}, 1204 buildtag.Analyzer.Name: {Analyzer: buildtag.Analyzer, Enabled: true}, 1205 cgocall.Analyzer.Name: {Analyzer: cgocall.Analyzer, Enabled: true}, 1206 composite.Analyzer.Name: {Analyzer: composite.Analyzer, Enabled: true}, 1207 copylock.Analyzer.Name: {Analyzer: copylock.Analyzer, Enabled: true}, 1208 errorsas.Analyzer.Name: {Analyzer: errorsas.Analyzer, Enabled: true}, 1209 httpresponse.Analyzer.Name: {Analyzer: httpresponse.Analyzer, Enabled: true}, 1210 ifaceassert.Analyzer.Name: {Analyzer: ifaceassert.Analyzer, Enabled: true}, 1211 loopclosure.Analyzer.Name: {Analyzer: loopclosure.Analyzer, Enabled: true}, 1212 lostcancel.Analyzer.Name: {Analyzer: lostcancel.Analyzer, Enabled: true}, 1213 nilfunc.Analyzer.Name: {Analyzer: nilfunc.Analyzer, Enabled: true}, 1214 printf.Analyzer.Name: {Analyzer: printf.Analyzer, Enabled: true}, 1215 shift.Analyzer.Name: {Analyzer: shift.Analyzer, Enabled: true}, 1216 stdmethods.Analyzer.Name: {Analyzer: stdmethods.Analyzer, Enabled: true}, 1217 stringintconv.Analyzer.Name: {Analyzer: stringintconv.Analyzer, Enabled: true}, 1218 structtag.Analyzer.Name: {Analyzer: structtag.Analyzer, Enabled: true}, 1219 tests.Analyzer.Name: {Analyzer: tests.Analyzer, Enabled: true}, 1220 unmarshal.Analyzer.Name: {Analyzer: unmarshal.Analyzer, Enabled: true}, 1221 unreachable.Analyzer.Name: {Analyzer: unreachable.Analyzer, Enabled: true}, 1222 unsafeptr.Analyzer.Name: {Analyzer: unsafeptr.Analyzer, Enabled: true}, 1223 unusedresult.Analyzer.Name: {Analyzer: unusedresult.Analyzer, Enabled: true}, 1224 1225 // Non-vet analyzers: 1226 atomicalign.Analyzer.Name: {Analyzer: atomicalign.Analyzer, Enabled: true}, 1227 deepequalerrors.Analyzer.Name: {Analyzer: deepequalerrors.Analyzer, Enabled: true}, 1228 fieldalignment.Analyzer.Name: {Analyzer: fieldalignment.Analyzer, Enabled: false}, 1229 nilness.Analyzer.Name: {Analyzer: nilness.Analyzer, Enabled: false}, 1230 shadow.Analyzer.Name: {Analyzer: shadow.Analyzer, Enabled: false}, 1231 sortslice.Analyzer.Name: {Analyzer: sortslice.Analyzer, Enabled: true}, 1232 testinggoroutine.Analyzer.Name: {Analyzer: testinggoroutine.Analyzer, Enabled: true}, 1233 unusedparams.Analyzer.Name: {Analyzer: unusedparams.Analyzer, Enabled: false}, 1234 unusedwrite.Analyzer.Name: {Analyzer: unusedwrite.Analyzer, Enabled: false}, 1235 1236 // gofmt -s suite: 1237 simplifycompositelit.Analyzer.Name: { 1238 Analyzer: simplifycompositelit.Analyzer, 1239 Enabled: true, 1240 ActionKind: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, 1241 }, 1242 simplifyrange.Analyzer.Name: { 1243 Analyzer: simplifyrange.Analyzer, 1244 Enabled: true, 1245 ActionKind: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, 1246 }, 1247 simplifyslice.Analyzer.Name: { 1248 Analyzer: simplifyslice.Analyzer, 1249 Enabled: true, 1250 ActionKind: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, 1251 }, 1252 } 1253} 1254 1255func urlRegexp() *regexp.Regexp { 1256 // Ensure links are matched as full words, not anywhere. 1257 re := regexp.MustCompile(`\b(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?\b`) 1258 re.Longest() 1259 return re 1260} 1261 1262type APIJSON struct { 1263 Options map[string][]*OptionJSON 1264 Commands []*CommandJSON 1265 Lenses []*LensJSON 1266 Analyzers []*AnalyzerJSON 1267} 1268 1269type OptionJSON struct { 1270 Name string 1271 Type string 1272 Doc string 1273 EnumKeys EnumKeys 1274 EnumValues []EnumValue 1275 Default string 1276 Status string 1277 Hierarchy string 1278} 1279 1280type EnumKeys struct { 1281 ValueType string 1282 Keys []EnumKey 1283} 1284 1285type EnumKey struct { 1286 Name string 1287 Doc string 1288 Default string 1289} 1290 1291type EnumValue struct { 1292 Value string 1293 Doc string 1294} 1295 1296type CommandJSON struct { 1297 Command string 1298 Title string 1299 Doc string 1300 ArgDoc string 1301 ResultDoc string 1302} 1303 1304type LensJSON struct { 1305 Lens string 1306 Title string 1307 Doc string 1308} 1309 1310type AnalyzerJSON struct { 1311 Name string 1312 Doc string 1313 Default bool 1314} 1315