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