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