mirror of
https://github.com/golang/go
synced 2024-11-18 14:54:40 -07:00
412b8bda49
To manually collect cache entries, we need to know when a snapshot is idle. Add a reference count in the form of a WaitGroup and keep track of its uses. The pattern is that any time a snapshot is returned, it comes with a release function that decrements the ref count. Almost all uses of a snapshot originate in a user-facing request, handled in beginFileRequest. There it's mostly an exercise in passing Snapshots around instead of Views. In the other places I took the path of least resistance. For file modifications I tried to minimize the amount of code that needed to deal with snapshots. For diagnostics I just acquired the snapshot at the diagnostics call. Change-Id: Id48a2df3acdd97f27d905e2c2be23072f28f196b Reviewed-on: https://go-review.googlesource.com/c/tools/+/241837 Run-TryBot: Heschi Kreinick <heschi@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rebecca Stambler <rstambler@golang.org>
763 lines
22 KiB
Go
763 lines
22 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/debug/tag"
|
|
"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,
|
|
},
|
|
},
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// 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
|
|
default:
|
|
o.SymbolStyle = PackageQualifiedSymbols
|
|
}
|
|
|
|
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", tag.HoverKind.Of(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 "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)
|
|
|
|
// 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
|
|
}
|