github.com/april1989/origin-go-tools@v0.0.32/internal/lsp/source/options.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 source
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"os"
    11  	"regexp"
    12  	"time"
    13  
    14  	"github.com/april1989/origin-go-tools/go/analysis"
    15  	"github.com/april1989/origin-go-tools/go/analysis/passes/asmdecl"
    16  	"github.com/april1989/origin-go-tools/go/analysis/passes/assign"
    17  	"github.com/april1989/origin-go-tools/go/analysis/passes/atomic"
    18  	"github.com/april1989/origin-go-tools/go/analysis/passes/atomicalign"
    19  	"github.com/april1989/origin-go-tools/go/analysis/passes/bools"
    20  	"github.com/april1989/origin-go-tools/go/analysis/passes/buildtag"
    21  	"github.com/april1989/origin-go-tools/go/analysis/passes/cgocall"
    22  	"github.com/april1989/origin-go-tools/go/analysis/passes/composite"
    23  	"github.com/april1989/origin-go-tools/go/analysis/passes/copylock"
    24  	"github.com/april1989/origin-go-tools/go/analysis/passes/deepequalerrors"
    25  	"github.com/april1989/origin-go-tools/go/analysis/passes/errorsas"
    26  	"github.com/april1989/origin-go-tools/go/analysis/passes/httpresponse"
    27  	"github.com/april1989/origin-go-tools/go/analysis/passes/loopclosure"
    28  	"github.com/april1989/origin-go-tools/go/analysis/passes/lostcancel"
    29  	"github.com/april1989/origin-go-tools/go/analysis/passes/nilfunc"
    30  	"github.com/april1989/origin-go-tools/go/analysis/passes/printf"
    31  	"github.com/april1989/origin-go-tools/go/analysis/passes/shift"
    32  	"github.com/april1989/origin-go-tools/go/analysis/passes/sortslice"
    33  	"github.com/april1989/origin-go-tools/go/analysis/passes/stdmethods"
    34  	"github.com/april1989/origin-go-tools/go/analysis/passes/structtag"
    35  	"github.com/april1989/origin-go-tools/go/analysis/passes/testinggoroutine"
    36  	"github.com/april1989/origin-go-tools/go/analysis/passes/tests"
    37  	"github.com/april1989/origin-go-tools/go/analysis/passes/unmarshal"
    38  	"github.com/april1989/origin-go-tools/go/analysis/passes/unreachable"
    39  	"github.com/april1989/origin-go-tools/go/analysis/passes/unsafeptr"
    40  	"github.com/april1989/origin-go-tools/go/analysis/passes/unusedresult"
    41  	"github.com/april1989/origin-go-tools/internal/lsp/analysis/fillreturns"
    42  	"github.com/april1989/origin-go-tools/internal/lsp/analysis/fillstruct"
    43  	"github.com/april1989/origin-go-tools/internal/lsp/analysis/nonewvars"
    44  	"github.com/april1989/origin-go-tools/internal/lsp/analysis/noresultvalues"
    45  	"github.com/april1989/origin-go-tools/internal/lsp/analysis/simplifycompositelit"
    46  	"github.com/april1989/origin-go-tools/internal/lsp/analysis/simplifyrange"
    47  	"github.com/april1989/origin-go-tools/internal/lsp/analysis/simplifyslice"
    48  	"github.com/april1989/origin-go-tools/internal/lsp/analysis/undeclaredname"
    49  	"github.com/april1989/origin-go-tools/internal/lsp/analysis/unusedparams"
    50  	"github.com/april1989/origin-go-tools/internal/lsp/diff"
    51  	"github.com/april1989/origin-go-tools/internal/lsp/diff/myers"
    52  	"github.com/april1989/origin-go-tools/internal/lsp/protocol"
    53  	errors "golang.org/x/xerrors"
    54  )
    55  
    56  // DefaultOptions is the options that are used for Gopls execution independent
    57  // of any externally provided configuration (LSP initialization, command
    58  // invokation, etc.).
    59  func DefaultOptions() Options {
    60  	var commands []string
    61  	for _, c := range Commands {
    62  		commands = append(commands, c.Name)
    63  	}
    64  	return Options{
    65  		ClientOptions: ClientOptions{
    66  			InsertTextFormat:                  protocol.PlainTextTextFormat,
    67  			PreferredContentFormat:            protocol.Markdown,
    68  			ConfigurationSupported:            true,
    69  			DynamicConfigurationSupported:     true,
    70  			DynamicWatchedFilesSupported:      true,
    71  			LineFoldingOnly:                   false,
    72  			HierarchicalDocumentSymbolSupport: true,
    73  		},
    74  		ServerOptions: ServerOptions{
    75  			SupportedCodeActions: map[FileKind]map[protocol.CodeActionKind]bool{
    76  				Go: {
    77  					protocol.SourceFixAll:          true,
    78  					protocol.SourceOrganizeImports: true,
    79  					protocol.QuickFix:              true,
    80  					protocol.RefactorRewrite:       true,
    81  					protocol.RefactorExtract:       true,
    82  				},
    83  				Mod: {
    84  					protocol.SourceOrganizeImports: true,
    85  				},
    86  				Sum: {},
    87  			},
    88  			SupportedCommands: commands,
    89  		},
    90  		UserOptions: UserOptions{
    91  			Env:                     os.Environ(),
    92  			HoverKind:               FullDocumentation,
    93  			LinkTarget:              "pkg.go.dev",
    94  			LinksInHover:            true,
    95  			Matcher:                 Fuzzy,
    96  			SymbolMatcher:           SymbolFuzzy,
    97  			DeepCompletion:          true,
    98  			UnimportedCompletion:    true,
    99  			CompletionDocumentation: true,
   100  			EnabledCodeLens: map[string]bool{
   101  				CommandGenerate.Name:          true,
   102  				CommandRegenerateCgo.Name:     true,
   103  				CommandTidy.Name:              true,
   104  				CommandToggleDetails.Name:     false,
   105  				CommandUpgradeDependency.Name: true,
   106  				CommandVendor.Name:            true,
   107  			},
   108  			ExpandWorkspaceToModule: true,
   109  		},
   110  		DebuggingOptions: DebuggingOptions{
   111  			CompletionBudget:   100 * time.Millisecond,
   112  			LiteralCompletions: true,
   113  		},
   114  		ExperimentalOptions: ExperimentalOptions{
   115  			TempModfile: true,
   116  		},
   117  		Hooks: Hooks{
   118  			ComputeEdits:         myers.ComputeEdits,
   119  			URLRegexp:            urlRegexp(),
   120  			DefaultAnalyzers:     defaultAnalyzers(),
   121  			TypeErrorAnalyzers:   typeErrorAnalyzers(),
   122  			ConvenienceAnalyzers: convenienceAnalyzers(),
   123  			GoDiff:               true,
   124  		},
   125  	}
   126  }
   127  
   128  // Options holds various configuration that affects Gopls execution, organized
   129  // by the nature or origin of the settings.
   130  type Options struct {
   131  	ClientOptions
   132  	ServerOptions
   133  	UserOptions
   134  	DebuggingOptions
   135  	ExperimentalOptions
   136  	Hooks
   137  }
   138  
   139  // ClientOptions holds LSP-specific configuration that is provided by the
   140  // client.
   141  type ClientOptions struct {
   142  	InsertTextFormat                  protocol.InsertTextFormat
   143  	ConfigurationSupported            bool
   144  	DynamicConfigurationSupported     bool
   145  	DynamicWatchedFilesSupported      bool
   146  	PreferredContentFormat            protocol.MarkupKind
   147  	LineFoldingOnly                   bool
   148  	HierarchicalDocumentSymbolSupport bool
   149  }
   150  
   151  // ServerOptions holds LSP-specific configuration that is provided by the
   152  // server.
   153  type ServerOptions struct {
   154  	SupportedCodeActions map[FileKind]map[protocol.CodeActionKind]bool
   155  	SupportedCommands    []string
   156  }
   157  
   158  // UserOptions holds custom Gopls configuration (not part of the LSP) that is
   159  // modified by the client.
   160  type UserOptions struct {
   161  	// Env is the current set of environment overrides on this view.
   162  	Env []string
   163  
   164  	// BuildFlags is used to adjust the build flags applied to the view.
   165  	BuildFlags []string
   166  
   167  	// HoverKind specifies the format of the content for hover requests.
   168  	HoverKind HoverKind
   169  
   170  	// UserEnabledAnalyses specifies analyses that the user would like to enable
   171  	// or disable. A map of the names of analysis passes that should be
   172  	// enabled/disabled. A full list of analyzers that gopls uses can be found
   173  	// [here](analyzers.md).
   174  	//
   175  	// Example Usage:
   176  	// ...
   177  	// "analyses": {
   178  	//   "unreachable": false, // Disable the unreachable analyzer.
   179  	//   "unusedparams": true  // Enable the unusedparams analyzer.
   180  	// }
   181  	UserEnabledAnalyses map[string]bool
   182  
   183  	// EnabledCodeLens specifies which codelens are enabled, keyed by the gopls
   184  	// command that they provide.
   185  	EnabledCodeLens map[string]bool
   186  
   187  	// StaticCheck enables additional analyses from staticcheck.io.
   188  	StaticCheck bool
   189  
   190  	// LinkTarget is the website used for documentation. If empty, no link is
   191  	// provided.
   192  	LinkTarget string
   193  
   194  	// LinksInHover toggles the presence of links to documentation in hover.
   195  	LinksInHover bool
   196  
   197  	// ImportShortcut specifies whether import statements should link to
   198  	// documentation or go to definitions. The default is both.
   199  	ImportShortcut ImportShortcut
   200  
   201  	// LocalPrefix is used to specify goimports's -local behavior.
   202  	LocalPrefix string
   203  
   204  	// Matcher specifies the type of matcher to use for completion requests.
   205  	Matcher Matcher
   206  
   207  	// SymbolMatcher specifies the type of matcher to use for symbol requests.
   208  	SymbolMatcher SymbolMatcher
   209  
   210  	// SymbolStyle specifies what style of symbols to return in symbol requests
   211  	// (package qualified, fully qualified, etc).
   212  	SymbolStyle SymbolStyle
   213  
   214  	// DeepCompletion allows completion to perform nested searches through
   215  	// possible candidates.
   216  	DeepCompletion bool
   217  
   218  	// UnimportedCompletion enables completion for unimported packages.
   219  	UnimportedCompletion bool
   220  
   221  	// CompletionDocumentation returns additional documentation with completion
   222  	// requests.
   223  	CompletionDocumentation bool
   224  
   225  	// Placeholders adds placeholders to parameters and structs in completion
   226  	// results.
   227  	Placeholders bool
   228  
   229  	// Gofumpt indicates if we should run gofumpt formatting.
   230  	Gofumpt bool
   231  
   232  	// ExpandWorkspaceToModule is true if we should expand the scope of the
   233  	// workspace to include the modules containing the workspace folders.
   234  	ExpandWorkspaceToModule bool
   235  }
   236  
   237  type ImportShortcut int
   238  
   239  const (
   240  	Both ImportShortcut = iota
   241  	Link
   242  	Definition
   243  )
   244  
   245  func (s ImportShortcut) ShowLinks() bool {
   246  	return s == Both || s == Link
   247  }
   248  
   249  func (s ImportShortcut) ShowDefinition() bool {
   250  	return s == Both || s == Definition
   251  }
   252  
   253  type completionOptions struct {
   254  	deepCompletion    bool
   255  	unimported        bool
   256  	documentation     bool
   257  	fullDocumentation bool
   258  	placeholders      bool
   259  	literal           bool
   260  	matcher           Matcher
   261  	budget            time.Duration
   262  }
   263  
   264  // Hooks contains configuration that is provided to the Gopls command by the
   265  // main package.
   266  type Hooks struct {
   267  	GoDiff               bool
   268  	ComputeEdits         diff.ComputeEdits
   269  	URLRegexp            *regexp.Regexp
   270  	DefaultAnalyzers     map[string]Analyzer
   271  	TypeErrorAnalyzers   map[string]Analyzer
   272  	ConvenienceAnalyzers map[string]Analyzer
   273  	GofumptFormat        func(ctx context.Context, src []byte) ([]byte, error)
   274  }
   275  
   276  func (o Options) AddDefaultAnalyzer(a *analysis.Analyzer) {
   277  	o.DefaultAnalyzers[a.Name] = Analyzer{Analyzer: a, enabled: true}
   278  }
   279  
   280  // ExperimentalOptions defines configuration for features under active
   281  // development. WARNING: This configuration will be changed in the future. It
   282  // only exists while these features are under development.
   283  type ExperimentalOptions struct {
   284  	// TempModfile controls the use of the -modfile flag in Go 1.14.
   285  	TempModfile bool
   286  
   287  	// VerboseWorkDoneProgress controls whether the LSP server should send
   288  	// progress reports for all work done outside the scope of an RPC.
   289  	VerboseWorkDoneProgress bool
   290  
   291  	// Annotations suppress various kinds of optimization diagnostics
   292  	// that would be reported by the gc_details command.
   293  	//   noNilcheck suppresses display of nilchecks.
   294  	//   noEscape suppresses escape choices.
   295  	//   noInline suppresses inlining choices.
   296  	//   noBounds suppresses bounds checking diagnositcs.
   297  	Annotations map[string]bool
   298  }
   299  
   300  // DebuggingOptions should not affect the logical execution of Gopls, but may
   301  // be altered for debugging purposes.
   302  type DebuggingOptions struct {
   303  	VerboseOutput bool
   304  
   305  	// CompletionBudget is the soft latency goal for completion requests. Most
   306  	// requests finish in a couple milliseconds, but in some cases deep
   307  	// completions can take much longer. As we use up our budget we
   308  	// dynamically reduce the search scope to ensure we return timely
   309  	// results. Zero means unlimited.
   310  	CompletionBudget time.Duration
   311  
   312  	// LiteralCompletions controls whether literal candidates such as
   313  	// "&someStruct{}" are offered. Tests disable this flag to simplify
   314  	// their expected values.
   315  	LiteralCompletions bool
   316  }
   317  
   318  type Matcher int
   319  
   320  const (
   321  	Fuzzy = Matcher(iota)
   322  	CaseInsensitive
   323  	CaseSensitive
   324  )
   325  
   326  type SymbolMatcher int
   327  
   328  const (
   329  	SymbolFuzzy = SymbolMatcher(iota)
   330  	SymbolCaseInsensitive
   331  	SymbolCaseSensitive
   332  )
   333  
   334  type SymbolStyle int
   335  
   336  const (
   337  	PackageQualifiedSymbols = SymbolStyle(iota)
   338  	FullyQualifiedSymbols
   339  	DynamicSymbols
   340  )
   341  
   342  type HoverKind int
   343  
   344  const (
   345  	SingleLine = HoverKind(iota)
   346  	NoDocumentation
   347  	SynopsisDocumentation
   348  	FullDocumentation
   349  
   350  	// Structured is an experimental setting that returns a structured hover format.
   351  	// This format separates the signature from the documentation, so that the client
   352  	// can do more manipulation of these fields.
   353  	//
   354  	// This should only be used by clients that support this behavior.
   355  	Structured
   356  )
   357  
   358  type OptionResults []OptionResult
   359  
   360  type OptionResult struct {
   361  	Name  string
   362  	Value interface{}
   363  	Error error
   364  
   365  	State       OptionState
   366  	Replacement string
   367  }
   368  
   369  type OptionState int
   370  
   371  const (
   372  	OptionHandled = OptionState(iota)
   373  	OptionDeprecated
   374  	OptionUnexpected
   375  )
   376  
   377  type LinkTarget string
   378  
   379  func SetOptions(options *Options, opts interface{}) OptionResults {
   380  	var results OptionResults
   381  	switch opts := opts.(type) {
   382  	case nil:
   383  	case map[string]interface{}:
   384  		for name, value := range opts {
   385  			results = append(results, options.set(name, value))
   386  		}
   387  	default:
   388  		results = append(results, OptionResult{
   389  			Value: opts,
   390  			Error: errors.Errorf("Invalid options type %T", opts),
   391  		})
   392  	}
   393  	return results
   394  }
   395  
   396  func (o *Options) ForClientCapabilities(caps protocol.ClientCapabilities) {
   397  	// Check if the client supports snippets in completion items.
   398  	if c := caps.TextDocument.Completion; c.CompletionItem.SnippetSupport {
   399  		o.InsertTextFormat = protocol.SnippetTextFormat
   400  	}
   401  	// Check if the client supports configuration messages.
   402  	o.ConfigurationSupported = caps.Workspace.Configuration
   403  	o.DynamicConfigurationSupported = caps.Workspace.DidChangeConfiguration.DynamicRegistration
   404  	o.DynamicWatchedFilesSupported = caps.Workspace.DidChangeWatchedFiles.DynamicRegistration
   405  
   406  	// Check which types of content format are supported by this client.
   407  	if hover := caps.TextDocument.Hover; len(hover.ContentFormat) > 0 {
   408  		o.PreferredContentFormat = hover.ContentFormat[0]
   409  	}
   410  	// Check if the client supports only line folding.
   411  	fr := caps.TextDocument.FoldingRange
   412  	o.LineFoldingOnly = fr.LineFoldingOnly
   413  	// Check if the client supports hierarchical document symbols.
   414  	o.HierarchicalDocumentSymbolSupport = caps.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport
   415  }
   416  
   417  func (o *Options) set(name string, value interface{}) OptionResult {
   418  	result := OptionResult{Name: name, Value: value}
   419  	switch name {
   420  	case "env":
   421  		menv, ok := value.(map[string]interface{})
   422  		if !ok {
   423  			result.errorf("invalid config gopls.env type %T", value)
   424  			break
   425  		}
   426  		for k, v := range menv {
   427  			o.Env = append(o.Env, fmt.Sprintf("%s=%s", k, v))
   428  		}
   429  
   430  	case "buildFlags":
   431  		iflags, ok := value.([]interface{})
   432  		if !ok {
   433  			result.errorf("invalid config gopls.buildFlags type %T", value)
   434  			break
   435  		}
   436  		flags := make([]string, 0, len(iflags))
   437  		for _, flag := range iflags {
   438  			flags = append(flags, fmt.Sprintf("%s", flag))
   439  		}
   440  		o.BuildFlags = flags
   441  
   442  	case "completionDocumentation":
   443  		result.setBool(&o.CompletionDocumentation)
   444  	case "usePlaceholders":
   445  		result.setBool(&o.Placeholders)
   446  	case "deepCompletion":
   447  		result.setBool(&o.DeepCompletion)
   448  	case "completeUnimported":
   449  		result.setBool(&o.UnimportedCompletion)
   450  	case "completionBudget":
   451  		if v, ok := result.asString(); ok {
   452  			d, err := time.ParseDuration(v)
   453  			if err != nil {
   454  				result.errorf("failed to parse duration %q: %v", v, err)
   455  				break
   456  			}
   457  			o.CompletionBudget = d
   458  		}
   459  
   460  	case "matcher":
   461  		matcher, ok := result.asString()
   462  		if !ok {
   463  			break
   464  		}
   465  		switch matcher {
   466  		case "fuzzy":
   467  			o.Matcher = Fuzzy
   468  		case "caseSensitive":
   469  			o.Matcher = CaseSensitive
   470  		default:
   471  			o.Matcher = CaseInsensitive
   472  		}
   473  
   474  	case "symbolMatcher":
   475  		matcher, ok := result.asString()
   476  		if !ok {
   477  			break
   478  		}
   479  		switch matcher {
   480  		case "fuzzy":
   481  			o.SymbolMatcher = SymbolFuzzy
   482  		case "caseSensitive":
   483  			o.SymbolMatcher = SymbolCaseSensitive
   484  		default:
   485  			o.SymbolMatcher = SymbolCaseInsensitive
   486  		}
   487  
   488  	case "symbolStyle":
   489  		style, ok := result.asString()
   490  		if !ok {
   491  			break
   492  		}
   493  		switch style {
   494  		case "full":
   495  			o.SymbolStyle = FullyQualifiedSymbols
   496  		case "dynamic":
   497  			o.SymbolStyle = DynamicSymbols
   498  		case "package":
   499  			o.SymbolStyle = PackageQualifiedSymbols
   500  		default:
   501  			result.errorf("Unsupported symbol style %q", style)
   502  		}
   503  
   504  	case "hoverKind":
   505  		hoverKind, ok := result.asString()
   506  		if !ok {
   507  			break
   508  		}
   509  		switch hoverKind {
   510  		case "NoDocumentation":
   511  			o.HoverKind = NoDocumentation
   512  		case "SingleLine":
   513  			o.HoverKind = SingleLine
   514  		case "SynopsisDocumentation":
   515  			o.HoverKind = SynopsisDocumentation
   516  		case "FullDocumentation":
   517  			o.HoverKind = FullDocumentation
   518  		case "Structured":
   519  			o.HoverKind = Structured
   520  		default:
   521  			result.errorf("Unsupported hover kind %q", hoverKind)
   522  		}
   523  
   524  	case "linkTarget":
   525  		result.setString(&o.LinkTarget)
   526  
   527  	case "linksInHover":
   528  		result.setBool(&o.LinksInHover)
   529  
   530  	case "importShortcut":
   531  		var s string
   532  		result.setString(&s)
   533  		switch s {
   534  		case "both":
   535  			o.ImportShortcut = Both
   536  		case "link":
   537  			o.ImportShortcut = Link
   538  		case "definition":
   539  			o.ImportShortcut = Definition
   540  		}
   541  
   542  	case "analyses":
   543  		result.setBoolMap(&o.UserEnabledAnalyses)
   544  
   545  	case "annotations":
   546  		result.setBoolMap(&o.Annotations)
   547  		for k := range o.Annotations {
   548  			switch k {
   549  			case "noEscape", "noNilcheck", "noInline", "noBounds":
   550  				continue
   551  			default:
   552  				result.Name += ":" + k // put mistake(s) in the message
   553  				result.State = OptionUnexpected
   554  			}
   555  		}
   556  
   557  	case "codelens":
   558  		var lensOverrides map[string]bool
   559  		result.setBoolMap(&lensOverrides)
   560  		if result.Error == nil {
   561  			if o.EnabledCodeLens == nil {
   562  				o.EnabledCodeLens = make(map[string]bool)
   563  			}
   564  			for lens, enabled := range lensOverrides {
   565  				o.EnabledCodeLens[lens] = enabled
   566  			}
   567  		}
   568  
   569  	case "staticcheck":
   570  		result.setBool(&o.StaticCheck)
   571  
   572  	case "local":
   573  		result.setString(&o.LocalPrefix)
   574  
   575  	case "verboseOutput":
   576  		result.setBool(&o.VerboseOutput)
   577  
   578  	case "verboseWorkDoneProgress":
   579  		result.setBool(&o.VerboseWorkDoneProgress)
   580  
   581  	case "tempModfile":
   582  		result.setBool(&o.TempModfile)
   583  
   584  	case "gofumpt":
   585  		result.setBool(&o.Gofumpt)
   586  
   587  	case "expandWorkspaceToModule":
   588  		result.setBool(&o.ExpandWorkspaceToModule)
   589  
   590  	// Replaced settings.
   591  	case "experimentalDisabledAnalyses":
   592  		result.State = OptionDeprecated
   593  		result.Replacement = "analyses"
   594  
   595  	case "disableDeepCompletion":
   596  		result.State = OptionDeprecated
   597  		result.Replacement = "deepCompletion"
   598  
   599  	case "disableFuzzyMatching":
   600  		result.State = OptionDeprecated
   601  		result.Replacement = "fuzzyMatching"
   602  
   603  	case "wantCompletionDocumentation":
   604  		result.State = OptionDeprecated
   605  		result.Replacement = "completionDocumentation"
   606  
   607  	case "wantUnimportedCompletions":
   608  		result.State = OptionDeprecated
   609  		result.Replacement = "completeUnimported"
   610  
   611  	case "fuzzyMatching":
   612  		result.State = OptionDeprecated
   613  		result.Replacement = "matcher"
   614  
   615  	case "caseSensitiveCompletion":
   616  		result.State = OptionDeprecated
   617  		result.Replacement = "matcher"
   618  
   619  	// Deprecated settings.
   620  	case "wantSuggestedFixes":
   621  		result.State = OptionDeprecated
   622  
   623  	case "noIncrementalSync":
   624  		result.State = OptionDeprecated
   625  
   626  	case "watchFileChanges":
   627  		result.State = OptionDeprecated
   628  
   629  	case "go-diff":
   630  		result.State = OptionDeprecated
   631  
   632  	default:
   633  		result.State = OptionUnexpected
   634  	}
   635  	return result
   636  }
   637  
   638  func (r *OptionResult) errorf(msg string, values ...interface{}) {
   639  	r.Error = errors.Errorf(msg, values...)
   640  }
   641  
   642  func (r *OptionResult) asBool() (bool, bool) {
   643  	b, ok := r.Value.(bool)
   644  	if !ok {
   645  		r.errorf("Invalid type %T for bool option %q", r.Value, r.Name)
   646  		return false, false
   647  	}
   648  	return b, true
   649  }
   650  
   651  func (r *OptionResult) setBool(b *bool) {
   652  	if v, ok := r.asBool(); ok {
   653  		*b = v
   654  	}
   655  }
   656  
   657  func (r *OptionResult) setBoolMap(bm *map[string]bool) {
   658  	all, ok := r.Value.(map[string]interface{})
   659  	if !ok {
   660  		r.errorf("Invalid type %T for map[string]interface{} option %q", r.Value, r.Name)
   661  		return
   662  	}
   663  	m := make(map[string]bool)
   664  	for a, enabled := range all {
   665  		if enabled, ok := enabled.(bool); ok {
   666  			m[a] = enabled
   667  		} else {
   668  			r.errorf("Invalid type %d for map key %q in option %q", a, r.Name)
   669  			return
   670  		}
   671  	}
   672  	*bm = m
   673  }
   674  
   675  func (r *OptionResult) asString() (string, bool) {
   676  	b, ok := r.Value.(string)
   677  	if !ok {
   678  		r.errorf("Invalid type %T for string option %q", r.Value, r.Name)
   679  		return "", false
   680  	}
   681  	return b, true
   682  }
   683  
   684  func (r *OptionResult) setString(s *string) {
   685  	if v, ok := r.asString(); ok {
   686  		*s = v
   687  	}
   688  }
   689  
   690  // EnabledAnalyzers returns all of the analyzers enabled for the given
   691  // snapshot.
   692  func EnabledAnalyzers(snapshot Snapshot) (analyzers []Analyzer) {
   693  	for _, a := range snapshot.View().Options().DefaultAnalyzers {
   694  		if a.Enabled(snapshot.View()) {
   695  			analyzers = append(analyzers, a)
   696  		}
   697  	}
   698  	for _, a := range snapshot.View().Options().TypeErrorAnalyzers {
   699  		if a.Enabled(snapshot.View()) {
   700  			analyzers = append(analyzers, a)
   701  		}
   702  	}
   703  	for _, a := range snapshot.View().Options().ConvenienceAnalyzers {
   704  		if a.Enabled(snapshot.View()) {
   705  			analyzers = append(analyzers, a)
   706  		}
   707  	}
   708  	return analyzers
   709  }
   710  
   711  func typeErrorAnalyzers() map[string]Analyzer {
   712  	return map[string]Analyzer{
   713  		fillreturns.Analyzer.Name: {
   714  			Analyzer:       fillreturns.Analyzer,
   715  			FixesError:     fillreturns.FixesError,
   716  			HighConfidence: true,
   717  			enabled:        true,
   718  		},
   719  		nonewvars.Analyzer.Name: {
   720  			Analyzer:   nonewvars.Analyzer,
   721  			FixesError: nonewvars.FixesError,
   722  			enabled:    true,
   723  		},
   724  		noresultvalues.Analyzer.Name: {
   725  			Analyzer:   noresultvalues.Analyzer,
   726  			FixesError: noresultvalues.FixesError,
   727  			enabled:    true,
   728  		},
   729  		undeclaredname.Analyzer.Name: {
   730  			Analyzer:   undeclaredname.Analyzer,
   731  			FixesError: undeclaredname.FixesError,
   732  			Command:    CommandUndeclaredName,
   733  			enabled:    true,
   734  		},
   735  	}
   736  }
   737  
   738  func convenienceAnalyzers() map[string]Analyzer {
   739  	return map[string]Analyzer{
   740  		fillstruct.Analyzer.Name: {
   741  			Analyzer: fillstruct.Analyzer,
   742  			Command:  CommandFillStruct,
   743  			enabled:  true,
   744  		},
   745  	}
   746  }
   747  
   748  func defaultAnalyzers() map[string]Analyzer {
   749  	return map[string]Analyzer{
   750  		// The traditional vet suite:
   751  		asmdecl.Analyzer.Name:      {Analyzer: asmdecl.Analyzer, enabled: true},
   752  		assign.Analyzer.Name:       {Analyzer: assign.Analyzer, enabled: true},
   753  		atomic.Analyzer.Name:       {Analyzer: atomic.Analyzer, enabled: true},
   754  		atomicalign.Analyzer.Name:  {Analyzer: atomicalign.Analyzer, enabled: true},
   755  		bools.Analyzer.Name:        {Analyzer: bools.Analyzer, enabled: true},
   756  		buildtag.Analyzer.Name:     {Analyzer: buildtag.Analyzer, enabled: true},
   757  		cgocall.Analyzer.Name:      {Analyzer: cgocall.Analyzer, enabled: true},
   758  		composite.Analyzer.Name:    {Analyzer: composite.Analyzer, enabled: true},
   759  		copylock.Analyzer.Name:     {Analyzer: copylock.Analyzer, enabled: true},
   760  		errorsas.Analyzer.Name:     {Analyzer: errorsas.Analyzer, enabled: true},
   761  		httpresponse.Analyzer.Name: {Analyzer: httpresponse.Analyzer, enabled: true},
   762  		loopclosure.Analyzer.Name:  {Analyzer: loopclosure.Analyzer, enabled: true},
   763  		lostcancel.Analyzer.Name:   {Analyzer: lostcancel.Analyzer, enabled: true},
   764  		nilfunc.Analyzer.Name:      {Analyzer: nilfunc.Analyzer, enabled: true},
   765  		printf.Analyzer.Name:       {Analyzer: printf.Analyzer, enabled: true},
   766  		shift.Analyzer.Name:        {Analyzer: shift.Analyzer, enabled: true},
   767  		stdmethods.Analyzer.Name:   {Analyzer: stdmethods.Analyzer, enabled: true},
   768  		structtag.Analyzer.Name:    {Analyzer: structtag.Analyzer, enabled: true},
   769  		tests.Analyzer.Name:        {Analyzer: tests.Analyzer, enabled: true},
   770  		unmarshal.Analyzer.Name:    {Analyzer: unmarshal.Analyzer, enabled: true},
   771  		unreachable.Analyzer.Name:  {Analyzer: unreachable.Analyzer, enabled: true},
   772  		unsafeptr.Analyzer.Name:    {Analyzer: unsafeptr.Analyzer, enabled: true},
   773  		unusedresult.Analyzer.Name: {Analyzer: unusedresult.Analyzer, enabled: true},
   774  
   775  		// Non-vet analyzers:
   776  		deepequalerrors.Analyzer.Name:  {Analyzer: deepequalerrors.Analyzer, enabled: true},
   777  		sortslice.Analyzer.Name:        {Analyzer: sortslice.Analyzer, enabled: true},
   778  		testinggoroutine.Analyzer.Name: {Analyzer: testinggoroutine.Analyzer, enabled: true},
   779  		unusedparams.Analyzer.Name:     {Analyzer: unusedparams.Analyzer, enabled: false},
   780  
   781  		// gofmt -s suite:
   782  		simplifycompositelit.Analyzer.Name: {Analyzer: simplifycompositelit.Analyzer, enabled: true, HighConfidence: true},
   783  		simplifyrange.Analyzer.Name:        {Analyzer: simplifyrange.Analyzer, enabled: true, HighConfidence: true},
   784  		simplifyslice.Analyzer.Name:        {Analyzer: simplifyslice.Analyzer, enabled: true, HighConfidence: true},
   785  	}
   786  }
   787  
   788  func urlRegexp() *regexp.Regexp {
   789  	// Ensure links are matched as full words, not anywhere.
   790  	re := regexp.MustCompile(`\b(http|ftp|https)://([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?\b`)
   791  	re.Longest()
   792  	return re
   793  }