golang.org/x/tools/gopls@v0.15.3/internal/settings/settings.go (about) 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 5 package settings 6 7 import ( 8 "context" 9 "fmt" 10 "path/filepath" 11 "regexp" 12 "runtime" 13 "strings" 14 "time" 15 16 "golang.org/x/tools/go/analysis" 17 "golang.org/x/tools/go/analysis/passes/appends" 18 "golang.org/x/tools/go/analysis/passes/asmdecl" 19 "golang.org/x/tools/go/analysis/passes/assign" 20 "golang.org/x/tools/go/analysis/passes/atomic" 21 "golang.org/x/tools/go/analysis/passes/atomicalign" 22 "golang.org/x/tools/go/analysis/passes/bools" 23 "golang.org/x/tools/go/analysis/passes/buildtag" 24 "golang.org/x/tools/go/analysis/passes/cgocall" 25 "golang.org/x/tools/go/analysis/passes/composite" 26 "golang.org/x/tools/go/analysis/passes/copylock" 27 "golang.org/x/tools/go/analysis/passes/deepequalerrors" 28 "golang.org/x/tools/go/analysis/passes/defers" 29 "golang.org/x/tools/go/analysis/passes/directive" 30 "golang.org/x/tools/go/analysis/passes/errorsas" 31 "golang.org/x/tools/go/analysis/passes/fieldalignment" 32 "golang.org/x/tools/go/analysis/passes/httpresponse" 33 "golang.org/x/tools/go/analysis/passes/ifaceassert" 34 "golang.org/x/tools/go/analysis/passes/loopclosure" 35 "golang.org/x/tools/go/analysis/passes/lostcancel" 36 "golang.org/x/tools/go/analysis/passes/nilfunc" 37 "golang.org/x/tools/go/analysis/passes/nilness" 38 "golang.org/x/tools/go/analysis/passes/printf" 39 "golang.org/x/tools/go/analysis/passes/shadow" 40 "golang.org/x/tools/go/analysis/passes/shift" 41 "golang.org/x/tools/go/analysis/passes/slog" 42 "golang.org/x/tools/go/analysis/passes/sortslice" 43 "golang.org/x/tools/go/analysis/passes/stdmethods" 44 "golang.org/x/tools/go/analysis/passes/stringintconv" 45 "golang.org/x/tools/go/analysis/passes/structtag" 46 "golang.org/x/tools/go/analysis/passes/testinggoroutine" 47 "golang.org/x/tools/go/analysis/passes/tests" 48 "golang.org/x/tools/go/analysis/passes/timeformat" 49 "golang.org/x/tools/go/analysis/passes/unmarshal" 50 "golang.org/x/tools/go/analysis/passes/unreachable" 51 "golang.org/x/tools/go/analysis/passes/unsafeptr" 52 "golang.org/x/tools/go/analysis/passes/unusedresult" 53 "golang.org/x/tools/go/analysis/passes/unusedwrite" 54 "golang.org/x/tools/gopls/internal/analysis/deprecated" 55 "golang.org/x/tools/gopls/internal/analysis/embeddirective" 56 "golang.org/x/tools/gopls/internal/analysis/fillreturns" 57 "golang.org/x/tools/gopls/internal/analysis/infertypeargs" 58 "golang.org/x/tools/gopls/internal/analysis/nonewvars" 59 "golang.org/x/tools/gopls/internal/analysis/noresultvalues" 60 "golang.org/x/tools/gopls/internal/analysis/simplifycompositelit" 61 "golang.org/x/tools/gopls/internal/analysis/simplifyrange" 62 "golang.org/x/tools/gopls/internal/analysis/simplifyslice" 63 "golang.org/x/tools/gopls/internal/analysis/stubmethods" 64 "golang.org/x/tools/gopls/internal/analysis/undeclaredname" 65 "golang.org/x/tools/gopls/internal/analysis/unusedparams" 66 "golang.org/x/tools/gopls/internal/analysis/unusedvariable" 67 "golang.org/x/tools/gopls/internal/analysis/useany" 68 "golang.org/x/tools/gopls/internal/file" 69 "golang.org/x/tools/gopls/internal/protocol" 70 "golang.org/x/tools/gopls/internal/protocol/command" 71 ) 72 73 type Annotation string 74 75 const ( 76 // Nil controls nil checks. 77 Nil Annotation = "nil" 78 79 // Escape controls diagnostics about escape choices. 80 Escape Annotation = "escape" 81 82 // Inline controls diagnostics about inlining choices. 83 Inline Annotation = "inline" 84 85 // Bounds controls bounds checking diagnostics. 86 Bounds Annotation = "bounds" 87 ) 88 89 // Options holds various configuration that affects Gopls execution, organized 90 // by the nature or origin of the settings. 91 type Options struct { 92 ClientOptions 93 ServerOptions 94 UserOptions 95 InternalOptions 96 Hooks 97 } 98 99 // IsAnalyzerEnabled reports whether an analyzer with the given name is 100 // enabled. 101 // 102 // TODO(rfindley): refactor to simplify this function. We no longer need the 103 // different categories of analyzer. 104 func (opts *Options) IsAnalyzerEnabled(name string) bool { 105 for _, amap := range []map[string]*Analyzer{opts.DefaultAnalyzers, opts.StaticcheckAnalyzers} { 106 for _, analyzer := range amap { 107 if analyzer.Analyzer.Name == name && analyzer.IsEnabled(opts) { 108 return true 109 } 110 } 111 } 112 return false 113 } 114 115 // ClientOptions holds LSP-specific configuration that is provided by the 116 // client. 117 type ClientOptions struct { 118 ClientInfo *protocol.ClientInfo 119 InsertTextFormat protocol.InsertTextFormat 120 ConfigurationSupported bool 121 DynamicConfigurationSupported bool 122 DynamicRegistrationSemanticTokensSupported bool 123 DynamicWatchedFilesSupported bool 124 RelativePatternsSupported bool 125 PreferredContentFormat protocol.MarkupKind 126 LineFoldingOnly bool 127 HierarchicalDocumentSymbolSupport bool 128 SemanticTypes []string 129 SemanticMods []string 130 RelatedInformationSupported bool 131 CompletionTags bool 132 CompletionDeprecated bool 133 SupportedResourceOperations []protocol.ResourceOperationKind 134 CodeActionResolveOptions []string 135 } 136 137 // ServerOptions holds LSP-specific configuration that is provided by the 138 // server. 139 type ServerOptions struct { 140 SupportedCodeActions map[file.Kind]map[protocol.CodeActionKind]bool 141 SupportedCommands []string 142 } 143 144 type BuildOptions struct { 145 // BuildFlags is the set of flags passed on to the build system when invoked. 146 // It is applied to queries like `go list`, which is used when discovering files. 147 // The most common use is to set `-tags`. 148 BuildFlags []string 149 150 // Env adds environment variables to external commands run by `gopls`, most notably `go list`. 151 Env map[string]string 152 153 // DirectoryFilters can be used to exclude unwanted directories from the 154 // workspace. By default, all directories are included. Filters are an 155 // operator, `+` to include and `-` to exclude, followed by a path prefix 156 // relative to the workspace folder. They are evaluated in order, and 157 // the last filter that applies to a path controls whether it is included. 158 // The path prefix can be empty, so an initial `-` excludes everything. 159 // 160 // DirectoryFilters also supports the `**` operator to match 0 or more directories. 161 // 162 // Examples: 163 // 164 // Exclude node_modules at current depth: `-node_modules` 165 // 166 // Exclude node_modules at any depth: `-**/node_modules` 167 // 168 // Include only project_a: `-` (exclude everything), `+project_a` 169 // 170 // Include only project_a, but not node_modules inside it: `-`, `+project_a`, `-project_a/node_modules` 171 DirectoryFilters []string 172 173 // TemplateExtensions gives the extensions of file names that are treateed 174 // as template files. (The extension 175 // is the part of the file name after the final dot.) 176 TemplateExtensions []string 177 178 // obsolete, no effect 179 MemoryMode string `status:"experimental"` 180 181 // ExpandWorkspaceToModule determines which packages are considered 182 // "workspace packages" when the workspace is using modules. 183 // 184 // Workspace packages affect the scope of workspace-wide operations. Notably, 185 // gopls diagnoses all packages considered to be part of the workspace after 186 // every keystroke, so by setting "ExpandWorkspaceToModule" to false, and 187 // opening a nested workspace directory, you can reduce the amount of work 188 // gopls has to do to keep your workspace up to date. 189 ExpandWorkspaceToModule bool `status:"experimental"` 190 191 // AllowModfileModifications disables -mod=readonly, allowing imports from 192 // out-of-scope modules. This option will eventually be removed. 193 AllowModfileModifications bool `status:"experimental"` 194 195 // AllowImplicitNetworkAccess disables GOPROXY=off, allowing implicit module 196 // downloads rather than requiring user action. This option will eventually 197 // be removed. 198 AllowImplicitNetworkAccess bool `status:"experimental"` 199 200 // StandaloneTags specifies a set of build constraints that identify 201 // individual Go source files that make up the entire main package of an 202 // executable. 203 // 204 // A common example of standalone main files is the convention of using the 205 // directive `//go:build ignore` to denote files that are not intended to be 206 // included in any package, for example because they are invoked directly by 207 // the developer using `go run`. 208 // 209 // Gopls considers a file to be a standalone main file if and only if it has 210 // package name "main" and has a build directive of the exact form 211 // "//go:build tag" or "// +build tag", where tag is among the list of tags 212 // configured by this setting. Notably, if the build constraint is more 213 // complicated than a simple tag (such as the composite constraint 214 // `//go:build tag && go1.18`), the file is not considered to be a standalone 215 // main file. 216 // 217 // This setting is only supported when gopls is built with Go 1.16 or later. 218 StandaloneTags []string 219 } 220 221 type UIOptions struct { 222 DocumentationOptions 223 CompletionOptions 224 NavigationOptions 225 DiagnosticOptions 226 InlayHintOptions 227 228 // Codelenses overrides the enabled/disabled state of code lenses. See the 229 // "Code Lenses" section of the 230 // [Settings page](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#code-lenses) 231 // for the list of supported lenses. 232 // 233 // Example Usage: 234 // 235 // ```json5 236 // "gopls": { 237 // ... 238 // "codelenses": { 239 // "generate": false, // Don't show the `go generate` lens. 240 // "gc_details": true // Show a code lens toggling the display of gc's choices. 241 // } 242 // ... 243 // } 244 // ``` 245 Codelenses map[string]bool 246 247 // SemanticTokens controls whether the LSP server will send 248 // semantic tokens to the client. 249 SemanticTokens bool `status:"experimental"` 250 251 // NoSemanticString turns off the sending of the semantic token 'string' 252 NoSemanticString bool `status:"experimental"` 253 254 // NoSemanticNumber turns off the sending of the semantic token 'number' 255 NoSemanticNumber bool `status:"experimental"` 256 } 257 258 type CompletionOptions struct { 259 // Placeholders enables placeholders for function parameters or struct 260 // fields in completion responses. 261 UsePlaceholders bool 262 263 // CompletionBudget is the soft latency goal for completion requests. Most 264 // requests finish in a couple milliseconds, but in some cases deep 265 // completions can take much longer. As we use up our budget we 266 // dynamically reduce the search scope to ensure we return timely 267 // results. Zero means unlimited. 268 CompletionBudget time.Duration `status:"debug"` 269 270 // Matcher sets the algorithm that is used when calculating completion 271 // candidates. 272 Matcher Matcher `status:"advanced"` 273 274 // ExperimentalPostfixCompletions enables artificial method snippets 275 // such as "someSlice.sort!". 276 ExperimentalPostfixCompletions bool `status:"experimental"` 277 278 // CompleteFunctionCalls enables function call completion. 279 // 280 // When completing a statement, or when a function return type matches the 281 // expected of the expression being completed, completion may suggest call 282 // expressions (i.e. may include parentheses). 283 CompleteFunctionCalls bool 284 } 285 286 type DocumentationOptions struct { 287 // HoverKind controls the information that appears in the hover text. 288 // SingleLine and Structured are intended for use only by authors of editor plugins. 289 HoverKind HoverKind 290 291 // LinkTarget controls where documentation links go. 292 // It might be one of: 293 // 294 // * `"godoc.org"` 295 // * `"pkg.go.dev"` 296 // 297 // If company chooses to use its own `godoc.org`, its address can be used as well. 298 // 299 // Modules matching the GOPRIVATE environment variable will not have 300 // documentation links in hover. 301 LinkTarget string 302 303 // LinksInHover toggles the presence of links to documentation in hover. 304 LinksInHover bool 305 } 306 307 type FormattingOptions struct { 308 // Local is the equivalent of the `goimports -local` flag, which puts 309 // imports beginning with this string after third-party packages. It should 310 // be the prefix of the import path whose imports should be grouped 311 // separately. 312 Local string 313 314 // Gofumpt indicates if we should run gofumpt formatting. 315 Gofumpt bool 316 } 317 318 type DiagnosticOptions struct { 319 // Analyses specify analyses that the user would like to enable or disable. 320 // A map of the names of analysis passes that should be enabled/disabled. 321 // A full list of analyzers that gopls uses can be found in 322 // [analyzers.md](https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md). 323 // 324 // Example Usage: 325 // 326 // ```json5 327 // ... 328 // "analyses": { 329 // "unreachable": false, // Disable the unreachable analyzer. 330 // "unusedvariable": true // Enable the unusedvariable analyzer. 331 // } 332 // ... 333 // ``` 334 Analyses map[string]bool 335 336 // Staticcheck enables additional analyses from staticcheck.io. 337 // These analyses are documented on 338 // [Staticcheck's website](https://staticcheck.io/docs/checks/). 339 Staticcheck bool `status:"experimental"` 340 341 // Annotations specifies the various kinds of optimization diagnostics 342 // that should be reported by the gc_details command. 343 Annotations map[Annotation]bool `status:"experimental"` 344 345 // Vulncheck enables vulnerability scanning. 346 Vulncheck VulncheckMode `status:"experimental"` 347 348 // DiagnosticsDelay controls the amount of time that gopls waits 349 // after the most recent file modification before computing deep diagnostics. 350 // Simple diagnostics (parsing and type-checking) are always run immediately 351 // on recently modified packages. 352 // 353 // This option must be set to a valid duration string, for example `"250ms"`. 354 DiagnosticsDelay time.Duration `status:"advanced"` 355 356 // DiagnosticsTrigger controls when to run diagnostics. 357 DiagnosticsTrigger DiagnosticsTrigger `status:"experimental"` 358 359 // AnalysisProgressReporting controls whether gopls sends progress 360 // notifications when construction of its index of analysis facts is taking a 361 // long time. Cancelling these notifications will cancel the indexing task, 362 // though it will restart after the next change in the workspace. 363 // 364 // When a package is opened for the first time and heavyweight analyses such as 365 // staticcheck are enabled, it can take a while to construct the index of 366 // analysis facts for all its dependencies. The index is cached in the 367 // filesystem, so subsequent analysis should be faster. 368 AnalysisProgressReporting bool 369 } 370 371 type InlayHintOptions struct { 372 // Hints specify inlay hints that users want to see. A full list of hints 373 // that gopls uses can be found in 374 // [inlayHints.md](https://github.com/golang/tools/blob/master/gopls/doc/inlayHints.md). 375 Hints map[string]bool `status:"experimental"` 376 } 377 378 type NavigationOptions struct { 379 // ImportShortcut specifies whether import statements should link to 380 // documentation or go to definitions. 381 ImportShortcut ImportShortcut 382 383 // SymbolMatcher sets the algorithm that is used when finding workspace symbols. 384 SymbolMatcher SymbolMatcher `status:"advanced"` 385 386 // SymbolStyle controls how symbols are qualified in symbol responses. 387 // 388 // Example Usage: 389 // 390 // ```json5 391 // "gopls": { 392 // ... 393 // "symbolStyle": "Dynamic", 394 // ... 395 // } 396 // ``` 397 SymbolStyle SymbolStyle `status:"advanced"` 398 399 // SymbolScope controls which packages are searched for workspace/symbol 400 // requests. The default value, "workspace", searches only workspace 401 // packages. The legacy behavior, "all", causes all loaded packages to be 402 // searched, including dependencies; this is more expensive and may return 403 // unwanted results. 404 SymbolScope SymbolScope 405 } 406 407 // UserOptions holds custom Gopls configuration (not part of the LSP) that is 408 // modified by the client. 409 type UserOptions struct { 410 BuildOptions 411 UIOptions 412 FormattingOptions 413 414 // VerboseOutput enables additional debug logging. 415 VerboseOutput bool `status:"debug"` 416 } 417 418 // EnvSlice returns Env as a slice of k=v strings. 419 func (u *UserOptions) EnvSlice() []string { 420 var result []string 421 for k, v := range u.Env { 422 result = append(result, fmt.Sprintf("%v=%v", k, v)) 423 } 424 return result 425 } 426 427 // SetEnvSlice sets Env from a slice of k=v strings. 428 func (u *UserOptions) SetEnvSlice(env []string) { 429 u.Env = map[string]string{} 430 for _, kv := range env { 431 split := strings.SplitN(kv, "=", 2) 432 if len(split) != 2 { 433 continue 434 } 435 u.Env[split[0]] = split[1] 436 } 437 } 438 439 // Hooks contains configuration that is provided to the Gopls command by the 440 // main package. 441 type Hooks struct { 442 // LicensesText holds third party licenses for software used by gopls. 443 LicensesText string 444 445 // Whether staticcheck is supported. 446 StaticcheckSupported bool 447 448 // URLRegexp is used to find potential URLs in comments/strings. 449 // 450 // Not all matches are shown to the user: if the matched URL is not detected 451 // as valid, it will be skipped. 452 URLRegexp *regexp.Regexp 453 454 // GofumptFormat allows the gopls module to wire-in a call to 455 // gofumpt/format.Source. langVersion and modulePath are used for some 456 // Gofumpt formatting rules -- see the Gofumpt documentation for details. 457 GofumptFormat func(ctx context.Context, langVersion, modulePath string, src []byte) ([]byte, error) 458 459 DefaultAnalyzers map[string]*Analyzer 460 StaticcheckAnalyzers map[string]*Analyzer 461 } 462 463 // InternalOptions contains settings that are not intended for use by the 464 // average user. These may be settings used by tests or outdated settings that 465 // will soon be deprecated. Some of these settings may not even be configurable 466 // by the user. 467 // 468 // TODO(rfindley): even though these settings are not intended for 469 // modification, some of them should be surfaced in our documentation. 470 type InternalOptions struct { 471 // VerboseWorkDoneProgress controls whether the LSP server should send 472 // progress reports for all work done outside the scope of an RPC. 473 // Used by the regression tests. 474 VerboseWorkDoneProgress bool 475 476 // The following options were previously available to users, but they 477 // really shouldn't be configured by anyone other than "power users". 478 479 // CompletionDocumentation enables documentation with completion results. 480 CompletionDocumentation bool 481 482 // CompleteUnimported enables completion for packages that you do not 483 // currently import. 484 CompleteUnimported bool 485 486 // DeepCompletion enables the ability to return completions from deep 487 // inside relevant entities, rather than just the locally accessible ones. 488 // 489 // Consider this example: 490 // 491 // ```go 492 // package main 493 // 494 // import "fmt" 495 // 496 // type wrapString struct { 497 // str string 498 // } 499 // 500 // func main() { 501 // x := wrapString{"hello world"} 502 // fmt.Printf(<>) 503 // } 504 // ``` 505 // 506 // At the location of the `<>` in this program, deep completion would suggest 507 // the result `x.str`. 508 DeepCompletion bool 509 510 // ShowBugReports causes a message to be shown when the first bug is reported 511 // on the server. 512 // This option applies only during initialization. 513 ShowBugReports bool 514 515 // SubdirWatchPatterns configures the file watching glob patterns registered 516 // by gopls. 517 // 518 // Some clients (namely VS Code) do not send workspace/didChangeWatchedFile 519 // notifications for files contained in a directory when that directory is 520 // deleted: 521 // https://github.com/microsoft/vscode/issues/109754 522 // 523 // In this case, gopls would miss important notifications about deleted 524 // packages. To work around this, gopls registers a watch pattern for each 525 // directory containing Go files. 526 // 527 // Unfortunately, other clients experience performance problems with this 528 // many watch patterns, so there is no single behavior that works well for 529 // all clients. 530 // 531 // The "subdirWatchPatterns" setting allows configuring this behavior. Its 532 // default value of "auto" attempts to guess the correct behavior based on 533 // the client name. We'd love to avoid this specialization, but as described 534 // above there is no single value that works for all clients. 535 // 536 // If any LSP client does not behave well with the default value (for 537 // example, if like VS Code it drops file notifications), please file an 538 // issue. 539 SubdirWatchPatterns SubdirWatchPatterns 540 541 // ReportAnalysisProgressAfter sets the duration for gopls to wait before starting 542 // progress reporting for ongoing go/analysis passes. 543 // 544 // It is intended to be used for testing only. 545 ReportAnalysisProgressAfter time.Duration 546 547 // TelemetryPrompt controls whether gopls prompts about enabling Go telemetry. 548 // 549 // Once the prompt is answered, gopls doesn't ask again, but TelemetryPrompt 550 // can prevent the question from ever being asked in the first place. 551 TelemetryPrompt bool 552 553 // LinkifyShowMessage controls whether the client wants gopls 554 // to linkify links in showMessage. e.g. [go.dev](https://go.dev). 555 LinkifyShowMessage bool 556 557 // IncludeReplaceInWorkspace controls whether locally replaced modules in a 558 // go.mod file are treated like workspace modules. 559 // Or in other words, if a go.mod file with local replaces behaves like a 560 // go.work file. 561 IncludeReplaceInWorkspace bool 562 563 // ZeroConfig enables the zero-config algorithm for workspace layout, 564 // dynamically creating build configurations for different modules, 565 // directories, and GOOS/GOARCH combinations to cover open files. 566 ZeroConfig bool 567 } 568 569 type SubdirWatchPatterns string 570 571 const ( 572 SubdirWatchPatternsOn SubdirWatchPatterns = "on" 573 SubdirWatchPatternsOff SubdirWatchPatterns = "off" 574 SubdirWatchPatternsAuto SubdirWatchPatterns = "auto" 575 ) 576 577 type ImportShortcut string 578 579 const ( 580 BothShortcuts ImportShortcut = "Both" 581 LinkShortcut ImportShortcut = "Link" 582 DefinitionShortcut ImportShortcut = "Definition" 583 ) 584 585 func (s ImportShortcut) ShowLinks() bool { 586 return s == BothShortcuts || s == LinkShortcut 587 } 588 589 func (s ImportShortcut) ShowDefinition() bool { 590 return s == BothShortcuts || s == DefinitionShortcut 591 } 592 593 type Matcher string 594 595 const ( 596 Fuzzy Matcher = "Fuzzy" 597 CaseInsensitive Matcher = "CaseInsensitive" 598 CaseSensitive Matcher = "CaseSensitive" 599 ) 600 601 // A SymbolMatcher controls the matching of symbols for workspace/symbol 602 // requests. 603 type SymbolMatcher string 604 605 const ( 606 SymbolFuzzy SymbolMatcher = "Fuzzy" 607 SymbolFastFuzzy SymbolMatcher = "FastFuzzy" 608 SymbolCaseInsensitive SymbolMatcher = "CaseInsensitive" 609 SymbolCaseSensitive SymbolMatcher = "CaseSensitive" 610 ) 611 612 // A SymbolStyle controls the formatting of symbols in workspace/symbol results. 613 type SymbolStyle string 614 615 const ( 616 // PackageQualifiedSymbols is package qualified symbols i.e. 617 // "pkg.Foo.Field". 618 PackageQualifiedSymbols SymbolStyle = "Package" 619 // FullyQualifiedSymbols is fully qualified symbols, i.e. 620 // "path/to/pkg.Foo.Field". 621 FullyQualifiedSymbols SymbolStyle = "Full" 622 // DynamicSymbols uses whichever qualifier results in the highest scoring 623 // match for the given symbol query. Here a "qualifier" is any "/" or "." 624 // delimited suffix of the fully qualified symbol. i.e. "to/pkg.Foo.Field" or 625 // just "Foo.Field". 626 DynamicSymbols SymbolStyle = "Dynamic" 627 ) 628 629 // A SymbolScope controls the search scope for workspace/symbol requests. 630 type SymbolScope string 631 632 const ( 633 // WorkspaceSymbolScope matches symbols in workspace packages only. 634 WorkspaceSymbolScope SymbolScope = "workspace" 635 // AllSymbolScope matches symbols in any loaded package, including 636 // dependencies. 637 AllSymbolScope SymbolScope = "all" 638 ) 639 640 type HoverKind string 641 642 const ( 643 SingleLine HoverKind = "SingleLine" 644 NoDocumentation HoverKind = "NoDocumentation" 645 SynopsisDocumentation HoverKind = "SynopsisDocumentation" 646 FullDocumentation HoverKind = "FullDocumentation" 647 648 // Structured is an experimental setting that returns a structured hover format. 649 // This format separates the signature from the documentation, so that the client 650 // can do more manipulation of these fields. 651 // 652 // This should only be used by clients that support this behavior. 653 Structured HoverKind = "Structured" 654 ) 655 656 type VulncheckMode string 657 658 const ( 659 // Disable vulnerability analysis. 660 ModeVulncheckOff VulncheckMode = "Off" 661 // In Imports mode, `gopls` will report vulnerabilities that affect packages 662 // directly and indirectly used by the analyzed main module. 663 ModeVulncheckImports VulncheckMode = "Imports" 664 665 // TODO: VulncheckRequire, VulncheckCallgraph 666 ) 667 668 type DiagnosticsTrigger string 669 670 const ( 671 // Trigger diagnostics on file edit and save. (default) 672 DiagnosticsOnEdit DiagnosticsTrigger = "Edit" 673 // Trigger diagnostics only on file save. Events like initial workspace load 674 // or configuration change will still trigger diagnostics. 675 DiagnosticsOnSave DiagnosticsTrigger = "Save" 676 // TODO: support "Manual"? 677 ) 678 679 type OptionResults []OptionResult 680 681 type OptionResult struct { 682 Name string 683 Value any 684 Error error 685 } 686 687 func SetOptions(options *Options, opts any) OptionResults { 688 var results OptionResults 689 switch opts := opts.(type) { 690 case nil: 691 case map[string]any: 692 // If the user's settings contains "allExperiments", set that first, 693 // and then let them override individual settings independently. 694 var enableExperiments bool 695 for name, value := range opts { 696 if b, ok := value.(bool); name == "allExperiments" && ok && b { 697 enableExperiments = true 698 options.EnableAllExperiments() 699 } 700 } 701 seen := map[string]struct{}{} 702 for name, value := range opts { 703 results = append(results, options.set(name, value, seen)) 704 } 705 // Finally, enable any experimental features that are specified in 706 // maps, which allows users to individually toggle them on or off. 707 if enableExperiments { 708 options.enableAllExperimentMaps() 709 } 710 default: 711 results = append(results, OptionResult{ 712 Value: opts, 713 Error: fmt.Errorf("Invalid options type %T", opts), 714 }) 715 } 716 return results 717 } 718 719 func (o *Options) ForClientCapabilities(clientName *protocol.ClientInfo, caps protocol.ClientCapabilities) { 720 o.ClientInfo = clientName 721 // Check if the client supports snippets in completion items. 722 if caps.Workspace.WorkspaceEdit != nil { 723 o.SupportedResourceOperations = caps.Workspace.WorkspaceEdit.ResourceOperations 724 } 725 if c := caps.TextDocument.Completion; c.CompletionItem.SnippetSupport { 726 o.InsertTextFormat = protocol.SnippetTextFormat 727 } 728 // Check if the client supports configuration messages. 729 o.ConfigurationSupported = caps.Workspace.Configuration 730 o.DynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration 731 o.DynamicRegistrationSemanticTokensSupported = caps.TextDocument.SemanticTokens.DynamicRegistration 732 o.DynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration 733 o.RelativePatternsSupported = caps.Workspace.DidChangeWatchedFiles.RelativePatternSupport 734 735 // Check which types of content format are supported by this client. 736 if hover := caps.TextDocument.Hover; hover != nil && len(hover.ContentFormat) > 0 { 737 o.PreferredContentFormat = hover.ContentFormat[0] 738 } 739 // Check if the client supports only line folding. 740 741 if fr := caps.TextDocument.FoldingRange; fr != nil { 742 o.LineFoldingOnly = fr.LineFoldingOnly 743 } 744 // Check if the client supports hierarchical document symbols. 745 o.HierarchicalDocumentSymbolSupport = caps.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport 746 747 // Client's semantic tokens 748 o.SemanticTypes = caps.TextDocument.SemanticTokens.TokenTypes 749 o.SemanticMods = caps.TextDocument.SemanticTokens.TokenModifiers 750 // we don't need Requests, as we support full functionality 751 // we don't need Formats, as there is only one, for now 752 753 // Check if the client supports diagnostic related information. 754 o.RelatedInformationSupported = caps.TextDocument.PublishDiagnostics.RelatedInformation 755 // Check if the client completion support includes tags (preferred) or deprecation 756 if caps.TextDocument.Completion.CompletionItem.TagSupport != nil && 757 caps.TextDocument.Completion.CompletionItem.TagSupport.ValueSet != nil { 758 o.CompletionTags = true 759 } else if caps.TextDocument.Completion.CompletionItem.DeprecatedSupport { 760 o.CompletionDeprecated = true 761 } 762 763 // Check if the client supports code actions resolving. 764 if caps.TextDocument.CodeAction.DataSupport && caps.TextDocument.CodeAction.ResolveSupport != nil { 765 o.CodeActionResolveOptions = caps.TextDocument.CodeAction.ResolveSupport.Properties 766 } 767 } 768 769 func (o *Options) Clone() *Options { 770 // TODO(rfindley): has this function gone stale? It appears that there are 771 // settings that are incorrectly cloned here (such as TemplateExtensions). 772 result := &Options{ 773 ClientOptions: o.ClientOptions, 774 InternalOptions: o.InternalOptions, 775 Hooks: Hooks{ 776 StaticcheckSupported: o.StaticcheckSupported, 777 GofumptFormat: o.GofumptFormat, 778 URLRegexp: o.URLRegexp, 779 }, 780 ServerOptions: o.ServerOptions, 781 UserOptions: o.UserOptions, 782 } 783 // Fully clone any slice or map fields. Only Hooks, ExperimentalOptions, 784 // and UserOptions can be modified. 785 copyStringMap := func(src map[string]bool) map[string]bool { 786 dst := make(map[string]bool) 787 for k, v := range src { 788 dst[k] = v 789 } 790 return dst 791 } 792 result.Analyses = copyStringMap(o.Analyses) 793 result.Codelenses = copyStringMap(o.Codelenses) 794 795 copySlice := func(src []string) []string { 796 dst := make([]string, len(src)) 797 copy(dst, src) 798 return dst 799 } 800 result.SetEnvSlice(o.EnvSlice()) 801 result.BuildFlags = copySlice(o.BuildFlags) 802 result.DirectoryFilters = copySlice(o.DirectoryFilters) 803 result.StandaloneTags = copySlice(o.StandaloneTags) 804 805 copyAnalyzerMap := func(src map[string]*Analyzer) map[string]*Analyzer { 806 dst := make(map[string]*Analyzer) 807 for k, v := range src { 808 dst[k] = v 809 } 810 return dst 811 } 812 result.DefaultAnalyzers = copyAnalyzerMap(o.DefaultAnalyzers) 813 result.StaticcheckAnalyzers = copyAnalyzerMap(o.StaticcheckAnalyzers) 814 return result 815 } 816 817 func (o *Options) AddStaticcheckAnalyzer(a *analysis.Analyzer, enabled bool, severity protocol.DiagnosticSeverity) { 818 o.StaticcheckAnalyzers[a.Name] = &Analyzer{ 819 Analyzer: a, 820 Enabled: enabled, 821 Severity: severity, 822 } 823 } 824 825 // EnableAllExperiments turns on all of the experimental "off-by-default" 826 // features offered by gopls. Any experimental features specified in maps 827 // should be enabled in enableAllExperimentMaps. 828 func (o *Options) EnableAllExperiments() { 829 o.SemanticTokens = true 830 } 831 832 func (o *Options) enableAllExperimentMaps() { 833 if _, ok := o.Codelenses[string(command.GCDetails)]; !ok { 834 o.Codelenses[string(command.GCDetails)] = true 835 } 836 if _, ok := o.Codelenses[string(command.RunGovulncheck)]; !ok { 837 o.Codelenses[string(command.RunGovulncheck)] = true 838 } 839 if _, ok := o.Analyses[unusedvariable.Analyzer.Name]; !ok { 840 o.Analyses[unusedvariable.Analyzer.Name] = true 841 } 842 } 843 844 // validateDirectoryFilter validates if the filter string 845 // - is not empty 846 // - start with either + or - 847 // - doesn't contain currently unsupported glob operators: *, ? 848 func validateDirectoryFilter(ifilter string) (string, error) { 849 filter := fmt.Sprint(ifilter) 850 if filter == "" || (filter[0] != '+' && filter[0] != '-') { 851 return "", fmt.Errorf("invalid filter %v, must start with + or -", filter) 852 } 853 segs := strings.Split(filter[1:], "/") 854 unsupportedOps := [...]string{"?", "*"} 855 for _, seg := range segs { 856 if seg != "**" { 857 for _, op := range unsupportedOps { 858 if strings.Contains(seg, op) { 859 return "", fmt.Errorf("invalid filter %v, operator %v not supported. If you want to have this operator supported, consider filing an issue.", filter, op) 860 } 861 } 862 } 863 } 864 865 return strings.TrimRight(filepath.FromSlash(filter), "/"), nil 866 } 867 868 func (o *Options) set(name string, value interface{}, seen map[string]struct{}) OptionResult { 869 // Flatten the name in case we get options with a hierarchy. 870 split := strings.Split(name, ".") 871 name = split[len(split)-1] 872 873 result := OptionResult{Name: name, Value: value} 874 if _, ok := seen[name]; ok { 875 result.parseErrorf("duplicate configuration for %s", name) 876 } 877 seen[name] = struct{}{} 878 879 switch name { 880 case "env": 881 menv, ok := value.(map[string]interface{}) 882 if !ok { 883 result.parseErrorf("invalid type %T, expect map", value) 884 break 885 } 886 if o.Env == nil { 887 o.Env = make(map[string]string) 888 } 889 for k, v := range menv { 890 o.Env[k] = fmt.Sprint(v) 891 } 892 893 case "buildFlags": 894 // TODO(rfindley): use asStringSlice. 895 iflags, ok := value.([]interface{}) 896 if !ok { 897 result.parseErrorf("invalid type %T, expect list", value) 898 break 899 } 900 flags := make([]string, 0, len(iflags)) 901 for _, flag := range iflags { 902 flags = append(flags, fmt.Sprintf("%s", flag)) 903 } 904 o.BuildFlags = flags 905 906 case "directoryFilters": 907 // TODO(rfindley): use asStringSlice. 908 ifilters, ok := value.([]interface{}) 909 if !ok { 910 result.parseErrorf("invalid type %T, expect list", value) 911 break 912 } 913 var filters []string 914 for _, ifilter := range ifilters { 915 filter, err := validateDirectoryFilter(fmt.Sprintf("%v", ifilter)) 916 if err != nil { 917 result.parseErrorf("%v", err) 918 return result 919 } 920 filters = append(filters, strings.TrimRight(filepath.FromSlash(filter), "/")) 921 } 922 o.DirectoryFilters = filters 923 924 case "memoryMode": 925 result.deprecated("") 926 case "completionDocumentation": 927 result.setBool(&o.CompletionDocumentation) 928 case "usePlaceholders": 929 result.setBool(&o.UsePlaceholders) 930 case "deepCompletion": 931 result.setBool(&o.DeepCompletion) 932 case "completeUnimported": 933 result.setBool(&o.CompleteUnimported) 934 case "completionBudget": 935 result.setDuration(&o.CompletionBudget) 936 case "matcher": 937 if s, ok := result.asOneOf( 938 string(Fuzzy), 939 string(CaseSensitive), 940 string(CaseInsensitive), 941 ); ok { 942 o.Matcher = Matcher(s) 943 } 944 945 case "symbolMatcher": 946 if s, ok := result.asOneOf( 947 string(SymbolFuzzy), 948 string(SymbolFastFuzzy), 949 string(SymbolCaseInsensitive), 950 string(SymbolCaseSensitive), 951 ); ok { 952 o.SymbolMatcher = SymbolMatcher(s) 953 } 954 955 case "symbolStyle": 956 if s, ok := result.asOneOf( 957 string(FullyQualifiedSymbols), 958 string(PackageQualifiedSymbols), 959 string(DynamicSymbols), 960 ); ok { 961 o.SymbolStyle = SymbolStyle(s) 962 } 963 964 case "symbolScope": 965 if s, ok := result.asOneOf( 966 string(WorkspaceSymbolScope), 967 string(AllSymbolScope), 968 ); ok { 969 o.SymbolScope = SymbolScope(s) 970 } 971 972 case "hoverKind": 973 if s, ok := result.asOneOf( 974 string(NoDocumentation), 975 string(SingleLine), 976 string(SynopsisDocumentation), 977 string(FullDocumentation), 978 string(Structured), 979 ); ok { 980 o.HoverKind = HoverKind(s) 981 } 982 983 case "linkTarget": 984 result.setString(&o.LinkTarget) 985 986 case "linksInHover": 987 result.setBool(&o.LinksInHover) 988 989 case "importShortcut": 990 if s, ok := result.asOneOf(string(BothShortcuts), string(LinkShortcut), string(DefinitionShortcut)); ok { 991 o.ImportShortcut = ImportShortcut(s) 992 } 993 994 case "analyses": 995 result.setBoolMap(&o.Analyses) 996 997 case "hints": 998 result.setBoolMap(&o.Hints) 999 1000 case "annotations": 1001 result.setAnnotationMap(&o.Annotations) 1002 1003 case "vulncheck": 1004 if s, ok := result.asOneOf( 1005 string(ModeVulncheckOff), 1006 string(ModeVulncheckImports), 1007 ); ok { 1008 o.Vulncheck = VulncheckMode(s) 1009 } 1010 1011 case "codelenses", "codelens": 1012 var lensOverrides map[string]bool 1013 result.setBoolMap(&lensOverrides) 1014 if result.Error == nil { 1015 if o.Codelenses == nil { 1016 o.Codelenses = make(map[string]bool) 1017 } 1018 for lens, enabled := range lensOverrides { 1019 o.Codelenses[lens] = enabled 1020 } 1021 } 1022 1023 // codelens is deprecated, but still works for now. 1024 // TODO(rstambler): Remove this for the gopls/v0.7.0 release. 1025 if name == "codelens" { 1026 result.deprecated("codelenses") 1027 } 1028 1029 case "staticcheck": 1030 if v, ok := result.asBool(); ok { 1031 o.Staticcheck = v 1032 if v && !o.StaticcheckSupported { 1033 result.Error = fmt.Errorf("applying setting %q: staticcheck is not supported at %s;"+ 1034 " rebuild gopls with a more recent version of Go", result.Name, runtime.Version()) 1035 } 1036 } 1037 1038 case "local": 1039 result.setString(&o.Local) 1040 1041 case "verboseOutput": 1042 result.setBool(&o.VerboseOutput) 1043 1044 case "verboseWorkDoneProgress": 1045 result.setBool(&o.VerboseWorkDoneProgress) 1046 1047 case "tempModFile": 1048 result.deprecated("") 1049 1050 case "showBugReports": 1051 result.setBool(&o.ShowBugReports) 1052 1053 case "gofumpt": 1054 if v, ok := result.asBool(); ok { 1055 o.Gofumpt = v 1056 if v && o.GofumptFormat == nil { 1057 result.Error = fmt.Errorf("applying setting %q: gofumpt is not supported at %s;"+ 1058 " rebuild gopls with a more recent version of Go", result.Name, runtime.Version()) 1059 } 1060 } 1061 case "completeFunctionCalls": 1062 result.setBool(&o.CompleteFunctionCalls) 1063 1064 case "semanticTokens": 1065 result.setBool(&o.SemanticTokens) 1066 1067 case "noSemanticString": 1068 result.setBool(&o.NoSemanticString) 1069 1070 case "noSemanticNumber": 1071 result.setBool(&o.NoSemanticNumber) 1072 1073 case "expandWorkspaceToModule": 1074 // See golang/go#63536: we can consider deprecating 1075 // expandWorkspaceToModule, but probably need to change the default 1076 // behavior in that case to *not* expand to the module. 1077 result.setBool(&o.ExpandWorkspaceToModule) 1078 1079 case "experimentalPostfixCompletions": 1080 result.setBool(&o.ExperimentalPostfixCompletions) 1081 1082 case "experimentalWorkspaceModule": 1083 result.deprecated("") 1084 1085 case "experimentalTemplateSupport": // TODO(pjw): remove after June 2022 1086 result.deprecated("") 1087 1088 case "templateExtensions": 1089 if iexts, ok := value.([]interface{}); ok { 1090 ans := []string{} 1091 for _, x := range iexts { 1092 ans = append(ans, fmt.Sprint(x)) 1093 } 1094 o.TemplateExtensions = ans 1095 break 1096 } 1097 if value == nil { 1098 o.TemplateExtensions = nil 1099 break 1100 } 1101 result.parseErrorf("unexpected type %T not []string", value) 1102 1103 case "experimentalDiagnosticsDelay": 1104 result.deprecated("diagnosticsDelay") 1105 1106 case "diagnosticsDelay": 1107 result.setDuration(&o.DiagnosticsDelay) 1108 1109 case "diagnosticsTrigger": 1110 if s, ok := result.asOneOf( 1111 string(DiagnosticsOnEdit), 1112 string(DiagnosticsOnSave), 1113 ); ok { 1114 o.DiagnosticsTrigger = DiagnosticsTrigger(s) 1115 } 1116 1117 case "analysisProgressReporting": 1118 result.setBool(&o.AnalysisProgressReporting) 1119 1120 case "experimentalWatchedFileDelay": 1121 result.deprecated("") 1122 1123 case "experimentalPackageCacheKey": 1124 result.deprecated("") 1125 1126 case "allowModfileModifications": 1127 result.softErrorf("gopls setting \"allowModfileModifications\" is deprecated.\nPlease comment on https://go.dev/issue/65546 if this impacts your workflow.") 1128 result.setBool(&o.AllowModfileModifications) 1129 1130 case "allowImplicitNetworkAccess": 1131 result.setBool(&o.AllowImplicitNetworkAccess) 1132 1133 case "experimentalUseInvalidMetadata": 1134 result.deprecated("") 1135 1136 case "standaloneTags": 1137 result.setStringSlice(&o.StandaloneTags) 1138 1139 case "allExperiments": 1140 // This setting should be handled before all of the other options are 1141 // processed, so do nothing here. 1142 1143 case "newDiff": 1144 result.deprecated("") 1145 1146 case "subdirWatchPatterns": 1147 if s, ok := result.asOneOf( 1148 string(SubdirWatchPatternsOn), 1149 string(SubdirWatchPatternsOff), 1150 string(SubdirWatchPatternsAuto), 1151 ); ok { 1152 o.SubdirWatchPatterns = SubdirWatchPatterns(s) 1153 } 1154 1155 case "reportAnalysisProgressAfter": 1156 result.setDuration(&o.ReportAnalysisProgressAfter) 1157 1158 case "telemetryPrompt": 1159 result.setBool(&o.TelemetryPrompt) 1160 1161 case "linkifyShowMessage": 1162 result.setBool(&o.LinkifyShowMessage) 1163 1164 case "includeReplaceInWorkspace": 1165 result.setBool(&o.IncludeReplaceInWorkspace) 1166 1167 case "zeroConfig": 1168 result.setBool(&o.ZeroConfig) 1169 1170 // Replaced settings. 1171 case "experimentalDisabledAnalyses": 1172 result.deprecated("analyses") 1173 1174 case "disableDeepCompletion": 1175 result.deprecated("deepCompletion") 1176 1177 case "disableFuzzyMatching": 1178 result.deprecated("fuzzyMatching") 1179 1180 case "wantCompletionDocumentation": 1181 result.deprecated("completionDocumentation") 1182 1183 case "wantUnimportedCompletions": 1184 result.deprecated("completeUnimported") 1185 1186 case "fuzzyMatching": 1187 result.deprecated("matcher") 1188 1189 case "caseSensitiveCompletion": 1190 result.deprecated("matcher") 1191 1192 // Deprecated settings. 1193 case "wantSuggestedFixes": 1194 result.deprecated("") 1195 1196 case "noIncrementalSync": 1197 result.deprecated("") 1198 1199 case "watchFileChanges": 1200 result.deprecated("") 1201 1202 case "go-diff": 1203 result.deprecated("") 1204 1205 default: 1206 result.unexpected() 1207 } 1208 return result 1209 } 1210 1211 // parseErrorf reports an error parsing the current configuration value. 1212 func (r *OptionResult) parseErrorf(msg string, values ...interface{}) { 1213 if false { 1214 _ = fmt.Sprintf(msg, values...) // this causes vet to check this like printf 1215 } 1216 prefix := fmt.Sprintf("parsing setting %q: ", r.Name) 1217 r.Error = fmt.Errorf(prefix+msg, values...) 1218 } 1219 1220 // A SoftError is an error that does not affect the functionality of gopls. 1221 type SoftError struct { 1222 msg string 1223 } 1224 1225 func (e *SoftError) Error() string { 1226 return e.msg 1227 } 1228 1229 // deprecated reports the current setting as deprecated. If 'replacement' is 1230 // non-nil, it is suggested to the user. 1231 func (r *OptionResult) deprecated(replacement string) { 1232 msg := fmt.Sprintf("gopls setting %q is deprecated", r.Name) 1233 if replacement != "" { 1234 msg = fmt.Sprintf("%s, use %q instead", msg, replacement) 1235 } 1236 r.Error = &SoftError{msg} 1237 } 1238 1239 // softErrorf reports a soft error related to the current option. 1240 func (r *OptionResult) softErrorf(format string, args ...any) { 1241 r.Error = &SoftError{fmt.Sprintf(format, args...)} 1242 } 1243 1244 // unexpected reports that the current setting is not known to gopls. 1245 func (r *OptionResult) unexpected() { 1246 r.Error = fmt.Errorf("unexpected gopls setting %q", r.Name) 1247 } 1248 1249 func (r *OptionResult) asBool() (bool, bool) { 1250 b, ok := r.Value.(bool) 1251 if !ok { 1252 r.parseErrorf("invalid type %T, expect bool", r.Value) 1253 return false, false 1254 } 1255 return b, true 1256 } 1257 1258 func (r *OptionResult) setBool(b *bool) { 1259 if v, ok := r.asBool(); ok { 1260 *b = v 1261 } 1262 } 1263 1264 func (r *OptionResult) setDuration(d *time.Duration) { 1265 if v, ok := r.asString(); ok { 1266 parsed, err := time.ParseDuration(v) 1267 if err != nil { 1268 r.parseErrorf("failed to parse duration %q: %v", v, err) 1269 return 1270 } 1271 *d = parsed 1272 } 1273 } 1274 1275 func (r *OptionResult) setBoolMap(bm *map[string]bool) { 1276 m := r.asBoolMap() 1277 *bm = m 1278 } 1279 1280 func (r *OptionResult) setAnnotationMap(bm *map[Annotation]bool) { 1281 all := r.asBoolMap() 1282 if all == nil { 1283 return 1284 } 1285 // Default to everything enabled by default. 1286 m := make(map[Annotation]bool) 1287 for k, enabled := range all { 1288 a, err := asOneOf( 1289 k, 1290 string(Nil), 1291 string(Escape), 1292 string(Inline), 1293 string(Bounds), 1294 ) 1295 if err != nil { 1296 // In case of an error, process any legacy values. 1297 switch k { 1298 case "noEscape": 1299 m[Escape] = false 1300 r.parseErrorf(`"noEscape" is deprecated, set "Escape: false" instead`) 1301 case "noNilcheck": 1302 m[Nil] = false 1303 r.parseErrorf(`"noNilcheck" is deprecated, set "Nil: false" instead`) 1304 case "noInline": 1305 m[Inline] = false 1306 r.parseErrorf(`"noInline" is deprecated, set "Inline: false" instead`) 1307 case "noBounds": 1308 m[Bounds] = false 1309 r.parseErrorf(`"noBounds" is deprecated, set "Bounds: false" instead`) 1310 default: 1311 r.parseErrorf("%v", err) 1312 } 1313 continue 1314 } 1315 m[Annotation(a)] = enabled 1316 } 1317 *bm = m 1318 } 1319 1320 func (r *OptionResult) asBoolMap() map[string]bool { 1321 all, ok := r.Value.(map[string]interface{}) 1322 if !ok { 1323 r.parseErrorf("invalid type %T for map[string]bool option", r.Value) 1324 return nil 1325 } 1326 m := make(map[string]bool) 1327 for a, enabled := range all { 1328 if e, ok := enabled.(bool); ok { 1329 m[a] = e 1330 } else { 1331 r.parseErrorf("invalid type %T for map key %q", enabled, a) 1332 return m 1333 } 1334 } 1335 return m 1336 } 1337 1338 func (r *OptionResult) asString() (string, bool) { 1339 b, ok := r.Value.(string) 1340 if !ok { 1341 r.parseErrorf("invalid type %T, expect string", r.Value) 1342 return "", false 1343 } 1344 return b, true 1345 } 1346 1347 func (r *OptionResult) asStringSlice() ([]string, bool) { 1348 iList, ok := r.Value.([]interface{}) 1349 if !ok { 1350 r.parseErrorf("invalid type %T, expect list", r.Value) 1351 return nil, false 1352 } 1353 var list []string 1354 for _, elem := range iList { 1355 s, ok := elem.(string) 1356 if !ok { 1357 r.parseErrorf("invalid element type %T, expect string", elem) 1358 return nil, false 1359 } 1360 list = append(list, s) 1361 } 1362 return list, true 1363 } 1364 1365 func (r *OptionResult) asOneOf(options ...string) (string, bool) { 1366 s, ok := r.asString() 1367 if !ok { 1368 return "", false 1369 } 1370 s, err := asOneOf(s, options...) 1371 if err != nil { 1372 r.parseErrorf("%v", err) 1373 } 1374 return s, err == nil 1375 } 1376 1377 func asOneOf(str string, options ...string) (string, error) { 1378 lower := strings.ToLower(str) 1379 for _, opt := range options { 1380 if strings.ToLower(opt) == lower { 1381 return opt, nil 1382 } 1383 } 1384 return "", fmt.Errorf("invalid option %q for enum", str) 1385 } 1386 1387 func (r *OptionResult) setString(s *string) { 1388 if v, ok := r.asString(); ok { 1389 *s = v 1390 } 1391 } 1392 1393 func (r *OptionResult) setStringSlice(s *[]string) { 1394 if v, ok := r.asStringSlice(); ok { 1395 *s = v 1396 } 1397 } 1398 1399 func analyzers() map[string]*Analyzer { 1400 return map[string]*Analyzer{ 1401 // The traditional vet suite: 1402 appends.Analyzer.Name: {Analyzer: appends.Analyzer, Enabled: true}, 1403 asmdecl.Analyzer.Name: {Analyzer: asmdecl.Analyzer, Enabled: true}, 1404 assign.Analyzer.Name: {Analyzer: assign.Analyzer, Enabled: true}, 1405 atomic.Analyzer.Name: {Analyzer: atomic.Analyzer, Enabled: true}, 1406 bools.Analyzer.Name: {Analyzer: bools.Analyzer, Enabled: true}, 1407 buildtag.Analyzer.Name: {Analyzer: buildtag.Analyzer, Enabled: true}, 1408 cgocall.Analyzer.Name: {Analyzer: cgocall.Analyzer, Enabled: true}, 1409 composite.Analyzer.Name: {Analyzer: composite.Analyzer, Enabled: true}, 1410 copylock.Analyzer.Name: {Analyzer: copylock.Analyzer, Enabled: true}, 1411 defers.Analyzer.Name: {Analyzer: defers.Analyzer, Enabled: true}, 1412 deprecated.Analyzer.Name: { 1413 Analyzer: deprecated.Analyzer, 1414 Enabled: true, 1415 Severity: protocol.SeverityHint, 1416 Tag: []protocol.DiagnosticTag{protocol.Deprecated}, 1417 }, 1418 directive.Analyzer.Name: {Analyzer: directive.Analyzer, Enabled: true}, 1419 errorsas.Analyzer.Name: {Analyzer: errorsas.Analyzer, Enabled: true}, 1420 httpresponse.Analyzer.Name: {Analyzer: httpresponse.Analyzer, Enabled: true}, 1421 ifaceassert.Analyzer.Name: {Analyzer: ifaceassert.Analyzer, Enabled: true}, 1422 loopclosure.Analyzer.Name: {Analyzer: loopclosure.Analyzer, Enabled: true}, 1423 lostcancel.Analyzer.Name: {Analyzer: lostcancel.Analyzer, Enabled: true}, 1424 nilfunc.Analyzer.Name: {Analyzer: nilfunc.Analyzer, Enabled: true}, 1425 printf.Analyzer.Name: {Analyzer: printf.Analyzer, Enabled: true}, 1426 shift.Analyzer.Name: {Analyzer: shift.Analyzer, Enabled: true}, 1427 slog.Analyzer.Name: {Analyzer: slog.Analyzer, Enabled: true}, 1428 stdmethods.Analyzer.Name: {Analyzer: stdmethods.Analyzer, Enabled: true}, 1429 stringintconv.Analyzer.Name: {Analyzer: stringintconv.Analyzer, Enabled: true}, 1430 structtag.Analyzer.Name: {Analyzer: structtag.Analyzer, Enabled: true}, 1431 tests.Analyzer.Name: {Analyzer: tests.Analyzer, Enabled: true}, 1432 unmarshal.Analyzer.Name: {Analyzer: unmarshal.Analyzer, Enabled: true}, 1433 unreachable.Analyzer.Name: {Analyzer: unreachable.Analyzer, Enabled: true}, 1434 unsafeptr.Analyzer.Name: {Analyzer: unsafeptr.Analyzer, Enabled: true}, 1435 unusedresult.Analyzer.Name: {Analyzer: unusedresult.Analyzer, Enabled: true}, 1436 1437 // Non-vet analyzers: 1438 atomicalign.Analyzer.Name: {Analyzer: atomicalign.Analyzer, Enabled: true}, 1439 deepequalerrors.Analyzer.Name: {Analyzer: deepequalerrors.Analyzer, Enabled: true}, 1440 fieldalignment.Analyzer.Name: {Analyzer: fieldalignment.Analyzer, Enabled: false}, 1441 nilness.Analyzer.Name: {Analyzer: nilness.Analyzer, Enabled: true}, 1442 shadow.Analyzer.Name: {Analyzer: shadow.Analyzer, Enabled: false}, 1443 sortslice.Analyzer.Name: {Analyzer: sortslice.Analyzer, Enabled: true}, 1444 testinggoroutine.Analyzer.Name: {Analyzer: testinggoroutine.Analyzer, Enabled: true}, 1445 unusedparams.Analyzer.Name: {Analyzer: unusedparams.Analyzer, Enabled: true}, 1446 unusedwrite.Analyzer.Name: {Analyzer: unusedwrite.Analyzer, Enabled: false}, 1447 useany.Analyzer.Name: {Analyzer: useany.Analyzer, Enabled: false}, 1448 infertypeargs.Analyzer.Name: { 1449 Analyzer: infertypeargs.Analyzer, 1450 Enabled: true, 1451 Severity: protocol.SeverityHint, 1452 }, 1453 timeformat.Analyzer.Name: {Analyzer: timeformat.Analyzer, Enabled: true}, 1454 embeddirective.Analyzer.Name: {Analyzer: embeddirective.Analyzer, Enabled: true}, 1455 1456 // gofmt -s suite: 1457 simplifycompositelit.Analyzer.Name: { 1458 Analyzer: simplifycompositelit.Analyzer, 1459 Enabled: true, 1460 ActionKinds: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, 1461 }, 1462 simplifyrange.Analyzer.Name: { 1463 Analyzer: simplifyrange.Analyzer, 1464 Enabled: true, 1465 ActionKinds: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, 1466 }, 1467 simplifyslice.Analyzer.Name: { 1468 Analyzer: simplifyslice.Analyzer, 1469 Enabled: true, 1470 ActionKinds: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, 1471 }, 1472 1473 // Type error analyzers. 1474 // These analyzers enrich go/types errors with suggested fixes. 1475 fillreturns.Analyzer.Name: {Analyzer: fillreturns.Analyzer, Enabled: true}, 1476 nonewvars.Analyzer.Name: {Analyzer: nonewvars.Analyzer, Enabled: true}, 1477 noresultvalues.Analyzer.Name: {Analyzer: noresultvalues.Analyzer, Enabled: true}, 1478 stubmethods.Analyzer.Name: {Analyzer: stubmethods.Analyzer, Enabled: true}, 1479 undeclaredname.Analyzer.Name: {Analyzer: undeclaredname.Analyzer, Enabled: true}, 1480 // TODO(rfindley): why isn't the 'unusedvariable' analyzer enabled, if it 1481 // is only enhancing type errors with suggested fixes? 1482 // 1483 // In particular, enabling this analyzer could cause unused variables to be 1484 // greyed out, (due to the 'deletions only' fix). That seems like a nice UI 1485 // feature. 1486 unusedvariable.Analyzer.Name: {Analyzer: unusedvariable.Analyzer, Enabled: false}, 1487 } 1488 } 1489 1490 func urlRegexp() *regexp.Regexp { 1491 // Ensure links are matched as full words, not anywhere. 1492 re := regexp.MustCompile(`\b(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?\b`) 1493 re.Longest() 1494 return re 1495 }