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