blob: c885f2365935455c3bbfaa35669dbe397927e3eb [file] [log] [blame]
diff -urN a/gopls/README.md b/gopls/README.md
--- a/gopls/README.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/README.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,113 +0,0 @@
-# `gopls`, the Go language server
-
-[![PkgGoDev](https://pkg.go.dev/badge/golang.org/x/tools/gopls)](https://pkg.go.dev/golang.org/x/tools/gopls)
-
-`gopls` (pronounced "Go please") is the official Go [language server] developed
-by the Go team. It provides IDE features to any [LSP]-compatible editor.
-
-<!--TODO(rstambler): Add gifs here.-->
-
-You should not need to interact with `gopls` directly--it will be automatically
-integrated into your editor. The specific features and settings vary slightly
-by editor, so we recommend that you proceed to the [documentation for your
-editor](#editors) below.
-
-## Editors
-
-To get started with `gopls`, install an LSP plugin in your editor of choice.
-
-* [VSCode](https://github.com/golang/vscode-go/blob/master/README.md)
-* [Vim / Neovim](doc/vim.md)
-* [Emacs](doc/emacs.md)
-* [Atom](https://github.com/MordFustang21/ide-gopls)
-* [Sublime Text](doc/subl.md)
-* [Acme](https://github.com/fhs/acme-lsp)
-
-If you use `gopls` with an editor that is not on this list, please let us know
-by [filing an issue](#new-issue) or [modifying this documentation](doc/contributing.md).
-
-## Installation
-
-For the most part, you should not need to install or update `gopls`. Your
-editor should handle that step for you.
-
-If you do want to get the latest stable version of `gopls`, change to any
-directory that is both outside of your `GOPATH` and outside of a module (a temp
-directory is fine), and run:
-
-```sh
-go install golang.org/x/tools/gopls@latest
-```
-
-Learn more in the [advanced installation
-instructions](doc/advanced.md#installing-unreleased-versions).
-
-## Setting up your workspace
-
-`gopls` supports both Go module and GOPATH modes, but if you are working with
-multiple modules or uncommon project layouts, you will need to specifically
-configure your workspace. See the [Workspace document](doc/workspace.md) for
-information on supported workspace layouts.
-
-## Configuration
-
-You can configure `gopls` to change your editor experience or view additional
-debugging information. Configuration options will be made available by your
-editor, so see your [editor's instructions](#editors) for specific details. A
-full list of `gopls` settings can be found in the [Settings documentation](doc/settings.md).
-
-### Environment variables
-
-`gopls` inherits your editor's environment, so be aware of any environment
-variables you configure. Some editors, such as VS Code, allow users to
-selectively override the values of some environment variables.
-
-## Troubleshooting
-
-If you are having issues with `gopls`, please follow the steps described in the
-[troubleshooting guide](doc/troubleshooting.md).
-
-## Supported Go versions and build systems
-
-`gopls` follows the
-[Go Release Policy](https://golang.org/doc/devel/release.html#policy),
-meaning that it officially supports the last 2 major Go releases. Per
-[issue #39146](golang.org/issues/39146), we attempt to maintain best-effort
-support for the last 4 major Go releases, but this support extends only to not
-breaking the build and avoiding easily fixable regressions.
-
-The following table shows the final gopls version that supports being built at
-a given Go Version. Any more recent Go versions missing from this table can
-still be built with the latest version of gopls.
-
-| Go Version | Final gopls Version With Support |
-| ----------- | -------------------------------- |
-| Go 1.12 | [gopls@v0.7.5](https://github.com/golang/tools/releases/tag/gopls%2Fv0.7.5) |
-
-Our extended support is enforced via [continuous integration with older Go
-versions](doc/contributing.md#ci). This legacy Go CI may not block releases:
-test failures may be skipped rather than fixed. Furthermore, if a regression in
-an older Go version causes irreconcilable CI failures, we may drop support for
-that Go version in CI if it is 3 or 4 Go versions old.
-
-`gopls` currently only supports the `go` command, so if you are using a
-different build system, `gopls` will not work well. Bazel is not officially
-supported, but Bazel support is in development (see
-[bazelbuild/rules_go#512](https://github.com/bazelbuild/rules_go/issues/512)).
-You can follow [these instructions](https://github.com/bazelbuild/rules_go/wiki/Editor-setup)
-to configure your `gopls` to work with Bazel.
-
-## Additional information
-
-* [Features](doc/features.md)
-* [Command-line interface](doc/command-line.md)
-* [Advanced topics](doc/advanced.md)
-* [Contributing to `gopls`](doc/contributing.md)
-* [Integrating `gopls` with an editor](doc/design/integrating.md)
-* [Design requirements and decisions](doc/design/design.md)
-* [Implementation details](doc/design/implementation.md)
-* [Open issues](https://github.com/golang/go/issues?q=is%3Aissue+is%3Aopen+label%3Agopls)
-
-[language server]: https://langserver.org
-[LSP]: https://microsoft.github.io/language-server-protocol/
-[Gophers Slack]: https://gophers.slack.com/
diff -urN a/gopls/api-diff/api_diff.go b/gopls/api-diff/api_diff.go
--- a/gopls/api-diff/api_diff.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/api-diff/api_diff.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,274 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.18
-// +build go1.18
-
-package main
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "flag"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "os"
- "os/exec"
- "path/filepath"
- "strings"
-
- "golang.org/x/tools/internal/gocommand"
- difflib "golang.org/x/tools/internal/lsp/diff"
- "golang.org/x/tools/internal/lsp/diff/myers"
- "golang.org/x/tools/internal/lsp/source"
-)
-
-var (
- previousVersionFlag = flag.String("prev", "", "version to compare against")
- versionFlag = flag.String("version", "", "version being tagged, or current version if omitted")
-)
-
-func main() {
- flag.Parse()
-
- apiDiff, err := diffAPI(*versionFlag, *previousVersionFlag)
- if err != nil {
- log.Fatal(err)
- }
- fmt.Printf(`
-%s
-`, apiDiff)
-}
-
-type JSON interface {
- String() string
- Write(io.Writer)
-}
-
-func diffAPI(version, prev string) (string, error) {
- ctx := context.Background()
- previousApi, err := loadAPI(ctx, prev)
- if err != nil {
- return "", fmt.Errorf("load previous API: %v", err)
- }
- var currentApi *source.APIJSON
- if version == "" {
- currentApi = source.GeneratedAPIJSON
- } else {
- var err error
- currentApi, err = loadAPI(ctx, version)
- if err != nil {
- return "", fmt.Errorf("load current API: %v", err)
- }
- }
-
- b := &strings.Builder{}
- if err := diff(b, previousApi.Commands, currentApi.Commands, "command", func(c *source.CommandJSON) string {
- return c.Command
- }, diffCommands); err != nil {
- return "", fmt.Errorf("diff commands: %v", err)
- }
- if diff(b, previousApi.Analyzers, currentApi.Analyzers, "analyzer", func(a *source.AnalyzerJSON) string {
- return a.Name
- }, diffAnalyzers); err != nil {
- return "", fmt.Errorf("diff analyzers: %v", err)
- }
- if err := diff(b, previousApi.Lenses, currentApi.Lenses, "code lens", func(l *source.LensJSON) string {
- return l.Lens
- }, diffLenses); err != nil {
- return "", fmt.Errorf("diff lenses: %v", err)
- }
- for key, prev := range previousApi.Options {
- current, ok := currentApi.Options[key]
- if !ok {
- panic(fmt.Sprintf("unexpected option key: %s", key))
- }
- if err := diff(b, prev, current, "option", func(o *source.OptionJSON) string {
- return o.Name
- }, diffOptions); err != nil {
- return "", fmt.Errorf("diff options (%s): %v", key, err)
- }
- }
-
- return b.String(), nil
-}
-
-func diff[T JSON](b *strings.Builder, previous, new []T, kind string, uniqueKey func(T) string, diffFunc func(*strings.Builder, T, T)) error {
- prevJSON := collect(previous, uniqueKey)
- newJSON := collect(new, uniqueKey)
- for k := range newJSON {
- delete(prevJSON, k)
- }
- for _, deleted := range prevJSON {
- b.WriteString(fmt.Sprintf("%s %s was deleted.\n", kind, deleted))
- }
- for _, prev := range previous {
- delete(newJSON, uniqueKey(prev))
- }
- if len(newJSON) > 0 {
- b.WriteString("The following commands were added:\n")
- for _, n := range newJSON {
- n.Write(b)
- b.WriteByte('\n')
- }
- }
- previousMap := collect(previous, uniqueKey)
- for _, current := range new {
- prev, ok := previousMap[uniqueKey(current)]
- if !ok {
- continue
- }
- c, p := bytes.NewBuffer(nil), bytes.NewBuffer(nil)
- prev.Write(p)
- current.Write(c)
- if diff, err := diffStr(p.String(), c.String()); err == nil && diff != "" {
- diffFunc(b, prev, current)
- b.WriteString("\n--\n")
- }
- }
- return nil
-}
-
-func collect[T JSON](args []T, uniqueKey func(T) string) map[string]T {
- m := map[string]T{}
- for _, arg := range args {
- m[uniqueKey(arg)] = arg
- }
- return m
-}
-
-var goCmdRunner = gocommand.Runner{}
-
-func loadAPI(ctx context.Context, version string) (*source.APIJSON, error) {
- tmpGopath, err := ioutil.TempDir("", "gopath*")
- if err != nil {
- return nil, fmt.Errorf("temp dir: %v", err)
- }
- defer os.RemoveAll(tmpGopath)
-
- exampleDir := fmt.Sprintf("%s/src/example.com", tmpGopath)
- if err := os.MkdirAll(exampleDir, 0776); err != nil {
- return nil, fmt.Errorf("mkdir: %v", err)
- }
-
- if stdout, err := goCmdRunner.Run(ctx, gocommand.Invocation{
- Verb: "mod",
- Args: []string{"init", "example.com"},
- WorkingDir: exampleDir,
- Env: append(os.Environ(), fmt.Sprintf("GOPATH=%s", tmpGopath)),
- }); err != nil {
- return nil, fmt.Errorf("go mod init failed: %v (stdout: %v)", err, stdout)
- }
- if stdout, err := goCmdRunner.Run(ctx, gocommand.Invocation{
- Verb: "install",
- Args: []string{fmt.Sprintf("golang.org/x/tools/gopls@%s", version)},
- WorkingDir: exampleDir,
- Env: append(os.Environ(), fmt.Sprintf("GOPATH=%s", tmpGopath)),
- }); err != nil {
- return nil, fmt.Errorf("go install failed: %v (stdout: %v)", err, stdout.String())
- }
- cmd := exec.Cmd{
- Path: filepath.Join(tmpGopath, "bin", "gopls"),
- Args: []string{"gopls", "api-json"},
- Dir: tmpGopath,
- }
- out, err := cmd.Output()
- if err != nil {
- return nil, fmt.Errorf("output: %v", err)
- }
- apiJson := &source.APIJSON{}
- if err := json.Unmarshal(out, apiJson); err != nil {
- return nil, fmt.Errorf("unmarshal: %v", err)
- }
- return apiJson, nil
-}
-
-func diffCommands(b *strings.Builder, prev, current *source.CommandJSON) {
- if prev.Title != current.Title {
- b.WriteString(fmt.Sprintf("Title changed from %q to %q\n", prev.Title, current.Title))
- }
- if prev.Doc != current.Doc {
- b.WriteString(fmt.Sprintf("Documentation changed from %q to %q\n", prev.Doc, current.Doc))
- }
- if prev.ArgDoc != current.ArgDoc {
- b.WriteString("Arguments changed from " + formatBlock(prev.ArgDoc) + " to " + formatBlock(current.ArgDoc))
- }
- if prev.ResultDoc != current.ResultDoc {
- b.WriteString("Results changed from " + formatBlock(prev.ResultDoc) + " to " + formatBlock(current.ResultDoc))
- }
-}
-
-func diffAnalyzers(b *strings.Builder, previous, current *source.AnalyzerJSON) {
- b.WriteString(fmt.Sprintf("Changes to analyzer %s:\n\n", current.Name))
- if previous.Doc != current.Doc {
- b.WriteString(fmt.Sprintf("Documentation changed from %q to %q\n", previous.Doc, current.Doc))
- }
- if previous.Default != current.Default {
- b.WriteString(fmt.Sprintf("Default changed from %v to %v\n", previous.Default, current.Default))
- }
-}
-
-func diffLenses(b *strings.Builder, previous, current *source.LensJSON) {
- b.WriteString(fmt.Sprintf("Changes to code lens %s:\n\n", current.Title))
- if previous.Title != current.Title {
- b.WriteString(fmt.Sprintf("Title changed from %q to %q\n", previous.Title, current.Title))
- }
- if previous.Doc != current.Doc {
- b.WriteString(fmt.Sprintf("Documentation changed from %q to %q\n", previous.Doc, current.Doc))
- }
-}
-
-func diffOptions(b *strings.Builder, previous, current *source.OptionJSON) {
- b.WriteString(fmt.Sprintf("Changes to option %s:\n\n", current.Name))
- if previous.Doc != current.Doc {
- diff, err := diffStr(previous.Doc, current.Doc)
- if err != nil {
- panic(err)
- }
- b.WriteString(fmt.Sprintf("Documentation changed:\n%s\n", diff))
- }
- if previous.Default != current.Default {
- b.WriteString(fmt.Sprintf("Default changed from %q to %q\n", previous.Default, current.Default))
- }
- if previous.Hierarchy != current.Hierarchy {
- b.WriteString(fmt.Sprintf("Categorization changed from %q to %q\n", previous.Hierarchy, current.Hierarchy))
- }
- if previous.Status != current.Status {
- b.WriteString(fmt.Sprintf("Status changed from %q to %q\n", previous.Status, current.Status))
- }
- if previous.Type != current.Type {
- b.WriteString(fmt.Sprintf("Type changed from %q to %q\n", previous.Type, current.Type))
- }
- // TODO(rstambler): Handle possibility of same number but different keys/values.
- if len(previous.EnumKeys.Keys) != len(current.EnumKeys.Keys) {
- b.WriteString(fmt.Sprintf("Enum keys changed from\n%s\n to \n%s\n", previous.EnumKeys, current.EnumKeys))
- }
- if len(previous.EnumValues) != len(current.EnumValues) {
- b.WriteString(fmt.Sprintf("Enum values changed from\n%s\n to \n%s\n", previous.EnumValues, current.EnumValues))
- }
-}
-
-func formatBlock(str string) string {
- if str == "" {
- return `""`
- }
- return "\n```\n" + str + "\n```\n"
-}
-
-func diffStr(before, after string) (string, error) {
- // Add newlines to avoid newline messages in diff.
- if before == after {
- return "", nil
- }
- before += "\n"
- after += "\n"
- d, err := myers.ComputeEdits("", before, after)
- if err != nil {
- return "", err
- }
- return fmt.Sprintf("%q", difflib.ToUnified("previous", "current", before, d)), err
-}
diff -urN a/gopls/doc/advanced.md b/gopls/doc/advanced.md
--- a/gopls/doc/advanced.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/advanced.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,69 +0,0 @@
-# Advanced topics
-
-This documentation is for advanced `gopls` users, who may want to test
-unreleased versions or try out special features.
-
-## Installing unreleased versions
-
-To get a specific version of `gopls` (for example, to test a prerelease
-version), run:
-
-```sh
-GO111MODULE=on go install golang.org/x/tools/gopls@vX.Y.Z
-```
-
-Where `vX.Y.Z` is the desired version.
-
-### Unstable versions
-
-To update `gopls` to the latest **unstable** version, use the following
-commands.
-
-```sh
-# Create an empty go.mod file, only for tracking requirements.
-cd $(mktemp -d)
-go mod init gopls-unstable
-
-# Use 'go get' to add requirements and to ensure they work together.
-go get -d golang.org/x/tools/gopls@master golang.org/x/tools@master
-
-go install golang.org/x/tools/gopls
-```
-
-## Working on the Go source distribution
-
-If you are working on the [Go project] itself, the `go` command that `gopls`
-invokes will have to correspond to the version of the source you are working
-on. That is, if you have checked out the Go project to `$HOME/go`, your `go`
-command should be the `$HOME/go/bin/go` executable that you built with
-`make.bash` or equivalent.
-
-You can achieve this by adding the right version of `go` to your `PATH`
-(`export PATH=$HOME/go/bin:$PATH` on Unix systems) or by configuring your
-editor.
-
-## Working with generic code
-
-Gopls has support for editing generic Go code. To enable this support, you need
-to **install gopls using Go 1.18 or later**. The easiest way to do this is by
-[installing Go 1.18+](https://go.dev/dl) and then using this Go version to
-install gopls:
-
-```
-$ go install golang.org/x/tools/gopls@latest
-```
-
-It is strongly recommended that you install the latest version of `gopls`, or
-the latest **unstable** version as [described above](#installing-unreleased-versions).
-We're still working on improving our generics support.
-
-The `gopls` built with these instructions understands generic code. See the
-[generics tutorial](https://go.dev/doc/tutorial/generics) for more information
-on how to use generics in Go!
-
-### Known issues
-
- * [`staticcheck`](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#staticcheck-bool)
- on generic code is not supported yet.
-
-[Go project]: https://go.googlesource.com/go
diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md
--- a/gopls/doc/analyzers.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/analyzers.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,676 +0,0 @@
-# Analyzers
-
-This document describes the analyzers that `gopls` uses inside the editor.
-
-Details about how to enable/disable these analyses can be found
-[here](settings.md#analyses).
-
-<!-- BEGIN Analyzers: DO NOT MANUALLY EDIT THIS SECTION -->
-## **asmdecl**
-
-report mismatches between assembly files and Go declarations
-
-**Enabled by default.**
-
-## **assign**
-
-check for useless assignments
-
-This checker reports assignments of the form x = x or a[i] = a[i].
-These are almost always useless, and even when they aren't they are
-usually a mistake.
-
-**Enabled by default.**
-
-## **atomic**
-
-check for common mistakes using the sync/atomic package
-
-The atomic checker looks for assignment statements of the form:
-
- x = atomic.AddUint64(&x, 1)
-
-which are not atomic.
-
-**Enabled by default.**
-
-## **atomicalign**
-
-check for non-64-bits-aligned arguments to sync/atomic functions
-
-**Enabled by default.**
-
-## **bools**
-
-check for common mistakes involving boolean operators
-
-**Enabled by default.**
-
-## **buildtag**
-
-check that +build tags are well-formed and correctly located
-
-**Enabled by default.**
-
-## **cgocall**
-
-detect some violations of the cgo pointer passing rules
-
-Check for invalid cgo pointer passing.
-This looks for code that uses cgo to call C code passing values
-whose types are almost always invalid according to the cgo pointer
-sharing rules.
-Specifically, it warns about attempts to pass a Go chan, map, func,
-or slice to C, either directly, or via a pointer, array, or struct.
-
-**Enabled by default.**
-
-## **composites**
-
-check for unkeyed composite literals
-
-This analyzer reports a diagnostic for composite literals of struct
-types imported from another package that do not use the field-keyed
-syntax. Such literals are fragile because the addition of a new field
-(even if unexported) to the struct will cause compilation to fail.
-
-As an example,
-
- err = &net.DNSConfigError{err}
-
-should be replaced by:
-
- err = &net.DNSConfigError{Err: err}
-
-
-**Enabled by default.**
-
-## **copylocks**
-
-check for locks erroneously passed by value
-
-Inadvertently copying a value containing a lock, such as sync.Mutex or
-sync.WaitGroup, may cause both copies to malfunction. Generally such
-values should be referred to through a pointer.
-
-**Enabled by default.**
-
-## **deepequalerrors**
-
-check for calls of reflect.DeepEqual on error values
-
-The deepequalerrors checker looks for calls of the form:
-
- reflect.DeepEqual(err1, err2)
-
-where err1 and err2 are errors. Using reflect.DeepEqual to compare
-errors is discouraged.
-
-**Enabled by default.**
-
-## **embed**
-
-check for //go:embed directive import
-
-This analyzer checks that the embed package is imported when source code contains //go:embed comment directives.
-The embed package must be imported for //go:embed directives to function.import _ "embed".
-
-**Enabled by default.**
-
-## **errorsas**
-
-report passing non-pointer or non-error values to errors.As
-
-The errorsas analysis reports calls to errors.As where the type
-of the second argument is not a pointer to a type implementing error.
-
-**Enabled by default.**
-
-## **fieldalignment**
-
-find structs that would use less memory if their fields were sorted
-
-This analyzer find structs that can be rearranged to use less memory, and provides
-a suggested edit with the optimal order.
-
-Note that there are two different diagnostics reported. One checks struct size,
-and the other reports "pointer bytes" used. Pointer bytes is how many bytes of the
-object that the garbage collector has to potentially scan for pointers, for example:
-
- struct { uint32; string }
-
-have 16 pointer bytes because the garbage collector has to scan up through the string's
-inner pointer.
-
- struct { string; *uint32 }
-
-has 24 pointer bytes because it has to scan further through the *uint32.
-
- struct { string; uint32 }
-
-has 8 because it can stop immediately after the string pointer.
-
-
-**Disabled by default. Enable it by setting `"analyses": {"fieldalignment": true}`.**
-
-## **httpresponse**
-
-check for mistakes using HTTP responses
-
-A common mistake when using the net/http package is to defer a function
-call to close the http.Response Body before checking the error that
-determines whether the response is valid:
-
- resp, err := http.Head(url)
- defer resp.Body.Close()
- if err != nil {
- log.Fatal(err)
- }
- // (defer statement belongs here)
-
-This checker helps uncover latent nil dereference bugs by reporting a
-diagnostic for such mistakes.
-
-**Enabled by default.**
-
-## **ifaceassert**
-
-detect impossible interface-to-interface type assertions
-
-This checker flags type assertions v.(T) and corresponding type-switch cases
-in which the static type V of v is an interface that cannot possibly implement
-the target interface T. This occurs when V and T contain methods with the same
-name but different signatures. Example:
-
- var v interface {
- Read()
- }
- _ = v.(io.Reader)
-
-The Read method in v has a different signature than the Read method in
-io.Reader, so this assertion cannot succeed.
-
-
-**Enabled by default.**
-
-## **infertypeargs**
-
-check for unnecessary type arguments in call expressions
-
-Explicit type arguments may be omitted from call expressions if they can be
-inferred from function arguments, or from other type arguments:
-
- func f[T any](T) {}
-
- func _() {
- f[string]("foo") // string could be inferred
- }
-
-
-**Enabled by default.**
-
-## **loopclosure**
-
-check references to loop variables from within nested functions
-
-This analyzer checks for references to loop variables from within a
-function literal inside the loop body. It checks only instances where
-the function literal is called in a defer or go statement that is the
-last statement in the loop body, as otherwise we would need whole
-program analysis.
-
-For example:
-
- for i, v := range s {
- go func() {
- println(i, v) // not what you might expect
- }()
- }
-
-See: https://golang.org/doc/go_faq.html#closures_and_goroutines
-
-**Enabled by default.**
-
-## **lostcancel**
-
-check cancel func returned by context.WithCancel is called
-
-The cancellation function returned by context.WithCancel, WithTimeout,
-and WithDeadline must be called or the new context will remain live
-until its parent context is cancelled.
-(The background context is never cancelled.)
-
-**Enabled by default.**
-
-## **nilfunc**
-
-check for useless comparisons between functions and nil
-
-A useless comparison is one like f == nil as opposed to f() == nil.
-
-**Enabled by default.**
-
-## **nilness**
-
-check for redundant or impossible nil comparisons
-
-The nilness checker inspects the control-flow graph of each function in
-a package and reports nil pointer dereferences, degenerate nil
-pointers, and panics with nil values. A degenerate comparison is of the form
-x==nil or x!=nil where x is statically known to be nil or non-nil. These are
-often a mistake, especially in control flow related to errors. Panics with nil
-values are checked because they are not detectable by
-
- if r := recover(); r != nil {
-
-This check reports conditions such as:
-
- if f == nil { // impossible condition (f is a function)
- }
-
-and:
-
- p := &v
- ...
- if p != nil { // tautological condition
- }
-
-and:
-
- if p == nil {
- print(*p) // nil dereference
- }
-
-and:
-
- if p == nil {
- panic(p)
- }
-
-
-**Disabled by default. Enable it by setting `"analyses": {"nilness": true}`.**
-
-## **printf**
-
-check consistency of Printf format strings and arguments
-
-The check applies to known functions (for example, those in package fmt)
-as well as any detected wrappers of known functions.
-
-A function that wants to avail itself of printf checking but is not
-found by this analyzer's heuristics (for example, due to use of
-dynamic calls) can insert a bogus call:
-
- if false {
- _ = fmt.Sprintf(format, args...) // enable printf checking
- }
-
-The -funcs flag specifies a comma-separated list of names of additional
-known formatting functions or methods. If the name contains a period,
-it must denote a specific function using one of the following forms:
-
- dir/pkg.Function
- dir/pkg.Type.Method
- (*dir/pkg.Type).Method
-
-Otherwise the name is interpreted as a case-insensitive unqualified
-identifier such as "errorf". Either way, if a listed name ends in f, the
-function is assumed to be Printf-like, taking a format string before the
-argument list. Otherwise it is assumed to be Print-like, taking a list
-of arguments with no format string.
-
-
-**Enabled by default.**
-
-## **shadow**
-
-check for possible unintended shadowing of variables
-
-This analyzer check for shadowed variables.
-A shadowed variable is a variable declared in an inner scope
-with the same name and type as a variable in an outer scope,
-and where the outer variable is mentioned after the inner one
-is declared.
-
-(This definition can be refined; the module generates too many
-false positives and is not yet enabled by default.)
-
-For example:
-
- func BadRead(f *os.File, buf []byte) error {
- var err error
- for {
- n, err := f.Read(buf) // shadows the function variable 'err'
- if err != nil {
- break // causes return of wrong value
- }
- foo(buf)
- }
- return err
- }
-
-
-**Disabled by default. Enable it by setting `"analyses": {"shadow": true}`.**
-
-## **shift**
-
-check for shifts that equal or exceed the width of the integer
-
-**Enabled by default.**
-
-## **simplifycompositelit**
-
-check for composite literal simplifications
-
-An array, slice, or map composite literal of the form:
- []T{T{}, T{}}
-will be simplified to:
- []T{{}, {}}
-
-This is one of the simplifications that "gofmt -s" applies.
-
-**Enabled by default.**
-
-## **simplifyrange**
-
-check for range statement simplifications
-
-A range of the form:
- for x, _ = range v {...}
-will be simplified to:
- for x = range v {...}
-
-A range of the form:
- for _ = range v {...}
-will be simplified to:
- for range v {...}
-
-This is one of the simplifications that "gofmt -s" applies.
-
-**Enabled by default.**
-
-## **simplifyslice**
-
-check for slice simplifications
-
-A slice expression of the form:
- s[a:len(s)]
-will be simplified to:
- s[a:]
-
-This is one of the simplifications that "gofmt -s" applies.
-
-**Enabled by default.**
-
-## **sortslice**
-
-check the argument type of sort.Slice
-
-sort.Slice requires an argument of a slice type. Check that
-the interface{} value passed to sort.Slice is actually a slice.
-
-**Enabled by default.**
-
-## **stdmethods**
-
-check signature of methods of well-known interfaces
-
-Sometimes a type may be intended to satisfy an interface but may fail to
-do so because of a mistake in its method signature.
-For example, the result of this WriteTo method should be (int64, error),
-not error, to satisfy io.WriterTo:
-
- type myWriterTo struct{...}
- func (myWriterTo) WriteTo(w io.Writer) error { ... }
-
-This check ensures that each method whose name matches one of several
-well-known interface methods from the standard library has the correct
-signature for that interface.
-
-Checked method names include:
- Format GobEncode GobDecode MarshalJSON MarshalXML
- Peek ReadByte ReadFrom ReadRune Scan Seek
- UnmarshalJSON UnreadByte UnreadRune WriteByte
- WriteTo
-
-
-**Enabled by default.**
-
-## **stringintconv**
-
-check for string(int) conversions
-
-This checker flags conversions of the form string(x) where x is an integer
-(but not byte or rune) type. Such conversions are discouraged because they
-return the UTF-8 representation of the Unicode code point x, and not a decimal
-string representation of x as one might expect. Furthermore, if x denotes an
-invalid code point, the conversion cannot be statically rejected.
-
-For conversions that intend on using the code point, consider replacing them
-with string(rune(x)). Otherwise, strconv.Itoa and its equivalents return the
-string representation of the value in the desired base.
-
-
-**Enabled by default.**
-
-## **structtag**
-
-check that struct field tags conform to reflect.StructTag.Get
-
-Also report certain struct tags (json, xml) used with unexported fields.
-
-**Enabled by default.**
-
-## **testinggoroutine**
-
-report calls to (*testing.T).Fatal from goroutines started by a test.
-
-Functions that abruptly terminate a test, such as the Fatal, Fatalf, FailNow, and
-Skip{,f,Now} methods of *testing.T, must be called from the test goroutine itself.
-This checker detects calls to these functions that occur within a goroutine
-started by the test. For example:
-
-func TestFoo(t *testing.T) {
- go func() {
- t.Fatal("oops") // error: (*T).Fatal called from non-test goroutine
- }()
-}
-
-
-**Enabled by default.**
-
-## **tests**
-
-check for common mistaken usages of tests and examples
-
-The tests checker walks Test, Benchmark and Example functions checking
-malformed names, wrong signatures and examples documenting non-existent
-identifiers.
-
-Please see the documentation for package testing in golang.org/pkg/testing
-for the conventions that are enforced for Tests, Benchmarks, and Examples.
-
-**Enabled by default.**
-
-## **unmarshal**
-
-report passing non-pointer or non-interface values to unmarshal
-
-The unmarshal analysis reports calls to functions such as json.Unmarshal
-in which the argument type is not a pointer or an interface.
-
-**Enabled by default.**
-
-## **unreachable**
-
-check for unreachable code
-
-The unreachable analyzer finds statements that execution can never reach
-because they are preceded by an return statement, a call to panic, an
-infinite loop, or similar constructs.
-
-**Enabled by default.**
-
-## **unsafeptr**
-
-check for invalid conversions of uintptr to unsafe.Pointer
-
-The unsafeptr analyzer reports likely incorrect uses of unsafe.Pointer
-to convert integers to pointers. A conversion from uintptr to
-unsafe.Pointer is invalid if it implies that there is a uintptr-typed
-word in memory that holds a pointer value, because that word will be
-invisible to stack copying and to the garbage collector.
-
-**Enabled by default.**
-
-## **unusedparams**
-
-check for unused parameters of functions
-
-The unusedparams analyzer checks functions to see if there are
-any parameters that are not being used.
-
-To reduce false positives it ignores:
-- methods
-- parameters that do not have a name or are underscored
-- functions in test files
-- functions with empty bodies or those with just a return stmt
-
-**Disabled by default. Enable it by setting `"analyses": {"unusedparams": true}`.**
-
-## **unusedresult**
-
-check for unused results of calls to some functions
-
-Some functions like fmt.Errorf return a result and have no side effects,
-so it is always a mistake to discard the result. This analyzer reports
-calls to certain functions in which the result of the call is ignored.
-
-The set of functions may be controlled using flags.
-
-**Enabled by default.**
-
-## **unusedwrite**
-
-checks for unused writes
-
-The analyzer reports instances of writes to struct fields and
-arrays that are never read. Specifically, when a struct object
-or an array is copied, its elements are copied implicitly by
-the compiler, and any element write to this copy does nothing
-with the original object.
-
-For example:
-
- type T struct { x int }
- func f(input []T) {
- for i, v := range input { // v is a copy
- v.x = i // unused write to field x
- }
- }
-
-Another example is about non-pointer receiver:
-
- type T struct { x int }
- func (t T) f() { // t is a copy
- t.x = i // unused write to field x
- }
-
-
-**Disabled by default. Enable it by setting `"analyses": {"unusedwrite": true}`.**
-
-## **useany**
-
-check for constraints that could be simplified to "any"
-
-**Disabled by default. Enable it by setting `"analyses": {"useany": true}`.**
-
-## **fillreturns**
-
-suggest fixes for errors due to an incorrect number of return values
-
-This checker provides suggested fixes for type errors of the
-type "wrong number of return values (want %d, got %d)". For example:
- func m() (int, string, *bool, error) {
- return
- }
-will turn into
- func m() (int, string, *bool, error) {
- return 0, "", nil, nil
- }
-
-This functionality is similar to https://github.com/sqs/goreturns.
-
-
-**Enabled by default.**
-
-## **nonewvars**
-
-suggested fixes for "no new vars on left side of :="
-
-This checker provides suggested fixes for type errors of the
-type "no new vars on left side of :=". For example:
- z := 1
- z := 2
-will turn into
- z := 1
- z = 2
-
-
-**Enabled by default.**
-
-## **noresultvalues**
-
-suggested fixes for unexpected return values
-
-This checker provides suggested fixes for type errors of the
-type "no result values expected" or "too many return values".
-For example:
- func z() { return nil }
-will turn into
- func z() { return }
-
-
-**Enabled by default.**
-
-## **undeclaredname**
-
-suggested fixes for "undeclared name: <>"
-
-This checker provides suggested fixes for type errors of the
-type "undeclared name: <>". It will either insert a new statement,
-such as:
-
-"<> := "
-
-or a new function declaration, such as:
-
-func <>(inferred parameters) {
- panic("implement me!")
-}
-
-
-**Enabled by default.**
-
-## **fillstruct**
-
-note incomplete struct initializations
-
-This analyzer provides diagnostics for any struct literals that do not have
-any fields initialized. Because the suggested fix for this analysis is
-expensive to compute, callers should compute it separately, using the
-SuggestedFix function below.
-
-
-**Enabled by default.**
-
-## **stubmethods**
-
-stub methods analyzer
-
-This analyzer generates method stubs for concrete types
-in order to implement a target interface
-
-**Enabled by default.**
-
-<!-- END Analyzers: DO NOT MANUALLY EDIT THIS SECTION -->
diff -urN a/gopls/doc/command-line.md b/gopls/doc/command-line.md
--- a/gopls/doc/command-line.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/command-line.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,15 +0,0 @@
-# Command line
-
-**Note: The `gopls` command-line is still experimental and subject to change at any point.**
-
-`gopls` exposes some (but not all) features on the command-line. This can be useful for debugging `gopls` itself.
-
-<!--TODO(rstambler): Generate this file.-->
-
-Learn about available commands and flags by running `gopls help`.
-
-Much of the functionality of `gopls` is available through a command line interface.
-
-There are two main reasons for this. The first is that we do not want users to rely on separate command line tools when they wish to do some task outside of an editor. The second is that the CLI assists in debugging. It is easier to reproduce behavior via single command.
-
-It is not a goal of `gopls` to be a high performance command line tool. Its command line is intended for single file/package user interaction speeds, not bulk processing.
diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md
--- a/gopls/doc/commands.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/commands.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,436 +0,0 @@
-# Commands
-
-This document describes the LSP-level commands supported by `gopls`. They cannot be invoked directly by users, and all the details are subject to change, so nobody should rely on this information.
-
-<!-- BEGIN Commands: DO NOT MANUALLY EDIT THIS SECTION -->
-### **Add a dependency**
-Identifier: `gopls.add_dependency`
-
-Adds a dependency to the go.mod file for a module.
-
-Args:
-
-```
-{
- // The go.mod file URI.
- "URI": string,
- // Additional args to pass to the go command.
- "GoCmdArgs": []string,
- // Whether to add a require directive.
- "AddRequire": bool,
-}
-```
-
-### **Add an import**
-Identifier: `gopls.add_import`
-
-Ask the server to add an import path to a given Go file. The method will
-call applyEdit on the client so that clients don't have to apply the edit
-themselves.
-
-Args:
-
-```
-{
- // ImportPath is the target import path that should
- // be added to the URI file
- "ImportPath": string,
- // URI is the file that the ImportPath should be
- // added to
- "URI": string,
-}
-```
-
-### **Apply a fix**
-Identifier: `gopls.apply_fix`
-
-Applies a fix to a region of source code.
-
-Args:
-
-```
-{
- // The fix to apply.
- "Fix": string,
- // The file URI for the document to fix.
- "URI": string,
- // The document range to scan for fixes.
- "Range": {
- "start": {
- "line": uint32,
- "character": uint32,
- },
- "end": {
- "line": uint32,
- "character": uint32,
- },
- },
-}
-```
-
-### **Check for upgrades**
-Identifier: `gopls.check_upgrades`
-
-Checks for module upgrades.
-
-Args:
-
-```
-{
- // The go.mod file URI.
- "URI": string,
- // The modules to check.
- "Modules": []string,
-}
-```
-
-### **Run go mod edit -go=version**
-Identifier: `gopls.edit_go_directive`
-
-Runs `go mod edit -go=version` for a module.
-
-Args:
-
-```
-{
- // Any document URI within the relevant module.
- "URI": string,
- // The version to pass to `go mod edit -go`.
- "Version": string,
-}
-```
-
-### **Toggle gc_details**
-Identifier: `gopls.gc_details`
-
-Toggle the calculation of gc annotations.
-
-Args:
-
-```
-string
-```
-
-### **Run go generate**
-Identifier: `gopls.generate`
-
-Runs `go generate` for a given directory.
-
-Args:
-
-```
-{
- // URI for the directory to generate.
- "Dir": string,
- // Whether to generate recursively (go generate ./...)
- "Recursive": bool,
-}
-```
-
-### **Generate gopls.mod**
-Identifier: `gopls.generate_gopls_mod`
-
-(Re)generate the gopls.mod file for a workspace.
-
-Args:
-
-```
-{
- // The file URI.
- "URI": string,
-}
-```
-
-### **go get a package**
-Identifier: `gopls.go_get_package`
-
-Runs `go get` to fetch a package.
-
-Args:
-
-```
-{
- // Any document URI within the relevant module.
- "URI": string,
- // The package to go get.
- "Pkg": string,
- "AddRequire": bool,
-}
-```
-
-### **List imports of a file and its package**
-Identifier: `gopls.list_imports`
-
-Retrieve a list of imports in the given Go file, and the package it
-belongs to.
-
-Args:
-
-```
-{
- // The file URI.
- "URI": string,
-}
-```
-
-Result:
-
-```
-{
- // Imports is a list of imports in the requested file.
- "Imports": []{
- "Path": string,
- "Name": string,
- },
- // PackageImports is a list of all imports in the requested file's package.
- "PackageImports": []{
- "Path": string,
- },
-}
-```
-
-### **List known packages**
-Identifier: `gopls.list_known_packages`
-
-Retrieve a list of packages that are importable from the given URI.
-
-Args:
-
-```
-{
- // The file URI.
- "URI": string,
-}
-```
-
-Result:
-
-```
-{
- // Packages is a list of packages relative
- // to the URIArg passed by the command request.
- // In other words, it omits paths that are already
- // imported or cannot be imported due to compiler
- // restrictions.
- "Packages": []string,
-}
-```
-
-### **Regenerate cgo**
-Identifier: `gopls.regenerate_cgo`
-
-Regenerates cgo definitions.
-
-Args:
-
-```
-{
- // The file URI.
- "URI": string,
-}
-```
-
-### **Remove a dependency**
-Identifier: `gopls.remove_dependency`
-
-Removes a dependency from the go.mod file of a module.
-
-Args:
-
-```
-{
- // The go.mod file URI.
- "URI": string,
- // The module path to remove.
- "ModulePath": string,
- "OnlyDiagnostic": bool,
-}
-```
-
-### **Run test(s)**
-Identifier: `gopls.run_tests`
-
-Runs `go test` for a specific set of test or benchmark functions.
-
-Args:
-
-```
-{
- // The test file containing the tests to run.
- "URI": string,
- // Specific test names to run, e.g. TestFoo.
- "Tests": []string,
- // Specific benchmarks to run, e.g. BenchmarkFoo.
- "Benchmarks": []string,
-}
-```
-
-### **Run vulncheck (experimental)**
-Identifier: `gopls.run_vulncheck_exp`
-
-Run vulnerability check (`govulncheck`).
-
-Args:
-
-```
-{
- // Dir is the directory from which vulncheck will run from.
- "Dir": string,
- // Package pattern. E.g. "", ".", "./...".
- "Pattern": string,
-}
-```
-
-Result:
-
-```
-{
- "Vuln": []{
- "ID": string,
- "Details": string,
- "Aliases": []string,
- "Symbol": string,
- "PkgPath": string,
- "ModPath": string,
- "URL": string,
- "CurrentVersion": string,
- "FixedVersion": string,
- "CallStacks": [][]golang.org/x/tools/internal/lsp/command.StackEntry,
- "CallStackSummaries": []string,
- },
-}
-```
-
-### **Start the gopls debug server**
-Identifier: `gopls.start_debugging`
-
-Start the gopls debug server if it isn't running, and return the debug
-address.
-
-Args:
-
-```
-{
- // Optional: the address (including port) for the debug server to listen on.
- // If not provided, the debug server will bind to "localhost:0", and the
- // full debug URL will be contained in the result.
- //
- // If there is more than one gopls instance along the serving path (i.e. you
- // are using a daemon), each gopls instance will attempt to start debugging.
- // If Addr specifies a port, only the daemon will be able to bind to that
- // port, and each intermediate gopls instance will fail to start debugging.
- // For this reason it is recommended not to specify a port (or equivalently,
- // to specify ":0").
- //
- // If the server was already debugging this field has no effect, and the
- // result will contain the previously configured debug URL(s).
- "Addr": string,
-}
-```
-
-Result:
-
-```
-{
- // The URLs to use to access the debug servers, for all gopls instances in
- // the serving path. For the common case of a single gopls instance (i.e. no
- // daemon), this will be exactly one address.
- //
- // In the case of one or more gopls instances forwarding the LSP to a daemon,
- // URLs will contain debug addresses for each server in the serving path, in
- // serving order. The daemon debug address will be the last entry in the
- // slice. If any intermediate gopls instance fails to start debugging, no
- // error will be returned but the debug URL for that server in the URLs slice
- // will be empty.
- "URLs": []string,
-}
-```
-
-### **Run test(s) (legacy)**
-Identifier: `gopls.test`
-
-Runs `go test` for a specific set of test or benchmark functions.
-
-Args:
-
-```
-string,
-[]string,
-[]string
-```
-
-### **Run go mod tidy**
-Identifier: `gopls.tidy`
-
-Runs `go mod tidy` for a module.
-
-Args:
-
-```
-{
- // The file URIs.
- "URIs": []string,
-}
-```
-
-### **Toggle gc_details**
-Identifier: `gopls.toggle_gc_details`
-
-Toggle the calculation of gc annotations.
-
-Args:
-
-```
-{
- // The file URI.
- "URI": string,
-}
-```
-
-### **Update go.sum**
-Identifier: `gopls.update_go_sum`
-
-Updates the go.sum file for a module.
-
-Args:
-
-```
-{
- // The file URIs.
- "URIs": []string,
-}
-```
-
-### **Upgrade a dependency**
-Identifier: `gopls.upgrade_dependency`
-
-Upgrades a dependency in the go.mod file for a module.
-
-Args:
-
-```
-{
- // The go.mod file URI.
- "URI": string,
- // Additional args to pass to the go command.
- "GoCmdArgs": []string,
- // Whether to add a require directive.
- "AddRequire": bool,
-}
-```
-
-### **Run go mod vendor**
-Identifier: `gopls.vendor`
-
-Runs `go mod vendor` for a module.
-
-Args:
-
-```
-{
- // The file URI.
- "URI": string,
-}
-```
-
-<!-- END Commands: DO NOT MANUALLY EDIT THIS SECTION -->
diff -urN a/gopls/doc/contributing.md b/gopls/doc/contributing.md
--- a/gopls/doc/contributing.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/contributing.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,119 +0,0 @@
-# Documentation for contributors
-
-This documentation augments the general documentation for contributing to the
-x/tools repository, described at the [repository root](../../CONTRIBUTING.md).
-
-Contributions are welcome, but since development is so active, we request that
-you file an issue and claim it before starting to work on something. Otherwise,
-it is likely that we might already be working on a fix for your issue.
-
-## Finding issues
-
-All `gopls` issues are labeled as such (see the [`gopls` label][issue-gopls]).
-Issues that are suitable for contributors are additionally tagged with the
-[`help-wanted` label][issue-wanted].
-
-Before you begin working on an issue, please leave a comment that you are
-claiming it.
-
-## Getting started
-
-Most of the `gopls` logic is actually in the `golang.org/x/tools/internal/lsp`
-directory, so you are most likely to develop in the golang.org/x/tools module.
-
-## Build
-
-To build a version of `gopls` with your changes applied:
-
-```bash
-cd /path/to/tools/gopls
-go install
-```
-
-To confirm that you are testing with the correct `gopls` version, check that
-your `gopls` version looks like this:
-
-```bash
-$ gopls version
-golang.org/x/tools/gopls master
- golang.org/x/tools/gopls@(devel)
-```
-
-## Getting help
-
-The best way to contact the gopls team directly is via the
-[#gopls-dev](https://app.slack.com/client/T029RQSE6/CRWSN9NCD) channel on the
-gophers slack. Please feel free to ask any questions about your contribution or
-about contributing in general.
-
-## Testing
-
-To run tests for just `gopls/`, run,
-
-```bash
-cd /path/to/tools/gopls
-go test ./...
-```
-
-But, much of the gopls work involves `internal/lsp` too, so you will want to
-run both:
-
-```bash
-cd /path/to/tools
-cd gopls && go test ./...
-cd ..
-go test ./internal/lsp/...
-```
-
-There is additional information about the `internal/lsp` tests in the
-[internal/lsp/tests `README`](https://github.com/golang/tools/blob/master/internal/lsp/tests/README.md).
-
-### Regtests
-
-gopls has a suite of regression tests defined in the `./gopls/internal/regtest`
-directory. Each of these tests writes files to a temporary directory, starts a
-separate gopls session, and scripts interactions using an editor-like API. As a
-result of this overhead they can be quite slow, particularly on systems where
-file operations are costly.
-
-Due to the asynchronous nature of the LSP, regtests assertions are written
-as 'expectations' that the editor state must achieve _eventually_. This can
-make debugging the regtests difficult. To aid with debugging, the regtests
-output their LSP logs on any failure. If your CL gets a test failure while
-running the regtests, please do take a look at the description of the error and
-the LSP logs, but don't hesitate to [reach out](#getting-help) to the gopls
-team if you need help.
-
-### CI
-
-When you mail your CL and you or a fellow contributor assigns the
-`Run-TryBot=1` label in Gerrit, the
-[TryBots](https://golang.org/doc/contribute.html#trybots) will run tests in
-both the `golang.org/x/tools` and `golang.org/x/tools/gopls` modules, as
-described above.
-
-Furthermore, an additional "gopls-CI" pass will be run by _Kokoro_, which is a
-Jenkins-like Google infrastructure for running Dockerized tests. This allows us
-to run gopls tests in various environments that would be difficult to add to
-the TryBots. Notably, Kokoro runs tests on
-[older Go versions](../README.md#supported-go-versions) that are no longer supported
-by the TryBots. Per that that policy, support for these older Go versions is
-best-effort, and test failures may be skipped rather than fixed.
-
-Kokoro runs are triggered by the `Run-TryBot=1` label, just like TryBots, but
-unlike TryBots they do not automatically re-run if the "gopls-CI" result is
-removed in Gerrit. To force a re-run of the Kokoro CI on a CL containing the
-`Run-TryBot=1` label, you can reply in Gerrit with the comment "kokoro rerun".
-
-## Debugging
-
-The easiest way to debug your change is to run a single `gopls` test with a
-debugger.
-
-See also [Troubleshooting](troubleshooting.md#troubleshooting).
-
-<!--TODO(rstambler): Add more details about the debug server and viewing
-telemetry.-->
-
-[issue-gopls]: https://github.com/golang/go/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Agopls "gopls issues"
-[issue-wanted]: https://github.com/golang/go/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Agopls+label%3A"help+wanted" "help wanted"
diff -urN a/gopls/doc/daemon.md b/gopls/doc/daemon.md
--- a/gopls/doc/daemon.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/daemon.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,183 +0,0 @@
-# Running gopls as a daemon
-
-**Note: this feature is new. If you encounter bugs, please [file an
-issue](troubleshooting.md#file-an-issue).**
-
-If you just want to try this out, skip ahead to the [quickstart](#quickstart).
-
-## Background: gopls execution modes
-
-Gopls was originally implemented as an LSP sidecar: a process started by
-editors or editor plugins, and communicated with using jsonrpc 2.0 over
-stdin/stdout. By executing as a stateful process, gopls can maintain a
-significant amount of cache and can eagerly perform analysis on the source code
-being edited.
-
-This execution mode does not work as well when there are many separate editor
-processes or when editor processes are short-lived, as is often the case for
-users of non-IDE editors such as Vim or Emacs. Having many processes means
-having many caches, consuming a significant amount of system resources. Using
-short-lived sessions means paying a start-up cost each time a session is
-created.
-
-To support these types of workflows, a new mode of gopls execution is supported
-wherein a single, persistent, shared gopls "daemon" process is responsible for
-managing all gopls sessions. In this mode, editors still start a gopls sidecar,
-but this sidecar merely acts as a thin "forwarder", responsible for forwarding
-the LSP to the shared gopls instance and recording metrics, logs, and rpc
-traces.
-
-## Quickstart
-
-To use a shared gopls instance you must either manage the daemon process
-yourself, or let the gopls forwarder processes start the shared daemon as
-needed.
-
-### Running with `-remote=auto`
-
-Automatic management of the daemon is easiest, and can be done by passing the
-flag `-remote=auto` to the gopls process started by your editor. This will
-cause this process to auto-start the gopls daemon if needed, connect to it, and
-forward the LSP. For example, here is a reasonable gopls invocation, that sets
-some additional flags for easier [debugging](#debugging):
-
-```bash
-gopls -remote=auto -logfile=auto -debug=:0 -remote.debug=:0 -rpc.trace
-```
-
-Note that the shared gopls process will automatically shut down after one
-minute with no connected clients.
-
-### Managing the daemon manually
-
-To manage the gopls daemon process via external means rather than having the
-forwarders manage it, you must start a gopls daemon process with the
-`-listen=<addr>` flag, and then pass `-remote=<addr>` to the gopls processes
-started by your editor.
-
-For example, to host the daemon on the TCP port `37374`, do:
-
-```bash
-gopls -listen=:37374 -logfile=auto -debug=:0
-```
-
-And then from the editor, run
-
-```bash
-gopls -remote=:37374 -logfile=auto -debug=:0 -rpc.trace
-```
-
-If you are on a POSIX system, you can also use unix domain sockets by prefixing
-the flag values with `unix;`. For example:
-
-```bash
-gopls -listen="unix;/tmp/gopls-daemon-socket" -logfile=auto -debug=:0
-```
-
-And connect via:
-
-```bash
-gopls -remote="unix;/tmp/gopls-daemon-socket" -logfile=auto -debug=:0 -rpc.trace
-```
-
-(Note that these flag values MUST be enclosed in quotes, because ';' is a
-special shell character. For this reason, this syntax is subject to change in
-the future.)
-
-## Debugging
-
-Debugging a shared gopls session is more complicated than a singleton session,
-because there are now two gopls processes involved with handling the LSP. Here
-are some tips:
-
-### Finding logfiles and debug addresses
-
-When running in daemon mode, you can use the `gopls inspect sessions` command
-to find the logfile and debug port for your gopls daemon instance (as well as
-for all its connected clients). By default, this inspects the default daemon
-(i.e. `-remote=auto`). To inspect a different daemon, use the `-remote` flag
-explicitly: `gopls -remote=localhost:12345 inspect sessions`.
-
-This works whether or not you have enabled `-remote.debug`.
-
-### Traversing debug pages
-
-When `-debug=:0` is passed to gopls, it runs a webserver that serves stateful
-debug pages (see [troubleshooting.md](troubleshooting.md)). You can find the
-actual port hosting these pages by either using the `gopls inspect sessions`
-command, or by checking the start of the logfile -- it will be one of the first
-log messages. For example, if using `-logfile=auto`, find the debug address by
-checking `head /tmp/gopls-<pid>.log`.
-
-By default, the gopls daemon is not started with `-debug`. To enable it, set
-the `-remote.debug` flag on the forwarder instance, so that it invokes gopls
-with `-debug` when starting the daemon.
-
-The debug pages of the forwarder process will have a link to the debug pages of
-the daemon server process. Correspondingly, the debug pages of the daemon
-process will have a link to each of its clients.
-
-This can help you find metrics, traces, and log files for all of the various
-servers and clients.
-
-### Using logfiles
-
-The gopls daemon is started with logging disabled by default. To customize
-this, pass `-remote.logfile` to the gopls forwarder. Using
-`-remote.logfile=auto`, the daemon will log to a default location (on posix
-systems: `/tmp/gopls-daemon-<pid>.log`).
-
-The gopls daemon does not log session-scoped messages: those are instead
-reflected back to the forwarder so that they can be accessed by the editor.
-Daemon logs will only contain global messages, for example logs when sessions
-connect and disconnect.
-
-It is recommended to start the forwarder gopls process with `-rpc.trace`, so
-that its logfile will contain rpc trace logs specific to the LSP session.
-
-## Using multiple shared gopls instances
-
-There may be environments where it is desirable to have more than one shared
-gopls instance. If managing the daemon manually, this can be done by simply
-choosing different `-listen` addresses for each distinct daemon process.
-
-On POSIX systems, there is also support for automatic management of distinct
-shared gopls processes: distinct daemons can be selected by passing
-`-remote="auto;<id>"`. Any gopls forwarder passing the same value for `<id>`
-will use the same shared daemon.
-
-## FAQ
-
-**Q: Why am I not saving as much memory as I expected when using a shared gopls?**
-
-A: As described in [implementation.md](design/implementation.md), gopls has a
-concept of view/session/cache. Each session and view map onto exactly one
-editor session (because they contain things like edited but unsaved buffers).
-The cache contains things that are independent of any editor session, and can
-therefore be shared.
-
-When, for example, three editor session are sharing a single gopls process,
-they will share the cache but will each have their own session and view. The
-memory savings in this mode, when compared to three separate gopls processes,
-corresponds to the amount of cache overlap across sessions.
-
-Because this hasn't mattered much in the past, it is likely that there is state
-that can be moved out of the session/view, and into the cache, thereby
-increasing the amount of memory savings in the shared mode.
-
-**Q: How do I customize the daemon instance when using `-remote=auto`?**
-
-The daemon may be customized using flags of the form `-remote.*` on the
-forwarder gopls. This causes the forwarder to invoke gopls with these settings
-when starting the daemon. As of writing, we expose the following configuration:
-
-* `-remote.logfile`: the location of the daemon logfile
-* `-remote.debug`: the daemon's debug address
-* `-remote.listen.timeout`: the amount of time the daemon should wait for new
- connections while there are no current connections, before shutting down.
- Must be set to a valid `time.Duration` (e.g. `30s` or `5m`). If `0`, listen
- indefinitely. Default: `1m`.
-
-Note that once the daemon is already running, setting these flags will not
-change its configuration. These flags only matter for the forwarder process
-that actually starts the daemon.
diff -urN a/gopls/doc/design/design.md b/gopls/doc/design/design.md
--- a/gopls/doc/design/design.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/design/design.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,394 +0,0 @@
-# `gopls` design documentation
-
-## Goals
-
-* `gopls` should **become the default editor backend** for the major editors used by Go programmers, fully supported by the Go team.
-* `gopls` will be a **full implementation of LSP**, as described in the [LSP specification], to standardize as many of its features as possible.
-* `gopls` will be **clean and extensible** so that it can encompass additional features in the future, allowing Go tooling to become best in class once more.
-* `gopls` will **support alternate build systems and file layouts**, allowing Go development to be simpler and more powerful in any environment.
-
-## Context
-
-While Go has a number of excellent and useful command-line tools that enhance the developer experience, it has become clear that integrating these tools with IDEs can pose challenges.
-
-Support of these tools has relied on the goodwill of community members, and they have been put under a large burden of support at times as the language, toolchain and environments change. As a result many tools have ceased to work, have had support problems, or become confusing with forks and replacements, or provided an experience that is not as good as it could be.
-See the section below on [existing solutions](#existing-solutions) for more problems and details.
-
-This is fine for tools used occasionally, but for core IDE features, this is not acceptable.
-Autocompletion, jump to definition, formatting, and other such features should always work, as they are key for Go development.
-
-The Go team will create an editor backend that works in any build system.
-It will also be able to improve upon the latency of Go tools, since each tool will no longer have to individually run the type-checker on each invocation, instead there will be a long-running process and data can be shared between the definitions, completions, diagnostics, and other features.
-
-By taking ownership of these tools and packaging them together in the form of gopls, the Go team will ensure that the Go development experience isn’t unnecessarily complicated for Go users.
-Having one editor backend will simplify the lives of Go developers, the Go team, and the maintainers of Go editor plugins.
-
-See Rebecca's excellent GopherCon keynote [talk] and [slides] for some more context.
-
-## Non-Goals
-
-* Command line speed
-
- Although gopls will have a command line mode, it will be optimized for long running and not command responsiveness, as such it may not be the right tool for things like CI systems.
- For such cases there will have to be an alternate tool using the same underlying libraries for consistency.
-
-* Low memory environments
-
- In order to do a good job of processing large projects with very low latencies gopls will be holding a lot of information in memory.
- It is presumed that developers are normally working on systems with significant RAM and this will not be a problem.
- In general this is upheld by the large memory usage of existing IDE solutions (like IntelliJ)
-
-* Syntax highlighting
-
- At the moment there is no editor that delegates this functionality to a separate binary, and no standard way of doing it.
-
-## Existing solutions
-
-Every year the Go team conducts a survey, asking developers about their experiences with the language.
-
-One question that is asked is “How do you feel about your editor?”.
-
-The responses told a very negative story. Some categorized quotes:
-
-* Setup
- * "Hard to install and configure"
- * "Inadequate documentation"
-* Performance
- * "Performance is very poor"
- * "Pretty slow in large projects"
-* Reliability
- * "Features work one day, but not the next"
- * "Tooling is not updated with new language features"
-
-Each editor has its own plugin that shells out to a variety of tools, many of which break with new Go releases or because they are no longer maintained.
-
-The individual tools each have to do the work to understand the code and all its transitive dependencies.
-
-Each feature is a different tool, with a different set of patterns for its command line, a different way to accept input and parse output, a different way of specifying source code locations.
-To support its existing feature set, VSCode installed 24 different command line tools, many of which have options or forks to configure. When looking at the set of tools that needed to be migrated to modules, across all the editors, there were 63 separate tools.
-
-All these tools need to understand the code, and they use the same standard libraries to do it. Those libraries are optimized for these kinds of tools, but even so processing that much code takes a lot of time time. Almost none of the tools are capable of returning results within 100ms.
-As developers type in their editor, multiple of these features need to activate, which means they are not just paying the cost once, but many times. The overall effect is an editing experience that feels sluggish, and features that are either not enabled or sometimes produce results that appear so slowly they are no longer useful when they arrive. This is a problem that increases with the size of the code base, which means it is getting worse over time, and is especially bad for the kinds of large code bases companies are dealing with as they use Go for more major tasks.
-
-## Requirements
-
-### Complete feature set
-
-For gopls to be considered a success it has to implement the full feature set discussed [below](#Features).
-This is the set of features that users need in order to feel as productive as they were with the tooling it is replacing. It does not include every feature of previous implementations, there are some features that are almost never used that should be dropped (like guru's pointer analysis) and some other features that do not easily fit and will have to be worked around (replacing the save hook/linter).
-
-### Equivalent or better experience
-
-For all of those features, the user experience must match or exceed the current one available in all editors.
-This is an easy statement to make, but a hard one to validate or measure. Many of the possible measures fail to capture the experience.
-
-For instance, if an attempt was made to measure the latency of a jump to definition call, the results would be fairly consistent from the old godef tool. From the gopls implementation there may be a much larger range of latencies, with the best being orders of magnitude faster, and the worse slightly worse, because gopls attempts to do far more work, but manages to cache it across calls.
-
-Or for a completion call, it might be slower but produce a better first match such that users accept it more often, resulting in an overall better experience.
-
-For the most part this has to rely on user reports. If users are refusing to switch because the experience is not better, it is clearly not done, if they are switching but most people are complaining, there are probably enough areas that are better to make the switch compelling but other areas which are worse. If most people are switching and either staying silent or being positive, it is probably done. When writing tools, the user is all that matters.
-
-### Solid community of contributors
-
-The scope and scale of the problem gopls is trying to solve is untenable for the core Go team, it is going to require a strong community to make it all happen.
-
-This implies the code must be easy to contribute to, and easy for many developers to work on in parallel. The functionality needs to be well decoupled, and have a thorough testing story.
-
-### Latencies that fall within user tolerance
-
-There has been a lot of research on acceptable latencies for user actions.
-<!-- TODO: research links -->
-The main result that affects gopls is that feedback in direct response to continuous user actions needs to be under 100ms to be imperceptible, and anything above 200ms aggravates the user.
-This means in general the aim has to be <100ms for anything that happens as the developer types.
-There will always be cases where gopls fails to meet this deadline, and there needs to be ways to make the user experience okay in those cases, but in general the point of this deadline is to inform the basic architecture design, any solution that cannot theoretically meet this goal in the long term is the wrong answer.
-
-### Easy to configure
-
-Developers are very particular, and have very differing desires in their coding experience. gopls is going to have to support a significant amount of flexibility, in order to meet those desires.
-The default settings however with no configuration at all must be the one that is best experience for most users, and where possible the features must be flexible without configuration so that the client can easily make the choices about treatment without changing its communication with gopls.
-
-## Difficulties
-
-### Volume of data
-
-<!-- TODO: project sizes -->
-* Small:
-* Medium:
-* Large:
-* Corporate mono-repo: Much much bigger
-
-Parsing and type checking large amounts of code is quite expensive, and the converted forms use a lot of space. As gopls has to keep updating this information while the developer types, it needs to manage how it caches the converted forms very carefully to balance memory use vs speed.
-
-### Cache invalidation
-
-The basic unit of operation for the type checking is the package, but the basic unit of operation for an editor is the file.
-gopls needs to be able to map files to packages efficiently, so that when files change it knows which packages need to be updated (along with any other packages that transitively depended on them).
-This is made especially difficult by the fact that changing the content of a file can modify which packages it is considered part of (either by changing the package declaration or the build tags), a file can be in more than one package, and changes can be made to files without using the editor, in which case it will not notify us of the changes.
-
-### Inappropriate core functionality
-
-The base libraries for Go (things like [go/token], [go/ast] and [go/types]) are all designed for compiler-like applications.
-They tend to worry more about throughput than memory use, they have structures that are intended to grow and then be thrown away at program exit, and they are not designed to keep going in the presence of errors in the source they are handling.
-They also have no abilities to do incremental changes.
-
-Making a long running service work well with those libraries is a very large challenge, but writing new libraries would be far more work, and cause a significant long term cost as both sets of libraries would have to be maintained. Right now it is more important to get a working tool into the hands of users. In the long term this decision may have to be revisited, new low level libraries may be the only way to keep pushing the capabilities forwards.
-
-### Build system capabilities
-
-gopls is supposed to be build system agnostic, but it must use the build system to discover how files map to packages. When it tries to do so, even when the functionality is the same, the costs (in time, CPU and memory) are very different, and can significantly impact the user experience. Designing how gopls interacts with the build system to try to minimize or hide these differences is hard.
-
-### Build tags
-
-The build tag system in Go is quite powerful, and has many use cases. Source files can exclude themselves using powerful boolean logic on the set of active tags.
-It is however designed for specifying the set of active tags on the command line, and the libraries are all designed to cope with only one valid combination at a time. There is also no way to work out the set of valid combinations.
-
-Type checking a file requires knowledge of all the other files in the same package, and that set of files is modified by the build tags. The set of exported identifiers of a package is also affected by which files are in the package, and thus its build tags.
-
-This means that even for files or packages that have no build tag controls it is not possible to produce correct results without knowing the set of build tags to consider.
-This makes it very hard to produce useful results when viewing a file.
-
-### Features not supported by LSP
-
-There are some things it would be good to be able to do that do not fit easily into the existing LSP protocol.
-For instance, displaying control flow information, automatic struct tags, complex refactoring...
-
-Each feature will have to be considered carefully, and either propose a change to LSP, or add a way to have gopls specific extensions to the protocol that are still easy to use in all the editor plugins.
-
-To avoid these at the start, only core LSP features will be implemented, as they are sufficient to meet the baseline requirements anyway, but the potential features need to be kept in mind in the core architecture.
-
-### Distribution
-
-Making sure that users are using the right version of gopls is going to be a problem. Each editor plugin is probably going to install the tools in its own way, some will choose to install it system wide, some will keep their own copy.
-
-Because it is a brand new tool, it will be changing rapidly. If users are not informed they are on an old version they will be experiencing problems that have already been fixed, which is worse for them, and then probably reporting them, which wastes time for the gopls team. There needs to be a mechanism for gopls to check if is up to date, and a recommended way to install an up to date version.
-
-### Debugging user problems
-
-gopls is essentially a very stateful long running server on the developer's machine. Its basic operation is affected by many things, from the users environment to the contents of the local build cache. The data it is operating on is often a confidential code base that cannot be shared.
-All of these things make it hard for users to report a bug usefully, or create a minimal reproduction.
-
-There needs to be easy ways for users to report what information they can, and ways to attempt to reproduce problems without their entire state. This is also needed to produce regression tests.
-
-## Basic design decisions
-
-There are some fundamental architecture decisions that affect much of the rest of the design of the tool, making fundamental trade offs that impact the user experience.
-
-### Process lifetime: *managed by the editor*
-
-Processing a large code base to fully type check and then analyze it within the latency requirements is not feasible, and is one of the primary problems with the existing solutions. This remains true even if the computed information was cached on disk, as running analyzers and type checkers ends up requiring the full AST of all files in the dependency graph.
-It is theoretically possible to do better, but only with a major re-write of the existing parsing and type checking libraries, something that is not feasible at this time.
-
-This implies that gopls should be a long running process, that is able to cache and pre-calculate results in memory so that when a request arrives it can produce the answer much faster.
-
-It could run as a daemon on the user's machine, but there are a lot of issues with managing a daemon. It may well be the right choice in the long term, and it should be allowed for in the fundamental architecture design, but to start with it will instead have a process that lasts as long as the editor that starts it, and that can easily be restarted.
-
-### Caching: *in memory*
-
-Persistent disk caches are very expensive to maintain, and require solving a lot of extra problems.
-Although building the information required is expensive compared to the latencies required of the requests, it is fairly minor compared to the startup times of an editor, so it is expected that rebuilding the information when gopls is restarted will be acceptable.
-
-The advantage gained from this is that gopls becomes stateless across restarts which means if it has issues or gets its state confused, a simple restart will often fix the problem.
-It also means that when users report problems, the entire state of the on disk cache is not needed to diagnose and reproduce the issue.
-
-### Communication: *stdin/stdout JSON*
-
-The LSP specification defines the JSON messages that are normally used, but it does not define how those message should be sent, and there are implementations of the LSP that do not use JSON (for instance, Protocol buffers are an option).
-
-The constraints on gopls are that it must be easy to integrate into *every editor* on *all operating systems*, and that it should not have large external dependencies.
-
-JSON is part of the Go standard library, and is also the native language of LSP, so it makes the most sense. By far the best supported communication mechanism is the standard input and output of a process, and the common client implementations all have ways of using [JSON rpc 2] in this mode. There were no complete and low dependency implementations of this protocol in Go, but it is a fairly small protocol on top of the JSON library that can be implemented with a moderate effort, and would be a generally useful library to have anyway.
-
-In the future it is expected to run in separated client server mode, so writing it in a way that could use sockets instead of stdin/stdout from the start was the best way to make sure it remained possible. It was also a huge debugging aid to be able to run the gopls server by hand and watch/debug it outside the editor.
-
-### Running other tools: *no*
-
-<!--- TODO: subprocess discuss --->
-
-## Features
-
-<!--TODO(rstambler): Generate a file that lists all of the supported features.-->
-
-There is a set of features that gopls needs to expose to be a comprehensive IDE solution.
-The following is the minimum set of features, along with their existing solutions and how they should map to the LSP.
-
-### Introspection
-
-Introspection features tell developers information about their code while they work. They do not make or suggest changes.
-
----
-Diagnostics | Static analysis results of the code, including compilation and lint errors
------------ | ---
-Requires | Full go/analysis run, which needs full AST, type and SSA information
-LSP | [`textDocument/publishDiagnostics`]
-Previous | `go build`, `go vet`, `golint`, [errcheck], [staticcheck] <!-- TODO: and all the rest -->
-| | This is one of the most important IDE features, allowing fast turn around without having to run compilers and checkers in the shell. Often used to power problem lists, gutter markers and squiggle underlines in the IDE. <br/> There is some complicated design work to do in order to let users customize the set of checks being run, preferably without having to recompile the main LSP binary.
-
----
-Hover | Information about the code under the cursor.
--------- | ---
-Requires | AST and type information for the file and all dependencies
-LSP | [`textDocument/hover`]
-Previous | [godoc], [gogetdoc]
-| | Used when reading code to display information known to the compiler but not always obvious from the code. For instance it may return the types of identifiers, or the documentation.
-
----
-Signature help | Function parameter information and documentation
--------------- | ---
-Requires | AST and type information for the file and all dependencies
-LSP | [`textDocument/signatureHelp`]
-Previous | [gogetdoc]
-| | As a function call is being typed into code, it is helpful to know the parameters of that call to enable the developer to call it correctly.
-
-### Navigation
-
-Navigation features are designed to make it easier for a developer to find their way round a code base.
-
----
-Definition | Select an identifier, and jump to the code where that identifier was defined.
----------- | ---
-Requires | Full type information for file and all dependencies
-LSP | [`textDocument/declaration`]
-| | [`textDocument/definition`]
-| | [`textDocument/typeDefinition`]
-Previous | [godef] |
-| | Asking the editor to open the place where a symbol was defined is one of the most commonly used code navigation tools inside an IDE when available. It is especially valuable when exploring an unfamiliar code base.<br/>Due to a limitation of the compiler output, it is not possible to use the binary data for this task (specifically it does not know column information) and thus it must parse from source.
-
----
-Implementation | Reports the types that implement an interface
--------------- | ---
-Requires | Full workspace type knowledge
-LSP | [`textDocument/implementation`]
-Previous | [impl]
-| | This feature is hard to scale up to large code bases, and is going to take thought to get right. It may be feasible to implemented a more limited form in the meantime.
-
----
-Document symbols | Provides the set of top level symbols in the current file.
----------------- | ---
-Requires | AST of the current file only
-LSP | [`textDocument/documentSymbol`]
-Previous | [go-outline], [go-symbols]
-| | Used to drive things like outline mode.
-
----
-References | Find all references to the symbol under the cursor.
----------- | ---
-Requires | AST and type information for the **reverse** transitive closure
-LSP | [`textDocument/references`]
-Previous | [guru]
-| | This requires knowledge of every package that could possible depend on any packages the current file is part of. In the past this has been implemented either by global knowledge, which does not scale, or by specifying a "scope" which confused users to the point where they just did not use the tools. gopls is probably going to need a more powerful solution in the long term, but to start with automatically limiting the scope may produce acceptable results. This would probably be the module if known, or some sensible parent directory otherwise.
-
----
-Folding | Report logical hierarchies of blocks
--------- | ---
-Requires | AST of the current file only
-LSP | [`textDocument/foldingRange`]
-Previous | [go-outline]
-| | This is normally used to provide expand and collapse behavior in editors.
-
----
-Selection | Report regions of logical selection around the cursor
---------- | ---
-Requires | AST of the current file only
-LSP | [`textDocument/selectionRange`]
-Previous | [guru]
-| | Used in editor features like expand selection.
-
-
-### Edit assistance
-
-These features suggest or apply edits to the code for the user, including refactoring features, for which there are many potential use cases.
-Refactoring is one of the places where Go tools could potentially be very strong, but have not been so far, and thus there is huge potential for improvements in the developer experience.
-There is not yet a clear understanding of the kinds of refactoring people need or how they should express them however, and there are weaknesses in the LSP protocol around this.
-This means it may be much more of a research project.
-
-
----
-Format | Fix the formatting of the file
--------- | ---
-Requires | AST of current file
-LSP | [`textDocument/formatting`]
-| | [`textDocument/rangeFormatting`]
-| | [`textDocument/onTypeFormatting`]
-Previous | [gofmt], [goimports], [goreturns]
-| | It will use the standard format package. <br/> Current limitations are that it does not work on malformed code. It may need some very careful changes to the formatter to allow for formatting an invalid AST or changes to force the AST to a valid mode. These changes would improve range and file mode as well, but are basically vital to onTypeFormatting
-
----
-Imports | Rewrite the imports block automatically to match the symbols used.
--------- | ---
-Requires | AST of the current file and full symbol knowledge for all candidate packages.
-LSP | [`textDocument/codeAction`]
-Previous | [goimports], [goreturns]
-| | This needs knowledge of packages that are not yet in use, and the ability to find those packages by name. <br/> It also needs exported symbol information for all the packages it discovers. <br/> It should be implemented using the standard imports package, but there may need to be exposed a more fine grained API than just a file rewrite for some of the interactions.
-
----
-Autocompletion | Makes suggestions to complete the entity currently being typed.
--------------- | ---
-Requires | AST and type information for the file and all dependencies<br/> Also full exported symbol knowledge for all packages.
-LSP | [`textDocument/completion`]
-| | [`completionItem/resolve`]
-Previous | [gocode]
-| | Autocomplete is one of the most complicated features, and the more it knows the better its suggestions can be. For instance it can autocomplete into packages that are not yet being imported if it has their public symbols. It can make better suggestions of options if it knows what kind of program you are writing. It can suggest better arguments if it knows how you normally call a function. It can suggest entire patterns of code if it knows they are common. Unlike many other features, which have a specific task, and once it is doing that task the feature is done, autocomplete will never be finished. Balancing and improving both the candidates and how they are ranked will be a research problem for a long time to come.
-
----
-Rename | Rename an identifier
--------- | ---
-Requires | AST and type information for the **reverse** transitive closure
-LSP | [`textDocument/rename`]
-| | [`textDocument/prepareRename`]
-Previous | [gorename]
-| | This uses the same information that find references does, with all the same problems and limitations. It is slightly worse because the changes it suggests make it intolerant of incorrect results. It is also dangerous using it to change the public API of a package.
-
----
-Suggested fixes | Suggestions that can be manually or automatically accepted to change the code
---------------- | ---
-Requires | Full go/analysis run, which needs full AST, type and SSA information
-LSP | [`textDocument/codeAction`]
-Previous | N/A
-| | This is a brand new feature powered by the new go/analysis engine, and it should allow a huge amount of automated refactoring.
-
-[LSP specification]: https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/
-[talk]: TODO
-[slides]: https://github.com/gophercon/2019-talks/blob/master/RebeccaStambler-GoPleaseStopBreakingMyEditor/slides.pdf "Go, please stop breaking my editor!"
-[JSON rpc 2]: https://www.jsonrpc.org/specification
-
-[errcheck]: https://github.com/kisielk/errcheck
-[go-outline]: https://github.com/lukehoban/go-outline
-[go-symbols]: https://github.com/acroca/go-symbols
-[gocode]: https://github.com/stamblerre/gocode
-[godef]: https://github.com/rogpeppe/godef
-[godoc]: https://golang.org/cmd/godoc
-[gofmt]: https://golang.org/cmd/gofmt
-[gogetdoc]: https://github.com/zmb3/gogetdoc
-[goimports]: https://pkg.go.dev/golang.org/x/tools/cmd/goimports
-[gorename]: https://pkg.go.dev/golang.org/x/tools/cmd/gorename
-[goreturns]: https://github.com/sqs/goreturns
-[gotags]: https://github.com/jstemmer/gotags
-[guru]: https://pkg.go.dev/golang.org/x/tools/cmd/guru
-[impl]: https://github.com/josharian/impl
-[staticcheck]: https://staticcheck.io/docs/
-[go/types]: https://golang.org/pkg/go/types/
-[go/ast]: https://golang.org/pkg/go/ast/
-[go/token]: https://golang.org/pkg/go/token/
-
-[`completionItem/resolve`]:https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#completionItem_resolve
-[`textDocument/codeAction`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_codeAction
-[`textDocument/completion`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_completion
-[`textDocument/declaration`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_declaration
-[`textDocument/definition`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_definition
-[`textDocument/documentLink`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_documentLink
-[`textDocument/documentSymbol`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_documentSymbol
-[`textDocument/foldingRange`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_foldingRange
-[`textDocument/formatting`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_formatting
-[`textDocument/highlight`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_highlight
-[`textDocument/hover`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_hover
-[`textDocument/implementation`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_implementation
-[`textDocument/onTypeFormatting`]:https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_onTypeFormatting
-[`textDocument/prepareRename`]:https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_prepareRename
-[`textDocument/publishDiagnostics`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_publishDiagnostics
-[`textDocument/rangeFormatting`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_rangeFormatting
-[`textDocument/references`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_references
-[`textDocument/rename`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_rename
-[`textDocument/selectionRange`]:https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_selectionRange
-[`textDocument/signatureHelp`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_signatureHelp
-[`textDocument/typeDefinition`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_typeDefinition
-[`workspace/didChangeWatchedFiles`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#workspace_didChangeWatchedFiles
diff -urN a/gopls/doc/design/implementation.md b/gopls/doc/design/implementation.md
--- a/gopls/doc/design/implementation.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/design/implementation.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,48 +0,0 @@
-# gopls implementation documentation
-
-This is not intended as a complete description of the implementation, for the most the part the package godoc, code comments and the code itself hold that.
-Instead this is meant to be a guide into finding parts of the implementation, and understanding some core concepts used throughout the implementation.
-
-## View/Session/Cache
-
-Throughout the code there are references to these three concepts, and they build on each other.
-
-At the base is the *Cache*. This is the level at which we hold information that is global in nature, for instance information about the file system and its contents.
-
-Above that is the *Session*, which holds information for a connection to an editor. This layer hold things like the edited files (referred to as overlays).
-
-The top layer is called the *View*. This holds the configuration, and the mapping to configured packages.
-
-The purpose of this layering is to allow a single editor session to have multiple views active whilst still sharing as much information as possible for efficiency.
-In theory if only the View layer existed, the results would be identical, but slower and using more memory.
-
-## Code location
-
-gopls will be developed in the [x/tools] Go repository; the core packages are in [internal/lsp], and the binary and integration tests are located in [gopls].
-
-Below is a list of the core packages of gopls, and their primary purpose:
-
-Package | Description
---- | ---
-[gopls] | the main binary, plugins and integration tests
-[internal/lsp] | the core message handling package
-[internal/lsp/cache] | the cache layer
-[internal/lsp/cmd] | the gopls command line layer
-[internal/lsp/debug] | features to aid in debugging gopls
-[internal/lsp/protocol] | the lsp protocol layer and wire format
-[internal/lsp/source] | the core feature implementations
-[internal/span] | a package for dealing with source file locations
-[internal/memoize] | a function invocation cache used to reduce the work done
-[internal/jsonrpc2] | an implementation of the JSON RPC2 specification
-
-[gopls]: https://github.com/golang/tools/tree/master/gopls
-[internal/jsonrpc2]: https://github.com/golang/tools/tree/master/internal/jsonrpc2
-[internal/lsp]: https://github.com/golang/tools/tree/master/internal/lsp
-[internal/lsp/cache]: https://github.com/golang/tools/tree/master/internal/lsp/cache
-[internal/lsp/cmd]: https://github.com/golang/tools/tree/master/internal/lsp/cmd
-[internal/lsp/debug]: https://github.com/golang/tools/tree/master/internal/lsp/debug
-[internal/lsp/protocol]: https://github.com/golang/tools/tree/master/internal/lsp/protocol
-[internal/lsp/source]: https://github.com/golang/tools/tree/master/internal/lsp/source
-[internal/memoize]: https://github.com/golang/tools/tree/master/internal/memoize
-[internal/span]: https://github.com/golang/tools/tree/master/internal/span
-[x/tools]: https://github.com/golang/tools
diff -urN a/gopls/doc/design/integrating.md b/gopls/doc/design/integrating.md
--- a/gopls/doc/design/integrating.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/design/integrating.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,91 +0,0 @@
-# Documentation for plugin authors
-
-If you are integrating `gopls` into an editor by writing an editor plugin, there are quite a few semantics of the communication between the editor and `gopls` that are not specified by the [LSP specification].
-
-We attempt to document those details along with any other information that has been helpful to other plugin authors here.
-
-If you are implementing a plugin yourself and have questions this page does not answer, please reach out to us to ask, and then also contribute your findings back to this page.
-
-## Supported features
-
-For the most part you should look at the [list](status.md#supported-features) in the current status document to know if gopls supports a feature.
-For a truly authoritative answer you should check the [result][InitializeResult] of the [initialize] request, where gopls enumerates its support in the [ServerCapabilities].
-
-
-## Positions and ranges
-
-Many LSP requests pass position or range information. This is described in the [LSP specification][lsp-text-documents]:
-
-> A position inside a document (see Position definition below) is expressed as a zero-based line and character offset. The offsets are based on a UTF-16 string representation. So a string of the form a𐐀b the character offset of the character a is 0, the character offset of 𐐀 is 1 and the character offset of b is 3 since 𐐀 is represented using two code units in UTF-16.
-
-This means that integrators will need to calculate UTF-16 based column offsets.
-
-[`golang.org/x/tools/internal/span`] has the code to do this in go.
-[#31080] tracks making `span` and other useful packages non-internal.
-
-## Edits
-
-In order to deliver changes from gopls to the editor, the LSP supports arrays of [`TextEdit`][lsp-textedit]s in responses.
-The spec specifies exactly how these should be applied:
-
-> All text edits ranges refer to positions in the original document. Text edits ranges must never overlap, that means no part of the original document must be manipulated by more than one edit. However, it is possible that multiple edits have the same start position: multiple inserts, or any number of inserts followed by a single remove or replace edit. If multiple inserts have the same position, the order in the array defines the order in which the inserted strings appear in the resulting text.
-
-All `[]TextEdit` are sorted such that applying the array of deltas received in reverse order achieves the desired result that holds with the spec.
-
-## Errors
-
-Various error codes are described in the [LSP specification][lsp-response]. We are still determining what it means for a method to return an error; are errors only for low-level LSP/transport issues or can other conditions cause errors to be returned? See some of this discussion on [#31526].
-
-The method chosen is currently influenced by the exact treatment in the currently popular editor integrations. It may well change, and ideally would become more coherent across requests.
-
-* [`textDocument/codeAction`]: Return error if there was an error computing code actions.
-* [`textDocument/completion`]: Log errors, return empty result list.
-* [`textDocument/definition`]: Return error if there was an error computing the definition for the position.
-* [`textDocument/typeDefinition`]: Return error if there was an error computing the type definition for the position.
-* [`textDocument/formatting`]: Return error if there was an error formatting the file.
-* [`textDocument/highlight`]: Log errors, return empty result.
-* [`textDocument/hover`]: Return empty result.
-* [`textDocument/documentLink`]: Log errors, return nil result.
-* [`textDocument/publishDiagnostics`]: Log errors if there were any while computing diagnostics.
-* [`textDocument/references`]: Log errors, return empty result.
-* [`textDocument/rename`]: Return error if there was an error computing renames.
-* [`textDocument/signatureHelp`]: Log errors, return nil result.
-* [`textDocument/documentSymbols`]: Return error if there was an error computing document symbols.
-
-## Watching files
-
-It is fairly normal for files that affect `gopls` to be modified outside of the editor it is associated with.
-
-For instance, files that are needed to do correct type checking are modified by switching branches in git, or updated by a code generator.
-
-Monitoring files inside gopls directly has a lot of awkward problems, but the [LSP specification] has methods that allow gopls to request that the client notify it of file system changes, specifically [`workspace/didChangeWatchedFiles`].
-This is currently being added to gopls by a community member, and tracked in [#31553]
-
-[InitializeResult]: https://pkg.go.dev/golang.org/x/tools/internal/lsp/protocol#InitializeResult
-[ServerCapabilities]: https://pkg.go.dev/golang.org/x/tools/internal/lsp/protocol#ServerCapabilities
-[`golang.org/x/tools/internal/span`]: https://pkg.go.dev/golang.org/x/tools/internal/span#NewPoint
-
-[LSP specification]: https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/
-[lsp-response]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#response-message
-[initialize]: https://microsoft.github.io/language-server-protocol/specifications/specification-3-14/#initialize
-[lsp-text-documents]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#text-documents
-[lsp-textedit]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textedit
-
-[`textDocument/codeAction`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_codeAction
-[`textDocument/completion`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_completion
-[`textDocument/definition`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_definition
-[`textDocument/typeDefinition`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_typeDefinition
-[`textDocument/formatting`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_formatting
-[`textDocument/highlight`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_highlight
-[`textDocument/hover`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_hover
-[`textDocument/documentLink`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_documentLink
-[`textDocument/publishDiagnostics`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_publishDiagnostics
-[`textDocument/references`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_references
-[`textDocument/rename`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_rename
-[`textDocument/signatureHelp`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_signatureHelp
-[`textDocument/documentSymbols`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#textDocument_documentSymbols
-[`workspace/didChangeWatchedFiles`]: https://github.com/Microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-14.md#workspace_didChangeWatchedFiles
-
-[#31080]: https://github.com/golang/go/issues/31080
-[#31553]: https://github.com/golang/go/issues/31553
-[#31526]: https://github.com/golang/go/issues/31526
diff -urN a/gopls/doc/emacs.md b/gopls/doc/emacs.md
--- a/gopls/doc/emacs.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/emacs.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,183 +0,0 @@
-# Emacs
-
-## Installing `gopls`
-
-To use `gopls` with Emacs, you must first
-[install the `gopls` binary](../README.md#installation) and ensure that the directory
-containing the resulting binary (either `$(go env GOBIN)` or `$(go env
-GOPATH)/bin`) is in your `PATH`.
-
-## Choosing an Emacs LSP client
-
-To use `gopls` with Emacs, you will need to choose and install an Emacs LSP
-client package. Two popular client packages are [LSP Mode] and [Eglot].
-
-LSP Mode takes a batteries-included approach, with many integrations enabled
-“out of the box” and several additional behaviors provided by `lsp-mode` itself.
-
-Eglot takes a minimally-intrusive approach, focusing on smooth integration with
-other established packages. It provides a few of its own `eglot-` commands but
-no additional keybindings by default.
-
-Once you have selected which client you want to use, install it per the packages
-instructions: see [Eglot 1-2-3](https://github.com/joaotavora/eglot#1-2-3) or
-[LSP Mode Installation](https://emacs-lsp.github.io/lsp-mode/page/installation/).
-
-## Common configuration
-
-Both Eglot and LSP Mode can integrate with popular packages in the Emacs
-ecosystem:
-
-* The built-in [`xref`] package provides cross-references.
-* The built-in [Flymake] package provides an on-the-fly diagnostic overlay.
-* [Company] mode displays code completion candidates (with a richer UI than
- the built-in [`completion-at-point`]).
-
-Eglot provides documentation using the built-in [ElDoc] minor mode, while LSP
-Mode by default provides documentation using its own [`lsp-ui`] mode.
-
-Eglot by default locates the project root using the [`project`] package. In LSP
-Mode, this behavior can be configured using the `lsp-auto-guess-root` setting.
-
-## Configuring LSP Mode
-
-### Loading LSP Mode in `.emacs`
-
-```elisp
-(require 'lsp-mode)
-(add-hook 'go-mode-hook #'lsp-deferred)
-
-;; Set up before-save hooks to format buffer and add/delete imports.
-;; Make sure you don't have other gofmt/goimports hooks enabled.
-(defun lsp-go-install-save-hooks ()
- (add-hook 'before-save-hook #'lsp-format-buffer t t)
- (add-hook 'before-save-hook #'lsp-organize-imports t t))
-(add-hook 'go-mode-hook #'lsp-go-install-save-hooks)
-```
-
-### Configuring `gopls` via LSP Mode
-
-See [settings] for information about available gopls settings.
-
-Stable gopls settings have corresponding configuration variables in `lsp-mode`.
-For example, `(setq lsp-gopls-use-placeholders nil)` will disable placeholders
-in completion snippets. See [`lsp-go`] for a list of available variables.
-
-Experimental settings can be configured via `lsp-register-custom-settings`:
-
-```lisp
-(lsp-register-custom-settings
- '(("gopls.completeUnimported" t t)
- ("gopls.staticcheck" t t)))
-```
-
-Note that after changing settings you must restart gopls using e.g. `M-x
-lsp-restart-workspace`.
-
-## Configuring Eglot
-
-### Configuring `project` for Go modules in `.emacs`
-
-Eglot uses the built-in `project` package to identify the LSP workspace for a
-newly-opened buffer. The `project` package does not natively know about `GOPATH`
-or Go modules. Fortunately, you can give it a custom hook to tell it to look for
-the nearest parent `go.mod` file (that is, the root of the Go module) as the
-project root.
-
-```elisp
-(require 'project)
-
-(defun project-find-go-module (dir)
- (when-let ((root (locate-dominating-file dir "go.mod")))
- (cons 'go-module root)))
-
-(cl-defmethod project-root ((project (head go-module)))
- (cdr project))
-
-(add-hook 'project-find-functions #'project-find-go-module)
-```
-
-### Loading Eglot in `.emacs`
-
-```elisp
-;; Optional: load other packages before eglot to enable eglot integrations.
-(require 'company)
-(require 'yasnippet)
-
-(require 'go-mode)
-(require 'eglot)
-(add-hook 'go-mode-hook 'eglot-ensure)
-
-;; Optional: install eglot-format-buffer as a save hook.
-;; The depth of -10 places this before eglot's willSave notification,
-;; so that that notification reports the actual contents that will be saved.
-(defun eglot-format-buffer-on-save ()
- (add-hook 'before-save-hook #'eglot-format-buffer -10 t))
-(add-hook 'go-mode-hook #'eglot-format-buffer-on-save)
-```
-
-### Configuring `gopls` via Eglot
-
-See [settings] for information about available gopls settings.
-
-LSP server settings are controlled by the `eglot-workspace-configuration`
-variable, which can be set either globally in `.emacs` or in a `.dir-locals.el` file in the project root.
-
-`.emacs`:
-```elisp
-(setq-default eglot-workspace-configuration
- '((:gopls .
- ((staticcheck . t)
- (matcher . "CaseSensitive")))))
-```
-
-`.dir-locals.el`:
-```elisp
-((nil (eglot-workspace-configuration . ((gopls . ((staticcheck . t)
- (matcher . "CaseSensitive")))))))
-```
-
-### Organizing imports with Eglot
-
-`gopls` provides the import-organizing functionality of `goimports` as an LSP
-code action, which you can invoke as needed by running `M-x eglot-code-actions`
-(or a key of your choice bound to the `eglot-code-actions` function) and
-selecting `Organize Imports` at the prompt.
-
-Eglot does not currently support a standalone function to execute a specific
-code action (see
-[joaotavora/eglot#411](https://github.com/joaotavora/eglot/issues/411)), nor an
-option to organize imports as a `before-save-hook` (see
-[joaotavora/eglot#574](https://github.com/joaotavora/eglot/issues/574)). In the
-meantime, see those issues for discussion and possible workarounds.
-
-## Troubleshooting
-
-Common errors:
-
-* When prompted by Emacs for your project folder, if you are using modules you
- must select the module's root folder (i.e. the directory with the "go.mod").
- If you are using GOPATH, select your $GOPATH as your folder.
-* Emacs must have your environment set properly (PATH, GOPATH, etc). You can
- run `M-x getenv <RET> PATH <RET>` to see if your PATH is set in Emacs. If
- not, you can try starting Emacs from your terminal, using [this
- package][exec-path-from-shell], or moving your shell config from `.bashrc`
- into `.profile` and logging out and back in.
-* Make sure only one LSP client mode is installed. (For example, if using
- `lsp-mode`, ensure that you are not _also_ enabling `eglot`.)
-* Look for errors in the `*lsp-log*` buffer or run `M-x eglot-events-buffer`.
-* Ask for help in the `#emacs` channel on the [Gophers slack].
-
-[LSP Mode]: https://emacs-lsp.github.io/lsp-mode/
-[Eglot]: https://github.com/joaotavora/eglot/blob/master/README.md
-[`xref`]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html
-[Flymake]: https://www.gnu.org/software/emacs/manual/html_node/flymake/Using-Flymake.html#Using-Flymake
-[Company]: https://company-mode.github.io/
-[`completion-at-point`]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Completion-in-Buffers.html
-[ElDoc]: https://elpa.gnu.org/packages/eldoc.html
-[`lsp-ui`]: https://emacs-lsp.github.io/lsp-ui/
-[`lsp-go`]: https://github.com/emacs-lsp/lsp-mode/blob/master/clients/lsp-go.el
-[`use-package`]: https://github.com/jwiegley/use-package
-[`exec-path-from-shell`]: https://github.com/purcell/exec-path-from-shell
-[settings]: settings.md
-[Gophers slack]: https://invite.slack.golangbridge.org/
diff -urN a/gopls/doc/features.md b/gopls/doc/features.md
--- a/gopls/doc/features.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/features.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,55 +0,0 @@
-# Features
-
-This document describes some of the features supported by `gopls`. It is
-currently under construction, so, for a comprehensive list, see the
-[Language Server Protocol](https://microsoft.github.io/language-server-protocol/).
-
-## Special features
-
-Here, only special features outside of the LSP are described.
-
-### Symbol Queries
-
-Gopls supports some extended syntax for `workspace/symbol` requests, when using
-the `fuzzy` symbol matcher (the default). Inspired by the popular fuzzy matcher
-[FZF](https://github.com/junegunn/fzf), the following special characters are
-supported within symbol queries:
-
-| Character | Usage | Match |
-| --------- | --------- | ------------ |
-| `'` | `'abc` | exact |
-| `^` | `^printf` | exact prefix |
-| `$` | `printf$` | exact suffix |
-
-## Template Files
-
-Gopls provides some support for Go template files, that is, files that
-are parsed by `text/template` or `html/template`.
-Gopls recognizes template files based on their file extension, which may be
-configured by the
-[`templateExtensions`](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#templateextensions-string) setting.
-Making this list empty turns off template support.
-
-In template files, template support works inside
-the default `{{` delimiters. (Go template parsing
-allows the user to specify other delimiters, but
-gopls does not know how to do that.)
-
-Gopls template support includes the following features:
-+ **Diagnostics**: if template parsing returns an error,
-it is presented as a diagnostic. (Missing functions do not produce errors.)
-+ **Syntax Highlighting**: syntax highlighting is provided for template files.
-+ **Definitions**: gopls provides jump-to-definition inside templates, though it does not understand scoping (all templates are considered to be in one global scope).
-+ **References**: gopls provides find-references, with the same scoping limitation as definitions.
-+ **Completions**: gopls will attempt to suggest completions inside templates.
-
-### Configuring your editor
-
-In addition to configuring `templateExtensions`, you may need to configure your
-editor or LSP client to activate `gopls` for template files. For example, in
-`VS Code` you will need to configure both
-[`files.associations`](https://code.visualstudio.com/docs/languages/identifiers)
-and `build.templateExtensions` (the gopls setting).
-
-<!--TODO(rstambler): Automatically generate a list of supported features.-->
-
diff -urN a/gopls/doc/generate.go b/gopls/doc/generate.go
--- a/gopls/doc/generate.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/generate.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,712 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.16
-// +build go1.16
-
-// Command generate creates API (settings, etc) documentation in JSON and
-// Markdown for machine and human consumption.
-package main
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "go/ast"
- "go/format"
- "go/token"
- "go/types"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "reflect"
- "regexp"
- "sort"
- "strconv"
- "strings"
- "time"
- "unicode"
-
- "github.com/jba/printsrc"
- "golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/internal/lsp/command"
- "golang.org/x/tools/internal/lsp/command/commandmeta"
- "golang.org/x/tools/internal/lsp/mod"
- "golang.org/x/tools/internal/lsp/source"
-)
-
-func main() {
- if _, err := doMain("..", true); err != nil {
- fmt.Fprintf(os.Stderr, "Generation failed: %v\n", err)
- os.Exit(1)
- }
-}
-
-func doMain(baseDir string, write bool) (bool, error) {
- api, err := loadAPI()
- if err != nil {
- return false, err
- }
-
- if ok, err := rewriteFile(filepath.Join(baseDir, "internal/lsp/source/api_json.go"), api, write, rewriteAPI); !ok || err != nil {
- return ok, err
- }
- if ok, err := rewriteFile(filepath.Join(baseDir, "gopls/doc/settings.md"), api, write, rewriteSettings); !ok || err != nil {
- return ok, err
- }
- if ok, err := rewriteFile(filepath.Join(baseDir, "gopls/doc/commands.md"), api, write, rewriteCommands); !ok || err != nil {
- return ok, err
- }
- if ok, err := rewriteFile(filepath.Join(baseDir, "gopls/doc/analyzers.md"), api, write, rewriteAnalyzers); !ok || err != nil {
- return ok, err
- }
-
- return true, nil
-}
-
-func loadAPI() (*source.APIJSON, error) {
- pkgs, err := packages.Load(
- &packages.Config{
- Mode: packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax | packages.NeedDeps,
- },
- "golang.org/x/tools/internal/lsp/source",
- )
- if err != nil {
- return nil, err
- }
- pkg := pkgs[0]
-
- api := &source.APIJSON{
- Options: map[string][]*source.OptionJSON{},
- }
- defaults := source.DefaultOptions()
-
- api.Commands, err = loadCommands(pkg)
- if err != nil {
- return nil, err
- }
- api.Lenses = loadLenses(api.Commands)
-
- // Transform the internal command name to the external command name.
- for _, c := range api.Commands {
- c.Command = command.ID(c.Command)
- }
- for _, m := range []map[string]*source.Analyzer{
- defaults.DefaultAnalyzers,
- defaults.TypeErrorAnalyzers,
- defaults.ConvenienceAnalyzers,
- // Don't yet add staticcheck analyzers.
- } {
- api.Analyzers = append(api.Analyzers, loadAnalyzers(m)...)
- }
- for _, category := range []reflect.Value{
- reflect.ValueOf(defaults.UserOptions),
- } {
- // Find the type information and ast.File corresponding to the category.
- optsType := pkg.Types.Scope().Lookup(category.Type().Name())
- if optsType == nil {
- return nil, fmt.Errorf("could not find %v in scope %v", category.Type().Name(), pkg.Types.Scope())
- }
- opts, err := loadOptions(category, optsType, pkg, "")
- if err != nil {
- return nil, err
- }
- catName := strings.TrimSuffix(category.Type().Name(), "Options")
- api.Options[catName] = opts
-
- // Hardcode the expected values for the analyses and code lenses
- // settings, since their keys are not enums.
- for _, opt := range opts {
- switch opt.Name {
- case "analyses":
- for _, a := range api.Analyzers {
- opt.EnumKeys.Keys = append(opt.EnumKeys.Keys, source.EnumKey{
- Name: fmt.Sprintf("%q", a.Name),
- Doc: a.Doc,
- Default: strconv.FormatBool(a.Default),
- })
- }
- case "codelenses":
- // Hack: Lenses don't set default values, and we don't want to
- // pass in the list of expected lenses to loadOptions. Instead,
- // format the defaults using reflection here. The hackiest part
- // is reversing lowercasing of the field name.
- reflectField := category.FieldByName(upperFirst(opt.Name))
- for _, l := range api.Lenses {
- def, err := formatDefaultFromEnumBoolMap(reflectField, l.Lens)
- if err != nil {
- return nil, err
- }
- opt.EnumKeys.Keys = append(opt.EnumKeys.Keys, source.EnumKey{
- Name: fmt.Sprintf("%q", l.Lens),
- Doc: l.Doc,
- Default: def,
- })
- }
- }
- }
- }
- return api, nil
-}
-
-func loadOptions(category reflect.Value, optsType types.Object, pkg *packages.Package, hierarchy string) ([]*source.OptionJSON, error) {
- file, err := fileForPos(pkg, optsType.Pos())
- if err != nil {
- return nil, err
- }
-
- enums, err := loadEnums(pkg)
- if err != nil {
- return nil, err
- }
-
- var opts []*source.OptionJSON
- optsStruct := optsType.Type().Underlying().(*types.Struct)
- for i := 0; i < optsStruct.NumFields(); i++ {
- // The types field gives us the type.
- typesField := optsStruct.Field(i)
-
- // If the field name ends with "Options", assume it is a struct with
- // additional options and process it recursively.
- if h := strings.TrimSuffix(typesField.Name(), "Options"); h != typesField.Name() {
- // Keep track of the parent structs.
- if hierarchy != "" {
- h = hierarchy + "." + h
- }
- options, err := loadOptions(category, typesField, pkg, strings.ToLower(h))
- if err != nil {
- return nil, err
- }
- opts = append(opts, options...)
- continue
- }
- path, _ := astutil.PathEnclosingInterval(file, typesField.Pos(), typesField.Pos())
- if len(path) < 2 {
- return nil, fmt.Errorf("could not find AST node for field %v", typesField)
- }
- // The AST field gives us the doc.
- astField, ok := path[1].(*ast.Field)
- if !ok {
- return nil, fmt.Errorf("unexpected AST path %v", path)
- }
-
- // The reflect field gives us the default value.
- reflectField := category.FieldByName(typesField.Name())
- if !reflectField.IsValid() {
- return nil, fmt.Errorf("could not find reflect field for %v", typesField.Name())
- }
-
- def, err := formatDefault(reflectField)
- if err != nil {
- return nil, err
- }
-
- typ := typesField.Type().String()
- if _, ok := enums[typesField.Type()]; ok {
- typ = "enum"
- }
- name := lowerFirst(typesField.Name())
-
- var enumKeys source.EnumKeys
- if m, ok := typesField.Type().(*types.Map); ok {
- e, ok := enums[m.Key()]
- if ok {
- typ = strings.Replace(typ, m.Key().String(), m.Key().Underlying().String(), 1)
- }
- keys, err := collectEnumKeys(name, m, reflectField, e)
- if err != nil {
- return nil, err
- }
- if keys != nil {
- enumKeys = *keys
- }
- }
-
- // Get the status of the field by checking its struct tags.
- reflectStructField, ok := category.Type().FieldByName(typesField.Name())
- if !ok {
- return nil, fmt.Errorf("no struct field for %s", typesField.Name())
- }
- status := reflectStructField.Tag.Get("status")
-
- opts = append(opts, &source.OptionJSON{
- Name: name,
- Type: typ,
- Doc: lowerFirst(astField.Doc.Text()),
- Default: def,
- EnumKeys: enumKeys,
- EnumValues: enums[typesField.Type()],
- Status: status,
- Hierarchy: hierarchy,
- })
- }
- return opts, nil
-}
-
-func loadEnums(pkg *packages.Package) (map[types.Type][]source.EnumValue, error) {
- enums := map[types.Type][]source.EnumValue{}
- for _, name := range pkg.Types.Scope().Names() {
- obj := pkg.Types.Scope().Lookup(name)
- cnst, ok := obj.(*types.Const)
- if !ok {
- continue
- }
- f, err := fileForPos(pkg, cnst.Pos())
- if err != nil {
- return nil, fmt.Errorf("finding file for %q: %v", cnst.Name(), err)
- }
- path, _ := astutil.PathEnclosingInterval(f, cnst.Pos(), cnst.Pos())
- spec := path[1].(*ast.ValueSpec)
- value := cnst.Val().ExactString()
- doc := valueDoc(cnst.Name(), value, spec.Doc.Text())
- v := source.EnumValue{
- Value: value,
- Doc: doc,
- }
- enums[obj.Type()] = append(enums[obj.Type()], v)
- }
- return enums, nil
-}
-
-func collectEnumKeys(name string, m *types.Map, reflectField reflect.Value, enumValues []source.EnumValue) (*source.EnumKeys, error) {
- // Make sure the value type gets set for analyses and codelenses
- // too.
- if len(enumValues) == 0 && !hardcodedEnumKeys(name) {
- return nil, nil
- }
- keys := &source.EnumKeys{
- ValueType: m.Elem().String(),
- }
- // We can get default values for enum -> bool maps.
- var isEnumBoolMap bool
- if basic, ok := m.Elem().(*types.Basic); ok && basic.Kind() == types.Bool {
- isEnumBoolMap = true
- }
- for _, v := range enumValues {
- var def string
- if isEnumBoolMap {
- var err error
- def, err = formatDefaultFromEnumBoolMap(reflectField, v.Value)
- if err != nil {
- return nil, err
- }
- }
- keys.Keys = append(keys.Keys, source.EnumKey{
- Name: v.Value,
- Doc: v.Doc,
- Default: def,
- })
- }
- return keys, nil
-}
-
-func formatDefaultFromEnumBoolMap(reflectMap reflect.Value, enumKey string) (string, error) {
- if reflectMap.Kind() != reflect.Map {
- return "", nil
- }
- name := enumKey
- if unquoted, err := strconv.Unquote(name); err == nil {
- name = unquoted
- }
- for _, e := range reflectMap.MapKeys() {
- if e.String() == name {
- value := reflectMap.MapIndex(e)
- if value.Type().Kind() == reflect.Bool {
- return formatDefault(value)
- }
- }
- }
- // Assume that if the value isn't mentioned in the map, it defaults to
- // the default value, false.
- return formatDefault(reflect.ValueOf(false))
-}
-
-// formatDefault formats the default value into a JSON-like string.
-// VS Code exposes settings as JSON, so showing them as JSON is reasonable.
-// TODO(rstambler): Reconsider this approach, as the VS Code Go generator now
-// marshals to JSON.
-func formatDefault(reflectField reflect.Value) (string, error) {
- def := reflectField.Interface()
-
- // Durations marshal as nanoseconds, but we want the stringy versions,
- // e.g. "100ms".
- if t, ok := def.(time.Duration); ok {
- def = t.String()
- }
- defBytes, err := json.Marshal(def)
- if err != nil {
- return "", err
- }
-
- // Nil values format as "null" so print them as hardcoded empty values.
- switch reflectField.Type().Kind() {
- case reflect.Map:
- if reflectField.IsNil() {
- defBytes = []byte("{}")
- }
- case reflect.Slice:
- if reflectField.IsNil() {
- defBytes = []byte("[]")
- }
- }
- return string(defBytes), err
-}
-
-// valueDoc transforms a docstring documenting an constant identifier to a
-// docstring documenting its value.
-//
-// If doc is of the form "Foo is a bar", it returns '`"fooValue"` is a bar'. If
-// doc is non-standard ("this value is a bar"), it returns '`"fooValue"`: this
-// value is a bar'.
-func valueDoc(name, value, doc string) string {
- if doc == "" {
- return ""
- }
- if strings.HasPrefix(doc, name) {
- // docstring in standard form. Replace the subject with value.
- return fmt.Sprintf("`%s`%s", value, doc[len(name):])
- }
- return fmt.Sprintf("`%s`: %s", value, doc)
-}
-
-func loadCommands(pkg *packages.Package) ([]*source.CommandJSON, error) {
- var commands []*source.CommandJSON
-
- _, cmds, err := commandmeta.Load()
- if err != nil {
- return nil, err
- }
- // Parse the objects it contains.
- for _, cmd := range cmds {
- cmdjson := &source.CommandJSON{
- Command: cmd.Name,
- Title: cmd.Title,
- Doc: cmd.Doc,
- ArgDoc: argsDoc(cmd.Args),
- }
- if cmd.Result != nil {
- cmdjson.ResultDoc = typeDoc(cmd.Result, 0)
- }
- commands = append(commands, cmdjson)
- }
- return commands, nil
-}
-
-func argsDoc(args []*commandmeta.Field) string {
- var b strings.Builder
- for i, arg := range args {
- b.WriteString(typeDoc(arg, 0))
- if i != len(args)-1 {
- b.WriteString(",\n")
- }
- }
- return b.String()
-}
-
-func typeDoc(arg *commandmeta.Field, level int) string {
- // Max level to expand struct fields.
- const maxLevel = 3
- if len(arg.Fields) > 0 {
- if level < maxLevel {
- return arg.FieldMod + structDoc(arg.Fields, level)
- }
- return "{ ... }"
- }
- under := arg.Type.Underlying()
- switch u := under.(type) {
- case *types.Slice:
- return fmt.Sprintf("[]%s", u.Elem().Underlying().String())
- }
- return types.TypeString(under, nil)
-}
-
-func structDoc(fields []*commandmeta.Field, level int) string {
- var b strings.Builder
- b.WriteString("{\n")
- indent := strings.Repeat("\t", level)
- for _, fld := range fields {
- if fld.Doc != "" && level == 0 {
- doclines := strings.Split(fld.Doc, "\n")
- for _, line := range doclines {
- fmt.Fprintf(&b, "%s\t// %s\n", indent, line)
- }
- }
- tag := strings.Split(fld.JSONTag, ",")[0]
- if tag == "" {
- tag = fld.Name
- }
- fmt.Fprintf(&b, "%s\t%q: %s,\n", indent, tag, typeDoc(fld, level+1))
- }
- fmt.Fprintf(&b, "%s}", indent)
- return b.String()
-}
-
-func loadLenses(commands []*source.CommandJSON) []*source.LensJSON {
- all := map[command.Command]struct{}{}
- for k := range source.LensFuncs() {
- all[k] = struct{}{}
- }
- for k := range mod.LensFuncs() {
- if _, ok := all[k]; ok {
- panic(fmt.Sprintf("duplicate lens %q", string(k)))
- }
- all[k] = struct{}{}
- }
-
- var lenses []*source.LensJSON
-
- for _, cmd := range commands {
- if _, ok := all[command.Command(cmd.Command)]; ok {
- lenses = append(lenses, &source.LensJSON{
- Lens: cmd.Command,
- Title: cmd.Title,
- Doc: cmd.Doc,
- })
- }
- }
- return lenses
-}
-
-func loadAnalyzers(m map[string]*source.Analyzer) []*source.AnalyzerJSON {
- var sorted []string
- for _, a := range m {
- sorted = append(sorted, a.Analyzer.Name)
- }
- sort.Strings(sorted)
- var json []*source.AnalyzerJSON
- for _, name := range sorted {
- a := m[name]
- json = append(json, &source.AnalyzerJSON{
- Name: a.Analyzer.Name,
- Doc: a.Analyzer.Doc,
- Default: a.Enabled,
- })
- }
- return json
-}
-
-func lowerFirst(x string) string {
- if x == "" {
- return x
- }
- return strings.ToLower(x[:1]) + x[1:]
-}
-
-func upperFirst(x string) string {
- if x == "" {
- return x
- }
- return strings.ToUpper(x[:1]) + x[1:]
-}
-
-func fileForPos(pkg *packages.Package, pos token.Pos) (*ast.File, error) {
- fset := pkg.Fset
- for _, f := range pkg.Syntax {
- if fset.Position(f.Pos()).Filename == fset.Position(pos).Filename {
- return f, nil
- }
- }
- return nil, fmt.Errorf("no file for pos %v", pos)
-}
-
-func rewriteFile(file string, api *source.APIJSON, write bool, rewrite func([]byte, *source.APIJSON) ([]byte, error)) (bool, error) {
- old, err := ioutil.ReadFile(file)
- if err != nil {
- return false, err
- }
-
- new, err := rewrite(old, api)
- if err != nil {
- return false, fmt.Errorf("rewriting %q: %v", file, err)
- }
-
- if !write {
- return bytes.Equal(old, new), nil
- }
-
- if err := ioutil.WriteFile(file, new, 0); err != nil {
- return false, err
- }
-
- return true, nil
-}
-
-func rewriteAPI(_ []byte, api *source.APIJSON) ([]byte, error) {
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "// Code generated by \"golang.org/x/tools/gopls/doc/generate\"; DO NOT EDIT.\n\npackage source\n\nvar GeneratedAPIJSON = ")
- if err := printsrc.NewPrinter("golang.org/x/tools/internal/lsp/source").Fprint(&buf, api); err != nil {
- return nil, err
- }
- return format.Source(buf.Bytes())
-}
-
-type optionsGroup struct {
- title string
- final string
- level int
- options []*source.OptionJSON
-}
-
-func rewriteSettings(doc []byte, api *source.APIJSON) ([]byte, error) {
- result := doc
- for category, opts := range api.Options {
- groups := collectGroups(opts)
-
- // First, print a table of contents.
- section := bytes.NewBuffer(nil)
- fmt.Fprintln(section, "")
- for _, h := range groups {
- writeBullet(section, h.final, h.level)
- }
- fmt.Fprintln(section, "")
-
- // Currently, the settings document has a title and a subtitle, so
- // start at level 3 for a header beginning with "###".
- baseLevel := 3
- for _, h := range groups {
- level := baseLevel + h.level
- writeTitle(section, h.final, level)
- for _, opt := range h.options {
- header := strMultiply("#", level+1)
- section.Write([]byte(fmt.Sprintf("%s ", header)))
- opt.Write(section)
- }
- }
- var err error
- result, err = replaceSection(result, category, section.Bytes())
- if err != nil {
- return nil, err
- }
- }
-
- section := bytes.NewBuffer(nil)
- for _, lens := range api.Lenses {
- fmt.Fprintf(section, "### **%v**\n\nIdentifier: `%v`\n\n%v\n", lens.Title, lens.Lens, lens.Doc)
- }
- return replaceSection(result, "Lenses", section.Bytes())
-}
-
-func collectGroups(opts []*source.OptionJSON) []optionsGroup {
- optsByHierarchy := map[string][]*source.OptionJSON{}
- for _, opt := range opts {
- optsByHierarchy[opt.Hierarchy] = append(optsByHierarchy[opt.Hierarchy], opt)
- }
-
- // As a hack, assume that uncategorized items are less important to
- // users and force the empty string to the end of the list.
- var containsEmpty bool
- var sorted []string
- for h := range optsByHierarchy {
- if h == "" {
- containsEmpty = true
- continue
- }
- sorted = append(sorted, h)
- }
- sort.Strings(sorted)
- if containsEmpty {
- sorted = append(sorted, "")
- }
- var groups []optionsGroup
- baseLevel := 0
- for _, h := range sorted {
- split := strings.SplitAfter(h, ".")
- last := split[len(split)-1]
- // Hack to capitalize all of UI.
- if last == "ui" {
- last = "UI"
- }
- // A hierarchy may look like "ui.formatting". If "ui" has no
- // options of its own, it may not be added to the map, but it
- // still needs a heading.
- components := strings.Split(h, ".")
- for i := 1; i < len(components); i++ {
- parent := strings.Join(components[0:i], ".")
- if _, ok := optsByHierarchy[parent]; !ok {
- groups = append(groups, optionsGroup{
- title: parent,
- final: last,
- level: baseLevel + i,
- })
- }
- }
- groups = append(groups, optionsGroup{
- title: h,
- final: last,
- level: baseLevel + strings.Count(h, "."),
- options: optsByHierarchy[h],
- })
- }
- return groups
-}
-
-func hardcodedEnumKeys(name string) bool {
- return name == "analyses" || name == "codelenses"
-}
-
-func writeBullet(w io.Writer, title string, level int) {
- if title == "" {
- return
- }
- // Capitalize the first letter of each title.
- prefix := strMultiply(" ", level)
- fmt.Fprintf(w, "%s* [%s](#%s)\n", prefix, capitalize(title), strings.ToLower(title))
-}
-
-func writeTitle(w io.Writer, title string, level int) {
- if title == "" {
- return
- }
- // Capitalize the first letter of each title.
- fmt.Fprintf(w, "%s %s\n\n", strMultiply("#", level), capitalize(title))
-}
-
-func capitalize(s string) string {
- return string(unicode.ToUpper(rune(s[0]))) + s[1:]
-}
-
-func strMultiply(str string, count int) string {
- var result string
- for i := 0; i < count; i++ {
- result += string(str)
- }
- return result
-}
-
-func rewriteCommands(doc []byte, api *source.APIJSON) ([]byte, error) {
- section := bytes.NewBuffer(nil)
- for _, command := range api.Commands {
- command.Write(section)
- }
- return replaceSection(doc, "Commands", section.Bytes())
-}
-
-func rewriteAnalyzers(doc []byte, api *source.APIJSON) ([]byte, error) {
- section := bytes.NewBuffer(nil)
- for _, analyzer := range api.Analyzers {
- fmt.Fprintf(section, "## **%v**\n\n", analyzer.Name)
- fmt.Fprintf(section, "%s\n\n", analyzer.Doc)
- switch analyzer.Default {
- case true:
- fmt.Fprintf(section, "**Enabled by default.**\n\n")
- case false:
- fmt.Fprintf(section, "**Disabled by default. Enable it by setting `\"analyses\": {\"%s\": true}`.**\n\n", analyzer.Name)
- }
- }
- return replaceSection(doc, "Analyzers", section.Bytes())
-}
-
-func replaceSection(doc []byte, sectionName string, replacement []byte) ([]byte, error) {
- re := regexp.MustCompile(fmt.Sprintf(`(?s)<!-- BEGIN %v.* -->\n(.*?)<!-- END %v.* -->`, sectionName, sectionName))
- idx := re.FindSubmatchIndex(doc)
- if idx == nil {
- return nil, fmt.Errorf("could not find section %q", sectionName)
- }
- result := append([]byte(nil), doc[:idx[2]]...)
- result = append(result, replacement...)
- result = append(result, doc[idx[3]:]...)
- return result, nil
-}
diff -urN a/gopls/doc/generate_test.go b/gopls/doc/generate_test.go
--- a/gopls/doc/generate_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/generate_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,26 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.16
-// +build go1.16
-
-package main
-
-import (
- "testing"
-
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestGenerated(t *testing.T) {
- testenv.NeedsGoBuild(t) // This is a lie. We actually need the source code.
-
- ok, err := doMain("../..", false)
- if err != nil {
- t.Fatal(err)
- }
- if !ok {
- t.Error("documentation needs updating. run: `go run doc/generate.go` from the gopls module.")
- }
-}
diff -urN a/gopls/doc/semantictokens.md b/gopls/doc/semantictokens.md
--- a/gopls/doc/semantictokens.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/semantictokens.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,121 +0,0 @@
-# Semantic Tokens
-
-The [LSP](https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocument_semanticTokens)
-specifies semantic tokens as a way of telling clients about language-specific
-properties of pieces of code in a file being edited.
-
-The client asks for a set of semantic tokens and modifiers. This note describe which ones
-gopls will return, and under what circumstances. Gopls has no control over how the client
-converts semantic tokens into colors (or some other visible indication). In vscode it
-is possible to modify the color a theme uses by setting the `editor.semanticTokenColorCustomizations`
-object. We provide a little [guidance](#Colors) later.
-
-There are 22 semantic tokens, with 10 possible modifiers. The protocol allows each semantic
-token to be used with any of the 1024 subsets of possible modifiers, but most combinations
-don't make intuitive sense (although `async documentation` has a certain appeal).
-
-The 22 semantic tokens are `namespace`, `type`, `class`, `enum`, `interface`,
- `struct`, `typeParameter`, `parameter`, `variable`, `property`, `enumMember`,
- `event`, `function`, `method`, `macro`, `keyword`, `modifier`, `comment`,
- `string`, `number`, `regexp`, `operator`.
-
-The 10 modifiers are `declaration`, `definition`, `readonly`, `static`,
- `deprecated`, `abstract`, `async`, `modification`, `documentation`, `defaultLibrary`.
-
-The authoritative lists are in the [specification](https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#semanticTokenTypes)
-
-For the implementation to work correctly the client and server have to agree on the ordering
-of the tokens and of the modifiers. Gopls, therefore, will only send tokens and modifiers
-that the client has asked for. This document says what gopls would send if the client
-asked for everything. By default, vscode asks for everything.
-
-Gopls sends 11 token types for `.go` files and 1 for `.*tmpl` files.
-Nothing is sent for any other kind of file.
-This all could change. (When Go has generics, gopls will return `typeParameter`.)
-
-For `.*tmpl` files gopls sends `macro`, and no modifiers, for each `{{`...`}}` scope.
-
-## Semantic tokens for Go files
-
-There are two contrasting guiding principles that might be used to decide what to mark
-with semantic tokens. All clients already do some kind of syntax marking. E.g., vscode
-uses a TextMate grammar. The minimal principle would send semantic tokens only for those
-language features that cannot be reliably found without parsing Go and looking at types.
-The maximal principle would attempt to convey as much as possible about the Go code,
-using all available parsing and type information.
-
-There is much to be said for returning minimal information, but the minimal principle is
-not well-specified. Gopls has no way of knowing what the clients know about the Go program
-being edited. Even in vscode the TextMate grammars can be more or less elaborate
-and change over time. (Nonetheless, a minimal implementation would not return `keyword`,
-`number`, `comment`, or `string`.)
-
-The maximal position isn't particularly well-specified either. To chose one example, a
-format string might have formatting codes (`%[4]-3.6f`), escape sequences (`\U00010604`), and regular
-characters. Should these all be distinguished? One could even imagine distinguishing
-different runes by their Unicode language assignment, or some other Unicode property, such as
-being [confusable](http://www.unicode.org/Public/security/10.0.0/confusables.txt).
-
-Gopls does not come close to either of these principles. Semantic tokens are returned for
-identifiers, keywords, operators, comments, and literals. (Sematic tokens do not
-cover the file. They are not returned for
-white space or punctuation, and there is no semantic token for labels.)
-The following describes more precisely what gopls
-does, with a few notes on possible alternative choices.
-The references to *object* refer to the
-```types.Object``` returned by the type checker. The references to *nodes* refer to the
-```ast.Node``` from the parser.
-
-1. __`keyword`__ All Go [keywords](https://golang.org/ref/spec#Keywords) are marked `keyword`.
-1. __`namespace`__ All package names are marked `namespace`. In an import, if there is an
-alias, it would be marked. Otherwise the last component of the import path is marked.
-1. __`type`__ Objects of type ```types.TypeName``` are marked `type`.
-If they are also ```types.Basic```
-the modifier is `defaultLibrary`. (And in ```type B struct{C}```, ```B``` has modifier `definition`.)
-1. __`parameter`__ The formal arguments in ```ast.FuncDecl``` and ```ast.FuncType``` nodes are marked `parameter`.
-1. __`variable`__ Identifiers in the
-scope of ```const``` are modified with `readonly`. ```nil``` is usually a `variable` modified with both
-`readonly` and `defaultLibrary`. (```nil``` is a predefined identifier; the user can redefine it,
-in which case it would just be a variable, or whatever.) Identifiers of type ```types.Variable``` are,
-not surprisingly, marked `variable`. Identifiers being defined (node ```ast.GenDecl```) are modified
-by `definition` and, if appropriate, `readonly`. Receivers (in method declarations) are
-`variable`.
-1. __`method`__ Methods are marked at their definition (```func (x foo) bar() {}```) or declaration
-in an ```interface```. Methods are not marked where they are used.
-In ```x.bar()```, ```x``` will be marked
-either as a `namespace` if it is a package name, or as a `variable` if it is an interface value,
-so distinguishing ```bar``` seemed superfluous.
-1. __`function`__ Bultins (```types.Builtin```) are modified with `defaultLibrary`
-(e.g., ```make```, ```len```, ```copy```). Identifiers whose
-object is ```types.Func``` or whose node is ```ast.FuncDecl``` are `function`.
-1. __`comment`__ Comments and struct tags. (Perhaps struct tags should be `property`?)
-1. __`string`__ Strings. Could add modifiers for e.g., escapes or format codes.
-1. __`number`__ Numbers. Should the ```i``` in ```23i``` be handled specially?
-1. __`operator`__ Assignment operators, binary operators, ellipses (```...```), increment/decrement
-operators, sends (```<-```), and unary operators.
-
-Gopls will send the modifier `deprecated` if it finds a comment
-```// deprecated``` in the godoc.
-
-The unused tokens for Go code are `class`, `enum`, `interface`,
- `struct`, `typeParameter`, `property`, `enumMember`,
- `event`, `macro`, `modifier`,
- `regexp`
-
-## Colors
-
-These comments are about vscode.
-
-The documentation has a [helpful](https://code.visualstudio.com/api/language-extensions/semantic-highlight-guide#custom-textmate-scope-mappings)
-description of which semantic tokens correspond to scopes in TextMate grammars. Themes seem
-to use the TextMate scopes to decide on colors.
-
-Some examples of color customizations are [here](https://medium.com/@danromans/how-to-customize-semantic-token-colorization-with-visual-studio-code-ac3eab96141b).
-
-## Note
-
-While a file is being edited it may temporarily contain either
-parsing errors or type errors. In this case gopls cannot determine some (or maybe any)
-of the semantic tokens. To avoid weird flickering it is the responsibility
-of clients to maintain the semantic token information
-in the unedited part of the file, and they do.
\ No newline at end of file
diff -urN a/gopls/doc/settings.md b/gopls/doc/settings.md
--- a/gopls/doc/settings.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/settings.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,484 +0,0 @@
-# Settings
-
-<!--TODO: Generate this file from the documentation in golang/org/x/tools/internal/lsp/source/options.go.-->
-
-This document describes the global settings for `gopls` inside the editor.
-The settings block will be called `"gopls"` and contains a collection of
-controls for `gopls` that the editor is not expected to understand or control.
-These settings can also be configured differently per workspace folder.
-
-In VSCode, this would be a section in your `settings.json` file that might look
-like this:
-
-```json5
- "gopls": {
- "ui.completion.usePlaceholders": true,
- ...
- },
-```
-
-## Officially supported
-
-Below is the list of settings that are officially supported for `gopls`.
-
-Any settings that are experimental or for debugging purposes are marked as
-such.
-
-To enable all experimental features, use **allExperiments: `true`**. You will
-still be able to independently override specific experimental features.
-
-<!-- BEGIN User: DO NOT MANUALLY EDIT THIS SECTION -->
-
-* [Build](#build)
-* [Formatting](#formatting)
-* [UI](#ui)
- * [Completion](#completion)
- * [Diagnostic](#diagnostic)
- * [Documentation](#documentation)
- * [Navigation](#navigation)
-
-### Build
-
-#### **buildFlags** *[]string*
-
-buildFlags is the set of flags passed on to the build system when invoked.
-It is applied to queries like `go list`, which is used when discovering files.
-The most common use is to set `-tags`.
-
-Default: `[]`.
-
-#### **env** *map[string]string*
-
-env adds environment variables to external commands run by `gopls`, most notably `go list`.
-
-Default: `{}`.
-
-#### **directoryFilters** *[]string*
-
-directoryFilters can be used to exclude unwanted directories from the
-workspace. By default, all directories are included. Filters are an
-operator, `+` to include and `-` to exclude, followed by a path prefix
-relative to the workspace folder. They are evaluated in order, and
-the last filter that applies to a path controls whether it is included.
-The path prefix can be empty, so an initial `-` excludes everything.
-
-Examples:
-
-Exclude node_modules: `-node_modules`
-
-Include only project_a: `-` (exclude everything), `+project_a`
-
-Include only project_a, but not node_modules inside it: `-`, `+project_a`, `-project_a/node_modules`
-
-Default: `["-node_modules"]`.
-
-#### **templateExtensions** *[]string*
-
-templateExtensions gives the extensions of file names that are treateed
-as template files. (The extension
-is the part of the file name after the final dot.)
-
-Default: `[]`.
-
-#### **memoryMode** *enum*
-
-**This setting is experimental and may be deleted.**
-
-memoryMode controls the tradeoff `gopls` makes between memory usage and
-correctness.
-
-Values other than `Normal` are untested and may break in surprising ways.
-
-Must be one of:
-
-* `"DegradeClosed"`: In DegradeClosed mode, `gopls` will collect less information about
-packages without open files. As a result, features like Find
-References and Rename will miss results in such packages.
-* `"Normal"`
-
-Default: `"Normal"`.
-
-#### **expandWorkspaceToModule** *bool*
-
-**This setting is experimental and may be deleted.**
-
-expandWorkspaceToModule instructs `gopls` to adjust the scope of the
-workspace to find the best available module root. `gopls` first looks for
-a go.mod file in any parent directory of the workspace folder, expanding
-the scope to that directory if it exists. If no viable parent directory is
-found, gopls will check if there is exactly one child directory containing
-a go.mod file, narrowing the scope to that directory if it exists.
-
-Default: `true`.
-
-#### **experimentalWorkspaceModule** *bool*
-
-**This setting is experimental and may be deleted.**
-
-experimentalWorkspaceModule opts a user into the experimental support
-for multi-module workspaces.
-
-Default: `false`.
-
-#### **experimentalPackageCacheKey** *bool*
-
-**This setting is experimental and may be deleted.**
-
-experimentalPackageCacheKey controls whether to use a coarser cache key
-for package type information to increase cache hits. This setting removes
-the user's environment, build flags, and working directory from the cache
-key, which should be a safe change as all relevant inputs into the type
-checking pass are already hashed into the key. This is temporarily guarded
-by an experiment because caching behavior is subtle and difficult to
-comprehensively test.
-
-Default: `true`.
-
-#### **allowModfileModifications** *bool*
-
-**This setting is experimental and may be deleted.**
-
-allowModfileModifications disables -mod=readonly, allowing imports from
-out-of-scope modules. This option will eventually be removed.
-
-Default: `false`.
-
-#### **allowImplicitNetworkAccess** *bool*
-
-**This setting is experimental and may be deleted.**
-
-allowImplicitNetworkAccess disables GOPROXY=off, allowing implicit module
-downloads rather than requiring user action. This option will eventually
-be removed.
-
-Default: `false`.
-
-#### **experimentalUseInvalidMetadata** *bool*
-
-**This setting is experimental and may be deleted.**
-
-experimentalUseInvalidMetadata enables gopls to fall back on outdated
-package metadata to provide editor features if the go command fails to
-load packages for some reason (like an invalid go.mod file). This will
-eventually be the default behavior, and this setting will be removed.
-
-Default: `false`.
-
-### Formatting
-
-#### **local** *string*
-
-local is the equivalent of the `goimports -local` flag, which puts
-imports beginning with this string after third-party packages. It should
-be the prefix of the import path whose imports should be grouped
-separately.
-
-Default: `""`.
-
-#### **gofumpt** *bool*
-
-gofumpt indicates if we should run gofumpt formatting.
-
-Default: `false`.
-
-### UI
-
-#### **codelenses** *map[string]bool*
-
-codelenses overrides the enabled/disabled state of code lenses. See the
-"Code Lenses" section of the
-[Settings page](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#code-lenses)
-for the list of supported lenses.
-
-Example Usage:
-
-```json5
-"gopls": {
-...
- "codelenses": {
- "generate": false, // Don't show the `go generate` lens.
- "gc_details": true // Show a code lens toggling the display of gc's choices.
- }
-...
-}
-```
-
-Default: `{"gc_details":false,"generate":true,"regenerate_cgo":true,"tidy":true,"upgrade_dependency":true,"vendor":true}`.
-
-#### **semanticTokens** *bool*
-
-**This setting is experimental and may be deleted.**
-
-semanticTokens controls whether the LSP server will send
-semantic tokens to the client.
-
-Default: `false`.
-
-#### Completion
-
-##### **usePlaceholders** *bool*
-
-placeholders enables placeholders for function parameters or struct
-fields in completion responses.
-
-Default: `false`.
-
-##### **completionBudget** *time.Duration*
-
-**This setting is for debugging purposes only.**
-
-completionBudget is the soft latency goal for completion requests. Most
-requests finish in a couple milliseconds, but in some cases deep
-completions can take much longer. As we use up our budget we
-dynamically reduce the search scope to ensure we return timely
-results. Zero means unlimited.
-
-Default: `"100ms"`.
-
-##### **matcher** *enum*
-
-**This is an advanced setting and should not be configured by most `gopls` users.**
-
-matcher sets the algorithm that is used when calculating completion
-candidates.
-
-Must be one of:
-
-* `"CaseInsensitive"`
-* `"CaseSensitive"`
-* `"Fuzzy"`
-
-Default: `"Fuzzy"`.
-
-##### **experimentalPostfixCompletions** *bool*
-
-**This setting is experimental and may be deleted.**
-
-experimentalPostfixCompletions enables artificial method snippets
-such as "someSlice.sort!".
-
-Default: `true`.
-
-#### Diagnostic
-
-##### **analyses** *map[string]bool*
-
-analyses specify analyses that the user would like to enable or disable.
-A map of the names of analysis passes that should be enabled/disabled.
-A full list of analyzers that gopls uses can be found
-[here](https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md).
-
-Example Usage:
-
-```json5
-...
-"analyses": {
- "unreachable": false, // Disable the unreachable analyzer.
- "unusedparams": true // Enable the unusedparams analyzer.
-}
-...
-```
-
-Default: `{}`.
-
-##### **staticcheck** *bool*
-
-**This setting is experimental and may be deleted.**
-
-staticcheck enables additional analyses from staticcheck.io.
-
-Default: `false`.
-
-##### **annotations** *map[string]bool*
-
-**This setting is experimental and may be deleted.**
-
-annotations specifies the various kinds of optimization diagnostics
-that should be reported by the gc_details command.
-
-Can contain any of:
-
-* `"bounds"` controls bounds checking diagnostics.
-* `"escape"` controls diagnostics about escape choices.
-* `"inline"` controls diagnostics about inlining choices.
-* `"nil"` controls nil checks.
-
-Default: `{"bounds":true,"escape":true,"inline":true,"nil":true}`.
-
-##### **diagnosticsDelay** *time.Duration*
-
-**This is an advanced setting and should not be configured by most `gopls` users.**
-
-diagnosticsDelay controls the amount of time that gopls waits
-after the most recent file modification before computing deep diagnostics.
-Simple diagnostics (parsing and type-checking) are always run immediately
-on recently modified packages.
-
-This option must be set to a valid duration string, for example `"250ms"`.
-
-Default: `"250ms"`.
-
-##### **experimentalWatchedFileDelay** *time.Duration*
-
-**This setting is experimental and may be deleted.**
-
-experimentalWatchedFileDelay controls the amount of time that gopls waits
-for additional workspace/didChangeWatchedFiles notifications to arrive,
-before processing all such notifications in a single batch. This is
-intended for use by LSP clients that don't support their own batching of
-file system notifications.
-
-This option must be set to a valid duration string, for example `"100ms"`.
-
-Default: `"0s"`.
-
-#### Documentation
-
-##### **hoverKind** *enum*
-
-hoverKind controls the information that appears in the hover text.
-SingleLine and Structured are intended for use only by authors of editor plugins.
-
-Must be one of:
-
-* `"FullDocumentation"`
-* `"NoDocumentation"`
-* `"SingleLine"`
-* `"Structured"` is an experimental setting that returns a structured hover format.
-This format separates the signature from the documentation, so that the client
-can do more manipulation of these fields.\
-This should only be used by clients that support this behavior.
-* `"SynopsisDocumentation"`
-
-Default: `"FullDocumentation"`.
-
-##### **linkTarget** *string*
-
-linkTarget controls where documentation links go.
-It might be one of:
-
-* `"godoc.org"`
-* `"pkg.go.dev"`
-
-If company chooses to use its own `godoc.org`, its address can be used as well.
-
-Default: `"pkg.go.dev"`.
-
-##### **linksInHover** *bool*
-
-linksInHover toggles the presence of links to documentation in hover.
-
-Default: `true`.
-
-#### Navigation
-
-##### **importShortcut** *enum*
-
-importShortcut specifies whether import statements should link to
-documentation or go to definitions.
-
-Must be one of:
-
-* `"Both"`
-* `"Definition"`
-* `"Link"`
-
-Default: `"Both"`.
-
-##### **symbolMatcher** *enum*
-
-**This is an advanced setting and should not be configured by most `gopls` users.**
-
-symbolMatcher sets the algorithm that is used when finding workspace symbols.
-
-Must be one of:
-
-* `"CaseInsensitive"`
-* `"CaseSensitive"`
-* `"FastFuzzy"`
-* `"Fuzzy"`
-
-Default: `"FastFuzzy"`.
-
-##### **symbolStyle** *enum*
-
-**This is an advanced setting and should not be configured by most `gopls` users.**
-
-symbolStyle controls how symbols are qualified in symbol responses.
-
-Example Usage:
-
-```json5
-"gopls": {
-...
- "symbolStyle": "Dynamic",
-...
-}
-```
-
-Must be one of:
-
-* `"Dynamic"` uses whichever qualifier results in the highest scoring
-match for the given symbol query. Here a "qualifier" is any "/" or "."
-delimited suffix of the fully qualified symbol. i.e. "to/pkg.Foo.Field" or
-just "Foo.Field".
-* `"Full"` is fully qualified symbols, i.e.
-"path/to/pkg.Foo.Field".
-* `"Package"` is package qualified symbols i.e.
-"pkg.Foo.Field".
-
-Default: `"Dynamic"`.
-
-#### **verboseOutput** *bool*
-
-**This setting is for debugging purposes only.**
-
-verboseOutput enables additional debug logging.
-
-Default: `false`.
-
-<!-- END User: DO NOT MANUALLY EDIT THIS SECTION -->
-
-## Code Lenses
-
-These are the code lenses that `gopls` currently supports. They can be enabled
-and disabled using the `codelenses` setting, documented above. Their names and
-features are subject to change.
-
-<!-- BEGIN Lenses: DO NOT MANUALLY EDIT THIS SECTION -->
-### **Toggle gc_details**
-
-Identifier: `gc_details`
-
-Toggle the calculation of gc annotations.
-### **Run go generate**
-
-Identifier: `generate`
-
-Runs `go generate` for a given directory.
-### **Regenerate cgo**
-
-Identifier: `regenerate_cgo`
-
-Regenerates cgo definitions.
-### **Run test(s) (legacy)**
-
-Identifier: `test`
-
-Runs `go test` for a specific set of test or benchmark functions.
-### **Run go mod tidy**
-
-Identifier: `tidy`
-
-Runs `go mod tidy` for a module.
-### **Upgrade a dependency**
-
-Identifier: `upgrade_dependency`
-
-Upgrades a dependency in the go.mod file for a module.
-### **Run go mod vendor**
-
-Identifier: `vendor`
-
-Runs `go mod vendor` for a module.
-<!-- END Lenses: DO NOT MANUALLY EDIT THIS SECTION -->
diff -urN a/gopls/doc/subl.md b/gopls/doc/subl.md
--- a/gopls/doc/subl.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/subl.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,81 +0,0 @@
-# Sublime Text
-
-Use the [LSP] package. After installing it using Package Control, do the following:
-
-* Open the **Command Palette**
-* Find and run the command **LSP: Enable Language Server Globally**
-* Select the **gopls** item. Be careful not to select the similarly named *golsp* by mistake.
-
-Finally, you should familiarise yourself with the LSP package's *Settings* and *Key Bindings*. Find them under the menu item **Preferences > Package Settings > LSP**.
-
-## Examples
-Minimal global LSP settings, that assume **gopls** and **go** appear on the PATH seen by Sublime Text:<br>
-```
-{
- "clients": {
- "gopls": {
- "enabled": true,
- }
- }
-}
-```
-
-Global LSP settings that supply a specific PATH for finding **gopls** and **go**, as well as some settings for Sublime LSP itself:
-```
-{
- "clients": {
- "gopls": {
- "enabled": true,
- "env": {
- "PATH": "/path/to/your/go/bin",
- }
- }
- },
- // Recommended by https://agniva.me/gopls/2021/01/02/setting-up-gopls-sublime.html
- // except log_stderr mentioned there is no longer recognized.
- "show_references_in_quick_panel": true,
- "log_debug": true,
- // These two are recommended by LSP-json as replacement for deprecated only_show_lsp_completions
- "inhibit_snippet_completions": true,
- "inhibit_word_completions": true,
- }
- ```
-
-LSP and gopls settings can also be adjusted on a per-project basis to override global settings.
-```
-{
- "folders": [
- {
- "path": "/path/to/a/folder/one"
- },
- {
- // If you happen to be working on Go itself, this can be helpful; go-dev/bin should be on PATH.
- "path": "/path/to/your/go-dev/src/cmd"
- }
- ],
- "settings": {
- "LSP": {
- "gopls": {
- // To use a specific version of gopls with Sublime Text LSP (e.g., to try new features in development)
- "command": [
- "/path/to/your/go/bin/gopls"
- ],
- "env": {
- "PATH": "/path/to/your/go-dev/bin:/path/to/your/go/bin",
- "GOPATH": "",
- },
- "settings": {
- "experimentalWorkspaceModule": true
- }
- }
- },
- // This will apply for all languages in this project that have
- // LSP servers, not just Go, however cannot enable just for Go.
- "lsp_format_on_save": true,
- }
-}
-```
-
-Usually changes to these settings are recognized after saving the project file, but it may sometimes be necessary to either restart the server(s) (**Tools > LSP > Restart Servers**) or quit and restart Sublime Text itself.
-
-[LSP]: https://packagecontrol.io/packages/LSP
diff -urN a/gopls/doc/troubleshooting.md b/gopls/doc/troubleshooting.md
--- a/gopls/doc/troubleshooting.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/troubleshooting.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,48 +0,0 @@
-# Troubleshooting
-
-If you suspect that `gopls` is crashing or not working correctly, please follow the troubleshooting steps below.
-
-If `gopls` is using too much memory, please follow the steps under [Memory usage](#debug-memory-usage).
-
-## Steps
-
-VS Code users should follow [their troubleshooting guide](https://github.com/golang/vscode-go/blob/master/docs/troubleshooting.md), which has more a more specific version of these instructions.
-
-1. Verify that your project is in good shape by working with it outside of your editor. Running a command like `go build ./...` in the workspace directory will compile everything. For modules, `go mod tidy` is another good check, though it may modify your `go.mod`.
-1. Check that your editor isn't showing any diagnostics that indicate a problem with your workspace. They may appear as diagnostics on a Go file's package declaration, diagnostics in a go.mod file, or as a status or progress message. Problems in the workspace configuration can cause many different symptoms. See the [workspace setup instructions](workspace.md) for help.
-1. Make sure `gopls` is up to date by following the [installation instructions](../README.md#installation), then [restarting gopls](#restart-gopls).
-1. Optionally, [ask for help](#ask-for-help) on Gophers Slack.
-1. Finally, [report the issue](#file-an-issue) to the `gopls` developers.
-
-## Restart `gopls`
-
-`gopls` has no persistent state, so restarting it will fix transient problems. This is good and bad: good, because you can keep working, and bad, because you won't be able to debug the issue until it recurs.
-
-In most cases, closing all your open editors will guarantee that `gopls` is killed and restarted. If you don't want to do that, there may be an editor command you can use to restart only `gopls`. Note that some `vim` configurations keep the server alive for a while after the editor exits; you may need to explicitly kill `gopls` if you use `vim`.
-
-## Ask for help
-
-Gophers Slack has active editor-specific channels like [#emacs](https://gophers.slack.com/archives/C0HKHULEM), [#vim](https://gophers.slack.com/archives/C07GBR52P), and [#vscode](https://gophers.slack.com/archives/C2B4L99RS) that can help debug further. If you're confident the problem is with `gopls`, you can go straight to [#gopls](https://gophers.slack.com/archives/CJZH85XCZ). Invites are [available to everyone](https://invite.slack.golangbridge.org). Come prepared with a short description of the issue, and try to be available to answer questions for a while afterward.
-
-## File an issue
-
-We can't diagnose a problem from just a description. When filing an issue, please include as much as possible of the following information:
-
-1. Your editor and any settings you have configured (for example, your VSCode `settings.json` file).
-1. A sample program that reproduces the issue, if possible.
-1. The output of `gopls version` on the command line.
-1. A complete gopls log file from a session where the issue occurred. It should have a `go env for <workspace folder>` log line near the beginning. It's also helpful to tell us the timestamp the problem occurred, so we can find it the log. See the [instructions](#capture-logs) for information on how to capture gopls logs.
-
-Your editor may have a command that fills out some of the necessary information, such as `:GoReportGitHubIssue` in `vim-go`. Otherwise, you can use `gopls bug` on the command line. If neither of those work you can start from scratch directly on the [Go issue tracker](https://github.com/golang/go/issues/new?title=x%2Ftools%2Fgopls%3A%20%3Cfill%20this%20in%3E).
-
-## Capture logs
-
-You may have to change your editor's configuration to pass a `-logfile` flag to gopls.
-
-To increase the level of detail in your logs, start `gopls` with the `-rpc.trace` flag. To start a debug server that will allow you to see profiles and memory usage, start `gopls` with `serve --debug=localhost:6060`. You will then be able to view debug information by navigating to `localhost:6060`.
-
-If you are unsure of how to pass a flag to `gopls` through your editor, please see the [documentation for your editor](../README.md#editors).
-
-## Debug memory usage
-
-`gopls` automatically writes out memory debug information when your usage exceeds 1GB. This information can be found in your temporary directory with names like `gopls.1234-5GiB-withnames.zip`. On Windows, your temporary directory will be located at `%TMP%`, and on Unixes, it will be `$TMPDIR`, which is usually `/tmp`. Please [file an issue](#file-an-issue) with this memory debug information attached. If you are uncomfortable sharing the package names of your code, you can share the `-nonames` zip instead, but it's much less useful.
diff -urN a/gopls/doc/vim.md b/gopls/doc/vim.md
--- a/gopls/doc/vim.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/vim.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,225 +0,0 @@
-# Vim / Neovim
-
-* [vim-go](#vimgo)
-* [LanguageClient-neovim](#lcneovim)
-* [Ale](#ale)
-* [vim-lsp](#vimlsp)
-* [vim-lsc](#vimlsc)
-* [coc.nvim](#cocnvim)
-* [govim](#govim)
-* [Neovim v0.5.0+](#neovim)
- * [Installation](#neovim-install)
- * [Custom Configuration](#neovim-config)
- * [Imports](#neovim-imports)
- * [Omnifunc](#neovim-omnifunc)
- * [Additional Links](#neovim-links)
-
-## <a href="#vimgo" id="vimgo">vim-go</a>
-
-Use [vim-go] ver 1.20+, with the following configuration:
-
-```vim
-let g:go_def_mode='gopls'
-let g:go_info_mode='gopls'
-```
-
-## <a href="#lcneovim" id="lcneovim">LanguageClient-neovim</a>
-
-Use [LanguageClient-neovim], with the following configuration:
-
-```vim
-" Launch gopls when Go files are in use
-let g:LanguageClient_serverCommands = {
- \ 'go': ['gopls']
- \ }
-" Run gofmt on save
-autocmd BufWritePre *.go :call LanguageClient#textDocument_formatting_sync()
-```
-
-## <a href="#ale" id="ale">Ale</a>
-
-Use [ale]:
-
-```vim
-let g:ale_linters = {
- \ 'go': ['gopls'],
- \}
-```
-
-see [this issue][ale-issue-2179]
-
-## <a href="#vimlsp" id="vimlsp">vim-lsp</a>
-
-Use [prabirshrestha/vim-lsp], with the following configuration:
-
-```vim
-augroup LspGo
- au!
- autocmd User lsp_setup call lsp#register_server({
- \ 'name': 'go-lang',
- \ 'cmd': {server_info->['gopls']},
- \ 'whitelist': ['go'],
- \ })
- autocmd FileType go setlocal omnifunc=lsp#complete
- "autocmd FileType go nmap <buffer> gd <plug>(lsp-definition)
- "autocmd FileType go nmap <buffer> ,n <plug>(lsp-next-error)
- "autocmd FileType go nmap <buffer> ,p <plug>(lsp-previous-error)
-augroup END
-```
-
-## <a href="#vimlsc" id="vimlsc">vim-lsc</a>
-
-Use [natebosch/vim-lsc], with the following configuration:
-
-```vim
-let g:lsc_server_commands = {
-\ "go": {
-\ "command": "gopls serve",
-\ "log_level": -1,
-\ "suppress_stderr": v:true,
-\ },
-\}
-```
-
-The `log_level` and `suppress_stderr` parts are needed to prevent breakage from logging. See
-issues [#180](https://github.com/natebosch/vim-lsc/issues/180) and
-[#213](https://github.com/natebosch/vim-lsc/issues/213).
-
-## <a href="#cocnvim" id="cocnvim">coc.nvim</a>
-
-Use [coc.nvim], with the following `coc-settings.json` configuration:
-
-```json
- "languageserver": {
- "golang": {
- "command": "gopls",
- "rootPatterns": ["go.work", "go.mod", ".vim/", ".git/", ".hg/"],
- "filetypes": ["go"],
- "initializationOptions": {
- "usePlaceholders": true
- }
- }
- }
-```
-
-If you use `go.work` files, you may want to set the
-`workspace.workspaceFolderCheckCwd` option. This will force coc.nvim to search
-parent directories for `go.work` files, even if the current open directory has
-a `go.mod` file. See the
-[coc.nvim documentation](https://github.com/neoclide/coc.nvim/wiki/Using-workspaceFolders)
-for more details.
-
-Other [settings](settings.md) can be added in `initializationOptions` too.
-
-The `editor.action.organizeImport` code action will auto-format code and add missing imports. To run this automatically on save, add the following line to your `init.vim`:
-
-```vim
-autocmd BufWritePre *.go :call CocAction('runCommand', 'editor.action.organizeImport')
-```
-
-## <a href="#govim" id="govim">govim</a>
-
-In vim classic only, use the experimental [`govim`], simply follow the [install steps][govim-install].
-
-## <a href="#neovim" id="neovim">Neovim v0.5.0+</a>
-
-To use the new native LSP client in Neovim, make sure you
-[install][nvim-install] Neovim v.0.5.0+,
-the `nvim-lspconfig` configuration helper plugin, and check the
-[`gopls` configuration section][nvim-lspconfig] there.
-
-### <a href="#neovim-install" id="neovim-install">Installation</a>
-
-You can use Neovim's native plugin system. On a Unix system, you can do that by
-cloning the `nvim-lspconfig` repository into the correct directory:
-
-```sh
-dir="${HOME}/.local/share/nvim/site/pack/nvim-lspconfig/opt/nvim-lspconfig/"
-mkdir -p "$dir"
-cd "$dir"
-git clone 'https://github.com/neovim/nvim-lspconfig.git' .
-```
-
-### <a href="#neovim-config" id="neovim-config">Custom Configuration</a>
-
-You can add custom configuration using Lua. Here is an example of enabling the
-`unusedparams` check as well as `staticcheck`:
-
-```vim
-lua <<EOF
- lspconfig = require "lspconfig"
- util = require "lspconfig/util"
-
- lspconfig.gopls.setup {
- cmd = {"gopls", "serve"},
- filetypes = {"go", "gomod"},
- root_dir = util.root_pattern("go.work", "go.mod", ".git"),
- settings = {
- gopls = {
- analyses = {
- unusedparams = true,
- },
- staticcheck = true,
- },
- },
- }
-EOF
-```
-
-### <a href="#neovim-imports" id="neovim-imports">Imports</a>
-
-To get your imports ordered on save, like `goimports` does, you can define
-a helper function in Lua:
-
-```vim
-lua <<EOF
- -- …
-
- function OrgImports(wait_ms)
- local params = vim.lsp.util.make_range_params()
- params.context = {only = {"source.organizeImports"}}
- local result = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, wait_ms)
- for _, res in pairs(result or {}) do
- for _, r in pairs(res.result or {}) do
- if r.edit then
- vim.lsp.util.apply_workspace_edit(r.edit, "UTF-8")
- else
- vim.lsp.buf.execute_command(r.command)
- end
- end
- end
- end
-EOF
-
-autocmd BufWritePre *.go lua OrgImports(1000)
-```
-
-(Taken from the [discussion][nvim-lspconfig-imports] on Neovim issue tracker.)
-
-### <a href="#neovim-omnifunc" id="neovim-omnifunc">Omnifunc</a>
-
-To make your <kbd>Ctrl</kbd>+<kbd>x</kbd>,<kbd>Ctrl</kbd>+<kbd>o</kbd> work, add
-this to your `init.vim`:
-
-```vim
-autocmd FileType go setlocal omnifunc=v:lua.vim.lsp.omnifunc
-```
-
-### <a href="#neovim-links" id="neovim-links">Additional Links</a>
-
-* [Neovim's official LSP documentation][nvim-docs].
-
-[vim-go]: https://github.com/fatih/vim-go
-[LanguageClient-neovim]: https://github.com/autozimu/LanguageClient-neovim
-[ale]: https://github.com/w0rp/ale
-[ale-issue-2179]: https://github.com/w0rp/ale/issues/2179
-[prabirshrestha/vim-lsp]: https://github.com/prabirshrestha/vim-lsp/
-[natebosch/vim-lsc]: https://github.com/natebosch/vim-lsc/
-[natebosch/vim-lsc#180]: https://github.com/natebosch/vim-lsc/issues/180
-[coc.nvim]: https://github.com/neoclide/coc.nvim/
-[`govim`]: https://github.com/myitcv/govim
-[govim-install]: https://github.com/myitcv/govim/blob/master/README.md#govim---go-development-plugin-for-vim8
-[nvim-docs]: https://neovim.io/doc/user/lsp.html
-[nvim-install]: https://github.com/neovim/neovim/wiki/Installing-Neovim
-[nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#gopls
-[nvim-lspconfig-imports]: https://github.com/neovim/nvim-lspconfig/issues/115
diff -urN a/gopls/doc/workspace.md b/gopls/doc/workspace.md
--- a/gopls/doc/workspace.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/doc/workspace.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,95 +0,0 @@
-# Setting up your workspace
-
-`gopls` supports both Go module and GOPATH modes. However, it needs a defined
-scope in which language features like references, rename, and implementation
-should operate.
-
-The following options are available for configuring this scope:
-
-## Module mode
-
-### One module
-
-If you are working with a single module, you can open the module root (the
-directory containing the `go.mod` file), a subdirectory within the module,
-or a parent directory containing the module.
-
-**Note**: If you open a parent directory containing a module, it must **only**
-contain that single module. Otherwise, you are working with multiple modules.
-
-### Multiple modules
-
-Gopls has several alternatives for working on multiple modules simultaneously,
-described below. Starting with Go 1.18, Go workspaces are the preferred solution.
-
-#### Go workspaces (Go 1.18+)
-
-Starting with Go 1.18, the `go` command has native support for multi-module
-workspaces, via [`go.work`](https://go.dev/ref/mod#workspaces) files. These
-files are recognized by gopls starting with `gopls@v0.8.0`.
-
-The easiest way to work on multiple modules in Go 1.18 and later is therefore
-to create a `go.work` file containing the modules you wish to work on, and set
-your workspace root to the directory containing the `go.work` file.
-
-For example, suppose this repo is checked out into the `$WORK/tools` directory.
-We can work on both `golang.org/x/tools` and `golang.org/x/tools/gopls`
-simultaneously by creating a `go.work` file:
-
-```
-cd $WORK
-go work init
-go work use tools tools/gopls
-```
-
-...followed by opening the `$WORK` directory in our editor.
-
-#### Experimental workspace module (Go 1.17 and earlier)
-
-With earlier versions of Go, `gopls` can simulate multi-module workspaces by
-creating a synthetic module requiring the the modules in the workspace root.
-See [the design document](https://github.com/golang/proposal/blob/master/design/37720-gopls-workspaces.md)
-for more information.
-
-This feature is experimental, and will eventually be removed once `go.work`
-files are accepted by all supported Go versions.
-
-You can enable this feature by configuring the
-[experimentalWorkspaceModule](settings.md#experimentalworkspacemodule-bool)
-setting.
-
-#### Multiple workspace folders
-
-If neither of the above solutions work, and your editor allows configuring the
-set of
-["workspace folders"](https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#workspaceFolder)
-used during your LSP session, you can still work on multiple modules by adding
-a workspace folder at each module root (the locations of `go.mod` files). This
-means that each module has its own scope, and features will not work across
-modules.
-
-In VS Code, you can create a workspace folder by setting up a
-[multi-root workspace](https://code.visualstudio.com/docs/editor/multi-root-workspaces).
-View the [documentation for your editor plugin](../README.md#editor) to learn how to
-configure a workspace folder in your editor.
-
-### GOPATH mode
-
-When opening a directory within your GOPATH, the workspace scope will be just
-that directory.
-
-### At your own risk
-
-Some users or companies may have projects that encompass one `$GOPATH`. If you
-open your entire `$GOPATH` or `$GOPATH/src` folder, the workspace scope will be
-your entire `GOPATH`. If your GOPATH is large, `gopls` to be very slow to start
-because it will try to find all of the Go files in the directory you have
-opened. It will then load all of the files it has found.
-
-To work around this case, you can create a new `$GOPATH` that contains only the
-packages you want to work on.
-
----
-
-If you have additional use cases that are not mentioned above, please
-[file a new issue](https://github.com/golang/go/issues/new).
diff -urN a/gopls/go.mod b/gopls/go.mod
--- a/gopls/go.mod 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/go.mod 1970-01-01 01:00:00.000000000 +0100
@@ -1,28 +0,0 @@
-module golang.org/x/tools/gopls
-
-go 1.18
-
-require (
- github.com/google/go-cmp v0.5.7
- github.com/jba/printsrc v0.2.2
- github.com/jba/templatecheck v0.6.0
- github.com/sergi/go-diff v1.1.0
- golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
- golang.org/x/sys v0.0.0-20220209214540-3681064d5158
- golang.org/x/tools v0.1.11-0.20220330174940-8e193c2ba95e
- golang.org/x/vuln v0.0.0-20220503210553-a5481fb0c8be
- honnef.co/go/tools v0.3.0
- mvdan.cc/gofumpt v0.3.0
- mvdan.cc/xurls/v2 v2.4.0
-)
-
-require (
- github.com/BurntSushi/toml v1.0.0 // indirect
- github.com/google/safehtml v0.0.2 // indirect
- golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect
- golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
- golang.org/x/text v0.3.7 // indirect
- golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
-)
-
-replace golang.org/x/tools => ../
diff -urN a/gopls/go.sum b/gopls/go.sum
--- a/gopls/go.sum 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/go.sum 1970-01-01 01:00:00.000000000 +0100
@@ -1,95 +0,0 @@
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
-github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
-github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnXyBns=
-github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
-github.com/google/go-cmdtest v0.4.0/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/safehtml v0.0.2 h1:ZOt2VXg4x24bW0m2jtzAOkhoXV0iM8vNKc0paByCZqM=
-github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
-github.com/jba/printsrc v0.2.2 h1:9OHK51UT+/iMAEBlQIIXW04qvKyF3/vvLuwW/hL8tDU=
-github.com/jba/printsrc v0.2.2/go.mod h1:1xULjw59sL0dPdWpDoVU06TIEO/Wnfv6AHRpiElTwYM=
-github.com/jba/templatecheck v0.6.0 h1:SwM8C4hlK/YNLsdcXStfnHWE2HKkuTVwy5FKQHt5ro8=
-github.com/jba/templatecheck v0.6.0/go.mod h1:/1k7EajoSErFI9GLHAsiIJEaNLt3ALKNw2TV7z2SYv4=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
-github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
-github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
-github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM=
-golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211213223007-03aa0b5f6827/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
-golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/vuln v0.0.0-20220503210553-a5481fb0c8be h1:jokAF1mfylAi1iTQx7C44B7vyXUcSEMw8eDv0PzNu8s=
-golang.org/x/vuln v0.0.0-20220503210553-a5481fb0c8be/go.mod h1:twca1SxmF6/i2wHY/mj1vLIkkHdp+nil/yA32ZOP4kg=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
-honnef.co/go/tools v0.3.0 h1:2LdYUZ7CIxnYgskbUZfY7FPggmqnh6shBqfWa8Tn3XU=
-honnef.co/go/tools v0.3.0/go.mod h1:vlRD9XErLMGT+mDuofSr0mMMquscM/1nQqtRSsh6m70=
-mvdan.cc/gofumpt v0.3.0 h1:kTojdZo9AcEYbQYhGuLf/zszYthRdhDNDUi2JKTxas4=
-mvdan.cc/gofumpt v0.3.0/go.mod h1:0+VyGZWleeIj5oostkOex+nDBA0eyavuDnDusAJ8ylo=
-mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 h1:Jh3LAeMt1eGpxomyu3jVkmVZWW2MxZ1qIIV2TZ/nRio=
-mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5/go.mod h1:b8RRCBm0eeiWR8cfN88xeq2G5SG3VKGO+5UPWi5FSOY=
-mvdan.cc/xurls/v2 v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc=
-mvdan.cc/xurls/v2 v2.4.0/go.mod h1:+GEjq9uNjqs8LQfM9nVnM8rff0OQ5Iash5rzX+N1CSg=
diff -urN a/gopls/integration/govim/Dockerfile b/gopls/integration/govim/Dockerfile
--- a/gopls/integration/govim/Dockerfile 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/integration/govim/Dockerfile 1970-01-01 01:00:00.000000000 +0100
@@ -1,16 +0,0 @@
-# Copyright 2019 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# govim requires a more recent version of vim than is available in most
-# distros, so we build from their base image.
-FROM govim/govim:latest-vim
-ARG GOVIM_REF
-
-ENV GOPROXY=https://proxy.golang.org GOPATH=/go VIM_FLAVOR=vim
-WORKDIR /src
-
-# Clone govim. In order to use the go command for resolving latest, we download
-# a redundant copy of govim to the build cache using `go mod download`.
-RUN git clone https://github.com/govim/govim /src/govim && cd /src/govim && \
- git checkout $GOVIM_REF
diff -urN a/gopls/integration/govim/README.md b/gopls/integration/govim/README.md
--- a/gopls/integration/govim/README.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/integration/govim/README.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,47 +0,0 @@
-# govim integration tests
-
-Files in this directory configure Cloud Build to run [govim] integration tests
-against a gopls binary built from source.
-
-## Running on GCP
-
-To run these integration tests in Cloud Build, use the following steps. Here
-we assume that `$PROJECT_ID` is a valid GCP project and `$BUCKET` is a cloud
-storage bucket owned by that project.
-
-- `cd` to the root directory of the tools project.
-- (at least once per GCP project) Build the test harness:
-```
-$ gcloud builds submit \
- --project="${PROJECT_ID}" \
- --config=gopls/integration/govim/cloudbuild.harness.yaml
-```
-- Run the integration tests:
-```
-$ gcloud builds submit \
- --project="${PROJECT_ID}" \
- --config=gopls/integration/govim/cloudbuild.yaml \
- --substitutions=_RESULT_BUCKET="${BUCKET}"
-```
-
-## Fetching Artifacts
-
-Assuming the artifacts bucket is world readable, you can fetch integration from
-GCS. They are located at:
-
-- logs: `https://storage.googleapis.com/${BUCKET}/log-${EVALUATION_ID}.txt`
-- artifact tarball: `https://storage.googleapis.com/${BUCKET}/govim/${EVALUATION_ID}/artifacts.tar.gz`
-
-The `artifacts.go` command can be used to fetch both artifacts using an
-evaluation id.
-
-## Running locally
-
-Run `gopls/integration/govim/run_local.sh`. This may take a while the first
-time it is run, as it will require building the test harness. This script
-accepts two flags to modify its behavior:
-
-**--sudo**: run docker with `sudo`
-**--short**: run `go test -short`
-
-[govim]: https://github.com/govim/govim
diff -urN a/gopls/integration/govim/artifacts.go b/gopls/integration/govim/artifacts.go
--- a/gopls/integration/govim/artifacts.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/integration/govim/artifacts.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,67 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package main
-
-import (
- "flag"
- "fmt"
- "io/ioutil"
- "net/http"
- "os"
- "path"
-)
-
-var bucket = flag.String("bucket", "golang-gopls_integration_tests", "GCS bucket holding test artifacts.")
-
-const usage = `
-artifacts [--bucket=<bucket ID>] <cloud build evaluation ID>
-
-Fetch artifacts from an integration test run. Evaluation ID should be extracted
-from the cloud build notification.
-
-In order for this to work, the GCS bucket that artifacts were written to must
-be publicly readable. By default, this fetches from the
-golang-gopls_integration_tests bucket.
-`
-
-func main() {
- flag.Usage = func() {
- fmt.Fprint(flag.CommandLine.Output(), usage)
- }
- flag.Parse()
- if flag.NArg() != 1 {
- flag.Usage()
- os.Exit(2)
- }
- evalID := flag.Arg(0)
- logURL := fmt.Sprintf("https://storage.googleapis.com/%s/log-%s.txt", *bucket, evalID)
- if err := download(logURL); err != nil {
- fmt.Fprintf(os.Stderr, "downloading logs: %v", err)
- }
- tarURL := fmt.Sprintf("https://storage.googleapis.com/%s/govim/%s/artifacts.tar.gz", *bucket, evalID)
- if err := download(tarURL); err != nil {
- fmt.Fprintf(os.Stderr, "downloading artifact tarball: %v", err)
- }
-}
-
-func download(artifactURL string) error {
- name := path.Base(artifactURL)
- resp, err := http.Get(artifactURL)
- if err != nil {
- return fmt.Errorf("fetching from GCS: %v", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- return fmt.Errorf("got status code %d from GCS", resp.StatusCode)
- }
- data, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return fmt.Errorf("reading result: %v", err)
- }
- if err := ioutil.WriteFile(name, data, 0644); err != nil {
- return fmt.Errorf("writing artifact: %v", err)
- }
- return nil
-}
diff -urN a/gopls/integration/govim/cloudbuild.harness.yaml b/gopls/integration/govim/cloudbuild.harness.yaml
--- a/gopls/integration/govim/cloudbuild.harness.yaml 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/integration/govim/cloudbuild.harness.yaml 1970-01-01 01:00:00.000000000 +0100
@@ -1,21 +0,0 @@
-# Copyright 2019 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Build the govim test harness that will be used to run govim integration tests
-# for gopls. See README.md for instructions on how to use this.
-steps:
- - name: 'gcr.io/cloud-builders/docker'
- args: ['build',
- # To allow for breaking changes to this test harness, tag with a major
- # version number.
- '-t', 'gcr.io/$PROJECT_ID/govim-harness:latest',
- '-t', 'gcr.io/$PROJECT_ID/govim-harness:3',
- # It is assumed that this build is running from the root directory of the
- # tools repository.
- '-f', 'gopls/integration/govim/Dockerfile',
- # Use the integration test directory as build context: the test harness
- # doesn't actually require any local files.
- 'gopls/integration/govim']
-images:
- - gcr.io/$PROJECT_ID/govim-harness
diff -urN a/gopls/integration/govim/cloudbuild.yaml b/gopls/integration/govim/cloudbuild.yaml
--- a/gopls/integration/govim/cloudbuild.yaml 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/integration/govim/cloudbuild.yaml 1970-01-01 01:00:00.000000000 +0100
@@ -1,51 +0,0 @@
-# Copyright 2019 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Build gopls, and run the govim integration tests. See README.md for
-# instructions on how to use this.
-
-substitutions:
- # This bucket must be owned by the GCP project executing the build. If
- # you are running this from your own project, override using --substitutions.
- _RESULT_BUCKET: 'golang-gopls_integration_tests'
-
-steps:
- # Build gopls from source, to use with the govim integration tests.
- - name: 'golang:1.14'
- env: ['GOPROXY=https://proxy.golang.org']
- dir: 'gopls'
- args: ['go', 'build']
-
- # Run the tests. Note that the script in this step does not return the exit
- # code from `go test`, but rather saves it for use in the final step after
- # uploading artifacts.
- - name: 'gcr.io/$PROJECT_ID/govim-harness:3'
- dir: '/src/govim'
- volumes:
- - name: artifacts
- path: /artifacts
- env:
- - GOVIM_TESTSCRIPT_WORKDIR_ROOT=/artifacts
- - VIM_FLAVOR=vim
- args: ['/workspace/gopls/integration/govim/run_tests_for_cloudbuild.sh']
-
- # The govim tests produce a large number of artifacts; tarball/gzip to reduce
- # roundtrips and save space.
- - name: 'ubuntu'
- volumes:
- - name: artifacts
- path: /artifacts
- args: ['tar', '-czf', 'artifacts.tar.gz', '/artifacts']
-
- # Upload artifacts to GCS.
- - name: 'gcr.io/cloud-builders/gsutil'
- args: ['cp', 'artifacts.tar.gz', 'gs://${_RESULT_BUCKET}/govim/${BUILD_ID}/artifacts.tar.gz']
-
- # Exit with the actual exit code of the integration tests.
- - name: 'ubuntu'
- args: ['bash', 'govim_test_result.sh']
-
-# Write build logs to the same bucket as artifacts, so they can be more easily
-# shared.
-logsBucket: 'gs://${_RESULT_BUCKET}'
diff -urN a/gopls/integration/govim/run_local.sh b/gopls/integration/govim/run_local.sh
--- a/gopls/integration/govim/run_local.sh 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/integration/govim/run_local.sh 1970-01-01 01:00:00.000000000 +0100
@@ -1,96 +0,0 @@
-#!/bin/bash -e
-
-# Copyright 2019 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Run govim integration tests against a local gopls.
-
-usage() {
- cat <<EOUSAGE
-Usage: $0 [--sudo] [--short] [--version (semver|latest)]
-
-Args:
- --sudo run docker with sudo
- --short run `go test` with `-short`
- --version run on the specific tagged govim version (or latest) rather
- than the default branch
-
-Run govim tests against HEAD using local docker.
-EOUSAGE
-}
-
-SUDO_IF_NEEDED=
-TEST_SHORT=
-DOCKERFILE=gopls/integration/govim/Dockerfile
-GOVIM_REF=main
-while [[ $# -gt 0 ]]; do
- case "$1" in
- "-h" | "--help" | "help")
- usage
- exit 0
- ;;
- "--sudo")
- SUDO_IF_NEEDED="sudo "
- shift
- ;;
- "--short")
- TEST_SHORT="-short"
- shift
- ;;
- "--version")
- if [[ -z "$2" ]]; then
- usage
- exit 1
- fi
- GOVIM_REF=$2
- if [[ "${GOVIM_REF}" == "latest" ]]; then
- TMPGOPATH=$(mktemp -d)
- trap "GOPATH=${TMPGOPATH} go clean -modcache && rm -r ${TMPGOPATH}" EXIT
- GOVIM_REF=$(GOPATH=${TMPGOPATH} go mod download -json \
- github.com/govim/govim@latest | jq -r .Version)
- fi
- shift 2
- ;;
- *)
- usage
- exit 1
- esac
-done
-
-# Find the tools root, so that this script can be run from any directory.
-script_dir=$(dirname "$(readlink -f "$0")")
-tools_dir=$(readlink -f "${script_dir}/../../..")
-
-# Build gopls.
-cd "${tools_dir}/gopls"
-temp_gopls=$(mktemp -p "$PWD")
-trap "rm -f \"${temp_gopls}\"" EXIT
-# For consistency across environments, use golang docker to build rather than
-# the local go command.
-${SUDO_IF_NEEDED}docker run --rm -t \
- -v "${tools_dir}:/src/tools" \
- -w "/src/tools/gopls" \
- golang:rc \
- go build -o $(basename ${temp_gopls})
-
-# Build the test harness. Here we are careful to pass in a very limited build
-# context so as to optimize caching.
-echo "Checking out govim@${GOVIM_REF}"
-cd "${tools_dir}"
-${SUDO_IF_NEEDED}docker build \
- --build-arg GOVIM_REF="${GOVIM_REF}" \
- -t gopls-govim-harness:${GOVIM_REF} \
- -f gopls/integration/govim/Dockerfile \
- gopls/integration/govim
-
-# Run govim integration tests.
-echo "running govim integration tests using ${temp_gopls}"
-temp_gopls_name=$(basename "${temp_gopls}")
-${SUDO_IF_NEEDED}docker run --rm -t \
- -v "${tools_dir}:/src/tools" \
- -w "/src/govim" \
- --ulimit memlock=-1:-1 \
- gopls-govim-harness:${GOVIM_REF} \
- go test ${TEST_SHORT} ./cmd/govim \
- -gopls "/src/tools/gopls/${temp_gopls_name}"
diff -urN a/gopls/integration/govim/run_tests_for_cloudbuild.sh b/gopls/integration/govim/run_tests_for_cloudbuild.sh
--- a/gopls/integration/govim/run_tests_for_cloudbuild.sh 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/integration/govim/run_tests_for_cloudbuild.sh 1970-01-01 01:00:00.000000000 +0100
@@ -1,28 +0,0 @@
-#!/bin/bash
-
-# Copyright 2020 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# This script runs govim integration tests but always succeeds, instead writing
-# their result to a file so that any test failure can be deferred to a later
-# build step. We do this so that we can capture govim test artifacts regardless
-# of the test results.
-
-# Substitute the locally built gopls binary for use in govim integration tests.
-go test -short ./cmd/govim -gopls /workspace/gopls/gopls
-
-# Stash the error, for use in a later build step.
-echo "exit $?" > /workspace/govim_test_result.sh
-
-# Clean up unnecessary artifacts. This is based on govim/_scripts/tidyUp.bash.
-# Since we're fetching govim using the go command, we won't have this non-go
-# source directory available to us.
-if [[ -n "$GOVIM_TESTSCRIPT_WORKDIR_ROOT" ]]; then
- echo "Cleaning up build artifacts..."
- # Make artifacts writable so that rm -rf doesn't complain.
- chmod -R u+w "$GOVIM_TESTSCRIPT_WORKDIR_ROOT"
-
- # Remove directories we don't care about.
- find "$GOVIM_TESTSCRIPT_WORKDIR_ROOT" -type d \( -name .vim -o -name gopath \) -prune -exec rm -rf '{}' \;
-fi
diff -urN a/gopls/internal/coverage/coverage.go b/gopls/internal/coverage/coverage.go
--- a/gopls/internal/coverage/coverage.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/coverage/coverage.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,261 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go.1.16
-// +build go.1.16
-
-// Running this program in the tools directory will produce a coverage file /tmp/cover.out
-// and a coverage report for all the packages under internal/lsp, accumulated by all the tests
-// under gopls.
-//
-// -o controls where the coverage file is written, defaulting to /tmp/cover.out
-// -i coverage-file will generate the report from an existing coverage file
-// -v controls verbosity (0: only report coverage, 1: report as each directory is finished,
-// 2: report on each test, 3: more details, 4: too much)
-// -t tests only tests packages in the given comma-separated list of directories in gopls.
-// The names should start with ., as in ./internal/regtest/bench
-// -run tests. If set, -run tests is passed on to the go test command.
-//
-// Despite gopls' use of goroutines, the counts are almost deterministic.
-package main
-
-import (
- "bytes"
- "encoding/json"
- "flag"
- "fmt"
- "log"
- "os"
- "os/exec"
- "path/filepath"
- "sort"
- "strings"
- "time"
-
- "golang.org/x/tools/cover"
-)
-
-var (
- proFile = flag.String("i", "", "existing profile file")
- outFile = flag.String("o", "/tmp/cover.out", "where to write the coverage file")
- verbose = flag.Int("v", 0, "how much detail to print as tests are running")
- tests = flag.String("t", "", "list of tests to run")
- run = flag.String("run", "", "value of -run to pass to go test")
-)
-
-func main() {
- log.SetFlags(log.Lshortfile)
- flag.Parse()
-
- if *proFile != "" {
- report(*proFile)
- return
- }
-
- checkCwd()
- // find the packages under gopls containing tests
- tests := listDirs("gopls")
- tests = onlyTests(tests)
- tests = realTestName(tests)
-
- // report coverage for packages under internal/lsp
- parg := "golang.org/x/tools/internal/lsp/..."
-
- accum := []string{}
- seen := make(map[string]bool)
- now := time.Now()
- for _, toRun := range tests {
- if excluded(toRun) {
- continue
- }
- x := runTest(toRun, parg)
- if *verbose > 0 {
- fmt.Printf("finished %s %.1fs\n", toRun, time.Since(now).Seconds())
- }
- lines := bytes.Split(x, []byte{'\n'})
- for _, l := range lines {
- if len(l) == 0 {
- continue
- }
- if !seen[string(l)] {
- // not accumulating counts, so only works for mode:set
- seen[string(l)] = true
- accum = append(accum, string(l))
- }
- }
- }
- sort.Strings(accum[1:])
- if err := os.WriteFile(*outFile, []byte(strings.Join(accum, "\n")), 0644); err != nil {
- log.Print(err)
- }
- report(*outFile)
-}
-
-type result struct {
- Time time.Time
- Test string
- Action string
- Package string
- Output string
- Elapsed float64
-}
-
-func runTest(tName, parg string) []byte {
- args := []string{"test", "-short", "-coverpkg", parg, "-coverprofile", *outFile,
- "-json"}
- if *run != "" {
- args = append(args, fmt.Sprintf("-run=%s", *run))
- }
- args = append(args, tName)
- cmd := exec.Command("go", args...)
- cmd.Dir = "./gopls"
- ans, err := cmd.Output()
- if *verbose > 1 {
- got := strings.Split(string(ans), "\n")
- for _, g := range got {
- if g == "" {
- continue
- }
- var m result
- if err := json.Unmarshal([]byte(g), &m); err != nil {
- log.Printf("%T/%v", err, err) // shouldn't happen
- continue
- }
- maybePrint(m)
- }
- }
- if err != nil {
- log.Printf("%s: %q, cmd=%s", tName, ans, cmd.String())
- }
- buf, err := os.ReadFile(*outFile)
- if err != nil {
- log.Fatal(err)
- }
- return buf
-}
-
-func report(fn string) {
- profs, err := cover.ParseProfiles(fn)
- if err != nil {
- log.Fatal(err)
- }
- for _, p := range profs {
- statements, counts := 0, 0
- for _, x := range p.Blocks {
- statements += x.NumStmt
- if x.Count != 0 {
- counts += x.NumStmt // sic: if any were executed, all were
- }
- }
- pc := 100 * float64(counts) / float64(statements)
- fmt.Printf("%3.0f%% %3d/%3d %s\n", pc, counts, statements, p.FileName)
- }
-}
-
-var todo []string // tests to run
-
-func excluded(tname string) bool {
- if *tests == "" { // run all tests
- return false
- }
- if todo == nil {
- todo = strings.Split(*tests, ",")
- }
- for _, nm := range todo {
- if tname == nm { // run this test
- return false
- }
- }
- // not in list, skip it
- return true
-}
-
-// should m.Package be printed sometime?
-func maybePrint(m result) {
- switch m.Action {
- case "pass", "fail", "skip":
- fmt.Printf("%s %s %.3f\n", m.Action, m.Test, m.Elapsed)
- case "run":
- if *verbose > 2 {
- fmt.Printf("%s %s %.3f\n", m.Action, m.Test, m.Elapsed)
- }
- case "output":
- if *verbose > 3 {
- fmt.Printf("%s %s %q %.3f\n", m.Action, m.Test, m.Output, m.Elapsed)
- }
- default:
- log.Fatalf("unknown action %s\n", m.Action)
- }
-}
-
-// return only the directories that contain tests
-func onlyTests(s []string) []string {
- ans := []string{}
-outer:
- for _, d := range s {
- files, err := os.ReadDir(d)
- if err != nil {
- log.Fatalf("%s: %v", d, err)
- }
- for _, de := range files {
- if strings.Contains(de.Name(), "_test.go") {
- ans = append(ans, d)
- continue outer
- }
- }
- }
- return ans
-}
-
-// replace the prefix gopls/ with ./ as the tests are run in the gopls directory
-func realTestName(p []string) []string {
- ans := []string{}
- for _, x := range p {
- x = x[len("gopls/"):]
- ans = append(ans, "./"+x)
- }
- return ans
-}
-
-// make sure we start in a tools directory
-func checkCwd() {
- dir, err := os.Getwd()
- if err != nil {
- log.Fatal(err)
- }
- // we expect to be a the root of golang.org/x/tools
- cmd := exec.Command("go", "list", "-m", "-f", "{{.Dir}}", "golang.org/x/tools")
- buf, err := cmd.Output()
- buf = bytes.Trim(buf, "\n \t") // remove \n at end
- if err != nil {
- log.Fatal(err)
- }
- if string(buf) != dir {
- log.Fatalf("wrong directory: in %q, should be in %q", dir, string(buf))
- }
- // and we expect gopls and internal/lsp as subdirectories
- _, err = os.Stat("gopls")
- if err != nil {
- log.Fatalf("expected a gopls directory, %v", err)
- }
- _, err = os.Stat("internal/lsp")
- if err != nil {
- log.Fatalf("expected to see internal/lsp, %v", err)
- }
-}
-
-func listDirs(dir string) []string {
- ans := []string{}
- f := func(path string, dirEntry os.DirEntry, err error) error {
- if strings.HasSuffix(path, "/testdata") || strings.HasSuffix(path, "/typescript") {
- return filepath.SkipDir
- }
- if dirEntry.IsDir() {
- ans = append(ans, path)
- }
- return nil
- }
- filepath.WalkDir(dir, f)
- return ans
-}
diff -urN a/gopls/internal/govulncheck/README.md b/gopls/internal/govulncheck/README.md
--- a/gopls/internal/govulncheck/README.md 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/govulncheck/README.md 1970-01-01 01:00:00.000000000 +0100
@@ -1,17 +0,0 @@
-# internal/govulncheck package
-
-This package is a literal copy of the cmd/govulncheck/internal/govulncheck
-package in the vuln repo (https://go.googlesource.com/vuln).
-
-The `copy.sh` does the copying, after removing all .go files here. To use it:
-
-1. Clone the vuln repo to a directory next to the directory holding this repo
- (tools). After doing that your directory structure should look something like
- ```
- ~/repos/x/tools/gopls/...
- ~/repos/x/vuln/...
- ```
-
-2. cd to this directory.
-
-3. Run `copy.sh`.
diff -urN a/gopls/internal/govulncheck/cache.go b/gopls/internal/govulncheck/cache.go
--- a/gopls/internal/govulncheck/cache.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/govulncheck/cache.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,138 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.18
-// +build go1.18
-
-// Package govulncheck supports the govulncheck command.
-package govulncheck
-
-import (
- "encoding/json"
- "go/build"
- "io/ioutil"
- "os"
- "path/filepath"
- "sync"
- "time"
-
- "golang.org/x/vuln/client"
- "golang.org/x/vuln/osv"
-)
-
-// The cache uses a single JSON index file for each vulnerability database
-// which contains the map from packages to the time the last
-// vulnerability for that package was added/modified and the time that
-// the index was retrieved from the vulnerability database. The JSON
-// format is as follows:
-//
-// $GOPATH/pkg/mod/cache/download/vulndb/{db hostname}/indexes/index.json
-// {
-// Retrieved time.Time
-// Index client.DBIndex
-// }
-//
-// Each package also has a JSON file which contains the array of vulnerability
-// entries for the package. The JSON format is as follows:
-//
-// $GOPATH/pkg/mod/cache/download/vulndb/{db hostname}/{import path}/vulns.json
-// []*osv.Entry
-
-// FSCache is a thread-safe file-system cache implementing osv.Cache
-//
-// TODO: use something like cmd/go/internal/lockedfile for thread safety?
-type FSCache struct {
- mu sync.Mutex
- rootDir string
-}
-
-// Assert that *FSCache implements client.Cache.
-var _ client.Cache = (*FSCache)(nil)
-
-// use cfg.GOMODCACHE available in cmd/go/internal?
-var defaultCacheRoot = filepath.Join(build.Default.GOPATH, "/pkg/mod/cache/download/vulndb")
-
-func DefaultCache() *FSCache {
- return &FSCache{rootDir: defaultCacheRoot}
-}
-
-type cachedIndex struct {
- Retrieved time.Time
- Index client.DBIndex
-}
-
-func (c *FSCache) ReadIndex(dbName string) (client.DBIndex, time.Time, error) {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- b, err := ioutil.ReadFile(filepath.Join(c.rootDir, dbName, "index.json"))
- if err != nil {
- if os.IsNotExist(err) {
- return nil, time.Time{}, nil
- }
- return nil, time.Time{}, err
- }
- var index cachedIndex
- if err := json.Unmarshal(b, &index); err != nil {
- return nil, time.Time{}, err
- }
- return index.Index, index.Retrieved, nil
-}
-
-func (c *FSCache) WriteIndex(dbName string, index client.DBIndex, retrieved time.Time) error {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- path := filepath.Join(c.rootDir, dbName)
- if err := os.MkdirAll(path, 0755); err != nil {
- return err
- }
- j, err := json.Marshal(cachedIndex{
- Index: index,
- Retrieved: retrieved,
- })
- if err != nil {
- return err
- }
- if err := ioutil.WriteFile(filepath.Join(path, "index.json"), j, 0666); err != nil {
- return err
- }
- return nil
-}
-
-func (c *FSCache) ReadEntries(dbName string, p string) ([]*osv.Entry, error) {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- b, err := ioutil.ReadFile(filepath.Join(c.rootDir, dbName, p, "vulns.json"))
- if err != nil {
- if os.IsNotExist(err) {
- return nil, nil
- }
- return nil, err
- }
- var entries []*osv.Entry
- if err := json.Unmarshal(b, &entries); err != nil {
- return nil, err
- }
- return entries, nil
-}
-
-func (c *FSCache) WriteEntries(dbName string, p string, entries []*osv.Entry) error {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- path := filepath.Join(c.rootDir, dbName, p)
- if err := os.MkdirAll(path, 0777); err != nil {
- return err
- }
- j, err := json.Marshal(entries)
- if err != nil {
- return err
- }
- if err := ioutil.WriteFile(filepath.Join(path, "vulns.json"), j, 0666); err != nil {
- return err
- }
- return nil
-}
diff -urN a/gopls/internal/govulncheck/cache_test.go b/gopls/internal/govulncheck/cache_test.go
--- a/gopls/internal/govulncheck/cache_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/govulncheck/cache_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,165 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.18
-// +build go1.18
-
-package govulncheck
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "reflect"
- "testing"
- "time"
-
- "golang.org/x/sync/errgroup"
- "golang.org/x/vuln/client"
- "golang.org/x/vuln/osv"
-)
-
-func TestCache(t *testing.T) {
- tmpDir := t.TempDir()
-
- cache := &FSCache{rootDir: tmpDir}
- dbName := "vulndb.golang.org"
-
- _, _, err := cache.ReadIndex(dbName)
- if err != nil {
- t.Fatalf("ReadIndex failed for non-existent database: %v", err)
- }
-
- if err = os.Mkdir(filepath.Join(tmpDir, dbName), 0777); err != nil {
- t.Fatalf("os.Mkdir failed: %v", err)
- }
- _, _, err = cache.ReadIndex(dbName)
- if err != nil {
- t.Fatalf("ReadIndex failed for database without cached index: %v", err)
- }
-
- now := time.Now()
- expectedIdx := client.DBIndex{
- "a.vuln.example.com": time.Time{}.Add(time.Hour),
- "b.vuln.example.com": time.Time{}.Add(time.Hour * 2),
- "c.vuln.example.com": time.Time{}.Add(time.Hour * 3),
- }
- if err = cache.WriteIndex(dbName, expectedIdx, now); err != nil {
- t.Fatalf("WriteIndex failed to write index: %v", err)
- }
-
- idx, retrieved, err := cache.ReadIndex(dbName)
- if err != nil {
- t.Fatalf("ReadIndex failed for database with cached index: %v", err)
- }
- if !reflect.DeepEqual(idx, expectedIdx) {
- t.Errorf("ReadIndex returned unexpected index, got:\n%s\nwant:\n%s", idx, expectedIdx)
- }
- if !retrieved.Equal(now) {
- t.Errorf("ReadIndex returned unexpected retrieved: got %s, want %s", retrieved, now)
- }
-
- if _, err = cache.ReadEntries(dbName, "vuln.example.com"); err != nil {
- t.Fatalf("ReadEntires failed for non-existent package: %v", err)
- }
-
- expectedEntries := []*osv.Entry{
- {ID: "001"},
- {ID: "002"},
- {ID: "003"},
- }
- if err := cache.WriteEntries(dbName, "vuln.example.com", expectedEntries); err != nil {
- t.Fatalf("WriteEntries failed: %v", err)
- }
-
- entries, err := cache.ReadEntries(dbName, "vuln.example.com")
- if err != nil {
- t.Fatalf("ReadEntries failed for cached package: %v", err)
- }
- if !reflect.DeepEqual(entries, expectedEntries) {
- t.Errorf("ReadEntries returned unexpected entries, got:\n%v\nwant:\n%v", entries, expectedEntries)
- }
-}
-
-func TestConcurrency(t *testing.T) {
- tmpDir := t.TempDir()
-
- cache := &FSCache{rootDir: tmpDir}
- dbName := "vulndb.golang.org"
-
- g := new(errgroup.Group)
- for i := 0; i < 1000; i++ {
- i := i
- g.Go(func() error {
- id := i % 5
- p := fmt.Sprintf("package%d", id)
-
- entries, err := cache.ReadEntries(dbName, p)
- if err != nil {
- return err
- }
-
- err = cache.WriteEntries(dbName, p, append(entries, &osv.Entry{ID: fmt.Sprint(id)}))
- if err != nil {
- return err
- }
- return nil
- })
- }
-
- if err := g.Wait(); err != nil {
- t.Errorf("error in parallel cache entries read/write: %v", err)
- }
-
- // sanity checking
- for i := 0; i < 5; i++ {
- id := fmt.Sprint(i)
- p := fmt.Sprintf("package%s", id)
-
- es, err := cache.ReadEntries(dbName, p)
- if err != nil {
- t.Fatalf("failed to read entries: %v", err)
- }
- for _, e := range es {
- if e.ID != id {
- t.Errorf("want %s ID for vuln entry; got %s", id, e.ID)
- }
- }
- }
-
- // do similar for cache index
- start := time.Now()
- for i := 0; i < 1000; i++ {
- i := i
- g.Go(func() error {
- id := i % 5
- p := fmt.Sprintf("package%v", id)
-
- idx, _, err := cache.ReadIndex(dbName)
- if err != nil {
- return err
- }
-
- if idx == nil {
- idx = client.DBIndex{}
- }
-
- // sanity checking
- if rt, ok := idx[p]; ok && rt.Before(start) {
- return fmt.Errorf("unexpected past time in index: %v before start %v", rt, start)
- }
-
- now := time.Now()
- idx[p] = now
- if err := cache.WriteIndex(dbName, idx, now); err != nil {
- return err
- }
- return nil
- })
- }
-
- if err := g.Wait(); err != nil {
- t.Errorf("error in parallel cache index read/write: %v", err)
- }
-}
diff -urN a/gopls/internal/govulncheck/copy.sh b/gopls/internal/govulncheck/copy.sh
--- a/gopls/internal/govulncheck/copy.sh 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/govulncheck/copy.sh 1970-01-01 01:00:00.000000000 +0100
@@ -1,13 +0,0 @@
-#!/bin/bash -eu
-
-# Copyright 2020 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-set -o pipefail
-
-# Copy golang.org/x/vuln/cmd/govulncheck/internal/govulncheck into this directory.
-# Assume the x/vuln repo is a sibling of the tools repo.
-
-rm -f *.go
-cp ../../../../vuln/cmd/govulncheck/internal/govulncheck/*.go .
diff -urN a/gopls/internal/govulncheck/source.go b/gopls/internal/govulncheck/source.go
--- a/gopls/internal/govulncheck/source.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/govulncheck/source.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,129 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.18
-// +build go1.18
-
-package govulncheck
-
-import (
- "context"
- "fmt"
- "sort"
- "strings"
-
- "golang.org/x/tools/go/packages"
- "golang.org/x/vuln/client"
- "golang.org/x/vuln/vulncheck"
-)
-
-// A PackageError contains errors from loading a set of packages.
-type PackageError struct {
- Errors []packages.Error
-}
-
-func (e *PackageError) Error() string {
- var b strings.Builder
- fmt.Fprintln(&b, "Packages contain errors:")
- for _, e := range e.Errors {
- fmt.Println(&b, e)
- }
- return b.String()
-}
-
-// LoadPackages loads the packages matching patterns using cfg, after setting
-// the cfg mode flags that vulncheck needs for analysis.
-// If the packages contain errors, a PackageError is returned containing a list of the errors,
-// along with the packages themselves.
-func LoadPackages(cfg *packages.Config, patterns ...string) ([]*vulncheck.Package, error) {
- cfg.Mode |= packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles |
- packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes |
- packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps |
- packages.NeedModule
-
- pkgs, err := packages.Load(cfg, patterns...)
- vpkgs := vulncheck.Convert(pkgs)
- if err != nil {
- return nil, err
- }
- var perrs []packages.Error
- packages.Visit(pkgs, nil, func(p *packages.Package) {
- perrs = append(perrs, p.Errors...)
- })
- if len(perrs) > 0 {
- err = &PackageError{perrs}
- }
- return vpkgs, err
-}
-
-// Source calls vulncheck.Source on the Go source in pkgs. It returns the result
-// with Vulns trimmed to those that are actually called.
-func Source(ctx context.Context, pkgs []*vulncheck.Package, c client.Client) (*vulncheck.Result, error) {
- r, err := vulncheck.Source(ctx, pkgs, &vulncheck.Config{Client: c})
- if err != nil {
- return nil, err
- }
- // Keep only the vulns that are called.
- var vulns []*vulncheck.Vuln
- for _, v := range r.Vulns {
- if v.CallSink != 0 {
- vulns = append(vulns, v)
- }
- }
- r.Vulns = vulns
- return r, nil
-}
-
-// CallInfo is information about calls to vulnerable functions.
-type CallInfo struct {
- CallStacks map[*vulncheck.Vuln][]vulncheck.CallStack // all call stacks
- VulnGroups [][]*vulncheck.Vuln // vulns grouped by ID and package
- ModuleVersions map[string]string // map from module paths to versions
- TopPackages map[string]bool // top-level packages
-}
-
-// GetCallInfo computes call stacks and related information from a vulncheck.Result.
-// I also makes a set of top-level packages from pkgs.
-func GetCallInfo(r *vulncheck.Result, pkgs []*vulncheck.Package) *CallInfo {
- pset := map[string]bool{}
- for _, p := range pkgs {
- pset[p.PkgPath] = true
- }
- return &CallInfo{
- CallStacks: vulncheck.CallStacks(r),
- VulnGroups: groupByIDAndPackage(r.Vulns),
- ModuleVersions: moduleVersionMap(r.Modules),
- TopPackages: pset,
- }
-}
-
-func groupByIDAndPackage(vs []*vulncheck.Vuln) [][]*vulncheck.Vuln {
- groups := map[[2]string][]*vulncheck.Vuln{}
- for _, v := range vs {
- key := [2]string{v.OSV.ID, v.PkgPath}
- groups[key] = append(groups[key], v)
- }
-
- var res [][]*vulncheck.Vuln
- for _, g := range groups {
- res = append(res, g)
- }
- sort.Slice(res, func(i, j int) bool {
- return res[i][0].PkgPath < res[j][0].PkgPath
- })
- return res
-}
-
-// moduleVersionMap builds a map from module paths to versions.
-func moduleVersionMap(mods []*vulncheck.Module) map[string]string {
- moduleVersions := map[string]string{}
- for _, m := range mods {
- v := m.Version
- if m.Replace != nil {
- v = m.Replace.Version
- }
- moduleVersions[m.Path] = v
- }
- return moduleVersions
-}
diff -urN a/gopls/internal/govulncheck/util.go b/gopls/internal/govulncheck/util.go
--- a/gopls/internal/govulncheck/util.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/govulncheck/util.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,109 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.18
-// +build go1.18
-
-package govulncheck
-
-import (
- "fmt"
- "strings"
-
- "golang.org/x/mod/semver"
- "golang.org/x/vuln/osv"
- "golang.org/x/vuln/vulncheck"
-)
-
-// LatestFixed returns the latest fixed version in the list of affected ranges,
-// or the empty string if there are no fixed versions.
-func LatestFixed(as []osv.Affected) string {
- v := ""
- for _, a := range as {
- for _, r := range a.Ranges {
- if r.Type == osv.TypeSemver {
- for _, e := range r.Events {
- if e.Fixed != "" && (v == "" || semver.Compare(e.Fixed, v) > 0) {
- v = e.Fixed
- }
- }
- }
- }
- }
- return v
-}
-
-// SummarizeCallStack returns a short description of the call stack.
-// It uses one of two forms, depending on what the lowest function F in topPkgs
-// calls:
-// - If it calls a function V from the vulnerable package, then summarizeCallStack
-// returns "F calls V".
-// - If it calls a function G in some other package, which eventually calls V,
-// it returns "F calls G, which eventually calls V".
-//
-// If it can't find any of these functions, summarizeCallStack returns the empty string.
-func SummarizeCallStack(cs vulncheck.CallStack, topPkgs map[string]bool, vulnPkg string) string {
- // Find the lowest function in the top packages.
- iTop := lowest(cs, func(e vulncheck.StackEntry) bool {
- return topPkgs[PkgPath(e.Function)]
- })
- if iTop < 0 {
- return ""
- }
- // Find the highest function in the vulnerable package that is below iTop.
- iVuln := highest(cs[iTop+1:], func(e vulncheck.StackEntry) bool {
- return PkgPath(e.Function) == vulnPkg
- })
- if iVuln < 0 {
- return ""
- }
- iVuln += iTop + 1 // adjust for slice in call to highest.
- topName := FuncName(cs[iTop].Function)
- vulnName := FuncName(cs[iVuln].Function)
- if iVuln == iTop+1 {
- return fmt.Sprintf("%s calls %s", topName, vulnName)
- }
- return fmt.Sprintf("%s calls %s, which eventually calls %s",
- topName, FuncName(cs[iTop+1].Function), vulnName)
-}
-
-// highest returns the highest (one with the smallest index) entry in the call
-// stack for which f returns true.
-func highest(cs vulncheck.CallStack, f func(e vulncheck.StackEntry) bool) int {
- for i := 0; i < len(cs); i++ {
- if f(cs[i]) {
- return i
- }
- }
- return -1
-}
-
-// lowest returns the lowest (one with the largets index) entry in the call
-// stack for which f returns true.
-func lowest(cs vulncheck.CallStack, f func(e vulncheck.StackEntry) bool) int {
- for i := len(cs) - 1; i >= 0; i-- {
- if f(cs[i]) {
- return i
- }
- }
- return -1
-}
-
-// PkgPath returns the package path from fn.
-func PkgPath(fn *vulncheck.FuncNode) string {
- if fn.PkgPath != "" {
- return fn.PkgPath
- }
- s := strings.TrimPrefix(fn.RecvType, "*")
- if i := strings.LastIndexByte(s, '.'); i > 0 {
- s = s[:i]
- }
- return s
-}
-
-// FuncName returns the function name from fn, adjusted
-// to remove pointer annotations.
-func FuncName(fn *vulncheck.FuncNode) string {
- return strings.TrimPrefix(fn.String(), "*")
-}
diff -urN a/gopls/internal/govulncheck/util_test.go b/gopls/internal/govulncheck/util_test.go
--- a/gopls/internal/govulncheck/util_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/govulncheck/util_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,85 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.18
-// +build go1.18
-
-package govulncheck
-
-import (
- "strings"
- "testing"
-
- "golang.org/x/vuln/vulncheck"
-)
-
-func TestPkgPath(t *testing.T) {
- for _, test := range []struct {
- in vulncheck.FuncNode
- want string
- }{
- {
- vulncheck.FuncNode{PkgPath: "math", Name: "Floor"},
- "math",
- },
- {
- vulncheck.FuncNode{RecvType: "a.com/b.T", Name: "M"},
- "a.com/b",
- },
- {
- vulncheck.FuncNode{RecvType: "*a.com/b.T", Name: "M"},
- "a.com/b",
- },
- } {
- got := PkgPath(&test.in)
- if got != test.want {
- t.Errorf("%+v: got %q, want %q", test.in, got, test.want)
- }
- }
-}
-
-func TestSummarizeCallStack(t *testing.T) {
- topPkgs := map[string]bool{"t1": true, "t2": true}
- vulnPkg := "v"
-
- for _, test := range []struct {
- in, want string
- }{
- {"a.F", ""},
- {"t1.F", ""},
- {"v.V", ""},
- {
- "t1.F v.V",
- "t1.F calls v.V",
- },
- {
- "t1.F t2.G v.V1 v.v2",
- "t2.G calls v.V1",
- },
- {
- "t1.F x.Y t2.G a.H b.I c.J v.V",
- "t2.G calls a.H, which eventually calls v.V",
- },
- } {
- in := stringToCallStack(test.in)
- got := SummarizeCallStack(in, topPkgs, vulnPkg)
- if got != test.want {
- t.Errorf("%s:\ngot %s\nwant %s", test.in, got, test.want)
- }
- }
-}
-
-func stringToCallStack(s string) vulncheck.CallStack {
- var cs vulncheck.CallStack
- for _, e := range strings.Fields(s) {
- parts := strings.Split(e, ".")
- cs = append(cs, vulncheck.StackEntry{
- Function: &vulncheck.FuncNode{
- PkgPath: parts[0],
- Name: parts[1],
- },
- })
- }
- return cs
-}
diff -urN a/gopls/internal/hooks/analysis.go b/gopls/internal/hooks/analysis.go
--- a/gopls/internal/hooks/analysis.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/hooks/analysis.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,62 +0,0 @@
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.17
-// +build go1.17
-
-package hooks
-
-import (
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/lsp/source"
- "honnef.co/go/tools/analysis/lint"
- "honnef.co/go/tools/quickfix"
- "honnef.co/go/tools/simple"
- "honnef.co/go/tools/staticcheck"
- "honnef.co/go/tools/stylecheck"
-)
-
-func updateAnalyzers(options *source.Options) {
- options.StaticcheckSupported = true
-
- mapSeverity := func(severity lint.Severity) protocol.DiagnosticSeverity {
- switch severity {
- case lint.SeverityError:
- return protocol.SeverityError
- case lint.SeverityDeprecated:
- // TODO(dh): in LSP, deprecated is a tag, not a severity.
- // We'll want to support this once we enable SA5011.
- return protocol.SeverityWarning
- case lint.SeverityWarning:
- return protocol.SeverityWarning
- case lint.SeverityInfo:
- return protocol.SeverityInformation
- case lint.SeverityHint:
- return protocol.SeverityHint
- default:
- return protocol.SeverityWarning
- }
- }
- add := func(analyzers []*lint.Analyzer, skip map[string]struct{}) {
- for _, a := range analyzers {
- if _, ok := skip[a.Analyzer.Name]; ok {
- continue
- }
-
- enabled := !a.Doc.NonDefault
- options.AddStaticcheckAnalyzer(a.Analyzer, enabled, mapSeverity(a.Doc.Severity))
- }
- }
-
- add(simple.Analyzers, nil)
- add(staticcheck.Analyzers, map[string]struct{}{
- // This check conflicts with the vet printf check (golang/go#34494).
- "SA5009": {},
- // This check relies on facts from dependencies, which
- // we don't currently compute.
- "SA5011": {},
- })
- add(stylecheck.Analyzers, nil)
- add(quickfix.Analyzers, nil)
-}
diff -urN a/gopls/internal/hooks/analysis_117.go b/gopls/internal/hooks/analysis_117.go
--- a/gopls/internal/hooks/analysis_117.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/hooks/analysis_117.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,14 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build !go1.17
-// +build !go1.17
-
-package hooks
-
-import "golang.org/x/tools/internal/lsp/source"
-
-func updateAnalyzers(options *source.Options) {
- options.StaticcheckSupported = false
-}
diff -urN a/gopls/internal/hooks/diff.go b/gopls/internal/hooks/diff.go
--- a/gopls/internal/hooks/diff.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/hooks/diff.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,41 +0,0 @@
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package hooks
-
-import (
- "fmt"
-
- "github.com/sergi/go-diff/diffmatchpatch"
- "golang.org/x/tools/internal/lsp/diff"
- "golang.org/x/tools/internal/span"
-)
-
-func ComputeEdits(uri span.URI, before, after string) (edits []diff.TextEdit, err error) {
- // The go-diff library has an unresolved panic (see golang/go#278774).
- // TODO(rstambler): Remove the recover once the issue has been fixed
- // upstream.
- defer func() {
- if r := recover(); r != nil {
- edits = nil
- err = fmt.Errorf("unable to compute edits for %s: %s", uri.Filename(), r)
- }
- }()
- diffs := diffmatchpatch.New().DiffMain(before, after, true)
- edits = make([]diff.TextEdit, 0, len(diffs))
- offset := 0
- for _, d := range diffs {
- start := span.NewPoint(0, 0, offset)
- switch d.Type {
- case diffmatchpatch.DiffDelete:
- offset += len(d.Text)
- edits = append(edits, diff.TextEdit{Span: span.New(uri, start, span.NewPoint(0, 0, offset))})
- case diffmatchpatch.DiffEqual:
- offset += len(d.Text)
- case diffmatchpatch.DiffInsert:
- edits = append(edits, diff.TextEdit{Span: span.New(uri, start, span.Point{}), NewText: d.Text})
- }
- }
- return edits, nil
-}
diff -urN a/gopls/internal/hooks/diff_test.go b/gopls/internal/hooks/diff_test.go
--- a/gopls/internal/hooks/diff_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/hooks/diff_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,16 +0,0 @@
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package hooks_test
-
-import (
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/diff/difftest"
-)
-
-func TestDiff(t *testing.T) {
- difftest.DiffTest(t, hooks.ComputeEdits)
-}
diff -urN a/gopls/internal/hooks/gen-licenses.sh b/gopls/internal/hooks/gen-licenses.sh
--- a/gopls/internal/hooks/gen-licenses.sh 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/hooks/gen-licenses.sh 1970-01-01 01:00:00.000000000 +0100
@@ -1,38 +0,0 @@
-#!/bin/bash -eu
-
-# Copyright 2020 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-set -o pipefail
-
-output=$1
-tempfile=$(mktemp)
-cd $(dirname $0)
-
-cat > $tempfile <<END
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:generate ./gen-licenses.sh licenses.go
-package hooks
-
-const licensesText = \`
-END
-
-# List all the modules gopls depends on, except other golang.org modules, which
-# are known to have the same license.
-mods=$(go list -deps -f '{{with .Module}}{{.Path}}{{end}}' golang.org/x/tools/gopls | sort -u | grep -v golang.org)
-for mod in $mods; do
- # Find the license file, either LICENSE or COPYING, and add it to the result.
- dir=$(go list -m -f {{.Dir}} $mod)
- license=$(ls -1 $dir | egrep -i '^(LICENSE|COPYING)$')
- echo "-- $mod $license --" >> $tempfile
- echo >> $tempfile
- sed 's/^-- / &/' $dir/$license >> $tempfile
- echo >> $tempfile
-done
-
-echo "\`" >> $tempfile
-mv $tempfile $output
\ No newline at end of file
diff -urN a/gopls/internal/hooks/hooks.go b/gopls/internal/hooks/hooks.go
--- a/gopls/internal/hooks/hooks.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/hooks/hooks.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,34 +0,0 @@
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package hooks adds all the standard gopls implementations.
-// This can be used in tests without needing to use the gopls main, and is
-// also the place to edit for custom builds of gopls.
-package hooks // import "golang.org/x/tools/gopls/internal/hooks"
-
-import (
- "context"
-
- "golang.org/x/tools/gopls/internal/vulncheck"
- "golang.org/x/tools/internal/lsp/source"
- "mvdan.cc/gofumpt/format"
- "mvdan.cc/xurls/v2"
-)
-
-func Options(options *source.Options) {
- options.LicensesText = licensesText
- if options.GoDiff {
- options.ComputeEdits = ComputeEdits
- }
- options.URLRegexp = xurls.Relaxed()
- options.GofumptFormat = func(ctx context.Context, langVersion, modulePath string, src []byte) ([]byte, error) {
- return format.Source(src, format.Options{
- LangVersion: langVersion,
- ModulePath: modulePath,
- })
- }
- updateAnalyzers(options)
-
- options.Govulncheck = vulncheck.Govulncheck
-}
diff -urN a/gopls/internal/hooks/licenses.go b/gopls/internal/hooks/licenses.go
--- a/gopls/internal/hooks/licenses.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/hooks/licenses.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,169 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:generate ./gen-licenses.sh licenses.go
-package hooks
-
-const licensesText = `
--- github.com/BurntSushi/toml COPYING --
-
-The MIT License (MIT)
-
-Copyright (c) 2013 TOML authors
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-
--- github.com/google/go-cmp LICENSE --
-
-Copyright (c) 2017 The Go Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
--- github.com/sergi/go-diff LICENSE --
-
-Copyright (c) 2012-2016 The go-diff Authors. All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a
-copy of this software and associated documentation files (the "Software"),
-to deal in the Software without restriction, including without limitation
-the rights to use, copy, modify, merge, publish, distribute, sublicense,
-and/or sell copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included
-in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
-
--- honnef.co/go/tools LICENSE --
-
-Copyright (c) 2016 Dominik Honnef
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
--- mvdan.cc/gofumpt LICENSE --
-
-Copyright (c) 2019, Daniel Martí. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of the copyright holder nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
--- mvdan.cc/xurls/v2 LICENSE --
-
-Copyright (c) 2015, Daniel Martí. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of the copyright holder nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-`
diff -urN a/gopls/internal/hooks/licenses_test.go b/gopls/internal/hooks/licenses_test.go
--- a/gopls/internal/hooks/licenses_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/hooks/licenses_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,46 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package hooks
-
-import (
- "bytes"
- "io/ioutil"
- "os/exec"
- "runtime"
- "testing"
-
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestLicenses(t *testing.T) {
- // License text differs for older Go versions because staticcheck isn't
- // supported for those versions.
- testenv.NeedsGo1Point(t, 17)
-
- if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
- t.Skip("generating licenses only works on Unixes")
- }
- tmp, err := ioutil.TempFile("", "")
- if err != nil {
- t.Fatal(err)
- }
- tmp.Close()
-
- if out, err := exec.Command("./gen-licenses.sh", tmp.Name()).CombinedOutput(); err != nil {
- t.Fatalf("generating licenses failed: %q, %v", out, err)
- }
-
- got, err := ioutil.ReadFile(tmp.Name())
- if err != nil {
- t.Fatal(err)
- }
- want, err := ioutil.ReadFile("licenses.go")
- if err != nil {
- t.Fatal(err)
- }
- if !bytes.Equal(got, want) {
- t.Error("combined license text needs updating. Run: `go generate ./internal/hooks` from the gopls module.")
- }
-}
diff -urN a/gopls/internal/regtest/bench/bench_test.go b/gopls/internal/regtest/bench/bench_test.go
--- a/gopls/internal/regtest/bench/bench_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/bench/bench_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,194 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package bench
-
-import (
- "flag"
- "fmt"
- "os"
- "runtime/pprof"
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- "golang.org/x/tools/internal/lsp/fake"
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/protocol"
-)
-
-func TestMain(m *testing.M) {
- bug.PanicOnBugs = true
- Main(m, hooks.Options)
-}
-
-func benchmarkOptions(dir string) []RunOption {
- return []RunOption{
- // Run in an existing directory, since we're trying to simulate known cases
- // that cause gopls memory problems.
- InExistingDir(dir),
- // Skip logs as they buffer up memory unnaturally.
- SkipLogs(),
- // The Debug server only makes sense if running in singleton mode.
- Modes(Singleton),
- // Remove the default timeout. Individual tests should control their
- // own graceful termination.
- NoDefaultTimeout(),
-
- // Use the actual proxy, since we want our builds to succeed.
- GOPROXY("https://proxy.golang.org"),
- }
-}
-
-func printBenchmarkResults(result testing.BenchmarkResult) {
- fmt.Printf("BenchmarkStatistics\t%s\t%s\n", result.String(), result.MemString())
-}
-
-var iwlOptions struct {
- workdir string
-}
-
-func init() {
- flag.StringVar(&iwlOptions.workdir, "iwl_workdir", "", "if set, run IWL benchmark in this directory")
-}
-
-func TestBenchmarkIWL(t *testing.T) {
- if iwlOptions.workdir == "" {
- t.Skip("-iwl_workdir not configured")
- }
-
- opts := stressTestOptions(iwlOptions.workdir)
- // Don't skip hooks, so that we can wait for IWL.
- opts = append(opts, SkipHooks(false))
-
- results := testing.Benchmark(func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- WithOptions(opts...).Run(t, "", func(t *testing.T, env *Env) {})
- }
- })
-
- printBenchmarkResults(results)
-}
-
-var symbolOptions struct {
- workdir, query, matcher, style string
- printResults bool
-}
-
-func init() {
- flag.StringVar(&symbolOptions.workdir, "symbol_workdir", "", "if set, run symbol benchmark in this directory")
- flag.StringVar(&symbolOptions.query, "symbol_query", "test", "symbol query to use in benchmark")
- flag.StringVar(&symbolOptions.matcher, "symbol_matcher", "", "symbol matcher to use in benchmark")
- flag.StringVar(&symbolOptions.style, "symbol_style", "", "symbol style to use in benchmark")
- flag.BoolVar(&symbolOptions.printResults, "symbol_print_results", false, "whether to print symbol query results")
-}
-
-func TestBenchmarkSymbols(t *testing.T) {
- if symbolOptions.workdir == "" {
- t.Skip("-symbol_workdir not configured")
- }
-
- opts := benchmarkOptions(symbolOptions.workdir)
- conf := EditorConfig{}
- if symbolOptions.matcher != "" {
- conf.SymbolMatcher = &symbolOptions.matcher
- }
- if symbolOptions.style != "" {
- conf.SymbolStyle = &symbolOptions.style
- }
- opts = append(opts, conf)
-
- WithOptions(opts...).Run(t, "", func(t *testing.T, env *Env) {
- // We can't Await in this test, since we have disabled hooks. Instead, run
- // one symbol request to completion to ensure all necessary cache entries
- // are populated.
- symbols, err := env.Editor.Server.Symbol(env.Ctx, &protocol.WorkspaceSymbolParams{
- Query: symbolOptions.query,
- })
- if err != nil {
- t.Fatal(err)
- }
-
- if symbolOptions.printResults {
- fmt.Println("Results:")
- for i := 0; i < len(symbols); i++ {
- fmt.Printf("\t%d. %s (%s)\n", i, symbols[i].Name, symbols[i].ContainerName)
- }
- }
-
- results := testing.Benchmark(func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- if _, err := env.Editor.Server.Symbol(env.Ctx, &protocol.WorkspaceSymbolParams{
- Query: symbolOptions.query,
- }); err != nil {
- t.Fatal(err)
- }
- }
- })
- printBenchmarkResults(results)
- })
-}
-
-var (
- benchDir = flag.String("didchange_dir", "", "If set, run benchmarks in this dir. Must also set didchange_file.")
- benchFile = flag.String("didchange_file", "", "The file to modify")
- benchProfile = flag.String("didchange_cpuprof", "", "file to write cpu profiling data to")
-)
-
-// TestBenchmarkDidChange benchmarks modifications of a single file by making
-// synthetic modifications in a comment. It controls pacing by waiting for the
-// server to actually start processing the didChange notification before
-// proceeding. Notably it does not wait for diagnostics to complete.
-//
-// Run it by passing -didchange_dir and -didchange_file, where -didchange_dir
-// is the path to a workspace root, and -didchange_file is the
-// workspace-relative path to a file to modify. e.g.:
-//
-// go test -run=TestBenchmarkDidChange \
-// -didchange_dir=path/to/kubernetes \
-// -didchange_file=pkg/util/hash/hash.go
-func TestBenchmarkDidChange(t *testing.T) {
- if *benchDir == "" {
- t.Skip("-didchange_dir is not set")
- }
- if *benchFile == "" {
- t.Fatal("-didchange_file must be set if -didchange_dir is set")
- }
-
- opts := benchmarkOptions(*benchDir)
- WithOptions(opts...).Run(t, "", func(_ *testing.T, env *Env) {
- env.OpenFile(*benchFile)
- env.Await(env.DoneWithOpen())
- // Insert the text we'll be modifying at the top of the file.
- env.EditBuffer(*benchFile, fake.Edit{Text: "// __REGTEST_PLACEHOLDER_0__\n"})
-
- // Run the profiler after the initial load,
- // across all benchmark iterations.
- if *benchProfile != "" {
- profile, err := os.Create(*benchProfile)
- if err != nil {
- t.Fatal(err)
- }
- defer profile.Close()
- if err := pprof.StartCPUProfile(profile); err != nil {
- t.Fatal(err)
- }
- defer pprof.StopCPUProfile()
- }
-
- result := testing.Benchmark(func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- env.EditBuffer(*benchFile, fake.Edit{
- Start: fake.Pos{Line: 0, Column: 0},
- End: fake.Pos{Line: 1, Column: 0},
- // Increment
- Text: fmt.Sprintf("// __REGTEST_PLACEHOLDER_%d__\n", i+1),
- })
- env.Await(StartedChange(uint64(i + 1)))
- }
- })
- printBenchmarkResults(result)
- })
-}
diff -urN a/gopls/internal/regtest/bench/completion_bench_test.go b/gopls/internal/regtest/bench/completion_bench_test.go
--- a/gopls/internal/regtest/bench/completion_bench_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/bench/completion_bench_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,187 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package bench
-
-import (
- "flag"
- "fmt"
- "strings"
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/fake"
-)
-
-// dummyCompletionFunction to test manually configured completion using CLI.
-func dummyCompletionFunction() { const s = "placeholder"; fmt.Printf("%s", s) }
-
-type completionBenchOptions struct {
- workdir, file, locationRegexp string
- printResults bool
- // hook to run edits before initial completion, not supported for manually
- // configured completions.
- preCompletionEdits func(*Env)
-}
-
-var completionOptions = completionBenchOptions{}
-
-func init() {
- flag.StringVar(&completionOptions.workdir, "completion_workdir", "", "directory to run completion benchmarks in")
- flag.StringVar(&completionOptions.file, "completion_file", "", "relative path to the file to complete in")
- flag.StringVar(&completionOptions.locationRegexp, "completion_regexp", "", "regexp location to complete at")
- flag.BoolVar(&completionOptions.printResults, "completion_print_results", false, "whether to print completion results")
-}
-
-func benchmarkCompletion(options completionBenchOptions, t *testing.T) {
- if completionOptions.workdir == "" {
- t.Skip("-completion_workdir not configured, skipping benchmark")
- }
-
- opts := stressTestOptions(options.workdir)
-
- // Completion gives bad results if IWL is not yet complete, so we must await
- // it first (and therefore need hooks).
- opts = append(opts, SkipHooks(false))
-
- WithOptions(opts...).Run(t, "", func(t *testing.T, env *Env) {
- env.OpenFile(options.file)
-
- // Run edits required for this completion.
- if options.preCompletionEdits != nil {
- options.preCompletionEdits(env)
- }
-
- // Run a completion to make sure the system is warm.
- pos := env.RegexpSearch(options.file, options.locationRegexp)
- completions := env.Completion(options.file, pos)
-
- if options.printResults {
- fmt.Println("Results:")
- for i := 0; i < len(completions.Items); i++ {
- fmt.Printf("\t%d. %v\n", i, completions.Items[i])
- }
- }
-
- results := testing.Benchmark(func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- env.Completion(options.file, pos)
- }
- })
-
- printBenchmarkResults(results)
- })
-}
-
-// endPosInBuffer returns the position for last character in the buffer for
-// the given file.
-func endPosInBuffer(env *Env, name string) fake.Pos {
- buffer := env.Editor.BufferText(name)
- lines := strings.Split(buffer, "\n")
- numLines := len(lines)
-
- return fake.Pos{
- Line: numLines - 1,
- Column: len([]rune(lines[numLines-1])),
- }
-}
-
-// Benchmark completion at a specified file and location. When no CLI options
-// are specified, this test is skipped.
-// To Run (from x/tools/gopls) against the dummy function above:
-//
-// go test -v ./internal/regtest/bench -run=TestBenchmarkConfiguredCompletion
-// -completion_workdir="$HOME/Developer/tools"
-// -completion_file="gopls/internal/regtest/completion_bench_test.go"
-// -completion_regexp="dummyCompletionFunction.*fmt\.Printf\(\"%s\", s(\))"
-func TestBenchmarkConfiguredCompletion(t *testing.T) {
- benchmarkCompletion(completionOptions, t)
-}
-
-// To run (from x/tools/gopls):
-// go test -v ./internal/regtest/bench -run TestBenchmark<>Completion
-// -completion_workdir="$HOME/Developer/tools"
-// where <> is one of the tests below. completion_workdir should be path to
-// x/tools on your system.
-
-// Benchmark struct completion in tools codebase.
-func TestBenchmarkStructCompletion(t *testing.T) {
- file := "internal/lsp/cache/session.go"
-
- preCompletionEdits := func(env *Env) {
- env.OpenFile(file)
- originalBuffer := env.Editor.BufferText(file)
- env.EditBuffer(file, fake.Edit{
- End: endPosInBuffer(env, file),
- Text: originalBuffer + "\nvar testVariable map[string]bool = Session{}.\n",
- })
- }
-
- benchmarkCompletion(completionBenchOptions{
- workdir: completionOptions.workdir,
- file: file,
- locationRegexp: `var testVariable map\[string\]bool = Session{}(\.)`,
- preCompletionEdits: preCompletionEdits,
- printResults: completionOptions.printResults,
- }, t)
-}
-
-// Benchmark import completion in tools codebase.
-func TestBenchmarkImportCompletion(t *testing.T) {
- benchmarkCompletion(completionBenchOptions{
- workdir: completionOptions.workdir,
- file: "internal/lsp/source/completion/completion.go",
- locationRegexp: `go\/()`,
- printResults: completionOptions.printResults,
- }, t)
-}
-
-// Benchmark slice completion in tools codebase.
-func TestBenchmarkSliceCompletion(t *testing.T) {
- file := "internal/lsp/cache/session.go"
-
- preCompletionEdits := func(env *Env) {
- env.OpenFile(file)
- originalBuffer := env.Editor.BufferText(file)
- env.EditBuffer(file, fake.Edit{
- End: endPosInBuffer(env, file),
- Text: originalBuffer + "\nvar testVariable []byte = \n",
- })
- }
-
- benchmarkCompletion(completionBenchOptions{
- workdir: completionOptions.workdir,
- file: file,
- locationRegexp: `var testVariable \[\]byte (=)`,
- preCompletionEdits: preCompletionEdits,
- printResults: completionOptions.printResults,
- }, t)
-}
-
-// Benchmark deep completion in function call in tools codebase.
-func TestBenchmarkFuncDeepCompletion(t *testing.T) {
- file := "internal/lsp/source/completion/completion.go"
- fileContent := `
-func (c *completer) _() {
- c.inference.kindMatches(c.)
-}
-`
- preCompletionEdits := func(env *Env) {
- env.OpenFile(file)
- originalBuffer := env.Editor.BufferText(file)
- env.EditBuffer(file, fake.Edit{
- End: endPosInBuffer(env, file),
- Text: originalBuffer + fileContent,
- })
- }
-
- benchmarkCompletion(completionBenchOptions{
- workdir: completionOptions.workdir,
- file: file,
- locationRegexp: `func \(c \*completer\) _\(\) {\n\tc\.inference\.kindMatches\((c)`,
- preCompletionEdits: preCompletionEdits,
- printResults: completionOptions.printResults,
- }, t)
-}
diff -urN a/gopls/internal/regtest/bench/stress_test.go b/gopls/internal/regtest/bench/stress_test.go
--- a/gopls/internal/regtest/bench/stress_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/bench/stress_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,66 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package bench
-
-import (
- "context"
- "flag"
- "fmt"
- "testing"
- "time"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-// Pilosa is a repository that has historically caused significant memory
-// problems for Gopls. We use it for a simple stress test that types
-// arbitrarily in a file with lots of dependents.
-
-var pilosaPath = flag.String("pilosa_path", "", "Path to a directory containing "+
- "github.com/pilosa/pilosa, for stress testing. Do not set this unless you "+
- "know what you're doing!")
-
-func stressTestOptions(dir string) []RunOption {
- opts := benchmarkOptions(dir)
- opts = append(opts, SkipHooks(true), DebugAddress(":8087"))
- return opts
-}
-
-func TestPilosaStress(t *testing.T) {
- if *pilosaPath == "" {
- t.Skip("-pilosa_path not configured")
- }
- opts := stressTestOptions(*pilosaPath)
-
- WithOptions(opts...).Run(t, "", func(_ *testing.T, env *Env) {
- files := []string{
- "cmd.go",
- "internal/private.pb.go",
- "roaring/roaring.go",
- "roaring/roaring_internal_test.go",
- "server/handler_test.go",
- }
- for _, file := range files {
- env.OpenFile(file)
- }
- ctx, cancel := context.WithTimeout(env.Ctx, 10*time.Minute)
- defer cancel()
-
- i := 1
- // MagicNumber is an identifier that occurs in roaring.go. Just change it
- // arbitrarily.
- env.RegexpReplace("roaring/roaring.go", "MagicNumber", fmt.Sprintf("MagicNumber%d", 1))
- for {
- select {
- case <-ctx.Done():
- return
- default:
- }
- env.RegexpReplace("roaring/roaring.go", fmt.Sprintf("MagicNumber%d", i), fmt.Sprintf("MagicNumber%d", i+1))
- time.Sleep(20 * time.Millisecond)
- i++
- }
- })
-}
diff -urN a/gopls/internal/regtest/codelens/codelens_test.go b/gopls/internal/regtest/codelens/codelens_test.go
--- a/gopls/internal/regtest/codelens/codelens_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/codelens/codelens_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,355 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package codelens
-
-import (
- "fmt"
- "runtime"
- "strings"
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/command"
- "golang.org/x/tools/internal/lsp/fake"
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/lsp/tests"
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestMain(m *testing.M) {
- bug.PanicOnBugs = true
- Main(m, hooks.Options)
-}
-
-func TestDisablingCodeLens(t *testing.T) {
- const workspace = `
--- go.mod --
-module codelens.test
-
-go 1.12
--- lib.go --
-package lib
-
-type Number int
-
-const (
- Zero Number = iota
- One
- Two
-)
-
-//go:generate stringer -type=Number
-`
- tests := []struct {
- label string
- enabled map[string]bool
- wantCodeLens bool
- }{
- {
- label: "default",
- wantCodeLens: true,
- },
- {
- label: "generate disabled",
- enabled: map[string]bool{string(command.Generate): false},
- wantCodeLens: false,
- },
- }
- for _, test := range tests {
- t.Run(test.label, func(t *testing.T) {
- WithOptions(
- EditorConfig{
- CodeLenses: test.enabled,
- },
- ).Run(t, workspace, func(t *testing.T, env *Env) {
- env.OpenFile("lib.go")
- lens := env.CodeLens("lib.go")
- if gotCodeLens := len(lens) > 0; gotCodeLens != test.wantCodeLens {
- t.Errorf("got codeLens: %t, want %t", gotCodeLens, test.wantCodeLens)
- }
- })
- })
- }
-}
-
-// This test confirms the full functionality of the code lenses for updating
-// dependencies in a go.mod file. It checks for the code lens that suggests
-// an update and then executes the command associated with that code lens. A
-// regression test for golang/go#39446.
-func TestUpgradeCodelens(t *testing.T) {
- const proxyWithLatest = `
--- golang.org/x/hello@v1.3.3/go.mod --
-module golang.org/x/hello
-
-go 1.12
--- golang.org/x/hello@v1.3.3/hi/hi.go --
-package hi
-
-var Goodbye error
--- golang.org/x/hello@v1.2.3/go.mod --
-module golang.org/x/hello
-
-go 1.12
--- golang.org/x/hello@v1.2.3/hi/hi.go --
-package hi
-
-var Goodbye error
-`
-
- const shouldUpdateDep = `
--- go.mod --
-module mod.com
-
-go 1.14
-
-require golang.org/x/hello v1.2.3
--- go.sum --
-golang.org/x/hello v1.2.3 h1:7Wesfkx/uBd+eFgPrq0irYj/1XfmbvLV8jZ/W7C2Dwg=
-golang.org/x/hello v1.2.3/go.mod h1:OgtlzsxVMUUdsdQCIDYgaauCTH47B8T8vofouNJfzgY=
--- main.go --
-package main
-
-import "golang.org/x/hello/hi"
-
-func main() {
- _ = hi.Goodbye
-}
-`
-
- const wantGoMod = `module mod.com
-
-go 1.14
-
-require golang.org/x/hello v1.3.3
-`
-
- for _, commandTitle := range []string{
- "Upgrade transitive dependencies",
- "Upgrade direct dependencies",
- } {
- t.Run(commandTitle, func(t *testing.T) {
- WithOptions(
- ProxyFiles(proxyWithLatest),
- ).Run(t, shouldUpdateDep, func(t *testing.T, env *Env) {
- env.OpenFile("go.mod")
- var lens protocol.CodeLens
- var found bool
- for _, l := range env.CodeLens("go.mod") {
- if l.Command.Title == commandTitle {
- lens = l
- found = true
- }
- }
- if !found {
- t.Fatalf("found no command with the title %s", commandTitle)
- }
- if _, err := env.Editor.ExecuteCommand(env.Ctx, &protocol.ExecuteCommandParams{
- Command: lens.Command.Command,
- Arguments: lens.Command.Arguments,
- }); err != nil {
- t.Fatal(err)
- }
- env.Await(env.DoneWithChangeWatchedFiles())
- if got := env.Editor.BufferText("go.mod"); got != wantGoMod {
- t.Fatalf("go.mod upgrade failed:\n%s", tests.Diff(t, wantGoMod, got))
- }
- })
- })
- }
- for _, vendoring := range []bool{false, true} {
- t.Run(fmt.Sprintf("Upgrade individual dependency vendoring=%v", vendoring), func(t *testing.T) {
- WithOptions(ProxyFiles(proxyWithLatest)).Run(t, shouldUpdateDep, func(t *testing.T, env *Env) {
- if vendoring {
- env.RunGoCommand("mod", "vendor")
- }
- env.Await(env.DoneWithChangeWatchedFiles())
- env.OpenFile("go.mod")
- env.ExecuteCodeLensCommand("go.mod", command.CheckUpgrades)
- d := &protocol.PublishDiagnosticsParams{}
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("go.mod", `require`, "can be upgraded"),
- ReadDiagnostics("go.mod", d),
- ),
- )
- env.ApplyQuickFixes("go.mod", d.Diagnostics)
- env.Await(env.DoneWithChangeWatchedFiles())
- if got := env.Editor.BufferText("go.mod"); got != wantGoMod {
- t.Fatalf("go.mod upgrade failed:\n%s", tests.Diff(t, wantGoMod, got))
- }
- })
- })
- }
-}
-
-func TestUnusedDependenciesCodelens(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
- const proxy = `
--- golang.org/x/hello@v1.0.0/go.mod --
-module golang.org/x/hello
-
-go 1.14
--- golang.org/x/hello@v1.0.0/hi/hi.go --
-package hi
-
-var Goodbye error
--- golang.org/x/unused@v1.0.0/go.mod --
-module golang.org/x/unused
-
-go 1.14
--- golang.org/x/unused@v1.0.0/nouse/nouse.go --
-package nouse
-
-var NotUsed error
-`
-
- const shouldRemoveDep = `
--- go.mod --
-module mod.com
-
-go 1.14
-
-require golang.org/x/hello v1.0.0
-require golang.org/x/unused v1.0.0
--- go.sum --
-golang.org/x/hello v1.0.0 h1:qbzE1/qT0/zojAMd/JcPsO2Vb9K4Bkeyq0vB2JGMmsw=
-golang.org/x/hello v1.0.0/go.mod h1:WW7ER2MRNXWA6c8/4bDIek4Hc/+DofTrMaQQitGXcco=
-golang.org/x/unused v1.0.0 h1:LecSbCn5P3vTcxubungSt1Pn4D/WocCaiWOPDC0y0rw=
-golang.org/x/unused v1.0.0/go.mod h1:ihoW8SgWzugwwj0N2SfLfPZCxTB1QOVfhMfB5PWTQ8U=
--- main.go --
-package main
-
-import "golang.org/x/hello/hi"
-
-func main() {
- _ = hi.Goodbye
-}
-`
- WithOptions(ProxyFiles(proxy)).Run(t, shouldRemoveDep, func(t *testing.T, env *Env) {
- env.OpenFile("go.mod")
- env.ExecuteCodeLensCommand("go.mod", command.Tidy)
- env.Await(env.DoneWithChangeWatchedFiles())
- got := env.Editor.BufferText("go.mod")
- const wantGoMod = `module mod.com
-
-go 1.14
-
-require golang.org/x/hello v1.0.0
-`
- if got != wantGoMod {
- t.Fatalf("go.mod tidy failed:\n%s", tests.Diff(t, wantGoMod, got))
- }
- })
-}
-
-func TestRegenerateCgo(t *testing.T) {
- testenv.NeedsTool(t, "cgo")
- testenv.NeedsGo1Point(t, 15)
-
- const workspace = `
--- go.mod --
-module example.com
-
-go 1.12
--- cgo.go --
-package x
-
-/*
-int fortythree() { return 42; }
-*/
-import "C"
-
-func Foo() {
- print(C.fortytwo())
-}
-`
- Run(t, workspace, func(t *testing.T, env *Env) {
- // Open the file. We have a nonexistant symbol that will break cgo processing.
- env.OpenFile("cgo.go")
- env.Await(env.DiagnosticAtRegexpWithMessage("cgo.go", ``, "go list failed to return CompiledGoFiles"))
-
- // Fix the C function name. We haven't regenerated cgo, so nothing should be fixed.
- env.RegexpReplace("cgo.go", `int fortythree`, "int fortytwo")
- env.SaveBuffer("cgo.go")
- env.Await(OnceMet(
- env.DoneWithSave(),
- env.DiagnosticAtRegexpWithMessage("cgo.go", ``, "go list failed to return CompiledGoFiles"),
- ))
-
- // Regenerate cgo, fixing the diagnostic.
- env.ExecuteCodeLensCommand("cgo.go", command.RegenerateCgo)
- env.Await(EmptyDiagnostics("cgo.go"))
- })
-}
-
-func TestGCDetails(t *testing.T) {
- testenv.NeedsGo1Point(t, 15)
- if runtime.GOOS == "android" {
- t.Skipf("the gc details code lens doesn't work on Android")
- }
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.15
--- main.go --
-package main
-
-import "fmt"
-
-func main() {
- fmt.Println(42)
-}
-`
- WithOptions(
- EditorConfig{
- CodeLenses: map[string]bool{
- "gc_details": true,
- }},
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.ExecuteCodeLensCommand("main.go", command.GCDetails)
- d := &protocol.PublishDiagnosticsParams{}
- env.Await(
- OnceMet(
- DiagnosticAt("main.go", 5, 13),
- ReadDiagnostics("main.go", d),
- ),
- )
- // Confirm that the diagnostics come from the gc details code lens.
- var found bool
- for _, d := range d.Diagnostics {
- if d.Severity != protocol.SeverityInformation {
- t.Fatalf("unexpected diagnostic severity %v, wanted Information", d.Severity)
- }
- if strings.Contains(d.Message, "42 escapes") {
- found = true
- }
- }
- if !found {
- t.Fatalf(`expected to find diagnostic with message "escape(42 escapes to heap)", found none`)
- }
-
- // Editing a buffer should cause gc_details diagnostics to disappear, since
- // they only apply to saved buffers.
- env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, "\n\n"))
- env.Await(EmptyDiagnostics("main.go"))
-
- // Saving a buffer should re-format back to the original state, and
- // re-enable the gc_details diagnostics.
- env.SaveBuffer("main.go")
- env.Await(DiagnosticAt("main.go", 5, 13))
-
- // Toggle the GC details code lens again so now it should be off.
- env.ExecuteCodeLensCommand("main.go", command.GCDetails)
- env.Await(
- EmptyDiagnostics("main.go"),
- )
- })
-}
diff -urN a/gopls/internal/regtest/completion/completion18_test.go b/gopls/internal/regtest/completion/completion18_test.go
--- a/gopls/internal/regtest/completion/completion18_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/completion/completion18_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,123 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.18
-// +build go1.18
-
-package completion
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-// test generic receivers
-func TestGenericReceiver(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.18
--- main.go --
-package main
-type SyncMap[K any, V comparable] struct {}
-func (s *SyncMap[K,V]) f() {}
-type XX[T any] struct {}
-type UU[T any] struct {}
-func (s SyncMap[XX,string]) g(v UU) {}
-`
-
- tests := []struct {
- pat string
- want []string
- }{
- {"s .Syn", []string{"SyncMap[K, V]"}},
- {"Map.X", []string{}}, // This is probably wrong, Maybe "XX"?
- {"v U", []string{"UU", "uint", "uint16", "uint32", "uint64", "uint8", "uintptr"}}, // not U[T]
- }
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.Await(env.DoneWithOpen())
- for _, tst := range tests {
- pos := env.RegexpSearch("main.go", tst.pat)
- pos.Column += len(tst.pat)
- completions := env.Completion("main.go", pos)
- result := compareCompletionResults(tst.want, completions.Items)
- if result != "" {
- t.Errorf("%s: wanted %v", result, tst.want)
- for i, g := range completions.Items {
- t.Errorf("got %d %s %s", i, g.Label, g.Detail)
- }
- }
- }
- })
-}
-func TestFuzzFunc(t *testing.T) {
- // use the example from the package documentation
- modfile := `
--- go.mod --
-module mod.com
-
-go 1.18
-`
- part0 := `package foo
-import "testing"
-func FuzzNone(f *testing.F) {
- f.Add(12) // better not find this f.Add
-}
-func FuzzHex(f *testing.F) {
- for _, seed := range [][]byte{{}, {0}, {9}, {0xa}, {0xf}, {1, 2, 3, 4}} {
- f.Ad`
- part1 := `d(seed)
- }
- f.F`
- part2 := `uzz(func(t *testing.T, in []byte) {
- enc := hex.EncodeToString(in)
- out, err := hex.DecodeString(enc)
- if err != nil {
- f.Failed()
- }
- if !bytes.Equal(in, out) {
- t.Fatalf("%v: round trip: %v, %s", in, out, f.Name())
- }
- })
-}
-`
- data := modfile + `-- a_test.go --
-` + part0 + `
--- b_test.go --
-` + part0 + part1 + `
--- c_test.go --
-` + part0 + part1 + part2
-
- tests := []struct {
- file string
- pat string
- offset int // from the beginning of pat to what the user just typed
- want []string
- }{
- {"a_test.go", "f.Ad", 3, []string{"Add"}},
- {"c_test.go", " f.F", 4, []string{"Failed"}},
- {"c_test.go", "f.N", 3, []string{"Name"}},
- {"b_test.go", "f.F", 3, []string{"Fuzz(func(t *testing.T, a []byte)", "Fail", "FailNow",
- "Failed", "Fatal", "Fatalf"}},
- }
- Run(t, data, func(t *testing.T, env *Env) {
- for _, test := range tests {
- env.OpenFile(test.file)
- env.Await(env.DoneWithOpen())
- pos := env.RegexpSearch(test.file, test.pat)
- pos.Column += test.offset // character user just typed? will type?
- completions := env.Completion(test.file, pos)
- result := compareCompletionResults(test.want, completions.Items)
- if result != "" {
- t.Errorf("pat %q %q", test.pat, result)
- for i, it := range completions.Items {
- t.Errorf("%d got %q %q", i, it.Label, it.Detail)
- }
- }
- }
- })
-}
diff -urN a/gopls/internal/regtest/completion/completion_test.go b/gopls/internal/regtest/completion/completion_test.go
--- a/gopls/internal/regtest/completion/completion_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/completion/completion_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,648 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package completion
-
-import (
- "fmt"
- "strings"
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/fake"
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestMain(m *testing.M) {
- bug.PanicOnBugs = true
- Main(m, hooks.Options)
-}
-
-const proxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.3/blah/blah.go --
-package blah
-
-const Name = "Blah"
--- random.org@v1.2.3/go.mod --
-module random.org
-
-go 1.12
--- random.org@v1.2.3/blah/blah.go --
-package hello
-
-const Name = "Hello"
-`
-
-func TestPackageCompletion(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- fruits/apple.go --
-package apple
-
-fun apple() int {
- return 0
-}
-
--- fruits/testfile.go --
-// this is a comment
-
-/*
- this is a multiline comment
-*/
-
-import "fmt"
-
-func test() {}
-
--- fruits/testfile2.go --
-package
-
--- fruits/testfile3.go --
-pac
--- 123f_r.u~its-123/testfile.go --
-package
-
--- .invalid-dir@-name/testfile.go --
-package
-`
- var (
- testfile4 = ""
- testfile5 = "/*a comment*/ "
- testfile6 = "/*a comment*/\n"
- )
- for _, tc := range []struct {
- name string
- filename string
- content *string
- triggerRegexp string
- want []string
- editRegexp string
- }{
- {
- name: "package completion at valid position",
- filename: "fruits/testfile.go",
- triggerRegexp: "\n()",
- want: []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
- editRegexp: "\n()",
- },
- {
- name: "package completion in a comment",
- filename: "fruits/testfile.go",
- triggerRegexp: "th(i)s",
- want: nil,
- },
- {
- name: "package completion in a multiline comment",
- filename: "fruits/testfile.go",
- triggerRegexp: `\/\*\n()`,
- want: nil,
- },
- {
- name: "package completion at invalid position",
- filename: "fruits/testfile.go",
- triggerRegexp: "import \"fmt\"\n()",
- want: nil,
- },
- {
- name: "package completion after keyword 'package'",
- filename: "fruits/testfile2.go",
- triggerRegexp: "package()",
- want: []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
- editRegexp: "package\n",
- },
- {
- name: "package completion with 'pac' prefix",
- filename: "fruits/testfile3.go",
- triggerRegexp: "pac()",
- want: []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
- editRegexp: "pac",
- },
- {
- name: "package completion for empty file",
- filename: "fruits/testfile4.go",
- triggerRegexp: "^$",
- content: &testfile4,
- want: []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
- editRegexp: "^$",
- },
- {
- name: "package completion without terminal newline",
- filename: "fruits/testfile5.go",
- triggerRegexp: `\*\/ ()`,
- content: &testfile5,
- want: []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
- editRegexp: `\*\/ ()`,
- },
- {
- name: "package completion on terminal newline",
- filename: "fruits/testfile6.go",
- triggerRegexp: `\*\/\n()`,
- content: &testfile6,
- want: []string{"package apple", "package apple_test", "package fruits", "package fruits_test", "package main"},
- editRegexp: `\*\/\n()`,
- },
- // Issue golang/go#44680
- {
- name: "package completion for dir name with punctuation",
- filename: "123f_r.u~its-123/testfile.go",
- triggerRegexp: "package()",
- want: []string{"package fruits123", "package fruits123_test", "package main"},
- editRegexp: "package\n",
- },
- {
- name: "package completion for invalid dir name",
- filename: ".invalid-dir@-name/testfile.go",
- triggerRegexp: "package()",
- want: []string{"package main"},
- editRegexp: "package\n",
- },
- } {
- t.Run(tc.name, func(t *testing.T) {
- Run(t, files, func(t *testing.T, env *Env) {
- if tc.content != nil {
- env.WriteWorkspaceFile(tc.filename, *tc.content)
- env.Await(
- env.DoneWithChangeWatchedFiles(),
- )
- }
- env.OpenFile(tc.filename)
- completions := env.Completion(tc.filename, env.RegexpSearch(tc.filename, tc.triggerRegexp))
-
- // Check that the completion item suggestions are in the range
- // of the file.
- lineCount := len(strings.Split(env.Editor.BufferText(tc.filename), "\n"))
- for _, item := range completions.Items {
- if start := int(item.TextEdit.Range.Start.Line); start >= lineCount {
- t.Fatalf("unexpected text edit range start line number: got %d, want less than %d", start, lineCount)
- }
- if end := int(item.TextEdit.Range.End.Line); end >= lineCount {
- t.Fatalf("unexpected text edit range end line number: got %d, want less than %d", end, lineCount)
- }
- }
-
- if tc.want != nil {
- start, end := env.RegexpRange(tc.filename, tc.editRegexp)
- expectedRng := protocol.Range{
- Start: fake.Pos.ToProtocolPosition(start),
- End: fake.Pos.ToProtocolPosition(end),
- }
- for _, item := range completions.Items {
- gotRng := item.TextEdit.Range
- if expectedRng != gotRng {
- t.Errorf("unexpected completion range for completion item %s: got %v, want %v",
- item.Label, gotRng, expectedRng)
- }
- }
- }
-
- diff := compareCompletionResults(tc.want, completions.Items)
- if diff != "" {
- t.Error(diff)
- }
- })
- })
- }
-}
-
-func TestPackageNameCompletion(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- math/add.go --
-package ma
-`
-
- want := []string{"ma", "ma_test", "main", "math", "math_test"}
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("math/add.go")
- completions := env.Completion("math/add.go", fake.Pos{
- Line: 0,
- Column: 10,
- })
-
- diff := compareCompletionResults(want, completions.Items)
- if diff != "" {
- t.Fatal(diff)
- }
- })
-}
-
-func compareCompletionResults(want []string, gotItems []protocol.CompletionItem) string {
- if len(gotItems) != len(want) {
- return fmt.Sprintf("got %v completion(s), want %v", len(gotItems), len(want))
- }
-
- var got []string
- for _, item := range gotItems {
- got = append(got, item.Label)
- if item.Label != item.InsertText && item.TextEdit == nil {
- // Label should be the same as InsertText, if InsertText is to be used
- return fmt.Sprintf("label not the same as InsertText %#v", item)
- }
- }
-
- for i, v := range got {
- if v != want[i] {
- return fmt.Sprintf("%d completion result not the same: got %q, want %q", i, v, want[i])
- }
- }
-
- return ""
-}
-
-func TestUnimportedCompletion(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.14
-
-require example.com v1.2.3
--- go.sum --
-example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
--- main.go --
-package main
-
-func main() {
- _ = blah
-}
--- main2.go --
-package main
-
-import "example.com/blah"
-
-func _() {
- _ = blah.Hello
-}
-`
- WithOptions(
- ProxyFiles(proxy),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- // Make sure the dependency is in the module cache and accessible for
- // unimported completions, and then remove it before proceeding.
- env.RemoveWorkspaceFile("main2.go")
- env.RunGoCommand("mod", "tidy")
- env.Await(env.DoneWithChangeWatchedFiles())
-
- // Trigger unimported completions for the example.com/blah package.
- env.OpenFile("main.go")
- env.Await(env.DoneWithOpen())
- pos := env.RegexpSearch("main.go", "ah")
- completions := env.Completion("main.go", pos)
- if len(completions.Items) == 0 {
- t.Fatalf("no completion items")
- }
- env.AcceptCompletion("main.go", pos, completions.Items[0])
- env.Await(env.DoneWithChange())
-
- // Trigger completions once again for the blah.<> selector.
- env.RegexpReplace("main.go", "_ = blah", "_ = blah.")
- env.Await(env.DoneWithChange())
- pos = env.RegexpSearch("main.go", "\n}")
- completions = env.Completion("main.go", pos)
- if len(completions.Items) != 1 {
- t.Fatalf("expected 1 completion item, got %v", len(completions.Items))
- }
- item := completions.Items[0]
- if item.Label != "Name" {
- t.Fatalf("expected completion item blah.Name, got %v", item.Label)
- }
- env.AcceptCompletion("main.go", pos, item)
-
- // Await the diagnostics to add example.com/blah to the go.mod file.
- env.Await(
- env.DiagnosticAtRegexp("main.go", `"example.com/blah"`),
- )
- })
-}
-
-// Test that completions still work with an undownloaded module, golang/go#43333.
-func TestUndownloadedModule(t *testing.T) {
- // mod.com depends on example.com, but only in a file that's hidden by a
- // build tag, so the IWL won't download example.com. That will cause errors
- // in the go list -m call performed by the imports package.
- const files = `
--- go.mod --
-module mod.com
-
-go 1.14
-
-require example.com v1.2.3
--- go.sum --
-example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
--- useblah.go --
-// +build hidden
-
-package pkg
-import "example.com/blah"
-var _ = blah.Name
--- mainmod/mainmod.go --
-package mainmod
-
-const Name = "mainmod"
-`
- WithOptions(ProxyFiles(proxy)).Run(t, files, func(t *testing.T, env *Env) {
- env.CreateBuffer("import.go", "package pkg\nvar _ = mainmod.Name\n")
- env.SaveBuffer("import.go")
- content := env.ReadWorkspaceFile("import.go")
- if !strings.Contains(content, `import "mod.com/mainmod`) {
- t.Errorf("expected import of mod.com/mainmod in %q", content)
- }
- })
-}
-
-// Test that we can doctor the source code enough so the file is
-// parseable and completion works as expected.
-func TestSourceFixup(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- foo.go --
-package foo
-
-func _() {
- var s S
- if s.
-}
-
-type S struct {
- i int
-}
-`
-
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("foo.go")
- completions := env.Completion("foo.go", env.RegexpSearch("foo.go", `if s\.()`))
- diff := compareCompletionResults([]string{"i"}, completions.Items)
- if diff != "" {
- t.Fatal(diff)
- }
- })
-}
-
-func TestCompletion_Issue45510(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-func _() {
- type a *a
- var aaaa1, aaaa2 a
- var _ a = aaaa
-
- type b a
- var bbbb1, bbbb2 b
- var _ b = bbbb
-}
-
-type (
- c *d
- d *e
- e **c
-)
-
-func _() {
- var (
- xxxxc c
- xxxxd d
- xxxxe e
- )
-
- var _ c = xxxx
- var _ d = xxxx
- var _ e = xxxx
-}
-`
-
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
-
- tests := []struct {
- re string
- want []string
- }{
- {`var _ a = aaaa()`, []string{"aaaa1", "aaaa2"}},
- {`var _ b = bbbb()`, []string{"bbbb1", "bbbb2"}},
- {`var _ c = xxxx()`, []string{"xxxxc", "xxxxd", "xxxxe"}},
- {`var _ d = xxxx()`, []string{"xxxxc", "xxxxd", "xxxxe"}},
- {`var _ e = xxxx()`, []string{"xxxxc", "xxxxd", "xxxxe"}},
- }
- for _, tt := range tests {
- completions := env.Completion("main.go", env.RegexpSearch("main.go", tt.re))
- diff := compareCompletionResults(tt.want, completions.Items)
- if diff != "" {
- t.Errorf("%s: %s", tt.re, diff)
- }
- }
- })
-}
-
-func TestCompletionDeprecation(t *testing.T) {
- const files = `
--- go.mod --
-module test.com
-
-go 1.16
--- prog.go --
-package waste
-// Deprecated, use newFoof
-func fooFunc() bool {
- return false
-}
-
-// Deprecated
-const badPi = 3.14
-
-func doit() {
- if fooF
- panic()
- x := badP
-}
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("prog.go")
- pos := env.RegexpSearch("prog.go", "if fooF")
- pos.Column += len("if fooF")
- completions := env.Completion("prog.go", pos)
- diff := compareCompletionResults([]string{"fooFunc"}, completions.Items)
- if diff != "" {
- t.Error(diff)
- }
- if completions.Items[0].Tags == nil {
- t.Errorf("expected Tags to show deprecation %#v", diff[0])
- }
- pos = env.RegexpSearch("prog.go", "= badP")
- pos.Column += len("= badP")
- completions = env.Completion("prog.go", pos)
- diff = compareCompletionResults([]string{"badPi"}, completions.Items)
- if diff != "" {
- t.Error(diff)
- }
- if completions.Items[0].Tags == nil {
- t.Errorf("expected Tags to show deprecation %#v", diff[0])
- }
- })
-}
-
-func TestUnimportedCompletion_VSCodeIssue1489(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const src = `
--- go.mod --
-module mod.com
-
-go 1.14
-
--- main.go --
-package main
-
-import "fmt"
-
-func main() {
- fmt.Println("a")
- math.Sqr
-}
-`
- WithOptions(
- EditorConfig{WindowsLineEndings: true},
- ).Run(t, src, func(t *testing.T, env *Env) {
- // Trigger unimported completions for the example.com/blah package.
- env.OpenFile("main.go")
- env.Await(env.DoneWithOpen())
- pos := env.RegexpSearch("main.go", "Sqr()")
- completions := env.Completion("main.go", pos)
- if len(completions.Items) == 0 {
- t.Fatalf("no completion items")
- }
- env.AcceptCompletion("main.go", pos, completions.Items[0])
- env.Await(env.DoneWithChange())
- got := env.Editor.BufferText("main.go")
- want := "package main\r\n\r\nimport (\r\n\t\"fmt\"\r\n\t\"math\"\r\n)\r\n\r\nfunc main() {\r\n\tfmt.Println(\"a\")\r\n\tmath.Sqrt(${1:})\r\n}\r\n"
- if got != want {
- t.Errorf("unimported completion: got %q, want %q", got, want)
- }
- })
-}
-
-func TestDefinition(t *testing.T) {
- stuff := `
--- go.mod --
-module mod.com
-
-go 1.18
--- a_test.go --
-package foo
-func T()
-func TestG()
-func TestM()
-func TestMi()
-func Ben()
-func Fuz()
-func Testx()
-func TestMe(t *testing.T)
-func BenchmarkFoo()
-`
- // All those parentheses are needed for the completion code to see
- // later lines as being definitions
- tests := []struct {
- pat string
- want []string
- }{
- {"T", []string{"TestXxx(t *testing.T)", "TestMain(m *testing.M)"}},
- {"TestM", []string{"TestMain(m *testing.M)", "TestM(t *testing.T)"}},
- {"TestMi", []string{"TestMi(t *testing.T)"}},
- {"TestG", []string{"TestG(t *testing.T)"}},
- {"B", []string{"BenchmarkXxx(b *testing.B)"}},
- {"BenchmarkFoo", []string{"BenchmarkFoo(b *testing.B)"}},
- {"F", []string{"FuzzXxx(f *testing.F)"}},
- {"Testx", nil},
- {"TestMe", []string{"TestMe"}},
- }
- fname := "a_test.go"
- Run(t, stuff, func(t *testing.T, env *Env) {
- env.OpenFile(fname)
- env.Await(env.DoneWithOpen())
- for _, tst := range tests {
- pos := env.RegexpSearch(fname, tst.pat)
- pos.Column += len(tst.pat)
- completions := env.Completion(fname, pos)
- result := compareCompletionResults(tst.want, completions.Items)
- if result != "" {
- t.Errorf("%s failed: %s:%q", tst.pat, result, tst.want)
- for i, it := range completions.Items {
- t.Errorf("%d got %q %q", i, it.Label, it.Detail)
- }
- }
- }
- })
-}
-
-func TestGoWorkCompletion(t *testing.T) {
- const files = `
--- go.work --
-go 1.18
-
-use ./a
-use ./a/ba
-use ./a/b/
-use ./dir/foo
-use ./dir/foobar/
--- a/go.mod --
--- go.mod --
--- a/bar/go.mod --
--- a/b/c/d/e/f/go.mod --
--- dir/bar --
--- dir/foobar/go.mod --
-`
-
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("go.work")
-
- tests := []struct {
- re string
- want []string
- }{
- {`use ()\.`, []string{".", "./a", "./a/bar", "./dir/foobar"}},
- {`use \.()`, []string{"", "/a", "/a/bar", "/dir/foobar"}},
- {`use \./()`, []string{"a", "a/bar", "dir/foobar"}},
- {`use ./a()`, []string{"", "/b/c/d/e/f", "/bar"}},
- {`use ./a/b()`, []string{"/c/d/e/f", "ar"}},
- {`use ./a/b/()`, []string{`c/d/e/f`}},
- {`use ./a/ba()`, []string{"r"}},
- {`use ./dir/foo()`, []string{"bar"}},
- {`use ./dir/foobar/()`, []string{}},
- }
- for _, tt := range tests {
- completions := env.Completion("go.work", env.RegexpSearch("go.work", tt.re))
- diff := compareCompletionResults(tt.want, completions.Items)
- if diff != "" {
- t.Errorf("%s: %s", tt.re, diff)
- }
- }
- })
-}
diff -urN a/gopls/internal/regtest/completion/postfix_snippet_test.go b/gopls/internal/regtest/completion/postfix_snippet_test.go
--- a/gopls/internal/regtest/completion/postfix_snippet_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/completion/postfix_snippet_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,442 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package completion
-
-import (
- "strings"
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
- "golang.org/x/tools/internal/lsp/source"
-)
-
-func TestPostfixSnippetCompletion(t *testing.T) {
- t.Skipf("skipping test due to suspected synchronization bug; see https://go.dev/issue/50707")
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
-`
-
- cases := []struct {
- name string
- before, after string
- }{
- {
- name: "sort",
- before: `
-package foo
-
-func _() {
- var foo []int
- foo.sort
-}
-`,
- after: `
-package foo
-
-import "sort"
-
-func _() {
- var foo []int
- sort.Slice(foo, func(i, j int) bool {
- $0
-})
-}
-`,
- },
- {
- name: "sort_renamed_sort_package",
- before: `
-package foo
-
-import blahsort "sort"
-
-var j int
-
-func _() {
- var foo []int
- foo.sort
-}
-`,
- after: `
-package foo
-
-import blahsort "sort"
-
-var j int
-
-func _() {
- var foo []int
- blahsort.Slice(foo, func(i, j2 int) bool {
- $0
-})
-}
-`,
- },
- {
- name: "last",
- before: `
-package foo
-
-func _() {
- var s struct { i []int }
- s.i.last
-}
-`,
- after: `
-package foo
-
-func _() {
- var s struct { i []int }
- s.i[len(s.i)-1]
-}
-`,
- },
- {
- name: "reverse",
- before: `
-package foo
-
-func _() {
- var foo []int
- foo.reverse
-}
-`,
- after: `
-package foo
-
-func _() {
- var foo []int
- for i, j := 0, len(foo)-1; i < j; i, j = i+1, j-1 {
- foo[i], foo[j] = foo[j], foo[i]
-}
-
-}
-`,
- },
- {
- name: "slice_range",
- before: `
-package foo
-
-func _() {
- type myThing struct{}
- var foo []myThing
- foo.range
-}
-`,
- after: `
-package foo
-
-func _() {
- type myThing struct{}
- var foo []myThing
- for i, mt := range foo {
- $0
-}
-}
-`,
- },
- {
- name: "append_stmt",
- before: `
-package foo
-
-func _() {
- var foo []int
- foo.append
-}
-`,
- after: `
-package foo
-
-func _() {
- var foo []int
- foo = append(foo, $0)
-}
-`,
- },
- {
- name: "append_expr",
- before: `
-package foo
-
-func _() {
- var foo []int
- var _ []int = foo.append
-}
-`,
- after: `
-package foo
-
-func _() {
- var foo []int
- var _ []int = append(foo, $0)
-}
-`,
- },
- {
- name: "slice_copy",
- before: `
-package foo
-
-func _() {
- var foo []int
- foo.copy
-}
-`,
- after: `
-package foo
-
-func _() {
- var foo []int
- fooCopy := make([]int, len(foo))
-copy(fooCopy, foo)
-
-}
-`,
- },
- {
- name: "map_range",
- before: `
-package foo
-
-func _() {
- var foo map[string]int
- foo.range
-}
-`,
- after: `
-package foo
-
-func _() {
- var foo map[string]int
- for k, v := range foo {
- $0
-}
-}
-`,
- },
- {
- name: "map_clear",
- before: `
-package foo
-
-func _() {
- var foo map[string]int
- foo.clear
-}
-`,
- after: `
-package foo
-
-func _() {
- var foo map[string]int
- for k := range foo {
- delete(foo, k)
-}
-
-}
-`,
- },
- {
- name: "map_keys",
- before: `
-package foo
-
-func _() {
- var foo map[string]int
- foo.keys
-}
-`,
- after: `
-package foo
-
-func _() {
- var foo map[string]int
- keys := make([]string, 0, len(foo))
-for k := range foo {
- keys = append(keys, k)
-}
-
-}
-`,
- },
- {
- name: "var",
- before: `
-package foo
-
-func foo() (int, error) { return 0, nil }
-
-func _() {
- foo().var
-}
-`,
- after: `
-package foo
-
-func foo() (int, error) { return 0, nil }
-
-func _() {
- i, err := foo()
-}
-`,
- },
- {
- name: "var_single_value",
- before: `
-package foo
-
-func foo() error { return nil }
-
-func _() {
- foo().var
-}
-`,
- after: `
-package foo
-
-func foo() error { return nil }
-
-func _() {
- err := foo()
-}
-`,
- },
- {
- name: "var_same_type",
- before: `
-package foo
-
-func foo() (int, int) { return 0, 0 }
-
-func _() {
- foo().var
-}
-`,
- after: `
-package foo
-
-func foo() (int, int) { return 0, 0 }
-
-func _() {
- i, i2 := foo()
-}
-`,
- },
- {
- name: "print_scalar",
- before: `
-package foo
-
-func _() {
- var foo int
- foo.print
-}
-`,
- after: `
-package foo
-
-import "fmt"
-
-func _() {
- var foo int
- fmt.Printf("foo: %v\n", foo)
-}
-`,
- },
- {
- name: "print_multi",
- before: `
-package foo
-
-func foo() (int, error) { return 0, nil }
-
-func _() {
- foo().print
-}
-`,
- after: `
-package foo
-
-import "fmt"
-
-func foo() (int, error) { return 0, nil }
-
-func _() {
- fmt.Println(foo())
-}
-`,
- },
- {
- name: "string split",
- before: `
-package foo
-
-func foo() []string {
- x := "test"
- return x.split
-}`,
- after: `
-package foo
-
-import "strings"
-
-func foo() []string {
- x := "test"
- return strings.Split(x, "$0")
-}`,
- },
- {
- name: "string slice join",
- before: `
-package foo
-
-func foo() string {
- x := []string{"a", "test"}
- return x.join
-}`,
- after: `
-package foo
-
-import "strings"
-
-func foo() string {
- x := []string{"a", "test"}
- return strings.Join(x, "$0")
-}`,
- },
- }
-
- r := WithOptions(Options(func(o *source.Options) {
- o.ExperimentalPostfixCompletions = true
- }))
- r.Run(t, mod, func(t *testing.T, env *Env) {
- for _, c := range cases {
- t.Run(c.name, func(t *testing.T) {
- c.before = strings.Trim(c.before, "\n")
- c.after = strings.Trim(c.after, "\n")
-
- env.CreateBuffer("foo.go", c.before)
-
- pos := env.RegexpSearch("foo.go", "\n}")
- completions := env.Completion("foo.go", pos)
- if len(completions.Items) != 1 {
- t.Fatalf("expected one completion, got %v", completions.Items)
- }
-
- env.AcceptCompletion("foo.go", pos, completions.Items[0])
-
- if buf := env.Editor.BufferText("foo.go"); buf != c.after {
- t.Errorf("\nGOT:\n%s\nEXPECTED:\n%s", buf, c.after)
- }
- })
- }
- })
-}
diff -urN a/gopls/internal/regtest/debug/debug_test.go b/gopls/internal/regtest/debug/debug_test.go
--- a/gopls/internal/regtest/debug/debug_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/debug/debug_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,34 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package debug
-
-import (
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestMain(m *testing.M) {
- Main(m, hooks.Options)
-}
-
-func TestBugNotification(t *testing.T) {
- // Verify that a properly configured session gets notified of a bug on the
- // server.
- WithOptions(
- Modes(Singleton), // must be in-process to receive the bug report below
- EditorConfig{
- Settings: map[string]interface{}{
- "showBugReports": true,
- },
- },
- ).Run(t, "", func(t *testing.T, env *Env) {
- const desc = "got a bug"
- bug.Report(desc, nil)
- env.Await(ShownMessage(desc))
- })
-}
diff -urN a/gopls/internal/regtest/diagnostics/builtin_test.go b/gopls/internal/regtest/diagnostics/builtin_test.go
--- a/gopls/internal/regtest/diagnostics/builtin_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/diagnostics/builtin_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,38 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package diagnostics
-
-import (
- "strings"
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestIssue44866(t *testing.T) {
- src := `
--- go.mod --
-module mod.com
-
-go 1.12
--- a.go --
-package a
-
-const (
- c = iota
-)
-`
- Run(t, src, func(t *testing.T, env *Env) {
- env.OpenFile("a.go")
- name, _ := env.GoToDefinition("a.go", env.RegexpSearch("a.go", "iota"))
- if !strings.HasSuffix(name, "builtin.go") {
- t.Fatalf("jumped to %q, want builtin.go", name)
- }
- env.Await(OnceMet(
- env.DoneWithOpen(),
- NoDiagnostics("builtin.go"),
- ))
- })
-}
diff -urN a/gopls/internal/regtest/diagnostics/diagnostics_test.go b/gopls/internal/regtest/diagnostics/diagnostics_test.go
--- a/gopls/internal/regtest/diagnostics/diagnostics_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/diagnostics/diagnostics_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,2268 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package diagnostics
-
-import (
- "context"
- "fmt"
- "os/exec"
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp"
- "golang.org/x/tools/internal/lsp/fake"
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestMain(m *testing.M) {
- bug.PanicOnBugs = true
- Main(m, hooks.Options)
-}
-
-// Use mod.com for all go.mod files due to golang/go#35230.
-const exampleProgram = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import "fmt"
-
-func main() {
- fmt.Println("Hello World.")
-}`
-
-func TestDiagnosticErrorInEditedFile(t *testing.T) {
- // This test is very basic: start with a clean Go program, make an error, and
- // get a diagnostic for that error. However, it also demonstrates how to
- // combine Expectations to await more complex state in the editor.
- Run(t, exampleProgram, func(t *testing.T, env *Env) {
- // Deleting the 'n' at the end of Println should generate a single error
- // diagnostic.
- env.OpenFile("main.go")
- env.RegexpReplace("main.go", "Printl(n)", "")
- env.Await(
- // Once we have gotten diagnostics for the change above, we should
- // satisfy the DiagnosticAtRegexp assertion.
- OnceMet(
- env.DoneWithChange(),
- env.DiagnosticAtRegexp("main.go", "Printl"),
- ),
- // Assert that this test has sent no error logs to the client. This is not
- // strictly necessary for testing this regression, but is included here
- // as an example of using the NoErrorLogs() expectation. Feel free to
- // delete.
- NoErrorLogs(),
- )
- })
-}
-
-func TestMissingImportDiagsClearOnFirstFile(t *testing.T) {
- const onlyMod = `
--- go.mod --
-module mod.com
-
-go 1.12
-`
- Run(t, onlyMod, func(t *testing.T, env *Env) {
- env.CreateBuffer("main.go", `package main
-
-func m() {
- log.Println()
-}
-`)
- env.Await(
- env.DiagnosticAtRegexp("main.go", "log"),
- )
- env.SaveBuffer("main.go")
- env.Await(
- EmptyDiagnostics("main.go"),
- )
- })
-}
-
-func TestDiagnosticErrorInNewFile(t *testing.T) {
- const brokenFile = `package main
-
-const Foo = "abc
-`
- Run(t, brokenFile, func(t *testing.T, env *Env) {
- env.CreateBuffer("broken.go", brokenFile)
- env.Await(env.DiagnosticAtRegexp("broken.go", "\"abc"))
- })
-}
-
-// badPackage contains a duplicate definition of the 'a' const.
-const badPackage = `
--- go.mod --
-module mod.com
-
-go 1.12
--- a.go --
-package consts
-
-const a = 1
--- b.go --
-package consts
-
-const a = 2
-`
-
-func TestDiagnosticClearingOnEdit(t *testing.T) {
- Run(t, badPackage, func(t *testing.T, env *Env) {
- env.OpenFile("b.go")
- env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))
-
- // Fix the error by editing the const name in b.go to `b`.
- env.RegexpReplace("b.go", "(a) = 2", "b")
- env.Await(
- EmptyDiagnostics("a.go"),
- EmptyDiagnostics("b.go"),
- )
- })
-}
-
-func TestDiagnosticClearingOnDelete_Issue37049(t *testing.T) {
- Run(t, badPackage, func(t *testing.T, env *Env) {
- env.OpenFile("a.go")
- env.Await(env.DiagnosticAtRegexp("a.go", "a = 1"), env.DiagnosticAtRegexp("b.go", "a = 2"))
- env.RemoveWorkspaceFile("b.go")
-
- env.Await(EmptyDiagnostics("a.go"), EmptyDiagnostics("b.go"))
- })
-}
-
-func TestDiagnosticClearingOnClose(t *testing.T) {
- Run(t, badPackage, func(t *testing.T, env *Env) {
- env.CreateBuffer("c.go", `package consts
-
-const a = 3`)
- env.Await(
- env.DiagnosticAtRegexp("a.go", "a = 1"),
- env.DiagnosticAtRegexp("b.go", "a = 2"),
- env.DiagnosticAtRegexp("c.go", "a = 3"),
- )
- env.CloseBuffer("c.go")
- env.Await(
- env.DiagnosticAtRegexp("a.go", "a = 1"),
- env.DiagnosticAtRegexp("b.go", "a = 2"),
- EmptyDiagnostics("c.go"),
- )
- })
-}
-
-// Tests golang/go#37978.
-func TestIssue37978(t *testing.T) {
- Run(t, exampleProgram, func(t *testing.T, env *Env) {
- // Create a new workspace-level directory and empty file.
- env.CreateBuffer("c/c.go", "")
-
- // Write the file contents with a missing import.
- env.EditBuffer("c/c.go", fake.Edit{
- Text: `package c
-
-const a = http.MethodGet
-`,
- })
- env.Await(
- env.DiagnosticAtRegexp("c/c.go", "http.MethodGet"),
- )
- // Save file, which will organize imports, adding the expected import.
- // Expect the diagnostics to clear.
- env.SaveBuffer("c/c.go")
- env.Await(
- EmptyDiagnostics("c/c.go"),
- )
- })
-}
-
-// Tests golang/go#38878: good a.go, bad a_test.go, remove a_test.go but its errors remain
-// If the file is open in the editor, this is working as intended
-// If the file is not open in the editor, the errors go away
-const test38878 = `
--- go.mod --
-module foo
-
-go 1.12
--- a.go --
-package x
-
-// import "fmt"
-
-func f() {}
-
--- a_test.go --
-package x
-
-import "testing"
-
-func TestA(t *testing.T) {
- f(3)
-}
-`
-
-// Tests golang/go#38878: deleting a test file should clear its errors, and
-// not break the workspace.
-func TestDeleteTestVariant(t *testing.T) {
- Run(t, test38878, func(t *testing.T, env *Env) {
- env.Await(env.DiagnosticAtRegexp("a_test.go", `f\((3)\)`))
- env.RemoveWorkspaceFile("a_test.go")
- env.Await(EmptyDiagnostics("a_test.go"))
-
- // Make sure the test variant has been removed from the workspace by
- // triggering a metadata load.
- env.OpenFile("a.go")
- env.RegexpReplace("a.go", `// import`, "import")
- env.Await(env.DiagnosticAtRegexp("a.go", `"fmt"`))
- })
-}
-
-// Tests golang/go#38878: deleting a test file on disk while it's still open
-// should not clear its errors.
-func TestDeleteTestVariant_DiskOnly(t *testing.T) {
- Run(t, test38878, func(t *testing.T, env *Env) {
- env.OpenFile("a_test.go")
- env.Await(DiagnosticAt("a_test.go", 5, 3))
- env.Sandbox.Workdir.RemoveFile(context.Background(), "a_test.go")
- env.Await(OnceMet(
- env.DoneWithChangeWatchedFiles(),
- DiagnosticAt("a_test.go", 5, 3)))
- })
-}
-
-// TestNoMod confirms that gopls continues to work when a user adds a go.mod
-// file to their workspace.
-func TestNoMod(t *testing.T) {
- const noMod = `
--- main.go --
-package main
-
-import "mod.com/bob"
-
-func main() {
- bob.Hello()
-}
--- bob/bob.go --
-package bob
-
-func Hello() {
- var x int
-}
-`
-
- t.Run("manual", func(t *testing.T) {
- Run(t, noMod, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
- )
- env.CreateBuffer("go.mod", `module mod.com
-
- go 1.12
-`)
- env.SaveBuffer("go.mod")
- env.Await(
- EmptyDiagnostics("main.go"),
- )
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexp("bob/bob.go", "x"),
- ReadDiagnostics("bob/bob.go", &d),
- ),
- )
- if len(d.Diagnostics) != 1 {
- t.Fatalf("expected 1 diagnostic, got %v", len(d.Diagnostics))
- }
- })
- })
- t.Run("initialized", func(t *testing.T) {
- Run(t, noMod, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
- )
- env.RunGoCommand("mod", "init", "mod.com")
- env.Await(
- EmptyDiagnostics("main.go"),
- env.DiagnosticAtRegexp("bob/bob.go", "x"),
- )
- })
- })
-
- t.Run("without workspace module", func(t *testing.T) {
- WithOptions(
- Modes(Singleton),
- ).Run(t, noMod, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
- )
- if err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}, true); err != nil {
- t.Fatal(err)
- }
- env.Await(
- EmptyDiagnostics("main.go"),
- env.DiagnosticAtRegexp("bob/bob.go", "x"),
- )
- })
- })
-}
-
-// Tests golang/go#38267.
-func TestIssue38267(t *testing.T) {
- const testPackage = `
--- go.mod --
-module mod.com
-
-go 1.12
--- lib.go --
-package lib
-
-func Hello(x string) {
- _ = x
-}
--- lib_test.go --
-package lib
-
-import "testing"
-
-type testStruct struct{
- name string
-}
-
-func TestHello(t *testing.T) {
- testStructs := []*testStruct{
- &testStruct{"hello"},
- &testStruct{"goodbye"},
- }
- for y := range testStructs {
- _ = y
- }
-}
-`
-
- Run(t, testPackage, func(t *testing.T, env *Env) {
- env.OpenFile("lib_test.go")
- env.Await(
- DiagnosticAt("lib_test.go", 10, 2),
- DiagnosticAt("lib_test.go", 11, 2),
- )
- env.OpenFile("lib.go")
- env.RegexpReplace("lib.go", "_ = x", "var y int")
- env.Await(
- env.DiagnosticAtRegexp("lib.go", "y int"),
- EmptyDiagnostics("lib_test.go"),
- )
- })
-}
-
-// Tests golang/go#38328.
-func TestPackageChange_Issue38328(t *testing.T) {
- const packageChange = `
--- go.mod --
-module fake
-
-go 1.12
--- a.go --
-package foo
-func main() {}
-`
- Run(t, packageChange, func(t *testing.T, env *Env) {
- env.OpenFile("a.go")
- env.RegexpReplace("a.go", "foo", "foox")
- env.Await(
- // When the bug reported in #38328 was present, we didn't get erroneous
- // file diagnostics until after the didChange message generated by the
- // package renaming was fully processed. Therefore, in order for this
- // test to actually exercise the bug, we must wait until that work has
- // completed.
- OnceMet(
- env.DoneWithChange(),
- NoDiagnostics("a.go"),
- ),
- )
- })
-}
-
-const testPackageWithRequire = `
--- go.mod --
-module mod.com
-
-go 1.12
-
-require foo.test v1.2.3
--- go.sum --
-foo.test v1.2.3 h1:TMA+lyd1ck0TqjSFpNe4T6cf/K6TYkoHwOOcMBMjaEw=
-foo.test v1.2.3/go.mod h1:Ij3kyLIe5lzjycjh13NL8I2gX0quZuTdW0MnmlwGBL4=
--- print.go --
-package lib
-
-import (
- "fmt"
-
- "foo.test/bar"
-)
-
-func PrintAnswer() {
- fmt.Printf("answer: %s", bar.Answer)
-}
-`
-
-const testPackageWithRequireProxy = `
--- foo.test@v1.2.3/go.mod --
-module foo.test
-
-go 1.12
--- foo.test@v1.2.3/bar/const.go --
-package bar
-
-const Answer = 42
-`
-
-func TestResolveDiagnosticWithDownload(t *testing.T) {
- WithOptions(
- ProxyFiles(testPackageWithRequireProxy),
- ).Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
- env.OpenFile("print.go")
- // Check that gopackages correctly loaded this dependency. We should get a
- // diagnostic for the wrong formatting type.
- // TODO: we should be able to easily also match the diagnostic message.
- env.Await(env.DiagnosticAtRegexp("print.go", "fmt.Printf"))
- })
-}
-
-func TestMissingDependency(t *testing.T) {
- Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
- env.OpenFile("print.go")
- env.Await(LogMatching(protocol.Error, "initial workspace load failed", 1, false))
- })
-}
-
-// Tests golang/go#36951.
-func TestAdHocPackages_Issue36951(t *testing.T) {
- const adHoc = `
--- b/b.go --
-package b
-
-func Hello() {
- var x int
-}
-`
- Run(t, adHoc, func(t *testing.T, env *Env) {
- env.OpenFile("b/b.go")
- env.Await(env.DiagnosticAtRegexp("b/b.go", "x"))
- })
-}
-
-// Tests golang/go#37984: GOPATH should be read from the go command.
-func TestNoGOPATH_Issue37984(t *testing.T) {
- const files = `
--- main.go --
-package main
-
-func _() {
- fmt.Println("Hello World")
-}
-`
- WithOptions(
- EditorConfig{
- Env: map[string]string{
- "GOPATH": "",
- "GO111MODULE": "off",
- },
- }).Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.Await(env.DiagnosticAtRegexp("main.go", "fmt"))
- env.SaveBuffer("main.go")
- env.Await(EmptyDiagnostics("main.go"))
- })
-}
-
-// Tests golang/go#38669.
-func TestEqualInEnv_Issue38669(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-var _ = x.X
--- x/x.go --
-package x
-
-var X = 0
-`
- editorConfig := EditorConfig{Env: map[string]string{"GOFLAGS": "-tags=foo"}}
- WithOptions(editorConfig).Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.OrganizeImports("main.go")
- env.Await(EmptyDiagnostics("main.go"))
- })
-}
-
-// Tests golang/go#38467.
-func TestNoSuggestedFixesForGeneratedFiles_Issue38467(t *testing.T) {
- const generated = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-// Code generated by generator.go. DO NOT EDIT.
-
-func _() {
- for i, _ := range []string{} {
- _ = i
- }
-}
-`
- Run(t, generated, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- DiagnosticAt("main.go", 5, 8),
- ReadDiagnostics("main.go", &d),
- ),
- )
- if fixes := env.GetQuickFixes("main.go", d.Diagnostics); len(fixes) != 0 {
- t.Errorf("got quick fixes %v, wanted none", fixes)
- }
- })
-}
-
-// Expect a module/GOPATH error if there is an error in the file at startup.
-// Tests golang/go#37279.
-func TestShowCriticalError_Issue37279(t *testing.T) {
- const noModule = `
--- a.go --
-package foo
-
-import "mod.com/hello"
-
-func f() {
- hello.Goodbye()
-}
-`
- Run(t, noModule, func(t *testing.T, env *Env) {
- env.OpenFile("a.go")
- env.Await(
- OutstandingWork(lsp.WorkspaceLoadFailure, "outside of a module"),
- )
- env.RegexpReplace("a.go", `import "mod.com/hello"`, "")
- env.Await(
- NoOutstandingWork(),
- )
- })
-}
-
-func TestNonGoFolder(t *testing.T) {
- const files = `
--- hello.txt --
-hi mom
-`
- for _, go111module := range []string{"on", "off", ""} {
- t.Run(fmt.Sprintf("GO111MODULE_%v", go111module), func(t *testing.T) {
- WithOptions(EditorConfig{
- Env: map[string]string{"GO111MODULE": go111module},
- }).Run(t, files, func(t *testing.T, env *Env) {
- env.Await(
- NoOutstandingWork(),
- )
- })
- })
- }
-}
-
-// Tests the repro case from golang/go#38602. Diagnostics are now handled properly,
-// which blocks type checking.
-func TestConflictingMainPackageErrors(t *testing.T) {
- const collision = `
--- x/x.go --
-package x
-
-import "x/hello"
-
-func Hello() {
- hello.HiThere()
-}
--- x/main.go --
-package main
-
-func main() {
- fmt.Println("")
-}
-`
- WithOptions(
- InGOPATH(),
- EditorConfig{
- Env: map[string]string{
- "GO111MODULE": "off",
- },
- },
- ).Run(t, collision, func(t *testing.T, env *Env) {
- env.OpenFile("x/x.go")
- env.Await(
- env.DiagnosticAtRegexpWithMessage("x/x.go", `^`, "found packages main (main.go) and x (x.go)"),
- env.DiagnosticAtRegexpWithMessage("x/main.go", `^`, "found packages main (main.go) and x (x.go)"),
- )
-
- // We don't recover cleanly from the errors without good overlay support.
- if testenv.Go1Point() >= 16 {
- env.RegexpReplace("x/x.go", `package x`, `package main`)
- env.Await(OnceMet(
- env.DoneWithChange(),
- env.DiagnosticAtRegexpWithMessage("x/main.go", `fmt`, "undeclared name")))
- }
- })
-}
-
-const ardanLabsProxy = `
--- github.com/ardanlabs/conf@v1.2.3/go.mod --
-module github.com/ardanlabs/conf
-
-go 1.12
--- github.com/ardanlabs/conf@v1.2.3/conf.go --
-package conf
-
-var ErrHelpWanted error
-`
-
-// Test for golang/go#38211.
-func Test_Issue38211(t *testing.T) {
- t.Skipf("Skipping flaky test: https://golang.org/issue/44098")
-
- testenv.NeedsGo1Point(t, 14)
- const ardanLabs = `
--- go.mod --
-module mod.com
-
-go 1.14
--- main.go --
-package main
-
-import "github.com/ardanlabs/conf"
-
-func main() {
- _ = conf.ErrHelpWanted
-}
-`
- WithOptions(
- ProxyFiles(ardanLabsProxy),
- ).Run(t, ardanLabs, func(t *testing.T, env *Env) {
- // Expect a diagnostic with a suggested fix to add
- // "github.com/ardanlabs/conf" to the go.mod file.
- env.OpenFile("go.mod")
- env.OpenFile("main.go")
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
- ReadDiagnostics("main.go", &d),
- ),
- )
- env.ApplyQuickFixes("main.go", d.Diagnostics)
- env.SaveBuffer("go.mod")
- env.Await(
- EmptyDiagnostics("main.go"),
- )
- // Comment out the line that depends on conf and expect a
- // diagnostic and a fix to remove the import.
- env.RegexpReplace("main.go", "_ = conf.ErrHelpWanted", "//_ = conf.ErrHelpWanted")
- env.Await(
- env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
- )
- env.SaveBuffer("main.go")
- // Expect a diagnostic and fix to remove the dependency in the go.mod.
- env.Await(EmptyDiagnostics("main.go"))
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("go.mod", "require github.com/ardanlabs/conf", "not used in this module"),
- ReadDiagnostics("go.mod", &d),
- ),
- )
- env.ApplyQuickFixes("go.mod", d.Diagnostics)
- env.SaveBuffer("go.mod")
- env.Await(
- EmptyDiagnostics("go.mod"),
- )
- // Uncomment the lines and expect a new diagnostic for the import.
- env.RegexpReplace("main.go", "//_ = conf.ErrHelpWanted", "_ = conf.ErrHelpWanted")
- env.SaveBuffer("main.go")
- env.Await(
- env.DiagnosticAtRegexp("main.go", `"github.com/ardanlabs/conf"`),
- )
- })
-}
-
-// Test for golang/go#38207.
-func TestNewModule_Issue38207(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
- const emptyFile = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-`
- WithOptions(
- ProxyFiles(ardanLabsProxy),
- ).Run(t, emptyFile, func(t *testing.T, env *Env) {
- env.CreateBuffer("main.go", `package main
-
-import "github.com/ardanlabs/conf"
-
-func main() {
- _ = conf.ErrHelpWanted
-}
-`)
- env.SaveBuffer("main.go")
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("main.go", `"github.com/ardanlabs/conf"`, "no required module"),
- ReadDiagnostics("main.go", &d),
- ),
- )
- env.ApplyQuickFixes("main.go", d.Diagnostics)
- env.Await(
- EmptyDiagnostics("main.go"),
- )
- })
-}
-
-// Test for golang/go#36960.
-func TestNewFileBadImports_Issue36960(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
- const simplePackage = `
--- go.mod --
-module mod.com
-
-go 1.14
--- a/a1.go --
-package a
-
-import "fmt"
-
-func _() {
- fmt.Println("hi")
-}
-`
- Run(t, simplePackage, func(t *testing.T, env *Env) {
- env.OpenFile("a/a1.go")
- env.CreateBuffer("a/a2.go", ``)
- env.SaveBufferWithoutActions("a/a2.go")
- env.Await(
- OnceMet(
- env.DoneWithSave(),
- NoDiagnostics("a/a1.go"),
- ),
- )
- env.EditBuffer("a/a2.go", fake.NewEdit(0, 0, 0, 0, `package a`))
- env.Await(
- OnceMet(env.DoneWithChange(), NoDiagnostics("a/a1.go")),
- )
- })
-}
-
-// This test tries to replicate the workflow of a user creating a new x test.
-// It also tests golang/go#39315.
-func TestManuallyCreatingXTest(t *testing.T) {
- // Only for 1.15 because of golang/go#37971.
- testenv.NeedsGo1Point(t, 15)
-
- // Create a package that already has a test variant (in-package test).
- const testVariant = `
--- go.mod --
-module mod.com
-
-go 1.15
--- hello/hello.go --
-package hello
-
-func Hello() {
- var x int
-}
--- hello/hello_test.go --
-package hello
-
-import "testing"
-
-func TestHello(t *testing.T) {
- var x int
- Hello()
-}
-`
- Run(t, testVariant, func(t *testing.T, env *Env) {
- // Open the file, triggering the workspace load.
- // There are errors in the code to ensure all is working as expected.
- env.OpenFile("hello/hello.go")
- env.Await(
- env.DiagnosticAtRegexp("hello/hello.go", "x"),
- env.DiagnosticAtRegexp("hello/hello_test.go", "x"),
- )
-
- // Create an empty file with the intention of making it an x test.
- // This resembles a typical flow in an editor like VS Code, in which
- // a user would create an empty file and add content, saving
- // intermittently.
- // TODO(rstambler): There might be more edge cases here, as file
- // content can be added incrementally.
- env.CreateBuffer("hello/hello_x_test.go", ``)
-
- // Save the empty file (no actions since formatting will fail).
- env.SaveBufferWithoutActions("hello/hello_x_test.go")
-
- // Add the content. The missing import is for the package under test.
- env.EditBuffer("hello/hello_x_test.go", fake.NewEdit(0, 0, 0, 0, `package hello_test
-
-import (
- "testing"
-)
-
-func TestHello(t *testing.T) {
- hello.Hello()
-}
-`))
- // Expect a diagnostic for the missing import. Save, which should
- // trigger import organization. The diagnostic should clear.
- env.Await(
- env.DiagnosticAtRegexp("hello/hello_x_test.go", "hello.Hello"),
- )
- env.SaveBuffer("hello/hello_x_test.go")
- env.Await(
- EmptyDiagnostics("hello/hello_x_test.go"),
- )
- })
-}
-
-// Reproduce golang/go#40690.
-func TestCreateOnlyXTest(t *testing.T) {
- testenv.NeedsGo1Point(t, 13)
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- foo/foo.go --
-package foo
--- foo/bar_test.go --
-`
- Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("foo/bar_test.go")
- env.EditBuffer("foo/bar_test.go", fake.NewEdit(0, 0, 0, 0, "package foo"))
- env.Await(env.DoneWithChange())
- env.RegexpReplace("foo/bar_test.go", "package foo", `package foo_test
-
-import "testing"
-
-func TestX(t *testing.T) {
- var x int
-}
-`)
- env.Await(
- env.DiagnosticAtRegexp("foo/bar_test.go", "x"),
- )
- })
-}
-
-func TestChangePackageName(t *testing.T) {
- t.Skip("This issue hasn't been fixed yet. See golang.org/issue/41061.")
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- foo/foo.go --
-package foo
--- foo/bar_test.go --
-package foo_
-`
- Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("foo/bar_test.go")
- env.RegexpReplace("foo/bar_test.go", "package foo_", "package foo_test")
- env.SaveBuffer("foo/bar_test.go")
- env.Await(
- OnceMet(
- env.DoneWithSave(),
- NoDiagnostics("foo/bar_test.go"),
- ),
- OnceMet(
- env.DoneWithSave(),
- NoDiagnostics("foo/foo.go"),
- ),
- )
- })
-}
-
-func TestIgnoredFiles(t *testing.T) {
- const ws = `
--- go.mod --
-module mod.com
-
-go 1.12
--- _foo/x.go --
-package x
-
-var _ = foo.Bar
-`
- Run(t, ws, func(t *testing.T, env *Env) {
- env.OpenFile("_foo/x.go")
- env.Await(
- OnceMet(
- env.DoneWithOpen(),
- NoDiagnostics("_foo/x.go"),
- ))
- })
-}
-
-// Partially reproduces golang/go#38977, moving a file between packages.
-// It also gets hit by some go command bug fixed in 1.15, but we don't
-// care about that so much here.
-func TestDeletePackage(t *testing.T) {
- const ws = `
--- go.mod --
-module mod.com
-
-go 1.15
--- a/a.go --
-package a
-
-const A = 1
-
--- b/b.go --
-package b
-
-import "mod.com/a"
-
-const B = a.A
-
--- c/c.go --
-package c
-
-import "mod.com/a"
-
-const C = a.A
-`
- Run(t, ws, func(t *testing.T, env *Env) {
- env.OpenFile("b/b.go")
- env.Await(env.DoneWithOpen())
- // Delete c/c.go, the only file in package c.
- env.RemoveWorkspaceFile("c/c.go")
-
- // We should still get diagnostics for files that exist.
- env.RegexpReplace("b/b.go", `a.A`, "a.Nonexistant")
- env.Await(env.DiagnosticAtRegexp("b/b.go", `Nonexistant`))
- })
-}
-
-// This is a copy of the scenario_default/quickfix_empty_files.txt test from
-// govim. Reproduces golang/go#39646.
-func TestQuickFixEmptyFiles(t *testing.T) {
- t.Skip("too flaky: golang/go#48773")
-
- testenv.NeedsGo1Point(t, 15)
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
-`
- // To fully recreate the govim tests, we create files by inserting
- // a newline, adding to the file, and then deleting the newline.
- // Wait for each event to process to avoid cancellations and force
- // package loads.
- writeGoVim := func(env *Env, name, content string) {
- env.WriteWorkspaceFile(name, "")
- env.Await(env.DoneWithChangeWatchedFiles())
-
- env.CreateBuffer(name, "\n")
- env.Await(env.DoneWithOpen())
-
- env.EditBuffer(name, fake.NewEdit(1, 0, 1, 0, content))
- env.Await(env.DoneWithChange())
-
- env.EditBuffer(name, fake.NewEdit(0, 0, 1, 0, ""))
- env.Await(env.DoneWithChange())
- }
-
- const p = `package p; func DoIt(s string) {};`
- const main = `package main
-
-import "mod.com/p"
-
-func main() {
- p.DoIt(5)
-}
-`
- // A simple version of the test that reproduces most of the problems it
- // exposes.
- t.Run("short", func(t *testing.T) {
- Run(t, mod, func(t *testing.T, env *Env) {
- writeGoVim(env, "p/p.go", p)
- writeGoVim(env, "main.go", main)
- env.Await(env.DiagnosticAtRegexp("main.go", "5"))
- })
- })
-
- // A full version that replicates the whole flow of the test.
- t.Run("full", func(t *testing.T) {
- Run(t, mod, func(t *testing.T, env *Env) {
- writeGoVim(env, "p/p.go", p)
- writeGoVim(env, "main.go", main)
- writeGoVim(env, "p/p_test.go", `package p
-
-import "testing"
-
-func TestDoIt(t *testing.T) {
- DoIt(5)
-}
-`)
- writeGoVim(env, "p/x_test.go", `package p_test
-
-import (
- "testing"
-
- "mod.com/p"
-)
-
-func TestDoIt(t *testing.T) {
- p.DoIt(5)
-}
-`)
- env.Await(
- env.DiagnosticAtRegexp("main.go", "5"),
- env.DiagnosticAtRegexp("p/p_test.go", "5"),
- env.DiagnosticAtRegexp("p/x_test.go", "5"),
- )
- env.RegexpReplace("p/p.go", "s string", "i int")
- env.Await(
- EmptyDiagnostics("main.go"),
- EmptyDiagnostics("p/p_test.go"),
- EmptyDiagnostics("p/x_test.go"),
- )
- })
- })
-}
-
-func TestSingleFile(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.13
--- a/a.go --
-package a
-
-func _() {
- var x int
-}
-`
- WithOptions(
- // Empty workspace folders.
- WorkspaceFolders(),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "x"),
- )
- })
-}
-
-// Reproduces the case described in
-// https://github.com/golang/go/issues/39296#issuecomment-652058883.
-func TestPkgm(t *testing.T) {
- const basic = `
--- go.mod --
-module mod.com
-
-go 1.15
--- foo/foo.go --
-package foo
-
-import "fmt"
-
-func Foo() {
- fmt.Println("")
-}
-`
- Run(t, basic, func(t *testing.T, env *Env) {
- testenv.NeedsGo1Point(t, 16) // We can't recover cleanly from this case without good overlay support.
-
- env.WriteWorkspaceFile("foo/foo_test.go", `package main
-
-func main() {
-
-}`)
- env.OpenFile("foo/foo_test.go")
- env.RegexpReplace("foo/foo_test.go", `package main`, `package foo`)
- env.Await(
- OnceMet(
- env.DoneWithChange(),
- NoDiagnostics("foo/foo.go"),
- ),
- )
- })
-}
-
-func TestClosingBuffer(t *testing.T) {
- const basic = `
--- go.mod --
-module mod.com
-
-go 1.14
--- main.go --
-package main
-
-func main() {}
-`
- Run(t, basic, func(t *testing.T, env *Env) {
- env.Editor.CreateBuffer(env.Ctx, "foo.go", `package main`)
- env.Await(
- env.DoneWithOpen(),
- )
- env.CloseBuffer("foo.go")
- env.Await(
- OnceMet(
- env.DoneWithClose(),
- NoLogMatching(protocol.Info, "packages=0"),
- ),
- )
- })
-}
-
-// Reproduces golang/go#38424.
-func TestCutAndPaste(t *testing.T) {
- const basic = `
--- go.mod --
-module mod.com
-
-go 1.14
--- main2.go --
-package main
-`
- Run(t, basic, func(t *testing.T, env *Env) {
- env.CreateBuffer("main.go", "")
- env.Await(env.DoneWithOpen())
-
- env.SaveBufferWithoutActions("main.go")
- env.Await(env.DoneWithSave(), env.DoneWithChangeWatchedFiles())
-
- env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main
-
-func main() {
-}
-`))
- env.Await(env.DoneWithChange())
-
- env.SaveBuffer("main.go")
- env.Await(env.DoneWithSave(), env.DoneWithChangeWatchedFiles())
-
- env.EditBuffer("main.go", fake.NewEdit(0, 0, 4, 0, ""))
- env.Await(env.DoneWithChange())
-
- env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main
-
-func main() {
- var x int
-}
-`))
- env.Await(
- env.DiagnosticAtRegexp("main.go", "x"),
- )
- })
-}
-
-// Reproduces golang/go#39763.
-func TestInvalidPackageName(t *testing.T) {
- testenv.NeedsGo1Point(t, 15)
-
- const pkgDefault = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package default
-
-func main() {}
-`
- Run(t, pkgDefault, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.Await(
- env.DiagnosticAtRegexpWithMessage("main.go", "default", "expected 'IDENT'"),
- )
- })
-}
-
-// This tests the functionality of the "limitWorkspaceScope"
-func TestLimitWorkspaceScope(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- a/main.go --
-package main
-
-func main() {}
--- main.go --
-package main
-
-func main() {
- var x int
-}
-`
- WithOptions(
- WorkspaceFolders("a"),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("a/main.go")
- env.Await(
- env.DiagnosticAtRegexp("main.go", "x"),
- )
- })
- WithOptions(
- WorkspaceFolders("a"),
- LimitWorkspaceScope(),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("a/main.go")
- env.Await(
- NoDiagnostics("main.go"),
- )
- })
-}
-
-func TestSimplifyCompositeLitDiagnostic(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import "fmt"
-
-type t struct {
- msg string
-}
-
-func main() {
- x := []t{t{"msg"}}
- fmt.Println(x)
-}
-`
-
- WithOptions(
- EditorConfig{
- Settings: map[string]interface{}{
- "staticcheck": true,
- },
- },
- ).Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- var d protocol.PublishDiagnosticsParams
- env.Await(OnceMet(
- env.DiagnosticAtRegexpWithMessage("main.go", `t{"msg"}`, "redundant type"),
- ReadDiagnostics("main.go", &d),
- ))
- if tags := d.Diagnostics[0].Tags; len(tags) == 0 || tags[0] != protocol.Unnecessary {
- t.Errorf("wanted Unnecessary tag on diagnostic, got %v", tags)
- }
- env.ApplyQuickFixes("main.go", d.Diagnostics)
- env.Await(EmptyDiagnostics("main.go"))
- })
-}
-
-// Test some secondary diagnostics
-func TestSecondaryDiagnostics(t *testing.T) {
- const dir = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-func main() {
- panic("not here")
-}
--- other.go --
-package main
-func main() {}
-`
- Run(t, dir, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.OpenFile("other.go")
- x := env.DiagnosticsFor("main.go")
- if x == nil {
- t.Fatalf("expected 1 diagnostic, got none")
- }
- if len(x.Diagnostics) != 1 {
- t.Fatalf("main.go, got %d diagnostics, expected 1", len(x.Diagnostics))
- }
- keep := x.Diagnostics[0]
- y := env.DiagnosticsFor("other.go")
- if len(y.Diagnostics) != 1 {
- t.Fatalf("other.go: got %d diagnostics, expected 1", len(y.Diagnostics))
- }
- if len(y.Diagnostics[0].RelatedInformation) != 1 {
- t.Fatalf("got %d RelatedInformations, expected 1", len(y.Diagnostics[0].RelatedInformation))
- }
- // check that the RelatedInformation matches the error from main.go
- c := y.Diagnostics[0].RelatedInformation[0]
- if c.Location.Range != keep.Range {
- t.Errorf("locations don't match. Got %v expected %v", c.Location.Range, keep.Range)
- }
- })
-}
-
-func TestNotifyOrphanedFiles(t *testing.T) {
- // Need GO111MODULE=on for this test to work with Go 1.12.
- testenv.NeedsGo1Point(t, 13)
-
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- a/a.go --
-package a
-
-func main() {
- var x int
-}
--- a/a_ignore.go --
-// +build ignore
-
-package a
-
-func _() {
- var x int
-}
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "x"),
- )
- env.OpenFile("a/a_ignore.go")
- env.Await(
- DiagnosticAt("a/a_ignore.go", 2, 8),
- )
- })
-}
-
-func TestEnableAllExperiments(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import "bytes"
-
-func b(c bytes.Buffer) {
- _ = 1
-}
-`
- WithOptions(
- EditorConfig{
- AllExperiments: true,
- },
- ).Run(t, mod, func(t *testing.T, env *Env) {
- // Confirm that the setting doesn't cause any warnings.
- env.Await(NoShowMessage())
- })
-}
-
-func TestSwig(t *testing.T) {
- // This is fixed in Go 1.17, but not earlier.
- testenv.NeedsGo1Point(t, 17)
-
- if _, err := exec.LookPath("swig"); err != nil {
- t.Skip("skipping test: swig not available")
- }
- if _, err := exec.LookPath("g++"); err != nil {
- t.Skip("skipping test: g++ not available")
- }
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- pkg/simple/export_swig.go --
-package simple
-
-func ExportSimple(x, y int) int {
- return Gcd(x, y)
-}
--- pkg/simple/simple.swigcxx --
-%module simple
-
-%inline %{
-extern int gcd(int x, int y)
-{
- int g;
- g = y;
- while (x > 0) {
- g = x;
- x = y % x;
- y = g;
- }
- return g;
-}
-%}
--- main.go --
-package a
-
-func main() {
- var x int
-}
-`
- Run(t, mod, func(t *testing.T, env *Env) {
- env.Await(
- OnceMet(
- InitialWorkspaceLoad,
- NoDiagnosticWithMessage("", "illegal character U+0023 '#'"),
- ),
- )
- })
-}
-
-// When foo_test.go is opened, gopls will object to the borked package name.
-// This test asserts that when the package name is fixed, gopls will soon after
-// have no more complaints about it.
-// https://github.com/golang/go/issues/41061
-func TestRenamePackage(t *testing.T) {
- testenv.NeedsGo1Point(t, 16)
-
- const proxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.3/blah/blah.go --
-package blah
-
-const Name = "Blah"
--- random.org@v1.2.3/go.mod --
-module random.org
-
-go 1.12
--- random.org@v1.2.3/blah/blah.go --
-package hello
-
-const Name = "Hello"
-`
-
- const contents = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import "example.com/blah"
-
-func main() {
- blah.Hello()
-}
--- bob.go --
-package main
--- foo/foo.go --
-package foo
--- foo/foo_test.go --
-package foo_
-`
-
- WithOptions(
- ProxyFiles(proxy),
- InGOPATH(),
- EditorConfig{
- Env: map[string]string{
- "GO111MODULE": "off",
- },
- },
- ).Run(t, contents, func(t *testing.T, env *Env) {
- // Simulate typing character by character.
- env.OpenFile("foo/foo_test.go")
- env.Await(env.DoneWithOpen())
- env.RegexpReplace("foo/foo_test.go", "_", "_t")
- env.Await(env.DoneWithChange())
- env.RegexpReplace("foo/foo_test.go", "_t", "_test")
- env.Await(env.DoneWithChange())
-
- env.Await(
- EmptyDiagnostics("foo/foo_test.go"),
- NoOutstandingWork(),
- )
- })
-}
-
-// TestProgressBarErrors confirms that critical workspace load errors are shown
-// and updated via progress reports.
-func TestProgressBarErrors(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const pkg = `
--- go.mod --
-modul mod.com
-
-go 1.12
--- main.go --
-package main
-`
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.OpenFile("go.mod")
- env.Await(
- OutstandingWork(lsp.WorkspaceLoadFailure, "unknown directive"),
- )
- env.EditBuffer("go.mod", fake.NewEdit(0, 0, 3, 0, `module mod.com
-
-go 1.hello
-`))
- // As of golang/go#42529, go.mod changes do not reload the workspace until
- // they are saved.
- env.SaveBufferWithoutActions("go.mod")
- env.Await(
- OutstandingWork(lsp.WorkspaceLoadFailure, "invalid go version"),
- )
- env.RegexpReplace("go.mod", "go 1.hello", "go 1.12")
- env.SaveBufferWithoutActions("go.mod")
- env.Await(
- NoOutstandingWork(),
- )
- })
-}
-
-func TestDeleteDirectory(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const mod = `
--- bob/bob.go --
-package bob
-
-func Hello() {
- var x int
-}
--- go.mod --
-module mod.com
--- main.go --
-package main
-
-import "mod.com/bob"
-
-func main() {
- bob.Hello()
-}
-`
- Run(t, mod, func(t *testing.T, env *Env) {
- env.RemoveWorkspaceFile("bob")
- env.Await(
- env.DiagnosticAtRegexp("main.go", `"mod.com/bob"`),
- EmptyDiagnostics("bob/bob.go"),
- RegistrationMatching("didChangeWatchedFiles"),
- )
- })
-}
-
-// Confirms that circular imports are tested and reported.
-func TestCircularImports(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- self/self.go --
-package self
-
-import _ "mod.com/self"
-func Hello() {}
--- double/a/a.go --
-package a
-
-import _ "mod.com/double/b"
--- double/b/b.go --
-package b
-
-import _ "mod.com/double/a"
--- triple/a/a.go --
-package a
-
-import _ "mod.com/triple/b"
--- triple/b/b.go --
-package b
-
-import _ "mod.com/triple/c"
--- triple/c/c.go --
-package c
-
-import _ "mod.com/triple/a"
-`
- Run(t, mod, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexpWithMessage("self/self.go", `_ "mod.com/self"`, "import cycle not allowed"),
- env.DiagnosticAtRegexpWithMessage("double/a/a.go", `_ "mod.com/double/b"`, "import cycle not allowed"),
- env.DiagnosticAtRegexpWithMessage("triple/a/a.go", `_ "mod.com/triple/b"`, "import cycle not allowed"),
- )
- })
-}
-
-// Tests golang/go#46667: deleting a problematic import path should resolve
-// import cycle errors.
-func TestResolveImportCycle(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.test
-
-go 1.16
--- a/a.go --
-package a
-
-import "mod.test/b"
-
-const A = b.A
-const B = 2
--- b/b.go --
-package b
-
-import "mod.test/a"
-
-const A = 1
-const B = a.B
- `
- Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.OpenFile("b/b.go")
- env.Await(
- OnceMet(
- env.DoneWithOpen(),
- // The Go command sometimes tells us about only one of the import cycle
- // errors below. For robustness of this test, succeed if we get either.
- //
- // TODO(golang/go#52904): we should get *both* of these errors.
- AnyOf(
- env.DiagnosticAtRegexpWithMessage("a/a.go", `"mod.test/b"`, "import cycle"),
- env.DiagnosticAtRegexpWithMessage("b/b.go", `"mod.test/a"`, "import cycle"),
- ),
- ),
- )
- env.RegexpReplace("b/b.go", `const B = a\.B`, "")
- env.SaveBuffer("b/b.go")
- env.Await(
- EmptyOrNoDiagnostics("a/a.go"),
- EmptyOrNoDiagnostics("b/b.go"),
- )
- })
-}
-
-func TestBadImport(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import (
- _ "nosuchpkg"
-)
-`
- t.Run("module", func(t *testing.T) {
- Run(t, mod, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexpWithMessage("main.go", `"nosuchpkg"`, `could not import nosuchpkg (no required module provides package "nosuchpkg"`),
- )
- })
- })
- t.Run("GOPATH", func(t *testing.T) {
- WithOptions(
- InGOPATH(),
- EditorConfig{
- Env: map[string]string{"GO111MODULE": "off"},
- },
- Modes(Singleton),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexpWithMessage("main.go", `"nosuchpkg"`, `cannot find package "nosuchpkg" in any of`),
- )
- })
- })
-}
-
-func TestMultipleModules_Warning(t *testing.T) {
- const modules = `
--- a/go.mod --
-module a.com
-
-go 1.12
--- a/a.go --
-package a
--- b/go.mod --
-module b.com
-
-go 1.12
--- b/b.go --
-package b
-`
- for _, go111module := range []string{"on", "auto"} {
- t.Run("GO111MODULE="+go111module, func(t *testing.T) {
- WithOptions(
- Modes(Singleton),
- EditorConfig{
- Env: map[string]string{
- "GO111MODULE": go111module,
- },
- },
- ).Run(t, modules, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.OpenFile("b/go.mod")
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "package a"),
- env.DiagnosticAtRegexp("b/go.mod", "module b.com"),
- OutstandingWork(lsp.WorkspaceLoadFailure, "gopls requires a module at the root of your workspace."),
- )
- })
- })
- }
-
- // Expect no warning if GO111MODULE=auto in a directory in GOPATH.
- t.Run("GOPATH_GO111MODULE_auto", func(t *testing.T) {
- WithOptions(
- Modes(Singleton),
- EditorConfig{
- Env: map[string]string{
- "GO111MODULE": "auto",
- },
- },
- InGOPATH(),
- ).Run(t, modules, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.Await(
- OnceMet(
- env.DoneWithOpen(),
- NoDiagnostics("a/a.go"),
- ),
- NoOutstandingWork(),
- )
- })
- })
-}
-
-func TestNestedModules(t *testing.T) {
- const proxy = `
--- nested.com@v1.0.0/go.mod --
-module nested.com
-
-go 1.12
--- nested.com@v1.0.0/hello/hello.go --
-package hello
-
-func Hello() {}
-`
-
- const nested = `
--- go.mod --
-module mod.com
-
-go 1.12
-
-require nested.com v1.0.0
--- go.sum --
-nested.com v1.0.0 h1:I6spLE4CgFqMdBPc+wTV2asDO2QJ3tU0YAT+jkLeN1I=
-nested.com v1.0.0/go.mod h1:ly53UzXQgVjSlV7wicdBB4p8BxfytuGT1Xcyv0ReJfI=
--- main.go --
-package main
-
-import "nested.com/hello"
-
-func main() {
- hello.Hello()
-}
--- nested/go.mod --
-module nested.com
-
--- nested/hello/hello.go --
-package hello
-
-func Hello() {
- helloHelper()
-}
--- nested/hello/hello_helper.go --
-package hello
-
-func helloHelper() {}
-`
- WithOptions(
- ProxyFiles(proxy),
- Modes(Singleton),
- ).Run(t, nested, func(t *testing.T, env *Env) {
- // Expect a diagnostic in a nested module.
- env.OpenFile("nested/hello/hello.go")
- didOpen := env.DoneWithOpen()
- env.Await(
- OnceMet(
- didOpen,
- env.DiagnosticAtRegexp("nested/hello/hello.go", "helloHelper"),
- ),
- OnceMet(
- didOpen,
- env.DiagnosticAtRegexpWithMessage("nested/hello/hello.go", "package hello", "nested module"),
- ),
- OnceMet(
- didOpen,
- OutstandingWork(lsp.WorkspaceLoadFailure, "nested module"),
- ),
- )
- })
-}
-
-func TestAdHocPackagesReloading(t *testing.T) {
- const nomod = `
--- main.go --
-package main
-
-func main() {}
-`
- Run(t, nomod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.RegexpReplace("main.go", "{}", "{ var x int; }") // simulate typing
- env.Await(
- OnceMet(
- env.DoneWithChange(),
- NoLogMatching(protocol.Info, "packages=1"),
- ),
- )
- })
-}
-
-func TestBuildTagChange(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- foo.go --
-// decoy comment
-// +build hidden
-// decoy comment
-
-package foo
-var Foo = 1
--- bar.go --
-package foo
-var Bar = Foo
-`
-
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("foo.go")
- env.Await(env.DiagnosticAtRegexpWithMessage("bar.go", `Foo`, "undeclared name"))
- env.RegexpReplace("foo.go", `\+build`, "")
- env.Await(EmptyDiagnostics("bar.go"))
- })
-
-}
-
-func TestIssue44736(t *testing.T) {
- const files = `
- -- go.mod --
-module blah.com
-
-go 1.16
--- main.go --
-package main
-
-import "fmt"
-
-func main() {
- asdf
- fmt.Printf("This is a test %v")
- fdas
-}
--- other.go --
-package main
-
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.OpenFile("other.go")
- env.Await(
- env.DiagnosticAtRegexpWithMessage("main.go", "asdf", "undeclared name"),
- env.DiagnosticAtRegexpWithMessage("main.go", "fdas", "undeclared name"),
- )
- env.SetBufferContent("other.go", "package main\n\nasdf")
- // The new diagnostic in other.go should not suppress diagnostics in main.go.
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("other.go", "asdf", "expected declaration"),
- env.DiagnosticAtRegexpWithMessage("main.go", "asdf", "undeclared name"),
- ),
- )
- })
-}
-
-func TestInitialization(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.16
--- main.go --
-package main
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("go.mod")
- env.Await(env.DoneWithOpen())
- env.RegexpReplace("go.mod", "module", "modul")
- env.SaveBufferWithoutActions("go.mod")
- env.Await(
- OnceMet(
- env.DoneWithSave(),
- NoLogMatching(protocol.Error, "initial workspace load failed"),
- ),
- )
- })
-}
-
-// Tests golang/go#45075: A panic in fillreturns broke diagnostics.
-// Expect an error log indicating that fillreturns panicked, as well type
-// errors for the broken code.
-func TestFillReturnsPanic(t *testing.T) {
- // At tip, the panic no longer reproduces.
- testenv.SkipAfterGo1Point(t, 16)
-
- const files = `
--- go.mod --
-module mod.com
-
-go 1.15
--- main.go --
-package main
-
-func foo() int {
- return x, nil
-}
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.Await(
- OnceMet(
- env.DoneWithOpen(),
- LogMatching(protocol.Error, `.*analysis fillreturns.*panicked.*`, 1, true),
- env.DiagnosticAtRegexpWithMessage("main.go", `return x`, "wrong number of return values"),
- ),
- )
- })
-}
-
-// This test confirms that the view does not reinitialize when a go.mod file is
-// opened.
-func TestNoReinitialize(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-func main() {}
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("go.mod")
- env.Await(
- OnceMet(
- env.DoneWithOpen(),
- LogMatching(protocol.Info, `.*query=\[builtin mod.com/...\].*`, 1, false),
- ),
- )
- })
-}
-
-func TestUseOfInvalidMetadata(t *testing.T) {
- testenv.NeedsGo1Point(t, 13)
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import (
- "mod.com/a"
- //"os"
-)
-
-func _() {
- a.Hello()
- os.Getenv("")
- //var x int
-}
--- a/a.go --
-package a
-
-func Hello() {}
-`
- WithOptions(
- EditorConfig{
- ExperimentalUseInvalidMetadata: true,
- },
- Modes(Singleton),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("go.mod")
- env.RegexpReplace("go.mod", "module mod.com", "modul mod.com") // break the go.mod file
- env.SaveBufferWithoutActions("go.mod")
- env.Await(
- env.DiagnosticAtRegexp("go.mod", "modul"),
- )
- // Confirm that language features work with invalid metadata.
- env.OpenFile("main.go")
- file, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", "Hello"))
- wantPos := env.RegexpSearch("a/a.go", "Hello")
- if file != "a/a.go" && pos != wantPos {
- t.Fatalf("expected a/a.go:%s, got %s:%s", wantPos, file, pos)
- }
- // Confirm that new diagnostics appear with invalid metadata by adding
- // an unused variable to the body of the function.
- env.RegexpReplace("main.go", "//var x int", "var x int")
- env.Await(
- env.DiagnosticAtRegexp("main.go", "x"),
- )
- // Add an import and confirm that we get a diagnostic for it, since the
- // metadata will not have been updated.
- env.RegexpReplace("main.go", "//\"os\"", "\"os\"")
- env.Await(
- env.DiagnosticAtRegexp("main.go", `"os"`),
- )
- // Fix the go.mod file and expect the diagnostic to resolve itself.
- env.RegexpReplace("go.mod", "modul mod.com", "module mod.com")
- env.SaveBuffer("go.mod")
- env.Await(
- env.DiagnosticAtRegexp("main.go", "x"),
- env.NoDiagnosticAtRegexp("main.go", `"os"`),
- EmptyDiagnostics("go.mod"),
- )
- })
-}
-
-func TestReloadInvalidMetadata(t *testing.T) {
- // We only use invalid metadata for Go versions > 1.12.
- testenv.NeedsGo1Point(t, 13)
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-func _() {}
-`
- WithOptions(
- EditorConfig{
- ExperimentalUseInvalidMetadata: true,
- },
- // ExperimentalWorkspaceModule has a different failure mode for this
- // case.
- Modes(Singleton),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.Await(
- OnceMet(
- InitialWorkspaceLoad,
- CompletedWork("Load", 1, false),
- ),
- )
-
- // Break the go.mod file on disk, expecting a reload.
- env.WriteWorkspaceFile("go.mod", `modul mod.com
-
-go 1.12
-`)
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- env.DiagnosticAtRegexp("go.mod", "modul"),
- CompletedWork("Load", 1, false),
- ),
- )
-
- env.OpenFile("main.go")
- env.Await(env.DoneWithOpen())
- // The first edit after the go.mod file invalidation should cause a reload.
- // Any subsequent simple edits should not.
- content := `package main
-
-func main() {
- _ = 1
-}
-`
- env.EditBuffer("main.go", fake.NewEdit(0, 0, 3, 0, content))
- env.Await(
- OnceMet(
- env.DoneWithChange(),
- CompletedWork("Load", 2, false),
- NoLogMatching(protocol.Error, "error loading file"),
- ),
- )
- env.RegexpReplace("main.go", "_ = 1", "_ = 2")
- env.Await(
- OnceMet(
- env.DoneWithChange(),
- CompletedWork("Load", 2, false),
- NoLogMatching(protocol.Error, "error loading file"),
- ),
- )
- // Add an import to the main.go file and confirm that it does get
- // reloaded, but the reload fails, so we see a diagnostic on the new
- // "fmt" import.
- env.EditBuffer("main.go", fake.NewEdit(0, 0, 5, 0, `package main
-
-import "fmt"
-
-func main() {
- fmt.Println("")
-}
-`))
- env.Await(
- OnceMet(
- env.DoneWithChange(),
- env.DiagnosticAtRegexp("main.go", `"fmt"`),
- CompletedWork("Load", 3, false),
- ),
- )
- })
-}
-
-func TestLangVersion(t *testing.T) {
- testenv.NeedsGo1Point(t, 18) // Requires types.Config.GoVersion, new in 1.18.
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-const C = 0b10
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.Await(env.DiagnosticAtRegexpWithMessage("main.go", `0b10`, "go1.13 or later"))
- env.WriteWorkspaceFile("go.mod", "module mod.com \n\ngo 1.13\n")
- env.Await(EmptyDiagnostics("main.go"))
- })
-}
-
-func TestNoQuickFixForUndeclaredConstraint(t *testing.T) {
- testenv.NeedsGo1Point(t, 18)
- const files = `
--- go.mod --
-module mod.com
-
-go 1.18
--- main.go --
-package main
-
-func F[T C](_ T) {
-}
-`
-
- Run(t, files, func(t *testing.T, env *Env) {
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("main.go", `C`, "undeclared name"),
- ReadDiagnostics("main.go", &d),
- ),
- )
- if fixes := env.GetQuickFixes("main.go", d.Diagnostics); len(fixes) != 0 {
- t.Errorf("got quick fixes %v, wanted none", fixes)
- }
- })
-}
-
-func TestEditGoDirective(t *testing.T) {
- testenv.NeedsGo1Point(t, 18)
- const files = `
--- go.mod --
-module mod.com
-
-go 1.16
--- main.go --
-package main
-
-func F[T any](_ T) {
-}
-`
- Run(t, files, func(_ *testing.T, env *Env) { // Create a new workspace-level directory and empty file.
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("main.go", `T any`, "type parameters require"),
- ReadDiagnostics("main.go", &d),
- ),
- )
-
- env.ApplyQuickFixes("main.go", d.Diagnostics)
-
- env.Await(
- EmptyDiagnostics("main.go"),
- )
- })
-}
-
-func TestEditGoDirectiveWorkspace(t *testing.T) {
- testenv.NeedsGo1Point(t, 18)
- const files = `
--- go.mod --
-module mod.com
-
-go 1.16
--- go.work --
-go 1.18
-
-use .
--- main.go --
-package main
-
-func F[T any](_ T) {
-}
-`
- Run(t, files, func(_ *testing.T, env *Env) { // Create a new workspace-level directory and empty file.
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("main.go", `T any`, "type parameters require"),
- ReadDiagnostics("main.go", &d),
- ),
- )
-
- env.ApplyQuickFixes("main.go", d.Diagnostics)
-
- env.Await(
- EmptyDiagnostics("main.go"),
- )
- })
-}
diff -urN a/gopls/internal/regtest/diagnostics/undeclared_test.go b/gopls/internal/regtest/diagnostics/undeclared_test.go
--- a/gopls/internal/regtest/diagnostics/undeclared_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/diagnostics/undeclared_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,67 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package diagnostics
-
-import (
- "testing"
-
- "golang.org/x/tools/internal/lsp/protocol"
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestUndeclaredDiagnostics(t *testing.T) {
- src := `
--- go.mod --
-module mod.com
-
-go 1.12
--- a/a.go --
-package a
-
-func _() int {
- return x
-}
--- b/b.go --
-package b
-
-func _() int {
- var y int
- y = y
- return y
-}
-`
- Run(t, src, func(t *testing.T, env *Env) {
- isUnnecessary := func(diag protocol.Diagnostic) bool {
- for _, tag := range diag.Tags {
- if tag == protocol.Unnecessary {
- return true
- }
- }
- return false
- }
-
- // 'x' is undeclared, but still necessary.
- env.OpenFile("a/a.go")
- env.Await(env.DiagnosticAtRegexp("a/a.go", "x"))
- diags := env.DiagnosticsFor("a/a.go")
- if got := len(diags.Diagnostics); got != 1 {
- t.Errorf("len(Diagnostics) = %d, want 1", got)
- }
- if diag := diags.Diagnostics[0]; isUnnecessary(diag) {
- t.Errorf("%v tagged unnecessary, want necessary", diag)
- }
-
- // 'y = y' is pointless, and should be detected as unnecessary.
- env.OpenFile("b/b.go")
- env.Await(env.DiagnosticAtRegexp("b/b.go", "y = y"))
- diags = env.DiagnosticsFor("b/b.go")
- if got := len(diags.Diagnostics); got != 1 {
- t.Errorf("len(Diagnostics) = %d, want 1", got)
- }
- if diag := diags.Diagnostics[0]; !isUnnecessary(diag) {
- t.Errorf("%v tagged necessary, want unnecessary", diag)
- }
- })
-}
diff -urN a/gopls/internal/regtest/misc/call_hierarchy_test.go b/gopls/internal/regtest/misc/call_hierarchy_test.go
--- a/gopls/internal/regtest/misc/call_hierarchy_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/call_hierarchy_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,35 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-package misc
-
-import (
- "testing"
-
- "golang.org/x/tools/internal/lsp/protocol"
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-// Test for golang/go#49125
-func TestCallHierarchy_Issue49125(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- p.go --
-package pkg
-`
- // TODO(rfindley): this could probably just be a marker test.
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("p.go")
- pos := env.RegexpSearch("p.go", "pkg")
-
- var params protocol.CallHierarchyPrepareParams
- params.TextDocument.URI = env.Sandbox.Workdir.URI("p.go")
- params.Position = pos.ToProtocolPosition()
-
- // Check that this doesn't panic.
- env.Editor.Server.PrepareCallHierarchy(env.Ctx, &params)
- })
-}
diff -urN a/gopls/internal/regtest/misc/configuration_test.go b/gopls/internal/regtest/misc/configuration_test.go
--- a/gopls/internal/regtest/misc/configuration_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/configuration_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,80 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/fake"
- "golang.org/x/tools/internal/testenv"
-)
-
-// Test that enabling and disabling produces the expected results of showing
-// and hiding staticcheck analysis results.
-func TestChangeConfiguration(t *testing.T) {
- // Staticcheck only supports Go versions >= 1.17.
- // Note: keep this in sync with TestStaticcheckWarning. Below this version we
- // should get an error when setting staticcheck configuration.
- testenv.NeedsGo1Point(t, 17)
-
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- a/a.go --
-package a
-
-import "errors"
-
-// FooErr should be called ErrFoo (ST1012)
-var FooErr = errors.New("foo")
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.Await(
- env.DoneWithOpen(),
- NoDiagnostics("a/a.go"),
- )
- cfg := &fake.EditorConfig{}
- *cfg = env.Editor.Config
- cfg.Settings = map[string]interface{}{
- "staticcheck": true,
- }
- env.ChangeConfiguration(t, cfg)
- env.Await(
- DiagnosticAt("a/a.go", 5, 4),
- )
- })
-}
-
-func TestStaticcheckWarning(t *testing.T) {
- // Note: keep this in sync with TestChangeConfiguration.
- testenv.SkipAfterGo1Point(t, 16)
-
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- a/a.go --
-package a
-
-import "errors"
-
-// FooErr should be called ErrFoo (ST1012)
-var FooErr = errors.New("foo")
-`
-
- WithOptions(EditorConfig{
- Settings: map[string]interface{}{
- "staticcheck": true,
- },
- }).Run(t, files, func(t *testing.T, env *Env) {
- env.Await(ShownMessage("staticcheck is not supported"))
- })
-}
diff -urN a/gopls/internal/regtest/misc/debugserver_test.go b/gopls/internal/regtest/misc/debugserver_test.go
--- a/gopls/internal/regtest/misc/debugserver_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/debugserver_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,46 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "net/http"
- "testing"
-
- "golang.org/x/tools/internal/lsp/command"
- "golang.org/x/tools/internal/lsp/protocol"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestStartDebugging(t *testing.T) {
- WithOptions(
- Modes(Forwarded),
- ).Run(t, "", func(t *testing.T, env *Env) {
- args, err := command.MarshalArgs(command.DebuggingArgs{})
- if err != nil {
- t.Fatal(err)
- }
- params := &protocol.ExecuteCommandParams{
- Command: command.StartDebugging.ID(),
- Arguments: args,
- }
- var result command.DebuggingResult
- env.ExecuteCommand(params, &result)
- if got, want := len(result.URLs), 2; got != want {
- t.Fatalf("got %d urls, want %d; urls: %#v", got, want, result.URLs)
- }
- for i, u := range result.URLs {
- resp, err := http.Get(u)
- if err != nil {
- t.Errorf("getting url #%d (%q): %v", i, u, err)
- continue
- }
- defer resp.Body.Close()
- if got, want := resp.StatusCode, http.StatusOK; got != want {
- t.Errorf("debug server #%d returned HTTP %d, want %d", i, got, want)
- }
- }
- })
-}
diff -urN a/gopls/internal/regtest/misc/definition_test.go b/gopls/internal/regtest/misc/definition_test.go
--- a/gopls/internal/regtest/misc/definition_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/definition_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,291 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "path"
- "strings"
- "testing"
-
- "golang.org/x/tools/internal/lsp/protocol"
- . "golang.org/x/tools/internal/lsp/regtest"
- "golang.org/x/tools/internal/testenv"
-
- "golang.org/x/tools/internal/lsp/fake"
- "golang.org/x/tools/internal/lsp/tests"
-)
-
-const internalDefinition = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import "fmt"
-
-func main() {
- fmt.Println(message)
-}
--- const.go --
-package main
-
-const message = "Hello World."
-`
-
-func TestGoToInternalDefinition(t *testing.T) {
- Run(t, internalDefinition, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- name, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", "message"))
- if want := "const.go"; name != want {
- t.Errorf("GoToDefinition: got file %q, want %q", name, want)
- }
- if want := env.RegexpSearch("const.go", "message"); pos != want {
- t.Errorf("GoToDefinition: got position %v, want %v", pos, want)
- }
- })
-}
-
-const stdlibDefinition = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import "fmt"
-
-func main() {
- fmt.Printf()
-}`
-
-func TestGoToStdlibDefinition_Issue37045(t *testing.T) {
- Run(t, stdlibDefinition, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- name, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `fmt.(Printf)`))
- if got, want := path.Base(name), "print.go"; got != want {
- t.Errorf("GoToDefinition: got file %q, want %q", name, want)
- }
-
- // Test that we can jump to definition from outside our workspace.
- // See golang.org/issues/37045.
- newName, newPos := env.GoToDefinition(name, pos)
- if newName != name {
- t.Errorf("GoToDefinition is not idempotent: got %q, want %q", newName, name)
- }
- if newPos != pos {
- t.Errorf("GoToDefinition is not idempotent: got %v, want %v", newPos, pos)
- }
- })
-}
-
-func TestUnexportedStdlib_Issue40809(t *testing.T) {
- Run(t, stdlibDefinition, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- name, _ := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `fmt.(Printf)`))
- env.OpenFile(name)
-
- pos := env.RegexpSearch(name, `:=\s*(newPrinter)\(\)`)
-
- // Check that we can find references on a reference
- refs := env.References(name, pos)
- if len(refs) < 5 {
- t.Errorf("expected 5+ references to newPrinter, found: %#v", refs)
- }
-
- name, pos = env.GoToDefinition(name, pos)
- content, _ := env.Hover(name, pos)
- if !strings.Contains(content.Value, "newPrinter") {
- t.Fatal("definition of newPrinter went to the incorrect place")
- }
- // And on the definition too.
- refs = env.References(name, pos)
- if len(refs) < 5 {
- t.Errorf("expected 5+ references to newPrinter, found: %#v", refs)
- }
- })
-}
-
-// Test the hover on an error's Error function.
-// This can't be done via the marker tests because Error is a builtin.
-func TestHoverOnError(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-func main() {
- var err error
- err.Error()
-}`
- Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- content, _ := env.Hover("main.go", env.RegexpSearch("main.go", "Error"))
- if content == nil {
- t.Fatalf("nil hover content for Error")
- }
- want := "```go\nfunc (error).Error() string\n```"
- if content.Value != want {
- t.Fatalf("hover failed:\n%s", tests.Diff(t, want, content.Value))
- }
- })
-}
-
-func TestImportShortcut(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import "fmt"
-
-func main() {}
-`
- for _, tt := range []struct {
- wantLinks int
- wantDef bool
- importShortcut string
- }{
- {1, false, "Link"},
- {0, true, "Definition"},
- {1, true, "Both"},
- } {
- t.Run(tt.importShortcut, func(t *testing.T) {
- WithOptions(
- EditorConfig{
- ImportShortcut: tt.importShortcut,
- },
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- file, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `"fmt"`))
- if !tt.wantDef && (file != "" || pos != (fake.Pos{})) {
- t.Fatalf("expected no definition, got one: %s:%v", file, pos)
- } else if tt.wantDef && file == "" && pos == (fake.Pos{}) {
- t.Fatalf("expected definition, got none")
- }
- links := env.DocumentLink("main.go")
- if len(links) != tt.wantLinks {
- t.Fatalf("expected %v links, got %v", tt.wantLinks, len(links))
- }
- })
- })
- }
-}
-
-func TestGoToTypeDefinition_Issue38589(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-type Int int
-
-type Struct struct{}
-
-func F1() {}
-func F2() (int, error) { return 0, nil }
-func F3() (**Struct, bool, *Int, error) { return nil, false, nil, nil }
-func F4() (**Struct, bool, *float64, error) { return nil, false, nil, nil }
-
-func main() {}
-`
-
- for _, tt := range []struct {
- re string
- wantError bool
- wantTypeRe string
- }{
- {re: `F1`, wantError: true},
- {re: `F2`, wantError: true},
- {re: `F3`, wantError: true},
- {re: `F4`, wantError: false, wantTypeRe: `type (Struct)`},
- } {
- t.Run(tt.re, func(t *testing.T) {
- Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
-
- _, pos, err := env.Editor.GoToTypeDefinition(env.Ctx, "main.go", env.RegexpSearch("main.go", tt.re))
- if tt.wantError {
- if err == nil {
- t.Fatal("expected error, got nil")
- }
- return
- }
- if err != nil {
- t.Fatalf("expected nil error, got %s", err)
- }
-
- typePos := env.RegexpSearch("main.go", tt.wantTypeRe)
- if pos != typePos {
- t.Errorf("invalid pos: want %+v, got %+v", typePos, pos)
- }
- })
- })
- }
-}
-
-// Test for golang/go#47825.
-func TestImportTestVariant(t *testing.T) {
- testenv.NeedsGo1Point(t, 13)
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- client/test/role.go --
-package test
-
-import _ "mod.com/client"
-
-type RoleSetup struct{}
--- client/client_role_test.go --
-package client_test
-
-import (
- "testing"
- _ "mod.com/client"
- ctest "mod.com/client/test"
-)
-
-func TestClient(t *testing.T) {
- _ = ctest.RoleSetup{}
-}
--- client/client_test.go --
-package client
-
-import "testing"
-
-func TestClient(t *testing.T) {}
--- client.go --
-package client
-`
- Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("client/client_role_test.go")
- env.GoToDefinition("client/client_role_test.go", env.RegexpSearch("client/client_role_test.go", "RoleSetup"))
- })
-}
-
-// This test exercises a crashing pattern from golang/go#49223.
-func TestGoToCrashingDefinition_Issue49223(t *testing.T) {
- Run(t, "", func(t *testing.T, env *Env) {
- params := &protocol.DefinitionParams{}
- params.TextDocument.URI = protocol.DocumentURI("fugitive%3A///Users/user/src/mm/ems/.git//0/pkg/domain/treasury/provider.go")
- params.Position.Character = 18
- params.Position.Line = 0
- env.Editor.Server.Definition(env.Ctx, params)
- })
-}
diff -urN a/gopls/internal/regtest/misc/embed_test.go b/gopls/internal/regtest/misc/embed_test.go
--- a/gopls/internal/regtest/misc/embed_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/embed_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,37 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-package misc
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestMissingPatternDiagnostic(t *testing.T) {
- testenv.NeedsGo1Point(t, 16)
- const files = `
--- go.mod --
-module example.com
--- x.go --
-package x
-
-import (
- _ "embed"
-)
-
-// Issue 47436
-func F() {}
-
-//go:embed NONEXISTENT
-var foo string
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("x.go")
- env.Await(env.DiagnosticAtRegexpWithMessage("x.go", `NONEXISTENT`, "no matching files found"))
- env.RegexpReplace("x.go", `NONEXISTENT`, "x.go")
- env.Await(EmptyDiagnostics("x.go"))
- })
-}
diff -urN a/gopls/internal/regtest/misc/failures_test.go b/gopls/internal/regtest/misc/failures_test.go
--- a/gopls/internal/regtest/misc/failures_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/failures_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,70 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-// This test passes (TestHoverOnError in definition_test.go) without
-// the //line directive
-func TestHoverFailure(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- a.y --
-DWIM(main)
-
--- main.go --
-//line a.y:1
-package main
-
-func main() {
- var err error
- err.Error()
-}`
- WithOptions(SkipLogs()).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- content, _ := env.Hover("main.go", env.RegexpSearch("main.go", "Error"))
- // without the //line comment content would be non-nil
- if content != nil {
- t.Fatalf("expected nil hover content for Error")
- }
- })
-}
-
-// badPackageDup contains a duplicate definition of the 'a' const.
-// this is from diagnostics_test.go,
-const badPackageDup = `
--- go.mod --
-module mod.com
-
-go 1.12
--- a.go --
-package consts
-
-const a = 1
--- b.go --
-package consts
-//line gen.go:5
-const a = 2
-`
-
-func TestFailingDiagnosticClearingOnEdit(t *testing.T) {
- Run(t, badPackageDup, func(t *testing.T, env *Env) {
- env.OpenFile("b.go")
- // no diagnostics for any files, but there should be
- env.Await(NoDiagnostics("a.go"), NoDiagnostics("b.go"))
-
- // Fix the error by editing the const name in b.go to `b`.
- env.RegexpReplace("b.go", "(a) = 2", "b")
-
- // The diagnostics that weren't sent above should now be cleared.
- })
-}
diff -urN a/gopls/internal/regtest/misc/fix_test.go b/gopls/internal/regtest/misc/fix_test.go
--- a/gopls/internal/regtest/misc/fix_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/fix_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,107 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/lsp/tests"
-)
-
-// A basic test for fillstruct, now that it uses a command.
-func TestFillStruct(t *testing.T) {
- const basic = `
--- go.mod --
-module mod.com
-
-go 1.14
--- main.go --
-package main
-
-type Info struct {
- WordCounts map[string]int
- Words []string
-}
-
-func Foo() {
- _ = Info{}
-}
-`
- Run(t, basic, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- pos := env.RegexpSearch("main.go", "Info{}").ToProtocolPosition()
- if err := env.Editor.RefactorRewrite(env.Ctx, "main.go", &protocol.Range{
- Start: pos,
- End: pos,
- }); err != nil {
- t.Fatal(err)
- }
- want := `package main
-
-type Info struct {
- WordCounts map[string]int
- Words []string
-}
-
-func Foo() {
- _ = Info{
- WordCounts: map[string]int{},
- Words: []string{},
- }
-}
-`
- if got := env.Editor.BufferText("main.go"); got != want {
- t.Fatalf("TestFillStruct failed:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-func TestFillReturns(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-func Foo() error {
- return
-}
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- var d protocol.PublishDiagnosticsParams
- env.Await(OnceMet(
- // The error message here changed in 1.18; "return values" covers both forms.
- env.DiagnosticAtRegexpWithMessage("main.go", `return`, "return values"),
- ReadDiagnostics("main.go", &d),
- ))
- codeActions := env.CodeAction("main.go", d.Diagnostics)
- if len(codeActions) != 2 {
- t.Fatalf("expected 2 code actions, got %v", len(codeActions))
- }
- var foundQuickFix, foundFixAll bool
- for _, a := range codeActions {
- if a.Kind == protocol.QuickFix {
- foundQuickFix = true
- }
- if a.Kind == protocol.SourceFixAll {
- foundFixAll = true
- }
- }
- if !foundQuickFix {
- t.Fatalf("expected quickfix code action, got none")
- }
- if !foundFixAll {
- t.Fatalf("expected fixall code action, got none")
- }
- env.ApplyQuickFixes("main.go", d.Diagnostics)
- env.Await(EmptyDiagnostics("main.go"))
- })
-}
diff -urN a/gopls/internal/regtest/misc/formatting_test.go b/gopls/internal/regtest/misc/formatting_test.go
--- a/gopls/internal/regtest/misc/formatting_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/formatting_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,369 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "strings"
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/tests"
-)
-
-const unformattedProgram = `
--- main.go --
-package main
-import "fmt"
-func main( ) {
- fmt.Println("Hello World.")
-}
--- main.go.golden --
-package main
-
-import "fmt"
-
-func main() {
- fmt.Println("Hello World.")
-}
-`
-
-func TestFormatting(t *testing.T) {
- Run(t, unformattedProgram, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.FormatBuffer("main.go")
- got := env.Editor.BufferText("main.go")
- want := env.ReadWorkspaceFile("main.go.golden")
- if got != want {
- t.Errorf("unexpected formatting result:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-// Tests golang/go#36824.
-func TestFormattingOneLine36824(t *testing.T) {
- const onelineProgram = `
--- a.go --
-package main; func f() {}
-
--- a.go.formatted --
-package main
-
-func f() {}
-`
- Run(t, onelineProgram, func(t *testing.T, env *Env) {
- env.OpenFile("a.go")
- env.FormatBuffer("a.go")
- got := env.Editor.BufferText("a.go")
- want := env.ReadWorkspaceFile("a.go.formatted")
- if got != want {
- t.Errorf("unexpected formatting result:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-// Tests golang/go#36824.
-func TestFormattingOneLineImports36824(t *testing.T) {
- const onelineProgramA = `
--- a.go --
-package x; func f() {fmt.Println()}
-
--- a.go.imported --
-package x
-
-import "fmt"
-
-func f() { fmt.Println() }
-`
- Run(t, onelineProgramA, func(t *testing.T, env *Env) {
- env.OpenFile("a.go")
- env.OrganizeImports("a.go")
- got := env.Editor.BufferText("a.go")
- want := env.ReadWorkspaceFile("a.go.imported")
- if got != want {
- t.Errorf("unexpected formatting result:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-func TestFormattingOneLineRmImports36824(t *testing.T) {
- const onelineProgramB = `
--- a.go --
-package x; import "os"; func f() {}
-
--- a.go.imported --
-package x
-
-func f() {}
-`
- Run(t, onelineProgramB, func(t *testing.T, env *Env) {
- env.OpenFile("a.go")
- env.OrganizeImports("a.go")
- got := env.Editor.BufferText("a.go")
- want := env.ReadWorkspaceFile("a.go.imported")
- if got != want {
- t.Errorf("unexpected formatting result:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-const disorganizedProgram = `
--- main.go --
-package main
-
-import (
- "fmt"
- "errors"
-)
-func main( ) {
- fmt.Println(errors.New("bad"))
-}
--- main.go.organized --
-package main
-
-import (
- "errors"
- "fmt"
-)
-func main( ) {
- fmt.Println(errors.New("bad"))
-}
--- main.go.formatted --
-package main
-
-import (
- "errors"
- "fmt"
-)
-
-func main() {
- fmt.Println(errors.New("bad"))
-}
-`
-
-func TestOrganizeImports(t *testing.T) {
- Run(t, disorganizedProgram, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.OrganizeImports("main.go")
- got := env.Editor.BufferText("main.go")
- want := env.ReadWorkspaceFile("main.go.organized")
- if got != want {
- t.Errorf("unexpected formatting result:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-func TestFormattingOnSave(t *testing.T) {
- Run(t, disorganizedProgram, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.SaveBuffer("main.go")
- got := env.Editor.BufferText("main.go")
- want := env.ReadWorkspaceFile("main.go.formatted")
- if got != want {
- t.Errorf("unexpected formatting result:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-// Tests various possibilities for comments in files with CRLF line endings.
-// Import organization in these files has historically been a source of bugs.
-func TestCRLFLineEndings(t *testing.T) {
- for _, tt := range []struct {
- issue, input, want string
- }{
- {
- issue: "41057",
- want: `package main
-
-/*
-Hi description
-*/
-func Hi() {
-}
-`,
- },
- {
- issue: "42646",
- want: `package main
-
-import (
- "fmt"
-)
-
-/*
-func upload(c echo.Context) error {
- if err := r.ParseForm(); err != nil {
- fmt.Fprintf(w, "ParseForm() err: %v", err)
- return
- }
- fmt.Fprintf(w, "POST request successful")
- path_ver := r.FormValue("path_ver")
- ukclin_ver := r.FormValue("ukclin_ver")
-
- fmt.Fprintf(w, "Name = %s\n", path_ver)
- fmt.Fprintf(w, "Address = %s\n", ukclin_ver)
-}
-*/
-
-func main() {
- const server_port = 8080
- fmt.Printf("port: %d\n", server_port)
-}
-`,
- },
- {
- issue: "42923",
- want: `package main
-
-// Line 1.
-// aa
-type Tree struct {
- arr []string
-}
-`,
- },
- {
- issue: "47200",
- input: `package main
-
-import "fmt"
-
-func main() {
- math.Sqrt(9)
- fmt.Println("hello")
-}
-`,
- want: `package main
-
-import (
- "fmt"
- "math"
-)
-
-func main() {
- math.Sqrt(9)
- fmt.Println("hello")
-}
-`,
- },
- } {
- t.Run(tt.issue, func(t *testing.T) {
- Run(t, "-- main.go --", func(t *testing.T, env *Env) {
- input := tt.input
- if input == "" {
- input = tt.want
- }
- crlf := strings.ReplaceAll(input, "\n", "\r\n")
- env.CreateBuffer("main.go", crlf)
- env.Await(env.DoneWithOpen())
- env.OrganizeImports("main.go")
- got := env.Editor.BufferText("main.go")
- got = strings.ReplaceAll(got, "\r\n", "\n") // convert everything to LF for simplicity
- if tt.want != got {
- t.Errorf("unexpected content after save:\n%s", tests.Diff(t, tt.want, got))
- }
- })
- })
- }
-}
-
-func TestFormattingOfGeneratedFile_Issue49555(t *testing.T) {
- const input = `
--- main.go --
-// Code generated by generator.go. DO NOT EDIT.
-
-package main
-
-import "fmt"
-
-func main() {
-
-
-
-
- fmt.Print("hello")
-}
-`
-
- Run(t, input, func(t *testing.T, env *Env) {
- wantErrSuffix := "file is generated"
-
- env.OpenFile("main.go")
- err := env.Editor.FormatBuffer(env.Ctx, "main.go")
- if err == nil {
- t.Fatal("expected error, got nil")
- }
- // Check only the suffix because an error contains a dynamic path to main.go
- if !strings.HasSuffix(err.Error(), wantErrSuffix) {
- t.Fatalf("unexpected error %q, want suffix %q", err.Error(), wantErrSuffix)
- }
- })
-}
-
-func TestGofumptFormatting(t *testing.T) {
-
- // Exercise some gofumpt formatting rules:
- // - No empty lines following an assignment operator
- // - Octal integer literals should use the 0o prefix on modules using Go
- // 1.13 and later. Requires LangVersion to be correctly resolved.
- // - std imports must be in a separate group at the top. Requires ModulePath
- // to be correctly resolved.
- const input = `
--- go.mod --
-module foo
-
-go 1.17
--- foo.go --
-package foo
-
-import (
- "foo/bar"
- "fmt"
-)
-
-const perm = 0755
-
-func foo() {
- foo :=
- "bar"
- fmt.Println(foo, bar.Bar)
-}
--- foo.go.formatted --
-package foo
-
-import (
- "fmt"
-
- "foo/bar"
-)
-
-const perm = 0o755
-
-func foo() {
- foo := "bar"
- fmt.Println(foo, bar.Bar)
-}
--- bar/bar.go --
-package bar
-
-const Bar = 42
-`
-
- WithOptions(
- EditorConfig{
- Settings: map[string]interface{}{
- "gofumpt": true,
- },
- },
- ).Run(t, input, func(t *testing.T, env *Env) {
- env.OpenFile("foo.go")
- env.FormatBuffer("foo.go")
- got := env.Editor.BufferText("foo.go")
- want := env.ReadWorkspaceFile("foo.go.formatted")
- if got != want {
- t.Errorf("unexpected formatting result:\n%s", tests.Diff(t, want, got))
- }
- })
-}
diff -urN a/gopls/internal/regtest/misc/generate_test.go b/gopls/internal/regtest/misc/generate_test.go
--- a/gopls/internal/regtest/misc/generate_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/generate_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,75 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// TODO(rfindley): figure out why go generate fails on android builders.
-
-//go:build !android
-// +build !android
-
-package misc
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestGenerateProgress(t *testing.T) {
- t.Skipf("skipping flaky test: https://golang.org/issue/49901")
-
- const generatedWorkspace = `
--- go.mod --
-module fake.test
-
-go 1.14
--- generate.go --
-// +build ignore
-
-package main
-
-import (
- "io/ioutil"
- "os"
-)
-
-func main() {
- ioutil.WriteFile("generated.go", []byte("package " + os.Args[1] + "\n\nconst Answer = 21"), 0644)
-}
-
--- lib1/lib.go --
-package lib1
-
-//go:generate go run ../generate.go lib1
-
--- lib2/lib.go --
-package lib2
-
-//go:generate go run ../generate.go lib2
-
--- main.go --
-package main
-
-import (
- "fake.test/lib1"
- "fake.test/lib2"
-)
-
-func main() {
- println(lib1.Answer + lib2.Answer)
-}
-`
-
- Run(t, generatedWorkspace, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("main.go", "lib1.(Answer)"),
- )
- env.RunGenerate("./lib1")
- env.RunGenerate("./lib2")
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- EmptyDiagnostics("main.go")),
- )
- })
-}
diff -urN a/gopls/internal/regtest/misc/highlight_test.go b/gopls/internal/regtest/misc/highlight_test.go
--- a/gopls/internal/regtest/misc/highlight_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/highlight_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,151 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "sort"
- "testing"
-
- "golang.org/x/tools/internal/lsp/fake"
- "golang.org/x/tools/internal/lsp/protocol"
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestWorkspacePackageHighlight(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-func main() {
- var A string = "A"
- x := "x-" + A
- println(A, x)
-}`
-
- Run(t, mod, func(t *testing.T, env *Env) {
- const file = "main.go"
- env.OpenFile(file)
- _, pos := env.GoToDefinition(file, env.RegexpSearch(file, `var (A) string`))
-
- checkHighlights(env, file, pos, 3)
- })
-}
-
-func TestStdPackageHighlight_Issue43511(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import "fmt"
-
-func main() {
- fmt.Printf()
-}`
-
- Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- file, _ := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `fmt\.(Printf)`))
- pos := env.RegexpSearch(file, `func Printf\((format) string`)
-
- checkHighlights(env, file, pos, 2)
- })
-}
-
-func TestThirdPartyPackageHighlight_Issue43511(t *testing.T) {
- const proxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.3/global/global.go --
-package global
-
-const A = 1
-
-func foo() {
- _ = A
-}
-
-func bar() int {
- return A + A
-}
--- example.com@v1.2.3/local/local.go --
-package local
-
-func foo() int {
- const b = 2
-
- return b * b * (b+1) + b
-}`
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
-
-require example.com v1.2.3
--- go.sum --
-example.com v1.2.3 h1:WFzrgiQJwEDJNLDUOV1f9qlasQkvzXf2UNLaNIqbWsI=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
--- main.go --
-package main
-
-import (
- _ "example.com/global"
- _ "example.com/local"
-)
-
-func main() {}`
-
- WithOptions(
- ProxyFiles(proxy),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
-
- file, _ := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `"example.com/global"`))
- pos := env.RegexpSearch(file, `const (A)`)
- checkHighlights(env, file, pos, 4)
-
- file, _ = env.GoToDefinition("main.go", env.RegexpSearch("main.go", `"example.com/local"`))
- pos = env.RegexpSearch(file, `const (b)`)
- checkHighlights(env, file, pos, 5)
- })
-}
-
-func checkHighlights(env *Env, file string, pos fake.Pos, highlightCount int) {
- t := env.T
- t.Helper()
-
- highlights := env.DocumentHighlight(file, pos)
- if len(highlights) != highlightCount {
- t.Fatalf("expected %v highlight(s), got %v", highlightCount, len(highlights))
- }
-
- references := env.References(file, pos)
- if len(highlights) != len(references) {
- t.Fatalf("number of highlights and references is expected to be equal: %v != %v", len(highlights), len(references))
- }
-
- sort.Slice(highlights, func(i, j int) bool {
- return protocol.CompareRange(highlights[i].Range, highlights[j].Range) < 0
- })
- sort.Slice(references, func(i, j int) bool {
- return protocol.CompareRange(references[i].Range, references[j].Range) < 0
- })
- for i := range highlights {
- if highlights[i].Range != references[i].Range {
- t.Errorf("highlight and reference ranges are expected to be equal: %v != %v", highlights[i].Range, references[i].Range)
- }
- }
-}
diff -urN a/gopls/internal/regtest/misc/hover_test.go b/gopls/internal/regtest/misc/hover_test.go
--- a/gopls/internal/regtest/misc/hover_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/hover_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,223 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "fmt"
- "strings"
- "testing"
-
- "golang.org/x/tools/internal/lsp/fake"
- . "golang.org/x/tools/internal/lsp/regtest"
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestHoverUnexported(t *testing.T) {
- const proxy = `
--- golang.org/x/structs@v1.0.0/go.mod --
-module golang.org/x/structs
-
-go 1.12
-
--- golang.org/x/structs@v1.0.0/types.go --
-package structs
-
-type Mixed struct {
- // Exported comment
- Exported int
- unexported string
-}
-
-func printMixed(m Mixed) {
- println(m)
-}
-`
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
-
-require golang.org/x/structs v1.0.0
--- go.sum --
-golang.org/x/structs v1.0.0 h1:Ito/a7hBYZaNKShFrZKjfBA/SIPvmBrcPCBWPx5QeKk=
-golang.org/x/structs v1.0.0/go.mod h1:47gkSIdo5AaQaWJS0upVORsxfEr1LL1MWv9dmYF3iq4=
--- main.go --
-package main
-
-import "golang.org/x/structs"
-
-func main() {
- var m structs.Mixed
- _ = m.Exported
-}
-`
-
- // TODO: use a nested workspace folder here.
- WithOptions(
- ProxyFiles(proxy),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- mixedPos := env.RegexpSearch("main.go", "Mixed")
- got, _ := env.Hover("main.go", mixedPos)
- if !strings.Contains(got.Value, "unexported") {
- t.Errorf("Workspace hover: missing expected field 'unexported'. Got:\n%q", got.Value)
- }
-
- cacheFile, _ := env.GoToDefinition("main.go", mixedPos)
- argPos := env.RegexpSearch(cacheFile, "printMixed.*(Mixed)")
- got, _ = env.Hover(cacheFile, argPos)
- if !strings.Contains(got.Value, "unexported") {
- t.Errorf("Non-workspace hover: missing expected field 'unexported'. Got:\n%q", got.Value)
- }
-
- exportedFieldPos := env.RegexpSearch("main.go", "Exported")
- got, _ = env.Hover("main.go", exportedFieldPos)
- if !strings.Contains(got.Value, "comment") {
- t.Errorf("Workspace hover: missing comment for field 'Exported'. Got:\n%q", got.Value)
- }
- })
-}
-
-func TestHoverIntLiteral(t *testing.T) {
- testenv.NeedsGo1Point(t, 13)
- const source = `
--- main.go --
-package main
-
-var (
- bigBin = 0b1001001
-)
-
-var hex = 0xe34e
-
-func main() {
-}
-`
- Run(t, source, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- hexExpected := "58190"
- got, _ := env.Hover("main.go", env.RegexpSearch("main.go", "hex"))
- if got != nil && !strings.Contains(got.Value, hexExpected) {
- t.Errorf("Hover: missing expected field '%s'. Got:\n%q", hexExpected, got.Value)
- }
-
- binExpected := "73"
- got, _ = env.Hover("main.go", env.RegexpSearch("main.go", "bigBin"))
- if got != nil && !strings.Contains(got.Value, binExpected) {
- t.Errorf("Hover: missing expected field '%s'. Got:\n%q", binExpected, got.Value)
- }
- })
-}
-
-// Tests that hovering does not trigger the panic in golang/go#48249.
-func TestPanicInHoverBrokenCode(t *testing.T) {
- testenv.NeedsGo1Point(t, 13)
- const source = `
--- main.go --
-package main
-
-type Example struct`
- Run(t, source, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.Editor.Hover(env.Ctx, "main.go", env.RegexpSearch("main.go", "Example"))
- })
-}
-
-func TestHoverRune_48492(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.18
--- main.go --
-package main
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.EditBuffer("main.go", fake.NewEdit(0, 0, 1, 0, "package main\nfunc main() {\nconst x = `\nfoo\n`\n}"))
- env.Editor.Hover(env.Ctx, "main.go", env.RegexpSearch("main.go", "foo"))
- })
-}
-
-func TestHoverImport(t *testing.T) {
- // For Go.13 and earlier versions, Go will try to download imported but missing packages. This behavior breaks the
- // workspace as Go fails to download non-existent package "mod.com/lib4"
- testenv.NeedsGo1Point(t, 14)
- const packageDoc1 = "Package lib1 hover documentation"
- const packageDoc2 = "Package lib2 hover documentation"
- tests := []struct {
- hoverPackage string
- want string
- }{
- {
- "mod.com/lib1",
- packageDoc1,
- },
- {
- "mod.com/lib2",
- packageDoc2,
- },
- {
- "mod.com/lib3",
- "",
- },
- }
- source := fmt.Sprintf(`
--- go.mod --
-module mod.com
-
-go 1.12
--- lib1/a.go --
-// %s
-package lib1
-
-const C = 1
-
--- lib1/b.go --
-package lib1
-
-const D = 1
-
--- lib2/a.go --
-// %s
-package lib2
-
-const E = 1
-
--- lib3/a.go --
-package lib3
-
-const F = 1
-
--- main.go --
-package main
-
-import (
- "mod.com/lib1"
- "mod.com/lib2"
- "mod.com/lib3"
- "mod.com/lib4"
-)
-
-func main() {
- println("Hello")
-}
- `, packageDoc1, packageDoc2)
- Run(t, source, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- for _, test := range tests {
- got, _ := env.Hover("main.go", env.RegexpSearch("main.go", test.hoverPackage))
- if !strings.Contains(got.Value, test.want) {
- t.Errorf("Hover: got:\n%q\nwant:\n%q", got.Value, test.want)
- }
- }
-
- got, _ := env.Hover("main.go", env.RegexpSearch("main.go", "mod.com/lib4"))
- if got != nil {
- t.Errorf("Hover: got:\n%q\nwant:\n%v", got.Value, nil)
- }
- })
-}
diff -urN a/gopls/internal/regtest/misc/import_test.go b/gopls/internal/regtest/misc/import_test.go
--- a/gopls/internal/regtest/misc/import_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/import_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,133 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "golang.org/x/tools/internal/lsp/command"
- "golang.org/x/tools/internal/lsp/protocol"
- . "golang.org/x/tools/internal/lsp/regtest"
- "golang.org/x/tools/internal/lsp/tests"
-)
-
-func TestAddImport(t *testing.T) {
- const before = `package main
-
-import "fmt"
-
-func main() {
- fmt.Println("hello world")
-}
-`
-
- const want = `package main
-
-import (
- "bytes"
- "fmt"
-)
-
-func main() {
- fmt.Println("hello world")
-}
-`
-
- Run(t, "", func(t *testing.T, env *Env) {
- env.CreateBuffer("main.go", before)
- cmd, err := command.NewAddImportCommand("Add Import", command.AddImportArgs{
- URI: env.Sandbox.Workdir.URI("main.go"),
- ImportPath: "bytes",
- })
- if err != nil {
- t.Fatal(err)
- }
- env.ExecuteCommand(&protocol.ExecuteCommandParams{
- Command: "gopls.add_import",
- Arguments: cmd.Arguments,
- }, nil)
- got := env.Editor.BufferText("main.go")
- if got != want {
- t.Fatalf("gopls.add_import failed\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-func TestListImports(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- foo.go --
-package foo
-const C = 1
--- import_strings_test.go --
-package foo
-import (
- x "strings"
- "testing"
-)
-
-func TestFoo(t *testing.T) {}
--- import_testing_test.go --
-package foo
-
-import "testing"
-
-func TestFoo2(t *testing.T) {}
-`
- tests := []struct {
- filename string
- want command.ListImportsResult
- }{
- {
- filename: "import_strings_test.go",
- want: command.ListImportsResult{
- Imports: []command.FileImport{
- {Name: "x", Path: "strings"},
- {Path: "testing"},
- },
- PackageImports: []command.PackageImport{
- {Path: "strings"},
- {Path: "testing"},
- },
- },
- },
- {
- filename: "import_testing_test.go",
- want: command.ListImportsResult{
- Imports: []command.FileImport{
- {Path: "testing"},
- },
- PackageImports: []command.PackageImport{
- {Path: "strings"},
- {Path: "testing"},
- },
- },
- },
- }
-
- Run(t, files, func(t *testing.T, env *Env) {
- for _, tt := range tests {
- cmd, err := command.NewListImportsCommand("List Imports", command.URIArg{
- URI: env.Sandbox.Workdir.URI(tt.filename),
- })
- if err != nil {
- t.Fatal(err)
- }
- var result command.ListImportsResult
- env.ExecuteCommand(&protocol.ExecuteCommandParams{
- Command: command.ListImports.ID(),
- Arguments: cmd.Arguments,
- }, &result)
- if diff := cmp.Diff(tt.want, result); diff != "" {
- t.Errorf("unexpected list imports result for %q (-want +got):\n%s", tt.filename, diff)
- }
- }
-
- })
-}
diff -urN a/gopls/internal/regtest/misc/imports_test.go b/gopls/internal/regtest/misc/imports_test.go
--- a/gopls/internal/regtest/misc/imports_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/imports_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,216 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/testenv"
-)
-
-// Tests golang/go#38815.
-func TestIssue38815(t *testing.T) {
- const needs = `
--- go.mod --
-module foo
-
-go 1.12
--- a.go --
-package main
-func f() {}
-`
- const ntest = `package main
-func TestZ(t *testing.T) {
- f()
-}
-`
- const want = `package main
-
-import "testing"
-
-func TestZ(t *testing.T) {
- f()
-}
-`
-
- // it was returning
- // "package main\nimport \"testing\"\npackage main..."
- Run(t, needs, func(t *testing.T, env *Env) {
- env.CreateBuffer("a_test.go", ntest)
- env.SaveBuffer("a_test.go")
- got := env.Editor.BufferText("a_test.go")
- if want != got {
- t.Errorf("got\n%q, wanted\n%q", got, want)
- }
- })
-}
-
-func TestVim1(t *testing.T) {
- const vim1 = `package main
-
-import "fmt"
-
-var foo = 1
-var bar = 2
-
-func main() {
- fmt.Printf("This is a test %v\n", foo)
- fmt.Printf("This is another test %v\n", foo)
- fmt.Printf("This is also a test %v\n", foo)
-}
-`
-
- // The file remains unchanged, but if there are any CodeActions returned, they confuse vim.
- // Therefore check for no CodeActions
- Run(t, "", func(t *testing.T, env *Env) {
- env.CreateBuffer("main.go", vim1)
- env.OrganizeImports("main.go")
- actions := env.CodeAction("main.go", nil)
- if len(actions) > 0 {
- got := env.Editor.BufferText("main.go")
- t.Errorf("unexpected actions %#v", actions)
- if got == vim1 {
- t.Errorf("no changes")
- } else {
- t.Errorf("got\n%q", got)
- t.Errorf("was\n%q", vim1)
- }
- }
- })
-}
-
-func TestVim2(t *testing.T) {
- const vim2 = `package main
-
-import (
- "fmt"
-
- "example.com/blah"
-
- "rubbish.com/useless"
-)
-
-func main() {
- fmt.Println(blah.Name, useless.Name)
-}
-`
-
- Run(t, "", func(t *testing.T, env *Env) {
- env.CreateBuffer("main.go", vim2)
- env.OrganizeImports("main.go")
- actions := env.CodeAction("main.go", nil)
- if len(actions) > 0 {
- t.Errorf("unexpected actions %#v", actions)
- }
- })
-}
-
-func TestGOMODCACHE(t *testing.T) {
- const proxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.3/x/x.go --
-package x
-
-const X = 1
--- example.com@v1.2.3/y/y.go --
-package y
-
-const Y = 2
-`
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
-
-require example.com v1.2.3
--- go.sum --
-example.com v1.2.3 h1:6vTQqzX+pnwngZF1+5gcO3ZEWmix1jJ/h+pWS8wUxK0=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
--- main.go --
-package main
-
-import "example.com/x"
-
-var _, _ = x.X, y.Y
-`
- testenv.NeedsGo1Point(t, 15)
-
- modcache, err := ioutil.TempDir("", "TestGOMODCACHE-modcache")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(modcache)
- editorConfig := EditorConfig{Env: map[string]string{"GOMODCACHE": modcache}}
- WithOptions(
- editorConfig,
- ProxyFiles(proxy),
- ).Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.Await(env.DiagnosticAtRegexp("main.go", `y.Y`))
- env.SaveBuffer("main.go")
- env.Await(EmptyDiagnostics("main.go"))
- path, _ := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `y.(Y)`))
- if !strings.HasPrefix(path, filepath.ToSlash(modcache)) {
- t.Errorf("found module dependency outside of GOMODCACHE: got %v, wanted subdir of %v", path, filepath.ToSlash(modcache))
- }
- })
-}
-
-// Tests golang/go#40685.
-func TestAcceptImportsQuickFixTestVariant(t *testing.T) {
- const pkg = `
--- go.mod --
-module mod.com
-
-go 1.12
--- a/a.go --
-package a
-
-import (
- "fmt"
-)
-
-func _() {
- fmt.Println("")
- os.Stat("")
-}
--- a/a_test.go --
-package a
-
-import (
- "os"
- "testing"
-)
-
-func TestA(t *testing.T) {
- os.Stat("")
-}
-`
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexp("a/a.go", "os.Stat"),
- ReadDiagnostics("a/a.go", &d),
- ),
- )
- env.ApplyQuickFixes("a/a.go", d.Diagnostics)
- env.Await(
- EmptyDiagnostics("a/a.go"),
- )
- })
-}
diff -urN a/gopls/internal/regtest/misc/link_test.go b/gopls/internal/regtest/misc/link_test.go
--- a/gopls/internal/regtest/misc/link_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/link_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,98 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "strings"
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestHoverAndDocumentLink(t *testing.T) {
- testenv.NeedsGo1Point(t, 13)
-
- const program = `
--- go.mod --
-module mod.test
-
-go 1.12
-
-require import.test v1.2.3
--- go.sum --
-import.test v1.2.3 h1:Mu4N9BICLJFxwwn8YNg6T3frkFWW1O7evXvo0HiRjBc=
-import.test v1.2.3/go.mod h1:KooCN1g237upRg7irU7F+3oADn5tVClU8YYW4I1xhMk=
--- main.go --
-package main
-
-import "import.test/pkg"
-
-func main() {
- // Issue 43990: this is not a link that most users can open from an LSP
- // client: mongodb://not.a.link.com
- println(pkg.Hello)
-}`
-
- const proxy = `
--- import.test@v1.2.3/go.mod --
-module import.test
-
-go 1.12
--- import.test@v1.2.3/pkg/const.go --
-package pkg
-
-const Hello = "Hello"
-`
- WithOptions(
- ProxyFiles(proxy),
- ).Run(t, program, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- env.OpenFile("go.mod")
-
- modLink := "https://pkg.go.dev/mod/import.test@v1.2.3?utm_source=gopls"
- pkgLink := "https://pkg.go.dev/import.test@v1.2.3/pkg?utm_source=gopls"
-
- // First, check that we get the expected links via hover and documentLink.
- content, _ := env.Hover("main.go", env.RegexpSearch("main.go", "pkg.Hello"))
- if content == nil || !strings.Contains(content.Value, pkgLink) {
- t.Errorf("hover: got %v in main.go, want contains %q", content, pkgLink)
- }
- content, _ = env.Hover("go.mod", env.RegexpSearch("go.mod", "import.test"))
- if content == nil || !strings.Contains(content.Value, pkgLink) {
- t.Errorf("hover: got %v in go.mod, want contains %q", content, pkgLink)
- }
- links := env.DocumentLink("main.go")
- if len(links) != 1 || links[0].Target != pkgLink {
- t.Errorf("documentLink: got %v for main.go, want link to %q", links, pkgLink)
- }
- links = env.DocumentLink("go.mod")
- if len(links) != 1 || links[0].Target != modLink {
- t.Errorf("documentLink: got %v for go.mod, want link to %q", links, modLink)
- }
-
- // Then change the environment to make these links private.
- env.ChangeEnv(map[string]string{"GOPRIVATE": "import.test"})
-
- // Finally, verify that the links are gone.
- content, _ = env.Hover("main.go", env.RegexpSearch("main.go", "pkg.Hello"))
- if content == nil || strings.Contains(content.Value, pkgLink) {
- t.Errorf("hover: got %v in main.go, want non-empty hover without %q", content, pkgLink)
- }
- content, _ = env.Hover("go.mod", env.RegexpSearch("go.mod", "import.test"))
- if content == nil || strings.Contains(content.Value, modLink) {
- t.Errorf("hover: got %v in go.mod, want contains %q", content, modLink)
- }
- links = env.DocumentLink("main.go")
- if len(links) != 0 {
- t.Errorf("documentLink: got %d document links for main.go, want 0\nlinks: %v", len(links), links)
- }
- links = env.DocumentLink("go.mod")
- if len(links) != 0 {
- t.Errorf("documentLink: got %d document links for go.mod, want 0\nlinks: %v", len(links), links)
- }
- })
-}
diff -urN a/gopls/internal/regtest/misc/misc_test.go b/gopls/internal/regtest/misc/misc_test.go
--- a/gopls/internal/regtest/misc/misc_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/misc_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,18 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestMain(m *testing.M) {
- bug.PanicOnBugs = true
- regtest.Main(m, hooks.Options)
-}
diff -urN a/gopls/internal/regtest/misc/multiple_adhoc_test.go b/gopls/internal/regtest/misc/multiple_adhoc_test.go
--- a/gopls/internal/regtest/misc/multiple_adhoc_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/multiple_adhoc_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,44 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestMultipleAdHocPackages(t *testing.T) {
- Run(t, `
--- a/a.go --
-package main
-
-import "fmt"
-
-func main() {
- fmt.Println("")
-}
--- a/b.go --
-package main
-
-import "fmt"
-
-func main() () {
- fmt.Println("")
-}
-`, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- if list := env.Completion("a/a.go", env.RegexpSearch("a/a.go", "Println")); list == nil || len(list.Items) == 0 {
- t.Fatal("expected completions, got none")
- }
- env.OpenFile("a/b.go")
- if list := env.Completion("a/b.go", env.RegexpSearch("a/b.go", "Println")); list == nil || len(list.Items) == 0 {
- t.Fatal("expected completions, got none")
- }
- if list := env.Completion("a/a.go", env.RegexpSearch("a/a.go", "Println")); list == nil || len(list.Items) == 0 {
- t.Fatal("expected completions, got none")
- }
- })
-}
diff -urN a/gopls/internal/regtest/misc/references_test.go b/gopls/internal/regtest/misc/references_test.go
--- a/gopls/internal/regtest/misc/references_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/references_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,83 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestStdlibReferences(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-import "fmt"
-
-func main() {
- fmt.Print()
-}
-`
-
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- file, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `fmt.(Print)`))
- refs, err := env.Editor.References(env.Ctx, file, pos)
- if err != nil {
- t.Fatal(err)
- }
- if len(refs) != 2 {
- t.Fatalf("got %v reference(s), want 2", len(refs))
- }
- // The first reference is guaranteed to be the definition.
- if got, want := refs[1].URI, env.Sandbox.Workdir.URI("main.go"); got != want {
- t.Errorf("found reference in %v, wanted %v", got, want)
- }
- })
-}
-
-// This reproduces and tests golang/go#48400.
-func TestReferencesPanicOnError(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-type t interface {
- error
-}
-
-type s struct{}
-
-func (*s) Error() string {
- return ""
-}
-
-func _() {
- var s s
- _ = s.Error()
-}
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- file, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `Error`))
- refs, err := env.Editor.References(env.Ctx, file, pos)
- if err == nil {
- t.Fatalf("expected error for references, instead got %v", refs)
- }
- wantErr := "no position for func (error).Error() string"
- if err.Error() != wantErr {
- t.Fatalf("expected error with message %s, instead got %s", wantErr, err.Error())
- }
- })
-}
diff -urN a/gopls/internal/regtest/misc/rename_test.go b/gopls/internal/regtest/misc/rename_test.go
--- a/gopls/internal/regtest/misc/rename_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/rename_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,58 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "strings"
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-// Test for golang/go#47564.
-func TestRenameInTestVariant(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- stringutil/stringutil.go --
-package stringutil
-
-func Identity(s string) string {
- return s
-}
--- stringutil/stringutil_test.go --
-package stringutil
-
-func TestIdentity(t *testing.T) {
- if got := Identity("foo"); got != "foo" {
- t.Errorf("bad")
- }
-}
--- main.go --
-package main
-
-import (
- "fmt"
-
- "mod.com/stringutil"
-)
-
-func main() {
- fmt.Println(stringutil.Identity("hello world"))
-}
-`
-
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- pos := env.RegexpSearch("main.go", `stringutil\.(Identity)`)
- env.Rename("main.go", pos, "Identityx")
- text := env.Editor.BufferText("stringutil/stringutil_test.go")
- if !strings.Contains(text, "Identityx") {
- t.Errorf("stringutil/stringutil_test.go: missing expected token `Identityx` after rename:\n%s", text)
- }
- })
-}
diff -urN a/gopls/internal/regtest/misc/semantictokens_test.go b/gopls/internal/regtest/misc/semantictokens_test.go
--- a/gopls/internal/regtest/misc/semantictokens_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/semantictokens_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,44 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- "golang.org/x/tools/internal/lsp/protocol"
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestBadURICrash_VSCodeIssue1498(t *testing.T) {
- const src = `
--- go.mod --
-module example.com
-
-go 1.12
-
--- main.go --
-package main
-
-func main() {}
-
-`
- WithOptions(
- Modes(Singleton),
- EditorConfig{
- AllExperiments: true,
- },
- ).Run(t, src, func(t *testing.T, env *Env) {
- params := &protocol.SemanticTokensParams{}
- const badURI = "http://foo"
- params.TextDocument.URI = badURI
- // This call panicked in the past: golang/vscode-go#1498.
- if _, err := env.Editor.Server.SemanticTokensFull(env.Ctx, params); err != nil {
- // Requests to an invalid URI scheme shouldn't result in an error, we
- // simply don't support this so return empty result. This could be
- // changed, but for now assert on the current behavior.
- t.Errorf("SemanticTokensFull(%q): %v", badURI, err)
- }
- })
-}
diff -urN a/gopls/internal/regtest/misc/settings_test.go b/gopls/internal/regtest/misc/settings_test.go
--- a/gopls/internal/regtest/misc/settings_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/settings_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,36 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestEmptyDirectoryFilters_Issue51843(t *testing.T) {
- const src = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-func main() {
-}
-`
-
- WithOptions(
- EditorConfig{
- Settings: map[string]interface{}{
- "directoryFilters": []string{""},
- },
- },
- ).Run(t, src, func(t *testing.T, env *Env) {
- // No need to do anything. Issue golang/go#51843 is triggered by the empty
- // directory filter above.
- })
-}
diff -urN a/gopls/internal/regtest/misc/shared_test.go b/gopls/internal/regtest/misc/shared_test.go
--- a/gopls/internal/regtest/misc/shared_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/shared_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,64 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-const sharedProgram = `
--- go.mod --
-module mod
-
-go 1.12
--- main.go --
-package main
-
-import "fmt"
-
-func main() {
- fmt.Println("Hello World.")
-}`
-
-func runShared(t *testing.T, testFunc func(env1 *Env, env2 *Env)) {
- // Only run these tests in forwarded modes.
- modes := DefaultModes() & (Forwarded | SeparateProcess)
- WithOptions(Modes(modes)).Run(t, sharedProgram, func(t *testing.T, env1 *Env) {
- // Create a second test session connected to the same workspace and server
- // as the first.
- env2 := NewEnv(env1.Ctx, t, env1.Sandbox, env1.Server, env1.Editor.Config, true)
- env2.Await(InitialWorkspaceLoad)
- testFunc(env1, env2)
- })
-}
-
-func TestSimultaneousEdits(t *testing.T) {
- runShared(t, func(env1 *Env, env2 *Env) {
- // In editor #1, break fmt.Println as before.
- env1.OpenFile("main.go")
- env1.RegexpReplace("main.go", "Printl(n)", "")
- // In editor #2 remove the closing brace.
- env2.OpenFile("main.go")
- env2.RegexpReplace("main.go", "\\)\n(})", "")
-
- // Now check that we got different diagnostics in each environment.
- env1.Await(env1.DiagnosticAtRegexp("main.go", "Printl"))
- env2.Await(env2.DiagnosticAtRegexp("main.go", "$"))
- })
-}
-
-func TestShutdown(t *testing.T) {
- runShared(t, func(env1 *Env, env2 *Env) {
- if err := env1.Editor.Close(env1.Ctx); err != nil {
- t.Errorf("closing first editor: %v", err)
- }
- // Now make an edit in editor #2 to trigger diagnostics.
- env2.OpenFile("main.go")
- env2.RegexpReplace("main.go", "\\)\n(})", "")
- env2.Await(env2.DiagnosticAtRegexp("main.go", "$"))
- })
-}
diff -urN a/gopls/internal/regtest/misc/staticcheck_test.go b/gopls/internal/regtest/misc/staticcheck_test.go
--- a/gopls/internal/regtest/misc/staticcheck_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/staticcheck_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,78 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- "golang.org/x/tools/internal/testenv"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestStaticcheckGenerics(t *testing.T) {
- testenv.NeedsGo1Point(t, 18) // generics were introduced in Go 1.18
-
- const files = `
--- go.mod --
-module mod.com
-
-go 1.18
--- a/a.go --
-package a
-
-import (
- "errors"
- "sort"
- "strings"
-)
-
-func Zero[P any]() P {
- var p P
- return p
-}
-
-type Inst[P any] struct {
- Field P
-}
-
-func testGenerics[P *T, T any](p P) {
- // Calls to instantiated functions should not break checks.
- slice := Zero[string]()
- sort.Slice(slice, func(i, j int) bool {
- return slice[i] < slice[j]
- })
-
- // Usage of instantiated fields should not break checks.
- g := Inst[string]{"hello"}
- g.Field = strings.TrimLeft(g.Field, "12234")
-
- // Use of type parameters should not break checks.
- var q P
- p = q // SA4009: p is overwritten before its first use
- q = &*p // SA4001: &* will be simplified
-}
-
-
-// FooErr should be called ErrFoo (ST1012)
-var FooErr error = errors.New("foo")
-`
-
- WithOptions(EditorConfig{
- Settings: map[string]interface{}{
- "staticcheck": true,
- },
- }).Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.Await(
- env.DiagnosticAtRegexpFromSource("a/a.go", "sort.Slice", "sortslice"),
- env.DiagnosticAtRegexpFromSource("a/a.go", "sort.Slice.(slice)", "SA1028"),
- env.DiagnosticAtRegexpFromSource("a/a.go", "var (FooErr)", "ST1012"),
- env.DiagnosticAtRegexpFromSource("a/a.go", `"12234"`, "SA1024"),
- env.DiagnosticAtRegexpFromSource("a/a.go", "testGenerics.*(p P)", "SA4009"),
- env.DiagnosticAtRegexpFromSource("a/a.go", "q = (&\\*p)", "SA4001"),
- )
- })
-}
diff -urN a/gopls/internal/regtest/misc/vendor_test.go b/gopls/internal/regtest/misc/vendor_test.go
--- a/gopls/internal/regtest/misc/vendor_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/vendor_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,79 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/testenv"
-)
-
-const basicProxy = `
--- golang.org/x/hello@v1.2.3/go.mod --
-module golang.org/x/hello
-
-go 1.14
--- golang.org/x/hello@v1.2.3/hi/hi.go --
-package hi
-
-var Goodbye error
-`
-
-func TestInconsistentVendoring(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- // TODO(golang/go#49646): delete this comment once this test is stable.
- //
- // In golang/go#49646, this test is reported as flaky on Windows. We believe
- // this is due to file contention from go mod vendor that should be resolved.
- // If this test proves to still be flaky, skip it.
- //
- // if runtime.GOOS == "windows" {
- // t.Skipf("skipping test due to flakiness on Windows: https://golang.org/issue/49646")
- // }
-
- const pkgThatUsesVendoring = `
--- go.mod --
-module mod.com
-
-go 1.14
-
-require golang.org/x/hello v1.2.3
--- go.sum --
-golang.org/x/hello v1.2.3 h1:EcMp5gSkIhaTkPXp8/3+VH+IFqTpk3ZbpOhqk0Ncmho=
-golang.org/x/hello v1.2.3/go.mod h1:WW7ER2MRNXWA6c8/4bDIek4Hc/+DofTrMaQQitGXcco=
--- vendor/modules.txt --
--- a/a1.go --
-package a
-
-import "golang.org/x/hello/hi"
-
-func _() {
- _ = hi.Goodbye
- var q int // hardcode a diagnostic
-}
-`
- WithOptions(
- Modes(Singleton),
- ProxyFiles(basicProxy),
- ).Run(t, pkgThatUsesVendoring, func(t *testing.T, env *Env) {
- env.OpenFile("a/a1.go")
- d := &protocol.PublishDiagnosticsParams{}
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("go.mod", "module mod.com", "Inconsistent vendoring"),
- ReadDiagnostics("go.mod", d),
- ),
- )
- env.ApplyQuickFixes("go.mod", d.Diagnostics)
-
- env.Await(
- env.DiagnosticAtRegexpWithMessage("a/a1.go", `q int`, "not used"),
- )
- })
-}
diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/misc/vuln_test.go
--- a/gopls/internal/regtest/misc/vuln_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/vuln_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,43 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- "golang.org/x/tools/internal/lsp/command"
- "golang.org/x/tools/internal/lsp/protocol"
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestRunVulncheckExpError(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- foo.go --
-package foo
-`
- Run(t, files, func(t *testing.T, env *Env) {
- cmd, err := command.NewRunVulncheckExpCommand("Run Vulncheck Exp", command.VulncheckArgs{
- Dir: "/invalid/file/url", // invalid arg
- })
- if err != nil {
- t.Fatal(err)
- }
-
- params := &protocol.ExecuteCommandParams{
- Command: command.RunVulncheckExp.ID(),
- Arguments: cmd.Arguments,
- }
-
- response, err := env.Editor.ExecuteCommand(env.Ctx, params)
- // We want an error!
- if err == nil {
- t.Errorf("got success, want invalid file URL error: %v", response)
- }
- })
-}
diff -urN a/gopls/internal/regtest/misc/workspace_symbol_test.go b/gopls/internal/regtest/misc/workspace_symbol_test.go
--- a/gopls/internal/regtest/misc/workspace_symbol_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/misc/workspace_symbol_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,131 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package misc
-
-import (
- "testing"
-
- "golang.org/x/tools/internal/lsp/protocol"
- . "golang.org/x/tools/internal/lsp/regtest"
- "golang.org/x/tools/internal/lsp/source"
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestWorkspaceSymbolMissingMetadata(t *testing.T) {
- // We get 2 symbols on 1.12, for some reason.
- testenv.NeedsGo1Point(t, 13)
-
- const files = `
--- go.mod --
-module mod.com
-
-go 1.17
--- a.go --
-package p
-
-const C1 = "a.go"
--- ignore.go --
-
-// +build ignore
-
-package ignore
-
-const C2 = "ignore.go"
-`
-
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("a.go")
- syms := env.WorkspaceSymbol("C")
- if got, want := len(syms), 1; got != want {
- t.Errorf("got %d symbols, want %d", got, want)
- }
-
- // Opening up an ignored file will result in an overlay with missing
- // metadata, but this shouldn't break workspace symbols requests.
- env.OpenFile("ignore.go")
- syms = env.WorkspaceSymbol("C")
- if got, want := len(syms), 1; got != want {
- t.Errorf("got %d symbols, want %d", got, want)
- }
- })
-}
-
-func TestWorkspaceSymbolSorting(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.17
--- a/a.go --
-package a
-
-const (
- Foo = iota
- FooBar
- Fooey
- Fooex
- Fooest
-)
-`
-
- var symbolMatcher = string(source.SymbolFastFuzzy)
- WithOptions(
- EditorConfig{
- SymbolMatcher: &symbolMatcher,
- },
- ).Run(t, files, func(t *testing.T, env *Env) {
- want := []string{
- "Foo", // prefer exact segment matches first
- "FooBar", // ...followed by exact word matches
- "Fooex", // shorter than Fooest, FooBar, lexically before Fooey
- "Fooey", // shorter than Fooest, Foobar
- "Fooest",
- }
- got := env.WorkspaceSymbol("Foo")
- compareSymbols(t, got, want)
- })
-}
-
-func TestWorkspaceSymbolSpecialPatterns(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.17
--- a/a.go --
-package a
-
-const (
- AxxBxxCxx
- ABC
-)
-`
-
- var symbolMatcher = string(source.SymbolFastFuzzy)
- WithOptions(
- EditorConfig{
- SymbolMatcher: &symbolMatcher,
- },
- ).Run(t, files, func(t *testing.T, env *Env) {
- compareSymbols(t, env.WorkspaceSymbol("ABC"), []string{"ABC", "AxxBxxCxx"})
- compareSymbols(t, env.WorkspaceSymbol("'ABC"), []string{"ABC"})
- compareSymbols(t, env.WorkspaceSymbol("^mod.com"), []string{"mod.com/a.ABC", "mod.com/a.AxxBxxCxx"})
- compareSymbols(t, env.WorkspaceSymbol("^mod.com Axx"), []string{"mod.com/a.AxxBxxCxx"})
- compareSymbols(t, env.WorkspaceSymbol("C$"), []string{"ABC"})
- })
-}
-
-func compareSymbols(t *testing.T, got []protocol.SymbolInformation, want []string) {
- t.Helper()
- if len(got) != len(want) {
- t.Errorf("got %d symbols, want %d", len(got), len(want))
- }
-
- for i := range got {
- if got[i].Name != want[i] {
- t.Errorf("got[%d] = %q, want %q", i, got[i].Name, want[i])
- }
- }
-}
diff -urN a/gopls/internal/regtest/modfile/modfile_test.go b/gopls/internal/regtest/modfile/modfile_test.go
--- a/gopls/internal/regtest/modfile/modfile_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/modfile/modfile_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,1199 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package modfile
-
-import (
- "path/filepath"
- "runtime"
- "strings"
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/lsp/tests"
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestMain(m *testing.M) {
- bug.PanicOnBugs = true
- Main(m, hooks.Options)
-}
-
-const workspaceProxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.3/blah/blah.go --
-package blah
-
-func SaySomething() {
- fmt.Println("something")
-}
--- random.org@v1.2.3/go.mod --
-module random.org
-
-go 1.12
--- random.org@v1.2.3/bye/bye.go --
-package bye
-
-func Goodbye() {
- println("Bye")
-}
-`
-
-const proxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.3/blah/blah.go --
-package blah
-
-const Name = "Blah"
--- random.org@v1.2.3/go.mod --
-module random.org
-
-go 1.12
--- random.org@v1.2.3/blah/blah.go --
-package hello
-
-const Name = "Hello"
-`
-
-func TestModFileModification(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const untidyModule = `
--- a/go.mod --
-module mod.com
-
--- a/main.go --
-package main
-
-import "example.com/blah"
-
-func main() {
- println(blah.Name)
-}
-`
-
- runner := RunMultiple{
- {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(proxy))},
- }
-
- t.Run("basic", func(t *testing.T) {
- runner.Run(t, untidyModule, func(t *testing.T, env *Env) {
- // Open the file and make sure that the initial workspace load does not
- // modify the go.mod file.
- goModContent := env.ReadWorkspaceFile("a/go.mod")
- env.OpenFile("a/main.go")
- env.Await(
- env.DiagnosticAtRegexp("a/main.go", "\"example.com/blah\""),
- )
- if got := env.ReadWorkspaceFile("a/go.mod"); got != goModContent {
- t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(t, goModContent, got))
- }
- // Save the buffer, which will format and organize imports.
- // Confirm that the go.mod file still does not change.
- env.SaveBuffer("a/main.go")
- env.Await(
- env.DiagnosticAtRegexp("a/main.go", "\"example.com/blah\""),
- )
- if got := env.ReadWorkspaceFile("a/go.mod"); got != goModContent {
- t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(t, goModContent, got))
- }
- })
- })
-
- // Reproduce golang/go#40269 by deleting and recreating main.go.
- t.Run("delete main.go", func(t *testing.T) {
- t.Skip("This test will be flaky until golang/go#40269 is resolved.")
-
- runner.Run(t, untidyModule, func(t *testing.T, env *Env) {
- goModContent := env.ReadWorkspaceFile("a/go.mod")
- mainContent := env.ReadWorkspaceFile("a/main.go")
- env.OpenFile("a/main.go")
- env.SaveBuffer("a/main.go")
-
- env.RemoveWorkspaceFile("a/main.go")
- env.Await(
- env.DoneWithOpen(),
- env.DoneWithSave(),
- env.DoneWithChangeWatchedFiles(),
- )
-
- env.WriteWorkspaceFile("main.go", mainContent)
- env.Await(
- env.DiagnosticAtRegexp("main.go", "\"example.com/blah\""),
- )
- if got := env.ReadWorkspaceFile("go.mod"); got != goModContent {
- t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(t, goModContent, got))
- }
- })
- })
-}
-
-func TestGoGetFix(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
- const mod = `
--- a/go.mod --
-module mod.com
-
-go 1.12
-
--- a/main.go --
-package main
-
-import "example.com/blah"
-
-var _ = blah.Name
-`
-
- const want = `module mod.com
-
-go 1.12
-
-require example.com v1.2.3
-`
-
- RunMultiple{
- {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(proxy))},
- }.Run(t, mod, func(t *testing.T, env *Env) {
- if strings.Contains(t.Name(), "workspace_module") {
- t.Skip("workspace module mode doesn't set -mod=readonly")
- }
- env.OpenFile("a/main.go")
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexp("a/main.go", `"example.com/blah"`),
- ReadDiagnostics("a/main.go", &d),
- ),
- )
- var goGetDiag protocol.Diagnostic
- for _, diag := range d.Diagnostics {
- if strings.Contains(diag.Message, "could not import") {
- goGetDiag = diag
- }
- }
- env.ApplyQuickFixes("a/main.go", []protocol.Diagnostic{goGetDiag})
- if got := env.ReadWorkspaceFile("a/go.mod"); got != want {
- t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-// Tests that multiple missing dependencies gives good single fixes.
-func TestMissingDependencyFixes(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
- const mod = `
--- a/go.mod --
-module mod.com
-
-go 1.12
-
--- a/main.go --
-package main
-
-import "example.com/blah"
-import "random.org/blah"
-
-var _, _ = blah.Name, hello.Name
-`
-
- const want = `module mod.com
-
-go 1.12
-
-require random.org v1.2.3
-`
-
- RunMultiple{
- {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(proxy))},
- }.Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("a/main.go")
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexp("a/main.go", `"random.org/blah"`),
- ReadDiagnostics("a/main.go", &d),
- ),
- )
- var randomDiag protocol.Diagnostic
- for _, diag := range d.Diagnostics {
- if strings.Contains(diag.Message, "random.org") {
- randomDiag = diag
- }
- }
- env.ApplyQuickFixes("a/main.go", []protocol.Diagnostic{randomDiag})
- if got := env.ReadWorkspaceFile("a/go.mod"); got != want {
- t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-// Tests that multiple missing dependencies gives good single fixes.
-func TestMissingDependencyFixesWithGoWork(t *testing.T) {
- testenv.NeedsGo1Point(t, 18)
- const mod = `
--- go.work --
-go 1.18
-
-use (
- ./a
-)
--- a/go.mod --
-module mod.com
-
-go 1.12
-
--- a/main.go --
-package main
-
-import "example.com/blah"
-import "random.org/blah"
-
-var _, _ = blah.Name, hello.Name
-`
-
- const want = `module mod.com
-
-go 1.12
-
-require random.org v1.2.3
-`
-
- RunMultiple{
- {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(proxy))},
- }.Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("a/main.go")
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexp("a/main.go", `"random.org/blah"`),
- ReadDiagnostics("a/main.go", &d),
- ),
- )
- var randomDiag protocol.Diagnostic
- for _, diag := range d.Diagnostics {
- if strings.Contains(diag.Message, "random.org") {
- randomDiag = diag
- }
- }
- env.ApplyQuickFixes("a/main.go", []protocol.Diagnostic{randomDiag})
- if got := env.ReadWorkspaceFile("a/go.mod"); got != want {
- t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-func TestIndirectDependencyFix(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const mod = `
--- a/go.mod --
-module mod.com
-
-go 1.12
-
-require example.com v1.2.3 // indirect
--- a/go.sum --
-example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
--- a/main.go --
-package main
-
-import "example.com/blah"
-
-func main() {
- fmt.Println(blah.Name)
-`
- const want = `module mod.com
-
-go 1.12
-
-require example.com v1.2.3
-`
-
- RunMultiple{
- {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(proxy))},
- }.Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("a/go.mod")
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexp("a/go.mod", "// indirect"),
- ReadDiagnostics("a/go.mod", &d),
- ),
- )
- env.ApplyQuickFixes("a/go.mod", d.Diagnostics)
- if got := env.Editor.BufferText("a/go.mod"); got != want {
- t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-func TestUnusedDiag(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const proxy = `
--- example.com@v1.0.0/x.go --
-package pkg
-const X = 1
-`
- const files = `
--- a/go.mod --
-module mod.com
-go 1.14
-require example.com v1.0.0
--- a/go.sum --
-example.com v1.0.0 h1:38O7j5rEBajXk+Q5wzLbRN7KqMkSgEiN9NqcM1O2bBM=
-example.com v1.0.0/go.mod h1:vUsPMGpx9ZXXzECCOsOmYCW7npJTwuA16yl89n3Mgls=
--- a/main.go --
-package main
-func main() {}
-`
-
- const want = `module mod.com
-
-go 1.14
-`
-
- RunMultiple{
- {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(proxy))},
- }.Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("a/go.mod")
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexp("a/go.mod", `require example.com`),
- ReadDiagnostics("a/go.mod", &d),
- ),
- )
- env.ApplyQuickFixes("a/go.mod", d.Diagnostics)
- if got := env.Editor.BufferText("a/go.mod"); got != want {
- t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-// Test to reproduce golang/go#39041. It adds a new require to a go.mod file
-// that already has an unused require.
-func TestNewDepWithUnusedDep(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const proxy = `
--- github.com/esimov/caire@v1.2.5/go.mod --
-module github.com/esimov/caire
-
-go 1.12
--- github.com/esimov/caire@v1.2.5/caire.go --
-package caire
-
-func RemoveTempImage() {}
--- google.golang.org/protobuf@v1.20.0/go.mod --
-module google.golang.org/protobuf
-
-go 1.12
--- google.golang.org/protobuf@v1.20.0/hello/hello.go --
-package hello
-`
- const repro = `
--- a/go.mod --
-module mod.com
-
-go 1.14
-
-require google.golang.org/protobuf v1.20.0
--- a/go.sum --
-github.com/esimov/caire v1.2.5 h1:OcqDII/BYxcBYj3DuwDKjd+ANhRxRqLa2n69EGje7qw=
-github.com/esimov/caire v1.2.5/go.mod h1:mXnjRjg3+WUtuhfSC1rKRmdZU9vJZyS1ZWU0qSvJhK8=
-google.golang.org/protobuf v1.20.0 h1:y9T1vAtFKQg0faFNMOxJU7WuEqPWolVkjIkU6aI8qCY=
-google.golang.org/protobuf v1.20.0/go.mod h1:FcqsytGClbtLv1ot8NvsJHjBi0h22StKVP+K/j2liKA=
--- a/main.go --
-package main
-
-import (
- "github.com/esimov/caire"
-)
-
-func _() {
- caire.RemoveTempImage()
-}`
-
- RunMultiple{
- {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(proxy))},
- }.Run(t, repro, func(t *testing.T, env *Env) {
- env.OpenFile("a/main.go")
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexp("a/main.go", `"github.com/esimov/caire"`),
- ReadDiagnostics("a/main.go", &d),
- ),
- )
- env.ApplyQuickFixes("a/main.go", d.Diagnostics)
- want := `module mod.com
-
-go 1.14
-
-require (
- github.com/esimov/caire v1.2.5
- google.golang.org/protobuf v1.20.0
-)
-`
- if got := env.ReadWorkspaceFile("a/go.mod"); got != want {
- t.Fatalf("TestNewDepWithUnusedDep failed:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-// TODO: For this test to be effective, the sandbox's file watcher must respect
-// the file watching GlobPattern in the capability registration. See
-// golang/go#39384.
-func TestModuleChangesOnDisk(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const mod = `
--- a/go.mod --
-module mod.com
-
-go 1.12
-
-require example.com v1.2.3
--- a/go.sum --
-example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
--- a/main.go --
-package main
-
-func main() {
- fmt.Println(blah.Name)
-`
- RunMultiple{
- {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(proxy))},
- }.Run(t, mod, func(t *testing.T, env *Env) {
- env.Await(env.DiagnosticAtRegexp("a/go.mod", "require"))
- env.RunGoCommandInDir("a", "mod", "tidy")
- env.Await(
- EmptyDiagnostics("a/go.mod"),
- )
- })
-}
-
-// Tests golang/go#39784: a missing indirect dependency, necessary
-// due to blah@v2.0.0's incomplete go.mod file.
-func TestBadlyVersionedModule(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const proxy = `
--- example.com/blah/@v/v1.0.0.mod --
-module example.com
-
-go 1.12
--- example.com/blah@v1.0.0/blah.go --
-package blah
-
-const Name = "Blah"
--- example.com/blah/v2/@v/v2.0.0.mod --
-module example.com
-
-go 1.12
--- example.com/blah/v2@v2.0.0/blah.go --
-package blah
-
-import "example.com/blah"
-
-var V1Name = blah.Name
-const Name = "Blah"
-`
- const files = `
--- a/go.mod --
-module mod.com
-
-go 1.12
-
-require example.com/blah/v2 v2.0.0
--- a/go.sum --
-example.com/blah v1.0.0 h1:kGPlWJbMsn1P31H9xp/q2mYI32cxLnCvauHN0AVaHnc=
-example.com/blah v1.0.0/go.mod h1:PZUQaGFeVjyDmAE8ywmLbmDn3fj4Ws8epg4oLuDzW3M=
-example.com/blah/v2 v2.0.0 h1:DNPsFPkKtTdxclRheaMCiYAoYizp6PuBzO0OmLOO0pY=
-example.com/blah/v2 v2.0.0/go.mod h1:UZiKbTwobERo/hrqFLvIQlJwQZQGxWMVY4xere8mj7w=
--- a/main.go --
-package main
-
-import "example.com/blah/v2"
-
-var _ = blah.Name
-`
- RunMultiple{
- {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(proxy))},
- }.Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("a/main.go")
- env.OpenFile("a/go.mod")
- env.Await(
- // We would like for the error to appear in the v2 module, but
- // as of writing non-workspace packages are not diagnosed.
- env.DiagnosticAtRegexpWithMessage("a/main.go", `"example.com/blah/v2"`, "cannot find module providing"),
- env.DiagnosticAtRegexpWithMessage("a/go.mod", `require example.com/blah/v2`, "cannot find module providing"),
- )
- env.ApplyQuickFixes("a/go.mod", env.DiagnosticsFor("a/go.mod").Diagnostics)
- const want = `module mod.com
-
-go 1.12
-
-require (
- example.com/blah v1.0.0 // indirect
- example.com/blah/v2 v2.0.0
-)
-`
- env.SaveBuffer("a/go.mod")
- env.Await(EmptyDiagnostics("a/main.go"))
- if got := env.Editor.BufferText("a/go.mod"); got != want {
- t.Fatalf("suggested fixes failed:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-// Reproduces golang/go#38232.
-func TestUnknownRevision(t *testing.T) {
- if runtime.GOOS == "plan9" {
- t.Skipf("skipping test that fails for unknown reasons on plan9; see https://go.dev/issue/50477")
- }
-
- testenv.NeedsGo1Point(t, 14)
-
- const unknown = `
--- a/go.mod --
-module mod.com
-
-require (
- example.com v1.2.2
-)
--- a/main.go --
-package main
-
-import "example.com/blah"
-
-func main() {
- var x = blah.Name
-}
-`
-
- runner := RunMultiple{
- {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(proxy))},
- }
- // Start from a bad state/bad IWL, and confirm that we recover.
- t.Run("bad", func(t *testing.T) {
- runner.Run(t, unknown, func(t *testing.T, env *Env) {
- env.OpenFile("a/go.mod")
- env.Await(
- env.DiagnosticAtRegexp("a/go.mod", "example.com v1.2.2"),
- )
- env.RegexpReplace("a/go.mod", "v1.2.2", "v1.2.3")
- env.SaveBuffer("a/go.mod") // Save to trigger diagnostics.
-
- d := protocol.PublishDiagnosticsParams{}
- env.Await(
- OnceMet(
- // Make sure the diagnostic mentions the new version -- the old diagnostic is in the same place.
- env.DiagnosticAtRegexpWithMessage("a/go.mod", "example.com v1.2.3", "example.com@v1.2.3"),
- ReadDiagnostics("a/go.mod", &d),
- ),
- )
- qfs := env.GetQuickFixes("a/go.mod", d.Diagnostics)
- if len(qfs) == 0 {
- t.Fatalf("got 0 code actions to fix %v, wanted at least 1", d.Diagnostics)
- }
- env.ApplyCodeAction(qfs[0]) // Arbitrarily pick a single fix to apply. Applying all of them seems to cause trouble in this particular test.
- env.SaveBuffer("a/go.mod") // Save to trigger diagnostics.
- env.Await(
- EmptyDiagnostics("a/go.mod"),
- env.DiagnosticAtRegexp("a/main.go", "x = "),
- )
- })
- })
-
- const known = `
--- a/go.mod --
-module mod.com
-
-require (
- example.com v1.2.3
-)
--- a/go.sum --
-example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
--- a/main.go --
-package main
-
-import "example.com/blah"
-
-func main() {
- var x = blah.Name
-}
-`
- // Start from a good state, transform to a bad state, and confirm that we
- // still recover.
- t.Run("good", func(t *testing.T) {
- runner.Run(t, known, func(t *testing.T, env *Env) {
- env.OpenFile("a/go.mod")
- env.Await(
- env.DiagnosticAtRegexp("a/main.go", "x = "),
- )
- env.RegexpReplace("a/go.mod", "v1.2.3", "v1.2.2")
- env.Editor.SaveBuffer(env.Ctx, "a/go.mod") // go.mod changes must be on disk
- env.Await(
- env.DiagnosticAtRegexp("a/go.mod", "example.com v1.2.2"),
- )
- env.RegexpReplace("a/go.mod", "v1.2.2", "v1.2.3")
- env.Editor.SaveBuffer(env.Ctx, "a/go.mod") // go.mod changes must be on disk
- env.Await(
- env.DiagnosticAtRegexp("a/main.go", "x = "),
- )
- })
- })
-}
-
-// Confirm that an error in an indirect dependency of a requirement is surfaced
-// as a diagnostic in the go.mod file.
-func TestErrorInIndirectDependency(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const badProxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
-
-require random.org v1.2.3 // indirect
--- example.com@v1.2.3/blah/blah.go --
-package blah
-
-const Name = "Blah"
--- random.org@v1.2.3/go.mod --
-module bob.org
-
-go 1.12
--- random.org@v1.2.3/blah/blah.go --
-package hello
-
-const Name = "Hello"
-`
- const module = `
--- a/go.mod --
-module mod.com
-
-go 1.14
-
-require example.com v1.2.3
--- a/main.go --
-package main
-
-import "example.com/blah"
-
-func main() {
- println(blah.Name)
-}
-`
- RunMultiple{
- {"default", WithOptions(ProxyFiles(badProxy), WorkspaceFolders("a"))},
- {"nested", WithOptions(ProxyFiles(badProxy))},
- }.Run(t, module, func(t *testing.T, env *Env) {
- env.OpenFile("a/go.mod")
- env.Await(
- env.DiagnosticAtRegexp("a/go.mod", "require example.com v1.2.3"),
- )
- })
-}
-
-// A copy of govim's config_set_env_goflags_mod_readonly test.
-func TestGovimModReadonly(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.13
--- main.go --
-package main
-
-import "example.com/blah"
-
-func main() {
- println(blah.Name)
-}
-`
- WithOptions(
- EditorConfig{
- Env: map[string]string{
- "GOFLAGS": "-mod=readonly",
- },
- },
- ProxyFiles(proxy),
- Modes(Singleton),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- original := env.ReadWorkspaceFile("go.mod")
- env.Await(
- env.DiagnosticAtRegexp("main.go", `"example.com/blah"`),
- )
- got := env.ReadWorkspaceFile("go.mod")
- if got != original {
- t.Fatalf("go.mod file modified:\n%s", tests.Diff(t, original, got))
- }
- env.RunGoCommand("get", "example.com/blah@v1.2.3")
- env.RunGoCommand("mod", "tidy")
- env.Await(
- EmptyDiagnostics("main.go"),
- )
- })
-}
-
-func TestMultiModuleModDiagnostics(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const mod = `
--- a/go.mod --
-module moda.com
-
-go 1.14
-
-require (
- example.com v1.2.3
-)
--- a/go.sum --
-example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
--- a/main.go --
-package main
-
-func main() {}
--- b/go.mod --
-module modb.com
-
-require example.com v1.2.3
-
-go 1.14
--- b/main.go --
-package main
-
-import "example.com/blah"
-
-func main() {
- blah.SaySomething()
-}
-`
- WithOptions(
- ProxyFiles(workspaceProxy),
- Modes(Experimental),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexpWithMessage("a/go.mod", "example.com v1.2.3", "is not used"),
- )
- })
-}
-
-func TestModTidyWithBuildTags(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.14
--- main.go --
-// +build bob
-
-package main
-
-import "example.com/blah"
-
-func main() {
- blah.SaySomething()
-}
-`
- WithOptions(
- ProxyFiles(workspaceProxy),
- EditorConfig{
- BuildFlags: []string{"-tags", "bob"},
- },
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("main.go", `"example.com/blah"`),
- )
- })
-}
-
-func TestModTypoDiagnostic(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-func main() {}
-`
- Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("go.mod")
- env.RegexpReplace("go.mod", "module", "modul")
- env.Await(
- env.DiagnosticAtRegexp("go.mod", "modul"),
- )
- })
-}
-
-func TestSumUpdateFixesDiagnostics(t *testing.T) {
- t.Skipf("Skipping known-flaky test; see https://go.dev/issue/51352.")
-
- testenv.NeedsGo1Point(t, 14)
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
-
-require (
- example.com v1.2.3
-)
--- go.sum --
--- main.go --
-package main
-
-import (
- "example.com/blah"
-)
-
-func main() {
- println(blah.Name)
-}
-`
- WithOptions(
- ProxyFiles(workspaceProxy),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- d := &protocol.PublishDiagnosticsParams{}
- env.OpenFile("go.mod")
- env.Await(
- OnceMet(
- env.GoSumDiagnostic("go.mod", `example.com v1.2.3`),
- ReadDiagnostics("go.mod", d),
- ),
- )
- env.ApplyQuickFixes("go.mod", d.Diagnostics)
- env.SaveBuffer("go.mod") // Save to trigger diagnostics.
- env.Await(
- EmptyDiagnostics("go.mod"),
- )
- })
-}
-
-// This test confirms that editing a go.mod file only causes metadata
-// to be invalidated when it's saved.
-func TestGoModInvalidatesOnSave(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- main.go --
-package main
-
-func main() {
- hello()
-}
--- hello.go --
-package main
-
-func hello() {}
-`
- WithOptions(
- // TODO(rFindley) this doesn't work in multi-module workspace mode, because
- // it keeps around the last parsing modfile. Update this test to also
- // exercise the workspace module.
- Modes(Singleton),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("go.mod")
- env.Await(env.DoneWithOpen())
- env.RegexpReplace("go.mod", "module", "modul")
- // Confirm that we still have metadata with only on-disk edits.
- env.OpenFile("main.go")
- file, _ := env.GoToDefinition("main.go", env.RegexpSearch("main.go", "hello"))
- if filepath.Base(file) != "hello.go" {
- t.Fatalf("expected definition in hello.go, got %s", file)
- }
- // Confirm that we no longer have metadata when the file is saved.
- env.SaveBufferWithoutActions("go.mod")
- _, _, err := env.Editor.GoToDefinition(env.Ctx, "main.go", env.RegexpSearch("main.go", "hello"))
- if err == nil {
- t.Fatalf("expected error, got none")
- }
- })
-}
-
-func TestRemoveUnusedDependency(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const proxy = `
--- hasdep.com@v1.2.3/go.mod --
-module hasdep.com
-
-go 1.12
-
-require example.com v1.2.3
--- hasdep.com@v1.2.3/a/a.go --
-package a
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.3/blah/blah.go --
-package blah
-
-const Name = "Blah"
--- random.com@v1.2.3/go.mod --
-module random.com
-
-go 1.12
--- random.com@v1.2.3/blah/blah.go --
-package blah
-
-const Name = "Blah"
-`
- t.Run("almost tidied", func(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
-
-require hasdep.com v1.2.3
--- go.sum --
-example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
-hasdep.com v1.2.3 h1:00y+N5oD+SpKoqV1zP2VOPawcW65Zb9NebANY3GSzGI=
-hasdep.com v1.2.3/go.mod h1:ePVZOlez+KZEOejfLPGL2n4i8qiAjrkhQZ4wcImqAes=
--- main.go --
-package main
-
-func main() {}
-`
- WithOptions(
- ProxyFiles(proxy),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("go.mod")
- d := &protocol.PublishDiagnosticsParams{}
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexp("go.mod", "require hasdep.com v1.2.3"),
- ReadDiagnostics("go.mod", d),
- ),
- )
- const want = `module mod.com
-
-go 1.12
-`
- env.ApplyQuickFixes("go.mod", d.Diagnostics)
- if got := env.Editor.BufferText("go.mod"); got != want {
- t.Fatalf("unexpected content in go.mod:\n%s", tests.Diff(t, want, got))
- }
- })
- })
-
- t.Run("not tidied", func(t *testing.T) {
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
-
-require hasdep.com v1.2.3
-require random.com v1.2.3
--- go.sum --
-example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
-hasdep.com v1.2.3 h1:00y+N5oD+SpKoqV1zP2VOPawcW65Zb9NebANY3GSzGI=
-hasdep.com v1.2.3/go.mod h1:ePVZOlez+KZEOejfLPGL2n4i8qiAjrkhQZ4wcImqAes=
-random.com v1.2.3 h1:PzYTykzqqH6+qU0dIgh9iPFbfb4Mm8zNBjWWreRKtx0=
-random.com v1.2.3/go.mod h1:8EGj+8a4Hw1clAp8vbaeHAsKE4sbm536FP7nKyXO+qQ=
--- main.go --
-package main
-
-func main() {}
-`
- WithOptions(
- ProxyFiles(proxy),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- d := &protocol.PublishDiagnosticsParams{}
- env.OpenFile("go.mod")
- pos := env.RegexpSearch("go.mod", "require hasdep.com v1.2.3")
- env.Await(
- OnceMet(
- DiagnosticAt("go.mod", pos.Line, pos.Column),
- ReadDiagnostics("go.mod", d),
- ),
- )
- const want = `module mod.com
-
-go 1.12
-
-require random.com v1.2.3
-`
- var diagnostics []protocol.Diagnostic
- for _, d := range d.Diagnostics {
- if d.Range.Start.Line != uint32(pos.Line) {
- continue
- }
- diagnostics = append(diagnostics, d)
- }
- env.ApplyQuickFixes("go.mod", diagnostics)
- if got := env.Editor.BufferText("go.mod"); got != want {
- t.Fatalf("unexpected content in go.mod:\n%s", tests.Diff(t, want, got))
- }
- })
- })
-}
-
-func TestSumUpdateQuickFix(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
-
-require (
- example.com v1.2.3
-)
--- go.sum --
--- main.go --
-package main
-
-import (
- "example.com/blah"
-)
-
-func main() {
- blah.Hello()
-}
-`
- WithOptions(
- ProxyFiles(workspaceProxy),
- Modes(Singleton),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("go.mod")
- params := &protocol.PublishDiagnosticsParams{}
- env.Await(
- OnceMet(
- env.GoSumDiagnostic("go.mod", "example.com"),
- ReadDiagnostics("go.mod", params),
- ),
- )
- env.ApplyQuickFixes("go.mod", params.Diagnostics)
- const want = `example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
-`
- if got := env.ReadWorkspaceFile("go.sum"); got != want {
- t.Fatalf("unexpected go.sum contents:\n%s", tests.Diff(t, want, got))
- }
- })
-}
-
-func TestDownloadDeps(t *testing.T) {
- testenv.NeedsGo1Point(t, 14)
-
- const proxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
-
-require random.org v1.2.3
--- example.com@v1.2.3/blah/blah.go --
-package blah
-
-import "random.org/bye"
-
-func SaySomething() {
- bye.Goodbye()
-}
--- random.org@v1.2.3/go.mod --
-module random.org
-
-go 1.12
--- random.org@v1.2.3/bye/bye.go --
-package bye
-
-func Goodbye() {
- println("Bye")
-}
-`
-
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
--- go.sum --
--- main.go --
-package main
-
-import (
- "example.com/blah"
-)
-
-func main() {
- blah.SaySomething()
-}
-`
- WithOptions(
- ProxyFiles(proxy),
- Modes(Singleton),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.OpenFile("main.go")
- d := &protocol.PublishDiagnosticsParams{}
- env.Await(
- env.DiagnosticAtRegexpWithMessage("main.go", `"example.com/blah"`, `could not import example.com/blah (no required module provides package "example.com/blah")`),
- ReadDiagnostics("main.go", d),
- )
- env.ApplyQuickFixes("main.go", d.Diagnostics)
- env.Await(
- EmptyDiagnostics("main.go"),
- NoDiagnostics("go.mod"),
- )
- })
-}
-
-func TestInvalidGoVersion(t *testing.T) {
- testenv.NeedsGo1Point(t, 14) // Times out on 1.13 for reasons unclear. Not worth worrying about.
- const files = `
--- go.mod --
-module mod.com
-
-go foo
--- main.go --
-package main
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.Await(env.DiagnosticAtRegexpWithMessage("go.mod", `go foo`, "invalid go version"))
- env.WriteWorkspaceFile("go.mod", "module mod.com \n\ngo 1.12\n")
- env.Await(EmptyDiagnostics("go.mod"))
- })
-}
diff -urN a/gopls/internal/regtest/template/template_test.go b/gopls/internal/regtest/template/template_test.go
--- a/gopls/internal/regtest/template/template_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/template/template_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,232 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package template
-
-import (
- "strings"
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- "golang.org/x/tools/internal/lsp/protocol"
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestMain(m *testing.M) {
- bug.PanicOnBugs = true
- Main(m, hooks.Options)
-}
-
-func TestMultilineTokens(t *testing.T) {
- // 51731: panic: runtime error: slice bounds out of range [38:3]
- const files = `
--- go.mod --
-module mod.com
-
-go 1.17
--- hi.tmpl --
-{{if (foÜx .X.Y)}}😀{{$A :=
- "hi"
- }}{{.Z $A}}{{else}}
-{{$A.X 12}}
-{{foo (.X.Y) 23 ($A.Z)}}
-{{end}}
-`
- WithOptions(
- EditorConfig{
- Settings: map[string]interface{}{
- "templateExtensions": []string{"tmpl"},
- "semanticTokens": true,
- },
- },
- ).Run(t, files, func(t *testing.T, env *Env) {
- var p protocol.SemanticTokensParams
- p.TextDocument.URI = env.Sandbox.Workdir.URI("hi.tmpl")
- toks, err := env.Editor.Server.SemanticTokensFull(env.Ctx, &p)
- if err != nil {
- t.Errorf("semantic token failed: %v", err)
- }
- if toks == nil || len(toks.Data) == 0 {
- t.Errorf("got no semantic tokens")
- }
- })
-}
-
-func TestTemplatesFromExtensions(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- hello.tmpl --
-{{range .Planets}}
-Hello {{}} <-- missing body
-{{end}}
-`
- WithOptions(
- EditorConfig{
- Settings: map[string]interface{}{
- "templateExtensions": []string{"tmpl"},
- "semanticTokens": true,
- },
- },
- ).Run(t, files, func(t *testing.T, env *Env) {
- // TODO: can we move this diagnostic onto {{}}?
- env.Await(env.DiagnosticAtRegexp("hello.tmpl", "()Hello {{}}"))
- d := env.DiagnosticsFor("hello.tmpl").Diagnostics // issue 50786: check for Source
- if len(d) != 1 {
- t.Errorf("expected 1 diagnostic, got %d", len(d))
- return
- }
- if d[0].Source != "template" {
- t.Errorf("expected Source 'template', got %q", d[0].Source)
- }
- // issue 50801 (even broken templates could return some semantic tokens)
- var p protocol.SemanticTokensParams
- p.TextDocument.URI = env.Sandbox.Workdir.URI("hello.tmpl")
- toks, err := env.Editor.Server.SemanticTokensFull(env.Ctx, &p)
- if err != nil {
- t.Errorf("semantic token failed: %v", err)
- }
- if toks == nil || len(toks.Data) == 0 {
- t.Errorf("got no semantic tokens")
- }
-
- env.WriteWorkspaceFile("hello.tmpl", "{{range .Planets}}\nHello {{.}}\n{{end}}")
- env.Await(EmptyDiagnostics("hello.tmpl"))
- })
-}
-
-func TestTemplatesObserveDirectoryFilters(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- a/a.tmpl --
-A {{}} <-- missing body
--- b/b.tmpl --
-B {{}} <-- missing body
-`
-
- WithOptions(
- EditorConfig{
- Settings: map[string]interface{}{
- "templateExtensions": []string{"tmpl"},
- },
- DirectoryFilters: []string{"-b"},
- },
- ).Run(t, files, func(t *testing.T, env *Env) {
- env.Await(
- OnceMet(env.DiagnosticAtRegexp("a/a.tmpl", "()A")),
- NoDiagnostics("b/b.tmpl"),
- )
- })
-}
-
-func TestTemplatesFromLangID(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
-`
-
- Run(t, files, func(t *testing.T, env *Env) {
- env.CreateBuffer("hello.tmpl", "")
- env.Await(
- OnceMet(
- env.DoneWithOpen(),
- NoDiagnostics("hello.tmpl"), // Don't get spurious errors for empty templates.
- ),
- )
- env.SetBufferContent("hello.tmpl", "{{range .Planets}}\nHello {{}}\n{{end}}")
- env.Await(env.DiagnosticAtRegexp("hello.tmpl", "()Hello {{}}"))
- env.RegexpReplace("hello.tmpl", "{{}}", "{{.}}")
- env.Await(EmptyOrNoDiagnostics("hello.tmpl"))
- })
-}
-
-func TestClosingTemplatesMakesDiagnosticsDisappear(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- hello.tmpl --
-{{range .Planets}}
-Hello {{}} <-- missing body
-{{end}}
-`
-
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("hello.tmpl")
- env.Await(env.DiagnosticAtRegexp("hello.tmpl", "()Hello {{}}"))
- // Since we don't have templateExtensions configured, closing hello.tmpl
- // should make its diagnostics disappear.
- env.CloseBuffer("hello.tmpl")
- env.Await(EmptyDiagnostics("hello.tmpl"))
- })
-}
-
-func TestMultipleSuffixes(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- b.gotmpl --
-{{define "A"}}goo{{end}}
--- a.tmpl --
-{{template "A"}}
-`
-
- WithOptions(
- EditorConfig{
- Settings: map[string]interface{}{
- "templateExtensions": []string{"tmpl", "gotmpl"},
- },
- },
- ).Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("a.tmpl")
- x := env.RegexpSearch("a.tmpl", `A`)
- file, pos := env.GoToDefinition("a.tmpl", x)
- refs := env.References(file, pos)
- if len(refs) != 2 {
- t.Fatalf("got %v reference(s), want 2", len(refs))
- }
- // make sure we got one from b.gotmpl
- want := env.Sandbox.Workdir.URI("b.gotmpl")
- if refs[0].URI != want && refs[1].URI != want {
- t.Errorf("failed to find reference to %s", shorten(want))
- for i, r := range refs {
- t.Logf("%d: URI:%s %v", i, shorten(r.URI), r.Range)
- }
- }
-
- content, npos := env.Hover(file, pos)
- if pos != npos {
- t.Errorf("pos? got %v, wanted %v", npos, pos)
- }
- if content.Value != "template A defined" {
- t.Errorf("got %s, wanted 'template A defined", content.Value)
- }
- })
-}
-
-// shorten long URIs
-func shorten(fn protocol.DocumentURI) string {
- if len(fn) <= 20 {
- return string(fn)
- }
- pieces := strings.Split(string(fn), "/")
- if len(pieces) < 2 {
- return string(fn)
- }
- j := len(pieces)
- return pieces[j-2] + "/" + pieces[j-1]
-}
-
-// Hover needs tests
diff -urN a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/watch/watch_test.go
--- a/gopls/internal/regtest/watch/watch_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/watch/watch_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,768 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package regtest
-
-import (
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- . "golang.org/x/tools/internal/lsp/regtest"
-
- "golang.org/x/tools/internal/lsp/fake"
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestMain(m *testing.M) {
- bug.PanicOnBugs = true
- Main(m, hooks.Options)
-}
-
-func TestEditFile(t *testing.T) {
- const pkg = `
--- go.mod --
-module mod.com
-
-go 1.14
--- a/a.go --
-package a
-
-func _() {
- var x int
-}
-`
- // Edit the file when it's *not open* in the workspace, and check that
- // diagnostics are updated.
- t.Run("unopened", func(t *testing.T) {
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "x"),
- )
- env.WriteWorkspaceFile("a/a.go", `package a; func _() {};`)
- env.Await(
- EmptyDiagnostics("a/a.go"),
- )
- })
- })
-
- // Edit the file when it *is open* in the workspace, and check that
- // diagnostics are *not* updated.
- t.Run("opened", func(t *testing.T) {
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- // Insert a trivial edit so that we don't automatically update the buffer
- // (see CL 267577).
- env.EditBuffer("a/a.go", fake.NewEdit(0, 0, 0, 0, " "))
- env.Await(env.DoneWithOpen())
- env.WriteWorkspaceFile("a/a.go", `package a; func _() {};`)
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- env.DiagnosticAtRegexp("a/a.go", "x"),
- ))
- })
- })
-}
-
-// Edit a dependency on disk and expect a new diagnostic.
-func TestEditDependency(t *testing.T) {
- const pkg = `
--- go.mod --
-module mod.com
-
-go 1.14
--- b/b.go --
-package b
-
-func B() int { return 0 }
--- a/a.go --
-package a
-
-import (
- "mod.com/b"
-)
-
-func _() {
- _ = b.B()
-}
-`
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.Await(env.DoneWithOpen())
- env.WriteWorkspaceFile("b/b.go", `package b; func B() {};`)
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "b.B"),
- )
- })
-}
-
-// Edit both the current file and one of its dependencies on disk and
-// expect diagnostic changes.
-func TestEditFileAndDependency(t *testing.T) {
- const pkg = `
--- go.mod --
-module mod.com
-
-go 1.14
--- b/b.go --
-package b
-
-func B() int { return 0 }
--- a/a.go --
-package a
-
-import (
- "mod.com/b"
-)
-
-func _() {
- var x int
- _ = b.B()
-}
-`
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "x"),
- )
- env.WriteWorkspaceFiles(map[string]string{
- "b/b.go": `package b; func B() {};`,
- "a/a.go": `package a
-
-import "mod.com/b"
-
-func _() {
- b.B()
-}`,
- })
- env.Await(
- EmptyDiagnostics("a/a.go"),
- NoDiagnostics("b/b.go"),
- )
- })
-}
-
-// Delete a dependency and expect a new diagnostic.
-func TestDeleteDependency(t *testing.T) {
- const pkg = `
--- go.mod --
-module mod.com
-
-go 1.14
--- b/b.go --
-package b
-
-func B() int { return 0 }
--- a/a.go --
-package a
-
-import (
- "mod.com/b"
-)
-
-func _() {
- _ = b.B()
-}
-`
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.Await(env.DoneWithOpen())
- env.RemoveWorkspaceFile("b/b.go")
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "\"mod.com/b\""),
- )
- })
-}
-
-// Create a dependency on disk and expect the diagnostic to go away.
-func TestCreateDependency(t *testing.T) {
- const missing = `
--- go.mod --
-module mod.com
-
-go 1.14
--- b/b.go --
-package b
-
-func B() int { return 0 }
--- a/a.go --
-package a
-
-import (
- "mod.com/c"
-)
-
-func _() {
- c.C()
-}
-`
- Run(t, missing, func(t *testing.T, env *Env) {
- t.Skip("the initial workspace load fails and never retries")
-
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "\"mod.com/c\""),
- )
- env.WriteWorkspaceFile("c/c.go", `package c; func C() {};`)
- env.Await(
- EmptyDiagnostics("c/c.go"),
- )
- })
-}
-
-// Create a new dependency and add it to the file on disk.
-// This is similar to what might happen if you switch branches.
-func TestCreateAndAddDependency(t *testing.T) {
- const original = `
--- go.mod --
-module mod.com
-
-go 1.14
--- a/a.go --
-package a
-
-func _() {}
-`
- Run(t, original, func(t *testing.T, env *Env) {
- env.WriteWorkspaceFile("c/c.go", `package c; func C() {};`)
- env.WriteWorkspaceFile("a/a.go", `package a; import "mod.com/c"; func _() { c.C() }`)
- env.Await(
- NoDiagnostics("a/a.go"),
- )
- })
-}
-
-// Create a new file that defines a new symbol, in the same package.
-func TestCreateFile(t *testing.T) {
- const pkg = `
--- go.mod --
-module mod.com
-
-go 1.14
--- a/a.go --
-package a
-
-func _() {
- hello()
-}
-`
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "hello"),
- )
- env.WriteWorkspaceFile("a/a2.go", `package a; func hello() {};`)
- env.Await(
- EmptyDiagnostics("a/a.go"),
- )
- })
-}
-
-// Add a new method to an interface and implement it.
-// Inspired by the structure of internal/lsp/source and internal/lsp/cache.
-func TestCreateImplementation(t *testing.T) {
- const pkg = `
--- go.mod --
-module mod.com
-
-go 1.14
--- b/b.go --
-package b
-
-type B interface{
- Hello() string
-}
-
-func SayHello(bee B) {
- println(bee.Hello())
-}
--- a/a.go --
-package a
-
-import "mod.com/b"
-
-type X struct {}
-
-func (_ X) Hello() string {
- return ""
-}
-
-func _() {
- x := X{}
- b.SayHello(x)
-}
-`
- const newMethod = `package b
-type B interface{
- Hello() string
- Bye() string
-}
-
-func SayHello(bee B) {
- println(bee.Hello())
-}`
- const implementation = `package a
-
-import "mod.com/b"
-
-type X struct {}
-
-func (_ X) Hello() string {
- return ""
-}
-
-func (_ X) Bye() string {
- return ""
-}
-
-func _() {
- x := X{}
- b.SayHello(x)
-}`
-
- // Add the new method before the implementation. Expect diagnostics.
- t.Run("method before implementation", func(t *testing.T) {
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.WriteWorkspaceFile("b/b.go", newMethod)
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- DiagnosticAt("a/a.go", 12, 12),
- ),
- )
- env.WriteWorkspaceFile("a/a.go", implementation)
- env.Await(
- EmptyDiagnostics("a/a.go"),
- )
- })
- })
- // Add the new implementation before the new method. Expect no diagnostics.
- t.Run("implementation before method", func(t *testing.T) {
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.WriteWorkspaceFile("a/a.go", implementation)
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- NoDiagnostics("a/a.go"),
- ),
- )
- env.WriteWorkspaceFile("b/b.go", newMethod)
- env.Await(
- NoDiagnostics("a/a.go"),
- )
- })
- })
- // Add both simultaneously. Expect no diagnostics.
- t.Run("implementation and method simultaneously", func(t *testing.T) {
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.WriteWorkspaceFiles(map[string]string{
- "a/a.go": implementation,
- "b/b.go": newMethod,
- })
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- NoDiagnostics("a/a.go"),
- ),
- NoDiagnostics("b/b.go"),
- )
- })
- })
-}
-
-// Tests golang/go#38498. Delete a file and then force a reload.
-// Assert that we no longer try to load the file.
-func TestDeleteFiles(t *testing.T) {
- testenv.NeedsGo1Point(t, 13) // Poor overlay support causes problems on 1.12.
- const pkg = `
--- go.mod --
-module mod.com
-
-go 1.14
--- a/a.go --
-package a
-
-func _() {
- var _ int
-}
--- a/a_unneeded.go --
-package a
-`
- t.Run("close then delete", func(t *testing.T) {
- WithOptions(EditorConfig{
- VerboseOutput: true,
- }).Run(t, pkg, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.OpenFile("a/a_unneeded.go")
- env.Await(
- OnceMet(
- env.DoneWithOpen(),
- LogMatching(protocol.Info, "a_unneeded.go", 1, false),
- ),
- )
-
- // Close and delete the open file, mimicking what an editor would do.
- env.CloseBuffer("a/a_unneeded.go")
- env.RemoveWorkspaceFile("a/a_unneeded.go")
- env.RegexpReplace("a/a.go", "var _ int", "fmt.Println(\"\")")
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "fmt"),
- )
- env.SaveBuffer("a/a.go")
- env.Await(
- OnceMet(
- env.DoneWithSave(),
- // There should only be one log message containing
- // a_unneeded.go, from the initial workspace load, which we
- // check for earlier. If there are more, there's a bug.
- LogMatching(protocol.Info, "a_unneeded.go", 1, false),
- ),
- EmptyDiagnostics("a/a.go"),
- )
- })
- })
-
- t.Run("delete then close", func(t *testing.T) {
- WithOptions(
- EditorConfig{VerboseOutput: true},
- ).Run(t, pkg, func(t *testing.T, env *Env) {
- env.OpenFile("a/a.go")
- env.OpenFile("a/a_unneeded.go")
- env.Await(
- OnceMet(
- env.DoneWithOpen(),
- LogMatching(protocol.Info, "a_unneeded.go", 1, false),
- ),
- )
-
- // Delete and then close the file.
- env.RemoveWorkspaceFile("a/a_unneeded.go")
- env.CloseBuffer("a/a_unneeded.go")
- env.RegexpReplace("a/a.go", "var _ int", "fmt.Println(\"\")")
- env.Await(
- env.DiagnosticAtRegexp("a/a.go", "fmt"),
- )
- env.SaveBuffer("a/a.go")
- env.Await(
- OnceMet(
- env.DoneWithSave(),
- // There should only be one log message containing
- // a_unneeded.go, from the initial workspace load, which we
- // check for earlier. If there are more, there's a bug.
- LogMatching(protocol.Info, "a_unneeded.go", 1, false),
- ),
- EmptyDiagnostics("a/a.go"),
- )
- })
- })
-}
-
-// This change reproduces the behavior of switching branches, with multiple
-// files being created and deleted. The key change here is the movement of a
-// symbol from one file to another in a given package through a deletion and
-// creation. To reproduce an issue with metadata invalidation in batched
-// changes, the last change in the batch is an on-disk file change that doesn't
-// require metadata invalidation.
-func TestMoveSymbol(t *testing.T) {
- const pkg = `
--- go.mod --
-module mod.com
-
-go 1.14
--- main.go --
-package main
-
-import "mod.com/a"
-
-func main() {
- var x int
- x = a.Hello
- println(x)
-}
--- a/a1.go --
-package a
-
-var Hello int
--- a/a2.go --
-package a
-
-func _() {}
-`
- Run(t, pkg, func(t *testing.T, env *Env) {
- env.ChangeFilesOnDisk([]fake.FileEvent{
- {
- Path: "a/a3.go",
- Content: `package a
-
-var Hello int
-`,
- ProtocolEvent: protocol.FileEvent{
- URI: env.Sandbox.Workdir.URI("a/a3.go"),
- Type: protocol.Created,
- },
- },
- {
- Path: "a/a1.go",
- ProtocolEvent: protocol.FileEvent{
- URI: env.Sandbox.Workdir.URI("a/a1.go"),
- Type: protocol.Deleted,
- },
- },
- {
- Path: "a/a2.go",
- Content: `package a; func _() {};`,
- ProtocolEvent: protocol.FileEvent{
- URI: env.Sandbox.Workdir.URI("a/a2.go"),
- Type: protocol.Changed,
- },
- },
- })
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- NoDiagnostics("main.go"),
- ),
- )
- })
-}
-
-// Reproduce golang/go#40456.
-func TestChangeVersion(t *testing.T) {
- const proxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.3/blah/blah.go --
-package blah
-
-const Name = "Blah"
-
-func X(x int) {}
--- example.com@v1.2.2/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.2/blah/blah.go --
-package blah
-
-const Name = "Blah"
-
-func X() {}
--- random.org@v1.2.3/go.mod --
-module random.org
-
-go 1.12
--- random.org@v1.2.3/blah/blah.go --
-package hello
-
-const Name = "Hello"
-`
- const mod = `
--- go.mod --
-module mod.com
-
-go 1.12
-
-require example.com v1.2.2
--- main.go --
-package main
-
-import "example.com/blah"
-
-func main() {
- blah.X()
-}
-`
- WithOptions(ProxyFiles(proxy)).Run(t, mod, func(t *testing.T, env *Env) {
- env.WriteWorkspaceFiles(map[string]string{
- "go.mod": `module mod.com
-
-go 1.12
-
-require example.com v1.2.3
-`,
- "main.go": `package main
-
-import (
- "example.com/blah"
-)
-
-func main() {
- blah.X(1)
-}
-`,
- })
- env.Await(
- env.DoneWithChangeWatchedFiles(),
- NoDiagnostics("main.go"),
- )
- })
-}
-
-// Reproduces golang/go#40340.
-func TestSwitchFromGOPATHToModules(t *testing.T) {
- testenv.NeedsGo1Point(t, 13)
-
- const files = `
--- foo/blah/blah.go --
-package blah
-
-const Name = ""
--- foo/main.go --
-package main
-
-import "blah"
-
-func main() {
- _ = blah.Name
-}
-`
- WithOptions(
- InGOPATH(),
- EditorConfig{
- Env: map[string]string{
- "GO111MODULE": "auto",
- },
- },
- Modes(Experimental), // module is in a subdirectory
- ).Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("foo/main.go")
- env.Await(env.DiagnosticAtRegexp("foo/main.go", `"blah"`))
- if err := env.Sandbox.RunGoCommand(env.Ctx, "foo", "mod", []string{"init", "mod.com"}, true); err != nil {
- t.Fatal(err)
- }
- env.RegexpReplace("foo/main.go", `"blah"`, `"mod.com/blah"`)
- env.Await(
- EmptyDiagnostics("foo/main.go"),
- )
- })
-}
-
-// Reproduces golang/go#40487.
-func TestSwitchFromModulesToGOPATH(t *testing.T) {
- testenv.NeedsGo1Point(t, 13)
-
- const files = `
--- foo/go.mod --
-module mod.com
-
-go 1.14
--- foo/blah/blah.go --
-package blah
-
-const Name = ""
--- foo/main.go --
-package main
-
-import "mod.com/blah"
-
-func main() {
- _ = blah.Name
-}
-`
- WithOptions(
- InGOPATH(),
- EditorConfig{
- Env: map[string]string{
- "GO111MODULE": "auto",
- },
- },
- ).Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("foo/main.go")
- env.RemoveWorkspaceFile("foo/go.mod")
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- env.DiagnosticAtRegexp("foo/main.go", `"mod.com/blah"`),
- ),
- )
- env.RegexpReplace("foo/main.go", `"mod.com/blah"`, `"foo/blah"`)
- env.Await(
- EmptyDiagnostics("foo/main.go"),
- )
- })
-}
-
-func TestNewSymbolInTestVariant(t *testing.T) {
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- a/a.go --
-package a
-
-func bob() {}
--- a/a_test.go --
-package a
-
-import "testing"
-
-func TestBob(t *testing.T) {
- bob()
-}
-`
- Run(t, files, func(t *testing.T, env *Env) {
- // Add a new symbol to the package under test and use it in the test
- // variant. Expect no diagnostics.
- env.WriteWorkspaceFiles(map[string]string{
- "a/a.go": `package a
-
-func bob() {}
-func george() {}
-`,
- "a/a_test.go": `package a
-
-import "testing"
-
-func TestAll(t *testing.T) {
- bob()
- george()
-}
-`,
- })
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- NoDiagnostics("a/a.go"),
- ),
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- NoDiagnostics("a/a_test.go"),
- ),
- )
- // Now, add a new file to the test variant and use its symbol in the
- // original test file. Expect no diagnostics.
- env.WriteWorkspaceFiles(map[string]string{
- "a/a_test.go": `package a
-
-import "testing"
-
-func TestAll(t *testing.T) {
- bob()
- george()
- hi()
-}
-`,
- "a/a2_test.go": `package a
-
-import "testing"
-
-func hi() {}
-
-func TestSomething(t *testing.T) {}
-`,
- })
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- NoDiagnostics("a/a_test.go"),
- ),
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- NoDiagnostics("a/a2_test.go"),
- ),
- )
- })
-}
diff -urN a/gopls/internal/regtest/workspace/metadata_test.go b/gopls/internal/regtest/workspace/metadata_test.go
--- a/gopls/internal/regtest/workspace/metadata_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/workspace/metadata_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,43 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package workspace
-
-import (
- "testing"
-
- . "golang.org/x/tools/internal/lsp/regtest"
- "golang.org/x/tools/internal/testenv"
-)
-
-// TODO(rfindley): move workspace tests related to metadata bugs into this
-// file.
-
-func TestFixImportDecl(t *testing.T) {
- // It appears that older Go versions don't even see p.go from the initial
- // workspace load.
- testenv.NeedsGo1Point(t, 15)
- const src = `
--- go.mod --
-module mod.test
-
-go 1.12
--- p.go --
-package p
-
-import (
- _ "fmt"
-
-const C = 42
-`
-
- Run(t, src, func(t *testing.T, env *Env) {
- env.OpenFile("p.go")
- env.RegexpReplace("p.go", "\"fmt\"", "\"fmt\"\n)")
- env.Await(OnceMet(
- env.DoneWithChange(),
- EmptyDiagnostics("p.go"),
- ))
- })
-}
diff -urN a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/regtest/workspace/workspace_test.go
--- a/gopls/internal/regtest/workspace/workspace_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/regtest/workspace/workspace_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,1307 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package workspace
-
-import (
- "fmt"
- "path/filepath"
- "sort"
- "strings"
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- "golang.org/x/tools/internal/lsp/fake"
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/lsp/source"
- "golang.org/x/tools/internal/testenv"
-
- . "golang.org/x/tools/internal/lsp/regtest"
-)
-
-func TestMain(m *testing.M) {
- bug.PanicOnBugs = true
- Main(m, hooks.Options)
-}
-
-const workspaceProxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.3/blah/blah.go --
-package blah
-
-func SaySomething() {
- fmt.Println("something")
-}
--- random.org@v1.2.3/go.mod --
-module random.org
-
-go 1.12
--- random.org@v1.2.3/bye/bye.go --
-package bye
-
-func Goodbye() {
- println("Bye")
-}
-`
-
-// TODO: Add a replace directive.
-const workspaceModule = `
--- pkg/go.mod --
-module mod.com
-
-go 1.14
-
-require (
- example.com v1.2.3
- random.org v1.2.3
-)
--- pkg/go.sum --
-example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
-random.org v1.2.3 h1:+JE2Fkp7gS0zsHXGEQJ7hraom3pNTlkxC4b2qPfA+/Q=
-random.org v1.2.3/go.mod h1:E9KM6+bBX2g5ykHZ9H27w16sWo3QwgonyjM44Dnej3I=
--- pkg/main.go --
-package main
-
-import (
- "example.com/blah"
- "mod.com/inner"
- "random.org/bye"
-)
-
-func main() {
- blah.SaySomething()
- inner.Hi()
- bye.Goodbye()
-}
--- pkg/main2.go --
-package main
-
-import "fmt"
-
-func _() {
- fmt.Print("%s")
-}
--- pkg/inner/inner.go --
-package inner
-
-import "example.com/blah"
-
-func Hi() {
- blah.SaySomething()
-}
--- goodbye/bye/bye.go --
-package bye
-
-func Bye() {}
--- goodbye/go.mod --
-module random.org
-
-go 1.12
-`
-
-// Confirm that find references returns all of the references in the module,
-// regardless of what the workspace root is.
-func TestReferences(t *testing.T) {
- for _, tt := range []struct {
- name, rootPath string
- }{
- {
- name: "module root",
- rootPath: "pkg",
- },
- {
- name: "subdirectory",
- rootPath: "pkg/inner",
- },
- } {
- t.Run(tt.name, func(t *testing.T) {
- opts := []RunOption{ProxyFiles(workspaceProxy)}
- if tt.rootPath != "" {
- opts = append(opts, WorkspaceFolders(tt.rootPath))
- }
- WithOptions(opts...).Run(t, workspaceModule, func(t *testing.T, env *Env) {
- f := "pkg/inner/inner.go"
- env.OpenFile(f)
- locations := env.References(f, env.RegexpSearch(f, `SaySomething`))
- want := 3
- if got := len(locations); got != want {
- t.Fatalf("expected %v locations, got %v", want, got)
- }
- })
- })
- }
-}
-
-// make sure that directory filters work
-func TestFilters(t *testing.T) {
- for _, tt := range []struct {
- name, rootPath string
- }{
- {
- name: "module root",
- rootPath: "pkg",
- },
- } {
- t.Run(tt.name, func(t *testing.T) {
- opts := []RunOption{ProxyFiles(workspaceProxy)}
- if tt.rootPath != "" {
- opts = append(opts, WorkspaceFolders(tt.rootPath))
- }
- f := func(o *source.Options) {
- o.DirectoryFilters = append(o.DirectoryFilters, "-inner")
- }
- opts = append(opts, Options(f))
- WithOptions(opts...).Run(t, workspaceModule, func(t *testing.T, env *Env) {
- syms := env.WorkspaceSymbol("Hi")
- sort.Slice(syms, func(i, j int) bool { return syms[i].ContainerName < syms[j].ContainerName })
- for i, s := range syms {
- if strings.Contains(s.ContainerName, "/inner") {
- t.Errorf("%s %v %s %s %d\n", s.Name, s.Kind, s.ContainerName, tt.name, i)
- }
- }
- })
- })
- }
-}
-
-// Make sure that analysis diagnostics are cleared for the whole package when
-// the only opened file is closed. This test was inspired by the experience in
-// VS Code, where clicking on a reference result triggers a
-// textDocument/didOpen without a corresponding textDocument/didClose.
-func TestClearAnalysisDiagnostics(t *testing.T) {
- WithOptions(
- ProxyFiles(workspaceProxy),
- WorkspaceFolders("pkg/inner"),
- ).Run(t, workspaceModule, func(t *testing.T, env *Env) {
- env.OpenFile("pkg/main.go")
- env.Await(
- env.DiagnosticAtRegexp("pkg/main2.go", "fmt.Print"),
- )
- env.CloseBuffer("pkg/main.go")
- env.Await(
- EmptyDiagnostics("pkg/main2.go"),
- )
- })
-}
-
-// This test checks that gopls updates the set of files it watches when a
-// replace target is added to the go.mod.
-func TestWatchReplaceTargets(t *testing.T) {
- t.Skipf("skipping known-flaky test: see https://go.dev/issue/50748")
-
- WithOptions(
- ProxyFiles(workspaceProxy),
- WorkspaceFolders("pkg"),
- ).Run(t, workspaceModule, func(t *testing.T, env *Env) {
- // Add a replace directive and expect the files that gopls is watching
- // to change.
- dir := env.Sandbox.Workdir.URI("goodbye").SpanURI().Filename()
- goModWithReplace := fmt.Sprintf(`%s
-replace random.org => %s
-`, env.ReadWorkspaceFile("pkg/go.mod"), dir)
- env.WriteWorkspaceFile("pkg/go.mod", goModWithReplace)
- env.Await(
- env.DoneWithChangeWatchedFiles(),
- UnregistrationMatching("didChangeWatchedFiles"),
- RegistrationMatching("didChangeWatchedFiles"),
- )
- })
-}
-
-const workspaceModuleProxy = `
--- example.com@v1.2.3/go.mod --
-module example.com
-
-go 1.12
--- example.com@v1.2.3/blah/blah.go --
-package blah
-
-func SaySomething() {
- fmt.Println("something")
-}
--- b.com@v1.2.3/go.mod --
-module b.com
-
-go 1.12
--- b.com@v1.2.3/b/b.go --
-package b
-
-func Hello() {}
-`
-
-func TestAutomaticWorkspaceModule_Interdependent(t *testing.T) {
- const multiModule = `
--- moda/a/go.mod --
-module a.com
-
-require b.com v1.2.3
--- moda/a/go.sum --
-b.com v1.2.3 h1:tXrlXP0rnjRpKNmkbLYoWBdq0ikb3C3bKK9//moAWBI=
-b.com v1.2.3/go.mod h1:D+J7pfFBZK5vdIdZEFquR586vKKIkqG7Qjw9AxG5BQ8=
--- moda/a/a.go --
-package a
-
-import (
- "b.com/b"
-)
-
-func main() {
- var x int
- _ = b.Hello()
-}
--- modb/go.mod --
-module b.com
-
--- modb/b/b.go --
-package b
-
-func Hello() int {
- var x int
-}
-`
- WithOptions(
- ProxyFiles(workspaceModuleProxy),
- Modes(Experimental),
- ).Run(t, multiModule, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("moda/a/a.go", "x"),
- env.DiagnosticAtRegexp("modb/b/b.go", "x"),
- env.NoDiagnosticAtRegexp("moda/a/a.go", `"b.com/b"`),
- )
- })
-}
-
-func TestMultiModuleWithExclude(t *testing.T) {
- testenv.NeedsGo1Point(t, 16)
-
- const proxy = `
--- c.com@v1.2.3/go.mod --
-module c.com
-
-go 1.12
-
-require b.com v1.2.3
--- c.com@v1.2.3/blah/blah.go --
-package blah
-
-func SaySomething() {
- fmt.Println("something")
-}
--- b.com@v1.2.3/go.mod --
-module b.com
-
-go 1.12
--- b.com@v1.2.4/b/b.go --
-package b
-
-func Hello() {}
--- b.com@v1.2.4/go.mod --
-module b.com
-
-go 1.12
--- b.com@v1.2.4/b/b.go --
-package b
-
-func Hello() {}
-`
- const multiModule = `
--- go.mod --
-module a.com
-
-require c.com v1.2.3
-
-exclude b.com v1.2.3
--- go.sum --
-c.com v1.2.3 h1:n07Dz9fYmpNqvZMwZi5NEqFcSHbvLa9lacMX+/g25tw=
-c.com v1.2.3/go.mod h1:/4TyYgU9Nu5tA4NymP5xyqE8R2VMzGD3TbJCwCOvHAg=
--- main.go --
-package a
-
-func main() {
- var x int
-}
-`
- WithOptions(
- ProxyFiles(proxy),
- Modes(Experimental),
- ).Run(t, multiModule, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("main.go", "x"),
- )
- })
-}
-
-// This change tests that the version of the module used changes after it has
-// been deleted from the workspace.
-func TestDeleteModule_Interdependent(t *testing.T) {
- t.Skip("Skipping due to golang/go#46375: race due to orphaned file reloading")
-
- const multiModule = `
--- moda/a/go.mod --
-module a.com
-
-require b.com v1.2.3
--- moda/a/go.sum --
-b.com v1.2.3 h1:tXrlXP0rnjRpKNmkbLYoWBdq0ikb3C3bKK9//moAWBI=
-b.com v1.2.3/go.mod h1:D+J7pfFBZK5vdIdZEFquR586vKKIkqG7Qjw9AxG5BQ8=
--- moda/a/a.go --
-package a
-
-import (
- "b.com/b"
-)
-
-func main() {
- var x int
- _ = b.Hello()
-}
--- modb/go.mod --
-module b.com
-
--- modb/b/b.go --
-package b
-
-func Hello() int {
- var x int
-}
-`
- WithOptions(
- ProxyFiles(workspaceModuleProxy),
- Modes(Experimental),
- ).Run(t, multiModule, func(t *testing.T, env *Env) {
- env.OpenFile("moda/a/a.go")
- env.Await(env.DoneWithOpen())
-
- original, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
- if want := "modb/b/b.go"; !strings.HasSuffix(original, want) {
- t.Errorf("expected %s, got %v", want, original)
- }
- env.CloseBuffer(original)
- env.Await(env.DoneWithClose())
-
- env.RemoveWorkspaceFile("modb/b/b.go")
- env.RemoveWorkspaceFile("modb/go.mod")
- env.Await(
- env.DoneWithChangeWatchedFiles(),
- )
-
- d := protocol.PublishDiagnosticsParams{}
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("moda/a/go.mod", "require b.com v1.2.3", "b.com@v1.2.3 has not been downloaded"),
- ReadDiagnostics("moda/a/go.mod", &d),
- ),
- )
- env.ApplyQuickFixes("moda/a/go.mod", d.Diagnostics)
- env.Await(env.DoneWithChangeWatchedFiles())
- got, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
- if want := "b.com@v1.2.3/b/b.go"; !strings.HasSuffix(got, want) {
- t.Errorf("expected %s, got %v", want, got)
- }
- })
-}
-
-// Tests that the version of the module used changes after it has been added
-// to the workspace.
-func TestCreateModule_Interdependent(t *testing.T) {
- const multiModule = `
--- moda/a/go.mod --
-module a.com
-
-require b.com v1.2.3
--- moda/a/go.sum --
-b.com v1.2.3 h1:tXrlXP0rnjRpKNmkbLYoWBdq0ikb3C3bKK9//moAWBI=
-b.com v1.2.3/go.mod h1:D+J7pfFBZK5vdIdZEFquR586vKKIkqG7Qjw9AxG5BQ8=
--- moda/a/a.go --
-package a
-
-import (
- "b.com/b"
-)
-
-func main() {
- var x int
- _ = b.Hello()
-}
-`
- WithOptions(
- Modes(Experimental),
- ProxyFiles(workspaceModuleProxy),
- ).Run(t, multiModule, func(t *testing.T, env *Env) {
- env.OpenFile("moda/a/a.go")
- original, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
- if want := "b.com@v1.2.3/b/b.go"; !strings.HasSuffix(original, want) {
- t.Errorf("expected %s, got %v", want, original)
- }
- env.CloseBuffer(original)
- env.WriteWorkspaceFiles(map[string]string{
- "modb/go.mod": "module b.com",
- "modb/b/b.go": `package b
-
-func Hello() int {
- var x int
-}
-`,
- })
- env.Await(
- OnceMet(
- env.DoneWithChangeWatchedFiles(),
- env.DiagnosticAtRegexp("modb/b/b.go", "x"),
- ),
- )
- got, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
- if want := "modb/b/b.go"; !strings.HasSuffix(got, want) {
- t.Errorf("expected %s, got %v", want, original)
- }
- })
-}
-
-// This test confirms that a gopls workspace can recover from initialization
-// with one invalid module.
-func TestOneBrokenModule(t *testing.T) {
- const multiModule = `
--- moda/a/go.mod --
-module a.com
-
-require b.com v1.2.3
-
--- moda/a/a.go --
-package a
-
-import (
- "b.com/b"
-)
-
-func main() {
- var x int
- _ = b.Hello()
-}
--- modb/go.mod --
-modul b.com // typo here
-
--- modb/b/b.go --
-package b
-
-func Hello() int {
- var x int
-}
-`
- WithOptions(
- ProxyFiles(workspaceModuleProxy),
- Modes(Experimental),
- ).Run(t, multiModule, func(t *testing.T, env *Env) {
- env.OpenFile("modb/go.mod")
- env.Await(
- OnceMet(
- env.DoneWithOpen(),
- DiagnosticAt("modb/go.mod", 0, 0),
- ),
- )
- env.RegexpReplace("modb/go.mod", "modul", "module")
- env.SaveBufferWithoutActions("modb/go.mod")
- env.Await(
- env.DiagnosticAtRegexp("modb/b/b.go", "x"),
- )
- })
-}
-
-func TestUseGoplsMod(t *testing.T) {
- // This test validates certain functionality related to using a gopls.mod
- // file to specify workspace modules.
- testenv.NeedsGo1Point(t, 14)
- const multiModule = `
--- moda/a/go.mod --
-module a.com
-
-require b.com v1.2.3
--- moda/a/go.sum --
-b.com v1.2.3 h1:tXrlXP0rnjRpKNmkbLYoWBdq0ikb3C3bKK9//moAWBI=
-b.com v1.2.3/go.mod h1:D+J7pfFBZK5vdIdZEFquR586vKKIkqG7Qjw9AxG5BQ8=
--- moda/a/a.go --
-package a
-
-import (
- "b.com/b"
-)
-
-func main() {
- var x int
- _ = b.Hello()
-}
--- modb/go.mod --
-module b.com
-
-require example.com v1.2.3
--- modb/go.sum --
-example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
--- modb/b/b.go --
-package b
-
-func Hello() int {
- var x int
-}
--- gopls.mod --
-module gopls-workspace
-
-require (
- a.com v0.0.0-goplsworkspace
- b.com v1.2.3
-)
-
-replace a.com => $SANDBOX_WORKDIR/moda/a
-`
- WithOptions(
- ProxyFiles(workspaceModuleProxy),
- Modes(Experimental),
- ).Run(t, multiModule, func(t *testing.T, env *Env) {
- // Initially, the gopls.mod should cause only the a.com module to be
- // loaded. Validate this by jumping to a definition in b.com and ensuring
- // that we go to the module cache.
- env.OpenFile("moda/a/a.go")
- env.Await(env.DoneWithOpen())
-
- // To verify which modules are loaded, we'll jump to the definition of
- // b.Hello.
- checkHelloLocation := func(want string) error {
- location, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
- if !strings.HasSuffix(location, want) {
- return fmt.Errorf("expected %s, got %v", want, location)
- }
- return nil
- }
-
- // Initially this should be in the module cache, as b.com is not replaced.
- if err := checkHelloLocation("b.com@v1.2.3/b/b.go"); err != nil {
- t.Fatal(err)
- }
-
- // Now, modify the gopls.mod file on disk to activate the b.com module in
- // the workspace.
- workdir := env.Sandbox.Workdir.RootURI().SpanURI().Filename()
- env.WriteWorkspaceFile("gopls.mod", fmt.Sprintf(`module gopls-workspace
-
-require (
- a.com v1.9999999.0-goplsworkspace
- b.com v1.9999999.0-goplsworkspace
-)
-
-replace a.com => %s/moda/a
-replace b.com => %s/modb
-`, workdir, workdir))
- env.Await(env.DoneWithChangeWatchedFiles())
- // Check that go.mod diagnostics picked up the newly active mod file.
- // The local version of modb has an extra dependency we need to download.
- env.OpenFile("modb/go.mod")
- env.Await(env.DoneWithOpen())
-
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("modb/go.mod", `require example.com v1.2.3`, "has not been downloaded"),
- ReadDiagnostics("modb/go.mod", &d),
- ),
- )
- env.ApplyQuickFixes("modb/go.mod", d.Diagnostics)
- env.Await(env.DiagnosticAtRegexp("modb/b/b.go", "x"))
- // Jumping to definition should now go to b.com in the workspace.
- if err := checkHelloLocation("modb/b/b.go"); err != nil {
- t.Fatal(err)
- }
-
- // Now, let's modify the gopls.mod *overlay* (not on disk), and verify that
- // this change is only picked up once it is saved.
- env.OpenFile("gopls.mod")
- env.Await(env.DoneWithOpen())
- env.SetBufferContent("gopls.mod", fmt.Sprintf(`module gopls-workspace
-
-require (
- a.com v0.0.0-goplsworkspace
-)
-
-replace a.com => %s/moda/a
-`, workdir))
-
- // Editing the gopls.mod removes modb from the workspace modules, and so
- // should clear outstanding diagnostics...
- env.Await(OnceMet(
- env.DoneWithChange(),
- EmptyDiagnostics("modb/go.mod"),
- ))
- // ...but does not yet cause a workspace reload, so we should still jump to modb.
- if err := checkHelloLocation("modb/b/b.go"); err != nil {
- t.Fatal(err)
- }
- // Saving should reload the workspace.
- env.SaveBufferWithoutActions("gopls.mod")
- if err := checkHelloLocation("b.com@v1.2.3/b/b.go"); err != nil {
- t.Fatal(err)
- }
- })
-}
-
-// TestBadGoWork exercises the panic from golang/vscode-go#2121.
-func TestBadGoWork(t *testing.T) {
- const files = `
--- go.work --
-use ./bar
--- bar/go.mod --
-module example.com/bar
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("go.work")
- })
-}
-
-func TestUseGoWork(t *testing.T) {
- // This test validates certain functionality related to using a go.work
- // file to specify workspace modules.
- testenv.NeedsGo1Point(t, 14)
- const multiModule = `
--- moda/a/go.mod --
-module a.com
-
-require b.com v1.2.3
--- moda/a/go.sum --
-b.com v1.2.3 h1:tXrlXP0rnjRpKNmkbLYoWBdq0ikb3C3bKK9//moAWBI=
-b.com v1.2.3/go.mod h1:D+J7pfFBZK5vdIdZEFquR586vKKIkqG7Qjw9AxG5BQ8=
--- moda/a/a.go --
-package a
-
-import (
- "b.com/b"
-)
-
-func main() {
- var x int
- _ = b.Hello()
-}
--- modb/go.mod --
-module b.com
-
-require example.com v1.2.3
--- modb/go.sum --
-example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c=
-example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
--- modb/b/b.go --
-package b
-
-func Hello() int {
- var x int
-}
--- go.work --
-go 1.17
-
-use (
- ./moda/a
-)
-`
- WithOptions(
- ProxyFiles(workspaceModuleProxy),
- ).Run(t, multiModule, func(t *testing.T, env *Env) {
- // Initially, the go.work should cause only the a.com module to be
- // loaded. Validate this by jumping to a definition in b.com and ensuring
- // that we go to the module cache.
- env.OpenFile("moda/a/a.go")
- env.Await(env.DoneWithOpen())
-
- // To verify which modules are loaded, we'll jump to the definition of
- // b.Hello.
- checkHelloLocation := func(want string) error {
- location, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
- if !strings.HasSuffix(location, want) {
- return fmt.Errorf("expected %s, got %v", want, location)
- }
- return nil
- }
-
- // Initially this should be in the module cache, as b.com is not replaced.
- if err := checkHelloLocation("b.com@v1.2.3/b/b.go"); err != nil {
- t.Fatal(err)
- }
-
- // Now, modify the go.work file on disk to activate the b.com module in
- // the workspace.
- env.WriteWorkspaceFile("go.work", `
-go 1.17
-
-use (
- ./moda/a
- ./modb
-)
-`)
- env.Await(env.DoneWithChangeWatchedFiles())
- // Check that go.mod diagnostics picked up the newly active mod file.
- // The local version of modb has an extra dependency we need to download.
- env.OpenFile("modb/go.mod")
- env.Await(env.DoneWithOpen())
-
- var d protocol.PublishDiagnosticsParams
- env.Await(
- OnceMet(
- env.DiagnosticAtRegexpWithMessage("modb/go.mod", `require example.com v1.2.3`, "has not been downloaded"),
- ReadDiagnostics("modb/go.mod", &d),
- ),
- )
- env.ApplyQuickFixes("modb/go.mod", d.Diagnostics)
- env.Await(env.DiagnosticAtRegexp("modb/b/b.go", "x"))
-
- // Jumping to definition should now go to b.com in the workspace.
- if err := checkHelloLocation("modb/b/b.go"); err != nil {
- t.Fatal(err)
- }
-
- // Now, let's modify the go.work *overlay* (not on disk), and verify that
- // this change is only picked up once it is saved.
- env.OpenFile("go.work")
- env.Await(env.DoneWithOpen())
- env.SetBufferContent("go.work", `go 1.17
-
-use (
- ./moda/a
-)`)
-
- // Simply modifying the go.work file does not cause a reload, so we should
- // still jump within the workspace.
- //
- // TODO: should editing the go.work above cause modb diagnostics to be
- // suppressed?
- env.Await(env.DoneWithChange())
- if err := checkHelloLocation("modb/b/b.go"); err != nil {
- t.Fatal(err)
- }
-
- // Saving should reload the workspace.
- env.SaveBufferWithoutActions("go.work")
- if err := checkHelloLocation("b.com@v1.2.3/b/b.go"); err != nil {
- t.Fatal(err)
- }
-
- // This fails if guarded with a OnceMet(DoneWithSave(), ...), because it is
- // debounced (and therefore not synchronous with the change).
- env.Await(EmptyOrNoDiagnostics("modb/go.mod"))
-
- // Test Formatting.
- env.SetBufferContent("go.work", `go 1.18
- use (
-
-
-
- ./moda/a
-)
-`) // TODO(matloob): For some reason there's a "start position 7:0 is out of bounds" error when the ")" is on the last character/line in the file. Rob probably knows what's going on.
- env.SaveBuffer("go.work")
- env.Await(env.DoneWithSave())
- gotWorkContents := env.ReadWorkspaceFile("go.work")
- wantWorkContents := `go 1.18
-
-use (
- ./moda/a
-)
-`
- if gotWorkContents != wantWorkContents {
- t.Fatalf("formatted contents of workspace: got %q; want %q", gotWorkContents, wantWorkContents)
- }
- })
-}
-
-func TestUseGoWorkDiagnosticMissingModule(t *testing.T) {
- const files = `
--- go.work --
-go 1.18
-
-use ./foo
--- bar/go.mod --
-module example.com/bar
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("go.work")
- env.Await(
- env.DiagnosticAtRegexpWithMessage("go.work", "use", "directory ./foo does not contain a module"),
- )
- // The following tests is a regression test against an issue where we weren't
- // copying the workFile struct field on workspace when a new one was created in
- // (*workspace).invalidate. Set the buffer content to a working file so that
- // invalidate recognizes the workspace to be change and copies over the workspace
- // struct, and then set the content back to the old contents to make sure
- // the diagnostic still shows up.
- env.SetBufferContent("go.work", "go 1.18 \n\n use ./bar\n")
- env.Await(
- env.NoDiagnosticAtRegexp("go.work", "use"),
- )
- env.SetBufferContent("go.work", "go 1.18 \n\n use ./foo\n")
- env.Await(
- env.DiagnosticAtRegexpWithMessage("go.work", "use", "directory ./foo does not contain a module"),
- )
- })
-}
-
-func TestUseGoWorkDiagnosticSyntaxError(t *testing.T) {
- const files = `
--- go.work --
-go 1.18
-
-usa ./foo
-replace
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("go.work")
- env.Await(
- env.DiagnosticAtRegexpWithMessage("go.work", "usa", "unknown directive: usa"),
- env.DiagnosticAtRegexpWithMessage("go.work", "replace", "usage: replace"),
- )
- })
-}
-
-func TestUseGoWorkHover(t *testing.T) {
- const files = `
--- go.work --
-go 1.18
-
-use ./foo
-use (
- ./bar
- ./bar/baz
-)
--- foo/go.mod --
-module example.com/foo
--- bar/go.mod --
-module example.com/bar
--- bar/baz/go.mod --
-module example.com/bar/baz
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.OpenFile("go.work")
-
- tcs := map[string]string{
- `\./foo`: "example.com/foo",
- `(?m)\./bar$`: "example.com/bar",
- `\./bar/baz`: "example.com/bar/baz",
- }
-
- for hoverRE, want := range tcs {
- pos := env.RegexpSearch("go.work", hoverRE)
- got, _ := env.Hover("go.work", pos)
- if got.Value != want {
- t.Errorf(`hover on %q: got %q, want %q`, hoverRE, got, want)
- }
- }
- })
-}
-
-func TestExpandToGoWork(t *testing.T) {
- testenv.NeedsGo1Point(t, 18)
- const workspace = `
--- moda/a/go.mod --
-module a.com
-
-require b.com v1.2.3
--- moda/a/a.go --
-package a
-
-import (
- "b.com/b"
-)
-
-func main() {
- var x int
- _ = b.Hello()
-}
--- modb/go.mod --
-module b.com
-
-require example.com v1.2.3
--- modb/b/b.go --
-package b
-
-func Hello() int {
- var x int
-}
--- go.work --
-go 1.17
-
-use (
- ./moda/a
- ./modb
-)
-`
- WithOptions(
- WorkspaceFolders("moda/a"),
- ).Run(t, workspace, func(t *testing.T, env *Env) {
- env.OpenFile("moda/a/a.go")
- env.Await(env.DoneWithOpen())
- location, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
- want := "modb/b/b.go"
- if !strings.HasSuffix(location, want) {
- t.Errorf("expected %s, got %v", want, location)
- }
- })
-}
-
-func TestNonWorkspaceFileCreation(t *testing.T) {
- testenv.NeedsGo1Point(t, 13)
-
- const files = `
--- go.mod --
-module mod.com
-
-go 1.12
--- x.go --
-package x
-`
-
- const code = `
-package foo
-import "fmt"
-var _ = fmt.Printf
-`
- Run(t, files, func(t *testing.T, env *Env) {
- env.CreateBuffer("/tmp/foo.go", "")
- env.EditBuffer("/tmp/foo.go", fake.NewEdit(0, 0, 0, 0, code))
- env.GoToDefinition("/tmp/foo.go", env.RegexpSearch("/tmp/foo.go", `Printf`))
- })
-}
-
-func TestMultiModuleV2(t *testing.T) {
- const multiModule = `
--- moda/a/go.mod --
-module a.com
-
-require b.com/v2 v2.1.9
--- moda/a/a.go --
-package a
-
-import (
- "b.com/v2/b"
-)
-
-func main() {
- var x int
- _ = b.Hi()
-}
--- modb/go.mod --
-module b.com
-
--- modb/b/b.go --
-package b
-
-func Hello() int {
- var x int
-}
--- modb/v2/go.mod --
-module b.com/v2
-
--- modb/v2/b/b.go --
-package b
-
-func Hi() int {
- var x int
-}
--- modc/go.mod --
-module gopkg.in/yaml.v1 // test gopkg.in versions
--- modc/main.go --
-package main
-
-func main() {
- var x int
-}
-`
- WithOptions(
- Modes(Experimental),
- ).Run(t, multiModule, func(t *testing.T, env *Env) {
- env.Await(
- env.DiagnosticAtRegexp("moda/a/a.go", "x"),
- env.DiagnosticAtRegexp("modb/b/b.go", "x"),
- env.DiagnosticAtRegexp("modb/v2/b/b.go", "x"),
- env.DiagnosticAtRegexp("modc/main.go", "x"),
- )
- })
-}
-
-func TestDirectoryFiltersLoads(t *testing.T) {
- // exclude, and its error, should be excluded from the workspace.
- const files = `
--- go.mod --
-module example.com
-
-go 1.12
--- exclude/exclude.go --
-package exclude
-
-const _ = Nonexistant
-`
- cfg := EditorConfig{
- DirectoryFilters: []string{"-exclude"},
- }
- WithOptions(cfg).Run(t, files, func(t *testing.T, env *Env) {
- env.Await(NoDiagnostics("exclude/x.go"))
- })
-}
-
-func TestDirectoryFiltersTransitiveDep(t *testing.T) {
- // Even though exclude is excluded from the workspace, it should
- // still be importable as a non-workspace package.
- const files = `
--- go.mod --
-module example.com
-
-go 1.12
--- include/include.go --
-package include
-import "example.com/exclude"
-
-const _ = exclude.X
--- exclude/exclude.go --
-package exclude
-
-const _ = Nonexistant // should be ignored, since this is a non-workspace package
-const X = 1
-`
-
- cfg := EditorConfig{
- DirectoryFilters: []string{"-exclude"},
- }
- WithOptions(cfg).Run(t, files, func(t *testing.T, env *Env) {
- env.Await(
- NoDiagnostics("exclude/exclude.go"), // filtered out
- NoDiagnostics("include/include.go"), // successfully builds
- )
- })
-}
-
-func TestDirectoryFiltersWorkspaceModules(t *testing.T) {
- // Define a module include.com which should be in the workspace, plus a
- // module exclude.com which should be excluded and therefore come from
- // the proxy.
- const files = `
--- include/go.mod --
-module include.com
-
-go 1.12
-
-require exclude.com v1.0.0
-
--- include/go.sum --
-exclude.com v1.0.0 h1:Q5QSfDXY5qyNCBeUiWovUGqcLCRZKoTs9XdBeVz+w1I=
-exclude.com v1.0.0/go.mod h1:hFox2uDlNB2s2Jfd9tHlQVfgqUiLVTmh6ZKat4cvnj4=
-
--- include/include.go --
-package include
-
-import "exclude.com"
-
-var _ = exclude.X // satisfied only by the workspace version
--- exclude/go.mod --
-module exclude.com
-
-go 1.12
--- exclude/exclude.go --
-package exclude
-
-const X = 1
-`
- const proxy = `
--- exclude.com@v1.0.0/go.mod --
-module exclude.com
-
-go 1.12
--- exclude.com@v1.0.0/exclude.go --
-package exclude
-`
- cfg := EditorConfig{
- DirectoryFilters: []string{"-exclude"},
- }
- WithOptions(cfg, Modes(Experimental), ProxyFiles(proxy)).Run(t, files, func(t *testing.T, env *Env) {
- env.Await(env.DiagnosticAtRegexp("include/include.go", `exclude.(X)`))
- })
-}
-
-// Confirm that a fix for a tidy module will correct all modules in the
-// workspace.
-func TestMultiModule_OneBrokenModule(t *testing.T) {
- testenv.NeedsGo1Point(t, 15)
-
- const mod = `
--- a/go.mod --
-module a.com
-
-go 1.12
--- a/main.go --
-package main
--- b/go.mod --
-module b.com
-
-go 1.12
-
-require (
- example.com v1.2.3
-)
--- b/go.sum --
--- b/main.go --
-package b
-
-import "example.com/blah"
-
-func main() {
- blah.Hello()
-}
-`
- WithOptions(
- ProxyFiles(workspaceProxy),
- Modes(Experimental),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- params := &protocol.PublishDiagnosticsParams{}
- env.OpenFile("b/go.mod")
- env.Await(
- OnceMet(
- env.GoSumDiagnostic("b/go.mod", `example.com v1.2.3`),
- ReadDiagnostics("b/go.mod", params),
- ),
- )
- for _, d := range params.Diagnostics {
- if !strings.Contains(d.Message, "go.sum is out of sync") {
- continue
- }
- actions := env.GetQuickFixes("b/go.mod", []protocol.Diagnostic{d})
- if len(actions) != 2 {
- t.Fatalf("expected 2 code actions, got %v", len(actions))
- }
- env.ApplyQuickFixes("b/go.mod", []protocol.Diagnostic{d})
- }
- env.Await(
- EmptyDiagnostics("b/go.mod"),
- )
- })
-}
-
-// Sometimes users may have their module cache within the workspace.
-// We shouldn't consider any module in the module cache to be in the workspace.
-func TestGOMODCACHEInWorkspace(t *testing.T) {
- const mod = `
--- a/go.mod --
-module a.com
-
-go 1.12
--- a/a.go --
-package a
-
-func _() {}
--- a/c/c.go --
-package c
--- gopath/src/b/b.go --
-package b
--- gopath/pkg/mod/example.com/go.mod --
-module example.com
-
-go 1.12
--- gopath/pkg/mod/example.com/main.go --
-package main
-`
- WithOptions(
- EditorConfig{Env: map[string]string{
- "GOPATH": filepath.FromSlash("$SANDBOX_WORKDIR/gopath"),
- }},
- Modes(Singleton),
- ).Run(t, mod, func(t *testing.T, env *Env) {
- env.Await(
- // Confirm that the build configuration is seen as valid,
- // even though there are technically multiple go.mod files in the
- // worskpace.
- LogMatching(protocol.Info, ".*valid build configuration = true.*", 1, false),
- )
- })
-}
-
-func TestAddGoWork(t *testing.T) {
- const nomod = `
--- a/go.mod --
-module a.com
-
-go 1.16
--- a/main.go --
-package main
-
-func main() {}
--- b/go.mod --
-module b.com
-
-go 1.16
--- b/main.go --
-package main
-
-func main() {}
-`
- WithOptions(
- Modes(Singleton),
- ).Run(t, nomod, func(t *testing.T, env *Env) {
- env.OpenFile("a/main.go")
- env.OpenFile("b/main.go")
- env.Await(
- DiagnosticAt("a/main.go", 0, 0),
- DiagnosticAt("b/main.go", 0, 0),
- )
- env.WriteWorkspaceFile("go.work", `go 1.16
-
-use (
- a
- b
-)
-`)
- env.Await(NoOutstandingDiagnostics())
- })
-}
-
-// Tests the fix for golang/go#52500.
-func TestChangeTestVariant_Issue52500(t *testing.T) {
- // This test fails for unknown reasons at Go <= 15. Presumably the loading of
- // test variants behaves differently, possibly due to lack of support for
- // native overlays.
- testenv.NeedsGo1Point(t, 16)
- const src = `
--- go.mod --
-module mod.test
-
-go 1.12
--- main_test.go --
-package main_test
-
-type Server struct{}
-
-const mainConst = otherConst
--- other_test.go --
-package main_test
-
-const otherConst = 0
-
-func (Server) Foo() {}
-`
-
- Run(t, src, func(t *testing.T, env *Env) {
- env.OpenFile("other_test.go")
- env.RegexpReplace("other_test.go", "main_test", "main")
-
- // For this test to function, it is necessary to wait on both of the
- // expectations below: the bug is that when switching the package name in
- // other_test.go from main->main_test, metadata for main_test is not marked
- // as invalid. So we need to wait for the metadata of main_test.go to be
- // updated before moving other_test.go back to the main_test package.
- env.Await(
- env.DiagnosticAtRegexpWithMessage("other_test.go", "Server", "undeclared"),
- env.DiagnosticAtRegexpWithMessage("main_test.go", "otherConst", "undeclared"),
- )
- env.RegexpReplace("other_test.go", "main", "main_test")
- env.Await(
- EmptyDiagnostics("other_test.go"),
- EmptyDiagnostics("main_test.go"),
- )
-
- // This will cause a test failure if other_test.go is not in any package.
- _, _ = env.GoToDefinition("other_test.go", env.RegexpSearch("other_test.go", "Server"))
- })
-}
diff -urN a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/command.go
--- a/gopls/internal/vulncheck/command.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/vulncheck/command.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,123 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.18
-// +build go1.18
-
-package vulncheck
-
-import (
- "context"
- "log"
- "os"
- "strings"
-
- "golang.org/x/tools/go/packages"
- gvc "golang.org/x/tools/gopls/internal/govulncheck"
- "golang.org/x/tools/internal/lsp/command"
- "golang.org/x/vuln/client"
-)
-
-func init() {
- Govulncheck = govulncheck
-}
-
-func govulncheck(ctx context.Context, cfg *packages.Config, args command.VulncheckArgs) (res command.VulncheckResult, _ error) {
- if args.Pattern == "" {
- args.Pattern = "."
- }
-
- dbClient, err := client.NewClient(findGOVULNDB(cfg), client.Options{HTTPCache: gvc.DefaultCache()})
- if err != nil {
- return res, err
- }
-
- c := cmd{Client: dbClient}
- vulns, err := c.Run(ctx, cfg, args.Pattern)
- if err != nil {
- return res, err
- }
-
- res.Vuln = vulns
- return res, err
-}
-
-func findGOVULNDB(cfg *packages.Config) []string {
- for _, kv := range cfg.Env {
- if strings.HasPrefix(kv, "GOVULNDB=") {
- return strings.Split(kv[len("GOVULNDB="):], ",")
- }
- }
- if GOVULNDB := os.Getenv("GOVULNDB"); GOVULNDB != "" {
- return strings.Split(GOVULNDB, ",")
- }
- return []string{"https://vuln.go.dev"}
-}
-
-type Vuln = command.Vuln
-type CallStack = command.CallStack
-type StackEntry = command.StackEntry
-
-// cmd is an in-process govulncheck command runner
-// that uses the provided client.Client.
-type cmd struct {
- Client client.Client
-}
-
-// Run runs the govulncheck after loading packages using the provided packages.Config.
-func (c *cmd) Run(ctx context.Context, cfg *packages.Config, patterns ...string) (_ []Vuln, err error) {
- cfg.Mode |= packages.NeedModule | packages.NeedName | packages.NeedFiles |
- packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes |
- packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps
-
- log.Println("loading packages...")
- loadedPkgs, err := gvc.LoadPackages(cfg, patterns...)
- if err != nil {
- log.Printf("package load failed: %v", err)
- return nil, err
- }
- log.Printf("loaded %d packages\n", len(loadedPkgs))
-
- r, err := gvc.Source(ctx, loadedPkgs, c.Client)
- if err != nil {
- return nil, err
- }
- callInfo := gvc.GetCallInfo(r, loadedPkgs)
- return toVulns(callInfo)
- // TODO: add import graphs.
-}
-
-func toVulns(ci *gvc.CallInfo) ([]Vuln, error) {
- var vulns []Vuln
-
- for _, vg := range ci.VulnGroups {
- v0 := vg[0]
- lf := gvc.LatestFixed(v0.OSV.Affected)
- if lf != "" && lf[0] != 'v' {
- lf = "v" + lf
- }
- vuln := Vuln{
- ID: v0.OSV.ID,
- PkgPath: v0.PkgPath,
- CurrentVersion: ci.ModuleVersions[v0.ModPath],
- FixedVersion: lf,
- Details: v0.OSV.Details,
-
- Aliases: v0.OSV.Aliases,
- Symbol: v0.Symbol,
- ModPath: v0.ModPath,
- URL: href(v0.OSV),
- }
-
- // Keep first call stack for each vuln.
- for _, v := range vg {
- if css := ci.CallStacks[v]; len(css) > 0 {
- vuln.CallStacks = append(vuln.CallStacks, toCallStack(css[0]))
- vuln.CallStackSummaries = append(vuln.CallStackSummaries, gvc.SummarizeCallStack(css[0], ci.TopPackages, v.PkgPath))
- }
- }
- vulns = append(vulns, vuln)
- }
- return vulns, nil
-}
diff -urN a/gopls/internal/vulncheck/command_test.go b/gopls/internal/vulncheck/command_test.go
--- a/gopls/internal/vulncheck/command_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/vulncheck/command_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,310 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.18
-// +build go1.18
-
-package vulncheck
-
-import (
- "bytes"
- "context"
- "fmt"
- "os"
- "path/filepath"
- "sort"
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/internal/lsp/cache"
- "golang.org/x/tools/internal/lsp/fake"
- "golang.org/x/tools/internal/lsp/source"
- "golang.org/x/tools/internal/lsp/tests"
- "golang.org/x/vuln/client"
- "golang.org/x/vuln/osv"
-)
-
-func TestCmd_Run(t *testing.T) {
- runTest(t, workspace1, proxy1, func(ctx context.Context, snapshot source.Snapshot) {
- cmd := &cmd{Client: testClient1}
- cfg := packagesCfg(ctx, snapshot)
- result, err := cmd.Run(ctx, cfg, "./...")
- if err != nil {
- t.Fatal(err)
- }
- // Check that we find the right number of vulnerabilities.
- // There should be three entries as there are three vulnerable
- // symbols in the two import-reachable OSVs.
- var got []report
- for _, v := range result {
- got = append(got, toReport(v))
- }
-
- var want = []report{
- {
- Vuln: Vuln{
- ID: "GO-2022-01",
- Symbol: "VulnData.Vuln1",
- PkgPath: "golang.org/amod/avuln",
- ModPath: "golang.org/amod",
- URL: "https://pkg.go.dev/vuln/GO-2022-01",
- CurrentVersion: "v1.1.3",
- FixedVersion: "v1.0.4",
- CallStackSummaries: []string{
- "golang.org/entry/x.X calls golang.org/amod/avuln.VulnData.Vuln1",
- "golang.org/entry/x.X calls golang.org/cmod/c.C1, which eventually calls golang.org/amod/avuln.VulnData.Vuln2",
- },
- },
- CallStacksStr: []string{
- "golang.org/entry/x.X [approx.] (x.go:8)\n" +
- "golang.org/amod/avuln.VulnData.Vuln1 (avuln.go:3)\n",
- "golang.org/entry/x.X (x.go:8)\n" +
- "golang.org/cmod/c.C1 (c.go:13)\n" +
- "golang.org/amod/avuln.VulnData.Vuln2 (avuln.go:4)\n",
- },
- },
- {
- Vuln: Vuln{
- ID: "GO-2022-02",
- Symbol: "Vuln",
- PkgPath: "golang.org/bmod/bvuln",
- ModPath: "golang.org/bmod",
- URL: "https://pkg.go.dev/vuln/GO-2022-02",
- CurrentVersion: "v0.5.0",
- CallStackSummaries: []string{"golang.org/entry/y.Y calls golang.org/bmod/bvuln.Vuln"},
- },
- CallStacksStr: []string{
- "golang.org/entry/y.Y [approx.] (y.go:5)\n" +
- "golang.org/bmod/bvuln.Vuln (bvuln.go:2)\n",
- },
- },
- }
- // sort reports for stability before comparison.
- for _, rpts := range [][]report{got, want} {
- sort.Slice(rpts, func(i, j int) bool {
- a, b := rpts[i], rpts[j]
- if a.ID != b.ID {
- return a.ID < b.ID
- }
- if a.PkgPath != b.PkgPath {
- return a.PkgPath < b.PkgPath
- }
- return a.Symbol < b.Symbol
- })
- }
- if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(report{}, "Vuln.CallStacks")); diff != "" {
- t.Error(diff)
- }
-
- })
-}
-
-type report struct {
- Vuln
- // Trace is stringified Vuln.CallStacks
- CallStacksStr []string
-}
-
-func toReport(v Vuln) report {
- var r = report{Vuln: v}
- for _, s := range v.CallStacks {
- r.CallStacksStr = append(r.CallStacksStr, CallStackString(s))
- }
- return r
-}
-
-func CallStackString(callstack CallStack) string {
- var b bytes.Buffer
- for _, entry := range callstack {
- fname := filepath.Base(entry.URI.SpanURI().Filename())
- fmt.Fprintf(&b, "%v (%v:%d)\n", entry.Name, fname, entry.Pos.Line)
- }
- return b.String()
-}
-
-const workspace1 = `
--- go.mod --
-module golang.org/entry
-
-require (
- golang.org/cmod v1.1.3
-)
-go 1.18
--- x/x.go --
-package x
-
-import (
- "golang.org/cmod/c"
- "golang.org/entry/y"
-)
-
-func X() {
- c.C1().Vuln1() // vuln use: X -> Vuln1
-}
-
-func CallY() {
- y.Y() // vuln use: CallY -> y.Y -> bvuln.Vuln
-}
-
--- y/y.go --
-package y
-
-import "golang.org/cmod/c"
-
-func Y() {
- c.C2()() // vuln use: Y -> bvuln.Vuln
-}
-`
-
-const proxy1 = `
--- golang.org/cmod@v1.1.3/go.mod --
-module golang.org/cmod
-
-go 1.12
--- golang.org/cmod@v1.1.3/c/c.go --
-package c
-
-import (
- "golang.org/amod/avuln"
- "golang.org/bmod/bvuln"
-)
-
-type I interface {
- Vuln1()
-}
-
-func C1() I {
- v := avuln.VulnData{}
- v.Vuln2() // vuln use
- return v
-}
-
-func C2() func() {
- return bvuln.Vuln
-}
--- golang.org/amod@v1.1.3/go.mod --
-module golang.org/amod
-
-go 1.14
--- golang.org/amod@v1.1.3/avuln/avuln.go --
-package avuln
-
-type VulnData struct {}
-func (v VulnData) Vuln1() {}
-func (v VulnData) Vuln2() {}
--- golang.org/bmod@v0.5.0/go.mod --
-module golang.org/bmod
-
-go 1.14
--- golang.org/bmod@v0.5.0/bvuln/bvuln.go --
-package bvuln
-
-func Vuln() {
- // something evil
-}
-`
-
-// testClient contains the following test vulnerabilities
-//
-// golang.org/amod/avuln.{VulnData.Vuln1, vulnData.Vuln2}
-// golang.org/bmod/bvuln.{Vuln}
-var testClient1 = &mockClient{
- ret: map[string][]*osv.Entry{
- "golang.org/amod": {
- {
- ID: "GO-2022-01",
- References: []osv.Reference{
- {
- Type: "href",
- URL: "pkg.go.dev/vuln/GO-2022-01",
- },
- },
- Affected: []osv.Affected{{
- Package: osv.Package{Name: "golang.org/amod/avuln"},
- Ranges: osv.Affects{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.0"}, {Fixed: "1.0.4"}, {Introduced: "1.1.2"}}}},
- EcosystemSpecific: osv.EcosystemSpecific{Symbols: []string{"VulnData.Vuln1", "VulnData.Vuln2"}},
- }},
- },
- },
- "golang.org/bmod": {
- {
- ID: "GO-2022-02",
- Affected: []osv.Affected{{
- Package: osv.Package{Name: "golang.org/bmod/bvuln"},
- Ranges: osv.Affects{{Type: osv.TypeSemver}},
- EcosystemSpecific: osv.EcosystemSpecific{Symbols: []string{"Vuln"}},
- }},
- },
- },
- },
-}
-
-type mockClient struct {
- client.Client
- ret map[string][]*osv.Entry
-}
-
-func (mc *mockClient) GetByModule(ctx context.Context, a string) ([]*osv.Entry, error) {
- return mc.ret[a], nil
-}
-
-func runTest(t *testing.T, workspaceData, proxyData string, test func(context.Context, source.Snapshot)) {
- ws, err := fake.NewSandbox(&fake.SandboxConfig{
- Files: fake.UnpackTxt(workspaceData),
- ProxyFiles: fake.UnpackTxt(proxyData),
- })
- if err != nil {
- t.Fatal(err)
- }
- defer ws.Close()
-
- ctx := tests.Context(t)
-
- // get the module cache populated and the go.sum file at the root auto-generated.
- dir := ws.Workdir.RootURI().SpanURI().Filename()
- if err := ws.RunGoCommand(ctx, dir, "list", []string{"-mod=mod", "..."}, true); err != nil {
- t.Fatal(err)
- }
-
- cache := cache.New(nil)
- session := cache.NewSession(ctx)
- options := source.DefaultOptions().Clone()
- tests.DefaultOptions(options)
- session.SetOptions(options)
- envs := []string{}
- for k, v := range ws.GoEnv() {
- envs = append(envs, k+"="+v)
- }
- options.SetEnvSlice(envs)
- name := ws.RootDir()
- folder := ws.Workdir.RootURI().SpanURI()
- view, snapshot, release, err := session.NewView(ctx, name, folder, options)
- if err != nil {
- t.Fatal(err)
- }
- defer release()
- defer view.Shutdown(ctx)
-
- test(ctx, snapshot)
-}
-
-// TODO: expose this as a method of Snapshot.
-func packagesCfg(ctx context.Context, snapshot source.Snapshot) *packages.Config {
- view := snapshot.View()
- viewBuildFlags := view.Options().BuildFlags
- var viewEnv []string
- if e := view.Options().EnvSlice(); e != nil {
- viewEnv = append(os.Environ(), e...)
- }
- return &packages.Config{
- // Mode will be set by cmd.Run.
- Context: ctx,
- Tests: true,
- BuildFlags: viewBuildFlags,
- Env: viewEnv,
- Dir: view.Folder().Filename(),
- }
-}
diff -urN a/gopls/internal/vulncheck/util.go b/gopls/internal/vulncheck/util.go
--- a/gopls/internal/vulncheck/util.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/vulncheck/util.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,82 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.18
-// +build go1.18
-
-package vulncheck
-
-import (
- "fmt"
- "go/token"
-
- gvc "golang.org/x/tools/gopls/internal/govulncheck"
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/vuln/osv"
- "golang.org/x/vuln/vulncheck"
-)
-
-func toCallStack(src vulncheck.CallStack) CallStack {
- var dest []StackEntry
- for _, e := range src {
- dest = append(dest, toStackEntry(e))
- }
- return dest
-}
-
-func toStackEntry(src vulncheck.StackEntry) StackEntry {
- f, call := src.Function, src.Call
- pos := f.Pos
- desc := gvc.FuncName(f)
- if src.Call != nil {
- pos = src.Call.Pos // Exact call site position is helpful.
- if !call.Resolved {
- // In case of a statically unresolved call site, communicate to the client
- // that this was approximately resolved to f
-
- desc += " [approx.]"
- }
- }
- return StackEntry{
- Name: desc,
- URI: filenameToURI(pos),
- Pos: posToPosition(pos),
- }
-}
-
-// href returns a URL embedded in the entry if any.
-// If no suitable URL is found, it returns a default entry in
-// pkg.go.dev/vuln.
-func href(vuln *osv.Entry) string {
- for _, affected := range vuln.Affected {
- if url := affected.DatabaseSpecific.URL; url != "" {
- return url
- }
- }
- for _, r := range vuln.References {
- if r.Type == "WEB" {
- return r.URL
- }
- }
- return fmt.Sprintf("https://pkg.go.dev/vuln/%s", vuln.ID)
-}
-
-func filenameToURI(pos *token.Position) protocol.DocumentURI {
- if pos == nil || pos.Filename == "" {
- return ""
- }
- return protocol.URIFromPath(pos.Filename)
-}
-
-func posToPosition(pos *token.Position) (p protocol.Position) {
- // token.Position.Line starts from 1, and
- // LSP protocol's position line is 0-based.
- if pos != nil {
- p.Line = uint32(pos.Line - 1)
- // TODO(hyangah): LSP uses UTF16 column.
- // We need utility like span.ToUTF16Column,
- // but somthing that does not require file contents.
- }
- return p
-}
diff -urN a/gopls/internal/vulncheck/vulncheck.go b/gopls/internal/vulncheck/vulncheck.go
--- a/gopls/internal/vulncheck/vulncheck.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/internal/vulncheck/vulncheck.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,23 +0,0 @@
-// Copyright 2022 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package vulncheck provides an analysis command
-// that runs vulnerability analysis using data from
-// golang.org/x/vuln/vulncheck.
-// This package requires go1.18 or newer.
-package vulncheck
-
-import (
- "context"
- "errors"
-
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/internal/lsp/command"
-)
-
-// Govulncheck runs the in-process govulncheck implementation.
-// With go1.18+, this is swapped with the real implementation.
-var Govulncheck = func(ctx context.Context, cfg *packages.Config, args command.VulncheckArgs) (res command.VulncheckResult, _ error) {
- return res, errors.New("not implemented")
-}
diff -urN a/gopls/main.go b/gopls/main.go
--- a/gopls/main.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/main.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,31 +0,0 @@
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Gopls (pronounced “go please”) is an LSP server for Go.
-// The Language Server Protocol allows any text editor
-// to be extended with IDE-like features;
-// see https://langserver.org/ for details.
-//
-// See https://github.com/golang/tools/blob/master/gopls/README.md
-// for the most up-to-date documentation.
-package main // import "golang.org/x/tools/gopls"
-
-import (
- "context"
- "golang.org/x/tools/internal/analysisinternal"
- "os"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/cmd"
- "golang.org/x/tools/internal/tool"
-)
-
-func main() {
- // In 1.18, diagnostics for Fuzz tests must not be used by cmd/vet.
- // So the code for Fuzz tests diagnostics is guarded behind flag analysisinternal.DiagnoseFuzzTests
- // Turn on analysisinternal.DiagnoseFuzzTests for gopls
- analysisinternal.DiagnoseFuzzTests = true
- ctx := context.Background()
- tool.Main(ctx, cmd.New("gopls", "", nil, hooks.Options), os.Args[1:])
-}
diff -urN a/gopls/release/release.go b/gopls/release/release.go
--- a/gopls/release/release.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/release/release.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,213 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-//
-// Package release checks that the a given version of gopls is ready for
-// release. It can also tag and publish the release.
-//
-// To run:
-//
-// $ cd $GOPATH/src/golang.org/x/tools/gopls
-// $ go run release/release.go -version=<version>
-package main
-
-import (
- "flag"
- "fmt"
- "go/types"
- exec "golang.org/x/sys/execabs"
- "io/ioutil"
- "log"
- "os"
- "os/user"
- "path/filepath"
- "strconv"
- "strings"
-
- "golang.org/x/mod/modfile"
- "golang.org/x/mod/semver"
- "golang.org/x/tools/go/packages"
-)
-
-var (
- versionFlag = flag.String("version", "", "version to tag")
- remoteFlag = flag.String("remote", "", "remote to which to push the tag")
- releaseFlag = flag.Bool("release", false, "release is true if you intend to tag and push a release")
-)
-
-func main() {
- flag.Parse()
-
- if *versionFlag == "" {
- log.Fatalf("must provide -version flag")
- }
- if !semver.IsValid(*versionFlag) {
- log.Fatalf("invalid version %s", *versionFlag)
- }
- if semver.Major(*versionFlag) != "v0" {
- log.Fatalf("expected major version v0, got %s", semver.Major(*versionFlag))
- }
- if semver.Build(*versionFlag) != "" {
- log.Fatalf("unexpected build suffix: %s", *versionFlag)
- }
- if *releaseFlag && *remoteFlag == "" {
- log.Fatalf("must provide -remote flag if releasing")
- }
- user, err := user.Current()
- if err != nil {
- log.Fatal(err)
- }
- // Validate that the user is running the program from the gopls module.
- wd, err := os.Getwd()
- if err != nil {
- log.Fatal(err)
- }
- if filepath.Base(wd) != "gopls" {
- log.Fatalf("must run from the gopls module")
- }
- // Confirm that they are running on a branch with a name following the
- // format of "gopls-release-branch.<major>.<minor>".
- if err := validateBranchName(*versionFlag); err != nil {
- log.Fatal(err)
- }
- // Confirm that they have updated the hardcoded version.
- if err := validateHardcodedVersion(wd, *versionFlag); err != nil {
- log.Fatal(err)
- }
- // Confirm that the versions in the go.mod file are correct.
- if err := validateGoModFile(wd); err != nil {
- log.Fatal(err)
- }
- earlyExitMsg := "Validated that the release is ready. Exiting without tagging and publishing."
- if !*releaseFlag {
- fmt.Println(earlyExitMsg)
- os.Exit(0)
- }
- fmt.Println(`Proceeding to tagging and publishing the release...
-Please enter Y if you wish to proceed or anything else if you wish to exit.`)
- // Accept and process user input.
- var input string
- fmt.Scanln(&input)
- switch input {
- case "Y":
- fmt.Println("Proceeding to tagging and publishing the release.")
- default:
- fmt.Println(earlyExitMsg)
- os.Exit(0)
- }
- // To tag the release:
- // $ git -c user.email=username@google.com tag -a -m “<message>” gopls/v<major>.<minor>.<patch>-<pre-release>
- goplsVersion := fmt.Sprintf("gopls/%s", *versionFlag)
- cmd := exec.Command("git", "-c", fmt.Sprintf("user.email=%s@google.com", user.Username), "tag", "-a", "-m", fmt.Sprintf("%q", goplsVersion), goplsVersion)
- if err := cmd.Run(); err != nil {
- log.Fatal(err)
- }
- // Push the tag to the remote:
- // $ git push <remote> gopls/v<major>.<minor>.<patch>-pre.1
- cmd = exec.Command("git", "push", *remoteFlag, goplsVersion)
- if err := cmd.Run(); err != nil {
- log.Fatal(err)
- }
-}
-
-// validateBranchName reports whether the user's current branch name is of the
-// form "gopls-release-branch.<major>.<minor>". It reports an error if not.
-func validateBranchName(version string) error {
- cmd := exec.Command("git", "branch", "--show-current")
- stdout, err := cmd.Output()
- if err != nil {
- return err
- }
- branch := strings.TrimSpace(string(stdout))
- expectedBranch := fmt.Sprintf("gopls-release-branch.%s", strings.TrimPrefix(semver.MajorMinor(version), "v"))
- if branch != expectedBranch {
- return fmt.Errorf("expected release branch %s, got %s", expectedBranch, branch)
- }
- return nil
-}
-
-// validateHardcodedVersion reports whether the version hardcoded in the gopls
-// binary is equivalent to the version being published. It reports an error if
-// not.
-func validateHardcodedVersion(wd string, version string) error {
- pkgs, err := packages.Load(&packages.Config{
- Dir: filepath.Dir(wd),
- Mode: packages.NeedName | packages.NeedFiles |
- packages.NeedCompiledGoFiles | packages.NeedImports |
- packages.NeedTypes | packages.NeedTypesSizes,
- }, "golang.org/x/tools/internal/lsp/debug")
- if err != nil {
- return err
- }
- if len(pkgs) != 1 {
- return fmt.Errorf("expected 1 package, got %v", len(pkgs))
- }
- pkg := pkgs[0]
- obj := pkg.Types.Scope().Lookup("Version")
- c, ok := obj.(*types.Const)
- if !ok {
- return fmt.Errorf("no constant named Version")
- }
- hardcodedVersion, err := strconv.Unquote(c.Val().ExactString())
- if err != nil {
- return err
- }
- if semver.Prerelease(hardcodedVersion) != "" {
- return fmt.Errorf("unexpected pre-release for hardcoded version: %s", hardcodedVersion)
- }
- // Don't worry about pre-release tags and expect that there is no build
- // suffix.
- version = strings.TrimSuffix(version, semver.Prerelease(version))
- if hardcodedVersion != version {
- return fmt.Errorf("expected version to be %s, got %s", *versionFlag, hardcodedVersion)
- }
- return nil
-}
-
-func validateGoModFile(wd string) error {
- filename := filepath.Join(wd, "go.mod")
- data, err := ioutil.ReadFile(filename)
- if err != nil {
- return err
- }
- gomod, err := modfile.Parse(filename, data, nil)
- if err != nil {
- return err
- }
- // Confirm that there is no replace directive in the go.mod file.
- if len(gomod.Replace) > 0 {
- return fmt.Errorf("expected no replace directives, got %v", len(gomod.Replace))
- }
- // Confirm that the version of x/tools in the gopls/go.mod file points to
- // the second-to-last commit. (The last commit will be the one to update the
- // go.mod file.)
- cmd := exec.Command("git", "rev-parse", "@~")
- stdout, err := cmd.Output()
- if err != nil {
- return err
- }
- hash := string(stdout)
- // Find the golang.org/x/tools require line and compare the versions.
- var version string
- for _, req := range gomod.Require {
- if req.Mod.Path == "golang.org/x/tools" {
- version = req.Mod.Version
- break
- }
- }
- if version == "" {
- return fmt.Errorf("no require for golang.org/x/tools")
- }
- split := strings.Split(version, "-")
- if len(split) != 3 {
- return fmt.Errorf("unexpected pseudoversion format %s", version)
- }
- last := split[len(split)-1]
- if last == "" {
- return fmt.Errorf("unexpected pseudoversion format %s", version)
- }
- if !strings.HasPrefix(hash, last) {
- return fmt.Errorf("golang.org/x/tools pseudoversion should be at commit %s, instead got %s", hash, last)
- }
- return nil
-}
diff -urN a/gopls/test/debug/debug_test.go b/gopls/test/debug/debug_test.go
--- a/gopls/test/debug/debug_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/test/debug/debug_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,181 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package debug_test
-
-// Provide 'static type checking' of the templates. This guards against changes is various
-// gopls datastructures causing template execution to fail. The checking is done by
-// the github.com/jba/templatecheck pacakge. Before that is run, the test checks that
-// its list of templates and their arguments corresponds to the arguments in
-// calls to render(). The test assumes that all uses of templates are done through render().
-
-import (
- "go/ast"
- "html/template"
- "log"
- "runtime"
- "sort"
- "strings"
- "testing"
-
- "github.com/jba/templatecheck"
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/internal/lsp/cache"
- "golang.org/x/tools/internal/lsp/debug"
- "golang.org/x/tools/internal/lsp/source"
- "golang.org/x/tools/internal/span"
-)
-
-type tdata struct {
- tmpl *template.Template
- data interface{} // a value of the needed type
-}
-
-var templates = map[string]tdata{
- "MainTmpl": {debug.MainTmpl, &debug.Instance{}},
- "DebugTmpl": {debug.DebugTmpl, nil},
- "RPCTmpl": {debug.RPCTmpl, &debug.Rpcs{}},
- "TraceTmpl": {debug.TraceTmpl, debug.TraceResults{}},
- "CacheTmpl": {debug.CacheTmpl, &cache.Cache{}},
- "SessionTmpl": {debug.SessionTmpl, &cache.Session{}},
- "ViewTmpl": {debug.ViewTmpl, &cache.View{}},
- "ClientTmpl": {debug.ClientTmpl, &debug.Client{}},
- "ServerTmpl": {debug.ServerTmpl, &debug.Server{}},
- //"FileTmpl": {FileTmpl, source.Overlay{}}, // need to construct a source.Overlay in init
- "InfoTmpl": {debug.InfoTmpl, "something"},
- "MemoryTmpl": {debug.MemoryTmpl, runtime.MemStats{}},
-}
-
-// construct a source.Overlay for fileTmpl
-type fakeOverlay struct{}
-
-func (fakeOverlay) Version() int32 {
- return 0
-}
-func (fakeOverlay) Session() string {
- return ""
-}
-func (fakeOverlay) VersionedFileIdentity() source.VersionedFileIdentity {
- return source.VersionedFileIdentity{}
-}
-func (fakeOverlay) FileIdentity() source.FileIdentity {
- return source.FileIdentity{}
-}
-func (fakeOverlay) Kind() source.FileKind {
- return 0
-}
-func (fakeOverlay) Read() ([]byte, error) {
- return nil, nil
-}
-func (fakeOverlay) Saved() bool {
- return true
-}
-func (fakeOverlay) URI() span.URI {
- return ""
-}
-
-var _ source.Overlay = fakeOverlay{}
-
-func init() {
- log.SetFlags(log.Lshortfile)
- var v fakeOverlay
- templates["FileTmpl"] = tdata{debug.FileTmpl, v}
-}
-
-func TestTemplates(t *testing.T) {
- if runtime.GOOS == "android" {
- t.Skip("this test is not supported for Android")
- }
- cfg := &packages.Config{
- Mode: packages.NeedTypesInfo | packages.LoadAllSyntax, // figure out what's necessary PJW
- }
- pkgs, err := packages.Load(cfg, "golang.org/x/tools/internal/lsp/debug")
- if err != nil {
- t.Fatal(err)
- }
- if len(pkgs) != 1 {
- t.Fatalf("expected a single package, but got %d", len(pkgs))
- }
- p := pkgs[0]
- if len(p.Errors) != 0 {
- t.Fatalf("compiler error, e.g. %v", p.Errors[0])
- }
- // find the calls to render in serve.go
- tree := treeOf(p, "serve.go")
- if tree == nil {
- t.Fatalf("found no syntax tree for %s", "serve.go")
- }
- renders := callsOf(p, tree, "render")
- if len(renders) == 0 {
- t.Fatalf("found no calls to render")
- }
- var found = make(map[string]bool)
- for _, r := range renders {
- if len(r.Args) != 2 {
- // template, func
- t.Fatalf("got %d args, expected 2", len(r.Args))
- }
- t0, ok := p.TypesInfo.Types[r.Args[0]]
- if !ok || !t0.IsValue() || t0.Type.String() != "*html/template.Template" {
- t.Fatalf("no type info for template")
- }
- if id, ok := r.Args[0].(*ast.Ident); !ok {
- t.Errorf("expected *ast.Ident, got %T", r.Args[0])
- } else {
- found[id.Name] = true
- }
- }
- // make sure found and templates have the same templates
- for k := range found {
- if _, ok := templates[k]; !ok {
- t.Errorf("code has template %s, but test does not", k)
- }
- }
- for k := range templates {
- if _, ok := found[k]; !ok {
- t.Errorf("test has template %s, code does not", k)
- }
- }
- // now check all the known templates, in alphabetic order, for determinacy
- keys := []string{}
- for k := range templates {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, k := range keys {
- v := templates[k]
- // the FuncMap is an annoyance; should not be necessary
- if err := templatecheck.CheckHTML(v.tmpl, v.data); err != nil {
- t.Errorf("%s: %v", k, err)
- }
- }
-}
-
-func callsOf(p *packages.Package, tree *ast.File, name string) []*ast.CallExpr {
- var ans []*ast.CallExpr
- f := func(n ast.Node) bool {
- x, ok := n.(*ast.CallExpr)
- if !ok {
- return true
- }
- if y, ok := x.Fun.(*ast.Ident); ok {
- if y.Name == name {
- ans = append(ans, x)
- }
- }
- return true
- }
- ast.Inspect(tree, f)
- return ans
-}
-func treeOf(p *packages.Package, fname string) *ast.File {
- for _, tree := range p.Syntax {
- loc := tree.Package
- pos := p.Fset.PositionFor(loc, false)
- if strings.HasSuffix(pos.Filename, fname) {
- return tree
- }
- }
- return nil
-}
diff -urN a/gopls/test/gopls_test.go b/gopls/test/gopls_test.go
--- a/gopls/test/gopls_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/test/gopls_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,34 +0,0 @@
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gopls_test
-
-import (
- "os"
- "testing"
-
- "golang.org/x/tools/gopls/internal/hooks"
- "golang.org/x/tools/internal/lsp/bug"
- cmdtest "golang.org/x/tools/internal/lsp/cmd/test"
- "golang.org/x/tools/internal/lsp/source"
- "golang.org/x/tools/internal/lsp/tests"
- "golang.org/x/tools/internal/testenv"
-)
-
-func TestMain(m *testing.M) {
- bug.PanicOnBugs = true
- testenv.ExitIfSmallMachine()
- os.Exit(m.Run())
-}
-
-func TestCommandLine(t *testing.T) {
- cmdtest.TestCommandLine(t, "../../internal/lsp/testdata", commandLineOptions)
-}
-
-func commandLineOptions(options *source.Options) {
- options.Staticcheck = true
- options.GoDiff = false
- tests.DefaultOptions(options)
- hooks.Options(options)
-}
diff -urN a/gopls/test/json_test.go b/gopls/test/json_test.go
--- a/gopls/test/json_test.go 2000-01-01 00:00:00.000000000 -0000
+++ b/gopls/test/json_test.go 1970-01-01 01:00:00.000000000 +0100
@@ -1,134 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gopls_test
-
-import (
- "encoding/json"
- "fmt"
- "regexp"
- "strings"
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "golang.org/x/tools/internal/lsp/protocol"
-)
-
-// verify that type errors in Initialize lsp messages don't cause
-// any other unmarshalling errors. The code looks at single values and the
-// first component of array values. Each occurrence is replaced by something
-// of a different type, the resulting string unmarshalled, and compared to
-// the unmarshalling of the unchanged strings. The test passes if there is no
-// more than a single difference reported. That is, if changing a single value
-// in the message changes no more than a single value in the unmarshalled struct,
-// it is safe to ignore *json.UnmarshalTypeError.
-
-// strings are changed to numbers or bools (true)
-// bools are changed to numbers or strings
-// numbers are changed to strings or bools
-
-// a recent Initialize message taken from a log
-const input = `{"processId":38349,"clientInfo":{"name":"vscode","version":"1.56.0-insider"},"rootPath":"/Users/pjw/latest/tools","rootUri":"file:///Users/pjw/latest/tools","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional"},"didChangeConfiguration":{"dynamicRegistration":true},"didChangeWatchedFiles":{"dynamicRegistration":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]}},"executeCommand":{"dynamicRegistration":true},"configuration":true,"workspaceFolders":true,"semanticTokens":{"refreshSupport":true}},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"codeDescriptionSupport":true,"dataSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true,"resolveSupport":{"properties":["documentation","detail","additionalTextEdits"]}},"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true},"activeParameterSupport":true},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]},"labelSupport":true},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"disabledSupport":true,"dataSupport":true,"resolveSupport":{"properties":["edit"]},"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}}},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true,"prepareSupportDefaultBehavior":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"callHierarchy":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["namespace","type","class","enum","interface","struct","typeParameter","parameter","variable","property","enumMember","event","function","member","macro","keyword","modifier","comment","string","number","regexp","operator"],"tokenModifiers":["declaration","definition","readonly","static","deprecated","abstract","async","modification","documentation","defaultLibrary"],"formats":["relative"],"requests":{"range":true,"full":{"delta":true}}}},"window":{"workDoneProgress":true}},"initializationOptions":{"usePlaceholders":true,"completionDocumentation":true,"verboseOutput":false,"codelenses":{"gc_details":true},"analyses":{"fillstruct":true,"staticcheck":true},"experimentalWorkspaceModule":true,"semanticTokens":true},"trace":"off","workspaceFolders":[{"uri":"file:///Users/pjw/latest/tools","name":"tools"}]}`
-
-type DiffReporter struct {
- path cmp.Path
- diffs []string
-}
-
-func (r *DiffReporter) PushStep(ps cmp.PathStep) {
- r.path = append(r.path, ps)
-}
-
-func (r *DiffReporter) Report(rs cmp.Result) {
- if !rs.Equal() {
- vx, vy := r.path.Last().Values()
- r.diffs = append(r.diffs, fmt.Sprintf("%#v:\n\t-: %+v\n\t+: %+v\n", r.path, vx, vy))
- }
-}
-
-func (r *DiffReporter) PopStep() {
- r.path = r.path[:len(r.path)-1]
-}
-
-func (r *DiffReporter) String() string {
- return strings.Join(r.diffs, "\n")
-}
-
-func TestStringChanges(t *testing.T) {
- // string as value
- stringLeaf := regexp.MustCompile(`:("[^"]*")`)
- leafs := stringLeaf.FindAllStringSubmatchIndex(input, -1)
- allDeltas(t, leafs, "23", "true")
- // string as first element of array
- stringArray := regexp.MustCompile(`[[]("[^"]*")`)
- arrays := stringArray.FindAllStringSubmatchIndex(input, -1)
- allDeltas(t, arrays, "23", "true")
-}
-
-func TestBoolChanges(t *testing.T) {
- boolLeaf := regexp.MustCompile(`:(true|false)(,|})`)
- leafs := boolLeaf.FindAllStringSubmatchIndex(input, -1)
- allDeltas(t, leafs, "23", `"xx"`)
- boolArray := regexp.MustCompile(`:[[](true|false)(,|])`)
- arrays := boolArray.FindAllStringSubmatchIndex(input, -1)
- allDeltas(t, arrays, "23", `"xx"`)
-}
-
-func TestNumberChanges(t *testing.T) {
- numLeaf := regexp.MustCompile(`:(\d+)(,|})`)
- leafs := numLeaf.FindAllStringSubmatchIndex(input, -1)
- allDeltas(t, leafs, "true", `"xx"`)
- numArray := regexp.MustCompile(`:[[](\d+)(,|])`)
- arrays := numArray.FindAllStringSubmatchIndex(input, -1)
- allDeltas(t, arrays, "true", `"xx"`)
-}
-
-// v is a set of matches. check that substituting any repl never
-// creates more than 1 unmarshaling error
-func allDeltas(t *testing.T, v [][]int, repls ...string) {
- t.Helper()
- for _, repl := range repls {
- for i, x := range v {
- err := tryChange(x[2], x[3], repl)
- if err != nil {
- t.Errorf("%d:%q %v", i, input[x[2]:x[3]], err)
- }
- }
- }
-}
-
-func tryChange(start, end int, repl string) error {
- var p, q protocol.InitializeParams
- mod := input[:start] + repl + input[end:]
- excerpt := func() (string, string) {
- a := start - 5
- if a < 0 {
- a = 0
- }
- b := end + 5
- if b > len(input) {
- // trusting repl to be no longer than what it replaces
- b = len(input)
- }
- ma := input[a:b]
- mb := mod[a:b]
- return ma, mb
- }
- if err := json.Unmarshal([]byte(input), &p); err != nil {
- return fmt.Errorf("%s %v", repl, err)
- }
- if err := json.Unmarshal([]byte(mod), &q); err == nil {
- return nil // no errors is ok
- } else if _, ok := err.(*json.UnmarshalTypeError); !ok {
- return fmt.Errorf("%T, not *json.UnmarshalTypeError", err)
- }
-
- var r DiffReporter
- cmp.Diff(p, q, cmp.Reporter(&r))
- if len(r.diffs) > 1 { // 0 is possible, e.g., for interface{}
- ma, mb := excerpt()
- return fmt.Errorf("got %d diffs for %q\n%s\n%s", len(r.diffs), repl, ma, mb)
- }
- return nil
-}