mirror of
https://github.com/golang/go
synced 2024-11-18 13:04:46 -07:00
17fd2f27a9
It's pretty easy to add an LSP benchmark using the regtests, provided we run the benchmark ourselves from inside the runner. Do this for workspace symbols to start, though we should add several of these. Also fix some error messages when setting options. Change-Id: Iab134018edec8837e90a0a926ec2e73addf95bb3 Reviewed-on: https://go-review.googlesource.com/c/tools/+/250798 Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
792 lines
23 KiB
Go
792 lines
23 KiB
Go
// Copyright 2019 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package source
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"regexp"
|
|
"time"
|
|
|
|
"golang.org/x/tools/go/analysis"
|
|
"golang.org/x/tools/go/analysis/passes/asmdecl"
|
|
"golang.org/x/tools/go/analysis/passes/assign"
|
|
"golang.org/x/tools/go/analysis/passes/atomic"
|
|
"golang.org/x/tools/go/analysis/passes/atomicalign"
|
|
"golang.org/x/tools/go/analysis/passes/bools"
|
|
"golang.org/x/tools/go/analysis/passes/buildtag"
|
|
"golang.org/x/tools/go/analysis/passes/cgocall"
|
|
"golang.org/x/tools/go/analysis/passes/composite"
|
|
"golang.org/x/tools/go/analysis/passes/copylock"
|
|
"golang.org/x/tools/go/analysis/passes/deepequalerrors"
|
|
"golang.org/x/tools/go/analysis/passes/errorsas"
|
|
"golang.org/x/tools/go/analysis/passes/httpresponse"
|
|
"golang.org/x/tools/go/analysis/passes/loopclosure"
|
|
"golang.org/x/tools/go/analysis/passes/lostcancel"
|
|
"golang.org/x/tools/go/analysis/passes/nilfunc"
|
|
"golang.org/x/tools/go/analysis/passes/printf"
|
|
"golang.org/x/tools/go/analysis/passes/shift"
|
|
"golang.org/x/tools/go/analysis/passes/sortslice"
|
|
"golang.org/x/tools/go/analysis/passes/stdmethods"
|
|
"golang.org/x/tools/go/analysis/passes/structtag"
|
|
"golang.org/x/tools/go/analysis/passes/testinggoroutine"
|
|
"golang.org/x/tools/go/analysis/passes/tests"
|
|
"golang.org/x/tools/go/analysis/passes/unmarshal"
|
|
"golang.org/x/tools/go/analysis/passes/unreachable"
|
|
"golang.org/x/tools/go/analysis/passes/unsafeptr"
|
|
"golang.org/x/tools/go/analysis/passes/unusedresult"
|
|
"golang.org/x/tools/internal/lsp/analysis/fillreturns"
|
|
"golang.org/x/tools/internal/lsp/analysis/fillstruct"
|
|
"golang.org/x/tools/internal/lsp/analysis/nonewvars"
|
|
"golang.org/x/tools/internal/lsp/analysis/noresultvalues"
|
|
"golang.org/x/tools/internal/lsp/analysis/simplifycompositelit"
|
|
"golang.org/x/tools/internal/lsp/analysis/simplifyrange"
|
|
"golang.org/x/tools/internal/lsp/analysis/simplifyslice"
|
|
"golang.org/x/tools/internal/lsp/analysis/undeclaredname"
|
|
"golang.org/x/tools/internal/lsp/analysis/unusedparams"
|
|
"golang.org/x/tools/internal/lsp/diff"
|
|
"golang.org/x/tools/internal/lsp/diff/myers"
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
|
errors "golang.org/x/xerrors"
|
|
)
|
|
|
|
// DefaultOptions is the options that are used for Gopls execution independent
|
|
// of any externally provided configuration (LSP initialization, command
|
|
// invokation, etc.).
|
|
func DefaultOptions() Options {
|
|
var commands []string
|
|
for _, c := range Commands {
|
|
commands = append(commands, c.Name)
|
|
}
|
|
return Options{
|
|
ClientOptions: ClientOptions{
|
|
InsertTextFormat: protocol.PlainTextTextFormat,
|
|
PreferredContentFormat: protocol.Markdown,
|
|
ConfigurationSupported: true,
|
|
DynamicConfigurationSupported: true,
|
|
DynamicWatchedFilesSupported: true,
|
|
LineFoldingOnly: false,
|
|
HierarchicalDocumentSymbolSupport: true,
|
|
},
|
|
ServerOptions: ServerOptions{
|
|
SupportedCodeActions: map[FileKind]map[protocol.CodeActionKind]bool{
|
|
Go: {
|
|
protocol.SourceFixAll: true,
|
|
protocol.SourceOrganizeImports: true,
|
|
protocol.QuickFix: true,
|
|
protocol.RefactorRewrite: true,
|
|
protocol.RefactorExtract: true,
|
|
},
|
|
Mod: {
|
|
protocol.SourceOrganizeImports: true,
|
|
},
|
|
Sum: {},
|
|
},
|
|
SupportedCommands: commands,
|
|
},
|
|
UserOptions: UserOptions{
|
|
Env: os.Environ(),
|
|
HoverKind: FullDocumentation,
|
|
LinkTarget: "pkg.go.dev",
|
|
LinksInHover: true,
|
|
Matcher: Fuzzy,
|
|
SymbolMatcher: SymbolFuzzy,
|
|
DeepCompletion: true,
|
|
UnimportedCompletion: true,
|
|
CompletionDocumentation: true,
|
|
EnabledCodeLens: map[string]bool{
|
|
CommandGenerate.Name: true,
|
|
CommandUpgradeDependency.Name: true,
|
|
CommandRegenerateCgo.Name: true,
|
|
CommandToggleDetails.Name: false,
|
|
},
|
|
ExpandWorkspaceToModule: true,
|
|
},
|
|
DebuggingOptions: DebuggingOptions{
|
|
CompletionBudget: 100 * time.Millisecond,
|
|
LiteralCompletions: true,
|
|
},
|
|
ExperimentalOptions: ExperimentalOptions{
|
|
TempModfile: true,
|
|
},
|
|
Hooks: Hooks{
|
|
ComputeEdits: myers.ComputeEdits,
|
|
URLRegexp: urlRegexp(),
|
|
DefaultAnalyzers: defaultAnalyzers(),
|
|
TypeErrorAnalyzers: typeErrorAnalyzers(),
|
|
ConvenienceAnalyzers: convenienceAnalyzers(),
|
|
GoDiff: true,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Options holds various configuration that affects Gopls execution, organized
|
|
// by the nature or origin of the settings.
|
|
type Options struct {
|
|
ClientOptions
|
|
ServerOptions
|
|
UserOptions
|
|
DebuggingOptions
|
|
ExperimentalOptions
|
|
Hooks
|
|
}
|
|
|
|
// ClientOptions holds LSP-specific configuration that is provided by the
|
|
// client.
|
|
type ClientOptions struct {
|
|
InsertTextFormat protocol.InsertTextFormat
|
|
ConfigurationSupported bool
|
|
DynamicConfigurationSupported bool
|
|
DynamicWatchedFilesSupported bool
|
|
PreferredContentFormat protocol.MarkupKind
|
|
LineFoldingOnly bool
|
|
HierarchicalDocumentSymbolSupport bool
|
|
}
|
|
|
|
// ServerOptions holds LSP-specific configuration that is provided by the
|
|
// server.
|
|
type ServerOptions struct {
|
|
SupportedCodeActions map[FileKind]map[protocol.CodeActionKind]bool
|
|
SupportedCommands []string
|
|
}
|
|
|
|
// UserOptions holds custom Gopls configuration (not part of the LSP) that is
|
|
// modified by the client.
|
|
type UserOptions struct {
|
|
// Env is the current set of environment overrides on this view.
|
|
Env []string
|
|
|
|
// BuildFlags is used to adjust the build flags applied to the view.
|
|
BuildFlags []string
|
|
|
|
// HoverKind specifies the format of the content for hover requests.
|
|
HoverKind HoverKind
|
|
|
|
// UserEnabledAnalyses specifies analyses that the user would like to enable
|
|
// or disable. A map of the names of analysis passes that should be
|
|
// enabled/disabled. A full list of analyzers that gopls uses can be found
|
|
// [here](analyzers.md).
|
|
//
|
|
// Example Usage:
|
|
// ...
|
|
// "analyses": {
|
|
// "unreachable": false, // Disable the unreachable analyzer.
|
|
// "unusedparams": true // Enable the unusedparams analyzer.
|
|
// }
|
|
UserEnabledAnalyses map[string]bool
|
|
|
|
// EnabledCodeLens specifies which codelens are enabled, keyed by the gopls
|
|
// command that they provide.
|
|
EnabledCodeLens map[string]bool
|
|
|
|
// StaticCheck enables additional analyses from staticcheck.io.
|
|
StaticCheck bool
|
|
|
|
// LinkTarget is the website used for documentation. If empty, no link is
|
|
// provided.
|
|
LinkTarget string
|
|
|
|
// LinksInHover toggles the presence of links to documentation in hover.
|
|
LinksInHover bool
|
|
|
|
// ImportShortcut specifies whether import statements should link to
|
|
// documentation or go to definitions. The default is both.
|
|
ImportShortcut ImportShortcut
|
|
|
|
// LocalPrefix is used to specify goimports's -local behavior.
|
|
LocalPrefix string
|
|
|
|
// Matcher specifies the type of matcher to use for completion requests.
|
|
Matcher Matcher
|
|
|
|
// SymbolMatcher specifies the type of matcher to use for symbol requests.
|
|
SymbolMatcher SymbolMatcher
|
|
|
|
// SymbolStyle specifies what style of symbols to return in symbol requests
|
|
// (package qualified, fully qualified, etc).
|
|
SymbolStyle SymbolStyle
|
|
|
|
// DeepCompletion allows completion to perform nested searches through
|
|
// possible candidates.
|
|
DeepCompletion bool
|
|
|
|
// UnimportedCompletion enables completion for unimported packages.
|
|
UnimportedCompletion bool
|
|
|
|
// CompletionDocumentation returns additional documentation with completion
|
|
// requests.
|
|
CompletionDocumentation bool
|
|
|
|
// Placeholders adds placeholders to parameters and structs in completion
|
|
// results.
|
|
Placeholders bool
|
|
|
|
// Gofumpt indicates if we should run gofumpt formatting.
|
|
Gofumpt bool
|
|
|
|
// ExpandWorkspaceToModule is true if we should expand the scope of the
|
|
// workspace to include the modules containing the workspace folders.
|
|
ExpandWorkspaceToModule bool
|
|
}
|
|
|
|
type ImportShortcut int
|
|
|
|
const (
|
|
Both ImportShortcut = iota
|
|
Link
|
|
Definition
|
|
)
|
|
|
|
func (s ImportShortcut) ShowLinks() bool {
|
|
return s == Both || s == Link
|
|
}
|
|
|
|
func (s ImportShortcut) ShowDefinition() bool {
|
|
return s == Both || s == Definition
|
|
}
|
|
|
|
type completionOptions struct {
|
|
deepCompletion bool
|
|
unimported bool
|
|
documentation bool
|
|
fullDocumentation bool
|
|
placeholders bool
|
|
literal bool
|
|
matcher Matcher
|
|
budget time.Duration
|
|
}
|
|
|
|
// Hooks contains configuration that is provided to the Gopls command by the
|
|
// main package.
|
|
type Hooks struct {
|
|
GoDiff bool
|
|
ComputeEdits diff.ComputeEdits
|
|
URLRegexp *regexp.Regexp
|
|
DefaultAnalyzers map[string]Analyzer
|
|
TypeErrorAnalyzers map[string]Analyzer
|
|
ConvenienceAnalyzers map[string]Analyzer
|
|
GofumptFormat func(ctx context.Context, src []byte) ([]byte, error)
|
|
}
|
|
|
|
func (o Options) AddDefaultAnalyzer(a *analysis.Analyzer) {
|
|
o.DefaultAnalyzers[a.Name] = Analyzer{Analyzer: a, enabled: true}
|
|
}
|
|
|
|
// ExperimentalOptions defines configuration for features under active
|
|
// development. WARNING: This configuration will be changed in the future. It
|
|
// only exists while these features are under development.
|
|
type ExperimentalOptions struct {
|
|
// TempModfile controls the use of the -modfile flag in Go 1.14.
|
|
TempModfile bool
|
|
|
|
// VerboseWorkDoneProgress controls whether the LSP server should send
|
|
// progress reports for all work done outside the scope of an RPC.
|
|
VerboseWorkDoneProgress bool
|
|
|
|
// Annotations suppress various kinds of optimization diagnostics
|
|
// that would be reported by the gc_details command.
|
|
// noNilcheck suppresses display of nilchecks.
|
|
// noEscape suppresses escape choices.
|
|
// noInline suppresses inlining choices.
|
|
// noBounds suppresses bounds checking diagnositcs.
|
|
Annotations map[string]bool
|
|
}
|
|
|
|
// DebuggingOptions should not affect the logical execution of Gopls, but may
|
|
// be altered for debugging purposes.
|
|
type DebuggingOptions struct {
|
|
VerboseOutput bool
|
|
|
|
// CompletionBudget is the soft latency goal for completion requests. Most
|
|
// requests finish in a couple milliseconds, but in some cases deep
|
|
// completions can take much longer. As we use up our budget we
|
|
// dynamically reduce the search scope to ensure we return timely
|
|
// results. Zero means unlimited.
|
|
CompletionBudget time.Duration
|
|
|
|
// LiteralCompletions controls whether literal candidates such as
|
|
// "&someStruct{}" are offered. Tests disable this flag to simplify
|
|
// their expected values.
|
|
LiteralCompletions bool
|
|
}
|
|
|
|
type Matcher int
|
|
|
|
const (
|
|
Fuzzy = Matcher(iota)
|
|
CaseInsensitive
|
|
CaseSensitive
|
|
)
|
|
|
|
type SymbolMatcher int
|
|
|
|
const (
|
|
SymbolFuzzy = SymbolMatcher(iota)
|
|
SymbolCaseInsensitive
|
|
SymbolCaseSensitive
|
|
)
|
|
|
|
type SymbolStyle int
|
|
|
|
const (
|
|
PackageQualifiedSymbols = SymbolStyle(iota)
|
|
FullyQualifiedSymbols
|
|
DynamicSymbols
|
|
)
|
|
|
|
type HoverKind int
|
|
|
|
const (
|
|
SingleLine = HoverKind(iota)
|
|
NoDocumentation
|
|
SynopsisDocumentation
|
|
FullDocumentation
|
|
|
|
// Structured is an experimental setting that returns a structured hover format.
|
|
// This format separates the signature from the documentation, so that the client
|
|
// can do more manipulation of these fields.
|
|
//
|
|
// This should only be used by clients that support this behavior.
|
|
Structured
|
|
)
|
|
|
|
type OptionResults []OptionResult
|
|
|
|
type OptionResult struct {
|
|
Name string
|
|
Value interface{}
|
|
Error error
|
|
|
|
State OptionState
|
|
Replacement string
|
|
}
|
|
|
|
type OptionState int
|
|
|
|
const (
|
|
OptionHandled = OptionState(iota)
|
|
OptionDeprecated
|
|
OptionUnexpected
|
|
)
|
|
|
|
type LinkTarget string
|
|
|
|
func SetOptions(options *Options, opts interface{}) OptionResults {
|
|
var results OptionResults
|
|
switch opts := opts.(type) {
|
|
case nil:
|
|
case map[string]interface{}:
|
|
for name, value := range opts {
|
|
results = append(results, options.set(name, value))
|
|
}
|
|
default:
|
|
results = append(results, OptionResult{
|
|
Value: opts,
|
|
Error: errors.Errorf("Invalid options type %T", opts),
|
|
})
|
|
}
|
|
return results
|
|
}
|
|
|
|
func (o *Options) ForClientCapabilities(caps protocol.ClientCapabilities) {
|
|
// Check if the client supports snippets in completion items.
|
|
if c := caps.TextDocument.Completion; c.CompletionItem.SnippetSupport {
|
|
o.InsertTextFormat = protocol.SnippetTextFormat
|
|
}
|
|
// Check if the client supports configuration messages.
|
|
o.ConfigurationSupported = caps.Workspace.Configuration
|
|
o.DynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration
|
|
o.DynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration
|
|
|
|
// Check which types of content format are supported by this client.
|
|
if hover := caps.TextDocument.Hover; len(hover.ContentFormat) > 0 {
|
|
o.PreferredContentFormat = hover.ContentFormat[0]
|
|
}
|
|
// Check if the client supports only line folding.
|
|
fr := caps.TextDocument.FoldingRange
|
|
o.LineFoldingOnly = fr.LineFoldingOnly
|
|
// Check if the client supports hierarchical document symbols.
|
|
o.HierarchicalDocumentSymbolSupport = caps.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport
|
|
}
|
|
|
|
func (o *Options) set(name string, value interface{}) OptionResult {
|
|
result := OptionResult{Name: name, Value: value}
|
|
switch name {
|
|
case "env":
|
|
menv, ok := value.(map[string]interface{})
|
|
if !ok {
|
|
result.errorf("invalid config gopls.env type %T", value)
|
|
break
|
|
}
|
|
for k, v := range menv {
|
|
o.Env = append(o.Env, fmt.Sprintf("%s=%s", k, v))
|
|
}
|
|
|
|
case "buildFlags":
|
|
iflags, ok := value.([]interface{})
|
|
if !ok {
|
|
result.errorf("invalid config gopls.buildFlags type %T", value)
|
|
break
|
|
}
|
|
flags := make([]string, 0, len(iflags))
|
|
for _, flag := range iflags {
|
|
flags = append(flags, fmt.Sprintf("%s", flag))
|
|
}
|
|
o.BuildFlags = flags
|
|
|
|
case "completionDocumentation":
|
|
result.setBool(&o.CompletionDocumentation)
|
|
case "usePlaceholders":
|
|
result.setBool(&o.Placeholders)
|
|
case "deepCompletion":
|
|
result.setBool(&o.DeepCompletion)
|
|
case "completeUnimported":
|
|
result.setBool(&o.UnimportedCompletion)
|
|
case "completionBudget":
|
|
if v, ok := result.asString(); ok {
|
|
d, err := time.ParseDuration(v)
|
|
if err != nil {
|
|
result.errorf("failed to parse duration %q: %v", v, err)
|
|
break
|
|
}
|
|
o.CompletionBudget = d
|
|
}
|
|
|
|
case "matcher":
|
|
matcher, ok := result.asString()
|
|
if !ok {
|
|
break
|
|
}
|
|
switch matcher {
|
|
case "fuzzy":
|
|
o.Matcher = Fuzzy
|
|
case "caseSensitive":
|
|
o.Matcher = CaseSensitive
|
|
default:
|
|
o.Matcher = CaseInsensitive
|
|
}
|
|
|
|
case "symbolMatcher":
|
|
matcher, ok := result.asString()
|
|
if !ok {
|
|
break
|
|
}
|
|
switch matcher {
|
|
case "fuzzy":
|
|
o.SymbolMatcher = SymbolFuzzy
|
|
case "caseSensitive":
|
|
o.SymbolMatcher = SymbolCaseSensitive
|
|
default:
|
|
o.SymbolMatcher = SymbolCaseInsensitive
|
|
}
|
|
|
|
case "symbolStyle":
|
|
style, ok := result.asString()
|
|
if !ok {
|
|
break
|
|
}
|
|
switch style {
|
|
case "full":
|
|
o.SymbolStyle = FullyQualifiedSymbols
|
|
case "dynamic":
|
|
o.SymbolStyle = DynamicSymbols
|
|
case "package":
|
|
o.SymbolStyle = PackageQualifiedSymbols
|
|
default:
|
|
result.errorf("Unsupported symbol style %q", style)
|
|
}
|
|
|
|
case "hoverKind":
|
|
hoverKind, ok := result.asString()
|
|
if !ok {
|
|
break
|
|
}
|
|
switch hoverKind {
|
|
case "NoDocumentation":
|
|
o.HoverKind = NoDocumentation
|
|
case "SingleLine":
|
|
o.HoverKind = SingleLine
|
|
case "SynopsisDocumentation":
|
|
o.HoverKind = SynopsisDocumentation
|
|
case "FullDocumentation":
|
|
o.HoverKind = FullDocumentation
|
|
case "Structured":
|
|
o.HoverKind = Structured
|
|
default:
|
|
result.errorf("Unsupported hover kind %q", hoverKind)
|
|
}
|
|
|
|
case "linkTarget":
|
|
result.setString(&o.LinkTarget)
|
|
|
|
case "linksInHover":
|
|
result.setBool(&o.LinksInHover)
|
|
|
|
case "importShortcut":
|
|
var s string
|
|
result.setString(&s)
|
|
switch s {
|
|
case "both":
|
|
o.ImportShortcut = Both
|
|
case "link":
|
|
o.ImportShortcut = Link
|
|
case "definition":
|
|
o.ImportShortcut = Definition
|
|
}
|
|
|
|
case "analyses":
|
|
result.setBoolMap(&o.UserEnabledAnalyses)
|
|
|
|
case "annotations":
|
|
result.setBoolMap(&o.Annotations)
|
|
for k := range o.Annotations {
|
|
switch k {
|
|
case "noEscape", "noNilcheck", "noInline", "noBounds":
|
|
continue
|
|
default:
|
|
result.Name += ":" + k // put mistake(s) in the message
|
|
result.State = OptionUnexpected
|
|
}
|
|
}
|
|
|
|
case "codelens":
|
|
var lensOverrides map[string]bool
|
|
result.setBoolMap(&lensOverrides)
|
|
if result.Error == nil {
|
|
if o.EnabledCodeLens == nil {
|
|
o.EnabledCodeLens = make(map[string]bool)
|
|
}
|
|
for lens, enabled := range lensOverrides {
|
|
o.EnabledCodeLens[lens] = enabled
|
|
}
|
|
}
|
|
|
|
case "staticcheck":
|
|
result.setBool(&o.StaticCheck)
|
|
|
|
case "local":
|
|
result.setString(&o.LocalPrefix)
|
|
|
|
case "verboseOutput":
|
|
result.setBool(&o.VerboseOutput)
|
|
|
|
case "verboseWorkDoneProgress":
|
|
result.setBool(&o.VerboseWorkDoneProgress)
|
|
|
|
case "tempModfile":
|
|
result.setBool(&o.TempModfile)
|
|
|
|
case "gofumpt":
|
|
result.setBool(&o.Gofumpt)
|
|
|
|
case "expandWorkspaceToModule":
|
|
result.setBool(&o.ExpandWorkspaceToModule)
|
|
|
|
// Replaced settings.
|
|
case "experimentalDisabledAnalyses":
|
|
result.State = OptionDeprecated
|
|
result.Replacement = "analyses"
|
|
|
|
case "disableDeepCompletion":
|
|
result.State = OptionDeprecated
|
|
result.Replacement = "deepCompletion"
|
|
|
|
case "disableFuzzyMatching":
|
|
result.State = OptionDeprecated
|
|
result.Replacement = "fuzzyMatching"
|
|
|
|
case "wantCompletionDocumentation":
|
|
result.State = OptionDeprecated
|
|
result.Replacement = "completionDocumentation"
|
|
|
|
case "wantUnimportedCompletions":
|
|
result.State = OptionDeprecated
|
|
result.Replacement = "completeUnimported"
|
|
|
|
case "fuzzyMatching":
|
|
result.State = OptionDeprecated
|
|
result.Replacement = "matcher"
|
|
|
|
case "caseSensitiveCompletion":
|
|
result.State = OptionDeprecated
|
|
result.Replacement = "matcher"
|
|
|
|
// Deprecated settings.
|
|
case "wantSuggestedFixes":
|
|
result.State = OptionDeprecated
|
|
|
|
case "noIncrementalSync":
|
|
result.State = OptionDeprecated
|
|
|
|
case "watchFileChanges":
|
|
result.State = OptionDeprecated
|
|
|
|
case "go-diff":
|
|
result.State = OptionDeprecated
|
|
|
|
default:
|
|
result.State = OptionUnexpected
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (r *OptionResult) errorf(msg string, values ...interface{}) {
|
|
r.Error = errors.Errorf(msg, values...)
|
|
}
|
|
|
|
func (r *OptionResult) asBool() (bool, bool) {
|
|
b, ok := r.Value.(bool)
|
|
if !ok {
|
|
r.errorf("Invalid type %T for bool option %q", r.Value, r.Name)
|
|
return false, false
|
|
}
|
|
return b, true
|
|
}
|
|
|
|
func (r *OptionResult) setBool(b *bool) {
|
|
if v, ok := r.asBool(); ok {
|
|
*b = v
|
|
}
|
|
}
|
|
|
|
func (r *OptionResult) setBoolMap(bm *map[string]bool) {
|
|
all, ok := r.Value.(map[string]interface{})
|
|
if !ok {
|
|
r.errorf("Invalid type %T for map[string]interface{} option %q", r.Value, r.Name)
|
|
return
|
|
}
|
|
m := make(map[string]bool)
|
|
for a, enabled := range all {
|
|
if enabled, ok := enabled.(bool); ok {
|
|
m[a] = enabled
|
|
} else {
|
|
r.errorf("Invalid type %d for map key %q in option %q", a, r.Name)
|
|
return
|
|
}
|
|
}
|
|
*bm = m
|
|
}
|
|
|
|
func (r *OptionResult) asString() (string, bool) {
|
|
b, ok := r.Value.(string)
|
|
if !ok {
|
|
r.errorf("Invalid type %T for string option %q", r.Value, r.Name)
|
|
return "", false
|
|
}
|
|
return b, true
|
|
}
|
|
|
|
func (r *OptionResult) setString(s *string) {
|
|
if v, ok := r.asString(); ok {
|
|
*s = v
|
|
}
|
|
}
|
|
|
|
// EnabledAnalyzers returns all of the analyzers enabled for the given
|
|
// snapshot.
|
|
func EnabledAnalyzers(snapshot Snapshot) (analyzers []Analyzer) {
|
|
for _, a := range snapshot.View().Options().DefaultAnalyzers {
|
|
if a.Enabled(snapshot.View()) {
|
|
analyzers = append(analyzers, a)
|
|
}
|
|
}
|
|
for _, a := range snapshot.View().Options().TypeErrorAnalyzers {
|
|
if a.Enabled(snapshot.View()) {
|
|
analyzers = append(analyzers, a)
|
|
}
|
|
}
|
|
for _, a := range snapshot.View().Options().ConvenienceAnalyzers {
|
|
if a.Enabled(snapshot.View()) {
|
|
analyzers = append(analyzers, a)
|
|
}
|
|
}
|
|
return analyzers
|
|
}
|
|
|
|
func typeErrorAnalyzers() map[string]Analyzer {
|
|
return map[string]Analyzer{
|
|
fillreturns.Analyzer.Name: {
|
|
Analyzer: fillreturns.Analyzer,
|
|
FixesError: fillreturns.FixesError,
|
|
HighConfidence: true,
|
|
enabled: true,
|
|
},
|
|
nonewvars.Analyzer.Name: {
|
|
Analyzer: nonewvars.Analyzer,
|
|
FixesError: nonewvars.FixesError,
|
|
enabled: true,
|
|
},
|
|
noresultvalues.Analyzer.Name: {
|
|
Analyzer: noresultvalues.Analyzer,
|
|
FixesError: noresultvalues.FixesError,
|
|
enabled: true,
|
|
},
|
|
undeclaredname.Analyzer.Name: {
|
|
Analyzer: undeclaredname.Analyzer,
|
|
FixesError: undeclaredname.FixesError,
|
|
Command: CommandUndeclaredName,
|
|
enabled: true,
|
|
},
|
|
}
|
|
}
|
|
|
|
func convenienceAnalyzers() map[string]Analyzer {
|
|
return map[string]Analyzer{
|
|
fillstruct.Analyzer.Name: {
|
|
Analyzer: fillstruct.Analyzer,
|
|
Command: CommandFillStruct,
|
|
enabled: true,
|
|
},
|
|
}
|
|
}
|
|
|
|
func defaultAnalyzers() map[string]Analyzer {
|
|
return map[string]Analyzer{
|
|
// The traditional vet suite:
|
|
asmdecl.Analyzer.Name: {Analyzer: asmdecl.Analyzer, enabled: true},
|
|
assign.Analyzer.Name: {Analyzer: assign.Analyzer, enabled: true},
|
|
atomic.Analyzer.Name: {Analyzer: atomic.Analyzer, enabled: true},
|
|
atomicalign.Analyzer.Name: {Analyzer: atomicalign.Analyzer, enabled: true},
|
|
bools.Analyzer.Name: {Analyzer: bools.Analyzer, enabled: true},
|
|
buildtag.Analyzer.Name: {Analyzer: buildtag.Analyzer, enabled: true},
|
|
cgocall.Analyzer.Name: {Analyzer: cgocall.Analyzer, enabled: true},
|
|
composite.Analyzer.Name: {Analyzer: composite.Analyzer, enabled: true},
|
|
copylock.Analyzer.Name: {Analyzer: copylock.Analyzer, enabled: true},
|
|
errorsas.Analyzer.Name: {Analyzer: errorsas.Analyzer, enabled: true},
|
|
httpresponse.Analyzer.Name: {Analyzer: httpresponse.Analyzer, enabled: true},
|
|
loopclosure.Analyzer.Name: {Analyzer: loopclosure.Analyzer, enabled: true},
|
|
lostcancel.Analyzer.Name: {Analyzer: lostcancel.Analyzer, enabled: true},
|
|
nilfunc.Analyzer.Name: {Analyzer: nilfunc.Analyzer, enabled: true},
|
|
printf.Analyzer.Name: {Analyzer: printf.Analyzer, enabled: true},
|
|
shift.Analyzer.Name: {Analyzer: shift.Analyzer, enabled: true},
|
|
stdmethods.Analyzer.Name: {Analyzer: stdmethods.Analyzer, enabled: true},
|
|
structtag.Analyzer.Name: {Analyzer: structtag.Analyzer, enabled: true},
|
|
tests.Analyzer.Name: {Analyzer: tests.Analyzer, enabled: true},
|
|
unmarshal.Analyzer.Name: {Analyzer: unmarshal.Analyzer, enabled: true},
|
|
unreachable.Analyzer.Name: {Analyzer: unreachable.Analyzer, enabled: true},
|
|
unsafeptr.Analyzer.Name: {Analyzer: unsafeptr.Analyzer, enabled: true},
|
|
unusedresult.Analyzer.Name: {Analyzer: unusedresult.Analyzer, enabled: true},
|
|
|
|
// Non-vet analyzers:
|
|
deepequalerrors.Analyzer.Name: {Analyzer: deepequalerrors.Analyzer, enabled: true},
|
|
sortslice.Analyzer.Name: {Analyzer: sortslice.Analyzer, enabled: true},
|
|
testinggoroutine.Analyzer.Name: {Analyzer: testinggoroutine.Analyzer, enabled: true},
|
|
unusedparams.Analyzer.Name: {Analyzer: unusedparams.Analyzer, enabled: false},
|
|
|
|
// gofmt -s suite:
|
|
simplifycompositelit.Analyzer.Name: {Analyzer: simplifycompositelit.Analyzer, enabled: true, HighConfidence: true},
|
|
simplifyrange.Analyzer.Name: {Analyzer: simplifyrange.Analyzer, enabled: true, HighConfidence: true},
|
|
simplifyslice.Analyzer.Name: {Analyzer: simplifyslice.Analyzer, enabled: true, HighConfidence: true},
|
|
}
|
|
}
|
|
|
|
func urlRegexp() *regexp.Regexp {
|
|
// Ensure links are matched as full words, not anywhere.
|
|
re := regexp.MustCompile(`\b(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?\b`)
|
|
re.Longest()
|
|
return re
|
|
}
|