diff --git a/go.mod b/go.mod index 8b81c68bbb..9e65dea578 100644 --- a/go.mod +++ b/go.mod @@ -9,9 +9,10 @@ require ( ) require ( - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect - golang.org/x/sys v0.0.0-20210510120138-977fb7262007 // indirect - golang.org/x/text v0.3.3 // indirect + golang.org/x/net v0.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + golang.org/x/tools v0.14.0 // indirect google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect google.golang.org/grpc v1.50.0 // indirect ) diff --git a/go.sum b/go.sum index 56a11be697..5631d83786 100644 --- a/go.sum +++ b/go.sum @@ -50,6 +50,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -65,10 +67,14 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -78,6 +84,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= diff --git a/go/private/repositories.bzl b/go/private/repositories.bzl index 2c702e93a9..8ee2fb9b82 100644 --- a/go/private/repositories.bzl +++ b/go/private/repositories.bzl @@ -64,13 +64,13 @@ def go_rules_dependencies(force = False): wrapper( http_archive, name = "org_golang_x_tools", - # v0.7.0, latest as of 2023-03-27 + # v0.14.0, latest as of 2023-10-29 urls = [ - "https://mirror.bazel.build/github.com/golang/tools/archive/refs/tags/v0.7.0.zip", - "https://github.com/golang/tools/archive/refs/tags/v0.7.0.zip", + "https://mirror.bazel.build/github.com/golang/tools/archive/refs/tags/v0.14.0.zip", + "https://github.com/golang/tools/archive/refs/tags/v0.14.0.zip", ], - sha256 = "9f20a20f29f4008d797a8be882ef82b69cf8f7f2b96dbdfe3814c57d8280fa4b", - strip_prefix = "tools-0.7.0", + sha256 = "9c71911c61a791d8b13368ffbc409a0b38859cac80a4b5039487d2a27399e8b9", + strip_prefix = "tools-0.14.0", patches = [ # deletegopls removes the gopls subdirectory. It contains a nested # module with additional dependencies. It's not needed by rules_go. @@ -105,13 +105,13 @@ def go_rules_dependencies(force = False): wrapper( http_archive, name = "org_golang_x_sys", - # v0.12.0, latest as of 2023-09-18 + # v0.13.0, latest as of 2023-10-29 urls = [ - "https://mirror.bazel.build/github.com/golang/sys/archive/refs/tags/v0.12.0.zip", - "https://github.com/golang/sys/archive/refs/tags/v0.12.0.zip", + "https://mirror.bazel.build/github.com/golang/sys/archive/refs/tags/v0.13.0.zip", + "https://github.com/golang/sys/archive/refs/tags/v0.13.0.zip", ], - sha256 = "229b079d23d18f5b1a0c46335020cddc6e5d543da2dae6e45b59d84b5d074e3a", - strip_prefix = "sys-0.12.0", + sha256 = "24abdcbecddb288fb1007f8cb814b1592531d5bb62817bc30925e3175f3f5749", + strip_prefix = "sys-0.13.0", patches = [ # releaser:patch-cmd gazelle -repo_root . -go_prefix golang.org/x/sys -go_naming_convention import_alias Label("//third_party:org_golang_x_sys-gazelle.patch"), diff --git a/go/tools/builders/nogo_main.go b/go/tools/builders/nogo_main.go index c6156e1dab..4f65f117f8 100644 --- a/go/tools/builders/nogo_main.go +++ b/go/tools/builders/nogo_main.go @@ -234,7 +234,7 @@ func checkPackage(analyzers []*analysis.Analyzer, packagePath string, packageFil // Process diagnostics and encode facts for importers of this package. diagnostics := checkAnalysisResults(roots, pkg) - facts := pkg.facts.Encode() + facts := pkg.facts.Encode(true/* skipMethodSorting */) return diagnostics, facts, nil } @@ -396,7 +396,7 @@ func load(packagePath string, imp *importer, filenames []string) (*goPackage, er } pkg.types, pkg.typesInfo = types, info - pkg.facts, err = facts.NewDecoder(pkg.types).Decode(imp.readFacts) + pkg.facts, err = facts.NewDecoder(pkg.types).Decode(true/* skipMethodSorting */, imp.readFacts) if err != nil { return nil, fmt.Errorf("internal error decoding facts: %v", err) } @@ -609,8 +609,8 @@ func (i *importer) Import(path string) (*types.Package, error) { return gcexportdata.Read(r, i.fset, i.packageCache, path) } -func (i *importer) readFacts(pkg *types.Package) ([]byte, error) { - archive := i.factMap[pkg.Path()] +func (i *importer) readFacts(pkgPath string) ([]byte, error) { + archive := i.factMap[pkgPath] if archive == "" { // Packages that were not built with the nogo toolchain will not be // analyzed, so there's no opportunity to store facts. This includes diff --git a/tests/integration/popular_repos/BUILD.bazel b/tests/integration/popular_repos/BUILD.bazel index b528656e60..79c1d68b88 100644 --- a/tests/integration/popular_repos/BUILD.bazel +++ b/tests/integration/popular_repos/BUILD.bazel @@ -102,7 +102,6 @@ test_suite( tests = [ "@org_golang_x_sys//cpu:cpu_test", "@org_golang_x_sys//execabs:execabs_test", - "@org_golang_x_sys//internal/unsafeheader:unsafeheader_test", "@org_golang_x_sys//plan9:plan9_test", "@org_golang_x_sys//unix/internal/mkmerge:mkmerge_test", "@org_golang_x_sys//windows/mkwinsyscall:mkwinsyscall_test", @@ -193,8 +192,6 @@ test_suite( "@org_golang_x_tools//internal/jsonrpc2:jsonrpc2_test", "@org_golang_x_tools//internal/jsonrpc2/servertest:servertest_test", "@org_golang_x_tools//internal/jsonrpc2_v2:jsonrpc2_v2_test", - "@org_golang_x_tools//internal/lockedfile:lockedfile_test", - "@org_golang_x_tools//internal/lockedfile/internal/filelock:filelock_test", "@org_golang_x_tools//internal/memoize:memoize_test", "@org_golang_x_tools//internal/persistent:persistent_test", "@org_golang_x_tools//internal/proxydir:proxydir_test", diff --git a/tests/integration/popular_repos/README.rst b/tests/integration/popular_repos/README.rst index 66f85a843f..446f1215cb 100644 --- a/tests/integration/popular_repos/README.rst +++ b/tests/integration/popular_repos/README.rst @@ -96,7 +96,6 @@ This runs tests from the repository `golang.org/x/sys * @org_golang_x_sys//cpu:cpu_test * @org_golang_x_sys//execabs:execabs_test -* @org_golang_x_sys//internal/unsafeheader:unsafeheader_test * @org_golang_x_sys//plan9:plan9_test * @org_golang_x_sys//unix/internal/mkmerge:mkmerge_test * @org_golang_x_sys//windows/mkwinsyscall:mkwinsyscall_test @@ -191,8 +190,6 @@ This runs tests from the repository `golang.org/x/tools +- +-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. +- +-* [VS Code](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) +-* [Lapce](https://github.com/lapce-community/lapce-go) +- +-If you use `gopls` with an editor that is not on this list, please send us a CL +-[updating 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`, run the following +-command: +- +-```sh +-go install golang.org/x/tools/gopls@latest +-``` +- +-Learn more in the +-[advanced installation instructions](doc/advanced.md#installing-unreleased-versions). +- +-Learn more about gopls releases in the [release policy](doc/releases.md). +- +-## Setting up your workspace +- +-`gopls` supports both Go module, multi-module and GOPATH modes. See the +-[workspace documentation](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. +- +-## Support Policy +- +-Gopls is maintained by engineers on the +-[Go tools team](https://github.com/orgs/golang/teams/tools-team/members), +-who actively monitor the +-[Go](https://github.com/golang/go/issues?q=is%3Aissue+is%3Aopen+label%3Agopls) +-and +-[VS Code Go](https://github.com/golang/vscode-go/issues) issue trackers. +- +-### Supported Go versions +- +-`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](https://go.dev/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. +- +-In the context of this discussion, gopls "supports" a Go version if it supports +-being built with that Go version as well as integrating with the `go` command +-of that Go version. +- +-The following table shows the final gopls version that supports a given Go +-version. Go releases more recent than any in the table can be used with any +-version of gopls. +- +-| Go Version | Final gopls version with support (without warnings) | +-| ----------- | --------------------------------------------------- | +-| Go 1.12 | [gopls@v0.7.5](https://github.com/golang/tools/releases/tag/gopls%2Fv0.7.5) | +-| Go 1.15 | [gopls@v0.9.5](https://github.com/golang/tools/releases/tag/gopls%2Fv0.9.5) | +-| Go 1.17 | [gopls@v0.11.0](https://github.com/golang/tools/releases/tag/gopls%2Fv0.11.0) | +- +-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. +- +-### Supported build systems +- +-`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 may be made to work with an appropriately configured +-`go/packages` driver. See +-[bazelbuild/rules_go#512](https://github.com/bazelbuild/rules_go/issues/512) +-for more information. +-You can follow [these instructions](https://github.com/bazelbuild/rules_go/wiki/Editor-setup) +-to configure your `gopls` to work with Bazel. +- +-### Troubleshooting +- +-If you are having issues with `gopls`, please follow the steps described in the +-[troubleshooting guide](doc/troubleshooting.md). +- +-## 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/ 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 00:00:00.000000000 +0000 ++++ b/gopls/api-diff/api_diff.go 1970-01-01 08:00:00 @@ -1,89 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -93,8 +229,8 @@ diff -urN a/gopls/api-diff/api_diff.go b/gopls/api-diff/api_diff.go -} 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 00:00:00.000000000 +0000 -@@ -1,69 +0,0 @@ ++++ b/gopls/doc/advanced.md 1970-01-01 08:00:00 +@@ -1,80 +0,0 @@ -# Advanced topics - -This documentation is for advanced `gopls` users, who may want to test @@ -139,6 +275,18 @@ diff -urN a/gopls/doc/advanced.md b/gopls/doc/advanced.md -(`export PATH=$HOME/go/bin:$PATH` on Unix systems) or by configuring your -editor. - +-To work on both `std` and `cmd` simultaneously, add a `go.work` file to +-`GOROOT/src`: +- +-``` +-cd $(go env GOROOT)/src +-go work init . cmd +-``` +- +-Note that you must work inside the `GOROOT/src` subdirectory, as the `go` +-command does not recognize `go.work` files in a parent of `GOROOT/src` +-(https://go.dev/issue/59429). +- -## Working with generic code - -Gopls has support for editing generic Go code. To enable this support, you need @@ -152,7 +300,6 @@ diff -urN a/gopls/doc/advanced.md b/gopls/doc/advanced.md - -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 @@ -166,8 +313,8 @@ diff -urN a/gopls/doc/advanced.md b/gopls/doc/advanced.md -[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 00:00:00.000000000 +0000 -@@ -1,762 +0,0 @@ ++++ b/gopls/doc/analyzers.md 1970-01-01 08:00:00 +@@ -1,837 +0,0 @@ -# Analyzers - -This document describes the analyzers that `gopls` uses inside the editor. @@ -176,6 +323,21 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md -[here](settings.md#analyses). - - +-## **appends** +- +-check for missing values after append +- +-This checker reports calls to append that pass +-no values to be appended to the slice. +- +- s := []string{"a", "b", "c"} +- _ = append(s) +- +-Such calls are always no-ops and often indicate an +-underlying mistake. +- +-**Enabled by default.** +- -## **asmdecl** - -report mismatches between assembly files and Go declarations @@ -278,6 +440,37 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md - -**Enabled by default.** - +-## **defers** +- +-report common mistakes in defer statements +- +-The defers analyzer reports a diagnostic when a defer statement would +-result in a non-deferred call to time.Since, as experience has shown +-that this is nearly always a mistake. +- +-For example: +- +- start := time.Now() +- ... +- defer recordLatency(time.Since(start)) // error: call to time.Since is not deferred +- +-The correct code is: +- +- defer func() { recordLatency(time.Since(start)) }() +- +-**Enabled by default.** +- +-## **deprecated** +- +-check for use of deprecated identifiers +- +-The deprecated analyzer looks for deprecated symbols and package imports. +- +-See https://go.dev/wiki/Deprecated to learn about Go's convention +-for documenting and signaling deprecated identifiers. +- +-**Enabled by default.** +- -## **directive** - -check Go toolchain directives such as //go:debug @@ -300,10 +493,14 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md - -## **embed** - --check for //go:embed directive import +-check //go:embed directive usage +- +-This analyzer checks that the embed package is imported if //go:embed +-directives are present, providing a suggested fix to add the import if +-it is missing. - --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". +-This analyzer also checks that //go:embed directives precede the +-declaration of a single variable. - -**Enabled by default.** - @@ -385,23 +582,6 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md -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** @@ -416,43 +596,43 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md -In this example, all the deferred functions run after the loop has -completed, so all observe the final value of v. - -- for _, v := range list { -- defer func() { -- use(v) // incorrect -- }() -- } +- for _, v := range list { +- defer func() { +- use(v) // incorrect +- }() +- } - -One fix is to create a new variable for each iteration of the loop: - -- for _, v := range list { -- v := v // new var per iteration -- defer func() { -- use(v) // ok -- }() -- } +- for _, v := range list { +- v := v // new var per iteration +- defer func() { +- use(v) // ok +- }() +- } - -The next example uses a go statement and has a similar problem. -In addition, it has a data race because the loop updates v -concurrent with the goroutines accessing it. - -- for _, v := range elem { -- go func() { -- use(v) // incorrect, and a data race -- }() -- } +- for _, v := range elem { +- go func() { +- use(v) // incorrect, and a data race +- }() +- } - -A fix is the same as before. The checker also reports problems -in goroutines started by golang.org/x/sync/errgroup.Group. -A hard-to-spot variant of this form is common in parallel tests: - -- func Test(t *testing.T) { -- for _, test := range tests { -- t.Run(test.name, func(t *testing.T) { -- t.Parallel() -- use(test) // incorrect, and a data race -- }) -- } -- } +- func Test(t *testing.T) { +- for _, test := range tests { +- t.Run(test.name, func(t *testing.T) { +- t.Parallel() +- use(test) // incorrect, and a data race +- }) +- } +- } - -The t.Parallel() call causes the rest of the function to execute -concurrent with the loop. @@ -523,22 +703,32 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md - 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. +-The check applies to calls of the formatting functions such as +-[fmt.Printf] and [fmt.Sprintf], as well as any detected wrappers of +-those 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: +-In this example, the %d format operator requires an integer operand: - -- if false { -- _ = fmt.Sprintf(format, args...) // enable printf checking +- fmt.Printf("%d", "hello") // fmt.Printf format %d has arg "hello" of wrong type string +- +-See the documentation of the fmt package for the complete set of +-format operators and their operand types. +- +-To enable printf checking on a function that is not found by this +-analyzer's heuristics (for example, because control is obscured by +-dynamic method calls), insert a bogus call: +- +- func MyPrintf(format string, args ...any) { +- if false { +- _ = fmt.Sprintf(format, args...) // enable printf checker +- } +- ... - } - -The -funcs flag specifies a comma-separated list of names of additional @@ -555,7 +745,6 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md -argument list. Otherwise it is assumed to be Print-like, taking a list -of arguments with no format string. - -- -**Enabled by default.** - -## **shadow** @@ -585,7 +774,6 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md - return err - } - -- -**Disabled by default. Enable it by setting `"analyses": {"shadow": true}`.** - -## **shift** @@ -638,6 +826,24 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md - -**Enabled by default.** - +-## **slog** +- +-check for invalid structured logging calls +- +-The slog checker looks for calls to functions from the log/slog +-package that take alternating key-value pairs. It reports calls +-where an argument in a key position is neither a string nor a +-slog.Attr, and where a final key is missing its value. +-For example,it would report +- +- slog.Warn("message", 11, "k") // slog.Warn arg "11" should be a string or a slog.Attr +- +-and +- +- slog.Info("message", "k1", v1, "k2") // call to slog.Info missing a final value +- +-**Enabled by default.** +- -## **sortslice** - -check the argument type of sort.Slice @@ -657,19 +863,19 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md -not error, to satisfy io.WriterTo: - - type myWriterTo struct{...} -- func (myWriterTo) WriteTo(w io.Writer) error { ... } +- 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** @@ -686,7 +892,6 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md -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** @@ -706,12 +911,11 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md -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 -- }() --} -- +- func TestFoo(t *testing.T) { +- go func() { +- t.Fatal("oops") // error: (*T).Fatal called from non-test goroutine +- }() +- } - -**Enabled by default.** - @@ -719,7 +923,7 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md - -check for common mistaken usages of tests and examples - --The tests checker walks Test, Benchmark and Example functions checking +-The tests checker walks Test, Benchmark, Fuzzing and Example functions checking -malformed names, wrong signatures and examples documenting non-existent -identifiers. - @@ -736,7 +940,6 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md -format. Internationally, "yyyy-dd-mm" does not occur in common calendar date -standards, and so it is more likely that 2006-01-02 (yyyy-mm-dd) was intended. - -- -**Enabled by default.** - -## **unmarshal** @@ -779,7 +982,7 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md - -To reduce false positives it ignores: -- methods --- parameters that do not have a name or are underscored +-- parameters that do not have a name or have the name '_' (the blank identifier) -- functions in test files -- functions with empty bodies or those with just a return stmt - @@ -789,9 +992,11 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md - -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. +-Some functions like fmt.Errorf return a result and have no side +-effects, so it is always a mistake to discard the result. Other +-functions may return an error that must not be ignored, or a cleanup +-operation that must be called. This analyzer reports calls to +-functions like these when the result of the call is ignored. - -The set of functions may be controlled using flags. - @@ -810,6 +1015,7 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md -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 @@ -819,11 +1025,11 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md -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** @@ -920,6 +1126,22 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md - -**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.** +- -## **stubmethods** - -stub methods analyzer @@ -932,7 +1154,7 @@ diff -urN a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md - 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 00:00:00.000000000 +0000 ++++ b/gopls/doc/command-line.md 1970-01-01 08:00:00 @@ -1,15 +0,0 @@ -# Command line - @@ -951,8 +1173,8 @@ diff -urN a/gopls/doc/command-line.md b/gopls/doc/command-line.md -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 00:00:00.000000000 +0000 -@@ -1,467 +0,0 @@ ++++ b/gopls/doc/commands.md 1970-01-01 08:00:00 +@@ -1,586 +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. @@ -996,6 +1218,22 @@ diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md -} -``` - +-### **update the given telemetry counters.** +-Identifier: `gopls.add_telemetry_counters` +- +-Gopls will prepend "fwd/" to all the counters updated using this command +-to avoid conflicts with other counters gopls collects. +- +-Args: +- +-``` +-{ +- // Names and Values must have the same length. +- "Names": []string, +- "Values": []int64, +-} +-``` +- -### **Apply a fix** -Identifier: `gopls.apply_fix` - @@ -1072,7 +1310,7 @@ diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md -Result: - -``` --map[golang.org/x/tools/gopls/internal/lsp/protocol.DocumentURI]*golang.org/x/tools/gopls/internal/govulncheck.Result +-map[golang.org/x/tools/gopls/internal/lsp/protocol.DocumentURI]*golang.org/x/tools/gopls/internal/vulncheck.Result -``` - -### **Toggle gc_details** @@ -1177,6 +1415,12 @@ diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md -} -``` - +-### **checks for the right conditions, and then prompts** +-Identifier: `gopls.maybe_prompt_for_telemetry` +- +-the user to ask if they want to enable Go telemetry uploading. If the user +-responds 'Yes', the telemetry mode is set to "on". +- -### **fetch memory statistics** -Identifier: `gopls.mem_stats` - @@ -1191,6 +1435,7 @@ diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md -{ - "HeapAlloc": uint64, - "HeapInUse": uint64, +- "TotalAlloc": uint64, -} -``` - @@ -1221,6 +1466,9 @@ diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md - "URI": string, - // The module path to remove. - "ModulePath": string, +- // If the module is tidied apart from the one unused diagnostic, we can +- // run `go get module@none`, and then run `go mod tidy`. Otherwise, we +- // must make textual edits. - "OnlyDiagnostic": bool, -} -``` @@ -1243,7 +1491,22 @@ diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md -} -``` - --### **Run govulncheck.** +-### **run `go work [args...]`, and apply the resulting go.work** +-Identifier: `gopls.run_go_work_command` +- +-edits to the current go.work file. +- +-Args: +- +-``` +-{ +- "ViewID": string, +- "InitFirst": bool, +- "Args": []string, +-} +-``` +- +-### **Run vulncheck.** -Identifier: `gopls.run_govulncheck` - -Run vulnerability check (`govulncheck`). @@ -1300,14 +1563,14 @@ diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md - // 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, @@ -1321,7 +1584,7 @@ diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md - // 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 @@ -1332,6 +1595,48 @@ diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md -} -``` - +-### **start capturing a profile of gopls' execution.** +-Identifier: `gopls.start_profile` +- +-Start a new pprof profile. Before using the resulting file, profiling must +-be stopped with a corresponding call to StopProfile. +- +-This command is intended for internal use only, by the gopls benchmark +-runner. +- +-Args: +- +-``` +-struct{} +-``` +- +-Result: +- +-``` +-struct{} +-``` +- +-### **stop an ongoing profile.** +-Identifier: `gopls.stop_profile` +- +-This command is intended for internal use only, by the gopls benchmark +-runner. +- +-Args: +- +-``` +-struct{} +-``` +- +-Result: +- +-``` +-{ +- // File is the profile file name. +- "File": string, +-} +-``` +- -### **Run test(s) (legacy)** -Identifier: `gopls.test` - @@ -1419,11 +1724,47 @@ diff -urN a/gopls/doc/commands.md b/gopls/doc/commands.md -} -``` - +-### **fetch workspace statistics** +-Identifier: `gopls.workspace_stats` +- +-Query statistics about workspace builds, modules, packages, and files. +- +-This command is intended for internal use only, by the gopls stats +-command. +- +-Result: +- +-``` +-{ +- "Files": { +- "Total": int, +- "Largest": int, +- "Errs": int, +- }, +- "Views": []{ +- "GoCommandVersion": string, +- "AllPackages": { +- "Packages": int, +- "LargestPackage": int, +- "CompiledGoFiles": int, +- "Modules": int, +- }, +- "WorkspacePackages": { +- "Packages": int, +- "LargestPackage": int, +- "CompiledGoFiles": int, +- "Modules": int, +- }, +- "Diagnostics": int, +- }, +-} +-``` +- - 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 00:00:00.000000000 +0000 -@@ -1,119 +0,0 @@ ++++ b/gopls/doc/contributing.md 1970-01-01 08:00:00 +@@ -1,165 +0,0 @@ -# Documentation for contributors - -This documentation augments the general documentation for contributing to the @@ -1472,6 +1813,52 @@ diff -urN a/gopls/doc/contributing.md b/gopls/doc/contributing.md -gophers slack. Please feel free to ask any questions about your contribution or -about contributing in general. - +- +-## Error handling +- +-It is important for the user experience that, whenever practical, +-minor logic errors in a particular feature don't cause the server to +-crash. +- +-The representation of a Go program is complex. The import graph of +-package metadata, the syntax trees of parsed files, and their +-associated type information together form a huge API surface area. +-Even when the input is valid, there are many edge cases to consider, +-and this grows by an order of magnitude when you consider missing +-imports, parse errors, and type errors. +- +-What should you do when your logic must handle an error that you +-believe "can't happen"? +- +-- If it's possible to return an error, then use the `bug.Errorf` +- function to return an error to the user, but also record the bug in +- gopls' cache so that it is less likely to be ignored. +- +-- If it's safe to proceed, you can call `bug.Reportf` to record the +- error and continue as normal. +- +-- If there's no way to proceed, call `bug.Fatalf` to record the error +- and then stop the program with `log.Fatalf`. You can also use +- `bug.Panicf` if there's a chance that a recover handler might save +- the situation. +- +-- Only if you can prove locally that an error is impossible should you +- call `log.Fatal`. If the error may happen for some input, however +- unlikely, then you should use one of the approaches above. Also, if +- the proof of safety depends on invariants broadly distributed across +- the code base, then you should instead use `bug.Panicf`. +- +-Note also that panicking is preferable to `log.Fatal` because it +-allows VS Code's crash reporting to recognize and capture the stack. +- +-Bugs reported through `bug.Errorf` and friends are retrieved using the +-`gopls bug` command, which opens a GitHub Issue template and populates +-it with a summary of each bug and its frequency. +-The text of the bug is rather fastidiously printed to stdout to avoid +-sharing user names and error message strings (which could contain +-project identifiers) with GitHub. +-Users are invited to share it if they are willing. +- -## Testing - -To run tests for just `gopls/`, run, @@ -1545,7 +1932,7 @@ diff -urN a/gopls/doc/contributing.md b/gopls/doc/contributing.md -[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 00:00:00.000000000 +0000 ++++ b/gopls/doc/daemon.md 1970-01-01 08:00:00 @@ -1,183 +0,0 @@ -# Running gopls as a daemon - @@ -1732,7 +2119,7 @@ diff -urN a/gopls/doc/daemon.md b/gopls/doc/daemon.md -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 00:00:00.000000000 +0000 ++++ b/gopls/doc/design/design.md 1970-01-01 08:00:00 @@ -1,394 +0,0 @@ -# `gopls` design documentation - @@ -2130,7 +2517,7 @@ diff -urN a/gopls/doc/design/design.md b/gopls/doc/design/design.md -[`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 00:00:00.000000000 +0000 ++++ b/gopls/doc/design/implementation.md 1970-01-01 08:00:00 @@ -1,48 +0,0 @@ -# gopls implementation documentation - @@ -2171,18 +2558,18 @@ diff -urN a/gopls/doc/design/implementation.md b/gopls/doc/design/implementation - -[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/lsp]: https://github.com/golang/tools/tree/master/gopls/internal/lsp +-[internal/lsp/cache]: https://github.com/golang/tools/tree/master/gopls/internal/lsp/cache +-[internal/lsp/cmd]: https://github.com/golang/tools/tree/master/gopls/internal/lsp/cmd +-[internal/lsp/debug]: https://github.com/golang/tools/tree/master/gopls/internal/lsp/debug +-[internal/lsp/protocol]: https://github.com/golang/tools/tree/master/gopls/internal/lsp/protocol +-[internal/lsp/source]: https://github.com/golang/tools/tree/master/gopls/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 +-[internal/span]: https://github.com/golang/tools/tree/master/gopls/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 00:00:00.000000000 +0000 ++++ b/gopls/doc/design/integrating.md 1970-01-01 08:00:00 @@ -1,91 +0,0 @@ -# Documentation for plugin authors - @@ -2277,7 +2664,7 @@ diff -urN a/gopls/doc/design/integrating.md b/gopls/doc/design/integrating.md -[#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 00:00:00.000000000 +0000 ++++ b/gopls/doc/emacs.md 1970-01-01 08:00:00 @@ -1,183 +0,0 @@ -# Emacs - @@ -2464,7 +2851,7 @@ diff -urN a/gopls/doc/emacs.md b/gopls/doc/emacs.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 00:00:00.000000000 +0000 ++++ b/gopls/doc/features.md 1970-01-01 08:00:00 @@ -1,55 +0,0 @@ -# Features - @@ -2523,8 +2910,8 @@ diff -urN a/gopls/doc/features.md b/gopls/doc/features.md - 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 00:00:00.000000000 +0000 -@@ -1,778 +0,0 @@ ++++ b/gopls/doc/generate.go 1970-01-01 08:00:00 +@@ -1,786 +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. @@ -2545,7 +2932,6 @@ diff -urN a/gopls/doc/generate.go b/gopls/doc/generate.go - "go/token" - "go/types" - "io" -- "io/ioutil" - "os" - "os/exec" - "path/filepath" @@ -2612,9 +2998,13 @@ diff -urN a/gopls/doc/generate.go b/gopls/doc/generate.go - -// pkgDir returns the directory corresponding to the import path pkgPath. -func pkgDir(pkgPath string) (string, error) { -- out, err := exec.Command("go", "list", "-f", "{{.Dir}}", pkgPath).Output() +- cmd := exec.Command("go", "list", "-f", "{{.Dir}}", pkgPath) +- out, err := cmd.Output() - if err != nil { -- return "", err +- if ee, _ := err.(*exec.ExitError); ee != nil && len(ee.Stderr) > 0 { +- return "", fmt.Errorf("%v: %w\n%s", cmd, err, ee.Stderr) +- } +- return "", fmt.Errorf("%v: %w", cmd, err) - } - return strings.TrimSpace(string(out)), nil -} @@ -2992,7 +3382,11 @@ diff -urN a/gopls/doc/generate.go b/gopls/doc/generate.go - if fld.Doc != "" && level == 0 { - doclines := strings.Split(fld.Doc, "\n") - for _, line := range doclines { -- fmt.Fprintf(&b, "%s\t// %s\n", indent, line) +- text := "" +- if line != "" { +- text = " " + line +- } +- fmt.Fprintf(&b, "%s\t//%s\n", indent, text) - } - } - tag := strings.Split(fld.JSONTag, ",")[0] @@ -3043,6 +3437,7 @@ diff -urN a/gopls/doc/generate.go b/gopls/doc/generate.go - json = append(json, &source.AnalyzerJSON{ - Name: a.Analyzer.Name, - Doc: a.Analyzer.Doc, +- URL: a.Analyzer.URL, - Default: a.Enabled, - }) - } @@ -3091,7 +3486,7 @@ diff -urN a/gopls/doc/generate.go b/gopls/doc/generate.go -} - -func rewriteFile(file string, api *source.APIJSON, write bool, rewrite func([]byte, *source.APIJSON) ([]byte, error)) (bool, error) { -- old, err := ioutil.ReadFile(file) +- old, err := os.ReadFile(file) - if err != nil { - return false, err - } @@ -3105,7 +3500,7 @@ diff -urN a/gopls/doc/generate.go b/gopls/doc/generate.go - return bytes.Equal(old, new), nil - } - -- if err := ioutil.WriteFile(file, new, 0); err != nil { +- if err := os.WriteFile(file, new, 0); err != nil { - return false, err - } - @@ -3305,8 +3700,8 @@ diff -urN a/gopls/doc/generate.go b/gopls/doc/generate.go -} 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 00:00:00.000000000 +0000 -@@ -1,26 +0,0 @@ ++++ b/gopls/doc/generate_test.go 1970-01-01 08:00:00 +@@ -1,28 +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. @@ -3320,22 +3715,24 @@ diff -urN a/gopls/doc/generate_test.go b/gopls/doc/generate_test.go -) - -func TestGenerated(t *testing.T) { -- // This test fails on 1.18 Kokoro for unknown reasons; in any case, it -- // suffices to run this test on any builder. -- testenv.NeedsGo1Point(t, 19) -- testenv.NeedsGoBuild(t) // This is a lie. We actually need the source code. +- testenv.NeedsGoPackages(t) +- // This test fails on Kokoro, for unknown reasons, so must be run only on TryBots. +- // In any case, it suffices to run this test on any builder. +- testenv.NeedsGo1Point(t, 21) +- +- testenv.NeedsLocalXTools(t) - - 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.") +- t.Error("documentation needs updating. Run: cd gopls && go generate") - } -} diff -urN a/gopls/doc/inlayHints.md b/gopls/doc/inlayHints.md --- a/gopls/doc/inlayHints.md 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/doc/inlayHints.md 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/doc/inlayHints.md 1970-01-01 08:00:00 @@ -1,80 +0,0 @@ -# Hints - @@ -3419,7 +3816,7 @@ diff -urN a/gopls/doc/inlayHints.md b/gopls/doc/inlayHints.md - diff -urN a/gopls/doc/releases.md b/gopls/doc/releases.md --- a/gopls/doc/releases.md 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/doc/releases.md 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/doc/releases.md 1970-01-01 08:00:00 @@ -1,25 +0,0 @@ -# Gopls release policy - @@ -3448,7 +3845,7 @@ diff -urN a/gopls/doc/releases.md b/gopls/doc/releases.md -For more background on this policy, see https://go.dev/issue/55267. 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 00:00:00.000000000 +0000 ++++ b/gopls/doc/semantictokens.md 1970-01-01 08:00:00 @@ -1,121 +0,0 @@ -# Semantic Tokens - @@ -3574,8 +3971,8 @@ diff -urN a/gopls/doc/semantictokens.md b/gopls/doc/semantictokens.md \ 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 00:00:00.000000000 +0000 -@@ -1,527 +0,0 @@ ++++ b/gopls/doc/settings.md 1970-01-01 08:00:00 +@@ -1,567 +0,0 @@ -# Settings - - @@ -3847,6 +4244,16 @@ diff -urN a/gopls/doc/settings.md b/gopls/doc/settings.md - -Default: `true`. - +-##### **completeFunctionCalls** *bool* +- +-completeFunctionCalls enables function call completion. +- +-When completing a statement, or when a function return type matches the +-expected of the expression being completed, completion may suggest call +-expressions (i.e. may include parentheses). +- +-Default: `true`. +- -#### Diagnostic - -##### **analyses** *map[string]bool* @@ -3920,7 +4327,21 @@ diff -urN a/gopls/doc/settings.md b/gopls/doc/settings.md - -This option must be set to a valid duration string, for example `"250ms"`. - --Default: `"250ms"`. +-Default: `"1s"`. +- +-##### **analysisProgressReporting** *bool* +- +-analysisProgressReporting controls whether gopls sends progress +-notifications when construction of its index of analysis facts is taking a +-long time. Cancelling these notifications will cancel the indexing task, +-though it will restart after the next change in the workspace. +- +-When a package is opened for the first time and heavyweight analyses such as +-staticcheck are enabled, it can take a while to construct the index of +-analysis facts for all its dependencies. The index is cached in the +-filesystem, so subsequent analysis should be faster. +- +-Default: `true`. - -#### Documentation - @@ -4034,6 +4455,22 @@ diff -urN a/gopls/doc/settings.md b/gopls/doc/settings.md - -Default: `"Dynamic"`. - +-##### **symbolScope** *enum* +- +-symbolScope controls which packages are searched for workspace/symbol +-requests. The default value, "workspace", searches only workspace +-packages. The legacy behavior, "all", causes all loaded packages to be +-searched, including dependencies; this is more expensive and may return +-unwanted results. +- +-Must be one of: +- +-* `"all"` matches symbols in any loaded package, including +-dependencies. +-* `"workspace"` matches symbols in workspace packages only. +- +-Default: `"all"`. +- -#### **verboseOutput** *bool* - -**This setting is for debugging purposes only.** @@ -4077,7 +4514,7 @@ diff -urN a/gopls/doc/settings.md b/gopls/doc/settings.md -Identifier: `regenerate_cgo` - -Regenerates cgo definitions. --### **Run govulncheck.** +-### **Run vulncheck.** - -Identifier: `run_govulncheck` - @@ -4105,7 +4542,7 @@ diff -urN a/gopls/doc/settings.md b/gopls/doc/settings.md - 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 00:00:00.000000000 +0000 ++++ b/gopls/doc/subl.md 1970-01-01 08:00:00 @@ -1,81 +0,0 @@ -# Sublime Text - @@ -4190,7 +4627,7 @@ diff -urN a/gopls/doc/subl.md b/gopls/doc/subl.md -[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 00:00:00.000000000 +0000 ++++ b/gopls/doc/troubleshooting.md 1970-01-01 08:00:00 @@ -1,48 +0,0 @@ -# Troubleshooting - @@ -4242,8 +4679,8 @@ diff -urN a/gopls/doc/troubleshooting.md b/gopls/doc/troubleshooting.md -`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 00:00:00.000000000 +0000 -@@ -1,216 +0,0 @@ ++++ b/gopls/doc/vim.md 1970-01-01 08:00:00 +@@ -1,234 +0,0 @@ -# Vim / Neovim - -* [vim-go](#vimgo) @@ -4337,7 +4774,7 @@ diff -urN a/gopls/doc/vim.md b/gopls/doc/vim.md - -```json - "languageserver": { -- "golang": { +- "go": { - "command": "gopls", - "rootPatterns": ["go.work", "go.mod", ".vim/", ".git/", ".hg/"], - "filetypes": ["go"], @@ -4386,42 +4823,60 @@ diff -urN a/gopls/doc/vim.md b/gopls/doc/vim.md -git clone 'https://github.com/neovim/nvim-lspconfig.git' . -``` - --### Custom Configuration +-### Configuration - --You can add custom configuration using Lua. Here is an example of enabling the --`unusedparams` check as well as `staticcheck`: +-nvim-lspconfig aims to provide reasonable defaults, so your setup can be very +-brief. - --```vim --lua <Imports +-### Imports and Formatting - -Use the following configuration to have your imports organized on save using --the logic of `goimports`. Note: this requires Neovim v0.7.0 or later. +-the logic of `goimports` and your code formatted. - -```lua --vim.api.nvim_create_autocmd('BufWritePre', { -- pattern = '*.go', +-autocmd("BufWritePre", { +- pattern = "*.go", - callback = function() -- vim.lsp.buf.code_action({ context = { only = { 'source.organizeImports' } }, apply = true }) +- local params = vim.lsp.util.make_range_params() +- params.context = {only = {"source.organizeImports"}} +- -- buf_request_sync defaults to a 1000ms timeout. Depending on your +- -- machine and codebase, you may want longer. Add an additional +- -- argument after params if you find that you have to write the file +- -- twice for changes to be saved. +- -- E.g., vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, 3000) +- local result = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params) +- for cid, res in pairs(result or {}) do +- for _, r in pairs(res.result or {}) do +- if r.edit then +- local enc = (vim.lsp.get_client_by_id(cid) or {}).offset_encoding or "utf-16" +- vim.lsp.util.apply_workspace_edit(r.edit, enc) +- end +- end +- end +- vim.lsp.buf.format({async = false}) - end -}) -``` @@ -4462,7 +4917,7 @@ diff -urN a/gopls/doc/vim.md b/gopls/doc/vim.md -[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 00:00:00.000000000 +0000 ++++ b/gopls/doc/workspace.md 1970-01-01 08:00:00 @@ -1,101 +0,0 @@ -# Setting up your workspace - @@ -4567,8 +5022,8 @@ diff -urN a/gopls/doc/workspace.md b/gopls/doc/workspace.md -[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 00:00:00.000000000 +0000 -@@ -1,29 +0,0 @@ ++++ b/gopls/go.mod 1970-01-01 08:00:00 +@@ -1,30 +0,0 @@ -module golang.org/x/tools/gopls - -go 1.18 @@ -4578,14 +5033,15 @@ diff -urN a/gopls/go.mod b/gopls/go.mod - 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.9.0 -- golang.org/x/sync v0.1.0 -- golang.org/x/sys v0.6.0 -- golang.org/x/text v0.8.0 -- golang.org/x/tools v0.6.0 -- golang.org/x/vuln v0.0.0-20230110180137-6ad3e3d07815 +- golang.org/x/mod v0.13.0 +- golang.org/x/sync v0.4.0 +- golang.org/x/sys v0.13.0 +- golang.org/x/telemetry v0.0.0-20231003223302-0168ef4ebbd3 +- golang.org/x/text v0.13.0 +- golang.org/x/tools v0.13.1-0.20230920233436-f9b8da7b22be +- golang.org/x/vuln v1.0.1 - gopkg.in/yaml.v3 v3.0.1 -- honnef.co/go/tools v0.4.2 +- honnef.co/go/tools v0.4.5 - mvdan.cc/gofumpt v0.4.0 - mvdan.cc/xurls/v2 v2.4.0 -) @@ -4593,33 +5049,23 @@ diff -urN a/gopls/go.mod b/gopls/go.mod -require ( - github.com/BurntSushi/toml v1.2.1 // indirect - github.com/google/safehtml v0.1.0 // indirect -- golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338 // 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 00:00:00.000000000 +0000 -@@ -1,101 +0,0 @@ --github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= ++++ b/gopls/go.sum 1970-01-01 08:00:00 +@@ -1,72 +0,0 @@ -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/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.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= --github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= --github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/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.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= --github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= --github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/safehtml v0.0.2/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= -github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8= -github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU= @@ -4629,61 +5075,45 @@ diff -urN a/gopls/go.sum b/gopls/go.sum -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/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= --github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -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.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= --golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= --golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= --golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= --golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338 h1:2O2DON6y3XMJiQRAS1UWU+54aec2uopH3x7MAiqGW6Y= -golang.org/x/exp/typeparams v0.0.0-20221212164502-fae10dda9338/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= --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/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= --golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= --golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= --golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= --golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= --golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +-golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +-golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +-golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= --golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= --golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= --golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= --golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= --golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +-golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +-golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= --golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= --golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= --golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= --golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +-golang.org/x/telemetry v0.0.0-20231003223302-0168ef4ebbd3 h1:vxxQvncMbcRAtqHV5HsHGJkbya+BIOYIY+y6cdPZhzk= +-golang.org/x/telemetry v0.0.0-20231003223302-0168ef4ebbd3/go.mod h1:ppZ76JTkRgJC2GQEgtVY3fiuJR+N8FU2MAlp+gfN1E4= +-golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= --golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= --golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= --golang.org/x/vuln v0.0.0-20230110180137-6ad3e3d07815 h1:A9kONVi4+AnuOr1dopsibH6hLi1Huy54cbeJxnq4vmU= --golang.org/x/vuln v0.0.0-20230110180137-6ad3e3d07815/go.mod h1:XJiVExZgoZfrrxoTeVsFYrSSk1snhfpOEC95JL+A4T0= --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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +-golang.org/x/vuln v1.0.1 h1:KUas02EjQK5LTuIx1OylBQdKKZ9jeugs+HiqO5HormU= +-golang.org/x/vuln v1.0.1/go.mod h1:bb2hMwln/tqxg32BNY4CcxHWtHXuYa3SbIBmtsyjxtM= -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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= @@ -4694,18 +5124,86 @@ diff -urN a/gopls/go.sum b/gopls/go.sum -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= --honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY= --honnef.co/go/tools v0.4.2 h1:6qXr+R5w+ktL5UkwEbPp+fEvfyoMPche6GkOpGHZcLc= --honnef.co/go/tools v0.4.2/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA= +-honnef.co/go/tools v0.4.5 h1:YGD4H+SuIOOqsyoLOpZDWcieM28W47/zRO7f+9V3nvo= +-honnef.co/go/tools v0.4.5/go.mod h1:GUV+uIBCLpdf0/v6UhHHG/yzI/z6qPskBeQCjcNB96k= -mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM= -mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ= --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 08:00:00 +@@ -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 08:00:00 +@@ -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 00:00:00.000000000 +0000 ++++ b/gopls/integration/govim/artifacts.go 1970-01-01 08:00:00 @@ -1,67 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -4716,7 +5214,7 @@ diff -urN a/gopls/integration/govim/artifacts.go b/gopls/integration/govim/artif -import ( - "flag" - "fmt" -- "io/ioutil" +- "io" - "net/http" - "os" - "path" @@ -4765,18 +5263,18 @@ diff -urN a/gopls/integration/govim/artifacts.go b/gopls/integration/govim/artif - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("got status code %d from GCS", resp.StatusCode) - } -- data, err := ioutil.ReadAll(resp.Body) +- data, err := io.ReadAll(resp.Body) - if err != nil { - return fmt.Errorf("reading result: %v", err) - } -- if err := ioutil.WriteFile(name, data, 0644); err != nil { +- if err := os.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 00:00:00.000000000 +0000 ++++ b/gopls/integration/govim/cloudbuild.harness.yaml 1970-01-01 08:00:00 @@ -1,21 +0,0 @@ -# Copyright 2019 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style @@ -4801,7 +5299,7 @@ diff -urN a/gopls/integration/govim/cloudbuild.harness.yaml b/gopls/integration/ - - 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 00:00:00.000000000 +0000 ++++ b/gopls/integration/govim/cloudbuild.yaml 1970-01-01 08:00:00 @@ -1,51 +0,0 @@ -# Copyright 2019 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style @@ -4854,80 +5352,9 @@ diff -urN a/gopls/integration/govim/cloudbuild.yaml b/gopls/integration/govim/cl -# 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/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 00:00:00.000000000 +0000 -@@ -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 00:00:00.000000000 +0000 -@@ -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/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 00:00:00.000000000 +0000 ++++ b/gopls/integration/govim/run_local.sh 1970-01-01 08:00:00 @@ -1,96 +0,0 @@ -#!/bin/bash -e - @@ -5027,7 +5454,7 @@ diff -urN a/gopls/integration/govim/run_local.sh b/gopls/integration/govim/run_l - -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 00:00:00.000000000 +0000 ++++ b/gopls/integration/govim/run_tests_for_cloudbuild.sh 1970-01-01 08:00:00 @@ -1,28 +0,0 @@ -#!/bin/bash - @@ -5057,9 +5484,486 @@ diff -urN a/gopls/integration/govim/run_tests_for_cloudbuild.sh b/gopls/integrat - # 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/astutil/purge.go b/gopls/internal/astutil/purge.go +--- a/gopls/internal/astutil/purge.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/astutil/purge.go 1970-01-01 08:00:00 +@@ -1,74 +0,0 @@ +-// Copyright 2023 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 astutil provides various AST utility functions for gopls. +-package astutil +- +-import ( +- "bytes" +- "go/scanner" +- "go/token" +- +- "golang.org/x/tools/gopls/internal/lsp/safetoken" +-) +- +-// PurgeFuncBodies returns a copy of src in which the contents of each +-// outermost {...} region except struct and interface types have been +-// deleted. This reduces the amount of work required to parse the +-// top-level declarations. +-// +-// PurgeFuncBodies does not preserve newlines or position information. +-// Also, if the input is invalid, parsing the output of +-// PurgeFuncBodies may result in a different tree due to its effects +-// on parser error recovery. +-func PurgeFuncBodies(src []byte) []byte { +- // Destroy the content of any {...}-bracketed regions that are +- // not immediately preceded by a "struct" or "interface" +- // token. That includes function bodies, composite literals, +- // switch/select bodies, and all blocks of statements. +- // This will lead to non-void functions that don't have return +- // statements, which of course is a type error, but that's ok. +- +- var out bytes.Buffer +- file := token.NewFileSet().AddFile("", -1, len(src)) +- var sc scanner.Scanner +- sc.Init(file, src, nil, 0) +- var prev token.Token +- var cursor int // last consumed src offset +- var braces []token.Pos // stack of unclosed braces or -1 for struct/interface type +- for { +- pos, tok, _ := sc.Scan() +- if tok == token.EOF { +- break +- } +- switch tok { +- case token.COMMENT: +- // TODO(adonovan): opt: skip, to save an estimated 20% of time. +- +- case token.LBRACE: +- if prev == token.STRUCT || prev == token.INTERFACE { +- pos = -1 +- } +- braces = append(braces, pos) +- +- case token.RBRACE: +- if last := len(braces) - 1; last >= 0 { +- top := braces[last] +- braces = braces[:last] +- if top < 0 { +- // struct/interface type: leave alone +- } else if len(braces) == 0 { // toplevel only +- // Delete {...} body. +- start, _ := safetoken.Offset(file, top) +- end, _ := safetoken.Offset(file, pos) +- out.Write(src[cursor : start+len("{")]) +- cursor = end +- } +- } +- } +- prev = tok +- } +- out.Write(src[cursor:]) +- return out.Bytes() +-} +diff -urN a/gopls/internal/astutil/purge_test.go b/gopls/internal/astutil/purge_test.go +--- a/gopls/internal/astutil/purge_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/astutil/purge_test.go 1970-01-01 08:00:00 +@@ -1,89 +0,0 @@ +-// Copyright 2023 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 astutil_test +- +-import ( +- "go/ast" +- "go/parser" +- "go/token" +- "os" +- "reflect" +- "testing" +- +- "golang.org/x/tools/go/packages" +- "golang.org/x/tools/gopls/internal/astutil" +- "golang.org/x/tools/internal/testenv" +-) +- +-// TestPurgeFuncBodies tests PurgeFuncBodies by comparing it against a +-// (less efficient) reference implementation that purges after parsing. +-func TestPurgeFuncBodies(t *testing.T) { +- testenv.NeedsGoBuild(t) // we need the source code for std +- +- // Load a few standard packages. +- config := packages.Config{Mode: packages.NeedCompiledGoFiles} +- pkgs, err := packages.Load(&config, "encoding/...") +- if err != nil { +- t.Fatal(err) +- } +- +- // preorder returns the nodes of tree f in preorder. +- preorder := func(f *ast.File) (nodes []ast.Node) { +- ast.Inspect(f, func(n ast.Node) bool { +- if n != nil { +- nodes = append(nodes, n) +- } +- return true +- }) +- return nodes +- } +- +- packages.Visit(pkgs, nil, func(p *packages.Package) { +- for _, filename := range p.CompiledGoFiles { +- content, err := os.ReadFile(filename) +- if err != nil { +- t.Fatal(err) +- } +- +- fset := token.NewFileSet() +- +- // Parse then purge (reference implementation). +- f1, _ := parser.ParseFile(fset, filename, content, 0) +- ast.Inspect(f1, func(n ast.Node) bool { +- switch n := n.(type) { +- case *ast.FuncDecl: +- if n.Body != nil { +- n.Body.List = nil +- } +- case *ast.FuncLit: +- n.Body.List = nil +- case *ast.CompositeLit: +- n.Elts = nil +- } +- return true +- }) +- +- // Purge before parse (logic under test). +- f2, _ := parser.ParseFile(fset, filename, astutil.PurgeFuncBodies(content), 0) +- +- // Compare sequence of node types. +- nodes1 := preorder(f1) +- nodes2 := preorder(f2) +- if len(nodes2) < len(nodes1) { +- t.Errorf("purged file has fewer nodes: %d vs %d", +- len(nodes2), len(nodes1)) +- nodes1 = nodes1[:len(nodes2)] // truncate +- } +- for i := range nodes1 { +- x, y := nodes1[i], nodes2[i] +- if reflect.TypeOf(x) != reflect.TypeOf(y) { +- t.Errorf("%s: got %T, want %T", +- fset.Position(x.Pos()), y, x) +- break +- } +- } +- } +- }) +-} +diff -urN a/gopls/internal/astutil/util.go b/gopls/internal/astutil/util.go +--- a/gopls/internal/astutil/util.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/astutil/util.go 1970-01-01 08:00:00 +@@ -1,61 +0,0 @@ +-// Copyright 2023 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 astutil +- +-import ( +- "go/ast" +- +- "golang.org/x/tools/internal/typeparams" +-) +- +-// UnpackRecv unpacks a receiver type expression, reporting whether it is a +-// pointer recever, along with the type name identifier and any receiver type +-// parameter identifiers. +-// +-// Copied (with modifications) from go/types. +-func UnpackRecv(rtyp ast.Expr) (ptr bool, rname *ast.Ident, tparams []*ast.Ident) { +-L: // unpack receiver type +- // This accepts invalid receivers such as ***T and does not +- // work for other invalid receivers, but we don't care. The +- // validity of receiver expressions is checked elsewhere. +- for { +- switch t := rtyp.(type) { +- case *ast.ParenExpr: +- rtyp = t.X +- case *ast.StarExpr: +- ptr = true +- rtyp = t.X +- default: +- break L +- } +- } +- +- // unpack type parameters, if any +- switch rtyp.(type) { +- case *ast.IndexExpr, *typeparams.IndexListExpr: +- var indices []ast.Expr +- rtyp, _, indices, _ = typeparams.UnpackIndexExpr(rtyp) +- for _, arg := range indices { +- var par *ast.Ident +- switch arg := arg.(type) { +- case *ast.Ident: +- par = arg +- default: +- // ignore errors +- } +- if par == nil { +- par = &ast.Ident{NamePos: arg.Pos(), Name: "_"} +- } +- tparams = append(tparams, par) +- } +- } +- +- // unpack receiver name +- if name, _ := rtyp.(*ast.Ident); name != nil { +- rname = name +- } +- +- return +-} +diff -urN a/gopls/internal/bug/bug.go b/gopls/internal/bug/bug.go +--- a/gopls/internal/bug/bug.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/bug/bug.go 1970-01-01 08:00:00 +@@ -1,142 +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 bug provides utilities for reporting internal bugs, and being +-// notified when they occur. +-// +-// Philosophically, because gopls runs as a sidecar process that the user does +-// not directly control, sometimes it keeps going on broken invariants rather +-// than panicking. In those cases, bug reports provide a mechanism to alert +-// developers and capture relevant metadata. +-package bug +- +-import ( +- "fmt" +- "runtime" +- "runtime/debug" +- "sort" +- "sync" +- "time" +- +- "golang.org/x/telemetry/counter" +-) +- +-// PanicOnBugs controls whether to panic when bugs are reported. +-// +-// It may be set to true during testing. +-var PanicOnBugs = false +- +-var ( +- mu sync.Mutex +- exemplars map[string]Bug +- handlers []func(Bug) +-) +- +-// A Bug represents an unexpected event or broken invariant. They are used for +-// capturing metadata that helps us understand the event. +-// +-// Bugs are JSON-serializable. +-type Bug struct { +- File string // file containing the call to bug.Report +- Line int // line containing the call to bug.Report +- Description string // description of the bug +- Key string // key identifying the bug (file:line if available) +- Stack string // call stack +- AtTime time.Time // time the bug was reported +-} +- +-// Reportf reports a formatted bug message. +-func Reportf(format string, args ...interface{}) { +- report(fmt.Sprintf(format, args...)) +-} +- +-// Errorf calls fmt.Errorf for the given arguments, and reports the resulting +-// error message as a bug. +-func Errorf(format string, args ...interface{}) error { +- err := fmt.Errorf(format, args...) +- report(err.Error()) +- return err +-} +- +-// Report records a new bug encountered on the server. +-// It uses reflection to report the position of the immediate caller. +-func Report(description string) { +- report(description) +-} +- +-// BugReportCount is a telemetry counter that tracks # of bug reports. +-var BugReportCount = counter.NewStack("gopls/bug", 16) +- +-func report(description string) { +- _, file, line, ok := runtime.Caller(2) // all exported reporting functions call report directly +- +- key := "" +- if ok { +- key = fmt.Sprintf("%s:%d", file, line) +- } +- +- if PanicOnBugs { +- panic(fmt.Sprintf("%s: %s", key, description)) +- } +- +- bug := Bug{ +- File: file, +- Line: line, +- Description: description, +- Key: key, +- Stack: string(debug.Stack()), +- AtTime: time.Now(), +- } +- +- newBug := false +- mu.Lock() +- if _, ok := exemplars[key]; !ok { +- if exemplars == nil { +- exemplars = make(map[string]Bug) +- } +- exemplars[key] = bug // capture one exemplar per key +- newBug = true +- } +- hh := handlers +- handlers = nil +- mu.Unlock() +- +- if newBug { +- BugReportCount.Inc() +- } +- // Call the handlers outside the critical section since a +- // handler may itself fail and call bug.Report. Since handlers +- // are one-shot, the inner call should be trivial. +- for _, handle := range hh { +- handle(bug) +- } +-} +- +-// Handle adds a handler function that will be called with the next +-// bug to occur on the server. The handler only ever receives one bug. +-// It is called synchronously, and should return in a timely manner. +-func Handle(h func(Bug)) { +- mu.Lock() +- defer mu.Unlock() +- handlers = append(handlers, h) +-} +- +-// List returns a slice of bug exemplars -- the first bugs to occur at each +-// callsite. +-func List() []Bug { +- mu.Lock() +- defer mu.Unlock() +- +- var bugs []Bug +- +- for _, bug := range exemplars { +- bugs = append(bugs, bug) +- } +- +- sort.Slice(bugs, func(i, j int) bool { +- return bugs[i].Key < bugs[j].Key +- }) +- +- return bugs +-} +diff -urN a/gopls/internal/bug/bug_test.go b/gopls/internal/bug/bug_test.go +--- a/gopls/internal/bug/bug_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/bug/bug_test.go 1970-01-01 08:00:00 +@@ -1,91 +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 bug +- +-import ( +- "encoding/json" +- "fmt" +- "testing" +- "time" +- +- "github.com/google/go-cmp/cmp" +-) +- +-func resetForTesting() { +- exemplars = nil +- handlers = nil +-} +- +-func TestListBugs(t *testing.T) { +- defer resetForTesting() +- +- Report("bad") +- +- wantBugs(t, "bad") +- +- for i := 0; i < 3; i++ { +- Report(fmt.Sprintf("index:%d", i)) +- } +- +- wantBugs(t, "bad", "index:0") +-} +- +-func wantBugs(t *testing.T, want ...string) { +- t.Helper() +- +- bugs := List() +- if got, want := len(bugs), len(want); got != want { +- t.Errorf("List(): got %d bugs, want %d", got, want) +- return +- } +- +- for i, b := range bugs { +- if got, want := b.Description, want[i]; got != want { +- t.Errorf("bug.List()[%d] = %q, want %q", i, got, want) +- } +- } +-} +- +-func TestBugHandler(t *testing.T) { +- defer resetForTesting() +- +- Report("unseen") +- +- // Both handlers are called, in order of registration, only once. +- var got string +- Handle(func(b Bug) { got += "1:" + b.Description }) +- Handle(func(b Bug) { got += "2:" + b.Description }) +- +- Report("seen") +- +- Report("again") +- +- if want := "1:seen2:seen"; got != want { +- t.Errorf("got %q, want %q", got, want) +- } +-} +- +-func TestBugJSON(t *testing.T) { +- b1 := Bug{ +- File: "foo.go", +- Line: 1, +- Description: "a bug", +- Key: "foo.go:1", +- Stack: "", +- AtTime: time.Now(), +- } +- +- data, err := json.Marshal(b1) +- if err != nil { +- t.Fatal(err) +- } +- var b2 Bug +- if err := json.Unmarshal(data, &b2); err != nil { +- t.Fatal(err) +- } +- if diff := cmp.Diff(b1, b2); diff != "" { +- t.Errorf("bugs differ after JSON Marshal/Unmarshal (-b1 +b2):\n%s", diff) +- } +-} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/coverage/coverage.go 1970-01-01 08:00:00 @@ -1,266 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -5327,463 +6231,9 @@ diff -urN a/gopls/internal/coverage/coverage.go b/gopls/internal/coverage/covera - filepath.WalkDir(dir, f) - return ans -} -diff -urN a/gopls/internal/govulncheck/semver/semver.go b/gopls/internal/govulncheck/semver/semver.go ---- a/gopls/internal/govulncheck/semver/semver.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/govulncheck/semver/semver.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,51 +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 semver provides shared utilities for manipulating --// Go semantic versions. --package semver -- --import ( -- "regexp" -- "strings" --) -- --// addSemverPrefix adds a 'v' prefix to s if it isn't already prefixed --// with 'v' or 'go'. This allows us to easily test go-style SEMVER --// strings against normal SEMVER strings. --func addSemverPrefix(s string) string { -- if !strings.HasPrefix(s, "v") && !strings.HasPrefix(s, "go") { -- return "v" + s -- } -- return s --} -- --// removeSemverPrefix removes the 'v' or 'go' prefixes from go-style --// SEMVER strings, for usage in the public vulnerability format. --func removeSemverPrefix(s string) string { -- s = strings.TrimPrefix(s, "v") -- s = strings.TrimPrefix(s, "go") -- return s --} -- --// CanonicalizeSemverPrefix turns a SEMVER string into the canonical --// representation using the 'v' prefix, as used by the OSV format. --// Input may be a bare SEMVER ("1.2.3"), Go prefixed SEMVER ("go1.2.3"), --// or already canonical SEMVER ("v1.2.3"). --func CanonicalizeSemverPrefix(s string) string { -- return addSemverPrefix(removeSemverPrefix(s)) --} -- --var ( -- // Regexp for matching go tags. The groups are: -- // 1 the major.minor version -- // 2 the patch version, or empty if none -- // 3 the entire prerelease, if present -- // 4 the prerelease type ("beta" or "rc") -- // 5 the prerelease number -- tagRegexp = regexp.MustCompile(`^go(\d+\.\d+)(\.\d+|)((beta|rc|-pre)(\d+))?$`) --) -diff -urN a/gopls/internal/govulncheck/semver/semver_test.go b/gopls/internal/govulncheck/semver/semver_test.go ---- a/gopls/internal/govulncheck/semver/semver_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/govulncheck/semver/semver_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,28 +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 semver -- --import ( -- "testing" --) -- --func TestCanonicalize(t *testing.T) { -- for _, test := range []struct { -- v string -- want string -- }{ -- {"v1.2.3", "v1.2.3"}, -- {"1.2.3", "v1.2.3"}, -- {"go1.2.3", "v1.2.3"}, -- } { -- got := CanonicalizeSemverPrefix(test.v) -- if got != test.want { -- t.Errorf("want %s; got %s", test.want, got) -- } -- } --} -diff -urN a/gopls/internal/govulncheck/types_118.go b/gopls/internal/govulncheck/types_118.go ---- a/gopls/internal/govulncheck/types_118.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/govulncheck/types_118.go 1970-01-01 00:00:00.000000000 +0000 -@@ -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. -- --//go:build go1.18 --// +build go1.18 -- --// Package govulncheck provides an experimental govulncheck API. --package govulncheck -- --import ( -- "golang.org/x/vuln/exp/govulncheck" --) -- --var ( -- // Source reports vulnerabilities that affect the analyzed packages. -- Source = govulncheck.Source -- -- // DefaultCache constructs cache for a vulnerability database client. -- DefaultCache = govulncheck.DefaultCache --) -- --type ( -- // Config is the configuration for Main. -- Config = govulncheck.Config -- -- // Vuln represents a single OSV entry. -- Vuln = govulncheck.Vuln -- -- // Module represents a specific vulnerability relevant to a -- // single module or package. -- Module = govulncheck.Module -- -- // Package is a Go package with known vulnerable symbols. -- Package = govulncheck.Package -- -- // CallStacks contains a representative call stack for each -- // vulnerable symbol that is called. -- CallStack = govulncheck.CallStack -- -- // StackFrame represents a call stack entry. -- StackFrame = govulncheck.StackFrame --) -diff -urN a/gopls/internal/govulncheck/types.go b/gopls/internal/govulncheck/types.go ---- a/gopls/internal/govulncheck/types.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/govulncheck/types.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,37 +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 govulncheck -- --import "time" -- --// Result is the result of vulnerability scanning. --type Result struct { -- // Vulns contains all vulnerabilities that are called or imported by -- // the analyzed module. -- Vulns []*Vuln `json:",omitempty"` -- -- // Mode contains the source of the vulnerability info. -- // Clients of the gopls.fetch_vulncheck_result command may need -- // to interpret the vulnerabilities differently based on the -- // analysis mode. For example, Vuln without callstack traces -- // indicate a vulnerability that is not used if the result was -- // from 'govulncheck' analysis mode. On the other hand, Vuln -- // without callstack traces just implies the package with the -- // vulnerability is known to the workspace and we do not know -- // whether the vulnerable symbols are actually used or not. -- Mode AnalysisMode `json:",omitempty"` -- -- // AsOf describes when this Result was computed using govulncheck. -- // It is valid only with the govulncheck analysis mode. -- AsOf time.Time `json:",omitempty"` --} -- --type AnalysisMode string -- --const ( -- ModeInvalid AnalysisMode = "" // zero value -- ModeGovulncheck AnalysisMode = "govulncheck" -- ModeImports AnalysisMode = "imports" --) -diff -urN a/gopls/internal/govulncheck/types_not118.go b/gopls/internal/govulncheck/types_not118.go ---- a/gopls/internal/govulncheck/types_not118.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/govulncheck/types_not118.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,126 +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 ( -- "go/token" -- -- "golang.org/x/vuln/osv" --) -- --// Vuln represents a single OSV entry. --type Vuln struct { -- // OSV contains all data from the OSV entry for this vulnerability. -- OSV *osv.Entry -- -- // Modules contains all of the modules in the OSV entry where a -- // vulnerable package is imported by the target source code or binary. -- // -- // For example, a module M with two packages M/p1 and M/p2, where only p1 -- // is vulnerable, will appear in this list if and only if p1 is imported by -- // the target source code or binary. -- Modules []*Module --} -- --func (v *Vuln) IsCalled() bool { -- return false --} -- --// Module represents a specific vulnerability relevant to a single module. --type Module struct { -- // Path is the module path of the module containing the vulnerability. -- // -- // Importable packages in the standard library will have the path "stdlib". -- Path string -- -- // FoundVersion is the module version where the vulnerability was found. -- FoundVersion string -- -- // FixedVersion is the module version where the vulnerability was -- // fixed. If there are multiple fixed versions in the OSV report, this will -- // be the latest fixed version. -- // -- // This is empty if a fix is not available. -- FixedVersion string -- -- // Packages contains all the vulnerable packages in OSV entry that are -- // imported by the target source code or binary. -- // -- // For example, given a module M with two packages M/p1 and M/p2, where -- // both p1 and p2 are vulnerable, p1 and p2 will each only appear in this -- // list they are individually imported by the target source code or binary. -- Packages []*Package --} -- --// Package is a Go package with known vulnerable symbols. --type Package struct { -- // Path is the import path of the package containing the vulnerability. -- Path string -- -- // CallStacks contains a representative call stack for each -- // vulnerable symbol that is called. -- // -- // For vulnerabilities found from binary analysis, only CallStack.Symbol -- // will be provided. -- // -- // For non-affecting vulnerabilities reported from the source mode -- // analysis, this will be empty. -- CallStacks []CallStack --} -- --// CallStacks contains a representative call stack for a vulnerable --// symbol. --type CallStack struct { -- // Symbol is the name of the detected vulnerable function -- // or method. -- // -- // This follows the naming convention in the OSV report. -- Symbol string -- -- // Summary is a one-line description of the callstack, used by the -- // default govulncheck mode. -- // -- // Example: module3.main calls github.com/shiyanhui/dht.DHT.Run -- Summary string -- -- // Frames contains an entry for each stack in the call stack. -- // -- // Frames are sorted starting from the entry point to the -- // imported vulnerable symbol. The last frame in Frames should match -- // Symbol. -- Frames []*StackFrame --} -- --// StackFrame represents a call stack entry. --type StackFrame struct { -- // PackagePath is the import path. -- PkgPath string -- -- // FuncName is the function name. -- FuncName string -- -- // RecvType is the fully qualified receiver type, -- // if the called symbol is a method. -- // -- // The client can create the final symbol name by -- // prepending RecvType to FuncName. -- RecvType string -- -- // Position describes an arbitrary source position -- // including the file, line, and column location. -- // A Position is valid if the line number is > 0. -- Position token.Position --} -- --func (sf *StackFrame) Name() string { -- return "" --} -- --func (sf *StackFrame) Pos() string { -- return "" --} -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 00:00:00.000000000 +0000 -@@ -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. -- --//go:build go1.18 --// +build go1.18 -- --package govulncheck -- --import ( -- "golang.org/x/mod/semver" -- isem "golang.org/x/tools/gopls/internal/govulncheck/semver" -- "golang.org/x/vuln/osv" --) -- --// LatestFixed returns the latest fixed version in the list of affected ranges, --// or the empty string if there are no fixed versions. --func LatestFixed(modulePath string, as []osv.Affected) string { -- v := "" -- for _, a := range as { -- if a.Package.Name != modulePath { -- continue -- } -- for _, r := range a.Ranges { -- if r.Type == osv.TypeSemver { -- for _, e := range r.Events { -- if e.Fixed != "" && (v == "" || -- semver.Compare(isem.CanonicalizeSemverPrefix(e.Fixed), isem.CanonicalizeSemverPrefix(v)) > 0) { -- v = e.Fixed -- } -- } -- } -- } -- } -- return v --} -diff -urN a/gopls/internal/govulncheck/vulncache.go b/gopls/internal/govulncheck/vulncache.go ---- a/gopls/internal/govulncheck/vulncache.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/govulncheck/vulncache.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,105 +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 ( -- "sync" -- "time" -- -- vulnc "golang.org/x/vuln/client" -- "golang.org/x/vuln/osv" --) -- --// inMemoryCache is an implementation of the [client.Cache] interface --// that "decorates" another instance of that interface to provide --// an additional layer of (memory-based) caching. --type inMemoryCache struct { -- mu sync.Mutex -- underlying vulnc.Cache -- db map[string]*db --} -- --var _ vulnc.Cache = &inMemoryCache{} -- --type db struct { -- retrieved time.Time -- index vulnc.DBIndex -- entry map[string][]*osv.Entry --} -- --// NewInMemoryCache returns a new memory-based cache that decorates --// the provided cache (file-based, perhaps). --func NewInMemoryCache(underlying vulnc.Cache) *inMemoryCache { -- return &inMemoryCache{ -- underlying: underlying, -- db: make(map[string]*db), -- } --} -- --func (c *inMemoryCache) lookupDBLocked(dbName string) *db { -- cached := c.db[dbName] -- if cached == nil { -- cached = &db{entry: make(map[string][]*osv.Entry)} -- c.db[dbName] = cached -- } -- return cached --} -- --// ReadIndex returns the index for dbName from the cache, or returns zero values --// if it is not present. --func (c *inMemoryCache) ReadIndex(dbName string) (vulnc.DBIndex, time.Time, error) { -- c.mu.Lock() -- defer c.mu.Unlock() -- cached := c.lookupDBLocked(dbName) -- -- if cached.retrieved.IsZero() { -- // First time ReadIndex is called. -- index, retrieved, err := c.underlying.ReadIndex(dbName) -- if err != nil { -- return index, retrieved, err -- } -- cached.index, cached.retrieved = index, retrieved -- } -- return cached.index, cached.retrieved, nil --} -- --// WriteIndex puts the index and retrieved time into the cache. --func (c *inMemoryCache) WriteIndex(dbName string, index vulnc.DBIndex, retrieved time.Time) error { -- c.mu.Lock() -- defer c.mu.Unlock() -- cached := c.lookupDBLocked(dbName) -- cached.index, cached.retrieved = index, retrieved -- // TODO(hyangah): shouldn't we invalidate all cached entries? -- return c.underlying.WriteIndex(dbName, index, retrieved) --} -- --// ReadEntries returns the vulndb entries for path from the cache. --func (c *inMemoryCache) ReadEntries(dbName, path string) ([]*osv.Entry, error) { -- c.mu.Lock() -- defer c.mu.Unlock() -- cached := c.lookupDBLocked(dbName) -- entries, ok := cached.entry[path] -- if !ok { -- // cache miss -- entries, err := c.underlying.ReadEntries(dbName, path) -- if err != nil { -- return entries, err -- } -- cached.entry[path] = entries -- } -- return entries, nil --} -- --// WriteEntries puts the entries for path into the cache. --func (c *inMemoryCache) WriteEntries(dbName, path string, entries []*osv.Entry) error { -- c.mu.Lock() -- defer c.mu.Unlock() -- cached := c.lookupDBLocked(dbName) -- cached.entry[path] = entries -- return c.underlying.WriteEntries(dbName, path, entries) --} diff -urN a/gopls/internal/hooks/analysis_116.go b/gopls/internal/hooks/analysis_116.go --- a/gopls/internal/hooks/analysis_116.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/hooks/analysis_116.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/hooks/analysis_116.go 1970-01-01 08:00:00 @@ -1,14 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -5801,7 +6251,7 @@ diff -urN a/gopls/internal/hooks/analysis_116.go b/gopls/internal/hooks/analysis -} diff -urN a/gopls/internal/hooks/analysis_119.go b/gopls/internal/hooks/analysis_119.go --- a/gopls/internal/hooks/analysis_119.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/hooks/analysis_119.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/hooks/analysis_119.go 1970-01-01 08:00:00 @@ -1,62 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -5867,8 +6317,8 @@ diff -urN a/gopls/internal/hooks/analysis_119.go b/gopls/internal/hooks/analysis -} 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 00:00:00.000000000 +0000 -@@ -1,169 +0,0 @@ ++++ b/gopls/internal/hooks/diff.go 1970-01-01 08:00:00 +@@ -1,168 +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. @@ -5878,7 +6328,6 @@ diff -urN a/gopls/internal/hooks/diff.go b/gopls/internal/hooks/diff.go -import ( - "encoding/json" - "fmt" -- "io/ioutil" - "log" - "os" - "path/filepath" @@ -5887,7 +6336,7 @@ diff -urN a/gopls/internal/hooks/diff.go b/gopls/internal/hooks/diff.go - "time" - - "github.com/sergi/go-diff/diffmatchpatch" -- "golang.org/x/tools/internal/bug" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/internal/diff" -) - @@ -5913,7 +6362,7 @@ diff -urN a/gopls/internal/hooks/diff.go b/gopls/internal/hooks/diff.go -// save writes a JSON record of statistics about diff requests to a temporary file. -func (s *diffstat) save() { - diffStatsOnce.Do(func() { -- f, err := ioutil.TempFile("", "gopls-diff-stats-*") +- f, err := os.CreateTemp("", "gopls-diff-stats-*") - if err != nil { - log.Printf("can't create diff stats temp file: %v", err) // e.g. disk full - return @@ -5962,7 +6411,7 @@ diff -urN a/gopls/internal/hooks/diff.go b/gopls/internal/hooks/diff.go - // We use NUL as a separator: it should never appear in Go source. - data := before + "\x00" + after - -- if err := ioutil.WriteFile(filename, []byte(data), 0600); err != nil { +- if err := os.WriteFile(filename, []byte(data), 0600); err != nil { - log.Printf("failed to write diff bug report: %v", err) - return "" - } @@ -6040,8 +6489,8 @@ diff -urN a/gopls/internal/hooks/diff.go b/gopls/internal/hooks/diff.go -} 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 00:00:00.000000000 +0000 -@@ -1,33 +0,0 @@ ++++ b/gopls/internal/hooks/diff_test.go 1970-01-01 08:00:00 +@@ -1,32 +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. @@ -6049,7 +6498,6 @@ diff -urN a/gopls/internal/hooks/diff_test.go b/gopls/internal/hooks/diff_test.g -package hooks - -import ( -- "io/ioutil" - "os" - "testing" - @@ -6064,7 +6512,7 @@ diff -urN a/gopls/internal/hooks/diff_test.go b/gopls/internal/hooks/diff_test.g - a := "This is a string,(\u0995) just for basic\nfunctionality" - b := "This is another string, (\u0996) to see if disaster will store stuff correctly" - fname := disaster(a, b) -- buf, err := ioutil.ReadFile(fname) +- buf, err := os.ReadFile(fname) - if err != nil { - t.Fatal(err) - } @@ -6077,7 +6525,7 @@ diff -urN a/gopls/internal/hooks/diff_test.go b/gopls/internal/hooks/diff_test.g -} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/hooks/gen-licenses.sh 1970-01-01 08:00:00 @@ -1,38 +0,0 @@ -#!/bin/bash -eu - @@ -6120,7 +6568,7 @@ diff -urN a/gopls/internal/hooks/gen-licenses.sh b/gopls/internal/hooks/gen-lice \ No newline at end of file diff -urN a/gopls/internal/hooks/gofumpt_117.go b/gopls/internal/hooks/gofumpt_117.go --- a/gopls/internal/hooks/gofumpt_117.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/hooks/gofumpt_117.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/hooks/gofumpt_117.go 1970-01-01 08:00:00 @@ -1,13 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -6137,8 +6585,8 @@ diff -urN a/gopls/internal/hooks/gofumpt_117.go b/gopls/internal/hooks/gofumpt_1 -} diff -urN a/gopls/internal/hooks/gofumpt_118.go b/gopls/internal/hooks/gofumpt_118.go --- a/gopls/internal/hooks/gofumpt_118.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/hooks/gofumpt_118.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ ++++ b/gopls/internal/hooks/gofumpt_118.go 1970-01-01 08:00:00 +@@ -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. @@ -6150,6 +6598,7 @@ diff -urN a/gopls/internal/hooks/gofumpt_118.go b/gopls/internal/hooks/gofumpt_1 - -import ( - "context" +- "fmt" - - "golang.org/x/tools/gopls/internal/lsp/source" - "mvdan.cc/gofumpt/format" @@ -6157,15 +6606,125 @@ diff -urN a/gopls/internal/hooks/gofumpt_118.go b/gopls/internal/hooks/gofumpt_1 - -func updateGofumpt(options *source.Options) { - options.GofumptFormat = func(ctx context.Context, langVersion, modulePath string, src []byte) ([]byte, error) { +- fixedVersion, err := fixLangVersion(langVersion) +- if err != nil { +- return nil, err +- } - return format.Source(src, format.Options{ -- LangVersion: langVersion, +- LangVersion: fixedVersion, - ModulePath: modulePath, - }) - } -} +- +-// fixLangVersion function cleans the input so that gofumpt doesn't panic. It is +-// rather permissive, and accepts version strings that aren't technically valid +-// in a go.mod file. +-// +-// More specifically, it looks for an optional 'v' followed by 1-3 +-// '.'-separated numbers. The resulting string is stripped of any suffix beyond +-// this expected version number pattern. +-// +-// See also golang/go#61692: gofumpt does not accept the new language versions +-// appearing in go.mod files (e.g. go1.21rc3). +-func fixLangVersion(input string) (string, error) { +- bad := func() (string, error) { +- return "", fmt.Errorf("invalid language version syntax %q", input) +- } +- if input == "" { +- return input, nil +- } +- i := 0 +- if input[0] == 'v' { // be flexible about 'v' +- i++ +- } +- // takeDigits consumes ascii numerals 0-9 and reports if at least one was +- // consumed. +- takeDigits := func() bool { +- found := false +- for ; i < len(input) && '0' <= input[i] && input[i] <= '9'; i++ { +- found = true +- } +- return found +- } +- if !takeDigits() { // versions must start with at least one number +- return bad() +- } +- +- // Accept optional minor and patch versions. +- for n := 0; n < 2; n++ { +- if i < len(input) && input[i] == '.' { +- // Look for minor/patch version. +- i++ +- if !takeDigits() { +- i-- +- break +- } +- } +- } +- // Accept any suffix. +- return input[:i], nil +-} +diff -urN a/gopls/internal/hooks/gofumpt_118_test.go b/gopls/internal/hooks/gofumpt_118_test.go +--- a/gopls/internal/hooks/gofumpt_118_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/hooks/gofumpt_118_test.go 1970-01-01 08:00:00 +@@ -1,53 +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 hooks +- +-import "testing" +- +-func TestFixLangVersion(t *testing.T) { +- tests := []struct { +- input, want string +- wantErr bool +- }{ +- {"", "", false}, +- {"1.18", "1.18", false}, +- {"v1.18", "v1.18", false}, +- {"1.21", "1.21", false}, +- {"1.21rc3", "1.21", false}, +- {"1.21.0", "1.21.0", false}, +- {"1.21.1", "1.21.1", false}, +- {"v1.21.1", "v1.21.1", false}, +- {"v1.21.0rc1", "v1.21.0", false}, // not technically valid, but we're flexible +- {"v1.21.0.0", "v1.21.0", false}, // also technically invalid +- {"1.1", "1.1", false}, +- {"v1", "v1", false}, +- {"1", "1", false}, +- {"v1.21.", "v1.21", false}, // also invalid +- {"1.21.", "1.21", false}, +- +- // Error cases. +- {"rc1", "", true}, +- {"x1.2.3", "", true}, +- } +- +- for _, test := range tests { +- got, err := fixLangVersion(test.input) +- if test.wantErr { +- if err == nil { +- t.Errorf("fixLangVersion(%q) succeeded unexpectedly", test.input) +- } +- continue +- } +- if err != nil { +- t.Fatalf("fixLangVersion(%q) failed: %v", test.input, err) +- } +- if got != test.want { +- t.Errorf("fixLangVersion(%q) = %s, want %s", test.input, got, test.want) +- } +- } +-} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/hooks/hooks.go 1970-01-01 08:00:00 @@ -1,31 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -6200,7 +6759,7 @@ diff -urN a/gopls/internal/hooks/hooks.go b/gopls/internal/hooks/hooks.go -} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/hooks/licenses.go 1970-01-01 08:00:00 @@ -1,169 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -6373,7 +6932,7 @@ diff -urN a/gopls/internal/hooks/licenses.go b/gopls/internal/hooks/licenses.go -` 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/hooks/licenses_test.go 1970-01-01 08:00:00 @@ -1,47 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -6383,7 +6942,7 @@ diff -urN a/gopls/internal/hooks/licenses_test.go b/gopls/internal/hooks/license - -import ( - "bytes" -- "io/ioutil" +- "os" - "os/exec" - "runtime" - "testing" @@ -6395,12 +6954,12 @@ diff -urN a/gopls/internal/hooks/licenses_test.go b/gopls/internal/hooks/license - // License text differs for older Go versions because staticcheck or gofumpt - // isn't supported for those versions, and this fails for unknown, unrelated - // reasons on Kokoro legacy CI. -- testenv.NeedsGo1Point(t, 19) +- testenv.NeedsGo1Point(t, 21) - - if runtime.GOOS != "linux" && runtime.GOOS != "darwin" { - t.Skip("generating licenses only works on Unixes") - } -- tmp, err := ioutil.TempFile("", "") +- tmp, err := os.CreateTemp("", "") - if err != nil { - t.Fatal(err) - } @@ -6410,11 +6969,11 @@ diff -urN a/gopls/internal/hooks/licenses_test.go b/gopls/internal/hooks/license - t.Fatalf("generating licenses failed: %q, %v", out, err) - } - -- got, err := ioutil.ReadFile(tmp.Name()) +- got, err := os.ReadFile(tmp.Name()) - if err != nil { - t.Fatal(err) - } -- want, err := ioutil.ReadFile("licenses.go") +- want, err := os.ReadFile("licenses.go") - if err != nil { - t.Fatal(err) - } @@ -6422,138 +6981,651 @@ diff -urN a/gopls/internal/hooks/licenses_test.go b/gopls/internal/hooks/license - t.Error("combined license text needs updating. Run: `go generate ./internal/hooks` from the gopls module.") - } -} -diff -urN a/gopls/internal/lsp/analysis/embeddirective/embeddirective.go b/gopls/internal/lsp/analysis/embeddirective/embeddirective.go ---- a/gopls/internal/lsp/analysis/embeddirective/embeddirective.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/embeddirective/embeddirective.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,58 +0,0 @@ --// Copyright 2022 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/README.md b/gopls/internal/lsp/README.md +--- a/gopls/internal/lsp/README.md 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/README.md 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-# lsp +- +-internal/lsp provides much of the Language Server Protocol (lsp) implementation +-for gopls. +- +-Documentation for users and contributors can be found in the +-[`gopls/doc`](../../gopls/doc) directory. +diff -urN a/gopls/internal/lsp/analysis/deprecated/deprecated.go b/gopls/internal/lsp/analysis/deprecated/deprecated.go +--- a/gopls/internal/lsp/analysis/deprecated/deprecated.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/analysis/deprecated/deprecated.go 1970-01-01 08:00:00 +@@ -1,270 +0,0 @@ +-// Copyright 2023 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 embeddirective defines an Analyzer that validates import for //go:embed directive. --package embeddirective +-// Package deprecated defines an Analyzer that marks deprecated symbols and package imports. +-package deprecated - -import ( +- "bytes" - "go/ast" +- "go/format" +- "go/token" +- "go/types" +- "strconv" - "strings" - - "golang.org/x/tools/go/analysis" +- "golang.org/x/tools/go/analysis/passes/inspect" +- "golang.org/x/tools/go/ast/inspector" +- "golang.org/x/tools/internal/typeparams" -) - --const Doc = `check for //go:embed directive import +-// TODO(hyangah): use analysisutil.MustExtractDoc. +-var doc = `check for use of deprecated identifiers - --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".` +-The deprecated analyzer looks for deprecated symbols and package imports. +- +-See https://go.dev/wiki/Deprecated to learn about Go's convention +-for documenting and signaling deprecated identifiers.` - -var Analyzer = &analysis.Analyzer{ -- Name: "embed", -- Doc: Doc, -- Requires: []*analysis.Analyzer{}, -- Run: run, +- Name: "deprecated", +- Doc: doc, +- Requires: []*analysis.Analyzer{inspect.Analyzer}, +- Run: checkDeprecated, +- FactTypes: []analysis.Fact{(*deprecationFact)(nil)}, - RunDespiteErrors: true, -} - --func run(pass *analysis.Pass) (interface{}, error) { +-// checkDeprecated is a simplified copy of staticcheck.CheckDeprecated. +-func checkDeprecated(pass *analysis.Pass) (interface{}, error) { +- inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) +- +- deprs, err := collectDeprecatedNames(pass, inspector) +- if err != nil || (len(deprs.packages) == 0 && len(deprs.objects) == 0) { +- return nil, err +- } +- +- reportDeprecation := func(depr *deprecationFact, node ast.Node) { +- // TODO(hyangah): staticcheck.CheckDeprecated has more complex logic. Do we need it here? +- // TODO(hyangah): Scrub depr.Msg. depr.Msg may contain Go comments +- // markdown syntaxes but LSP diagnostics do not support markdown syntax. +- +- buf := new(bytes.Buffer) +- if err := format.Node(buf, pass.Fset, node); err != nil { +- // This shouldn't happen but let's be conservative. +- buf.Reset() +- buf.WriteString("declaration") +- } +- pass.ReportRangef(node, "%s is deprecated: %s", buf, depr.Msg) +- } +- +- nodeFilter := []ast.Node{(*ast.SelectorExpr)(nil)} +- inspector.Preorder(nodeFilter, func(node ast.Node) { +- // Caveat: this misses dot-imported objects +- sel, ok := node.(*ast.SelectorExpr) +- if !ok { +- return +- } +- +- obj := pass.TypesInfo.ObjectOf(sel.Sel) +- if obj_, ok := obj.(*types.Func); ok { +- obj = typeparams.OriginMethod(obj_) +- } +- if obj == nil || obj.Pkg() == nil { +- // skip invalid sel.Sel. +- return +- } +- +- if obj.Pkg() == pass.Pkg { +- // A package is allowed to use its own deprecated objects +- return +- } +- +- // A package "foo" has two related packages "foo_test" and "foo.test", for external tests and the package main +- // generated by 'go test' respectively. "foo_test" can import and use "foo", "foo.test" imports and uses "foo" +- // and "foo_test". +- +- if strings.TrimSuffix(pass.Pkg.Path(), "_test") == obj.Pkg().Path() { +- // foo_test (the external tests of foo) can use objects from foo. +- return +- } +- if strings.TrimSuffix(pass.Pkg.Path(), ".test") == obj.Pkg().Path() { +- // foo.test (the main package of foo's tests) can use objects from foo. +- return +- } +- if strings.TrimSuffix(pass.Pkg.Path(), ".test") == strings.TrimSuffix(obj.Pkg().Path(), "_test") { +- // foo.test (the main package of foo's tests) can use objects from foo's external tests. +- return +- } +- +- if depr, ok := deprs.objects[obj]; ok { +- reportDeprecation(depr, sel) +- } +- }) +- - for _, f := range pass.Files { -- com := hasEmbedDirectiveComment(f) -- if com != nil { -- assertEmbedImport(pass, com, f) +- for _, spec := range f.Imports { +- var imp *types.Package +- var obj types.Object +- if spec.Name != nil { +- obj = pass.TypesInfo.ObjectOf(spec.Name) +- } else { +- obj = pass.TypesInfo.Implicits[spec] +- } +- pkgName, ok := obj.(*types.PkgName) +- if !ok { +- continue +- } +- imp = pkgName.Imported() +- +- path, err := strconv.Unquote(spec.Path.Value) +- if err != nil { +- continue +- } +- pkgPath := pass.Pkg.Path() +- if strings.TrimSuffix(pkgPath, "_test") == path { +- // foo_test can import foo +- continue +- } +- if strings.TrimSuffix(pkgPath, ".test") == path { +- // foo.test can import foo +- continue +- } +- if strings.TrimSuffix(pkgPath, ".test") == strings.TrimSuffix(path, "_test") { +- // foo.test can import foo_test +- continue +- } +- if depr, ok := deprs.packages[imp]; ok { +- reportDeprecation(depr, spec.Path) +- } - } - } - return nil, nil -} - --// Check if the comment contains //go:embed directive. --func hasEmbedDirectiveComment(f *ast.File) *ast.Comment { -- for _, cg := range f.Comments { -- for _, c := range cg.List { -- if strings.HasPrefix(c.Text, "//go:embed ") { -- return c +-type deprecationFact struct{ Msg string } +- +-func (*deprecationFact) AFact() {} +-func (d *deprecationFact) String() string { return "Deprecated: " + d.Msg } +- +-type deprecatedNames struct { +- objects map[types.Object]*deprecationFact +- packages map[*types.Package]*deprecationFact +-} +- +-// collectDeprecatedNames collects deprecated identifiers and publishes +-// them both as Facts and the return value. This is a simplified copy +-// of staticcheck's fact_deprecated analyzer. +-func collectDeprecatedNames(pass *analysis.Pass, ins *inspector.Inspector) (deprecatedNames, error) { +- extractDeprecatedMessage := func(docs []*ast.CommentGroup) string { +- for _, doc := range docs { +- if doc == nil { +- continue +- } +- parts := strings.Split(doc.Text(), "\n\n") +- for _, part := range parts { +- if !strings.HasPrefix(part, "Deprecated: ") { +- continue +- } +- alt := part[len("Deprecated: "):] +- alt = strings.Replace(alt, "\n", " ", -1) +- return strings.TrimSpace(alt) - } - } +- return "" - } -- return nil --} - --// Verifies that "embed" import exists for //go:embed directive. --func assertEmbedImport(pass *analysis.Pass, com *ast.Comment, f *ast.File) { -- for _, imp := range f.Imports { -- if "\"embed\"" == imp.Path.Value { +- doDocs := func(names []*ast.Ident, docs *ast.CommentGroup) { +- alt := extractDeprecatedMessage([]*ast.CommentGroup{docs}) +- if alt == "" { - return - } +- +- for _, name := range names { +- obj := pass.TypesInfo.ObjectOf(name) +- pass.ExportObjectFact(obj, &deprecationFact{alt}) +- } +- } +- +- var docs []*ast.CommentGroup +- for _, f := range pass.Files { +- docs = append(docs, f.Doc) +- } +- if alt := extractDeprecatedMessage(docs); alt != "" { +- // Don't mark package syscall as deprecated, even though +- // it is. A lot of people still use it for simple +- // constants like SIGKILL, and I am not comfortable +- // telling them to use x/sys for that. +- if pass.Pkg.Path() != "syscall" { +- pass.ExportPackageFact(&deprecationFact{alt}) +- } +- } +- nodeFilter := []ast.Node{ +- (*ast.GenDecl)(nil), +- (*ast.FuncDecl)(nil), +- (*ast.TypeSpec)(nil), +- (*ast.ValueSpec)(nil), +- (*ast.File)(nil), +- (*ast.StructType)(nil), +- (*ast.InterfaceType)(nil), +- } +- ins.Preorder(nodeFilter, func(node ast.Node) { +- var names []*ast.Ident +- var docs *ast.CommentGroup +- switch node := node.(type) { +- case *ast.GenDecl: +- switch node.Tok { +- case token.TYPE, token.CONST, token.VAR: +- docs = node.Doc +- for i := range node.Specs { +- switch n := node.Specs[i].(type) { +- case *ast.ValueSpec: +- names = append(names, n.Names...) +- case *ast.TypeSpec: +- names = append(names, n.Name) +- } +- } +- default: +- return +- } +- case *ast.FuncDecl: +- docs = node.Doc +- names = []*ast.Ident{node.Name} +- case *ast.TypeSpec: +- docs = node.Doc +- names = []*ast.Ident{node.Name} +- case *ast.ValueSpec: +- docs = node.Doc +- names = node.Names +- case *ast.StructType: +- for _, field := range node.Fields.List { +- doDocs(field.Names, field.Doc) +- } +- case *ast.InterfaceType: +- for _, field := range node.Methods.List { +- doDocs(field.Names, field.Doc) +- } +- } +- if docs != nil && len(names) > 0 { +- doDocs(names, docs) +- } +- }) +- +- // Every identifier is potentially deprecated, so we will need +- // to look up facts a lot. Construct maps of all facts propagated +- // to this pass for fast lookup. +- out := deprecatedNames{ +- objects: map[types.Object]*deprecationFact{}, +- packages: map[*types.Package]*deprecationFact{}, +- } +- for _, fact := range pass.AllObjectFacts() { +- out.objects[fact.Object] = fact.Fact.(*deprecationFact) +- } +- for _, fact := range pass.AllPackageFacts() { +- out.packages[fact.Package] = fact.Fact.(*deprecationFact) - } -- pass.Report(analysis.Diagnostic{Pos: com.Pos(), End: com.Pos() + 10, Message: "The \"embed\" package must be imported when using go:embed directives."}) +- +- return out, nil -} -diff -urN a/gopls/internal/lsp/analysis/embeddirective/embeddirective_test.go b/gopls/internal/lsp/analysis/embeddirective/embeddirective_test.go ---- a/gopls/internal/lsp/analysis/embeddirective/embeddirective_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/embeddirective/embeddirective_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,22 +0,0 @@ --// Copyright 2022 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/analysis/deprecated/deprecated_test.go b/gopls/internal/lsp/analysis/deprecated/deprecated_test.go +--- a/gopls/internal/lsp/analysis/deprecated/deprecated_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/analysis/deprecated/deprecated_test.go 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +-// Copyright 2023 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 embeddirective +-package deprecated - -import ( - "testing" - - "golang.org/x/tools/go/analysis/analysistest" -- "golang.org/x/tools/internal/typeparams" +- "golang.org/x/tools/internal/testenv" -) - -func Test(t *testing.T) { +- testenv.NeedsGo1Point(t, 19) - testdata := analysistest.TestData() -- tests := []string{"a"} -- if typeparams.Enabled { -- tests = append(tests) -- } -- -- analysistest.RunWithSuggestedFixes(t, testdata, Analyzer, tests...) +- analysistest.Run(t, testdata, Analyzer, "a") -} -diff -urN a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/a.go b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/a.go ---- a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package a +diff -urN a/gopls/internal/lsp/analysis/deprecated/testdata/src/a/a.go b/gopls/internal/lsp/analysis/deprecated/testdata/src/a/a.go +--- a/gopls/internal/lsp/analysis/deprecated/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/analysis/deprecated/testdata/src/a/a.go 1970-01-01 08:00:00 +@@ -1,17 +0,0 @@ +-// Copyright 2023 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. - --import ( -- "fmt" --) +-package usedeprecated - --//go:embed embedText // want "The \"embed\" package must be imported when using go:embed directives" --var s string +-import "io/ioutil" // want "\"io/ioutil\" is deprecated: .*" - --// This is main function --func main() { -- fmt.Println(s) +-func x() { +- _, _ = ioutil.ReadFile("") // want "ioutil.ReadFile is deprecated: As of Go 1.16, .*" +- Legacy() // expect no deprecation notice. -} -diff -urN a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/b.go b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/b.go ---- a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/b.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/b.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ --package a - --import ( -- _ "embed" +-// Legacy is deprecated. +-// +-// Deprecated: use X instead. +-func Legacy() {} // want Legacy:"Deprecated: use X instead." +diff -urN a/gopls/internal/lsp/analysis/deprecated/testdata/src/a/a_test.go b/gopls/internal/lsp/analysis/deprecated/testdata/src/a/a_test.go +--- a/gopls/internal/lsp/analysis/deprecated/testdata/src/a/a_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/analysis/deprecated/testdata/src/a/a_test.go 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +-// Copyright 2023 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 usedeprecated +- +-import "testing" +- +-func TestF(t *testing.T) { +- Legacy() // expect no deprecation notice. +- x() +-} +diff -urN a/gopls/internal/lsp/analysis/embeddirective/embeddirective.go b/gopls/internal/lsp/analysis/embeddirective/embeddirective.go +--- a/gopls/internal/lsp/analysis/embeddirective/embeddirective.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/analysis/embeddirective/embeddirective.go 1970-01-01 08:00:00 +@@ -1,134 +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 embeddirective defines an Analyzer that validates //go:embed directives. +-// The analyzer defers fixes to its parent source.Analyzer. +-package embeddirective +- +-import ( +- "go/ast" +- "go/token" +- "strings" +- +- "golang.org/x/tools/go/analysis" +-) +- +-const Doc = `check //go:embed directive usage +- +-This analyzer checks that the embed package is imported if //go:embed +-directives are present, providing a suggested fix to add the import if +-it is missing. +- +-This analyzer also checks that //go:embed directives precede the +-declaration of a single variable.` +- +-var Analyzer = &analysis.Analyzer{ +- Name: "embed", +- Doc: Doc, +- Requires: []*analysis.Analyzer{}, +- Run: run, +- RunDespiteErrors: true, +-} +- +-// source.fixedByImportingEmbed relies on this message to filter +-// out fixable diagnostics from this Analyzer. +-const MissingImportMessage = `must import "embed" when using go:embed directives` +- +-func run(pass *analysis.Pass) (interface{}, error) { +- for _, f := range pass.Files { +- comments := embedDirectiveComments(f) +- if len(comments) == 0 { +- continue // nothing to check +- } +- +- hasEmbedImport := false +- for _, imp := range f.Imports { +- if imp.Path.Value == `"embed"` { +- hasEmbedImport = true +- break +- } +- } +- +- for _, c := range comments { +- report := func(msg string) { +- pass.Report(analysis.Diagnostic{ +- Pos: c.Pos(), +- End: c.Pos() + token.Pos(len("//go:embed")), +- Message: msg, +- }) +- } +- +- if !hasEmbedImport { +- report(MissingImportMessage) +- } +- +- spec := nextVarSpec(c, f) +- switch { +- case spec == nil: +- report(`go:embed directives must precede a "var" declaration`) +- case len(spec.Names) > 1: +- report("declarations following go:embed directives must define a single variable") +- case len(spec.Values) > 0: +- report("declarations following go:embed directives must not specify a value") +- } +- } +- } +- return nil, nil +-} +- +-// embedDirectiveComments returns all comments in f that contains a //go:embed directive. +-func embedDirectiveComments(f *ast.File) []*ast.Comment { +- comments := []*ast.Comment{} +- for _, cg := range f.Comments { +- for _, c := range cg.List { +- if strings.HasPrefix(c.Text, "//go:embed ") { +- comments = append(comments, c) +- } +- } +- } +- return comments +-} +- +-// nextVarSpec returns the ValueSpec for the variable declaration immediately following +-// the go:embed comment, or nil if the next declaration is not a variable declaration. +-func nextVarSpec(com *ast.Comment, f *ast.File) *ast.ValueSpec { +- // Embed directives must be followed by a declaration of one variable with no value. +- // There may be comments and empty lines between the directive and the declaration. +- var nextDecl ast.Decl +- for _, d := range f.Decls { +- if com.End() < d.End() { +- nextDecl = d +- break +- } +- } +- if nextDecl == nil || nextDecl.Pos() == token.NoPos { +- return nil +- } +- decl, ok := nextDecl.(*ast.GenDecl) +- if !ok { +- return nil +- } +- if decl.Tok != token.VAR { +- return nil +- } +- +- // var declarations can be both freestanding and blocks (with parenthesis). +- // Only the first variable spec following the directive is interesting. +- var nextSpec ast.Spec +- for _, s := range decl.Specs { +- if com.End() < s.End() { +- nextSpec = s +- break +- } +- } +- if nextSpec == nil { +- return nil +- } +- spec, ok := nextSpec.(*ast.ValueSpec) +- if !ok { +- // Invalid AST, but keep going. +- return nil +- } +- return spec +-} +diff -urN a/gopls/internal/lsp/analysis/embeddirective/embeddirective_test.go b/gopls/internal/lsp/analysis/embeddirective/embeddirective_test.go +--- a/gopls/internal/lsp/analysis/embeddirective/embeddirective_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/analysis/embeddirective/embeddirective_test.go 1970-01-01 08:00:00 +@@ -1,22 +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 embeddirective +- +-import ( +- "testing" +- +- "golang.org/x/tools/go/analysis/analysistest" +- "golang.org/x/tools/internal/typeparams" +-) +- +-func Test(t *testing.T) { +- testdata := analysistest.TestData() +- tests := []string{"a"} +- if typeparams.Enabled { +- tests = append(tests) +- } +- +- analysistest.RunWithSuggestedFixes(t, testdata, Analyzer, tests...) +-} +diff -urN a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/embedText b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/embedText +--- a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/embedText 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/embedText 1970-01-01 08:00:00 +@@ -1 +0,0 @@ +-Hello World +\ No newline at end of file +diff -urN a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_missing.go b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_missing.go +--- a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_missing.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_missing.go 1970-01-01 08:00:00 +@@ -1,17 +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 a +- +-import ( - "fmt" -) - +-//go:embed embedtext // want "must import \"embed\" when using go:embed directives" +-var s string +- +-// This is main function +-func main() { +- fmt.Println(s) +-} +diff -urN a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_present.go b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_present.go +--- a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_present.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_present.go 1970-01-01 08:00:00 +@@ -1,73 +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 a +- +-// Misplaced, above imports. +-//go:embed embedText // want "go:embed directives must precede a \"var\" declaration" +- +-import ( +- "fmt" +- +- _ "embed" +-) +- -//go:embed embedText // ok -var s string - +-// The analyzer does not check for many directives using the same var. +-// +-//go:embed embedText // ok +-//go:embed embedText // ok +-var s string +- +-// Comments and blank lines between are OK. +-// +-//go:embed embedText // ok +-// +-// foo +- +-var s string +- +-// Followed by wrong kind of decl. +-// +-//go:embed embedText // want "go:embed directives must precede a \"var\" declaration" +-func foo() +- +-// Multiple variable specs. +-// +-//go:embed embedText // want "declarations following go:embed directives must define a single variable" +-var foo, bar []byte +- +-// Specifying a value is not allowed. +-// +-//go:embed embedText // want "declarations following go:embed directives must not specify a value" +-var s string = "foo" +- +-// TODO: This should not be OK, misplaced according to compiler. +-// +-//go:embed embedText // ok +-var ( +- s string +- x string +-) +- +-// var blocks are OK as long as the variable following the directive is OK. +-var ( +- x, y, z string +- //go:embed embedText // ok +- s string +- q, r, t string +-) +- +-//go:embed embedText // want "go:embed directives must precede a \"var\" declaration" +-var () +- +-// This is main function +-func main() { +- fmt.Println(s) +-} +- +-// No declaration following. +-//go:embed embedText // want "go:embed directives must precede a \"var\" declaration" +diff -urN a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_present_go120.go b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_present_go120.go +--- a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_present_go120.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/import_present_go120.go 1970-01-01 08:00:00 +@@ -1,26 +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.20 +-// +build go1.20 +- +-package a +- +-var ( +- // Okay directive wise but the compiler will complain that +- // imports must appear before other declarations. +- //go:embed embedText // ok +- "foo" +-) +- +-import ( +- "fmt" +- +- _ "embed" +-) +- -// This is main function -func main() { - fmt.Println(s) -} -diff -urN a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/embedText b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/embedText ---- a/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/embedText 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/embeddirective/testdata/src/a/embedText 1970-01-01 00:00:00.000000000 +0000 -@@ -1 +0,0 @@ --Hello World -\ No newline at end of file diff -urN a/gopls/internal/lsp/analysis/fillreturns/fillreturns.go b/gopls/internal/lsp/analysis/fillreturns/fillreturns.go --- a/gopls/internal/lsp/analysis/fillreturns/fillreturns.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillreturns/fillreturns.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/fillreturns/fillreturns.go 1970-01-01 08:00:00 @@ -1,279 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -6836,7 +7908,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillreturns/fillreturns.go b/gopls/inter -} diff -urN a/gopls/internal/lsp/analysis/fillreturns/fillreturns_test.go b/gopls/internal/lsp/analysis/fillreturns/fillreturns_test.go --- a/gopls/internal/lsp/analysis/fillreturns/fillreturns_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillreturns/fillreturns_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/fillreturns/fillreturns_test.go 1970-01-01 08:00:00 @@ -1,22 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -6862,7 +7934,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillreturns/fillreturns_test.go b/gopls/ -} diff -urN a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go --- a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go 1970-01-01 08:00:00 @@ -1,139 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -7005,7 +8077,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go b/gopls/ -} diff -urN a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden --- a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden 1970-01-01 08:00:00 @@ -1,139 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -7148,7 +8220,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden b -} diff -urN a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a.go b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a.go --- a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a.go 1970-01-01 08:00:00 @@ -1,5 +0,0 @@ -package fillreturns - @@ -7157,7 +8229,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a. -} diff -urN a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a.go.golden b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a.go.golden --- a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a.go.golden 1970-01-01 08:00:00 @@ -1,5 +0,0 @@ -package fillreturns - @@ -7166,8 +8238,8 @@ diff -urN a/gopls/internal/lsp/analysis/fillreturns/testdata/src/a/typeparams/a. -} diff -urN a/gopls/internal/lsp/analysis/fillstruct/fillstruct.go b/gopls/internal/lsp/analysis/fillstruct/fillstruct.go --- a/gopls/internal/lsp/analysis/fillstruct/fillstruct.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillstruct/fillstruct.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,506 +0,0 @@ ++++ b/gopls/internal/lsp/analysis/fillstruct/fillstruct.go 1970-01-01 08:00:00 +@@ -1,515 +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. @@ -7218,26 +8290,34 @@ diff -urN a/gopls/internal/lsp/analysis/fillstruct/fillstruct.go b/gopls/interna - RunDespiteErrors: true, -} - +-// TODO(rfindley): remove this thin wrapper around the fillstruct refactoring, +-// and eliminate the fillstruct analyzer. +-// +-// Previous iterations used the analysis framework for computing refactorings, +-// which proved inefficient. -func run(pass *analysis.Pass) (interface{}, error) { - inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) +- for _, d := range DiagnoseFillableStructs(inspect, token.NoPos, token.NoPos, pass.Pkg, pass.TypesInfo) { +- pass.Report(d) +- } +- return nil, nil +-} +- +-// DiagnoseFillableStructs computes diagnostics for fillable struct composite +-// literals overlapping with the provided start and end position. +-// +-// If either start or end is invalid, it is considered an unbounded condition. +-func DiagnoseFillableStructs(inspect *inspector.Inspector, start, end token.Pos, pkg *types.Package, info *types.Info) []analysis.Diagnostic { +- var diags []analysis.Diagnostic - nodeFilter := []ast.Node{(*ast.CompositeLit)(nil)} - inspect.Preorder(nodeFilter, func(n ast.Node) { - expr := n.(*ast.CompositeLit) - -- // Find enclosing file. -- // TODO(adonovan): use inspect.WithStack? -- var file *ast.File -- for _, f := range pass.Files { -- if f.Pos() <= expr.Pos() && expr.Pos() <= f.End() { -- file = f -- break -- } -- } -- if file == nil { -- return +- if (start.IsValid() && expr.End() < start) || (end.IsValid() && expr.Pos() > end) { +- return // non-overlapping - } - -- typ := pass.TypesInfo.TypeOf(expr) +- typ := info.TypeOf(expr) - if typ == nil { - return - } @@ -7262,7 +8342,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillstruct/fillstruct.go b/gopls/interna - for i := 0; i < fieldCount; i++ { - field := tStruct.Field(i) - // Ignore fields that are not accessible in the current package. -- if field.Pkg() != nil && field.Pkg() != pass.Pkg && !field.Exported() { +- if field.Pkg() != nil && field.Pkg() != pkg && !field.Exported() { - continue - } - fillableFields = append(fillableFields, fmt.Sprintf("%s: %s", field.Name(), field.Type().String())) @@ -7275,7 +8355,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillstruct/fillstruct.go b/gopls/interna - var name string - if typ != tStruct { - // named struct type (e.g. pkg.S[T]) -- name = types.TypeString(typ, types.RelativeTo(pass.Pkg)) +- name = types.TypeString(typ, types.RelativeTo(pkg)) - } else { - // anonymous struct type - totalFields := len(fillableFields) @@ -7294,13 +8374,14 @@ diff -urN a/gopls/internal/lsp/analysis/fillstruct/fillstruct.go b/gopls/interna - } - name = fmt.Sprintf("anonymous struct { %s }", strings.Join(fillableFields, ", ")) - } -- pass.Report(analysis.Diagnostic{ +- diags = append(diags, analysis.Diagnostic{ - Message: fmt.Sprintf("Fill %s", name), - Pos: expr.Pos(), - End: expr.End(), - }) - }) -- return nil, nil +- +- return diags -} - -// SuggestedFix computes the suggested fix for the kinds of @@ -7338,7 +8419,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillstruct/fillstruct.go b/gopls/interna - return nil, fmt.Errorf("%s is not a (pointer to) struct type", - types.TypeString(typ, types.RelativeTo(pkg))) - } -- // Inv: typ is the the possibly-named struct type. +- // Inv: typ is the possibly-named struct type. - - fieldCount := tStruct.NumFields() - @@ -7676,7 +8757,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillstruct/fillstruct.go b/gopls/interna -} diff -urN a/gopls/internal/lsp/analysis/fillstruct/fillstruct_test.go b/gopls/internal/lsp/analysis/fillstruct/fillstruct_test.go --- a/gopls/internal/lsp/analysis/fillstruct/fillstruct_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillstruct/fillstruct_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/fillstruct/fillstruct_test.go 1970-01-01 08:00:00 @@ -1,22 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -7702,7 +8783,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillstruct/fillstruct_test.go b/gopls/in -} diff -urN a/gopls/internal/lsp/analysis/fillstruct/testdata/src/a/a.go b/gopls/internal/lsp/analysis/fillstruct/testdata/src/a/a.go --- a/gopls/internal/lsp/analysis/fillstruct/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillstruct/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/fillstruct/testdata/src/a/a.go 1970-01-01 08:00:00 @@ -1,113 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -7819,7 +8900,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillstruct/testdata/src/a/a.go b/gopls/i -var _ = unsafeStruct{} // want `Fill unsafeStruct` diff -urN a/gopls/internal/lsp/analysis/fillstruct/testdata/src/b/b.go b/gopls/internal/lsp/analysis/fillstruct/testdata/src/b/b.go --- a/gopls/internal/lsp/analysis/fillstruct/testdata/src/b/b.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillstruct/testdata/src/b/b.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/fillstruct/testdata/src/b/b.go 1970-01-01 08:00:00 @@ -1,6 +0,0 @@ -package fillstruct - @@ -7829,7 +8910,7 @@ diff -urN a/gopls/internal/lsp/analysis/fillstruct/testdata/src/b/b.go b/gopls/i -} diff -urN a/gopls/internal/lsp/analysis/fillstruct/testdata/src/typeparams/typeparams.go b/gopls/internal/lsp/analysis/fillstruct/testdata/src/typeparams/typeparams.go --- a/gopls/internal/lsp/analysis/fillstruct/testdata/src/typeparams/typeparams.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/fillstruct/testdata/src/typeparams/typeparams.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/fillstruct/testdata/src/typeparams/typeparams.go 1970-01-01 08:00:00 @@ -1,50 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -7883,8 +8964,8 @@ diff -urN a/gopls/internal/lsp/analysis/fillstruct/testdata/src/typeparams/typep -} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/infertypeargs.go b/gopls/internal/lsp/analysis/infertypeargs/infertypeargs.go --- a/gopls/internal/lsp/analysis/infertypeargs/infertypeargs.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/infertypeargs.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,31 +0,0 @@ ++++ b/gopls/internal/lsp/analysis/infertypeargs/infertypeargs.go 1970-01-01 08:00:00 +@@ -1,47 +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. @@ -7894,8 +8975,11 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/infertypeargs.go b/gopls/i -package infertypeargs - -import ( +- "go/token" +- - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" +- "golang.org/x/tools/go/ast/inspector" -) - -const Doc = `check for unnecessary type arguments in call expressions @@ -7916,9 +9000,22 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/infertypeargs.go b/gopls/i - Requires: []*analysis.Analyzer{inspect.Analyzer}, - Run: run, -} +- +-// TODO(rfindley): remove this thin wrapper around the infertypeargs refactoring, +-// and eliminate the infertypeargs analyzer. +-// +-// Previous iterations used the analysis framework for computing refactorings, +-// which proved inefficient. +-func run(pass *analysis.Pass) (interface{}, error) { +- inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) +- for _, diag := range DiagnoseInferableTypeArgs(pass.Fset, inspect, token.NoPos, token.NoPos, pass.Pkg, pass.TypesInfo) { +- pass.Report(diag) +- } +- return nil, nil +-} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/infertypeargs_test.go b/gopls/internal/lsp/analysis/infertypeargs/infertypeargs_test.go --- a/gopls/internal/lsp/analysis/infertypeargs/infertypeargs_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/infertypeargs_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/infertypeargs/infertypeargs_test.go 1970-01-01 08:00:00 @@ -1,21 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -7943,8 +9040,8 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/infertypeargs_test.go b/go -} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/run_go117.go b/gopls/internal/lsp/analysis/infertypeargs/run_go117.go --- a/gopls/internal/lsp/analysis/infertypeargs/run_go117.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/run_go117.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,16 +0,0 @@ ++++ b/gopls/internal/lsp/analysis/infertypeargs/run_go117.go 1970-01-01 08:00:00 +@@ -1,22 +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. @@ -7954,17 +9051,23 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/run_go117.go b/gopls/inter - -package infertypeargs - --import "golang.org/x/tools/go/analysis" +-import ( +- "go/token" +- "go/types" +- +- "golang.org/x/tools/go/analysis" +- "golang.org/x/tools/go/ast/inspector" +-) - --// This analyzer only relates to go1.18+, and uses the types.CheckExpr API that --// was added in Go 1.13. --func run(pass *analysis.Pass) (interface{}, error) { -- return nil, nil +-// DiagnoseInferableTypeArgs returns an empty slice, as generics are not supported at +-// this go version. +-func DiagnoseInferableTypeArgs(fset *token.FileSet, inspect *inspector.Inspector, start, end token.Pos, pkg *types.Package, info *types.Info) []analysis.Diagnostic { +- return nil -} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/run_go118.go b/gopls/internal/lsp/analysis/infertypeargs/run_go118.go --- a/gopls/internal/lsp/analysis/infertypeargs/run_go118.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/run_go118.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,111 +0,0 @@ ++++ b/gopls/internal/lsp/analysis/infertypeargs/run_go118.go 1970-01-01 08:00:00 +@@ -1,120 +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. @@ -7980,18 +9083,19 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/run_go118.go b/gopls/inter - "go/types" - - "golang.org/x/tools/go/analysis" -- "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/inspector" - "golang.org/x/tools/internal/typeparams" -) - --func run(pass *analysis.Pass) (interface{}, error) { -- inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) -- -- nodeFilter := []ast.Node{ -- (*ast.CallExpr)(nil), -- } +-// DiagnoseInferableTypeArgs reports diagnostics describing simplifications to type +-// arguments overlapping with the provided start and end position. +-// +-// If start or end is token.NoPos, the corresponding bound is not checked +-// (i.e. if both start and end are NoPos, all call expressions are considered). +-func DiagnoseInferableTypeArgs(fset *token.FileSet, inspect *inspector.Inspector, start, end token.Pos, pkg *types.Package, info *types.Info) []analysis.Diagnostic { +- var diags []analysis.Diagnostic - +- nodeFilter := []ast.Node{(*ast.CallExpr)(nil)} - inspect.Preorder(nodeFilter, func(node ast.Node) { - call := node.(*ast.CallExpr) - x, lbrack, indices, rbrack := typeparams.UnpackIndexExpr(call.Fun) @@ -8000,8 +9104,12 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/run_go118.go b/gopls/inter - return // no explicit args, nothing to do - } - +- if (start.IsValid() && call.End() < start) || (end.IsValid() && call.Pos() > end) { +- return // non-overlapping +- } +- - // Confirm that instantiation actually occurred at this ident. -- idata, ok := typeparams.GetInstances(pass.TypesInfo)[ident] +- idata, ok := typeparams.GetInstances(info)[ident] - if !ok { - return // something went wrong, but fail open - } @@ -8027,7 +9135,7 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/run_go118.go b/gopls/inter - } - info := new(types.Info) - typeparams.InitInstanceInfo(info) -- if err := types.CheckExpr(pass.Fset, pass.Pkg, call.Pos(), newCall, info); err != nil { +- if err := types.CheckExpr(fset, pkg, call.Pos(), newCall, info); err != nil { - // Most likely inference failed. - break - } @@ -8041,20 +9149,24 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/run_go118.go b/gopls/inter - required = i - } - if required < len(indices) { -- var start, end token.Pos +- var s, e token.Pos - var edit analysis.TextEdit - if required == 0 { -- start, end = lbrack, rbrack+1 // erase the entire index -- edit = analysis.TextEdit{Pos: start, End: end} +- s, e = lbrack, rbrack+1 // erase the entire index +- edit = analysis.TextEdit{Pos: s, End: e} - } else { -- start = indices[required].Pos() -- end = rbrack +- s = indices[required].Pos() +- e = rbrack - // erase from end of last arg to include last comma & white-spaces -- edit = analysis.TextEdit{Pos: indices[required-1].End(), End: end} +- edit = analysis.TextEdit{Pos: indices[required-1].End(), End: e} - } -- pass.Report(analysis.Diagnostic{ -- Pos: start, -- End: end, +- // Recheck that our (narrower) fixes overlap with the requested range. +- if (start.IsValid() && e < start) || (end.IsValid() && s > end) { +- return // non-overlapping +- } +- diags = append(diags, analysis.Diagnostic{ +- Pos: s, +- End: e, - Message: "unnecessary type arguments", - SuggestedFixes: []analysis.SuggestedFix{{ - Message: "simplify type arguments", @@ -8064,7 +9176,7 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/run_go118.go b/gopls/inter - } - }) - -- return nil, nil +- return diags -} - -func calledIdent(x ast.Expr) *ast.Ident { @@ -8078,7 +9190,7 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/run_go118.go b/gopls/inter -} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go --- a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go 1970-01-01 08:00:00 @@ -1,20 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8102,7 +9214,7 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go b/ -} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go.golden b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go.golden --- a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go.golden 1970-01-01 08:00:00 @@ -1,20 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8126,7 +9238,7 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/basic.go.go -} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported/imported.go b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported/imported.go --- a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported/imported.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported/imported.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported/imported.go 1970-01-01 08:00:00 @@ -1,7 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8137,7 +9249,7 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported/im -func F[T any](T) {} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go --- a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go 1970-01-01 08:00:00 @@ -1,12 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8153,7 +9265,7 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go -} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go.golden b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go.golden --- a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go.golden 1970-01-01 08:00:00 @@ -1,12 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8169,7 +9281,7 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/imported.go -} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechange.go b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechange.go --- a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechange.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechange.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechange.go 1970-01-01 08:00:00 @@ -1,26 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8199,7 +9311,7 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechang -} diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechange.go.golden b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechange.go.golden --- a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechange.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechange.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechange.go.golden 1970-01-01 08:00:00 @@ -1,26 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8229,7 +9341,7 @@ diff -urN a/gopls/internal/lsp/analysis/infertypeargs/testdata/src/a/notypechang -} diff -urN a/gopls/internal/lsp/analysis/nonewvars/nonewvars.go b/gopls/internal/lsp/analysis/nonewvars/nonewvars.go --- a/gopls/internal/lsp/analysis/nonewvars/nonewvars.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/nonewvars/nonewvars.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/nonewvars/nonewvars.go 1970-01-01 08:00:00 @@ -1,95 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8328,7 +9440,7 @@ diff -urN a/gopls/internal/lsp/analysis/nonewvars/nonewvars.go b/gopls/internal/ -} diff -urN a/gopls/internal/lsp/analysis/nonewvars/nonewvars_test.go b/gopls/internal/lsp/analysis/nonewvars/nonewvars_test.go --- a/gopls/internal/lsp/analysis/nonewvars/nonewvars_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/nonewvars/nonewvars_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/nonewvars/nonewvars_test.go 1970-01-01 08:00:00 @@ -1,22 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8354,7 +9466,7 @@ diff -urN a/gopls/internal/lsp/analysis/nonewvars/nonewvars_test.go b/gopls/inte -} diff -urN a/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go b/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go --- a/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go 1970-01-01 08:00:00 @@ -1,16 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8374,7 +9486,7 @@ diff -urN a/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go b/gopls/in -} diff -urN a/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go.golden b/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go.golden --- a/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go.golden 1970-01-01 08:00:00 @@ -1,16 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8394,7 +9506,7 @@ diff -urN a/gopls/internal/lsp/analysis/nonewvars/testdata/src/a/a.go.golden b/g -} diff -urN a/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go b/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go --- a/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go 1970-01-01 08:00:00 @@ -1,6 +0,0 @@ -package nonewvars - @@ -8404,7 +9516,7 @@ diff -urN a/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go b -} diff -urN a/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go.golden b/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go.golden --- a/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go.golden 1970-01-01 08:00:00 @@ -1,6 +0,0 @@ -package nonewvars - @@ -8414,7 +9526,7 @@ diff -urN a/gopls/internal/lsp/analysis/nonewvars/testdata/src/typeparams/a.go.g -} diff -urN a/gopls/internal/lsp/analysis/noresultvalues/noresultvalues.go b/gopls/internal/lsp/analysis/noresultvalues/noresultvalues.go --- a/gopls/internal/lsp/analysis/noresultvalues/noresultvalues.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/noresultvalues/noresultvalues.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/noresultvalues/noresultvalues.go 1970-01-01 08:00:00 @@ -1,92 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8510,7 +9622,7 @@ diff -urN a/gopls/internal/lsp/analysis/noresultvalues/noresultvalues.go b/gopls -} diff -urN a/gopls/internal/lsp/analysis/noresultvalues/noresultvalues_test.go b/gopls/internal/lsp/analysis/noresultvalues/noresultvalues_test.go --- a/gopls/internal/lsp/analysis/noresultvalues/noresultvalues_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/noresultvalues/noresultvalues_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/noresultvalues/noresultvalues_test.go 1970-01-01 08:00:00 @@ -1,22 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8536,7 +9648,7 @@ diff -urN a/gopls/internal/lsp/analysis/noresultvalues/noresultvalues_test.go b/ -} diff -urN a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go --- a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go 1970-01-01 08:00:00 @@ -1,9 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8549,7 +9661,7 @@ diff -urN a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go b/gop -func y() { return nil, "hello" } // want `no result values expected|too many return values` diff -urN a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go.golden b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go.golden --- a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go.golden 1970-01-01 08:00:00 @@ -1,9 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8562,7 +9674,7 @@ diff -urN a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/a/a.go.golde -func y() { return } // want `no result values expected|too many return values` diff -urN a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a.go b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a.go --- a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a.go 1970-01-01 08:00:00 @@ -1,6 +0,0 @@ -package noresult - @@ -8572,7 +9684,7 @@ diff -urN a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a -} diff -urN a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a.go.golden b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a.go.golden --- a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a.go.golden 1970-01-01 08:00:00 @@ -1,6 +0,0 @@ -package noresult - @@ -8582,7 +9694,7 @@ diff -urN a/gopls/internal/lsp/analysis/noresultvalues/testdata/src/typeparams/a -} diff -urN a/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositelit.go b/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositelit.go --- a/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositelit.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositelit.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositelit.go 1970-01-01 08:00:00 @@ -1,196 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8782,7 +9894,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositeli -) diff -urN a/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositelit_test.go b/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositelit_test.go --- a/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositelit_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositelit_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositelit_test.go 1970-01-01 08:00:00 @@ -1,17 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -8803,7 +9915,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifycompositelit/simplifycompositeli -} diff -urN a/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go b/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go --- a/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go 1970-01-01 08:00:00 @@ -1,234 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9041,7 +10153,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go -} diff -urN a/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go.golden b/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go.golden --- a/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go.golden 1970-01-01 08:00:00 @@ -1,234 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9279,7 +10391,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifycompositelit/testdata/src/a/a.go -} diff -urN a/gopls/internal/lsp/analysis/simplifyrange/simplifyrange.go b/gopls/internal/lsp/analysis/simplifyrange/simplifyrange.go --- a/gopls/internal/lsp/analysis/simplifyrange/simplifyrange.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifyrange/simplifyrange.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifyrange/simplifyrange.go 1970-01-01 08:00:00 @@ -1,116 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9399,7 +10511,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifyrange/simplifyrange.go b/gopls/i -} diff -urN a/gopls/internal/lsp/analysis/simplifyrange/simplifyrange_test.go b/gopls/internal/lsp/analysis/simplifyrange/simplifyrange_test.go --- a/gopls/internal/lsp/analysis/simplifyrange/simplifyrange_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifyrange/simplifyrange_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifyrange/simplifyrange_test.go 1970-01-01 08:00:00 @@ -1,17 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9420,7 +10532,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifyrange/simplifyrange_test.go b/go -} diff -urN a/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go b/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go --- a/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go 1970-01-01 08:00:00 @@ -1,16 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9440,7 +10552,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go b/gopl -} diff -urN a/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go.golden b/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go.golden --- a/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go.golden 1970-01-01 08:00:00 @@ -1,16 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9460,7 +10572,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifyrange/testdata/src/a/a.go.golden -} diff -urN a/gopls/internal/lsp/analysis/simplifyslice/simplifyslice.go b/gopls/internal/lsp/analysis/simplifyslice/simplifyslice.go --- a/gopls/internal/lsp/analysis/simplifyslice/simplifyslice.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifyslice/simplifyslice.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifyslice/simplifyslice.go 1970-01-01 08:00:00 @@ -1,94 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9558,7 +10670,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifyslice/simplifyslice.go b/gopls/i -} diff -urN a/gopls/internal/lsp/analysis/simplifyslice/simplifyslice_test.go b/gopls/internal/lsp/analysis/simplifyslice/simplifyslice_test.go --- a/gopls/internal/lsp/analysis/simplifyslice/simplifyslice_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifyslice/simplifyslice_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifyslice/simplifyslice_test.go 1970-01-01 08:00:00 @@ -1,22 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9584,7 +10696,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifyslice/simplifyslice_test.go b/go -} diff -urN a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go --- a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go 1970-01-01 08:00:00 @@ -1,70 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9658,7 +10770,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go b/gopl -} diff -urN a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go.golden b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go.golden --- a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go.golden 1970-01-01 08:00:00 @@ -1,70 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9732,7 +10844,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/a/a.go.golden -} diff -urN a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/typeparams.go b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/typeparams.go --- a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/typeparams.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/typeparams.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/typeparams.go 1970-01-01 08:00:00 @@ -1,39 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9775,7 +10887,7 @@ diff -urN a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/ty -} diff -urN a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/typeparams.go.golden b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/typeparams.go.golden --- a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/typeparams.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/typeparams.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/typeparams.go.golden 1970-01-01 08:00:00 @@ -1,39 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -9818,8 +10930,8 @@ diff -urN a/gopls/internal/lsp/analysis/simplifyslice/testdata/src/typeparams/ty -} diff -urN a/gopls/internal/lsp/analysis/stubmethods/stubmethods.go b/gopls/internal/lsp/analysis/stubmethods/stubmethods.go --- a/gopls/internal/lsp/analysis/stubmethods/stubmethods.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/stubmethods/stubmethods.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,418 +0,0 @@ ++++ b/gopls/internal/lsp/analysis/stubmethods/stubmethods.go 1970-01-01 08:00:00 +@@ -1,449 +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. @@ -9837,7 +10949,6 @@ diff -urN a/gopls/internal/lsp/analysis/stubmethods/stubmethods.go b/gopls/inter - "strings" - - "golang.org/x/tools/go/analysis" -- "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/internal/analysisinternal" - "golang.org/x/tools/internal/typesinternal" @@ -9851,17 +10962,17 @@ diff -urN a/gopls/internal/lsp/analysis/stubmethods/stubmethods.go b/gopls/inter -var Analyzer = &analysis.Analyzer{ - Name: "stubmethods", - Doc: Doc, -- Requires: []*analysis.Analyzer{inspect.Analyzer}, - Run: run, - RunDespiteErrors: true, -} - +-// TODO(rfindley): remove this thin wrapper around the stubmethods refactoring, +-// and eliminate the stubmethods analyzer. +-// +-// Previous iterations used the analysis framework for computing refactorings, +-// which proved inefficient. -func run(pass *analysis.Pass) (interface{}, error) { - for _, err := range pass.TypeErrors { -- ifaceErr := strings.Contains(err.Msg, "missing method") || strings.HasPrefix(err.Msg, "cannot convert") -- if !ifaceErr { -- continue -- } - var file *ast.File - for _, f := range pass.Files { - if f.Pos() <= err.Pos && err.Pos < f.End() { @@ -9869,33 +10980,54 @@ diff -urN a/gopls/internal/lsp/analysis/stubmethods/stubmethods.go b/gopls/inter - break - } - } -- if file == nil { -- continue -- } - // Get the end position of the error. -- _, _, endPos, ok := typesinternal.ReadGo116ErrorData(err) +- _, _, end, ok := typesinternal.ReadGo116ErrorData(err) - if !ok { - var buf bytes.Buffer - if err := format.Node(&buf, pass.Fset, file); err != nil { - continue - } -- endPos = analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), err.Pos) +- end = analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), err.Pos) - } -- path, _ := astutil.PathEnclosingInterval(file, err.Pos, endPos) -- si := GetStubInfo(pass.Fset, pass.TypesInfo, path, err.Pos) -- if si == nil { -- continue +- if diag, ok := DiagnosticForError(pass.Fset, file, err.Pos, end, err.Msg, pass.TypesInfo); ok { +- pass.Report(diag) - } -- qf := RelativeToFiles(si.Concrete.Obj().Pkg(), file, nil, nil) -- pass.Report(analysis.Diagnostic{ -- Pos: err.Pos, -- End: endPos, -- Message: fmt.Sprintf("Implement %s", types.TypeString(si.Interface.Type(), qf)), -- }) - } +- - return nil, nil -} - +-// MatchesMessage reports whether msg matches the error message sought after by +-// the stubmethods fix. +-func MatchesMessage(msg string) bool { +- return strings.Contains(msg, "missing method") || strings.HasPrefix(msg, "cannot convert") +-} +- +-// DiagnosticForError computes a diagnostic suggesting to implement an +-// interface to fix the type checking error defined by (start, end, msg). +-// +-// If no such fix is possible, the second result is false. +-// +-// TODO(rfindley): simplify this signature once the stubmethods refactoring is +-// no longer wedged into the analysis framework. +-func DiagnosticForError(fset *token.FileSet, file *ast.File, start, end token.Pos, msg string, info *types.Info) (analysis.Diagnostic, bool) { +- if !MatchesMessage(msg) { +- return analysis.Diagnostic{}, false +- } +- +- path, _ := astutil.PathEnclosingInterval(file, start, end) +- si := GetStubInfo(fset, info, path, start) +- if si == nil { +- return analysis.Diagnostic{}, false +- } +- qf := RelativeToFiles(si.Concrete.Obj().Pkg(), file, nil, nil) +- return analysis.Diagnostic{ +- Pos: start, +- End: end, +- Message: fmt.Sprintf("Implement %s", types.TypeString(si.Interface.Type(), qf)), +- }, true +-} +- -// StubInfo represents a concrete type -// that wants to stub out an interface type -type StubInfo struct { @@ -9917,7 +11049,7 @@ diff -urN a/gopls/internal/lsp/analysis/stubmethods/stubmethods.go b/gopls/inter -// -// TODO(adonovan): this function (and its following 5 helpers) tries -// to deduce a pair of (concrete, interface) types that are related by --// an assignment, either explictly or through a return statement or +-// an assignment, either explicitly or through a return statement or -// function call. This is essentially what the refactor/satisfy does, -// more generally. Refactor to share logic, after auditing 'satisfy' -// for safety on ill-typed code. @@ -9976,8 +11108,19 @@ diff -urN a/gopls/internal/lsp/analysis/stubmethods/stubmethods.go b/gopls/inter - if !ok { - return nil - } -- sigVar := sig.Params().At(paramIdx) -- iface := ifaceObjFromType(sigVar.Type()) +- var paramType types.Type +- if sig.Variadic() && paramIdx >= sig.Params().Len()-1 { +- v := sig.Params().At(sig.Params().Len() - 1) +- if s, _ := v.Type().(*types.Slice); s != nil { +- paramType = s.Elem() +- } +- } else if paramIdx < sig.Params().Len() { +- paramType = sig.Params().At(paramIdx).Type() +- } +- if paramType == nil { +- return nil // A type error prevents us from determining the param type. +- } +- iface := ifaceObjFromType(paramType) - if iface == nil { - return nil - } @@ -10240,7 +11383,7 @@ diff -urN a/gopls/internal/lsp/analysis/stubmethods/stubmethods.go b/gopls/inter -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/a.go b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/a.go --- a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/a.go 1970-01-01 08:00:00 @@ -1,28 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10272,7 +11415,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/a.go b/gop -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/channels.go b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/channels.go --- a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/channels.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/channels.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/channels.go 1970-01-01 08:00:00 @@ -1,13 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10289,7 +11432,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/channels.g -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/consecutive_params.go b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/consecutive_params.go --- a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/consecutive_params.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/consecutive_params.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/consecutive_params.go 1970-01-01 08:00:00 @@ -1,10 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10303,7 +11446,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/consecutiv -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/error_param.go b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/error_param.go --- a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/error_param.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/error_param.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/error_param.go 1970-01-01 08:00:00 @@ -1,10 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10317,7 +11460,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/error_para -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/literals.go b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/literals.go --- a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/literals.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/literals.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/literals.go 1970-01-01 08:00:00 @@ -1,11 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10332,7 +11475,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/literals.g -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/operation.go b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/operation.go --- a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/operation.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/operation.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/operation.go 1970-01-01 08:00:00 @@ -1,11 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10347,7 +11490,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/operation. -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/selector.go b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/selector.go --- a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/selector.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/selector.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/selector.go 1970-01-01 08:00:00 @@ -1,10 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10361,7 +11504,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/selector.g -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/slice.go b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/slice.go --- a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/slice.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/slice.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/slice.go 1970-01-01 08:00:00 @@ -1,9 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10374,7 +11517,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/slice.go b -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/tuple.go b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/tuple.go --- a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/tuple.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/tuple.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/tuple.go 1970-01-01 08:00:00 @@ -1,13 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10391,7 +11534,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/tuple.go b -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/unique_params.go b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/unique_params.go --- a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/unique_params.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/unique_params.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/unique_params.go 1970-01-01 08:00:00 @@ -1,11 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10406,7 +11549,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/testdata/src/a/unique_par -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/undeclared.go b/gopls/internal/lsp/analysis/undeclaredname/undeclared.go --- a/gopls/internal/lsp/analysis/undeclaredname/undeclared.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/undeclared.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/undeclared.go 1970-01-01 08:00:00 @@ -1,347 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10757,7 +11900,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/undeclared.go b/gopls/int -} diff -urN a/gopls/internal/lsp/analysis/undeclaredname/undeclared_test.go b/gopls/internal/lsp/analysis/undeclaredname/undeclared_test.go --- a/gopls/internal/lsp/analysis/undeclaredname/undeclared_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/undeclaredname/undeclared_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/undeclaredname/undeclared_test.go 1970-01-01 08:00:00 @@ -1,17 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10778,7 +11921,7 @@ diff -urN a/gopls/internal/lsp/analysis/undeclaredname/undeclared_test.go b/gopl -} diff -urN a/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go b/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go --- a/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go 1970-01-01 08:00:00 @@ -1,55 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10837,7 +11980,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go b/gopls -} diff -urN a/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go.golden b/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go.golden --- a/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go.golden 1970-01-01 08:00:00 @@ -1,55 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10896,7 +12039,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedparams/testdata/src/a/a.go.golden -} diff -urN a/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go b/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go --- a/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go 1970-01-01 08:00:00 @@ -1,55 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -10955,7 +12098,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typ -} diff -urN a/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go.golden b/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go.golden --- a/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typeparams.go.golden 1970-01-01 08:00:00 @@ -1,55 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11014,7 +12157,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedparams/testdata/src/typeparams/typ -} diff -urN a/gopls/internal/lsp/analysis/unusedparams/unusedparams.go b/gopls/internal/lsp/analysis/unusedparams/unusedparams.go --- a/gopls/internal/lsp/analysis/unusedparams/unusedparams.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedparams/unusedparams.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedparams/unusedparams.go 1970-01-01 08:00:00 @@ -1,152 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11042,7 +12185,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedparams/unusedparams.go b/gopls/int - -To reduce false positives it ignores: -- methods --- parameters that do not have a name or are underscored +-- parameters that do not have a name or have the name '_' (the blank identifier) -- functions in test files -- functions with empty bodies or those with just a return stmt` - @@ -11170,7 +12313,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedparams/unusedparams.go b/gopls/int -} diff -urN a/gopls/internal/lsp/analysis/unusedparams/unusedparams_test.go b/gopls/internal/lsp/analysis/unusedparams/unusedparams_test.go --- a/gopls/internal/lsp/analysis/unusedparams/unusedparams_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedparams/unusedparams_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedparams/unusedparams_test.go 1970-01-01 08:00:00 @@ -1,22 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11196,7 +12339,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedparams/unusedparams_test.go b/gopl -} diff -urN a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go --- a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go 1970-01-01 08:00:00 @@ -1,74 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11274,7 +12417,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go -} diff -urN a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go.golden b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go.golden --- a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go.golden 1970-01-01 08:00:00 @@ -1,59 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11337,7 +12480,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/assign/a.go. -} diff -urN a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go --- a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go 1970-01-01 08:00:00 @@ -1,30 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11371,7 +12514,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go b/ -} diff -urN a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go.golden b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go.golden --- a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go.golden 1970-01-01 08:00:00 @@ -1,24 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11399,7 +12542,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedvariable/testdata/src/decl/a.go.go -} diff -urN a/gopls/internal/lsp/analysis/unusedvariable/unusedvariable.go b/gopls/internal/lsp/analysis/unusedvariable/unusedvariable.go --- a/gopls/internal/lsp/analysis/unusedvariable/unusedvariable.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedvariable/unusedvariable.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedvariable/unusedvariable.go 1970-01-01 08:00:00 @@ -1,300 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11703,7 +12846,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedvariable/unusedvariable.go b/gopls -} diff -urN a/gopls/internal/lsp/analysis/unusedvariable/unusedvariable_test.go b/gopls/internal/lsp/analysis/unusedvariable/unusedvariable_test.go --- a/gopls/internal/lsp/analysis/unusedvariable/unusedvariable_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/unusedvariable/unusedvariable_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/unusedvariable/unusedvariable_test.go 1970-01-01 08:00:00 @@ -1,24 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11731,7 +12874,7 @@ diff -urN a/gopls/internal/lsp/analysis/unusedvariable/unusedvariable_test.go b/ -} diff -urN a/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go b/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go --- a/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go 1970-01-01 08:00:00 @@ -1,25 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11760,7 +12903,7 @@ diff -urN a/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go b/gopls/inter -type _[T any] int diff -urN a/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go.golden b/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go.golden --- a/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go.golden 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go.golden 1970-01-01 08:00:00 @@ -1,25 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11789,7 +12932,7 @@ diff -urN a/gopls/internal/lsp/analysis/useany/testdata/src/a/a.go.golden b/gopl -type _[T any] int diff -urN a/gopls/internal/lsp/analysis/useany/useany.go b/gopls/internal/lsp/analysis/useany/useany.go --- a/gopls/internal/lsp/analysis/useany/useany.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/useany/useany.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/useany/useany.go 1970-01-01 08:00:00 @@ -1,102 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11895,7 +13038,7 @@ diff -urN a/gopls/internal/lsp/analysis/useany/useany.go b/gopls/internal/lsp/an -} diff -urN a/gopls/internal/lsp/analysis/useany/useany_test.go b/gopls/internal/lsp/analysis/useany/useany_test.go --- a/gopls/internal/lsp/analysis/useany/useany_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/analysis/useany/useany_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/analysis/useany/useany_test.go 1970-01-01 08:00:00 @@ -1,21 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11918,9 +13061,15 @@ diff -urN a/gopls/internal/lsp/analysis/useany/useany_test.go b/gopls/internal/l - testdata := analysistest.TestData() - analysistest.RunWithSuggestedFixes(t, testdata, useany.Analyzer, "a") -} +diff -urN a/gopls/internal/lsp/browser/README.md b/gopls/internal/lsp/browser/README.md +--- a/gopls/internal/lsp/browser/README.md 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/browser/README.md 1970-01-01 08:00:00 +@@ -1 +0,0 @@ +-This package is a copy of cmd/internal/browser from the go distribution +\ No newline at end of file diff -urN a/gopls/internal/lsp/browser/browser.go b/gopls/internal/lsp/browser/browser.go --- a/gopls/internal/lsp/browser/browser.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/browser/browser.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/browser/browser.go 1970-01-01 08:00:00 @@ -1,67 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -11989,16 +13138,10 @@ diff -urN a/gopls/internal/lsp/browser/browser.go b/gopls/internal/lsp/browser/b - return err == nil - } -} -diff -urN a/gopls/internal/lsp/browser/README.md b/gopls/internal/lsp/browser/README.md ---- a/gopls/internal/lsp/browser/README.md 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/browser/README.md 1970-01-01 00:00:00.000000000 +0000 -@@ -1 +0,0 @@ --This package is a copy of cmd/internal/browser from the go distribution -\ No newline at end of file diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/analysis.go --- a/gopls/internal/lsp/cache/analysis.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/analysis.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1247 +0,0 @@ ++++ b/gopls/internal/lsp/cache/analysis.go 1970-01-01 08:00:00 +@@ -1,1515 +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. @@ -12019,22 +13162,28 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - "go/token" - "go/types" - "log" +- urlpkg "net/url" - "reflect" +- "runtime" - "runtime/debug" - "sort" - "strings" - "sync" +- "sync/atomic" - "time" - - "golang.org/x/sync/errgroup" - "golang.org/x/tools/go/analysis" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/filecache" +- "golang.org/x/tools/gopls/internal/lsp/frob" +- "golang.org/x/tools/gopls/internal/lsp/progress" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/internal/bug" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" - "golang.org/x/tools/internal/facts" - "golang.org/x/tools/internal/gcimporter" -- "golang.org/x/tools/internal/memoize" - "golang.org/x/tools/internal/typeparams" - "golang.org/x/tools/internal/typesinternal" -) @@ -12043,48 +13192,50 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - - DESIGN - -- An analysis request is for a set of analyzers and an individual -- package ID, notated (a*, p). The result is the set of diagnostics -- for that package. It could easily be generalized to a set of -- packages, (a*, p*), and perhaps should be, to improve performance -- versus calling it in a loop. -- -- The snapshot holds a cache (persistent.Map) of entries keyed by -- (a*, p) pairs ("analysisKey") that have been requested so far. Some -- of these entries may be invalidated during snapshot cloning after a -- modification event. The cache maps each (a*, p) to a promise of -- the analysis result or "analysisSummary". The summary contains the -- results of analysis (e.g. diagnostics) as well as the intermediate -- results required by the recursion, such as serialized types and -- facts. -- -- The promise represents the result of a call to analyzeImpl, which -- type-checks a package and then applies a graph of analyzers to it -- in parallel postorder. (These graph edges are "horizontal": within -- the same package.) First, analyzeImpl reads the source files of -- package p, and obtains (recursively) the results of the "vertical" -- dependencies (i.e. analyzers applied to the packages imported by -- p). Only the subset of analyzers that use facts need be executed -- recursively, but even if this subset is empty, the step is still -- necessary because it provides type information. It is possible that -- a package may need to be type-checked and analyzed twice, for -- different subsets of analyzers, but the overlap is typically -- insignificant. -- -- With the file contents and the results of vertical dependencies, -- analyzeImpl is then in a position to produce a key representing the -- unit of work (parsing, type-checking, and analysis) that it has to -- do. The key is a cryptographic hash of the "recipe" for this step, -- including the Metadata, the file contents, the set of analyzers, -- and the type and fact information from the vertical dependencies. +- An analysis request (Snapshot.Analyze) is for a set of Analyzers and +- PackageIDs. The result is the set of diagnostics for those +- packages. Each request constructs a transitively closed DAG of +- nodes, each representing a package, then works bottom up in +- parallel postorder calling runCached to ensure that each node's +- analysis summary is up to date. The summary contains the analysis +- diagnostics as well as the intermediate results required by the +- recursion, such as serialized types and facts. +- +- The entire DAG is ephemeral. Each node in the DAG records the set +- of analyzers to run: the complete set for the root packages, and +- the "facty" subset for dependencies. Each package is thus analyzed +- at most once. The entire DAG shares a single FileSet for parsing +- and importing. +- +- Each node is processed by runCached. It gets the source file +- content hashes for package p, and the summaries of its "vertical" +- dependencies (direct imports), and from them it computes a key +- representing the unit of work (parsing, type-checking, and +- analysis) that it has to do. The key is a cryptographic hash of the +- "recipe" for this step, including the Metadata, the file contents, +- the set of analyzers, and the type and fact information from the +- vertical dependencies. - - The key is sought in a machine-global persistent file-system based - cache. If this gopls process, or another gopls process on the same -- machine, has already performed this analysis step, analyzeImpl will +- machine, has already performed this analysis step, runCached will - make a cache hit and load the serialized summary of the results. If -- not, it will have to proceed to type-checking and analysis, and -- write a new cache entry. The entry contains serialized types -- (export data) and analysis facts. +- not, it will have to proceed to run() to parse and type-check the +- package and then apply a set of analyzers to it. (The set of +- analyzers applied to a single package itself forms a graph of +- "actions", and it too is evaluated in parallel postorder; these +- dependency edges within the same package are called "horizontal".) +- Finally it writes a new cache entry. The entry contains serialized +- types (export data) and analysis facts. +- +- Each node in the DAG acts like a go/types importer mapping, +- providing a consistent view of packages and their objects: the +- mapping for a node is a superset of its dependencies' mappings. +- Every node has an associated *types.Package, initially nil. A +- package is populated during run (cache miss) by type-checking its +- syntax; but for a cache hit, the package is populated lazily, i.e. +- not until it later becomes necessary because it is imported +- directly or referenced by export data higher up in the DAG. - - For types, we use "shallow" export data. Historically, the Go - compiler always produced a summary of the types for a given package @@ -12103,11 +13254,8 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - "Shallow" export data means that the serialized types describe only - a single package. If those types mention types from other packages, - the type checker may need to request additional packages beyond -- just the direct imports. This means type information for the entire -- transitive closure of imports may need to be available just in -- case. After a cache hit or a cache miss, the summary is -- postprocessed so that it contains the union of export data payloads -- of all its direct dependencies. +- just the direct imports. Type information for the entire transitive +- closure of imports is provided (lazily) by the DAG. - - For correct dependency analysis, the digest used as a cache key - must reflect the "deep" export data, so it is derived recursively @@ -12119,8 +13267,9 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - but if its export data is unchanged as a result, then indirect - consumers may not need to be re-executed. This allows, for example, - one to insert a print statement in a function and not "rebuild" the -- whole application (though export data does record line numbers of -- types which may be perturbed by otherwise insignificant changes.) +- whole application (though export data does record line numbers and +- offsets of types which may be perturbed by otherwise insignificant +- changes.) - - The summary must record whether a package is transitively - error-free (whether it would compile) because many analyzers are @@ -12133,13 +13282,6 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal -*/ - -// TODO(adonovan): --// - Profile + optimize: --// - on a cold run, mostly type checking + export data, unsurprisingly. --// - on a hot-disk run, mostly type checking the IWL. --// Would be nice to have a benchmark that separates this out. --// - measure and record in the code the typical operation times --// and file sizes (export data + facts = cache entries). --// - Do "port the old logic" tasks (see TODO in actuallyAnalyze). -// - Add a (white-box) test of pruning when a change doesn't affect export data. -// - Optimise pruning based on subset of packages mentioned in exportdata. -// - Better logging so that it is possible to deduce why an analyzer @@ -12147,7 +13289,6 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal -// Even if the ultimate consumer decides to ignore errors, -// tests and other situations want to be assured of freedom from -// errors, not just missing results. This should be recorded. --// - Check that the event trace is intelligible. -// - Split this into a subpackage, gopls/internal/lsp/cache/driver, -// consisting of this file and three helpers from errors.go. -// The (*snapshot).Analyze method would stay behind and make calls @@ -12155,35 +13296,47 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal -// Steps: -// - define a narrow driver.Snapshot interface with only these methods: -// Metadata(PackageID) source.Metadata --// GetFile(Context, URI) (source.FileHandle, error) +-// ReadFile(Context, URI) (source.FileHandle, error) -// View() *View // for Options --// - define a State type that encapsulates the persistent map --// (with its own mutex), and has methods: --// New() *State --// Clone(invalidate map[PackageID]bool) *State --// Destroy() -// - share cache.{goVersionRx,parseGoImpl} - --var born = time.Now() +-// AnalysisProgressTitle is the title of the progress report for ongoing +-// analysis. It is sought by regression tests for the progress reporting +-// feature. +-const AnalysisProgressTitle = "Analyzing Dependencies" - -// Analyze applies a set of analyzers to the package denoted by id, -// and returns their diagnostics for that package. -// -// The analyzers list must be duplicate free; order does not matter. -// +-// Notifications of progress may be sent to the optional reporter. +-// -// Precondition: all analyzers within the process have distinct names. -// (The names are relied on by the serialization logic.) --func (s *snapshot) Analyze(ctx context.Context, id PackageID, analyzers []*source.Analyzer) ([]*source.Diagnostic, error) { -- if false { // debugging -- log.Println("Analyze@", time.Since(born)) // called after the 7s IWL in k8s +-func (snapshot *snapshot) Analyze(ctx context.Context, pkgs map[PackageID]unit, analyzers []*source.Analyzer, reporter *progress.Tracker) ([]*source.Diagnostic, error) { +- start := time.Now() // for progress reporting +- +- var tagStr string // sorted comma-separated list of PackageIDs +- { +- // TODO(adonovan): replace with a generic map[S]any -> string +- // function in the tag package, and use maps.Keys + slices.Sort. +- keys := make([]string, 0, len(pkgs)) +- for id := range pkgs { +- keys = append(keys, string(id)) +- } +- sort.Strings(keys) +- tagStr = strings.Join(keys, ",") - } +- ctx, done := event.Start(ctx, "snapshot.Analyze", tag.Package.Of(tagStr)) +- defer done() - - // Filter and sort enabled root analyzers. - // A disabled analyzer may still be run if required by another. - toSrc := make(map[*analysis.Analyzer]*source.Analyzer) -- var enabled []*analysis.Analyzer +- var enabled []*analysis.Analyzer // enabled subset + transitive requirements - for _, a := range analyzers { -- if a.IsEnabled(s.view.Options()) { +- if a.IsEnabled(snapshot.options) { - toSrc[a.Analyzer] = a - enabled = append(enabled, a.Analyzer) - } @@ -12191,26 +13344,216 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - sort.Slice(enabled, func(i, j int) bool { - return enabled[i].Name < enabled[j].Name - }) +- analyzers = nil // prevent accidental use - - // Register fact types of required analyzers. -- for _, a := range requiredAnalyzers(enabled) { -- for _, f := range a.FactTypes { -- gob.Register(f) +- enabled = requiredAnalyzers(enabled) +- var facty []*analysis.Analyzer // facty subset of enabled + transitive requirements +- for _, a := range enabled { +- if len(a.FactTypes) > 0 { +- facty = append(facty, a) +- for _, f := range a.FactTypes { +- gob.Register(f) // <2us +- } - } - } +- facty = requiredAnalyzers(facty) - -- if false { // debugging -- // TODO(adonovan): use proper tracing. -- t0 := time.Now() +- // File set for this batch (entire graph) of analysis. +- fset := token.NewFileSet() +- +- // Starting from the root packages and following DepsByPkgPath, +- // build the DAG of packages we're going to analyze. +- // +- // Root nodes will run the enabled set of analyzers, +- // whereas dependencies will run only the facty set. +- // Because (by construction) enabled is a superset of facty, +- // we can analyze each node with exactly one set of analyzers. +- nodes := make(map[PackageID]*analysisNode) +- var leaves []*analysisNode // nodes with no unfinished successors +- var makeNode func(from *analysisNode, id PackageID) (*analysisNode, error) +- makeNode = func(from *analysisNode, id PackageID) (*analysisNode, error) { +- an, ok := nodes[id] +- if !ok { +- m := snapshot.Metadata(id) +- if m == nil { +- return nil, bug.Errorf("no metadata for %s", id) +- } +- +- // -- preorder -- +- +- an = &analysisNode{ +- fset: fset, +- m: m, +- analyzers: facty, // all nodes run at least the facty analyzers +- allDeps: make(map[PackagePath]*analysisNode), +- exportDeps: make(map[PackagePath]*analysisNode), +- } +- nodes[id] = an +- +- // -- recursion -- +- +- // Build subgraphs for dependencies. +- an.succs = make(map[PackageID]*analysisNode, len(m.DepsByPkgPath)) +- for _, depID := range m.DepsByPkgPath { +- dep, err := makeNode(an, depID) +- if err != nil { +- return nil, err +- } +- an.succs[depID] = dep +- +- // Compute the union of all dependencies. +- // (This step has quadratic complexity.) +- for pkgPath, node := range dep.allDeps { +- an.allDeps[pkgPath] = node +- } +- } +- +- // -- postorder -- +- +- an.allDeps[m.PkgPath] = an // add self entry (reflexive transitive closure) +- +- // Add leaf nodes (no successors) directly to queue. +- if len(an.succs) == 0 { +- leaves = append(leaves, an) +- } +- +- // Load the contents of each compiled Go file through +- // the snapshot's cache. (These are all cache hits as +- // files are pre-loaded following packages.Load) +- an.files = make([]source.FileHandle, len(m.CompiledGoFiles)) +- for i, uri := range m.CompiledGoFiles { +- fh, err := snapshot.ReadFile(ctx, uri) +- if err != nil { +- return nil, err +- } +- an.files[i] = fh +- } +- } +- // Add edge from predecessor. +- if from != nil { +- atomic.AddInt32(&from.unfinishedSuccs, 1) // TODO(adonovan): use generics +- an.preds = append(an.preds, from) +- } +- atomic.AddInt32(&an.unfinishedPreds, 1) +- return an, nil +- } +- +- // For root packages, we run the enabled set of analyzers. +- var roots []*analysisNode +- for id := range pkgs { +- root, err := makeNode(nil, id) +- if err != nil { +- return nil, err +- } +- root.analyzers = enabled +- roots = append(roots, root) +- } +- +- // Now that we have read all files, +- // we no longer need the snapshot. +- // (but options are needed for progress reporting) +- options := snapshot.options +- snapshot = nil +- +- // Progress reporting. If supported, gopls reports progress on analysis +- // passes that are taking a long time. +- maybeReport := func(completed int64) {} +- +- // Enable progress reporting if enabled by the user +- // and we have a capable reporter. +- if reporter != nil && reporter.SupportsWorkDoneProgress() && options.AnalysisProgressReporting { +- var reportAfter = options.ReportAnalysisProgressAfter // tests may set this to 0 +- const reportEvery = 1 * time.Second +- +- ctx, cancel := context.WithCancel(ctx) +- defer cancel() +- +- var ( +- reportMu sync.Mutex +- lastReport time.Time +- wd *progress.WorkDone +- ) - defer func() { -- log.Printf("%v for analyze(%s, %s)", time.Since(t0), id, enabled) +- reportMu.Lock() +- defer reportMu.Unlock() +- +- if wd != nil { +- wd.End(ctx, "Done.") // ensure that the progress report exits +- } - }() +- maybeReport = func(completed int64) { +- now := time.Now() +- if now.Sub(start) < reportAfter { +- return +- } +- +- reportMu.Lock() +- defer reportMu.Unlock() +- +- if wd == nil { +- wd = reporter.Start(ctx, AnalysisProgressTitle, "", nil, cancel) +- } +- +- if now.Sub(lastReport) > reportEvery { +- lastReport = now +- // Trailing space is intentional: some LSP clients strip newlines. +- msg := fmt.Sprintf(`Indexed %d/%d packages. (Set "analysisProgressReporting" to false to disable notifications.)`, +- completed, len(nodes)) +- pct := 100 * float64(completed) / float64(len(nodes)) +- wd.Report(ctx, msg, pct) +- } +- } - } - -- // Run the analysis. -- res, err := s.analyze(ctx, id, enabled) -- if err != nil { -- return nil, err +- // Execute phase: run leaves first, adding +- // new nodes to the queue as they become leaves. +- var g errgroup.Group +- +- // Analysis is CPU-bound. +- // +- // Note: avoid g.SetLimit here: it makes g.Go stop accepting work, which +- // prevents workers from enqeuing, and thus finishing, and thus allowing the +- // group to make progress: deadlock. +- limiter := make(chan unit, runtime.GOMAXPROCS(0)) +- var completed int64 +- +- var enqueue func(*analysisNode) +- enqueue = func(an *analysisNode) { +- g.Go(func() error { +- limiter <- unit{} +- defer func() { <-limiter }() +- +- summary, err := an.runCached(ctx) +- if err != nil { +- return err // cancelled, or failed to produce a package +- } +- maybeReport(atomic.AddInt64(&completed, 1)) +- an.summary = summary +- +- // Notify each waiting predecessor, +- // and enqueue it when it becomes a leaf. +- for _, pred := range an.preds { +- if atomic.AddInt32(&pred.unfinishedSuccs, -1) == 0 { +- enqueue(pred) +- } +- } +- +- // Notify each successor that we no longer need +- // its action summaries, which hold Result values. +- // After the last one, delete it, so that we +- // free up large results such as SSA. +- for _, succ := range an.succs { +- succ.decrefPreds() +- } +- return nil +- }) +- } +- for _, leaf := range leaves { +- enqueue(leaf) +- } +- if err := g.Wait(); err != nil { +- return nil, err // cancelled, or failed to produce a package - } - - // Report diagnostics only from enabled actions that succeeded. @@ -12224,47 +13567,162 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - // Even if current callers choose to discard the - // results, we should propagate the per-action errors. - var results []*source.Diagnostic -- for _, a := range enabled { -- summary := res.Actions[a.Name] -- if summary.Err != "" { -- continue // action failed -- } -- for _, gobDiag := range summary.Diagnostics { -- results = append(results, toSourceDiagnostic(toSrc[a], &gobDiag)) +- for _, root := range roots { +- for _, a := range enabled { +- // Skip analyzers that were added only to +- // fulfil requirements of the original set. +- srcAnalyzer, ok := toSrc[a] +- if !ok { +- // Although this 'skip' operation is logically sound, +- // it is nonetheless surprising that its absence should +- // cause #60909 since none of the analyzers added for +- // requirements (e.g. ctrlflow, inspect, buildssa) +- // is capable of reporting diagnostics. +- if summary := root.summary.Actions[a.Name]; summary != nil { +- if n := len(summary.Diagnostics); n > 0 { +- bug.Reportf("Internal error: got %d unexpected diagnostics from analyzer %s. This analyzer was added only to fulfil the requirements of the requested set of analyzers, and it is not expected that such analyzers report diagnostics. Please report this in issue #60909.", n, a) +- } +- } +- continue +- } +- +- // Inv: root.summary is the successful result of run (via runCached). +- summary, ok := root.summary.Actions[a.Name] +- if summary == nil { +- panic(fmt.Sprintf("analyzeSummary.Actions[%q] = (nil, %t); got %v (#60551)", +- a.Name, ok, root.summary.Actions)) +- } +- if summary.Err != "" { +- continue // action failed +- } +- for _, gobDiag := range summary.Diagnostics { +- results = append(results, toSourceDiagnostic(srcAnalyzer, &gobDiag)) +- } - } - } - return results, nil -} - --// analysisKey is the type of keys in the snapshot.analyses map. --type analysisKey struct { -- analyzerNames string -- pkgid PackageID --} +-func (an *analysisNode) decrefPreds() { +- if atomic.AddInt32(&an.unfinishedPreds, -1) == 0 { +- an.summary.Actions = nil +- } +-} +- +-// An analysisNode is a node in a doubly-linked DAG isomorphic to the +-// import graph. Each node represents a single package, and the DAG +-// represents a batch of analysis work done at once using a single +-// realm of token.Pos or types.Object values. +-// +-// A complete DAG is created anew for each batch of analysis; +-// subgraphs are not reused over time. Each node's *types.Package +-// field is initially nil and is populated on demand, either from +-// type-checking syntax trees (typeCheck) or from importing export +-// data (_import). When this occurs, the typesOnce event becomes +-// "done". +-// +-// Each node's allDeps map is a "view" of all its dependencies keyed by +-// package path, which defines the types.Importer mapping used when +-// populating the node's types.Package. Different nodes have different +-// views (e.g. due to variants), but two nodes that are related by +-// graph ordering have views that are consistent in their overlap. +-// exportDeps is the subset actually referenced by export data; +-// this is the set for which we attempt to decode facts. +-// +-// Each node's run method is called in parallel postorder. On success, +-// its summary field is populated, either from the cache (hit), or by +-// type-checking and analyzing syntax (miss). +-type analysisNode struct { +- fset *token.FileSet // file set shared by entire batch (DAG) +- m *source.Metadata // metadata for this package +- files []source.FileHandle // contents of CompiledGoFiles +- analyzers []*analysis.Analyzer // set of analyzers to run +- preds []*analysisNode // graph edges: +- succs map[PackageID]*analysisNode // (preds -> self -> succs) +- unfinishedSuccs int32 +- unfinishedPreds int32 // effectively a summary.Actions refcount +- allDeps map[PackagePath]*analysisNode // all dependencies including self +- exportDeps map[PackagePath]*analysisNode // subset of allDeps ref'd by export data (+self) +- summary *analyzeSummary // serializable result of analyzing this package +- +- typesOnce sync.Once // guards lazy population of types and typesErr fields +- types *types.Package // type information lazily imported from summary +- typesErr error // an error producing type information +-} +- +-func (an *analysisNode) String() string { return string(an.m.ID) } +- +-// _import imports this node's types.Package from export data, if not already done. +-// Precondition: analysis was a success. +-// Postcondition: an.types and an.exportDeps are populated. +-func (an *analysisNode) _import() (*types.Package, error) { +- an.typesOnce.Do(func() { +- if an.m.PkgPath == "unsafe" { +- an.types = types.Unsafe +- return +- } +- +- an.types = types.NewPackage(string(an.m.PkgPath), string(an.m.Name)) - --func (key analysisKey) String() string { -- return fmt.Sprintf("%s@%s", key.analyzerNames, key.pkgid) +- // getPackages recursively imports each dependency +- // referenced by the export data, in parallel. +- getPackages := func(items []gcimporter.GetPackagesItem) error { +- var g errgroup.Group +- for i, item := range items { +- path := PackagePath(item.Path) +- dep, ok := an.allDeps[path] +- if !ok { +- // This early return bypasses Wait; that's ok. +- return fmt.Errorf("%s: unknown dependency %q", an.m, path) +- } +- an.exportDeps[path] = dep // record, for later fact decoding +- if dep == an { +- if an.typesErr != nil { +- return an.typesErr +- } else { +- items[i].Pkg = an.types +- } +- } else { +- i := i +- g.Go(func() error { +- depPkg, err := dep._import() +- if err == nil { +- items[i].Pkg = depPkg +- } +- return err +- }) +- } +- } +- return g.Wait() +- } +- pkg, err := gcimporter.IImportShallow(an.fset, getPackages, an.summary.Export, string(an.m.PkgPath), bug.Reportf) +- if err != nil { +- an.typesErr = bug.Errorf("%s: invalid export data: %v", an.m, err) +- an.types = nil +- } else if pkg != an.types { +- log.Fatalf("%s: inconsistent packages", an.m) +- } +- }) +- return an.types, an.typesErr -} - -// analyzeSummary is a gob-serializable summary of successfully -// applying a list of analyzers to a package. -type analyzeSummary struct { -- PkgPath PackagePath // types.Package.Path() (needed to decode export data) -- Export []byte +- Export []byte // encoded types of package - DeepExportHash source.Hash // hash of reflexive transitive closure of export data - Compiles bool // transitively free of list/parse/type errors - Actions actionsMap // map from analyzer name to analysis results (*actionSummary) -- -- // Not serialized: populated after the summary is computed or deserialized. -- allExport map[PackagePath][]byte // transitive export data -} - -// actionsMap defines a stable Gob encoding for a map. -// TODO(adonovan): generalize and move to a library when we can use generics. -type actionsMap map[string]*actionSummary - --var _ gob.GobEncoder = (actionsMap)(nil) --var _ gob.GobDecoder = (*actionsMap)(nil) +-var ( +- _ gob.GobEncoder = (actionsMap)(nil) +- _ gob.GobDecoder = (*actionsMap)(nil) +-) - -type actionsMapEntry struct { - K string @@ -12305,133 +13763,16 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - Err string // "" => success -} - --// analyze is a memoization of analyzeImpl. --func (s *snapshot) analyze(ctx context.Context, id PackageID, analyzers []*analysis.Analyzer) (*analyzeSummary, error) { -- // Use the sorted list of names of analyzers in the key. -- // -- // TODO(adonovan): opt: account for analysis results at a -- // finer grain to avoid duplicate work when a -- // a proper subset of analyzers is requested? -- // In particular, TypeErrorAnalyzers don't use facts -- // but need to request vdeps just for type information. -- names := make([]string, 0, len(analyzers)) -- for _, a := range analyzers { -- names = append(names, a.Name) -- } -- // This key describes the result of applying a list of analyzers to a package. -- key := analysisKey{strings.Join(names, ","), id} -- -- // An analysisPromise represents the result of loading, parsing, -- // type-checking and analyzing a single package. -- type analysisPromise struct { -- promise *memoize.Promise // [analyzeImplResult] -- } -- -- type analyzeImplResult struct { -- summary *analyzeSummary -- err error -- } -- -- // Access the map once, briefly, and atomically. -- s.mu.Lock() -- entry, hit := s.analyses.Get(key) -- if !hit { -- entry = analysisPromise{ -- promise: memoize.NewPromise("analysis", func(ctx context.Context, arg interface{}) interface{} { -- summary, err := analyzeImpl(ctx, arg.(*snapshot), analyzers, id) -- return analyzeImplResult{summary, err} -- }), -- } -- s.analyses.Set(key, entry, nil) // nothing needs releasing -- } -- s.mu.Unlock() -- -- // Await result. -- ap := entry.(analysisPromise) -- v, err := s.awaitPromise(ctx, ap.promise) -- if err != nil { -- return nil, err // e.g. cancelled -- } -- res := v.(analyzeImplResult) -- return res.summary, res.err --} -- --// analyzeImpl applies a list of analyzers (plus any others +-// runCached applies a list of analyzers (plus any others -// transitively required by them) to a package. It succeeds as long -// as it could produce a types.Package, even if there were direct or -// indirect list/parse/type errors, and even if all the analysis -// actions failed. It usually fails only if the package was unknown, -// a file was missing, or the operation was cancelled. -// --// Postcondition: analyzeImpl must not continue to use the snapshot +-// Postcondition: runCached must not continue to use the snapshot -// (in background goroutines) after it has returned; see memoize.RefCounted. --func analyzeImpl(ctx context.Context, snapshot *snapshot, analyzers []*analysis.Analyzer, id PackageID) (*analyzeSummary, error) { -- m := snapshot.Metadata(id) -- if m == nil { -- return nil, fmt.Errorf("no metadata for %s", id) -- } -- -- // Recursively analyze each "vertical" dependency -- // for its types.Package and (perhaps) analysis.Facts. -- // If any of them fails to produce a package, we cannot continue. -- // We request only the analyzers that produce facts. -- // -- // Also, load the contents of each "compiled" Go file through -- // the snapshot's cache. -- // -- // Both loops occur in parallel, and parallel with each other. -- vdeps := make(map[PackageID]*analyzeSummary) -- compiledGoFiles := make([]source.FileHandle, len(m.CompiledGoFiles)) -- { -- var group errgroup.Group -- -- // Analyze vertical dependencies. -- // We request only the required analyzers that use facts. -- var useFacts []*analysis.Analyzer -- for _, a := range requiredAnalyzers(analyzers) { -- if len(a.FactTypes) > 0 { -- useFacts = append(useFacts, a) -- } -- } -- var vdepsMu sync.Mutex -- for _, id := range m.DepsByPkgPath { -- id := id -- group.Go(func() error { -- res, err := snapshot.analyze(ctx, id, useFacts) -- if err != nil { -- return err // cancelled, or failed to produce a package -- } -- -- vdepsMu.Lock() -- vdeps[id] = res -- vdepsMu.Unlock() -- return nil -- }) -- } -- -- // Read file contents. -- // (In practice these will be cache hits -- // on reads done by the initial workspace load -- // or after a change modification event.) -- for i, uri := range m.CompiledGoFiles { -- i, uri := i, uri -- group.Go(func() error { -- fh, err := snapshot.GetFile(ctx, uri) // ~25us -- compiledGoFiles[i] = fh -- return err // e.g. cancelled -- }) -- } -- -- if err := group.Wait(); err != nil { -- return nil, err -- } -- } -- -- // Inv: analyze() of all vdeps succeeded (though some actions may have failed). -- -- // We no longer depend on the snapshot. -- snapshot = nil -- +-func (an *analysisNode) runCached(ctx context.Context) (*analyzeSummary, error) { - // At this point we have the action results (serialized - // packages and facts) of our immediate dependencies, - // and the metadata and content of this package. @@ -12443,62 +13784,54 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - // The hash of our inputs is based on the serialized export - // data and facts so that immaterial changes can be pruned - // without decoding. -- key := analysisCacheKey(analyzers, m, compiledGoFiles, vdeps) +- key := an.cacheKey() - - // Access the cache. - var summary *analyzeSummary - const cacheKind = "analysis" - if data, err := filecache.Get(cacheKind, key); err == nil { - // cache hit -- mustDecode(data, &summary) -- +- analyzeSummaryCodec.Decode(data, &summary) - } else if err != filecache.ErrNotFound { - return nil, bug.Errorf("internal error reading shared cache: %v", err) -- - } else { - // Cache miss: do the work. - var err error -- summary, err = actuallyAnalyze(ctx, analyzers, m, vdeps, compiledGoFiles) +- summary, err = an.run(ctx) - if err != nil { - return nil, err - } -- data := mustEncode(summary) -- if false { -- log.Printf("Set key=%d value=%d id=%s\n", len(key), len(data), id) -- } -- if err := filecache.Set(cacheKind, key, data); err != nil { -- return nil, fmt.Errorf("internal error updating shared cache: %v", err) -- } -- } - -- // Hit or miss, we need to merge the export data from -- // dependencies so that it includes all the types -- // that might be summoned by the type checker. -- // -- // TODO(adonovan): opt: reduce this set by recording -- // which packages were actually summoned by insert(). -- // (Just makes map smaller; probably marginal?) -- allExport := make(map[PackagePath][]byte) -- for _, vdep := range vdeps { -- for k, v := range vdep.allExport { -- allExport[k] = v -- } +- atomic.AddInt32(&an.unfinishedPreds, +1) // incref +- go func() { +- defer an.decrefPreds() //decref +- +- cacheLimit <- unit{} // acquire token +- defer func() { <-cacheLimit }() // release token +- +- data := analyzeSummaryCodec.Encode(summary) +- if false { +- log.Printf("Set key=%d value=%d id=%s\n", len(key), len(data), an.m.ID) +- } +- if err := filecache.Set(cacheKind, key, data); err != nil { +- event.Error(ctx, "internal error updating analysis shared cache", err) +- } +- }() - } -- allExport[m.PkgPath] = summary.Export -- summary.allExport = allExport - - return summary, nil -} - +-// cacheLimit reduces parallelism of cache updates. +-// We allow more than typical GOMAXPROCS as it's a mix of CPU and I/O. +-var cacheLimit = make(chan unit, 32) +- -// analysisCacheKey returns a cache key that is a cryptographic digest -// of the all the values that might affect type checking and analysis: -// the analyzer names, package metadata, names and contents of --// compiled Go files, and vdeps information (export data and facts). --// --// TODO(adonovan): safety: define our own flavor of Metadata --// containing just the fields we need, and using it in the subsequent --// logic, to keep us honest about hashing all parts that matter? --func analysisCacheKey(analyzers []*analysis.Analyzer, m *source.Metadata, compiledGoFiles []source.FileHandle, vdeps map[PackageID]*analyzeSummary) [sha256.Size]byte { +-// compiled Go files, and vdeps (successor) information +-// (export data and facts). +-func (an *analysisNode) cacheKey() [sha256.Size]byte { - hasher := sha256.New() - - // In principle, a key must be the hash of an @@ -12506,21 +13839,21 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - // If it's ambiguous, we risk collisions. - - // analyzers -- fmt.Fprintf(hasher, "analyzers: %d\n", len(analyzers)) -- for _, a := range analyzers { +- fmt.Fprintf(hasher, "analyzers: %d\n", len(an.analyzers)) +- for _, a := range an.analyzers { - fmt.Fprintln(hasher, a.Name) - } - - // package metadata +- m := an.m - fmt.Fprintf(hasher, "package: %s %s %s\n", m.ID, m.Name, m.PkgPath) - // We can ignore m.DepsBy{Pkg,Import}Path: although the logic - // uses those fields, we account for them by hashing vdeps. - - // type sizes -- // This assertion is safe, but if a black-box implementation -- // is ever needed, record Sizeof(*int) and Alignof(int64). -- sz := m.TypesSizes.(*types.StdSizes) -- fmt.Fprintf(hasher, "sizes: %d %d\n", sz.WordSize, sz.MaxAlign) +- wordSize := an.m.TypesSizes.Sizeof(types.Typ[types.Int]) +- maxAlign := an.m.TypesSizes.Alignof(types.NewPointer(types.Typ[types.Int64])) +- fmt.Fprintf(hasher, "sizes: %d %d\n", wordSize, maxAlign) - - // metadata errors: used for 'compiles' field - fmt.Fprintf(hasher, "errors: %d", len(m.Errors)) @@ -12531,30 +13864,31 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - } - - // file names and contents -- fmt.Fprintf(hasher, "files: %d\n", len(compiledGoFiles)) -- for _, fh := range compiledGoFiles { +- fmt.Fprintf(hasher, "files: %d\n", len(an.files)) +- for _, fh := range an.files { - fmt.Fprintln(hasher, fh.FileIdentity()) - } - - // vdeps, in PackageID order -- depIDs := make([]string, 0, len(vdeps)) -- for depID := range vdeps { +- depIDs := make([]string, 0, len(an.succs)) +- for depID := range an.succs { - depIDs = append(depIDs, string(depID)) - } -- sort.Strings(depIDs) +- sort.Strings(depIDs) // TODO(adonovan): avoid conversions by using slices.Sort[PackageID] - for _, depID := range depIDs { -- vdep := vdeps[PackageID(depID)] -- fmt.Fprintf(hasher, "dep: %s\n", vdep.PkgPath) -- fmt.Fprintf(hasher, "export: %s\n", vdep.DeepExportHash) +- vdep := an.succs[PackageID(depID)] +- fmt.Fprintf(hasher, "dep: %s\n", vdep.m.PkgPath) +- fmt.Fprintf(hasher, "export: %s\n", vdep.summary.DeepExportHash) - - // action results: errors and facts -- names := make([]string, 0, len(vdep.Actions)) -- for name := range vdep.Actions { +- actions := vdep.summary.Actions +- names := make([]string, 0, len(actions)) +- for name := range actions { - names = append(names, name) - } - sort.Strings(names) - for _, name := range names { -- summary := vdep.Actions[name] +- summary := actions[name] - fmt.Fprintf(hasher, "action %s\n", name) - if summary.Err != "" { - fmt.Fprintf(hasher, "error %s\n", summary.Err) @@ -12571,24 +13905,26 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - return hash -} - --// actuallyAnalyze implements the cache-miss case. +-// run implements the cache-miss case. -// This function does not access the snapshot. --func actuallyAnalyze(ctx context.Context, analyzers []*analysis.Analyzer, m *source.Metadata, vdeps map[PackageID]*analyzeSummary, compiledGoFiles []source.FileHandle) (*analyzeSummary, error) { -- -- // Create a local FileSet for processing this package only. -- fset := token.NewFileSet() -- +-// +-// Postcondition: on success, the analyzeSummary.Actions +-// key set is {a.Name for a in analyzers}. +-func (an *analysisNode) run(ctx context.Context) (*analyzeSummary, error) { - // Parse only the "compiled" Go files. - // Do the computation in parallel. -- parsed := make([]*source.ParsedGoFile, len(compiledGoFiles)) +- parsed := make([]*source.ParsedGoFile, len(an.files)) - { - var group errgroup.Group -- for i, fh := range compiledGoFiles { +- group.SetLimit(4) // not too much: run itself is already called in parallel +- for i, fh := range an.files { - i, fh := i, fh - group.Go(func() error { - // Call parseGoImpl directly, not the caching wrapper, - // as cached ASTs require the global FileSet. -- pgf, err := parseGoImpl(ctx, fset, fh, source.ParseFull) +- // ast.Object resolution is unfortunately an implied part of the +- // go/analysis contract. +- pgf, err := parseGoImpl(ctx, an.fset, fh, source.ParseFull&^source.SkipObjectResolution, false) - parsed[i] = pgf - return err - }) @@ -12598,23 +13934,52 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - } - } - -- // Type-check the package. -- pkg := typeCheckForAnalysis(fset, parsed, m, vdeps) +- // Type-check the package syntax. +- pkg := an.typeCheck(parsed) - -- // Build a map of PkgPath to *Package for all packages mentioned -- // in exportdata for use by facts. -- pkg.factsDecoder = facts.NewDecoder(pkg.types) +- // Publish the completed package. +- an.typesOnce.Do(func() { an.types = pkg.types }) +- if an.types != pkg.types { +- log.Fatalf("typesOnce prematurely done") +- } +- +- // Compute the union of exportDeps across our direct imports. +- // This is the set that will be needed by the fact decoder. +- allExportDeps := make(map[PackagePath]*analysisNode) +- for _, succ := range an.succs { +- for k, v := range succ.exportDeps { +- allExportDeps[k] = v +- } +- } +- +- // The fact decoder needs a means to look up a Package by path. +- pkg.factsDecoder = facts.NewDecoderFunc(pkg.types, func(path string) *types.Package { +- // Note: Decode is called concurrently, and thus so is this function. +- +- // Does the fact relate to a package referenced by export data? +- if dep, ok := allExportDeps[PackagePath(path)]; ok { +- dep.typesOnce.Do(func() { log.Fatal("dep.types not populated") }) +- if dep.typesErr == nil { +- return dep.types +- } +- return nil +- } +- +- // If the fact relates to a dependency not referenced +- // by export data, it is safe to ignore it. +- // (In that case dep.types exists but may be unpopulated +- // or in the process of being populated from export data.) +- if an.allDeps[PackagePath(path)] == nil { +- log.Fatalf("fact package %q is not a dependency", path) +- } +- return nil +- }) - - // Poll cancellation state. - if err := ctx.Err(); err != nil { - return nil, err - } - -- // TODO(adonovan): port the old logic to: -- // - gather go/packages diagnostics from m.Errors? (port goPackagesErrorDiagnostics) -- // - record unparseable file URIs so we can suppress type errors for these files. -- // - gather diagnostics from expandErrors + typeErrorDiagnostics + depsErrors. -- - // -- analysis -- - - // Build action graph for this package. @@ -12628,7 +13993,7 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - for _, req := range a.Requires { - hdeps = append(hdeps, mkAction(req)) - } -- act = &action{a: a, pkg: pkg, vdeps: vdeps, hdeps: hdeps} +- act = &action{a: a, pkg: pkg, vdeps: an.succs, hdeps: hdeps} - actions[a] = act - } - return act @@ -12636,12 +14001,13 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - - // Build actions for initial package. - var roots []*action -- for _, a := range analyzers { +- for _, a := range an.analyzers { - roots = append(roots, mkAction(a)) - } - - // Execute the graph in parallel. - execActions(roots) +- // Inv: each root's summary is set (whether success or error). - - // Don't return (or cache) the result in case of cancellation. - if err := ctx.Err(); err != nil { @@ -12650,12 +14016,14 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - - // Return summaries only for the requested actions. - summaries := make(map[string]*actionSummary) -- for _, act := range roots { -- summaries[act.a.Name] = act.summary +- for _, root := range roots { +- if root.summary == nil { +- panic("root has nil action.summary (#60551)") +- } +- summaries[root.a.Name] = root.summary - } - - return &analyzeSummary{ -- PkgPath: PackagePath(pkg.types.Path()), - Export: pkg.export, - DeepExportHash: pkg.deepExportHash, - Compiles: pkg.compiles, @@ -12663,14 +14031,17 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - }, nil -} - --func typeCheckForAnalysis(fset *token.FileSet, parsed []*source.ParsedGoFile, m *source.Metadata, vdeps map[PackageID]*analyzeSummary) *analysisPackage { +-// Postcondition: analysisPackage.types and an.exportDeps are populated. +-func (an *analysisNode) typeCheck(parsed []*source.ParsedGoFile) *analysisPackage { +- m := an.m +- - if false { // debugging -- log.Println("typeCheckForAnalysis", m.PkgPath) +- log.Println("typeCheck", m.ID) - } - - pkg := &analysisPackage{ - m: m, -- fset: fset, +- fset: an.fset, - parsed: parsed, - files: make([]*ast.File, len(parsed)), - compiles: len(m.Errors) == 0, // false => list error @@ -12687,81 +14058,22 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - } - typeparams.InitInstanceInfo(pkg.typesInfo) - -- for i, p := range parsed { -- pkg.files[i] = p.File -- if p.ParseErr != nil { -- pkg.compiles = false // parse error -- } -- } -- -- // Unsafe is special. +- // Unsafe has no syntax. - if m.PkgPath == "unsafe" { - pkg.types = types.Unsafe - return pkg - } - -- // Compute the union of transitive export data. -- // (The actual values are shared, and not serialized.) -- allExport := make(map[PackagePath][]byte) -- for _, vdep := range vdeps { -- for k, v := range vdep.allExport { -- allExport[k] = v -- } -- -- if !vdep.Compiles { -- pkg.compiles = false // transitive error +- for i, p := range parsed { +- pkg.files[i] = p.File +- if p.ParseErr != nil { +- pkg.compiles = false // parse error - } - } - -- // exportHasher computes a hash of the names and export data of -- // each package that was actually loaded during type checking. -- // -- // Because we use shallow export data, the hash for dependency -- // analysis must incorporate indirect dependencies. As an -- // optimization, we include only those that were actually -- // used, which may be a small subset of those available. -- // -- // TODO(adonovan): opt: even better would be to implement a -- // traversal over the package API like facts.NewDecoder does -- // and only mention that set of packages in the hash. -- // Perhaps there's a way to do that more efficiently. -- // -- // TODO(adonovan): opt: record the shallow hash alongside the -- // shallow export data in the allExport map to avoid repeatedly -- // hashing the export data. -- // -- // The writes to hasher below assume that type checking imports -- // packages in a deterministic order. -- exportHasher := sha256.New() -- hashExport := func(pkgPath PackagePath, export []byte) { -- fmt.Fprintf(exportHasher, "%s %d ", pkgPath, len(export)) -- exportHasher.Write(export) -- } -- -- // importer state -- var ( -- insert func(p *types.Package, name string) -- importMap = make(map[string]*types.Package) // keys are PackagePaths -- ) -- loadFromExportData := func(pkgPath PackagePath) (*types.Package, error) { -- export, ok := allExport[pkgPath] -- if !ok { -- return nil, bug.Errorf("missing export data for %q", pkgPath) -- } -- hashExport(pkgPath, export) -- imported, err := gcimporter.IImportShallow(fset, importMap, export, string(pkgPath), insert) -- if err != nil { -- return nil, bug.Errorf("invalid export data for %q: %v", pkgPath, err) -- } -- return imported, nil -- } -- insert = func(p *types.Package, name string) { -- imported, err := loadFromExportData(PackagePath(p.Path())) -- if err != nil { -- log.Fatalf("internal error: %v", err) -- } -- if imported != p { -- log.Fatalf("internal error: inconsistent packages") +- for _, vdep := range an.succs { +- if !vdep.summary.Compiles { +- pkg.compiles = false // transitive error - } - } - @@ -12769,19 +14081,25 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - Sizes: m.TypesSizes, - Error: func(e error) { - pkg.compiles = false // type error -- pkg.typeErrors = append(pkg.typeErrors, e.(types.Error)) +- +- // Suppress type errors in files with parse errors +- // as parser recovery can be quite lossy (#59888). +- typeError := e.(types.Error) +- for _, p := range parsed { +- if p.ParseErr != nil && source.NodeContains(p.File, typeError.Pos) { +- return +- } +- } +- pkg.typeErrors = append(pkg.typeErrors, typeError) - }, - Importer: importerFunc(func(importPath string) (*types.Package, error) { -- if importPath == "unsafe" { -- return types.Unsafe, nil // unsafe has no export data -- } -- - // Beware that returning an error from this function - // will cause the type checker to synthesize a fake - // package whose Path is importPath, potentially - // losing a vendor/ prefix. If type-checking errors - // are swallowed, these packages may be confusing. - +- // Map ImportPath to ID. - id, ok := m.DepsByImpPath[ImportPath(importPath)] - if !ok { - // The import syntax is inconsistent with the metadata. @@ -12792,22 +14110,23 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - return nil, fmt.Errorf("missing metadata for import of %q", importPath) - } - -- depResult, ok := vdeps[id] // id may be "" -- if !ok { +- // Map ID to node. (id may be "") +- dep := an.succs[id] +- if dep == nil { - // Analogous to (*snapshot).missingPkgError - // in the logic for regular type-checking, - // but without a snapshot we can't provide - // such detail, and anyway most analysis - // failures aren't surfaced in the UI. -- return nil, fmt.Errorf("no required module provides package %q (id=%q)", importPath, id) +- return nil, fmt.Errorf("no required module provides analysis package %q (id=%q)", importPath, id) - } - - // (Duplicates logic from check.go.) -- if !source.IsValidImport(m.PkgPath, depResult.PkgPath) { +- if !source.IsValidImport(an.m.PkgPath, dep.m.PkgPath) { - return nil, fmt.Errorf("invalid use of internal package %s", importPath) - } - -- return loadFromExportData(depResult.PkgPath) +- return dep._import() - }), - } - @@ -12827,7 +14146,7 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - // TODO(adonovan): do we actually need this?? - typesinternal.SetUsesCgo(cfg) - -- check := types.NewChecker(cfg, fset, pkg.types, pkg.typesInfo) +- check := types.NewChecker(cfg, pkg.fset, pkg.types, pkg.typesInfo) - - // Type checking errors are handled via the config, so ignore them here. - _ = check.Files(pkg.files) @@ -12839,8 +14158,8 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - } - } - -- // Emit the export data and compute the deep hash. -- export, err := gcimporter.IExportShallow(pkg.fset, pkg.types) +- // Emit the export data and compute the recursive hash. +- export, err := gcimporter.IExportShallow(pkg.fset, pkg.types, bug.Reportf) - if err != nil { - // TODO(adonovan): in light of exporter bugs such as #57729, - // consider using bug.Report here and retrying the IExportShallow @@ -12848,12 +14167,56 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - log.Fatalf("internal error writing shallow export data: %v", err) - } - pkg.export = export -- hashExport(m.PkgPath, export) -- exportHasher.Sum(pkg.deepExportHash[:0]) +- +- // Compute a recursive hash to account for the export data of +- // this package and each dependency referenced by it. +- // Also, populate exportDeps. +- hash := sha256.New() +- fmt.Fprintf(hash, "%s %d\n", m.PkgPath, len(export)) +- hash.Write(export) +- paths, err := readShallowManifest(export) +- if err != nil { +- log.Fatalf("internal error: bad export data: %v", err) +- } +- for _, path := range paths { +- dep, ok := an.allDeps[PackagePath(path)] +- if !ok { +- log.Fatalf("%s: missing dependency: %q", an, path) +- } +- fmt.Fprintf(hash, "%s %s\n", dep.m.PkgPath, dep.summary.DeepExportHash) +- an.exportDeps[PackagePath(path)] = dep +- } +- an.exportDeps[m.PkgPath] = an // self +- hash.Sum(pkg.deepExportHash[:0]) - - return pkg -} - +-// readShallowManifest returns the manifest of packages referenced by +-// a shallow export data file for a package (excluding the package itself). +-// TODO(adonovan): add a test. +-func readShallowManifest(export []byte) ([]PackagePath, error) { +- const selfPath = "" // dummy path +- var paths []PackagePath +- getPackages := func(items []gcimporter.GetPackagesItem) error { +- paths = []PackagePath{} // non-nil +- for _, item := range items { +- if item.Path != selfPath { +- paths = append(paths, PackagePath(item.Path)) +- } +- } +- return errors.New("stop") // terminate importer +- } +- _, err := gcimporter.IImportShallow(token.NewFileSet(), getPackages, export, selfPath, bug.Reportf) +- if paths == nil { +- if err != nil { +- return nil, err // failed before getPackages callback +- } +- return nil, bug.Errorf("internal error: IImportShallow did not call getPackages") +- } +- return paths, nil // success +-} +- -// analysisPackage contains information about a package, including -// syntax trees, used transiently during its type-checking and analysis. -type analysisPackage struct { @@ -12879,8 +14242,8 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - once sync.Once - a *analysis.Analyzer - pkg *analysisPackage -- hdeps []*action // horizontal dependencies -- vdeps map[PackageID]*analyzeSummary // vertical dependencies +- hdeps []*action // horizontal dependencies +- vdeps map[PackageID]*analysisNode // vertical dependencies - - // results of action.exec(): - result interface{} // result of Run function, of type a.ResultType @@ -12893,6 +14256,7 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal -} - -// execActions executes a set of action graph nodes in parallel. +-// Postcondition: each action.summary is set, even in case of error. -func execActions(actions []*action) { - var wg sync.WaitGroup - for _, act := range actions { @@ -12913,6 +14277,9 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - } - } - }) +- if act.summary == nil { +- panic("nil action.summary (#60551)") +- } - }() - } - wg.Wait() @@ -12934,9 +14301,9 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - // we return the dependencies' error' unadorned. - if hasFacts { - // TODO(adonovan): use deterministic order. -- for _, res := range act.vdeps { -- if vdep := res.Actions[analyzer.Name]; vdep.Err != "" { -- return nil, nil, errors.New(vdep.Err) +- for _, vdep := range act.vdeps { +- if summ := vdep.summary.Actions[analyzer.Name]; summ.Err != "" { +- return nil, nil, errors.New(summ.Err) - } - } - } @@ -12958,7 +14325,7 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - } - - // Gather analysis Result values from horizontal dependencies. -- var inputs = make(map[*analysis.Analyzer]interface{}) +- inputs := make(map[*analysis.Analyzer]interface{}) - for _, dep := range act.hdeps { - inputs[dep.a] = dep.result - } @@ -12967,26 +14334,28 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - // efficient to fork and tailor it to our precise needs. - // - // We've already sharded the fact encoding by action -- // so that it can be done in parallel (hoisting the -- // ImportMap call so that we build the map once per package). +- // so that it can be done in parallel. - // We could eliminate locking. - // We could also dovetail more closely with the export data - // decoder to obtain a more compact representation of - // packages and objects (e.g. its internal IDs, instead - // of PkgPaths and objectpaths.) +- // More importantly, we should avoid re-export of +- // facts that related to objects that are discarded +- // by "deep" export data. Better still, use a "shallow" approach. - -- // Read and decode analysis facts for each imported package. -- factset, err := pkg.factsDecoder.Decode(func(imp *types.Package) ([]byte, error) { +- // Read and decode analysis facts for each direct import. +- factset, err := pkg.factsDecoder.Decode(true, func(pkgPath string) ([]byte, error) { - if !hasFacts { - return nil, nil // analyzer doesn't use facts, so no vdeps - } - - // Package.Imports() may contain a fake "C" package. Ignore it. -- if imp.Path() == "C" { +- if pkgPath == "C" { - return nil, nil - } - -- id, ok := pkg.m.DepsByPkgPath[PackagePath(imp.Path())] +- id, ok := pkg.m.DepsByPkgPath[PackagePath(pkgPath)] - if !ok { - // This may mean imp was synthesized by the type - // checker because it failed to import it for any reason @@ -13002,11 +14371,12 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - return nil, nil - } - -- vdep, ok := act.vdeps[id] -- if !ok { +- vdep := act.vdeps[id] +- if vdep == nil { - return nil, bug.Errorf("internal error in %s: missing vdep for id=%s", pkg.types.Path(), id) - } -- return vdep.Actions[analyzer.Name].Facts, nil +- +- return vdep.summary.Actions[analyzer.Name].Facts, nil - }) - if err != nil { - return nil, nil, fmt.Errorf("internal error decoding analysis facts: %w", err) @@ -13047,14 +14417,7 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - TypeErrors: pkg.typeErrors, - ResultOf: inputs, - Report: func(d analysis.Diagnostic) { -- // Prefix the diagnostic category with the analyzer's name. -- if d.Category == "" { -- d.Category = analyzer.Name -- } else { -- d.Category = analyzer.Name + "." + d.Category -- } -- -- diagnostic, err := toGobDiagnostic(posToLocation, d) +- diagnostic, err := toGobDiagnostic(posToLocation, analyzer, d) - if err != nil { - bug.Reportf("internal error converting diagnostic from analyzer %q: %v", analyzer.Name, err) - return @@ -13073,6 +14436,7 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - // (Use an anonymous function to limit the recover scope.) - var result interface{} - func() { +- start := time.Now() - defer func() { - if r := recover(); r != nil { - // An Analyzer panicked, likely due to a bug. @@ -13095,7 +14459,13 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - err = fmt.Errorf("analysis %s for package %s panicked: %v", analyzer.Name, pass.Pkg.Path(), r) - } - } +- +- // Accumulate running time for each checker. +- analyzerRunTimesMu.Lock() +- analyzerRunTimes[analyzer] += time.Since(start) +- analyzerRunTimesMu.Unlock() - }() +- - result, err = pass.Analyzer.Run(pass) - }() - if err != nil { @@ -13117,7 +14487,7 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - panic(fmt.Sprintf("%v: Pass.ExportPackageFact(%T) called after Run", act, fact)) - } - -- factsdata := factset.Encode() +- factsdata := factset.Encode(true) - return result, &actionSummary{ - Diagnostics: diagnostics, - Facts: factsdata, @@ -13125,6 +14495,32 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - }, nil -} - +-var ( +- analyzerRunTimesMu sync.Mutex +- analyzerRunTimes = make(map[*analysis.Analyzer]time.Duration) +-) +- +-type LabelDuration struct { +- Label string +- Duration time.Duration +-} +- +-// AnalyzerTimes returns the accumulated time spent in each Analyzer's +-// Run function since process start, in descending order. +-func AnalyzerRunTimes() []LabelDuration { +- analyzerRunTimesMu.Lock() +- defer analyzerRunTimesMu.Unlock() +- +- slice := make([]LabelDuration, 0, len(analyzerRunTimes)) +- for a, t := range analyzerRunTimes { +- slice = append(slice, LabelDuration{Label: a.Name, Duration: t}) +- } +- sort.Slice(slice, func(i, j int) bool { +- return slice[i].Duration > slice[j].Duration +- }) +- return slice +-} +- -// requiredAnalyzers returns the transitive closure of required analyzers in preorder. -func requiredAnalyzers(analyzers []*analysis.Analyzer) []*analysis.Analyzer { - var result []*analysis.Analyzer @@ -13143,22 +14539,13 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - return result -} - --func mustEncode(x interface{}) []byte { -- var buf bytes.Buffer -- if err := gob.NewEncoder(&buf).Encode(x); err != nil { -- log.Fatalf("internal error encoding %T: %v", x, err) -- } -- return buf.Bytes() --} -- --func mustDecode(data []byte, ptr interface{}) { -- if err := gob.NewDecoder(bytes.NewReader(data)).Decode(ptr); err != nil { -- log.Fatalf("internal error decoding %T: %v", ptr, err) -- } --} +-var analyzeSummaryCodec = frob.CodecFor[*analyzeSummary]() - -// -- data types for serialization of analysis.Diagnostic and source.Diagnostic -- - +-// (The name says gob but we use frob.) +-var diagnosticsCodec = frob.CodecFor[[]gobDiagnostic]() +- -type gobDiagnostic struct { - Location protocol.Location - Severity protocol.DiagnosticSeverity @@ -13196,7 +14583,7 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - -// toGobDiagnostic converts an analysis.Diagnosic to a serializable gobDiagnostic, -// which requires expanding token.Pos positions into protocol.Location form. --func toGobDiagnostic(posToLocation func(start, end token.Pos) (protocol.Location, error), diag analysis.Diagnostic) (gobDiagnostic, error) { +-func toGobDiagnostic(posToLocation func(start, end token.Pos) (protocol.Location, error), a *analysis.Analyzer, diag analysis.Diagnostic) (gobDiagnostic, error) { - var fixes []gobSuggestedFix - for _, fix := range diag.SuggestedFixes { - var gobEdits []gobTextEdit @@ -13233,23 +14620,47 @@ diff -urN a/gopls/internal/lsp/cache/analysis.go b/gopls/internal/lsp/cache/anal - return gobDiagnostic{}, err - } - +- // The Code column of VSCode's Problems table renders this +- // information as "Source(Code)" where code is a link to CodeHref. +- // (The code field must be nonempty for anything to appear.) +- diagURL := effectiveURL(a, diag) +- code := "default" +- if diag.Category != "" { +- code = diag.Category +- } +- - return gobDiagnostic{ - Location: loc, -- // Severity for analysis diagnostics is dynamic, based on user -- // configuration per analyzer. -- // Code and CodeHref are unset for Analysis diagnostics, -- // TODO(rfindley): set Code fields if/when golang/go#57906 is accepted. -- Source: diag.Category, +- // Severity for analysis diagnostics is dynamic, +- // based on user configuration per analyzer. +- Code: code, +- CodeHref: diagURL, +- Source: a.Name, - Message: diag.Message, - SuggestedFixes: fixes, - Related: related, - // Analysis diagnostics do not contain tags. - }, nil -} +- +-// effectiveURL computes the effective URL of diag, +-// using the algorithm specified at Diagnostic.URL. +-func effectiveURL(a *analysis.Analyzer, diag analysis.Diagnostic) string { +- u := diag.URL +- if u == "" && diag.Category != "" { +- u = "#" + diag.Category +- } +- if base, err := urlpkg.Parse(a.URL); err == nil { +- if rel, err := urlpkg.Parse(u); err == nil { +- u = base.ResolveReference(rel).String() +- } +- } +- return u +-} diff -urN a/gopls/internal/lsp/cache/cache.go b/gopls/internal/lsp/cache/cache.go --- a/gopls/internal/lsp/cache/cache.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/cache.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,78 +0,0 @@ ++++ b/gopls/internal/lsp/cache/cache.go 1970-01-01 08:00:00 +@@ -1,80 +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. @@ -13261,8 +14672,8 @@ diff -urN a/gopls/internal/lsp/cache/cache.go b/gopls/internal/lsp/cache/cache.g - "reflect" - "strconv" - "sync/atomic" +- "time" - -- "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/gocommand" - "golang.org/x/tools/internal/memoize" @@ -13307,18 +14718,14 @@ diff -urN a/gopls/internal/lsp/cache/cache.go b/gopls/internal/lsp/cache/cache.g -// The provided optionsOverrides may be nil. -// -// TODO(rfindley): move this to session.go. --func NewSession(ctx context.Context, c *Cache, optionsOverrides func(*source.Options)) *Session { +-func NewSession(ctx context.Context, c *Cache) *Session { - index := atomic.AddInt64(&sessionIndex, 1) -- options := source.DefaultOptions().Clone() -- if optionsOverrides != nil { -- optionsOverrides(options) -- } - s := &Session{ - id: strconv.FormatInt(index, 10), - cache: c, - gocmdRunner: &gocommand.Runner{}, -- options: options, - overlayFS: newOverlayFS(c), +- parseCache: newParseCache(1 * time.Minute), // keep recently parsed files for a minute, to optimize typing CPU - } - event.Log(ctx, "New session", KeyCreateSession.Of(s)) - return s @@ -13328,10 +14735,16 @@ diff -urN a/gopls/internal/lsp/cache/cache.go b/gopls/internal/lsp/cache/cache.g - -func (c *Cache) ID() string { return c.id } -func (c *Cache) MemStats() map[reflect.Type]int { return c.store.Stats() } +- +-// FileStats returns information about the set of files stored in the cache. +-// It is intended for debugging only. +-func (c *Cache) FileStats() (files, largest, errs int) { +- return c.fileStats() +-} diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.go --- a/gopls/internal/lsp/cache/check.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/check.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1227 +0,0 @@ ++++ b/gopls/internal/lsp/cache/check.go 1970-01-01 08:00:00 +@@ -1,1863 +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. @@ -13343,56 +14756,72 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - "crypto/sha256" - "fmt" - "go/ast" +- "go/parser" - "go/token" - "go/types" -- "log" - "regexp" +- "runtime" - "sort" - "strings" - "sync" +- "sync/atomic" - - "golang.org/x/mod/module" - "golang.org/x/sync/errgroup" - "golang.org/x/tools/go/ast/astutil" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/filecache" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/lsp/source/methodsets" -- "golang.org/x/tools/gopls/internal/lsp/source/xrefs" +- "golang.org/x/tools/gopls/internal/lsp/source/typerefs" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/event/tag" - "golang.org/x/tools/internal/gcimporter" -- "golang.org/x/tools/internal/memoize" - "golang.org/x/tools/internal/packagesinternal" +- "golang.org/x/tools/internal/tokeninternal" - "golang.org/x/tools/internal/typeparams" - "golang.org/x/tools/internal/typesinternal" -) - +-// Various optimizations that should not affect correctness. +-const ( +- preserveImportGraph = true // hold on to the import graph for open packages +-) +- +-type unit = struct{} +- -// A typeCheckBatch holds data for a logical type-checking operation, which may -// type-check many unrelated packages. -// -// It shares state such as parsed files and imports, to optimize type-checking -// for packages with overlapping dependency graphs. -type typeCheckBatch struct { -- meta *metadataGraph -- -- parsedFiles map[span.URI]*source.ParsedGoFile // parsed files necessary for type-checking -- fset *token.FileSet // FileSet describing all parsed files +- activePackageCache interface { +- getActivePackage(id PackageID) *Package +- setActivePackage(id PackageID, pkg *Package) +- } +- syntaxIndex map[PackageID]int // requested ID -> index in ids +- pre preTypeCheck +- post postTypeCheck +- handles map[PackageID]*packageHandle +- parseCache *parseCache +- fset *token.FileSet // describes all parsed or imported files +- cpulimit chan unit // concurrency limiter for CPU-bound operations - -- // Promises holds promises to either read export data for the package, or -- // parse and type-check its syntax. -- // -- // The return value of these promises is not used: after promises are -- // awaited, they must write an entry into the imports map. -- promises map[PackageID]*memoize.Promise +- mu sync.Mutex +- syntaxPackages map[PackageID]*futurePackage // results of processing a requested package; may hold (nil, nil) +- importPackages map[PackageID]*futurePackage // package results to use for importing +-} - -- mu sync.Mutex -- needFiles map[span.URI]source.FileHandle // de-duplicated file handles required for type-checking -- imports map[PackageID]pkgOrErr // types.Packages to use for importing -- exportData map[PackageID][]byte -- packages map[PackageID]*Package +-// A futurePackage is a future result of type checking or importing a package, +-// to be cached in a map. +-// +-// The goroutine that creates the futurePackage is responsible for evaluating +-// its value, and closing the done channel. +-type futurePackage struct { +- done chan unit +- v pkgOrErr -} - -type pkgOrErr struct { @@ -13410,262 +14839,499 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g -// indicates context cancellation or otherwise significant failure to perform -// the type-checking operation. -func (s *snapshot) TypeCheck(ctx context.Context, ids ...PackageID) ([]source.Package, error) { -- // Check for existing active packages. -- // -- // Since gopls can't depend on package identity, any instance of the -- // requested package must be ok to return. -- // -- // This is an optimization to avoid redundant type-checking: following -- // changes to an open package many LSP clients send several successive -- // requests for package information for the modified package (semantic -- // tokens, code lens, inlay hints, etc.) - pkgs := make([]source.Package, len(ids)) -- needSyntax := make(map[PackageID]bool) +- +- var ( +- needIDs []PackageID // ids to type-check +- indexes []int // original index of requested ids +- ) +- +- // Check for existing active packages, as any package will do. +- // +- // This is also done inside forEachPackage, but doing it here avoids +- // unnecessary set up for type checking (e.g. assembling the package handle +- // graph). - for i, id := range ids { - if pkg := s.getActivePackage(id); pkg != nil { - pkgs[i] = pkg - } else { -- needSyntax[id] = true +- needIDs = append(needIDs, id) +- indexes = append(indexes, i) - } - } - -- if len(needSyntax) == 0 { -- return pkgs, nil +- post := func(i int, pkg *Package) { +- pkgs[indexes[i]] = pkg - } +- return pkgs, s.forEachPackage(ctx, needIDs, nil, post) +-} - -- // Build up shared state for efficient type-checking. -- b := &typeCheckBatch{ -- parsedFiles: make(map[span.URI]*source.ParsedGoFile), -- // fset is built during the parsing pass. -- needFiles: make(map[span.URI]source.FileHandle), +-// getImportGraph returns a shared import graph use for this snapshot, or nil. +-// +-// This is purely an optimization: holding on to more imports allows trading +-// memory for CPU and latency. Currently, getImportGraph returns an import +-// graph containing all packages imported by open packages, since these are +-// highly likely to be needed when packages change. +-// +-// Furthermore, since we memoize active packages, including their imports in +-// the shared import graph means we don't run the risk of pinning duplicate +-// copies of common imports, if active packages are computed in separate type +-// checking batches. +-func (s *snapshot) getImportGraph(ctx context.Context) *importGraph { +- if !preserveImportGraph { +- return nil +- } +- s.mu.Lock() +- +- // Evaluate the shared import graph for the snapshot. There are three major +- // codepaths here: +- // +- // 1. importGraphDone == nil, importGraph == nil: it is this goroutine's +- // responsibility to type-check the shared import graph. +- // 2. importGraphDone == nil, importGraph != nil: it is this goroutine's +- // responsibility to resolve the import graph, which may result in +- // type-checking only if the existing importGraph (carried over from the +- // preceding snapshot) is invalid. +- // 3. importGraphDone != nil: some other goroutine is doing (1) or (2), wait +- // for the work to be done. +- done := s.importGraphDone +- if done == nil { +- done = make(chan unit) +- s.importGraphDone = done +- release := s.Acquire() // must acquire to use the snapshot asynchronously +- go func() { +- defer release() +- importGraph, err := s.resolveImportGraph() // may be nil +- if err != nil { +- if ctx.Err() == nil { +- event.Error(ctx, "computing the shared import graph", err) +- } +- importGraph = nil +- } +- s.mu.Lock() +- s.importGraph = importGraph +- s.mu.Unlock() +- close(done) +- }() +- } +- s.mu.Unlock() - -- promises: make(map[PackageID]*memoize.Promise), -- imports: make(map[PackageID]pkgOrErr), -- exportData: make(map[PackageID][]byte), -- packages: make(map[PackageID]*Package), +- select { +- case <-done: +- return s.importGraph +- case <-ctx.Done(): +- return nil - } +-} +- +-// resolveImportGraph evaluates the shared import graph to use for +-// type-checking in this snapshot. This may involve re-using the import graph +-// of the previous snapshot (stored in s.importGraph), or computing a fresh +-// import graph. +-// +-// resolveImportGraph should only be called from getImportGraph. +-func (s *snapshot) resolveImportGraph() (*importGraph, error) { +- ctx := s.backgroundCtx +- ctx, done := event.Start(event.Detach(ctx), "cache.resolveImportGraph") +- defer done() - -- // Capture metadata once to ensure a consistent view. - s.mu.Lock() -- b.meta = s.meta +- lastImportGraph := s.importGraph - s.mu.Unlock() - -- // -- Step 1: assemble the promises graph -- +- openPackages := make(map[PackageID]bool) +- for _, fh := range s.overlays() { +- meta, err := s.MetadataForFile(ctx, fh.URI()) +- if err != nil { +- return nil, err +- } +- source.RemoveIntermediateTestVariants(&meta) +- for _, m := range meta { +- openPackages[m.ID] = true +- } +- } - -- var ( -- needExportData = make(map[PackageID]packageHandleKey) -- packageHandles = make(map[PackageID]*packageHandle) -- ) +- var openPackageIDs []source.PackageID +- for id := range openPackages { +- openPackageIDs = append(openPackageIDs, id) +- } - -- // collectPromises collects promises to load packages from export data or -- // type-check. -- var collectPromises func(PackageID) error -- collectPromises = func(id PackageID) error { -- if _, ok := b.promises[id]; ok { -- return nil -- } -- b.promises[id] = nil // break cycles +- handles, err := s.getPackageHandles(ctx, openPackageIDs) +- if err != nil { +- return nil, err +- } - -- m := b.meta.metadata[id] -- if m == nil { -- return bug.Errorf("missing metadata for %v", id) +- // Subtlety: we erase the upward cone of open packages from the shared import +- // graph, to increase reusability. +- // +- // This is easiest to understand via an example: suppose A imports B, and B +- // imports C. Now suppose A and B are open. If we preserve the entire set of +- // shared deps by open packages, deps will be {B, C}. But this means that any +- // change to the open package B will invalidate the shared import graph, +- // meaning we will experience no benefit from sharing when B is edited. +- // Consider that this will be a common scenario, when A is foo_test and B is +- // foo. Better to just preserve the shared import C. +- // +- // With precise pruning, we may want to truncate this search based on +- // reachability. +- // +- // TODO(rfindley): this logic could use a unit test. +- volatileDeps := make(map[PackageID]bool) +- var isVolatile func(*packageHandle) bool +- isVolatile = func(ph *packageHandle) (volatile bool) { +- if v, ok := volatileDeps[ph.m.ID]; ok { +- return v - } -- for _, id := range m.DepsByPkgPath { -- if err := collectPromises(id); err != nil { -- return err +- defer func() { +- volatileDeps[ph.m.ID] = volatile +- }() +- if openPackages[ph.m.ID] { +- return true +- } +- for _, dep := range ph.m.DepsByPkgPath { +- if isVolatile(handles[dep]) { +- return true - } - } +- return false +- } +- for _, dep := range handles { +- isVolatile(dep) +- } +- for id, volatile := range volatileDeps { +- if volatile { +- delete(handles, id) +- } +- } - -- // Note that we can't reuse active packages here, as they will have the -- // wrong FileSet. Any active packages that exist as dependencies of other -- // packages will need to be loaded from export data. -- ph, err := s.buildPackageHandle(ctx, id) -- if err != nil { -- return err +- // We reuse the last import graph if and only if none of the dependencies +- // have changed. Doing better would involve analyzing dependencies to find +- // subgraphs that are still valid. Not worth it, especially when in the +- // common case nothing has changed. +- unchanged := lastImportGraph != nil && len(handles) == len(lastImportGraph.depKeys) +- var ids []PackageID +- depKeys := make(map[PackageID]source.Hash) +- for id, ph := range handles { +- ids = append(ids, id) +- depKeys[id] = ph.key +- if unchanged { +- prevKey, ok := lastImportGraph.depKeys[id] +- unchanged = ok && prevKey == ph.key - } -- packageHandles[id] = ph +- } - -- if needSyntax[id] { -- // We will need to parse and type-check this package. -- // -- // We may also need to parse and type-check if export data is missing, -- // but that is handled after fetching export data below. -- b.addNeededFiles(ph) -- } else if id != "unsafe" { // we can't load export data for unsafe -- needExportData[id] = ph.key -- } -- -- debugName := fmt.Sprintf("check(%s)", id) -- b.promises[id] = memoize.NewPromise(debugName, func(ctx context.Context, _ interface{}) interface{} { -- var res pkgOrErr -- if err := b.awaitPredecessors(ctx, ph.m); err != nil { -- res.err = err -- } else { -- b.mu.Lock() -- data, ok := b.exportData[id] -- b.mu.Unlock() +- if unchanged { +- return lastImportGraph, nil +- } - -- if ok { -- // We need export data, and have it. -- res.pkg, res.err = b.importPackage(ctx, m, data) -- } else if !needSyntax[id] { -- // We need only a types.Package, but don't have export data. -- // Type-check as fast as possible (skipping function bodies). -- res.pkg, res.err = b.checkPackageForImport(ctx, ph) -- } else { -- // We need a syntax package. -- var pkg *Package -- pkg, res.err = b.checkPackage(ctx, ph) -- if res.err == nil { -- res.pkg = pkg.pkg.types -- b.mu.Lock() -- b.packages[id] = pkg -- b.mu.Unlock() -- } -- } -- } +- b, err := s.forEachPackageInternal(ctx, nil, ids, nil, nil, nil, handles) +- if err != nil { +- return nil, err +- } - -- b.mu.Lock() -- b.imports[m.ID] = res -- b.mu.Unlock() -- return nil -- }) -- return nil +- next := &importGraph{ +- fset: b.fset, +- depKeys: depKeys, +- imports: make(map[PackageID]pkgOrErr), - } -- for id := range needSyntax { -- collectPromises(id) +- for id, fut := range b.importPackages { +- if fut.v.pkg == nil && fut.v.err == nil { +- panic(fmt.Sprintf("internal error: import node %s is not evaluated", id)) +- } +- next.imports[id] = fut.v - } +- return next, nil +-} - -- // -- Step 2: collect export data -- -- // -- // This must be done before parsing in order to determine which files must be -- // parsed. -- { -- var g errgroup.Group -- for id, key := range needExportData { -- id := id -- key := key -- g.Go(func() error { -- data, err := filecache.Get(exportDataKind, key) -- if err != nil { -- if err == filecache.ErrNotFound { -- ph := packageHandles[id] -- b.addNeededFiles(ph) // we will need to parse and type check -- return nil // ok: we will type check later -- } -- return err -- } -- b.mu.Lock() -- b.exportData[id] = data -- b.mu.Unlock() -- return nil -- }) -- } -- if err := g.Wait(); err != nil { -- return pkgs, err +-// An importGraph holds selected results of a type-checking pass, to be re-used +-// by subsequent snapshots. +-type importGraph struct { +- fset *token.FileSet // fileset used for type checking imports +- depKeys map[PackageID]source.Hash // hash of direct dependencies for this graph +- imports map[PackageID]pkgOrErr // results of type checking +-} +- +-// Package visiting functions used by forEachPackage; see the documentation of +-// forEachPackage for details. +-type ( +- preTypeCheck = func(int, *packageHandle) bool // false => don't type check +- postTypeCheck = func(int, *Package) +-) +- +-// forEachPackage does a pre- and post- order traversal of the packages +-// specified by ids using the provided pre and post functions. +-// +-// The pre func is optional. If set, pre is evaluated after the package +-// handle has been constructed, but before type-checking. If pre returns false, +-// type-checking is skipped for this package handle. +-// +-// post is called with a syntax package after type-checking completes +-// successfully. It is only called if pre returned true. +-// +-// Both pre and post may be called concurrently. +-func (s *snapshot) forEachPackage(ctx context.Context, ids []PackageID, pre preTypeCheck, post postTypeCheck) error { +- ctx, done := event.Start(ctx, "cache.forEachPackage", tag.PackageCount.Of(len(ids))) +- defer done() +- +- if len(ids) == 0 { +- return nil // short cut: many call sites do not handle empty ids +- } +- +- handles, err := s.getPackageHandles(ctx, ids) +- if err != nil { +- return err +- } +- +- impGraph := s.getImportGraph(ctx) +- _, err = s.forEachPackageInternal(ctx, impGraph, nil, ids, pre, post, handles) +- return err +-} +- +-// forEachPackageInternal is used by both forEachPackage and loadImportGraph to +-// type-check a graph of packages. +-// +-// If a non-nil importGraph is provided, imports in this graph will be reused. +-func (s *snapshot) forEachPackageInternal(ctx context.Context, importGraph *importGraph, importIDs, syntaxIDs []PackageID, pre preTypeCheck, post postTypeCheck, handles map[PackageID]*packageHandle) (*typeCheckBatch, error) { +- b := &typeCheckBatch{ +- activePackageCache: s, +- pre: pre, +- post: post, +- handles: handles, +- parseCache: s.view.parseCache, +- fset: fileSetWithBase(reservedForParsing), +- syntaxIndex: make(map[PackageID]int), +- cpulimit: make(chan unit, runtime.GOMAXPROCS(0)), +- syntaxPackages: make(map[PackageID]*futurePackage), +- importPackages: make(map[PackageID]*futurePackage), +- } +- +- if importGraph != nil { +- // Clone the file set every time, to ensure we do not leak files. +- b.fset = tokeninternal.CloneFileSet(importGraph.fset) +- // Pre-populate future cache with 'done' futures. +- done := make(chan unit) +- close(done) +- for id, res := range importGraph.imports { +- b.importPackages[id] = &futurePackage{done, res} - } +- } else { +- b.fset = fileSetWithBase(reservedForParsing) +- } +- +- for i, id := range syntaxIDs { +- b.syntaxIndex[id] = i - } - -- // -- Step 3: parse files required for type checking. -- +- // Start a single goroutine for each requested package. - // -- // Parse all necessary files in parallel. Unfortunately we can't start -- // parsing each package's file as soon as we discover that it is a syntax -- // package, because the parseCache cannot add files to an existing FileSet. -- { -- var fhs []source.FileHandle -- for _, fh := range b.needFiles { -- fhs = append(fhs, fh) +- // Other packages are reached recursively, and will not be evaluated if they +- // are not needed. +- var g errgroup.Group +- for _, id := range importIDs { +- id := id +- g.Go(func() error { +- _, err := b.getImportPackage(ctx, id) +- return err +- }) +- } +- for i, id := range syntaxIDs { +- i := i +- id := id +- g.Go(func() error { +- _, err := b.handleSyntaxPackage(ctx, i, id) +- return err +- }) +- } +- return b, g.Wait() +-} +- +-// TODO(rfindley): re-order the declarations below to read better from top-to-bottom. +- +-// getImportPackage returns the *types.Package to use for importing the +-// package referenced by id. +-// +-// This may be the package produced by type-checking syntax (as in the case +-// where id is in the set of requested IDs), a package loaded from export data, +-// or a package type-checked for import only. +-func (b *typeCheckBatch) getImportPackage(ctx context.Context, id PackageID) (pkg *types.Package, err error) { +- b.mu.Lock() +- f, ok := b.importPackages[id] +- if ok { +- b.mu.Unlock() +- +- select { +- case <-ctx.Done(): +- return nil, ctx.Err() +- case <-f.done: +- return f.v.pkg, f.v.err - } -- pgfs, fset, err := s.parseCache.parseFiles(ctx, source.ParseFull, fhs...) +- } +- +- f = &futurePackage{done: make(chan unit)} +- b.importPackages[id] = f +- b.mu.Unlock() +- +- defer func() { +- f.v = pkgOrErr{pkg, err} +- close(f.done) +- }() +- +- if index, ok := b.syntaxIndex[id]; ok { +- pkg, err := b.handleSyntaxPackage(ctx, index, id) - if err != nil { -- return pkgs, err +- return nil, err - } -- for _, pgf := range pgfs { -- b.parsedFiles[pgf.URI] = pgf +- if pkg != nil { +- return pkg, nil - } -- b.fset = fset +- // type-checking was short-circuited by the pre- func. - } - -- // -- Step 4: await type-checking. -- -- // -- // Start a single goroutine for each promise. -- { -- var g errgroup.Group -- // TODO(rfindley): find a good way to limit concurrency of type-checking, -- // which is CPU bound at this point. -- // -- // (calling g.SetLimit here is mostly ineffective, as promises are -- // recursively concurrent.) -- for _, promise := range b.promises { -- promise := promise -- g.Go(func() error { -- _, err := promise.Get(ctx, nil) -- return err -- }) -- } -- if err := g.Wait(); err != nil { -- return pkgs, err -- } +- // unsafe cannot be imported or type-checked. +- if id == "unsafe" { +- return types.Unsafe, nil - } - -- // Fill in the gaps of the results slice. -- var firstErr error -- for i, id := range ids { -- if pkgs[i] != nil { -- continue -- } -- if err := b.imports[id].err; err != nil { -- if firstErr == nil { -- firstErr = err -- } -- continue -- } -- pkg := b.packages[id] -- if pkg == nil { -- panic("nil package") -- } -- if alt := s.memoizeActivePackage(id, pkg); alt != nil && alt != pkg { -- // pkg is an open package, but we've lost a race and an existing package -- // has already been memoized. -- pkg = alt -- } -- pkgs[i] = pkg +- ph := b.handles[id] +- +- // Do a second check for "unsafe" defensively, due to golang/go#60890. +- if ph.m.PkgPath == "unsafe" { +- bug.Reportf("encountered \"unsafe\" as %s (golang/go#60890)", id) +- return types.Unsafe, nil - } - -- return pkgs, firstErr +- data, err := filecache.Get(exportDataKind, ph.key) +- if err == filecache.ErrNotFound { +- // No cached export data: type-check as fast as possible. +- return b.checkPackageForImport(ctx, ph) +- } +- if err != nil { +- return nil, fmt.Errorf("failed to read cache data for %s: %v", ph.m.ID, err) +- } +- return b.importPackage(ctx, ph.m, data) -} - --// addNeededFiles records the files necessary for type-checking ph, for later --// parsing. --func (b *typeCheckBatch) addNeededFiles(ph *packageHandle) { +-// handleSyntaxPackage handles one package from the ids slice. +-// +-// If type checking occurred while handling the package, it returns the +-// resulting types.Package so that it may be used for importing. +-// +-// handleSyntaxPackage returns (nil, nil) if pre returned false. +-func (b *typeCheckBatch) handleSyntaxPackage(ctx context.Context, i int, id PackageID) (pkg *types.Package, err error) { - b.mu.Lock() -- defer b.mu.Unlock() +- f, ok := b.syntaxPackages[id] +- if ok { +- b.mu.Unlock() +- <-f.done +- return f.v.pkg, f.v.err +- } +- +- f = &futurePackage{done: make(chan unit)} +- b.syntaxPackages[id] = f +- b.mu.Unlock() +- defer func() { +- f.v = pkgOrErr{pkg, err} +- close(f.done) +- }() +- +- ph := b.handles[id] +- if b.pre != nil && !b.pre(i, ph) { +- return nil, nil // skip: export data only +- } +- +- // Check for existing active packages. +- // +- // Since gopls can't depend on package identity, any instance of the +- // requested package must be ok to return. +- // +- // This is an optimization to avoid redundant type-checking: following +- // changes to an open package many LSP clients send several successive +- // requests for package information for the modified package (semantic +- // tokens, code lens, inlay hints, etc.) +- if pkg := b.activePackageCache.getActivePackage(id); pkg != nil { +- b.post(i, pkg) +- return nil, nil // skip: not checked in this batch +- } - -- // Technically for export-only packages we only need compiledGoFiles, but -- // these slices are usually redundant. -- for _, fh := range ph.inputs.goFiles { -- b.needFiles[fh.URI()] = fh +- if err := b.awaitPredecessors(ctx, ph.m); err != nil { +- // One failed precessesor should not fail the entire type checking +- // operation. Errors related to imports will be reported as type checking +- // diagnostics. +- if ctx.Err() != nil { +- return nil, ctx.Err() +- } - } -- for _, fh := range ph.inputs.compiledGoFiles { -- b.needFiles[fh.URI()] = fh +- +- // Wait to acquire a CPU token. +- // +- // Note: it is important to acquire this token only after awaiting +- // predecessors, to avoid starvation. +- select { +- case <-ctx.Done(): +- return nil, ctx.Err() +- case b.cpulimit <- unit{}: +- defer func() { +- <-b.cpulimit // release CPU token +- }() +- } +- +- // We need a syntax package. +- syntaxPkg, err := b.checkPackage(ctx, ph) +- if err != nil { +- return nil, err - } +- b.activePackageCache.setActivePackage(id, syntaxPkg) +- b.post(i, syntaxPkg) +- +- return syntaxPkg.pkg.types, nil -} - -// importPackage loads the given package from its export data in p.exportData -// (which must already be populated). -func (b *typeCheckBatch) importPackage(ctx context.Context, m *source.Metadata, data []byte) (*types.Package, error) { -- impMap, errMap := b.importMap(m.ID) -- // Any failure to populate an import will cause confusing errors from -- // IImportShallow below. -- for path, err := range errMap { -- return nil, fmt.Errorf("error importing %q for %q: %v", path, m.ID, err) +- ctx, done := event.Start(ctx, "cache.typeCheckBatch.importPackage", tag.Package.Of(string(m.ID))) +- defer done() +- +- impMap := b.importMap(m.ID) +- +- thisPackage := types.NewPackage(string(m.PkgPath), string(m.Name)) +- getPackages := func(items []gcimporter.GetPackagesItem) error { +- for i, item := range items { +- var id PackageID +- var pkg *types.Package +- if item.Path == string(m.PkgPath) { +- id = m.ID +- pkg = thisPackage +- } else { +- id = impMap[item.Path] +- var err error +- pkg, err = b.getImportPackage(ctx, id) +- if err != nil { +- return err +- } +- } +- items[i].Pkg = pkg +- +- // debugging issue #60904 +- if pkg.Name() != item.Name { +- return bug.Errorf("internal error: package name is %q, want %q (id=%q, path=%q) (see issue #60904)", +- pkg.Name(), item.Name, id, item.Path) +- } +- } +- return nil +- } +- +- // Importing is potentially expensive, and might not encounter cancellations +- // via dependencies (e.g. if they have already been evaluated). +- if ctx.Err() != nil { +- return nil, ctx.Err() - } - -- // TODO(rfindley): collect "deep" hashes here using the provided +- // TODO(rfindley): collect "deep" hashes here using the getPackages - // callback, for precise pruning. -- imported, err := gcimporter.IImportShallow(b.fset, impMap, data, string(m.PkgPath), func(*types.Package, string) {}) +- imported, err := gcimporter.IImportShallow(b.fset, getPackages, data, string(m.PkgPath), bug.Reportf) - if err != nil { -- return nil, bug.Errorf("invalid export data for %q: %v", m.ID, err) +- return nil, fmt.Errorf("import failed for %q: %v", m.ID, err) - } - return imported, nil -} @@ -13673,26 +15339,55 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g -// checkPackageForImport type checks, but skips function bodies and does not -// record syntax information. -func (b *typeCheckBatch) checkPackageForImport(ctx context.Context, ph *packageHandle) (*types.Package, error) { -- if ph.m.ID == "unsafe" { -- return types.Unsafe, nil -- } -- impMap, errMap := b.importMap(ph.inputs.id) +- ctx, done := event.Start(ctx, "cache.typeCheckBatch.checkPackageForImport", tag.Package.Of(string(ph.m.ID))) +- defer done() +- - onError := func(e error) { - // Ignore errors for exporting. - } -- cfg := b.typesConfig(ph.inputs, onError, impMap, errMap) -- var files []*ast.File -- for _, fh := range ph.inputs.compiledGoFiles { -- pgf := b.parsedFiles[fh.URI()] -- if pgf == nil { -- return nil, fmt.Errorf("compiled go file %q failed to parse", fh.URI().Filename()) +- cfg := b.typesConfig(ctx, ph.localInputs, onError) +- cfg.IgnoreFuncBodies = true +- +- // Parse the compiled go files, bypassing the parse cache as packages checked +- // for import are unlikely to get cache hits. Additionally, we can optimize +- // parsing slightly by not passing parser.ParseComments. +- pgfs := make([]*source.ParsedGoFile, len(ph.localInputs.compiledGoFiles)) +- { +- var group errgroup.Group +- // Set an arbitrary concurrency limit; we want some parallelism but don't +- // need GOMAXPROCS, as there is already a lot of concurrency among calls to +- // checkPackageForImport. +- // +- // TODO(rfindley): is there a better way to limit parallelism here? We could +- // have a global limit on the type-check batch, but would have to be very +- // careful to avoid starvation. +- group.SetLimit(4) +- for i, fh := range ph.localInputs.compiledGoFiles { +- i, fh := i, fh +- group.Go(func() error { +- pgf, err := parseGoImpl(ctx, b.fset, fh, parser.SkipObjectResolution, false) +- pgfs[i] = pgf +- return err +- }) +- } +- if err := group.Wait(); err != nil { +- return nil, err // cancelled, or catastrophic error (e.g. missing file) - } -- files = append(files, pgf.File) - } -- cfg.IgnoreFuncBodies = true -- pkg := types.NewPackage(string(ph.inputs.pkgPath), string(ph.inputs.name)) +- pkg := types.NewPackage(string(ph.localInputs.pkgPath), string(ph.localInputs.name)) - check := types.NewChecker(cfg, b.fset, pkg, nil) - +- files := make([]*ast.File, len(pgfs)) +- for i, pgf := range pgfs { +- files[i] = pgf.File +- } +- +- // Type checking is expensive, and we may not have ecountered cancellations +- // via parsing (e.g. if we got nothing but cache hits for parsed files). +- if ctx.Err() != nil { +- return nil, ctx.Err() +- } +- - _ = check.Files(files) // ignore errors - - // If the context was cancelled, we may have returned a ton of transient @@ -13703,7 +15398,7 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - - // Asynchronously record export data. - go func() { -- exportData, err := gcimporter.IExportShallow(b.fset, pkg) +- exportData, err := gcimporter.IExportShallow(b.fset, pkg, bug.Reportf) - if err != nil { - bug.Reportf("exporting package %v: %v", ph.m.ID, err) - return @@ -13717,27 +15412,34 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - -// checkPackage "fully type checks" to produce a syntax package. -func (b *typeCheckBatch) checkPackage(ctx context.Context, ph *packageHandle) (*Package, error) { +- ctx, done := event.Start(ctx, "cache.typeCheckBatch.checkPackage", tag.Package.Of(string(ph.m.ID))) +- defer done() +- - // TODO(rfindley): refactor to inline typeCheckImpl here. There is no need - // for so many layers to build up the package - // (checkPackage->typeCheckImpl->doTypeCheck). -- pkg, err := typeCheckImpl(ctx, b, ph.inputs) +- pkg, err := typeCheckImpl(ctx, b, ph.localInputs) - - if err == nil { - // Write package data to disk asynchronously. - go func() { - toCache := map[string][]byte{ -- xrefsKind: pkg.xrefs, -- methodSetsKind: pkg.methodsets.Encode(), +- xrefsKind: pkg.xrefs(), +- methodSetsKind: pkg.methodsets().Encode(), - diagnosticsKind: encodeDiagnostics(pkg.diagnostics), - } - -- if ph.m.ID != "unsafe" { // unsafe cannot be exported -- exportData, err := gcimporter.IExportShallow(pkg.fset, pkg.types) +- if ph.m.PkgPath != "unsafe" { // unsafe cannot be exported +- exportData, err := gcimporter.IExportShallow(pkg.fset, pkg.types, bug.Reportf) - if err != nil { - bug.Reportf("exporting package %v: %v", ph.m.ID, err) - } else { - toCache[exportDataKind] = exportData - } +- } else if ph.m.ID != "unsafe" { +- // golang/go#60890: we should only ever see one variant of the "unsafe" +- // package. +- bug.Reportf("encountered \"unsafe\" as %s (golang/go#60890)", ph.m.ID) - } - - for kind, data := range toCache { @@ -13751,230 +15453,583 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - return &Package{ph.m, pkg}, err -} - --// awaitPredecessors awaits all promises for m.DepsByPkgPath, returning an +-// awaitPredecessors awaits all packages for m.DepsByPkgPath, returning an -// error if awaiting failed due to context cancellation or if there was an -// unrecoverable error loading export data. +-// +-// TODO(rfindley): inline, now that this is only called in one place. -func (b *typeCheckBatch) awaitPredecessors(ctx context.Context, m *source.Metadata) error { +- // await predecessors concurrently, as some of them may be non-syntax +- // packages, and therefore will not have been started by the type-checking +- // batch. +- var g errgroup.Group - for _, depID := range m.DepsByPkgPath { - depID := depID -- if p, ok := b.promises[depID]; ok { -- if _, err := p.Get(ctx, nil); err != nil { -- return err +- g.Go(func() error { +- _, err := b.getImportPackage(ctx, depID) +- return err +- }) +- } +- return g.Wait() +-} +- +-// importMap returns the map of package path -> package ID relative to the +-// specified ID. +-func (b *typeCheckBatch) importMap(id PackageID) map[string]source.PackageID { +- impMap := make(map[string]source.PackageID) +- var populateDeps func(m *source.Metadata) +- populateDeps = func(parent *source.Metadata) { +- for _, id := range parent.DepsByPkgPath { +- m := b.handles[id].m +- if _, ok := impMap[string(m.PkgPath)]; ok { +- continue - } +- impMap[string(m.PkgPath)] = m.ID +- populateDeps(m) - } - } -- return nil +- m := b.handles[id].m +- populateDeps(m) +- return impMap -} - --// importMap returns an import map for the given package ID, populated with --// type-checked packages for its dependencies. It is intended for compatibility --// with gcimporter.IImportShallow, so the first result uses the map signature --// of that API, where keys are package path strings. +-// A packageHandle holds inputs required to compute a type-checked package, +-// including inputs to type checking itself, and a key for looking up +-// precomputed data. -// --// importMap must only be used once all promises for dependencies of id have --// been awaited. +-// packageHandles may be invalid following an invalidation via snapshot.clone, +-// but the handles returned by getPackageHandles will always be valid. -// --// For any missing packages, importMap returns an entry in the resulting errMap --// reporting the error for that package. +-// packageHandles are critical for implementing "precise pruning" in gopls: +-// packageHandle.key is a hash of a precise set of inputs, such as package +-// files and "reachable" syntax, that may affect type checking. -// --// Invariant: for all recursive dependencies, either impMap[path] or --// errMap[path] is set. --func (b *typeCheckBatch) importMap(id PackageID) (impMap map[string]*types.Package, errMap map[PackagePath]error) { -- impMap = make(map[string]*types.Package) -- outerID := id -- var populateDepsOf func(m *source.Metadata) -- populateDepsOf = func(parent *source.Metadata) { -- for _, id := range parent.DepsByPkgPath { -- m := b.meta.metadata[id] -- if _, ok := impMap[string(m.PkgPath)]; ok { -- continue +-// packageHandles also keep track of state that allows gopls to compute, and +-// then quickly recompute, these keys. This state is split into two categories: +-// - local state, which depends only on the package's local files and metadata +-// - other state, which includes data derived from dependencies. +-// +-// Dividing the data in this way allows gopls to minimize invalidation when a +-// package is modified. For example, any change to a package file fully +-// invalidates the package handle. On the other hand, if that change was not +-// metadata-affecting it may be the case that packages indirectly depending on +-// the modified package are unaffected by the change. For that reason, we have +-// two types of invalidation, corresponding to the two types of data above: +-// - deletion of the handle, which occurs when the package itself changes +-// - clearing of the validated field, which marks the package as possibly +-// invalid. +-// +-// With the second type of invalidation, packageHandles are re-evaluated from the +-// bottom up. If this process encounters a packageHandle whose deps have not +-// changed (as detected by the depkeys field), then the packageHandle in +-// question must also not have changed, and we need not re-evaluate its key. +-type packageHandle struct { +- m *source.Metadata +- +- // Local data: +- +- // localInputs holds all local type-checking localInputs, excluding +- // dependencies. +- localInputs typeCheckInputs +- // localKey is a hash of localInputs. +- localKey source.Hash +- // refs is the result of syntactic dependency analysis produced by the +- // typerefs package. +- refs map[string][]typerefs.Symbol +- +- // Data derived from dependencies: +- +- // validated indicates whether the current packageHandle is known to have a +- // valid key. Invalidated package handles are stored for packages whose +- // type information may have changed. +- validated bool +- // depKeys records the key of each dependency that was used to calculate the +- // key above. If the handle becomes invalid, we must re-check that each still +- // matches. +- depKeys map[PackageID]source.Hash +- // key is the hashed key for the package. +- // +- // It includes the all bits of the transitive closure of +- // dependencies's sources. +- key source.Hash +-} +- +-// clone returns a copy of the receiver with the validated bit set to the +-// provided value. +-func (ph *packageHandle) clone(validated bool) *packageHandle { +- copy := *ph +- copy.validated = validated +- return © +-} +- +-// getPackageHandles gets package handles for all given ids and their +-// dependencies, recursively. +-func (s *snapshot) getPackageHandles(ctx context.Context, ids []PackageID) (map[PackageID]*packageHandle, error) { +- // perform a two-pass traversal. +- // +- // On the first pass, build up a bidirectional graph of handle nodes, and collect leaves. +- // Then build package handles from bottom up. +- +- s.mu.Lock() // guard s.meta and s.packages below +- b := &packageHandleBuilder{ +- s: s, +- transitiveRefs: make(map[typerefs.IndexID]*partialRefs), +- nodes: make(map[typerefs.IndexID]*handleNode), +- } +- +- var leaves []*handleNode +- var makeNode func(*handleNode, PackageID) *handleNode +- makeNode = func(from *handleNode, id PackageID) *handleNode { +- idxID := b.s.pkgIndex.IndexID(id) +- n, ok := b.nodes[idxID] +- if !ok { +- m := s.meta.metadata[id] +- if m == nil { +- panic(fmt.Sprintf("nil metadata for %q", id)) - } -- if _, ok := errMap[m.PkgPath]; ok { -- continue +- n = &handleNode{ +- m: m, +- idxID: idxID, +- unfinishedSuccs: int32(len(m.DepsByPkgPath)), - } -- b.mu.Lock() -- result, ok := b.imports[m.ID] -- b.mu.Unlock() -- if !ok { -- panic(fmt.Sprintf("import map for %q missing package data for %q", outerID, m.ID)) +- if entry, hit := b.s.packages.Get(m.ID); hit { +- n.ph = entry - } -- // We may fail to produce a package due to e.g. context cancellation -- // (handled elsewhere), or some catastrophic failure such as a package with -- // no files. -- switch { -- case result.err != nil: -- if errMap == nil { -- errMap = make(map[PackagePath]error) -- } -- errMap[m.PkgPath] = result.err -- case result.pkg != nil: -- impMap[string(m.PkgPath)] = result.pkg -- default: -- panic("invalid import for " + id) +- if n.unfinishedSuccs == 0 { +- leaves = append(leaves, n) +- } else { +- n.succs = make(map[source.PackageID]*handleNode, n.unfinishedSuccs) - } -- populateDepsOf(m) +- b.nodes[idxID] = n +- for _, depID := range m.DepsByPkgPath { +- n.succs[depID] = makeNode(n, depID) +- } +- } +- // Add edge from predecessor. +- if from != nil { +- n.preds = append(n.preds, from) - } +- return n - } -- m := b.meta.metadata[id] -- populateDepsOf(m) -- return impMap, errMap --} +- for _, id := range ids { +- makeNode(nil, id) +- } +- s.mu.Unlock() - --// packageData holds binary data (e.g. types, xrefs) extracted from a syntax --// package. --type packageData struct { -- m *source.Metadata -- data []byte --} +- g, ctx := errgroup.WithContext(ctx) +- +- // files are preloaded, so building package handles is CPU-bound. +- // +- // Note that we can't use g.SetLimit, as that could result in starvation: +- // g.Go blocks until a slot is available, and so all existing goroutines +- // could be blocked trying to enqueue a predecessor. +- limiter := make(chan unit, runtime.GOMAXPROCS(0)) +- +- var enqueue func(*handleNode) +- enqueue = func(n *handleNode) { +- g.Go(func() error { +- limiter <- unit{} +- defer func() { <-limiter }() - --// getPackageData gets package data (e.g. types, xrefs) for the requested ids, --// either loading from the file-based cache or type-checking and extracting --// data using the provided get function. --func (s *snapshot) getPackageData(ctx context.Context, kind string, ids []PackageID, get func(*syntaxPackage) []byte) ([]*packageData, error) { -- var needIDs []PackageID -- keys := make([]packageHandleKey, len(ids)) -- pkgData := make([]*packageData, len(ids)) -- var firstErr error -- // Compute package keys and query file cache. -- for i, id := range ids { -- ph, err := s.buildPackageHandle(ctx, id) -- if err != nil { -- if firstErr == nil { -- firstErr = err -- } - if ctx.Err() != nil { -- return pkgData, firstErr +- return ctx.Err() - } -- continue -- } -- keys[i] = ph.key -- data, err := filecache.Get(kind, ph.key) -- switch err { -- case nil: -- pkgData[i] = &packageData{m: ph.m, data: data} -- case filecache.ErrNotFound: -- needIDs = append(needIDs, id) -- default: -- if firstErr == nil { -- firstErr = err +- +- b.buildPackageHandle(ctx, n) +- +- for _, pred := range n.preds { +- if atomic.AddInt32(&pred.unfinishedSuccs, -1) == 0 { +- enqueue(pred) +- } - } -- if ctx.Err() != nil { -- return pkgData, firstErr +- +- return n.err +- }) +- } +- for _, leaf := range leaves { +- enqueue(leaf) +- } +- +- if err := g.Wait(); err != nil { +- return nil, err +- } +- +- // Copy handles into the result map. +- handles := make(map[PackageID]*packageHandle, len(b.nodes)) +- for _, v := range b.nodes { +- assert(v.ph != nil, "nil handle") +- handles[v.m.ID] = v.ph +- } +- +- return handles, nil +-} +- +-// A packageHandleBuilder computes a batch of packageHandles concurrently, +-// sharing computed transitive reachability sets used to compute package keys. +-type packageHandleBuilder struct { +- meta *metadataGraph +- s *snapshot +- +- // nodes are assembled synchronously. +- nodes map[typerefs.IndexID]*handleNode +- +- // transitiveRefs is incrementally evaluated as package handles are built. +- transitiveRefsMu sync.Mutex +- transitiveRefs map[typerefs.IndexID]*partialRefs // see getTransitiveRefs +-} +- +-// A handleNode represents a to-be-computed packageHandle within a graph of +-// predecessors and successors. +-// +-// It is used to implement a bottom-up construction of packageHandles. +-type handleNode struct { +- m *source.Metadata +- idxID typerefs.IndexID +- ph *packageHandle +- err error +- preds []*handleNode +- succs map[PackageID]*handleNode +- unfinishedSuccs int32 +-} +- +-// partialRefs maps names declared by a given package to their set of +-// transitive references. +-// +-// If complete is set, refs is known to be complete for the package in +-// question. Otherwise, it may only map a subset of all names declared by the +-// package. +-type partialRefs struct { +- refs map[string]*typerefs.PackageSet +- complete bool +-} +- +-// getTransitiveRefs gets or computes the set of transitively reachable +-// packages for each exported name in the package specified by id. +-// +-// The operation may fail if building a predecessor failed. If and only if this +-// occurs, the result will be nil. +-func (b *packageHandleBuilder) getTransitiveRefs(pkgID PackageID) map[string]*typerefs.PackageSet { +- b.transitiveRefsMu.Lock() +- defer b.transitiveRefsMu.Unlock() +- +- idxID := b.s.pkgIndex.IndexID(pkgID) +- trefs, ok := b.transitiveRefs[idxID] +- if !ok { +- trefs = &partialRefs{ +- refs: make(map[string]*typerefs.PackageSet), +- } +- b.transitiveRefs[idxID] = trefs +- } +- +- if !trefs.complete { +- trefs.complete = true +- ph := b.nodes[idxID].ph +- for name := range ph.refs { +- if ('A' <= name[0] && name[0] <= 'Z') || token.IsExported(name) { +- if _, ok := trefs.refs[name]; !ok { +- pkgs := b.s.pkgIndex.NewSet() +- for _, sym := range ph.refs[name] { +- pkgs.Add(sym.Package) +- otherSet := b.getOneTransitiveRefLocked(sym) +- pkgs.Union(otherSet) +- } +- trefs.refs[name] = pkgs +- } - } - } - } - -- // Type-check the packages for which we got file-cache misses. -- pkgs, err := s.TypeCheck(ctx, needIDs...) -- if err != nil { -- return nil, err +- return trefs.refs +-} +- +-// getOneTransitiveRefLocked computes the full set packages transitively +-// reachable through the given sym reference. +-// +-// It may return nil if the reference is invalid (i.e. the referenced name does +-// not exist). +-func (b *packageHandleBuilder) getOneTransitiveRefLocked(sym typerefs.Symbol) *typerefs.PackageSet { +- assert(token.IsExported(sym.Name), "expected exported symbol") +- +- trefs := b.transitiveRefs[sym.Package] +- if trefs == nil { +- trefs = &partialRefs{ +- refs: make(map[string]*typerefs.PackageSet), +- complete: false, +- } +- b.transitiveRefs[sym.Package] = trefs - } - -- pkgMap := make(map[PackageID]source.Package) -- for i, id := range needIDs { -- pkgMap[id] = pkgs[i] +- pkgs, ok := trefs.refs[sym.Name] +- if ok && pkgs == nil { +- // See below, where refs is set to nil before recursing. +- bug.Reportf("cycle detected to %q in reference graph", sym.Name) - } - -- // Fill in the gaps using data derived from type checking. -- for i, id := range ids { -- if pkgData[i] != nil { -- continue +- // Note that if (!ok && trefs.complete), the name does not exist in the +- // referenced package, and we should not write to trefs as that may introduce +- // a race. +- if !ok && !trefs.complete { +- n := b.nodes[sym.Package] +- if n == nil { +- // We should always have IndexID in our node set, because symbol references +- // should only be recorded for packages that actually exist in the import graph. +- // +- // However, it is not easy to prove this (typerefs are serialized and +- // deserialized), so make this code temporarily defensive while we are on a +- // point release. +- // +- // TODO(rfindley): in the future, we should turn this into an assertion. +- bug.Reportf("missing reference to package %s", b.s.pkgIndex.PackageID(sym.Package)) +- return nil - } -- result := pkgMap[id] -- if result == nil { -- panic(fmt.Sprintf("missing type-check result for %s", id)) +- +- // Break cycles. This is perhaps overly defensive as cycles should not +- // exist at this point: metadata cycles should have been broken at load +- // time, and intra-package reference cycles should have been contracted by +- // the typerefs algorithm. +- // +- // See the "cycle detected" bug report above. +- trefs.refs[sym.Name] = nil +- +- pkgs := b.s.pkgIndex.NewSet() +- for _, sym2 := range n.ph.refs[sym.Name] { +- pkgs.Add(sym2.Package) +- otherSet := b.getOneTransitiveRefLocked(sym2) +- pkgs.Union(otherSet) - } -- data := get(result.(*Package).pkg) -- pkgData[i] = &packageData{m: result.Metadata(), data: data} +- trefs.refs[sym.Name] = pkgs - } - -- return pkgData, firstErr +- return pkgs -} - --type packageHandleKey source.Hash -- --// A packageHandle holds package information, some of which may not be fully --// evaluated. +-// buildPackageHandle gets or builds a package handle for the given id, storing +-// its result in the snapshot.packages map. -// --// The only methods on packageHandle that are safe to call before calling await --// are Metadata and await itself. --type packageHandle struct { -- m *source.Metadata +-// buildPackageHandle must only be called from getPackageHandles. +-func (b *packageHandleBuilder) buildPackageHandle(ctx context.Context, n *handleNode) { +- var prevPH *packageHandle +- if n.ph != nil { +- // Existing package handle: if it is valid, return it. Otherwise, create a +- // copy to update. +- if n.ph.validated { +- return +- } +- prevPH = n.ph +- // Either prevPH is still valid, or we will update the key and depKeys of +- // this copy. In either case, the result will be valid. +- n.ph = prevPH.clone(true) +- } else { +- // No package handle: read and analyze the package syntax. +- inputs, err := b.s.typeCheckInputs(ctx, n.m) +- if err != nil { +- n.err = err +- return +- } +- refs, err := b.s.typerefs(ctx, n.m, inputs.compiledGoFiles) +- if err != nil { +- n.err = err +- return +- } +- n.ph = &packageHandle{ +- m: n.m, +- localInputs: inputs, +- localKey: localPackageKey(inputs), +- refs: refs, +- validated: true, +- } +- } - -- inputs typeCheckInputs +- // ph either did not exist, or was invalid. We must re-evaluate deps and key. +- if err := b.evaluatePackageHandle(prevPH, n); err != nil { +- n.err = err +- return +- } - -- // key is the hashed key for the package. +- assert(n.ph.validated, "unvalidated handle") +- +- // Ensure the result (or an equivalent) is recorded in the snapshot. +- b.s.mu.Lock() +- defer b.s.mu.Unlock() +- +- // Check that the metadata has not changed +- // (which should invalidate this handle). - // -- // It includes the all bits of the transitive closure of -- // dependencies's sources. This is more than type checking -- // really depends on: export data of direct deps should be -- // enough. (The key for analysis actions could similarly -- // hash only Facts of direct dependencies.) -- key packageHandleKey -- -- // Note: as an optimization, we could join in-flight type-checking by -- // recording a transient ref-counted promise here. -- // (This was done previously, but proved to be a premature optimization). --} -- --// buildPackageHandle returns a handle for the future results of --// type-checking the package identified by id in the given mode. --// It assumes that the given ID already has metadata available, so it does not --// attempt to reload missing or invalid metadata. The caller must reload --// metadata if needed. --func (s *snapshot) buildPackageHandle(ctx context.Context, id PackageID) (*packageHandle, error) { -- s.mu.Lock() -- entry, hit := s.packages.Get(id) -- m := s.meta.metadata[id] -- s.mu.Unlock() +- // TODO(rfindley): eventually promote this to an assert. +- // TODO(rfindley): move this to after building the package handle graph? +- if b.s.meta.metadata[n.m.ID] != n.m { +- bug.Reportf("stale metadata for %s", n.m.ID) +- } - -- if m == nil { -- return nil, fmt.Errorf("no metadata for %s", id) +- // Check the packages map again in case another goroutine got there first. +- if alt, ok := b.s.packages.Get(n.m.ID); ok && alt.validated { +- if alt.m != n.m { +- bug.Reportf("existing package handle does not match for %s", n.m.ID) +- } +- n.ph = alt +- } else { +- b.s.packages.Set(n.m.ID, n.ph, nil) +- } +-} +- +-// evaluatePackageHandle validates and/or computes the key of ph, setting key, +-// depKeys, and the validated flag on ph. +-// +-// It uses prevPH to avoid recomputing keys that can't have changed, since +-// their depKeys did not change. +-// +-// See the documentation for packageHandle for more details about packageHandle +-// state, and see the documentation for the typerefs package for more details +-// about precise reachability analysis. +-func (b *packageHandleBuilder) evaluatePackageHandle(prevPH *packageHandle, n *handleNode) error { +- // Opt: if no dep keys have changed, we need not re-evaluate the key. +- if prevPH != nil { +- depsChanged := false +- assert(len(prevPH.depKeys) == len(n.succs), "mismatching dep count") +- for id, succ := range n.succs { +- oldKey, ok := prevPH.depKeys[id] +- assert(ok, "missing dep") +- if oldKey != succ.ph.key { +- depsChanged = true +- break +- } +- } +- if !depsChanged { +- return nil // key cannot have changed +- } +- } +- +- // Deps have changed, so we must re-evaluate the key. +- n.ph.depKeys = make(map[PackageID]source.Hash) +- +- // See the typerefs package: the reachable set of packages is defined to be +- // the set of packages containing syntax that is reachable through the +- // exported symbols in the dependencies of n.ph. +- reachable := b.s.pkgIndex.NewSet() +- for depID, succ := range n.succs { +- n.ph.depKeys[depID] = succ.ph.key +- reachable.Add(succ.idxID) +- trefs := b.getTransitiveRefs(succ.m.ID) +- if trefs == nil { +- // A predecessor failed to build due to e.g. context cancellation. +- return fmt.Errorf("missing transitive refs for %s", succ.m.ID) +- } +- for _, set := range trefs { +- reachable.Union(set) +- } +- } +- +- // Collect reachable handles. +- var reachableHandles []*packageHandle +- // In the presence of context cancellation, any package may be missing. +- // We need all dependencies to produce a valid key. +- missingReachablePackage := false +- reachable.Elems(func(id typerefs.IndexID) { +- dh := b.nodes[id] +- if dh == nil { +- missingReachablePackage = true +- } else { +- assert(dh.ph.validated, "unvalidated dependency") +- reachableHandles = append(reachableHandles, dh.ph) +- } +- }) +- if missingReachablePackage { +- return fmt.Errorf("missing reachable package") +- } +- // Sort for stability. +- sort.Slice(reachableHandles, func(i, j int) bool { +- return reachableHandles[i].m.ID < reachableHandles[j].m.ID +- }) +- +- // Key is the hash of the local key, and the local key of all reachable +- // packages. +- depHasher := sha256.New() +- depHasher.Write(n.ph.localKey[:]) +- for _, rph := range reachableHandles { +- depHasher.Write(rph.localKey[:]) - } +- depHasher.Sum(n.ph.key[:0]) +- +- return nil +-} - -- if hit { -- return entry.(*packageHandle), nil +-// typerefs returns typerefs for the package described by m and cgfs, after +-// either computing it or loading it from the file cache. +-func (s *snapshot) typerefs(ctx context.Context, m *source.Metadata, cgfs []source.FileHandle) (map[string][]typerefs.Symbol, error) { +- imports := make(map[ImportPath]*source.Metadata) +- for impPath, id := range m.DepsByImpPath { +- if id != "" { +- imports[impPath] = s.Metadata(id) +- } - } - -- inputs, err := s.typeCheckInputs(ctx, m) +- data, err := s.typerefData(ctx, m.ID, imports, cgfs) - if err != nil { - return nil, err - } -- // All the file reading has now been done. -- // Create a handle for the result of type checking. -- phKey := computePackageKey(s, inputs) -- ph := &packageHandle{ -- m: m, -- inputs: inputs, -- key: phKey, +- classes := typerefs.Decode(s.pkgIndex, m.ID, data) +- refs := make(map[string][]typerefs.Symbol) +- for _, class := range classes { +- for _, decl := range class.Decls { +- refs[decl] = class.Refs +- } - } +- return refs, nil +-} - -- s.mu.Lock() -- defer s.mu.Unlock() +-// typerefData retrieves encoded typeref data from the filecache, or computes it on +-// a cache miss. +-func (s *snapshot) typerefData(ctx context.Context, id PackageID, imports map[ImportPath]*source.Metadata, cgfs []source.FileHandle) ([]byte, error) { +- key := typerefsKey(id, imports, cgfs) +- if data, err := filecache.Get(typerefsKind, key); err == nil { +- return data, nil +- } else if err != filecache.ErrNotFound { +- bug.Reportf("internal error reading typerefs data: %v", err) +- } - -- // Check that the metadata has not changed -- // (which should invalidate this handle). -- // -- // (In future, handles should form a graph with edges from a -- // packageHandle to the handles for parsing its files and the -- // handles for type-checking its immediate deps, at which -- // point there will be no need to even access s.meta.) -- if s.meta.metadata[ph.m.ID] != ph.m { -- // TODO(rfindley): this should be bug.Errorf. -- return nil, fmt.Errorf("stale metadata for %s", ph.m.ID) +- pgfs, err := s.view.parseCache.parseFiles(ctx, token.NewFileSet(), source.ParseFull&^parser.ParseComments, true, cgfs...) +- if err != nil { +- return nil, err - } +- data := typerefs.Encode(pgfs, id, imports) - -- // Check cache again in case another goroutine got there first. -- if prev, ok := s.packages.Get(id); ok { -- prevPH := prev.(*packageHandle) -- if prevPH.m != ph.m { -- return nil, bug.Errorf("existing package handle does not match for %s", ph.m.ID) +- // Store the resulting data in the cache. +- go func() { +- if err := filecache.Set(typerefsKind, key, data); err != nil { +- event.Error(ctx, fmt.Sprintf("storing typerefs data for %s", id), err) - } -- return prevPH, nil +- }() +- +- return data, nil +-} +- +-// typerefsKey produces a key for the reference information produced by the +-// typerefs package. +-func typerefsKey(id PackageID, imports map[ImportPath]*source.Metadata, compiledGoFiles []source.FileHandle) source.Hash { +- hasher := sha256.New() +- +- fmt.Fprintf(hasher, "typerefs: %s\n", id) +- +- importPaths := make([]string, 0, len(imports)) +- for impPath := range imports { +- importPaths = append(importPaths, string(impPath)) +- } +- sort.Strings(importPaths) +- for _, importPath := range importPaths { +- imp := imports[ImportPath(importPath)] +- // TODO(rfindley): strength reduce the typerefs.Export API to guarantee +- // that it only depends on these attributes of dependencies. +- fmt.Fprintf(hasher, "import %s %s %s", importPath, imp.ID, imp.Name) - } - -- s.packages.Set(id, ph, nil) -- return ph, nil +- fmt.Fprintf(hasher, "compiledGoFiles: %d\n", len(compiledGoFiles)) +- for _, fh := range compiledGoFiles { +- fmt.Fprintln(hasher, fh.FileIdentity()) +- } +- +- var hash [sha256.Size]byte +- hasher.Sum(hash[:0]) +- return hash -} - -// typeCheckInputs contains the inputs of a call to typeCheckImpl, which @@ -13990,7 +16045,6 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - name PackageName - goFiles, compiledGoFiles []source.FileHandle - sizes types.Sizes -- deps map[PackageID]*packageHandle - depsByImpPath map[ImportPath]PackageID - goVersion string // packages.Module.GoVersion, e.g. "1.18" - @@ -14001,38 +16055,21 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g -} - -func (s *snapshot) typeCheckInputs(ctx context.Context, m *source.Metadata) (typeCheckInputs, error) { -- deps := make(map[PackageID]*packageHandle) -- for _, depID := range m.DepsByPkgPath { -- depHandle, err := s.buildPackageHandle(ctx, depID) -- if err != nil { -- // If err is non-nil, we either have an invalid dependency, or a -- // catastrophic failure to read a file (context cancellation or -- // permission issues). -- // -- // We don't want one bad dependency to prevent us from type-checking the -- // package -- we should instead get an import error. So we only abort -- // this operation if the context is cancelled. -- // -- // We could do a better job of handling permission errors on files, but -- // this is rare, and it is reasonable to treat the same an invalid -- // dependency. -- event.Error(ctx, fmt.Sprintf("%s: no dep handle for %s", m.ID, depID), err, source.SnapshotLabels(s)...) -- if ctx.Err() != nil { -- return typeCheckInputs{}, ctx.Err() // cancelled -- } -- continue -- } -- deps[depID] = depHandle -- } -- -- // Read both lists of files of this package, in parallel. +- // Read both lists of files of this package. +- // +- // Parallelism is not necessary here as the files will have already been +- // pre-read at load time. - // - // goFiles aren't presented to the type checker--nor - // are they included in the key, unsoundly--but their - // syntax trees are available from (*pkg).File(URI). - // TODO(adonovan): consider parsing them on demand? - // The need should be rare. -- goFiles, compiledGoFiles, err := readGoFiles(ctx, s, m) +- goFiles, err := readFiles(ctx, s, m.GoFiles) +- if err != nil { +- return typeCheckInputs{}, err +- } +- compiledGoFiles, err := readFiles(ctx, s, m.CompiledGoFiles) - if err != nil { - return typeCheckInputs{}, err - } @@ -14049,40 +16086,31 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - goFiles: goFiles, - compiledGoFiles: compiledGoFiles, - sizes: m.TypesSizes, -- deps: deps, - depsByImpPath: m.DepsByImpPath, - goVersion: goVersion, - -- relatedInformation: s.view.Options().RelatedInformationSupported, -- linkTarget: s.view.Options().LinkTarget, -- moduleMode: s.moduleMode(), +- relatedInformation: s.options.RelatedInformationSupported, +- linkTarget: s.options.LinkTarget, +- moduleMode: s.view.moduleMode(), - }, nil -} - --// readGoFiles reads the content of Metadata.GoFiles and --// Metadata.CompiledGoFiles, in parallel. --func readGoFiles(ctx context.Context, s *snapshot, m *source.Metadata) (goFiles, compiledGoFiles []source.FileHandle, err error) { -- var group errgroup.Group -- getFileHandles := func(files []span.URI) []source.FileHandle { -- fhs := make([]source.FileHandle, len(files)) -- for i, uri := range files { -- i, uri := i, uri -- group.Go(func() (err error) { -- fhs[i], err = s.GetFile(ctx, uri) // ~25us -- return -- }) +-// readFiles reads the content of each file URL from the source +-// (e.g. snapshot or cache). +-func readFiles(ctx context.Context, fs source.FileSource, uris []span.URI) (_ []source.FileHandle, err error) { +- fhs := make([]source.FileHandle, len(uris)) +- for i, uri := range uris { +- fhs[i], err = fs.ReadFile(ctx, uri) +- if err != nil { +- return nil, err - } -- return fhs - } -- return getFileHandles(m.GoFiles), -- getFileHandles(m.CompiledGoFiles), -- group.Wait() +- return fhs, nil -} - --// computePackageKey returns a key representing the act of type checking --// a package named id containing the specified files, metadata, and --// combined dependency hash. --func computePackageKey(s *snapshot, inputs typeCheckInputs) packageHandleKey { +-// localPackageKey returns a key for local inputs into type-checking, excluding +-// dependency information: files, metadata, and configuration. +-func localPackageKey(inputs typeCheckInputs) source.Hash { - hasher := sha256.New() - - // In principle, a key must be the hash of an @@ -14105,17 +16133,6 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - fmt.Fprintf(hasher, "import %s %s", impPath, string(inputs.depsByImpPath[ImportPath(impPath)])) - } - -- // deps, in PackageID order -- depIDs := make([]string, 0, len(inputs.deps)) -- for depID := range inputs.deps { -- depIDs = append(depIDs, string(depID)) -- } -- sort.Strings(depIDs) -- for _, depID := range depIDs { -- dep := inputs.deps[PackageID(depID)] -- fmt.Fprintf(hasher, "dep: %s key:%s\n", dep.m.PkgPath, dep.key) -- } -- - // file names and contents - fmt.Fprintf(hasher, "compiledGoFiles: %d\n", len(inputs.compiledGoFiles)) - for _, fh := range inputs.compiledGoFiles { @@ -14127,8 +16144,9 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - } - - // types sizes -- sz := inputs.sizes.(*types.StdSizes) -- fmt.Fprintf(hasher, "sizes: %d %d\n", sz.WordSize, sz.MaxAlign) +- wordSize := inputs.sizes.Sizeof(types.Typ[types.Int]) +- maxAlign := inputs.sizes.Alignof(types.NewPointer(types.Typ[types.Int64])) +- fmt.Fprintf(hasher, "sizes: %d %d\n", wordSize, maxAlign) - - fmt.Fprintf(hasher, "relatedInformation: %t\n", inputs.relatedInformation) - fmt.Fprintf(hasher, "linkTarget: %s\n", inputs.linkTarget) @@ -14136,7 +16154,7 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - - var hash [sha256.Size]byte - hasher.Sum(hash[:0]) -- return packageHandleKey(hash) +- return hash -} - -// typeCheckImpl type checks the parsed source files in compiledGoFiles. @@ -14150,8 +16168,6 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - if err != nil { - return nil, err - } -- pkg.methodsets = methodsets.NewIndex(pkg.fset, pkg.types) -- pkg.xrefs = xrefs.Index(pkg.compiledGoFiles, pkg.types, pkg.typesInfo) - - // Our heuristic for whether to show type checking errors is: - // + If any file was 'fixed', don't show type checking errors as we @@ -14187,7 +16203,13 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - for _, e := range expandErrors(unexpanded, inputs.relatedInformation) { - diags, err := typeErrorDiagnostics(inputs.moduleMode, inputs.linkTarget, pkg, e) - if err != nil { -- event.Error(ctx, "unable to compute positions for type errors", err, tag.Package.Of(string(inputs.id))) +- // If we fail here and there are no parse errors, it means we are hiding +- // a valid type-checking error from the user. This must be a bug, with +- // one exception: relocated primary errors may fail processing, because +- // they reference locations outside of the package. +- if len(pkg.parseErrors) == 0 && !e.relocated { +- bug.Reportf("failed to compute position for type error %v: %v", e, err) +- } - continue - } - pkg.typeErrors = append(pkg.typeErrors, e.primary) @@ -14201,13 +16223,20 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - } - } - +- // Work around golang/go#61561: interface instances aren't concurrency-safe +- // as they are not completed by the type checker. +- for _, inst := range typeparams.GetInstances(pkg.typesInfo) { +- if iface, _ := inst.Type.Underlying().(*types.Interface); iface != nil { +- iface.Complete() +- } +- } +- - return pkg, nil -} - -var goVersionRx = regexp.MustCompile(`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$`) - -func doTypeCheck(ctx context.Context, b *typeCheckBatch, inputs typeCheckInputs) (*syntaxPackage, error) { -- impMap, errMap := b.importMap(inputs.id) - pkg := &syntaxPackage{ - id: inputs.id, - fset: b.fset, // must match parse call below @@ -14220,30 +16249,24 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - Selections: make(map[*ast.SelectorExpr]*types.Selection), - Scopes: make(map[ast.Node]*types.Scope), - }, -- importMap: impMap, - } - typeparams.InitInstanceInfo(pkg.typesInfo) - - // Collect parsed files from the type check pass, capturing parse errors from - // compiled files. -- for _, fh := range inputs.goFiles { -- pgf := b.parsedFiles[fh.URI()] -- if pgf == nil { -- // If go/packages told us that a file is in a package, it should be -- // parseable (after all, it was parsed by go list). -- return nil, bug.Errorf("go file %q failed to parse", fh.URI().Filename()) -- } -- pkg.goFiles = append(pkg.goFiles, pgf) +- var err error +- pkg.goFiles, err = b.parseCache.parseFiles(ctx, b.fset, source.ParseFull, false, inputs.goFiles...) +- if err != nil { +- return nil, err - } -- for _, fh := range inputs.compiledGoFiles { -- pgf := b.parsedFiles[fh.URI()] -- if pgf == nil { -- return nil, fmt.Errorf("compiled go file %q failed to parse", fh.URI().Filename()) -- } +- pkg.compiledGoFiles, err = b.parseCache.parseFiles(ctx, b.fset, source.ParseFull, false, inputs.compiledGoFiles...) +- if err != nil { +- return nil, err +- } +- for _, pgf := range pkg.compiledGoFiles { - if pgf.ParseErr != nil { - pkg.parseErrors = append(pkg.parseErrors, pgf.ParseErr) - } -- pkg.compiledGoFiles = append(pkg.compiledGoFiles, pgf) - } - - // Use the default type information for the unsafe package. @@ -14266,7 +16289,7 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - onError := func(e error) { - pkg.typeErrors = append(pkg.typeErrors, e.(types.Error)) - } -- cfg := b.typesConfig(inputs, onError, impMap, errMap) +- cfg := b.typesConfig(ctx, inputs, onError) - - check := types.NewChecker(cfg, pkg.fset, pkg.types, pkg.typesInfo) - @@ -14275,6 +16298,12 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - files = append(files, cgf.File) - } - +- // Type checking is expensive, and we may not have ecountered cancellations +- // via parsing (e.g. if we got nothing but cache hits for parsed files). +- if ctx.Err() != nil { +- return nil, ctx.Err() +- } +- - // Type checking errors are handled via the config, so ignore them here. - _ = check.Files(files) // 50us-15ms, depending on size of package - @@ -14283,10 +16312,26 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - if ctx.Err() != nil { - return nil, ctx.Err() - } +- +- // Collect imports by package path for the DependencyTypes API. +- pkg.importMap = make(map[PackagePath]*types.Package) +- var collectDeps func(*types.Package) +- collectDeps = func(p *types.Package) { +- pkgPath := PackagePath(p.Path()) +- if _, ok := pkg.importMap[pkgPath]; ok { +- return +- } +- pkg.importMap[pkgPath] = p +- for _, imp := range p.Imports() { +- collectDeps(imp) +- } +- } +- collectDeps(pkg.types) +- - return pkg, nil -} - --func (b *typeCheckBatch) typesConfig(inputs typeCheckInputs, onError func(e error), impMap map[string]*types.Package, errMap map[PackagePath]error) *types.Config { +-func (b *typeCheckBatch) typesConfig(ctx context.Context, inputs typeCheckInputs, onError func(e error)) *types.Config { - cfg := &types.Config{ - Sizes: inputs.sizes, - Error: onError, @@ -14302,23 +16347,15 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - // See TestFixImportDecl for an example. - return nil, fmt.Errorf("missing metadata for import of %q", path) - } -- depPH := inputs.deps[id] +- depPH := b.handles[id] - if depPH == nil { - // e.g. missing metadata for dependencies in buildPackageHandle -- return nil, missingPkgError(path, inputs.moduleMode) +- return nil, missingPkgError(inputs.id, path, inputs.moduleMode) - } - if !source.IsValidImport(inputs.pkgPath, depPH.m.PkgPath) { - return nil, fmt.Errorf("invalid use of internal package %q", path) - } -- pkg, ok := impMap[string(depPH.m.PkgPath)] -- if !ok { -- err := errMap[depPH.m.PkgPath] -- if err == nil { -- log.Fatalf("neither pkg nor error is set") -- } -- return nil, err -- } -- return pkg, nil +- return b.getImportPackage(ctx, id) - }), - } - @@ -14383,7 +16420,7 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - if err != nil { - return nil, err - } -- fset := source.FileSetFor(pgf.Tok) +- fset := tokeninternal.FileSetFor(pgf.Tok) - // TODO(adonovan): modify Imports() to accept a single token.File (cgf.Tok). - for _, group := range astutil.Imports(fset, pgf.File) { - for _, imp := range group { @@ -14415,14 +16452,18 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - if err != nil { - return nil, err - } -- errors = append(errors, &source.Diagnostic{ +- diag := &source.Diagnostic{ - URI: imp.cgf.URI, - Range: rng, - Severity: protocol.SeverityError, - Source: source.TypeError, - Message: fmt.Sprintf("error while importing %v: %v", item, depErr.Err), - SuggestedFixes: fixes, -- }) +- } +- if !source.BundleQuickFixes(diag) { +- bug.Reportf("failed to bundle fixes for diagnostic %q", diag.Message) +- } +- errors = append(errors, diag) - } - } - } @@ -14458,14 +16499,18 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - if err != nil { - return nil, err - } -- errors = append(errors, &source.Diagnostic{ +- diag := &source.Diagnostic{ - URI: pm.URI, - Range: rng, - Severity: protocol.SeverityError, - Source: source.TypeError, - Message: fmt.Sprintf("error while importing %v: %v", item, depErr.Err), - SuggestedFixes: fixes, -- }) +- } +- if !source.BundleQuickFixes(diag) { +- bug.Reportf("failed to bundle fixes for diagnostic %q", diag.Message) +- } +- errors = append(errors, diag) - break - } - } @@ -14474,13 +16519,17 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - -// missingPkgError returns an error message for a missing package that varies -// based on the user's workspace mode. --func missingPkgError(pkgPath string, moduleMode bool) error { +-func missingPkgError(from PackageID, pkgPath string, moduleMode bool) error { - // TODO(rfindley): improve this error. Previous versions of this error had - // access to the full snapshot, and could provide more information (such as - // the initialization error). - if moduleMode { -- // Previously, we would present the initialization error here. -- return fmt.Errorf("no required module provides package %q", pkgPath) +- if source.IsCommandLineArguments(from) { +- return fmt.Errorf("current file is not included in a workspace module") +- } else { +- // Previously, we would present the initialization error here. +- return fmt.Errorf("no required module provides package %q", pkgPath) +- } - } else { - // Previously, we would list the directories in GOROOT and GOPATH here. - return fmt.Errorf("cannot find package %q in GOROOT or GOPATH", pkgPath) @@ -14488,6 +16537,7 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g -} - -type extendedError struct { +- relocated bool // if set, this is a relocation of a primary error to a secondary location - primary types.Error - secondaries []types.Error -} @@ -14540,7 +16590,7 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - - // Copy over the secondary errors, noting the location of the - // current error we're cloning. -- clonedError := extendedError{primary: relocatedSecondary, secondaries: []types.Error{original.primary}} +- clonedError := extendedError{relocated: true, primary: relocatedSecondary, secondaries: []types.Error{original.primary}} - for j, secondary := range original.secondaries { - if i == j { - secondary.Msg += " (this error)" @@ -14549,7 +16599,6 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g - } - result = append(result, clonedError) - } -- - } - return result -} @@ -14559,10 +16608,10 @@ diff -urN a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.g -type importerFunc func(path string) (*types.Package, error) - -func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) } -diff -urN a/gopls/internal/lsp/cache/debug.go b/gopls/internal/lsp/cache/debug.go ---- a/gopls/internal/lsp/cache/debug.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/debug.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,55 +0,0 @@ +diff -urN a/gopls/internal/lsp/cache/constraints.go b/gopls/internal/lsp/cache/constraints.go +--- a/gopls/internal/lsp/cache/constraints.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cache/constraints.go 1970-01-01 08:00:00 +@@ -1,61 +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. @@ -14570,58 +16619,365 @@ diff -urN a/gopls/internal/lsp/cache/debug.go b/gopls/internal/lsp/cache/debug.g -package cache - -import ( -- "fmt" -- "os" -- "sort" +- "go/ast" +- "go/build/constraint" +- "go/parser" +- "go/token" -) - --// This file contains helpers that can be used to instrument code while --// debugging. +-// isStandaloneFile reports whether a file with the given contents should be +-// considered a 'standalone main file', meaning a package that consists of only +-// a single file. +-func isStandaloneFile(src []byte, standaloneTags []string) bool { +- f, err := parser.ParseFile(token.NewFileSet(), "", src, parser.PackageClauseOnly|parser.ParseComments) +- if err != nil { +- return false +- } +- +- if f.Name == nil || f.Name.Name != "main" { +- return false +- } - --// debugEnabled toggles the helpers below. --const debugEnabled = false +- found := false +- walkConstraints(f, func(c constraint.Expr) bool { +- if tag, ok := c.(*constraint.TagExpr); ok { +- for _, t := range standaloneTags { +- if t == tag.Tag { +- found = true +- return false +- } +- } +- } +- return true +- }) - --// If debugEnabled is true, debugf formats its arguments and prints to stderr. --// If debugEnabled is false, it is a no-op. --func debugf(format string, args ...interface{}) { -- if !debugEnabled { -- return +- return found +-} +- +-// walkConstraints calls f for each constraint expression in the file, until +-// all constraints are exhausted or f returns false. +-func walkConstraints(file *ast.File, f func(constraint.Expr) bool) { +- for _, cg := range file.Comments { +- // Even with PackageClauseOnly the parser consumes the semicolon following +- // the package clause, so we must guard against comments that come after +- // the package name. +- if cg.Pos() > file.Name.Pos() { +- continue +- } +- for _, comment := range cg.List { +- if c, err := constraint.Parse(comment.Text); err == nil { +- if !f(c) { +- return +- } +- } +- } - } -- if false { -- _ = fmt.Sprintf(format, args...) // encourage vet to validate format strings +-} +diff -urN a/gopls/internal/lsp/cache/constraints_test.go b/gopls/internal/lsp/cache/constraints_test.go +--- a/gopls/internal/lsp/cache/constraints_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cache/constraints_test.go 1970-01-01 08:00:00 +@@ -1,96 +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.16 +-// +build go1.16 +- +-package cache +- +-import ( +- "testing" +-) +- +-func TestIsStandaloneFile(t *testing.T) { +- tests := []struct { +- desc string +- contents string +- standaloneTags []string +- want bool +- }{ +- { +- "new syntax", +- "//go:build ignore\n\npackage main\n", +- []string{"ignore"}, +- true, +- }, +- { +- "legacy syntax", +- "// +build ignore\n\npackage main\n", +- []string{"ignore"}, +- true, +- }, +- { +- "multiple tags", +- "//go:build ignore\n\npackage main\n", +- []string{"exclude", "ignore"}, +- true, +- }, +- { +- "invalid tag", +- "// +build ignore\n\npackage main\n", +- []string{"script"}, +- false, +- }, +- { +- "non-main package", +- "//go:build ignore\n\npackage p\n", +- []string{"ignore"}, +- false, +- }, +- { +- "alternate tag", +- "// +build script\n\npackage main\n", +- []string{"script"}, +- true, +- }, +- { +- "both syntax", +- "//go:build ignore\n// +build ignore\n\npackage main\n", +- []string{"ignore"}, +- true, +- }, +- { +- "after comments", +- "// A non-directive comment\n//go:build ignore\n\npackage main\n", +- []string{"ignore"}, +- true, +- }, +- { +- "after package decl", +- "package main //go:build ignore\n", +- []string{"ignore"}, +- false, +- }, +- { +- "on line after package decl", +- "package main\n\n//go:build ignore\n", +- []string{"ignore"}, +- false, +- }, +- { +- "combined with other expressions", +- "\n\n//go:build ignore || darwin\n\npackage main\n", +- []string{"ignore"}, +- false, +- }, +- } +- +- for _, test := range tests { +- t.Run(test.desc, func(t *testing.T) { +- if got := isStandaloneFile([]byte(test.contents), test.standaloneTags); got != test.want { +- t.Errorf("isStandaloneFile(%q, %v) = %t, want %t", test.contents, test.standaloneTags, got, test.want) +- } +- }) - } -- fmt.Fprintf(os.Stderr, ">>> "+format+"\n", args...) -} +diff -urN a/gopls/internal/lsp/cache/cycle_test.go b/gopls/internal/lsp/cache/cycle_test.go +--- a/gopls/internal/lsp/cache/cycle_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cache/cycle_test.go 1970-01-01 08:00:00 +@@ -1,181 +0,0 @@ +-// Copyright 2023 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. - --// If debugEnabled is true, dumpWorkspace prints a summary of workspace --// packages to stderr. If debugEnabled is false, it is a no-op. --// --// TODO(rfindley): this has served its purpose. Delete. --func (s *snapshot) dumpWorkspace(context string) { -- if !debugEnabled { -- return +-package cache +- +-import ( +- "sort" +- "strings" +- "testing" +- +- "golang.org/x/tools/gopls/internal/lsp/source" +-) +- +-// This is an internal test of the breakImportCycles logic. +-func TestBreakImportCycles(t *testing.T) { +- +- type Graph = map[PackageID]*source.Metadata +- +- // cyclic returns a description of a cycle, +- // if the graph is cyclic, otherwise "". +- cyclic := func(graph Graph) string { +- const ( +- unvisited = 0 +- visited = 1 +- onstack = 2 +- ) +- color := make(map[PackageID]int) +- var visit func(id PackageID) string +- visit = func(id PackageID) string { +- switch color[id] { +- case unvisited: +- color[id] = onstack +- case onstack: +- return string(id) // cycle! +- case visited: +- return "" +- } +- if m := graph[id]; m != nil { +- for _, depID := range m.DepsByPkgPath { +- if cycle := visit(depID); cycle != "" { +- return string(id) + "->" + cycle +- } +- } +- } +- color[id] = visited +- return "" +- } +- for id := range graph { +- if cycle := visit(id); cycle != "" { +- return cycle +- } +- } +- return "" - } - -- debugf("workspace (after %s):", context) -- var ids []PackageID -- for id := range s.workspacePackages { -- ids = append(ids, id) +- // parse parses an import dependency graph. +- // The input is a semicolon-separated list of node descriptions. +- // Each node description is a package ID, optionally followed by +- // "->" and a comma-separated list of successor IDs. +- // Thus "a->b;b->c,d;e" represents the set of nodes {a,b,e} +- // and the set of edges {a->b, b->c, b->d}. +- parse := func(s string) Graph { +- m := make(Graph) +- makeNode := func(name string) *source.Metadata { +- id := PackageID(name) +- n, ok := m[id] +- if !ok { +- n = &source.Metadata{ +- ID: id, +- DepsByPkgPath: make(map[PackagePath]PackageID), +- } +- m[id] = n +- } +- return n +- } +- if s != "" { +- for _, item := range strings.Split(s, ";") { +- nodeID, succIDs, ok := strings.Cut(item, "->") +- node := makeNode(nodeID) +- if ok { +- for _, succID := range strings.Split(succIDs, ",") { +- node.DepsByPkgPath[PackagePath(succID)] = PackageID(succID) +- } +- } +- } +- } +- return m - } - -- sort.Slice(ids, func(i, j int) bool { -- return ids[i] < ids[j] -- }) +- // Sanity check of cycle detector. +- { +- got := cyclic(parse("a->b;b->c;c->a,d")) +- has := func(s string) bool { return strings.Contains(got, s) } +- if !(has("a->b") && has("b->c") && has("c->a") && !has("d")) { +- t.Fatalf("cyclic: got %q, want a->b->c->a or equivalent", got) +- } +- } - -- for _, id := range ids { -- pkgPath := s.workspacePackages[id] -- _, ok := s.meta.metadata[id] -- debugf(" %s:%s (metadata: %t)", id, pkgPath, ok) +- // format formats an import graph, in lexicographic order, +- // in the notation of parse, but with a "!" after the name +- // of each node that has errors. +- format := func(graph Graph) string { +- var items []string +- for _, m := range graph { +- item := string(m.ID) +- if len(m.Errors) > 0 { +- item += "!" +- } +- var succs []string +- for _, depID := range m.DepsByPkgPath { +- succs = append(succs, string(depID)) +- } +- if succs != nil { +- sort.Strings(succs) +- item += "->" + strings.Join(succs, ",") +- } +- items = append(items, item) +- } +- sort.Strings(items) +- return strings.Join(items, ";") +- } +- +- // We needn't test self-cycles as they are eliminated at Metadata construction. +- for _, test := range []struct { +- metadata, updates, want string +- }{ +- // Simple 2-cycle. +- {"a->b", "b->a", +- "a->b;b!"}, // broke b->a +- +- {"a->b;b->c;c", "b->a,c", +- "a->b;b!->c;c"}, // broke b->a +- +- // Reversing direction of p->s edge creates pqrs cycle. +- {"a->p,q,r,s;p->q,s,z;q->r,z;r->s,z;s->z", "p->q,z;s->p,z", +- "a->p,q,r,s;p!->z;q->r,z;r->s,z;s!->z"}, // broke p->q, s->p +- +- // We break all intra-SCC edges from updated nodes, +- // which may be more than necessary (e.g. a->b). +- {"a->b;b->c;c;d->a", "a->b,e;c->d", +- "a!->e;b->c;c!;d->a"}, // broke a->b, c->d +- } { +- metadata := parse(test.metadata) +- updates := parse(test.updates) +- +- if cycle := cyclic(metadata); cycle != "" { +- t.Errorf("initial metadata %s has cycle %s: ", format(metadata), cycle) +- continue +- } +- +- t.Log("initial", format(metadata)) +- +- // Apply updates. +- // (parse doesn't have a way to express node deletions, +- // but they aren't very interesting.) +- for id, m := range updates { +- metadata[id] = m +- } +- +- t.Log("updated", format(metadata)) +- +- // breakImportCycles accesses only these fields of Metadata: +- // DepsByImpPath, ID - read +- // DepsByPkgPath - read, updated +- // Errors - updated +- breakImportCycles(metadata, updates) +- +- t.Log("acyclic", format(metadata)) +- +- if cycle := cyclic(metadata); cycle != "" { +- t.Errorf("resulting metadata %s has cycle %s: ", format(metadata), cycle) +- } +- +- got := format(metadata) +- if got != test.want { +- t.Errorf("test.metadata=%s test.updates=%s: got=%s want=%s", +- test.metadata, test.updates, got, test.want) +- } +- } +-} +diff -urN a/gopls/internal/lsp/cache/debug.go b/gopls/internal/lsp/cache/debug.go +--- a/gopls/internal/lsp/cache/debug.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cache/debug.go 1970-01-01 08:00:00 +@@ -1,12 +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 cache +- +-// assert panics with the given msg if cond is not true. +-func assert(cond bool, msg string) { +- if !cond { +- panic(msg) - } -} diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors.go --- a/gopls/internal/lsp/cache/errors.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/errors.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,528 +0,0 @@ ++++ b/gopls/internal/lsp/cache/errors.go 1970-01-01 08:00:00 +@@ -1,545 +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. @@ -14635,6 +16991,7 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors -import ( - "context" - "fmt" +- "go/parser" - "go/scanner" - "go/token" - "go/types" @@ -14644,13 +17001,13 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors - "strings" - - "golang.org/x/tools/go/packages" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/command" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" - "golang.org/x/tools/internal/analysisinternal" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/typesinternal" -) - @@ -14759,7 +17116,13 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors - for _, secondary := range e.secondaries { - _, secondaryLoc, err := typeErrorData(pkg, secondary) - if err != nil { -- return nil, err +- // We may not be able to compute type error data in scenarios where the +- // secondary position is outside of the current package. In this case, we +- // don't want to ignore the diagnostic entirely. +- // +- // See golang/go#59005 for an example where gopls was missing diagnostics +- // due to returning an error here. +- continue - } - diag.Related = append(diag.Related, protocol.DiagnosticRelatedInformation{ - Location: secondaryLoc, @@ -14866,13 +17229,13 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors - } - gobDiags = append(gobDiags, gobDiag) - } -- return mustEncode(gobDiags) +- return diagnosticsCodec.Encode(gobDiags) -} - -// decodeDiagnostics decodes the given gob-encoded diagnostics. -func decodeDiagnostics(data []byte) []*source.Diagnostic { - var gobDiags []gobDiagnostic -- mustDecode(data, &gobDiags) +- diagnosticsCodec.Decode(data, &gobDiags) - var srcDiags []*source.Diagnostic - for _, gobDiag := range gobDiags { - var srcFixes []source.SuggestedFix @@ -14893,7 +17256,7 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors - srcFix.Edits[uri] = append(srcFix.Edits[uri], srcEdit) - } - if gobCmd := gobFix.Command; gobCmd != nil { -- gobFix.Command = &gobCommand{ +- srcFix.Command = &protocol.Command{ - Title: gobCmd.Title, - Command: gobCmd.Command, - Arguments: gobCmd.Arguments, @@ -14910,6 +17273,8 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors - URI: gobDiag.Location.URI.SpanURI(), - Range: gobDiag.Location.Range, - Severity: gobDiag.Severity, +- Code: gobDiag.Code, +- CodeHref: gobDiag.CodeHref, - Source: source.AnalyzerErrorKind(gobDiag.Source), - Message: gobDiag.Message, - Tags: gobDiag.Tags, @@ -14954,17 +17319,23 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors - } - - diag := &source.Diagnostic{ -- URI: gobDiag.Location.URI.SpanURI(), -- Range: gobDiag.Location.Range, -- Severity: severity, -- Source: source.AnalyzerErrorKind(gobDiag.Source), -- Message: gobDiag.Message, -- Related: related, -- SuggestedFixes: fixes, +- URI: gobDiag.Location.URI.SpanURI(), +- Range: gobDiag.Location.Range, +- Severity: severity, +- Code: gobDiag.Code, +- CodeHref: gobDiag.CodeHref, +- Source: source.AnalyzerErrorKind(gobDiag.Source), +- Message: gobDiag.Message, +- Related: related, +- Tags: srcAnalyzer.Tag, - } +- if srcAnalyzer.FixesDiagnostic(diag) { +- diag.SuggestedFixes = fixes +- } +- - // If the fixes only delete code, assume that the diagnostic is reporting dead code. - if onlyDeletions(fixes) { -- diag.Tags = []protocol.DiagnosticTag{protocol.Unnecessary} +- diag.Tags = append(diag.Tags, protocol.Unnecessary) - } - return diag -} @@ -15052,11 +17423,11 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors -// contained in the provided FileSource. -func spanToRange(ctx context.Context, fs source.FileSource, spn span.Span) (protocol.Range, error) { - uri := spn.URI() -- fh, err := fs.GetFile(ctx, uri) +- fh, err := fs.ReadFile(ctx, uri) - if err != nil { - return protocol.Range{}, err - } -- content, err := fh.Read() +- content, err := fh.Content() - if err != nil { - return protocol.Range{}, err - } @@ -15131,12 +17502,14 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors -// to use in a list of file of a package, for example. -// -// It returns an error if the file could not be read. --func parseGoURI(ctx context.Context, fs source.FileSource, uri span.URI, mode source.ParseMode) (*source.ParsedGoFile, error) { -- fh, err := fs.GetFile(ctx, uri) +-// +-// TODO(rfindley): eliminate this helper. +-func parseGoURI(ctx context.Context, fs source.FileSource, uri span.URI, mode parser.Mode) (*source.ParsedGoFile, error) { +- fh, err := fs.ReadFile(ctx, uri) - if err != nil { - return nil, err - } -- return parseGoImpl(ctx, token.NewFileSet(), fh, source.ParseHeader) +- return parseGoImpl(ctx, token.NewFileSet(), fh, mode, false) -} - -// parseModURI is a helper to parse the Mod file at the given URI from the file @@ -15144,7 +17517,7 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors -// -// It returns an error if the file could not be read. -func parseModURI(ctx context.Context, fs source.FileSource, uri span.URI) (*source.ParsedModule, error) { -- fh, err := fs.GetFile(ctx, uri) +- fh, err := fs.ReadFile(ctx, uri) - if err != nil { - return nil, err - } @@ -15152,8 +17525,8 @@ diff -urN a/gopls/internal/lsp/cache/errors.go b/gopls/internal/lsp/cache/errors -} diff -urN a/gopls/internal/lsp/cache/errors_test.go b/gopls/internal/lsp/cache/errors_test.go --- a/gopls/internal/lsp/cache/errors_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/errors_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,52 +0,0 @@ ++++ b/gopls/internal/lsp/cache/errors_test.go 1970-01-01 08:00:00 +@@ -1,130 +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. @@ -15161,8 +17534,14 @@ diff -urN a/gopls/internal/lsp/cache/errors_test.go b/gopls/internal/lsp/cache/e -package cache - -import ( +- "encoding/json" - "strings" - "testing" +- +- "github.com/google/go-cmp/cmp" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" -) - -func TestParseErrorMessage(t *testing.T) { @@ -15206,10 +17585,349 @@ diff -urN a/gopls/internal/lsp/cache/errors_test.go b/gopls/internal/lsp/cache/e - }) - } -} +- +-func TestDiagnosticEncoding(t *testing.T) { +- diags := []*source.Diagnostic{ +- {}, // empty +- { +- URI: "file///foo", +- Range: protocol.Range{ +- Start: protocol.Position{Line: 4, Character: 2}, +- End: protocol.Position{Line: 6, Character: 7}, +- }, +- Severity: protocol.SeverityWarning, +- Code: "red", +- CodeHref: "https://go.dev", +- Source: "test", +- Message: "something bad happened", +- Tags: []protocol.DiagnosticTag{81}, +- Related: []protocol.DiagnosticRelatedInformation{ +- { +- Location: protocol.Location{ +- URI: "file:///other", +- Range: protocol.Range{ +- Start: protocol.Position{Line: 3, Character: 6}, +- End: protocol.Position{Line: 4, Character: 9}, +- }, +- }, +- Message: "psst, over here", +- }, +- }, +- +- // Fields below are used internally to generate quick fixes. They aren't +- // part of the LSP spec and don't leave the server. +- SuggestedFixes: []source.SuggestedFix{ +- { +- Title: "fix it!", +- Edits: map[span.URI][]protocol.TextEdit{ +- "file:///foo": {{ +- Range: protocol.Range{ +- Start: protocol.Position{Line: 4, Character: 2}, +- End: protocol.Position{Line: 6, Character: 7}, +- }, +- NewText: "abc", +- }}, +- "file:///other": {{ +- Range: protocol.Range{ +- Start: protocol.Position{Line: 4, Character: 2}, +- End: protocol.Position{Line: 6, Character: 7}, +- }, +- NewText: "!@#!", +- }}, +- }, +- Command: &protocol.Command{ +- Title: "run a command", +- Command: "gopls.fix", +- Arguments: []json.RawMessage{json.RawMessage(`{"a":1}`)}, +- }, +- ActionKind: protocol.QuickFix, +- }, +- }, +- }, +- { +- URI: "file//bar", +- // other fields tested above +- }, +- } +- +- data := encodeDiagnostics(diags) +- diags2 := decodeDiagnostics(data) +- +- if diff := cmp.Diff(diags, diags2); diff != "" { +- t.Errorf("decoded diagnostics do not match (-original +decoded):\n%s", diff) +- } +-} +diff -urN a/gopls/internal/lsp/cache/filemap.go b/gopls/internal/lsp/cache/filemap.go +--- a/gopls/internal/lsp/cache/filemap.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cache/filemap.go 1970-01-01 08:00:00 +@@ -1,151 +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 cache +- +-import ( +- "path/filepath" +- +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/persistent" +-) +- +-// A fileMap maps files in the snapshot, with some additional bookkeeping: +-// It keeps track of overlays as well as directories containing any observed +-// file. +-type fileMap struct { +- files *persistent.Map[span.URI, source.FileHandle] +- overlays *persistent.Map[span.URI, *Overlay] // the subset of files that are overlays +- dirs *persistent.Set[string] // all dirs containing files; if nil, dirs have not been initialized +-} +- +-func newFileMap() *fileMap { +- return &fileMap{ +- files: new(persistent.Map[span.URI, source.FileHandle]), +- overlays: new(persistent.Map[span.URI, *Overlay]), +- dirs: new(persistent.Set[string]), +- } +-} +- +-// Clone creates a copy of the fileMap, incorporating the changes specified by +-// the changes map. +-func (m *fileMap) Clone(changes map[span.URI]source.FileHandle) *fileMap { +- m2 := &fileMap{ +- files: m.files.Clone(), +- overlays: m.overlays.Clone(), +- } +- if m.dirs != nil { +- m2.dirs = m.dirs.Clone() +- } +- +- // Handle file changes. +- // +- // Note, we can't simply delete the file unconditionally and let it be +- // re-read by the snapshot, as (1) the snapshot must always observe all +- // overlays, and (2) deleting a file forces directories to be reevaluated, as +- // it may be the last file in a directory. We want to avoid that work in the +- // common case where a file has simply changed. +- // +- // For that reason, we also do this in two passes, processing deletions +- // first, as a set before a deletion would result in pointless work. +- for uri, fh := range changes { +- if !fileExists(fh) { +- m2.Delete(uri) +- } +- } +- for uri, fh := range changes { +- if fileExists(fh) { +- m2.Set(uri, fh) +- } +- } +- return m2 +-} +- +-func (m *fileMap) Destroy() { +- m.files.Destroy() +- m.overlays.Destroy() +- if m.dirs != nil { +- m.dirs.Destroy() +- } +-} +- +-// Get returns the file handle mapped by the given key, or (nil, false) if the +-// key is not present. +-func (m *fileMap) Get(key span.URI) (source.FileHandle, bool) { +- return m.files.Get(key) +-} +- +-// Range calls f for each (uri, fh) in the map. +-func (m *fileMap) Range(f func(uri span.URI, fh source.FileHandle)) { +- m.files.Range(f) +-} +- +-// Set stores the given file handle for key, updating overlays and directories +-// accordingly. +-func (m *fileMap) Set(key span.URI, fh source.FileHandle) { +- m.files.Set(key, fh, nil) +- +- // update overlays +- if o, ok := fh.(*Overlay); ok { +- m.overlays.Set(key, o, nil) +- } else { +- // Setting a non-overlay must delete the corresponding overlay, to preserve +- // the accuracy of the overlay set. +- m.overlays.Delete(key) +- } +- +- // update dirs, if they have been computed +- if m.dirs != nil { +- m.addDirs(key) +- } +-} +- +-// addDirs adds all directories containing u to the dirs set. +-func (m *fileMap) addDirs(u span.URI) { +- dir := filepath.Dir(u.Filename()) +- for dir != "" && !m.dirs.Contains(dir) { +- m.dirs.Add(dir) +- dir = filepath.Dir(dir) +- } +-} +- +-// Delete removes a file from the map, and updates overlays and dirs +-// accordingly. +-func (m *fileMap) Delete(key span.URI) { +- m.files.Delete(key) +- m.overlays.Delete(key) +- +- // Deleting a file may cause the set of dirs to shrink; therefore we must +- // re-evaluate the dir set. +- // +- // Do this lazily, to avoid work if there are multiple deletions in a row. +- if m.dirs != nil { +- m.dirs.Destroy() +- m.dirs = nil +- } +-} +- +-// Overlays returns a new unordered array of overlay files. +-func (m *fileMap) Overlays() []*Overlay { +- var overlays []*Overlay +- m.overlays.Range(func(_ span.URI, o *Overlay) { +- overlays = append(overlays, o) +- }) +- return overlays +-} +- +-// Dirs reports returns the set of dirs observed by the fileMap. +-// +-// This operation mutates the fileMap. +-// The result must not be mutated by the caller. +-func (m *fileMap) Dirs() *persistent.Set[string] { +- if m.dirs == nil { +- m.dirs = new(persistent.Set[string]) +- m.files.Range(func(u span.URI, _ source.FileHandle) { +- m.addDirs(u) +- }) +- } +- return m.dirs +-} +diff -urN a/gopls/internal/lsp/cache/filemap_test.go b/gopls/internal/lsp/cache/filemap_test.go +--- a/gopls/internal/lsp/cache/filemap_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cache/filemap_test.go 1970-01-01 08:00:00 +@@ -1,108 +0,0 @@ +-// Copyright 2023 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 cache +- +-import ( +- "path/filepath" +- "sort" +- "strings" +- "testing" +- +- "github.com/google/go-cmp/cmp" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" +-) +- +-func TestFileMap(t *testing.T) { +- const ( +- set = iota +- del +- ) +- type op struct { +- op int // set or remove +- path string +- overlay bool +- } +- tests := []struct { +- label string +- ops []op +- wantFiles []string +- wantOverlays []string +- wantDirs []string +- }{ +- {"empty", nil, nil, nil, nil}, +- {"singleton", []op{ +- {set, "/a/b", false}, +- }, []string{"/a/b"}, nil, []string{"/", "/a"}}, +- {"overlay", []op{ +- {set, "/a/b", true}, +- }, []string{"/a/b"}, []string{"/a/b"}, []string{"/", "/a"}}, +- {"replace overlay", []op{ +- {set, "/a/b", true}, +- {set, "/a/b", false}, +- }, []string{"/a/b"}, nil, []string{"/", "/a"}}, +- {"multi dir", []op{ +- {set, "/a/b", false}, +- {set, "/c/d", false}, +- }, []string{"/a/b", "/c/d"}, nil, []string{"/", "/a", "/c"}}, +- {"empty dir", []op{ +- {set, "/a/b", false}, +- {set, "/c/d", false}, +- {del, "/a/b", false}, +- }, []string{"/c/d"}, nil, []string{"/", "/c"}}, +- } +- +- // Normalize paths for windows compatibility. +- normalize := func(path string) string { +- return strings.TrimPrefix(filepath.ToSlash(path), "C:") // the span packages adds 'C:' +- } +- +- for _, test := range tests { +- t.Run(test.label, func(t *testing.T) { +- m := newFileMap() +- for _, op := range test.ops { +- uri := span.URIFromPath(filepath.FromSlash(op.path)) +- switch op.op { +- case set: +- var fh source.FileHandle +- if op.overlay { +- fh = &Overlay{uri: uri} +- } else { +- fh = &DiskFile{uri: uri} +- } +- m.Set(uri, fh) +- case del: +- m.Delete(uri) +- } +- } +- +- var gotFiles []string +- m.Range(func(uri span.URI, _ source.FileHandle) { +- gotFiles = append(gotFiles, normalize(uri.Filename())) +- }) +- sort.Strings(gotFiles) +- if diff := cmp.Diff(test.wantFiles, gotFiles); diff != "" { +- t.Errorf("Files mismatch (-want +got):\n%s", diff) +- } +- +- var gotOverlays []string +- for _, o := range m.Overlays() { +- gotOverlays = append(gotOverlays, normalize(o.URI().Filename())) +- } +- if diff := cmp.Diff(test.wantOverlays, gotOverlays); diff != "" { +- t.Errorf("Overlays mismatch (-want +got):\n%s", diff) +- } +- +- var gotDirs []string +- m.Dirs().Range(func(dir string) { +- gotDirs = append(gotDirs, normalize(dir)) +- }) +- sort.Strings(gotDirs) +- if diff := cmp.Diff(test.wantDirs, gotDirs); diff != "" { +- t.Errorf("Dirs mismatch (-want +got):\n%s", diff) +- } +- }) +- } +-} diff -urN a/gopls/internal/lsp/cache/fs_memoized.go b/gopls/internal/lsp/cache/fs_memoized.go --- a/gopls/internal/lsp/cache/fs_memoized.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/fs_memoized.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,149 +0,0 @@ ++++ b/gopls/internal/lsp/cache/fs_memoized.go 1970-01-01 08:00:00 +@@ -1,167 +0,0 @@ -// Copyright 2023 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. @@ -15239,10 +17957,6 @@ diff -urN a/gopls/internal/lsp/cache/fs_memoized.go b/gopls/internal/lsp/cache/f - filesByID map[robustio.FileID][]*DiskFile -} - --func newMemoizedFS() *memoizedFS { -- return &memoizedFS{filesByID: make(map[robustio.FileID][]*DiskFile)} --} -- -// A DiskFile is a file on the filesystem, or a failure to read one. -// It implements the source.FileHandle interface. -type DiskFile struct { @@ -15262,12 +17976,12 @@ diff -urN a/gopls/internal/lsp/cache/fs_memoized.go b/gopls/internal/lsp/cache/f - } -} - --func (h *DiskFile) Saved() bool { return true } --func (h *DiskFile) Version() int32 { return 0 } --func (h *DiskFile) Read() ([]byte, error) { return h.content, h.err } +-func (h *DiskFile) SameContentsOnDisk() bool { return true } +-func (h *DiskFile) Version() int32 { return 0 } +-func (h *DiskFile) Content() ([]byte, error) { return h.content, h.err } - --// GetFile stats and (maybe) reads the file, updates the cache, and returns it. --func (fs *memoizedFS) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) { +-// ReadFile stats and (maybe) reads the file, updates the cache, and returns it. +-func (fs *memoizedFS) ReadFile(ctx context.Context, uri span.URI) (source.FileHandle, error) { - id, mtime, err := robustio.GetFileID(uri.Filename()) - if err != nil { - // file does not exist @@ -15328,6 +18042,28 @@ diff -urN a/gopls/internal/lsp/cache/fs_memoized.go b/gopls/internal/lsp/cache/f - return fh, nil -} - +-// fileStats returns information about the set of files stored in fs. It is +-// intended for debugging only. +-func (fs *memoizedFS) fileStats() (files, largest, errs int) { +- fs.mu.Lock() +- defer fs.mu.Unlock() +- +- files = len(fs.filesByID) +- largest = 0 +- errs = 0 +- +- for _, files := range fs.filesByID { +- rep := files[0] +- if len(rep.content) > largest { +- largest = len(rep.content) +- } +- if rep.err != nil { +- errs++ +- } +- } +- return files, largest, errs +-} +- -// ioLimit limits the number of parallel file reads per process. -var ioLimit = make(chan struct{}, 128) - @@ -15361,7 +18097,7 @@ diff -urN a/gopls/internal/lsp/cache/fs_memoized.go b/gopls/internal/lsp/cache/f -} diff -urN a/gopls/internal/lsp/cache/fs_overlay.go b/gopls/internal/lsp/cache/fs_overlay.go --- a/gopls/internal/lsp/cache/fs_overlay.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/fs_overlay.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cache/fs_overlay.go 1970-01-01 08:00:00 @@ -1,78 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -15404,14 +18140,14 @@ diff -urN a/gopls/internal/lsp/cache/fs_overlay.go b/gopls/internal/lsp/cache/fs - return overlays -} - --func (fs *overlayFS) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) { +-func (fs *overlayFS) ReadFile(ctx context.Context, uri span.URI) (source.FileHandle, error) { - fs.mu.Lock() - overlay, ok := fs.overlays[uri] - fs.mu.Unlock() - if ok { - return overlay, nil - } -- return fs.delegate.GetFile(ctx, uri) +- return fs.delegate.ReadFile(ctx, uri) -} - -// An Overlay is a file open in the editor. It may have unsaved edits. @@ -15437,14 +18173,14 @@ diff -urN a/gopls/internal/lsp/cache/fs_overlay.go b/gopls/internal/lsp/cache/fs - } -} - --func (o *Overlay) Read() ([]byte, error) { return o.content, nil } --func (o *Overlay) Version() int32 { return o.version } --func (o *Overlay) Saved() bool { return o.saved } --func (o *Overlay) Kind() source.FileKind { return o.kind } +-func (o *Overlay) Content() ([]byte, error) { return o.content, nil } +-func (o *Overlay) Version() int32 { return o.version } +-func (o *Overlay) SameContentsOnDisk() bool { return o.saved } +-func (o *Overlay) Kind() source.FileKind { return o.kind } diff -urN a/gopls/internal/lsp/cache/graph.go b/gopls/internal/lsp/cache/graph.go --- a/gopls/internal/lsp/cache/graph.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/graph.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,131 +0,0 @@ ++++ b/gopls/internal/lsp/cache/graph.go 1970-01-01 08:00:00 +@@ -1,364 +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. @@ -15454,6 +18190,8 @@ diff -urN a/gopls/internal/lsp/cache/graph.go b/gopls/internal/lsp/cache/graph.g -import ( - "sort" - +- "golang.org/x/tools/go/packages" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" -) @@ -15469,6 +18207,8 @@ diff -urN a/gopls/internal/lsp/cache/graph.go b/gopls/internal/lsp/cache/graph.g - - // ids maps file URIs to package IDs, sorted by (!valid, cli, packageID). - // A single file may belong to multiple packages due to tests packages. +- // +- // Invariant: all IDs present in the ids map exist in the metadata map. - ids map[span.URI][]PackageID -} - @@ -15478,43 +18218,46 @@ diff -urN a/gopls/internal/lsp/cache/graph.go b/gopls/internal/lsp/cache/graph.g -} - -// Clone creates a new metadataGraph, applying the given updates to the --// receiver. +-// receiver. A nil map value represents a deletion. -func (g *metadataGraph) Clone(updates map[PackageID]*source.Metadata) *metadataGraph { - if len(updates) == 0 { - // Optimization: since the graph is immutable, we can return the receiver. - return g - } -- result := &metadataGraph{metadata: make(map[PackageID]*source.Metadata, len(g.metadata))} -- // Copy metadata. +- +- // Copy metadata map then apply updates. +- metadata := make(map[PackageID]*source.Metadata, len(g.metadata)) - for id, m := range g.metadata { -- result.metadata[id] = m +- metadata[id] = m - } - for id, m := range updates { - if m == nil { -- delete(result.metadata, id) +- delete(metadata, id) - } else { -- result.metadata[id] = m +- metadata[id] = m - } - } -- result.build() -- return result +- +- // Break import cycles involving updated nodes. +- breakImportCycles(metadata, updates) +- +- return newMetadataGraph(metadata) -} - --// build constructs g.importedBy and g.uris from g.metadata. --// --// TODO(rfindley): we should enforce that the graph is acyclic here. --func (g *metadataGraph) build() { +-// newMetadataGraph returns a new metadataGraph, +-// deriving relations from the specified metadata. +-func newMetadataGraph(metadata map[PackageID]*source.Metadata) *metadataGraph { - // Build the import graph. -- g.importedBy = make(map[PackageID][]PackageID) -- for id, m := range g.metadata { +- importedBy := make(map[PackageID][]PackageID) +- for id, m := range metadata { - for _, depID := range m.DepsByPkgPath { -- g.importedBy[depID] = append(g.importedBy[depID], id) +- importedBy[depID] = append(importedBy[depID], id) - } - } - - // Collect file associations. -- g.ids = make(map[span.URI][]PackageID) -- for id, m := range g.metadata { +- uriIDs := make(map[span.URI][]PackageID) +- for id, m := range metadata { - uris := map[span.URI]struct{}{} - for _, uri := range m.CompiledGoFiles { - uris[uri] = struct{}{} @@ -15523,12 +18266,12 @@ diff -urN a/gopls/internal/lsp/cache/graph.go b/gopls/internal/lsp/cache/graph.g - uris[uri] = struct{}{} - } - for uri := range uris { -- g.ids[uri] = append(g.ids[uri], id) +- uriIDs[uri] = append(uriIDs[uri], id) - } - } - - // Sort and filter file associations. -- for uri, ids := range g.ids { +- for uri, ids := range uriIDs { - sort.Slice(ids, func(i, j int) bool { - cli := source.IsCommandLineArguments(ids[i]) - clj := source.IsCommandLineArguments(ids[j]) @@ -15550,11 +18293,17 @@ diff -urN a/gopls/internal/lsp/cache/graph.go b/gopls/internal/lsp/cache/graph.g - // If we've seen *anything* prior to command-line arguments package, take - // it. Note that ids[0] may itself be command-line-arguments. - if i > 0 && source.IsCommandLineArguments(id) { -- g.ids[uri] = ids[:i] +- uriIDs[uri] = ids[:i] - break - } - } - } +- +- return &metadataGraph{ +- metadata: metadata, +- importedBy: importedBy, +- ids: uriIDs, +- } -} - -// reverseReflexiveTransitiveClosure returns a new mapping containing the @@ -15576,10 +18325,230 @@ diff -urN a/gopls/internal/lsp/cache/graph.go b/gopls/internal/lsp/cache/graph.g - visitAll(ids) - return seen -} +- +-// breakImportCycles breaks import cycles in the metadata by deleting +-// Deps* edges. It modifies only metadata present in the 'updates' +-// subset. This function has an internal test. +-func breakImportCycles(metadata, updates map[PackageID]*source.Metadata) { +- // 'go list' should never report a cycle without flagging it +- // as such, but we're extra cautious since we're combining +- // information from multiple runs of 'go list'. Also, Bazel +- // may silently report cycles. +- cycles := detectImportCycles(metadata, updates) +- if len(cycles) > 0 { +- // There were cycles (uncommon). Break them. +- // +- // The naive way to break cycles would be to perform a +- // depth-first traversal and to detect and delete +- // cycle-forming edges as we encounter them. +- // However, we're not allowed to modify the existing +- // Metadata records, so we can only break edges out of +- // the 'updates' subset. +- // +- // Another possibility would be to delete not the +- // cycle forming edge but the topmost edge on the +- // stack whose tail is an updated node. +- // However, this would require that we retroactively +- // undo all the effects of the traversals that +- // occurred since that edge was pushed on the stack. +- // +- // We use a simpler scheme: we compute the set of cycles. +- // All cyclic paths necessarily involve at least one +- // updated node, so it is sufficient to break all +- // edges from each updated node to other members of +- // the strong component. +- // +- // This may result in the deletion of dominating +- // edges, causing some dependencies to appear +- // spuriously unreachable. Consider A <-> B -> C +- // where updates={A,B}. The cycle is {A,B} so the +- // algorithm will break both A->B and B->A, causing +- // A to no longer depend on B or C. +- // +- // But that's ok: any error in Metadata.Errors is +- // conservatively assumed by snapshot.clone to be a +- // potential import cycle error, and causes special +- // invalidation so that if B later drops its +- // cycle-forming import of A, both A and B will be +- // invalidated. +- for _, cycle := range cycles { +- cyclic := make(map[PackageID]bool) +- for _, m := range cycle { +- cyclic[m.ID] = true +- } +- for id := range cyclic { +- if m := updates[id]; m != nil { +- for path, depID := range m.DepsByImpPath { +- if cyclic[depID] { +- delete(m.DepsByImpPath, path) +- } +- } +- for path, depID := range m.DepsByPkgPath { +- if cyclic[depID] { +- delete(m.DepsByPkgPath, path) +- } +- } +- +- // Set m.Errors to enable special +- // invalidation logic in snapshot.clone. +- if len(m.Errors) == 0 { +- m.Errors = []packages.Error{{ +- Msg: "detected import cycle", +- Kind: packages.ListError, +- }} +- } +- } +- } +- } +- +- // double-check when debugging +- if false { +- if cycles := detectImportCycles(metadata, updates); len(cycles) > 0 { +- bug.Reportf("unbroken cycle: %v", cycles) +- } +- } +- } +-} +- +-// detectImportCycles reports cycles in the metadata graph. It returns a new +-// unordered array of all cycles (nontrivial strong components) in the +-// metadata graph reachable from a non-nil 'updates' value. +-func detectImportCycles(metadata, updates map[PackageID]*source.Metadata) [][]*source.Metadata { +- // We use the depth-first algorithm of Tarjan. +- // https://doi.org/10.1137/0201010 +- // +- // TODO(adonovan): when we can use generics, consider factoring +- // in common with the other implementation of Tarjan (in typerefs), +- // abstracting over the node and edge representation. +- +- // A node wraps a Metadata with its working state. +- // (Unfortunately we can't intrude on shared Metadata.) +- type node struct { +- rep *node +- m *source.Metadata +- index, lowlink int32 +- scc int8 // TODO(adonovan): opt: cram these 1.5 bits into previous word +- } +- nodes := make(map[PackageID]*node, len(metadata)) +- nodeOf := func(id PackageID) *node { +- n, ok := nodes[id] +- if !ok { +- m := metadata[id] +- if m == nil { +- // Dangling import edge. +- // Not sure whether a go/packages driver ever +- // emits this, but create a dummy node in case. +- // Obviously it won't be part of any cycle. +- m = &source.Metadata{ID: id} +- } +- n = &node{m: m} +- n.rep = n +- nodes[id] = n +- } +- return n +- } +- +- // find returns the canonical node decl. +- // (The nodes form a disjoint set forest.) +- var find func(*node) *node +- find = func(n *node) *node { +- rep := n.rep +- if rep != n { +- rep = find(rep) +- n.rep = rep // simple path compression (no union-by-rank) +- } +- return rep +- } +- +- // global state +- var ( +- index int32 = 1 +- stack []*node +- sccs [][]*source.Metadata // set of nontrivial strongly connected components +- ) +- +- // visit implements the depth-first search of Tarjan's SCC algorithm +- // Precondition: x is canonical. +- var visit func(*node) +- visit = func(x *node) { +- x.index = index +- x.lowlink = index +- index++ +- +- stack = append(stack, x) // push +- x.scc = -1 +- +- for _, yid := range x.m.DepsByPkgPath { +- y := nodeOf(yid) +- // Loop invariant: x is canonical. +- y = find(y) +- if x == y { +- continue // nodes already combined (self-edges are impossible) +- } +- +- switch { +- case y.scc > 0: +- // y is already a collapsed SCC +- +- case y.scc < 0: +- // y is on the stack, and thus in the current SCC. +- if y.index < x.lowlink { +- x.lowlink = y.index +- } +- +- default: +- // y is unvisited; visit it now. +- visit(y) +- // Note: x and y are now non-canonical. +- x = find(x) +- if y.lowlink < x.lowlink { +- x.lowlink = y.lowlink +- } +- } +- } +- +- // Is x the root of an SCC? +- if x.lowlink == x.index { +- // Gather all metadata in the SCC (if nontrivial). +- var scc []*source.Metadata +- for { +- // Pop y from stack. +- i := len(stack) - 1 +- y := stack[i] +- stack = stack[:i] +- if x != y || scc != nil { +- scc = append(scc, y.m) +- } +- if x == y { +- break // complete +- } +- // x becomes y's canonical representative. +- y.rep = x +- } +- if scc != nil { +- sccs = append(sccs, scc) +- } +- x.scc = 1 +- } +- } +- +- // Visit only the updated nodes: +- // the existing metadata graph has no cycles, +- // so any new cycle must involve an updated node. +- for id, m := range updates { +- if m != nil { +- if n := nodeOf(id); n.index == 0 { // unvisited +- visit(n) +- } +- } +- } +- +- return sccs +-} diff -urN a/gopls/internal/lsp/cache/imports.go b/gopls/internal/lsp/cache/imports.go --- a/gopls/internal/lsp/cache/imports.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/imports.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,188 +0,0 @@ ++++ b/gopls/internal/lsp/cache/imports.go 1970-01-01 08:00:00 +@@ -1,182 +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. @@ -15611,16 +18580,12 @@ diff -urN a/gopls/internal/lsp/cache/imports.go b/gopls/internal/lsp/cache/impor - cachedModFileHash source.Hash - cachedBuildFlags []string - cachedDirectoryFilters []string -- -- // runOnce records whether runProcessEnvFunc has been called at least once. -- // This is necessary to avoid resetting state before the process env is -- // populated. -- // -- // TODO(rfindley): this shouldn't be necessary. -- runOnce bool -} - --func (s *importsState) runProcessEnvFunc(ctx context.Context, snapshot *snapshot, fn func(*imports.Options) error) error { +-func (s *importsState) runProcessEnvFunc(ctx context.Context, snapshot *snapshot, fn func(context.Context, *imports.Options) error) error { +- ctx, done := event.Start(ctx, "cache.importsState.runProcessEnvFunc") +- defer done() +- - s.mu.Lock() - defer s.mu.Unlock() - @@ -15631,7 +18596,7 @@ diff -urN a/gopls/internal/lsp/cache/imports.go b/gopls/internal/lsp/cache/impor - // TODO(rfindley): consider instead hashing on-disk modfiles here. - var modFileHash source.Hash - for m := range snapshot.workspaceModFiles { -- fh, err := snapshot.GetFile(ctx, m) +- fh, err := snapshot.ReadFile(ctx, m) - if err != nil { - return err - } @@ -15640,38 +18605,31 @@ diff -urN a/gopls/internal/lsp/cache/imports.go b/gopls/internal/lsp/cache/impor - - // view.goEnv is immutable -- changes make a new view. Options can change. - // We can't compare build flags directly because we may add -modfile. -- snapshot.view.optionsMu.Lock() -- localPrefix := snapshot.view.options.Local -- currentBuildFlags := snapshot.view.options.BuildFlags -- currentDirectoryFilters := snapshot.view.options.DirectoryFilters +- localPrefix := snapshot.options.Local +- currentBuildFlags := snapshot.options.BuildFlags +- currentDirectoryFilters := snapshot.options.DirectoryFilters - changed := !reflect.DeepEqual(currentBuildFlags, s.cachedBuildFlags) || -- snapshot.view.options.VerboseOutput != (s.processEnv.Logf != nil) || +- snapshot.options.VerboseOutput != (s.processEnv.Logf != nil) || - modFileHash != s.cachedModFileHash || -- !reflect.DeepEqual(snapshot.view.options.DirectoryFilters, s.cachedDirectoryFilters) -- snapshot.view.optionsMu.Unlock() +- !reflect.DeepEqual(snapshot.options.DirectoryFilters, s.cachedDirectoryFilters) - - // If anything relevant to imports has changed, clear caches and - // update the processEnv. Clearing caches blocks on any background - // scans. - if changed { -- // As a special case, skip cleanup the first time -- we haven't fully -- // initialized the environment yet and calling GetResolver will do -- // unnecessary work and potentially mess up the go.mod file. -- if s.runOnce { -- if resolver, err := s.processEnv.GetResolver(); err == nil { -- if modResolver, ok := resolver.(*imports.ModuleResolver); ok { -- modResolver.ClearForNewMod() -- } +- if err := populateProcessEnvFromSnapshot(ctx, s.processEnv, snapshot); err != nil { +- return err +- } +- +- if resolver, err := s.processEnv.GetResolver(); err == nil { +- if modResolver, ok := resolver.(*imports.ModuleResolver); ok { +- modResolver.ClearForNewMod() - } - } - - s.cachedModFileHash = modFileHash - s.cachedBuildFlags = currentBuildFlags - s.cachedDirectoryFilters = currentDirectoryFilters -- if err := s.populateProcessEnv(ctx, snapshot); err != nil { -- return err -- } -- s.runOnce = true - } - - // Run the user function. @@ -15687,7 +18645,7 @@ diff -urN a/gopls/internal/lsp/cache/imports.go b/gopls/internal/lsp/cache/impor - LocalPrefix: localPrefix, - } - -- if err := fn(opts); err != nil { +- if err := fn(ctx, opts); err != nil { - return err - } - @@ -15704,12 +18662,14 @@ diff -urN a/gopls/internal/lsp/cache/imports.go b/gopls/internal/lsp/cache/impor - return nil -} - --// populateProcessEnv sets the dynamically configurable fields for the view's --// process environment. Assumes that the caller is holding the s.view.importsMu. --func (s *importsState) populateProcessEnv(ctx context.Context, snapshot *snapshot) error { -- pe := s.processEnv +-// populateProcessEnvFromSnapshot sets the dynamically configurable fields for +-// the view's process environment. Assumes that the caller is holding the +-// importsState mutex. +-func populateProcessEnvFromSnapshot(ctx context.Context, pe *imports.ProcessEnv, snapshot *snapshot) error { +- ctx, done := event.Start(ctx, "cache.populateProcessEnvFromSnapshot") +- defer done() - -- if snapshot.view.Options().VerboseOutput { +- if snapshot.options.VerboseOutput { - pe.Logf = func(format string, args ...interface{}) { - event.Log(ctx, fmt.Sprintf(format, args...)) - } @@ -15724,7 +18684,7 @@ diff -urN a/gopls/internal/lsp/cache/imports.go b/gopls/internal/lsp/cache/impor - // and has led to memory leaks in the past, when the snapshot was - // unintentionally held past its lifetime. - _, inv, cleanupInvocation, err := snapshot.goCommandInvocation(ctx, source.LoadWorkspace, &gocommand.Invocation{ -- WorkingDir: snapshot.view.workingDir().Filename(), +- WorkingDir: snapshot.view.goCommandDir.Filename(), - }) - if err != nil { - return err @@ -15743,11 +18703,14 @@ diff -urN a/gopls/internal/lsp/cache/imports.go b/gopls/internal/lsp/cache/impor - // We don't actually use the invocation, so clean it up now. - cleanupInvocation() - // TODO(rfindley): should this simply be inv.WorkingDir? -- pe.WorkingDir = snapshot.view.workingDir().Filename() +- pe.WorkingDir = snapshot.view.goCommandDir.Filename() - return nil -} - -func (s *importsState) refreshProcessEnv() { +- ctx, done := event.Start(s.ctx, "cache.importsState.refreshProcessEnv") +- defer done() +- - start := time.Now() - - s.mu.Lock() @@ -15759,9 +18722,9 @@ diff -urN a/gopls/internal/lsp/cache/imports.go b/gopls/internal/lsp/cache/impor - - event.Log(s.ctx, "background imports cache refresh starting") - if err := imports.PrimeCache(context.Background(), env); err == nil { -- event.Log(s.ctx, fmt.Sprintf("background refresh finished after %v", time.Since(start))) +- event.Log(ctx, fmt.Sprintf("background refresh finished after %v", time.Since(start))) - } else { -- event.Log(s.ctx, fmt.Sprintf("background refresh finished after %v", time.Since(start)), keys.Err.Of(err)) +- event.Log(ctx, fmt.Sprintf("background refresh finished after %v", time.Since(start)), keys.Err.Of(err)) - } - s.mu.Lock() - s.cacheRefreshDuration = time.Since(start) @@ -15770,7 +18733,7 @@ diff -urN a/gopls/internal/lsp/cache/imports.go b/gopls/internal/lsp/cache/impor -} diff -urN a/gopls/internal/lsp/cache/keys.go b/gopls/internal/lsp/cache/keys.go --- a/gopls/internal/lsp/cache/keys.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/keys.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cache/keys.go 1970-01-01 08:00:00 @@ -1,52 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -15826,8 +18789,8 @@ diff -urN a/gopls/internal/lsp/cache/keys.go b/gopls/internal/lsp/cache/keys.go -} diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go --- a/gopls/internal/lsp/cache/load.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/load.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,782 +0,0 @@ ++++ b/gopls/internal/lsp/cache/load.go 1970-01-01 08:00:00 +@@ -1,762 +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. @@ -15846,10 +18809,10 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - "time" - - "golang.org/x/tools/go/packages" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/event/tag" - "golang.org/x/tools/internal/gocommand" @@ -15866,12 +18829,15 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go -// -// The resulting error may wrap the moduleErrorMap error type, representing -// errors associated with specific modules. +-// +-// If scopes contains a file scope there must be exactly one scope. -func (s *snapshot) load(ctx context.Context, allowNetwork bool, scopes ...loadScope) (err error) { - id := atomic.AddUint64(&loadID, 1) - eventName := fmt.Sprintf("go/packages.Load #%d", id) // unique name for logging - - var query []string - var containsDir bool // for logging +- var standalone bool // whether this is a load of a standalone file - - // Keep track of module query -> module path so that we can later correlate query - // errors with errors. @@ -15885,36 +18851,39 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - query = append(query, string(scope)) - - case fileLoadScope: +- // Given multiple scopes, the resulting load might contain inaccurate +- // information. For example go/packages returns at most one command-line +- // arguments package, and does not handle a combination of standalone +- // files and packages. - uri := span.URI(scope) +- if len(scopes) > 1 { +- panic(fmt.Sprintf("internal error: load called with multiple scopes when a file scope is present (file: %s)", uri)) +- } - fh := s.FindFile(uri) -- if fh == nil || s.View().FileKind(fh) != source.Go { +- if fh == nil || s.FileKind(fh) != source.Go { - // Don't try to load a file that doesn't exist, or isn't a go file. - continue - } -- contents, err := fh.Read() +- contents, err := fh.Content() - if err != nil { - continue - } -- if isStandaloneFile(contents, s.view.Options().StandaloneTags) { +- if isStandaloneFile(contents, s.options.StandaloneTags) { +- standalone = true - query = append(query, uri.Filename()) - } else { - query = append(query, fmt.Sprintf("file=%s", uri.Filename())) - } - - case moduleLoadScope: -- switch scope { -- case "std", "cmd": -- query = append(query, string(scope)) -- default: -- modQuery := fmt.Sprintf("%s/...", scope) -- query = append(query, modQuery) -- moduleQueries[modQuery] = string(scope) -- } +- modQuery := fmt.Sprintf("%s%c...", scope.dir, filepath.Separator) +- query = append(query, modQuery) +- moduleQueries[modQuery] = string(scope.modulePath) - - case viewLoadScope: - // If we are outside of GOPATH, a module, or some other known - // build system, don't load subdirectories. -- if !s.ValidBuildConfiguration() { +- if !s.validBuildConfiguration() { - query = append(query, "./") - } else { - query = append(query, "./...") @@ -15933,7 +18902,7 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - } - sort.Strings(query) // for determinism - -- ctx, done := event.Start(ctx, "cache.view.load", tag.Query.Of(query)) +- ctx, done := event.Start(ctx, "cache.snapshot.load", tag.Query.Of(query)) - defer done() - - flags := source.LoadWorkspace @@ -15941,7 +18910,7 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - flags |= source.AllowNetwork - } - _, inv, cleanup, err := s.goCommandInvocation(ctx, flags, &gocommand.Invocation{ -- WorkingDir: s.view.workingDir().Filename(), +- WorkingDir: s.view.goCommandDir.Filename(), - }) - if err != nil { - return err @@ -15979,8 +18948,12 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - return fmt.Errorf("packages.Load error: %w", err) - } - +- if standalone && len(pkgs) > 1 { +- return bug.Errorf("internal error: go/packages returned multiple packages for standalone file") +- } +- - moduleErrs := make(map[string][]packages.Error) // module path -> errors -- filterFunc := s.view.filterFunc() +- filterFunc := s.filterFunc() - newMetadata := make(map[PackageID]*source.Metadata) - for _, pkg := range pkgs { - // The Go command returns synthetic list results for module queries that @@ -15998,7 +18971,7 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - continue - } - -- if !containsDir || s.view.Options().VerboseOutput { +- if !containsDir || s.options.VerboseOutput { - event.Log(ctx, eventName, append( - source.SnapshotLabels(s), - tag.Package.Of(pkg.ID), @@ -16031,31 +19004,36 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - if allFilesExcluded(pkg.GoFiles, filterFunc) { - continue - } -- if err := buildMetadata(ctx, pkg, cfg, query, newMetadata, nil); err != nil { -- return err -- } +- buildMetadata(newMetadata, pkg, cfg.Dir, standalone) - } - - s.mu.Lock() - +- // Assert the invariant s.packages.Get(id).m == s.meta.metadata[id]. +- s.packages.Range(func(id PackageID, ph *packageHandle) { +- if s.meta.metadata[id] != ph.m { +- panic("inconsistent metadata") +- } +- }) +- - // Compute the minimal metadata updates (for Clone) -- // required to preserve this invariant: -- // for all id, s.packages.Get(id).m == s.meta.metadata[id]. +- // required to preserve the above invariant. +- var files []span.URI // files to preload +- seenFiles := make(map[span.URI]bool) - updates := make(map[PackageID]*source.Metadata) - for _, m := range newMetadata { - if existing := s.meta.metadata[m.ID]; existing == nil { +- // Record any new files we should pre-load. +- for _, uri := range m.CompiledGoFiles { +- if !seenFiles[uri] { +- seenFiles[uri] = true +- files = append(files, uri) +- } +- } - updates[m.ID] = m - delete(s.shouldLoad, m.ID) - } - } -- // Assert the invariant. -- s.packages.Range(func(k, v interface{}) { -- id, ph := k.(PackageID), v.(*packageHandle) -- if s.meta.metadata[id] != ph.m { -- // TODO(adonovan): upgrade to unconditional panic after Jan 2023. -- bug.Reportf("inconsistent metadata") -- } -- }) - - event.Log(ctx, fmt.Sprintf("%s: updating metadata for %d packages", eventName, len(updates))) - @@ -16065,31 +19043,26 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - meta := s.meta.Clone(updates) - workspacePackages := computeWorkspacePackagesLocked(s, meta) - for _, update := range updates { -- if err := computeLoadDiagnostics(ctx, update, meta, lockedSnapshot{s}, workspacePackages); err != nil { -- return err -- } +- computeLoadDiagnostics(ctx, update, meta, lockedSnapshot{s}, workspacePackages) - } - s.meta = meta - s.workspacePackages = workspacePackages - s.resetActivePackagesLocked() - -- s.dumpWorkspace("load") - s.mu.Unlock() - -- // Recompute the workspace package handle for any packages we invalidated. +- // Opt: preLoad files in parallel. - // -- // This is (putatively) an optimization since handle construction prefetches -- // the content of all Go source files. -- // -- // However, one necessary side effect of this operation is that we are -- // guaranteed to visit all package files during load. This is required for -- // e.g. determining the set of directories to watch. +- // Requesting files in batch optimizes the underlying filesystem reads. +- // However, this is also currently necessary for correctness: populating all +- // files in the snapshot is necessary for certain operations that rely on the +- // completeness of the file map, e.g. computing the set of directories to +- // watch. - // - // TODO(rfindley, golang/go#57558): determine the set of directories based on -- // loaded packages, and skip this precomputation. -- for _, m := range updates { -- s.buildPackageHandle(ctx, m.ID) // ignore error -- } +- // loaded packages, so that reading files here is not necessary for +- // correctness. +- s.preloadFiles(ctx, files) - - if len(moduleErrs) > 0 { - return &moduleErrorMap{moduleErrs} @@ -16147,12 +19120,12 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - - // Apply diagnostics about the workspace configuration to relevant open - // files. -- openFiles := s.openFiles() +- openFiles := s.overlays() - - // If the snapshot does not have a valid build configuration, it may be - // that the user has opened a directory that contains multiple modules. - // Check for that an warn about it. -- if !s.ValidBuildConfiguration() { +- if !s.validBuildConfiguration() { - var msg string - if s.view.goversion >= 18 { - msg = `gopls was not able to find modules in your workspace. @@ -16171,62 +19144,15 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - return fmt.Errorf(msg), s.applyCriticalErrorToFiles(ctx, msg, openFiles) - } - -- // If the user has one active go.mod file, they may still be editing files -- // in nested modules. Check the module of each open file and add warnings -- // that the nested module must be opened as a workspace folder. -- if len(s.workspaceModFiles) == 1 { -- // Get the active root go.mod file to compare against. -- var rootMod string -- for uri := range s.workspaceModFiles { -- rootMod = uri.Filename() -- } -- rootDir := filepath.Dir(rootMod) -- nestedModules := make(map[string][]source.FileHandle) -- for _, fh := range openFiles { -- mod, err := findRootPattern(ctx, filepath.Dir(fh.URI().Filename()), "go.mod", s) -- if err != nil { -- if ctx.Err() != nil { -- return ctx.Err(), nil -- } -- continue -- } -- if mod == "" { -- continue -- } -- if mod != rootMod && source.InDir(rootDir, mod) { -- modDir := filepath.Dir(mod) -- nestedModules[modDir] = append(nestedModules[modDir], fh) -- } -- } -- var multiModuleMsg string -- if s.view.goversion >= 18 { -- multiModuleMsg = `To work on multiple modules at once, please use a go.work file. --See https://github.com/golang/tools/blob/master/gopls/doc/workspace.md for more information on using workspaces.` -- } else { -- multiModuleMsg = `To work on multiple modules at once, please upgrade to Go 1.18 and use a go.work file. --See https://github.com/golang/tools/blob/master/gopls/doc/workspace.md for more information on using workspaces.` -- } -- // Add a diagnostic to each file in a nested module to mark it as -- // "orphaned". Don't show a general diagnostic in the progress bar, -- // because the user may still want to edit a file in a nested module. -- var srcDiags []*source.Diagnostic -- for modDir, uris := range nestedModules { -- msg := fmt.Sprintf("This file is in %s, which is a nested module in the %s module.\n%s", modDir, rootMod, multiModuleMsg) -- srcDiags = append(srcDiags, s.applyCriticalErrorToFiles(ctx, msg, uris)...) -- } -- if len(srcDiags) != 0 { -- return fmt.Errorf("You have opened a nested module.\n%s", multiModuleMsg), srcDiags -- } -- } - return nil, nil -} - --func (s *snapshot) applyCriticalErrorToFiles(ctx context.Context, msg string, files []source.FileHandle) []*source.Diagnostic { +-func (s *snapshot) applyCriticalErrorToFiles(ctx context.Context, msg string, files []*Overlay) []*source.Diagnostic { - var srcDiags []*source.Diagnostic - for _, fh := range files { - // Place the diagnostics on the package or module declarations. - var rng protocol.Range -- switch s.view.FileKind(fh) { +- switch s.FileKind(fh) { - case source.Go: - if pgf, err := s.ParseGo(ctx, fh, source.ParseHeader); err == nil { - // Check that we have a valid `package foo` range to use for positioning the error. @@ -16255,35 +19181,29 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go -// buildMetadata populates the updates map with metadata updates to -// apply, based on the given pkg. It recurs through pkg.Imports to ensure that -// metadata exists for all dependencies. --func buildMetadata(ctx context.Context, pkg *packages.Package, cfg *packages.Config, query []string, updates map[PackageID]*source.Metadata, path []PackageID) error { +-func buildMetadata(updates map[PackageID]*source.Metadata, pkg *packages.Package, loadDir string, standalone bool) { - // Allow for multiple ad-hoc packages in the workspace (see #47584). - pkgPath := PackagePath(pkg.PkgPath) - id := PackageID(pkg.ID) +- - if source.IsCommandLineArguments(id) { -- suffix := ":" + strings.Join(query, ",") +- if len(pkg.CompiledGoFiles) != 1 { +- bug.Reportf("unexpected files in command-line-arguments package: %v", pkg.CompiledGoFiles) +- return +- } +- suffix := pkg.CompiledGoFiles[0] - id = PackageID(pkg.ID + suffix) - pkgPath = PackagePath(pkg.PkgPath + suffix) - } - +- // Duplicate? - if _, ok := updates[id]; ok { -- // If we've already seen this dependency, there may be an import cycle, or -- // we may have reached the same package transitively via distinct paths. -- // Check the path to confirm. -- -- // TODO(rfindley): this doesn't look sufficient. Any single piece of new -- // metadata could theoretically introduce import cycles in the metadata -- // graph. What's the point of this limited check here (and is it even -- // possible to get an import cycle in data from go/packages)? Consider -- // simply returning, so that this function need not return an error. -- // -- // We should consider doing a more complete guard against import cycles -- // elsewhere. -- for _, prev := range path { -- if prev == id { -- return fmt.Errorf("import cycle detected: %q", id) -- } -- } -- return nil +- // A package was encountered twice due to shared +- // subgraphs (common) or cycles (rare). Although "go +- // list" usually breaks cycles, we don't rely on it. +- // breakImportCycles in metadataGraph.Clone takes care +- // of it later. +- return - } - - // Recreate the metadata rather than reusing it to avoid locking. @@ -16293,10 +19213,11 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - Name: PackageName(pkg.Name), - ForTest: PackagePath(packagesinternal.GetForTest(pkg)), - TypesSizes: pkg.TypesSizes, -- LoadDir: cfg.Dir, +- LoadDir: loadDir, - Module: pkg.Module, - Errors: pkg.Errors, - DepsErrors: packagesinternal.GetDepsErrors(pkg), +- Standalone: standalone, - } - - updates[id] = m @@ -16309,6 +19230,10 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - uri := span.URIFromPath(filename) - m.GoFiles = append(m.GoFiles, uri) - } +- for _, filename := range pkg.IgnoredFiles { +- uri := span.URIFromPath(filename) +- m.IgnoredFiles = append(m.IgnoredFiles, uri) +- } - - depsByImpPath := make(map[ImportPath]PackageID) - depsByPkgPath := make(map[PackagePath]PackageID) @@ -16386,25 +19311,42 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - continue - } - +- // Don't record self-import edges. +- // (This simplifies metadataGraph's cycle check.) +- if PackageID(imported.ID) == id { +- if len(pkg.Errors) == 0 { +- bug.Reportf("self-import without error in package %s", id) +- } +- continue +- } +- +- buildMetadata(updates, imported, loadDir, false) // only top level packages can be standalone +- +- // Don't record edges to packages with no name, as they cause trouble for +- // the importer (golang/go#60952). +- // +- // However, we do want to insert these packages into the update map +- // (buildMetadata above), so that we get type-checking diagnostics for the +- // invalid packages. +- if imported.Name == "" { +- depsByImpPath[importPath] = "" // missing +- continue +- } +- - depsByImpPath[importPath] = PackageID(imported.ID) - depsByPkgPath[PackagePath(imported.PkgPath)] = PackageID(imported.ID) -- if err := buildMetadata(ctx, imported, cfg, query, updates, append(path, id)); err != nil { -- event.Error(ctx, "error in dependency", err) -- } - } - m.DepsByImpPath = depsByImpPath - m.DepsByPkgPath = depsByPkgPath - - // m.Diagnostics is set later in the loading pass, using - // computeLoadDiagnostics. -- -- return nil -} - -// computeLoadDiagnostics computes and sets m.Diagnostics for the given metadata m. -// -// It should only be called during metadata construction in snapshot.load. --func computeLoadDiagnostics(ctx context.Context, m *source.Metadata, meta *metadataGraph, fs source.FileSource, workspacePackages map[PackageID]PackagePath) error { +-func computeLoadDiagnostics(ctx context.Context, m *source.Metadata, meta *metadataGraph, fs source.FileSource, workspacePackages map[PackageID]PackagePath) { - for _, packagesErr := range m.Errors { - // Filter out parse errors from go list. We'll get them when we - // actually parse, and buggy overlay support may generate spurious @@ -16432,10 +19374,8 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - // not normally fail. - event.Error(ctx, "unable to compute deps errors", err, tag.Package.Of(string(m.ID))) - } -- return nil - } - m.Diagnostics = append(m.Diagnostics, depsDiags...) -- return nil -} - -// containsPackageLocked reports whether p is a workspace package for the @@ -16469,7 +19409,7 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - uris[uri] = struct{}{} - } - -- filterFunc := s.view.filterFunc() +- filterFunc := s.filterFunc() - for uri := range uris { - // Don't use view.contains here. go.work files may include modules - // outside of the workspace folder. @@ -16497,7 +19437,8 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - } - - for uri := range uris { -- if s.isOpenLocked(uri) { +- fh, _ := s.files.Get(uri) +- if _, open := fh.(*Overlay); open { - return true - } - } @@ -16523,15 +19464,16 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - - // The package's files are in this view. It may be a workspace package. - // Vendored packages are not likely to be interesting to the user. -- if !strings.Contains(string(uri), "/vendor/") && s.view.contains(uri) { +- if !strings.Contains(string(uri), "/vendor/") && s.contains(uri) { - return true - } - } - return false -} - --// computeWorkspacePackagesLocked computes workspace packages in the snapshot s --// for the given metadata graph. +-// computeWorkspacePackagesLocked computes workspace packages in the +-// snapshot s for the given metadata graph. The result does not +-// contain intermediate test variants. -// -// s.mu must be held while calling this function. -func computeWorkspacePackagesLocked(s *snapshot, meta *metadataGraph) map[PackageID]PackagePath { @@ -16565,6 +19507,7 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - // - // Notably, this excludes intermediate test variants from workspace - // packages. +- assert(!m.IsIntermediateTestVariant(), "unexpected ITV") - workspacePackages[m.ID] = m.ForTest - } - } @@ -16610,135 +19553,10 @@ diff -urN a/gopls/internal/lsp/cache/load.go b/gopls/internal/lsp/cache/load.go - } - return true -} -diff -urN a/gopls/internal/lsp/cache/maps.go b/gopls/internal/lsp/cache/maps.go ---- a/gopls/internal/lsp/cache/maps.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/maps.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,121 +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 cache -- --import ( -- "strings" -- -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/persistent" --) -- --// TODO(euroelessar): Use generics once support for go1.17 is dropped. -- --type filesMap struct { -- impl *persistent.Map --} -- --// uriLessInterface is the < relation for "any" values containing span.URIs. --func uriLessInterface(a, b interface{}) bool { -- return a.(span.URI) < b.(span.URI) --} -- --func newFilesMap() filesMap { -- return filesMap{ -- impl: persistent.NewMap(uriLessInterface), -- } --} -- --func (m filesMap) Clone() filesMap { -- return filesMap{ -- impl: m.impl.Clone(), -- } --} -- --func (m filesMap) Destroy() { -- m.impl.Destroy() --} -- --func (m filesMap) Get(key span.URI) (source.FileHandle, bool) { -- value, ok := m.impl.Get(key) -- if !ok { -- return nil, false -- } -- return value.(source.FileHandle), true --} -- --func (m filesMap) Range(do func(key span.URI, value source.FileHandle)) { -- m.impl.Range(func(key, value interface{}) { -- do(key.(span.URI), value.(source.FileHandle)) -- }) --} -- --func (m filesMap) Set(key span.URI, value source.FileHandle) { -- m.impl.Set(key, value, nil) --} -- --func (m filesMap) Delete(key span.URI) { -- m.impl.Delete(key) --} -- --func packageIDLessInterface(x, y interface{}) bool { -- return x.(PackageID) < y.(PackageID) --} -- --type knownDirsSet struct { -- impl *persistent.Map --} -- --func newKnownDirsSet() knownDirsSet { -- return knownDirsSet{ -- impl: persistent.NewMap(func(a, b interface{}) bool { -- return a.(span.URI) < b.(span.URI) -- }), -- } --} -- --func (s knownDirsSet) Clone() knownDirsSet { -- return knownDirsSet{ -- impl: s.impl.Clone(), -- } --} -- --func (s knownDirsSet) Destroy() { -- s.impl.Destroy() --} -- --func (s knownDirsSet) Contains(key span.URI) bool { -- _, ok := s.impl.Get(key) -- return ok --} -- --func (s knownDirsSet) Range(do func(key span.URI)) { -- s.impl.Range(func(key, value interface{}) { -- do(key.(span.URI)) -- }) --} -- --func (s knownDirsSet) SetAll(other knownDirsSet) { -- s.impl.SetAll(other.impl) --} -- --func (s knownDirsSet) Insert(key span.URI) { -- s.impl.Set(key, nil, nil) --} -- --func (s knownDirsSet) Remove(key span.URI) { -- s.impl.Delete(key) --} -- --// analysisKeyLessInterface is the less-than relation for analysisKey --// values wrapped in an interface. --func analysisKeyLessInterface(a, b interface{}) bool { -- x, y := a.(analysisKey), b.(analysisKey) -- if cmp := strings.Compare(x.analyzerNames, y.analyzerNames); cmp != 0 { -- return cmp < 0 -- } -- return x.pkgid < y.pkgid --} diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go --- a/gopls/internal/lsp/cache/mod.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/mod.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,522 +0,0 @@ ++++ b/gopls/internal/lsp/cache/mod.go 1970-01-01 08:00:00 +@@ -1,518 +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. @@ -16793,7 +19611,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - } - - // Await result. -- v, err := s.awaitPromise(ctx, entry.(*memoize.Promise)) +- v, err := s.awaitPromise(ctx, entry) - if err != nil { - return nil, err - } @@ -16807,7 +19625,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - _, done := event.Start(ctx, "cache.ParseMod", tag.URI.Of(fh.URI())) - defer done() - -- contents, err := fh.Read() +- contents, err := fh.Content() - if err != nil { - return nil, err - } @@ -16871,7 +19689,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - } - - // Await result. -- v, err := s.awaitPromise(ctx, entry.(*memoize.Promise)) +- v, err := s.awaitPromise(ctx, entry) - if err != nil { - return nil, err - } @@ -16884,12 +19702,12 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - _, done := event.Start(ctx, "cache.ParseWork", tag.URI.Of(fh.URI())) - defer done() - -- contents, err := fh.Read() +- content, err := fh.Content() - if err != nil { - return nil, err - } -- m := protocol.NewMapper(fh.URI(), contents) -- file, parseErr := modfile.ParseWork(fh.URI().Filename(), contents, nil) +- m := protocol.NewMapper(fh.URI(), content) +- file, parseErr := modfile.ParseWork(fh.URI().Filename(), content, nil) - // Attempt to convert the error to a standardized parse error. - var parseErrors []*source.Diagnostic - if parseErr != nil { @@ -16923,7 +19741,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go -// it doesn't exist, it returns nil. -func (s *snapshot) goSum(ctx context.Context, modURI span.URI) []byte { - // Get the go.sum file, either from the snapshot or directly from the -- // cache. Avoid (*snapshot).GetFile here, as we don't want to add +- // cache. Avoid (*snapshot).ReadFile here, as we don't want to add - // nonexistent file handles to the snapshot if the file does not exist. - // - // TODO(rfindley): but that's not right. Changes to sum files should @@ -16932,12 +19750,12 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - var sumFH source.FileHandle = s.FindFile(sumURI) - if sumFH == nil { - var err error -- sumFH, err = s.view.fs.GetFile(ctx, sumURI) +- sumFH, err = s.view.fs.ReadFile(ctx, sumURI) - if err != nil { - return nil - } - } -- content, err := sumFH.Read() +- content, err := sumFH.Content() - if err != nil { - return nil - } @@ -16954,7 +19772,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go -func (s *snapshot) ModWhy(ctx context.Context, fh source.FileHandle) (map[string]string, error) { - uri := fh.URI() - -- if s.View().FileKind(fh) != source.Mod { +- if s.FileKind(fh) != source.Mod { - return nil, fmt.Errorf("%s is not a go.mod file", uri) - } - @@ -16981,7 +19799,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - } - - // Await result. -- v, err := s.awaitPromise(ctx, entry.(*memoize.Promise)) +- v, err := s.awaitPromise(ctx, entry) - if err != nil { - return nil, err - } @@ -17035,7 +19853,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - } - - type locatedErr struct { -- spn span.Span +- loc protocol.Location - msg string - } - diagLocations := map[*source.ParsedModule]locatedErr{} @@ -17048,7 +19866,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - - // Match the error against all the mod files in the workspace. - for _, uri := range s.ModFiles() { -- fh, err := s.GetFile(ctx, uri) +- fh, err := s.ReadFile(ctx, uri) - if err != nil { - event.Error(ctx, "getting modfile for Go command error", err) - continue @@ -17076,13 +19894,13 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - // file/position information, so don't even try to find it. - continue - } -- spn, found, err := s.matchErrorToModule(ctx, pm, msg) +- loc, found, err := s.matchErrorToModule(ctx, pm, msg) - if err != nil { - event.Error(ctx, "matching error to module", err) - continue - } - le := locatedErr{ -- spn: spn, +- loc: loc, - msg: msg, - } - if found { @@ -17100,7 +19918,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - - var srcErrs []*source.Diagnostic - for pm, le := range diagLocations { -- diag, err := s.goCommandDiagnostic(pm, le.spn, le.msg) +- diag, err := s.goCommandDiagnostic(pm, le.loc, le.msg) - if err != nil { - event.Error(ctx, "building go command diagnostic", err) - continue @@ -17121,7 +19939,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go -// -// It returns the location of a reference to the one of the modules and true -// if one exists. If none is found it returns a fallback location and false. --func (s *snapshot) matchErrorToModule(ctx context.Context, pm *source.ParsedModule, goCmdError string) (span.Span, bool, error) { +-func (s *snapshot) matchErrorToModule(ctx context.Context, pm *source.ParsedModule, goCmdError string) (protocol.Location, bool, error) { - var reference *modfile.Line - matches := moduleVersionInErrorRe.FindAllStringSubmatch(goCmdError, -1) - @@ -17140,25 +19958,21 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - // No match for the module path was found in the go.mod file. - // Show the error on the module declaration, if one exists, or - // just the first line of the file. -- if pm.File.Module == nil { -- return span.New(pm.URI, span.NewPoint(1, 1, 0), span.Point{}), false, nil +- var start, end int +- if pm.File.Module != nil && pm.File.Module.Syntax != nil { +- syntax := pm.File.Module.Syntax +- start, end = syntax.Start.Byte, syntax.End.Byte - } -- syntax := pm.File.Module.Syntax -- spn, err := pm.Mapper.OffsetSpan(syntax.Start.Byte, syntax.End.Byte) -- return spn, false, err +- loc, err := pm.Mapper.OffsetLocation(start, end) +- return loc, false, err - } - -- spn, err := pm.Mapper.OffsetSpan(reference.Start.Byte, reference.End.Byte) -- return spn, true, err +- loc, err := pm.Mapper.OffsetLocation(reference.Start.Byte, reference.End.Byte) +- return loc, true, err -} - -// goCommandDiagnostic creates a diagnostic for a given go command error. --func (s *snapshot) goCommandDiagnostic(pm *source.ParsedModule, spn span.Span, goCmdError string) (*source.Diagnostic, error) { -- rng, err := pm.Mapper.SpanRange(spn) -- if err != nil { -- return nil, err -- } -- +-func (s *snapshot) goCommandDiagnostic(pm *source.ParsedModule, loc protocol.Location, goCmdError string) (*source.Diagnostic, error) { - matches := moduleVersionInErrorRe.FindAllStringSubmatch(goCmdError, -1) - var innermost *module.Version - for i := len(matches) - 1; i >= 0; i-- { @@ -17178,7 +19992,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - } - return &source.Diagnostic{ - URI: pm.URI, -- Range: rng, +- Range: loc.Range, - Severity: protocol.SeverityError, - Source: source.ListError, - Message: `Inconsistent vendoring detected. Please re-run "go mod vendor". @@ -17205,7 +20019,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - } - return &source.Diagnostic{ - URI: pm.URI, -- Range: rng, +- Range: loc.Range, - Severity: protocol.SeverityError, - Source: source.ListError, - Message: msg, @@ -17226,7 +20040,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - } - return &source.Diagnostic{ - URI: pm.URI, -- Range: rng, +- Range: loc.Range, - Severity: protocol.SeverityError, - Message: fmt.Sprintf("%v@%v has not been downloaded", innermost.Path, innermost.Version), - Source: source.ListError, @@ -17235,7 +20049,7 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go - default: - return &source.Diagnostic{ - URI: pm.URI, -- Range: rng, +- Range: loc.Range, - Severity: protocol.SeverityError, - Source: source.ListError, - Message: goCmdError, @@ -17263,8 +20077,8 @@ diff -urN a/gopls/internal/lsp/cache/mod.go b/gopls/internal/lsp/cache/mod.go -} diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_tidy.go --- a/gopls/internal/lsp/cache/mod_tidy.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/mod_tidy.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,469 +0,0 @@ ++++ b/gopls/internal/lsp/cache/mod_tidy.go 1970-01-01 08:00:00 +@@ -1,501 +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. @@ -17275,7 +20089,7 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ - "context" - "fmt" - "go/ast" -- "io/ioutil" +- "go/token" - "os" - "path/filepath" - "strconv" @@ -17295,6 +20109,9 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ -// ModTidy returns the go.mod file that would be obtained by running -// "go mod tidy". Concurrent requests are combined into a single command. -func (s *snapshot) ModTidy(ctx context.Context, pm *source.ParsedModule) (*source.TidiedModule, error) { +- ctx, done := event.Start(ctx, "cache.snapshot.ModTidy") +- defer done() +- - uri := pm.URI - if pm.File == nil { - return nil, fmt.Errorf("cannot tidy unparseable go.mod file: %v", uri) @@ -17314,7 +20131,7 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ - // If the file handle is an overlay, it may not be written to disk. - // The go.mod file has to be on disk for `go mod tidy` to work. - // TODO(rfindley): is this still true with Go 1.16 overlay support? -- fh, err := s.GetFile(ctx, pm.URI) +- fh, err := s.ReadFile(ctx, pm.URI) - if err != nil { - return nil, err - } @@ -17324,7 +20141,7 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ - } - } - -- if criticalErr := s.GetCriticalError(ctx); criticalErr != nil { +- if criticalErr := s.CriticalError(ctx); criticalErr != nil { - return &source.TidiedModule{ - Diagnostics: criticalErr.Diagnostics, - }, nil @@ -17349,7 +20166,7 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ - } - - // Await result. -- v, err := s.awaitPromise(ctx, entry.(*memoize.Promise)) +- v, err := s.awaitPromise(ctx, entry) - if err != nil { - return nil, err - } @@ -17381,7 +20198,7 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ - - // Go directly to disk to get the temporary mod file, - // since it is always on disk. -- tempContents, err := ioutil.ReadFile(tmpURI.Filename()) +- tempContents, err := os.ReadFile(tmpURI.Filename()) - if err != nil { - return nil, err - } @@ -17432,7 +20249,7 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ - for _, req := range wrongDirectness { - // Handle dependencies that are incorrectly labeled indirect and - // vice versa. -- srcDiag, err := directnessDiagnostic(pm.Mapper, req, snapshot.View().Options().ComputeEdits) +- srcDiag, err := directnessDiagnostic(pm.Mapper, req, snapshot.Options().ComputeEdits) - if err != nil { - // We're probably in a bad state if we can't compute a - // directnessDiagnostic, but try to keep going so as to not suppress @@ -17446,7 +20263,33 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ - // go.mod file. The fixes will be for the go.mod file, but the - // diagnostics should also appear in both the go.mod file and the import - // statements in the Go files in which the dependencies are used. +- // Finally, add errors for any unused dependencies. +- if len(missing) > 0 { +- missingModuleDiagnostics, err := missingModuleDiagnostics(ctx, snapshot, pm, ideal, missing) +- if err != nil { +- return nil, err +- } +- diagnostics = append(diagnostics, missingModuleDiagnostics...) +- } +- +- // Opt: if this is the only diagnostic, we can avoid textual edits and just +- // run the Go command. +- // +- // See also the documentation for command.RemoveDependencyArgs.OnlyDiagnostic. +- onlyDiagnostic := len(diagnostics) == 0 && len(unused) == 1 +- for _, req := range unused { +- srcErr, err := unusedDiagnostic(pm.Mapper, req, onlyDiagnostic) +- if err != nil { +- return nil, err +- } +- diagnostics = append(diagnostics, srcErr) +- } +- return diagnostics, nil +-} +- +-func missingModuleDiagnostics(ctx context.Context, snapshot *snapshot, pm *source.ParsedModule, ideal *modfile.File, missing map[string]*modfile.Require) ([]*source.Diagnostic, error) { - missingModuleFixes := map[*modfile.Require][]source.SuggestedFix{} +- var diagnostics []*source.Diagnostic - for _, req := range missing { - srcDiag, err := missingModuleDiagnostic(pm, req) - if err != nil { @@ -17455,12 +20298,24 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ - missingModuleFixes[req] = srcDiag.SuggestedFixes - diagnostics = append(diagnostics, srcDiag) - } +- - // Add diagnostics for missing modules anywhere they are imported in the - // workspace. +- metas, err := snapshot.WorkspaceMetadata(ctx) +- if err != nil { +- return nil, err +- } - // TODO(adonovan): opt: opportunities for parallelism abound. -- for _, m := range snapshot.workspaceMetadata() { -- // Read both lists of files of this package, in parallel. -- goFiles, compiledGoFiles, err := readGoFiles(ctx, snapshot, m) +- for _, m := range metas { +- // Read both lists of files of this package. +- // +- // Parallelism is not necessary here as the files will have already been +- // pre-read at load time. +- goFiles, err := readFiles(ctx, snapshot, m.GoFiles) +- if err != nil { +- return nil, err +- } +- compiledGoFiles, err := readFiles(ctx, snapshot, m.CompiledGoFiles) - if err != nil { - return nil, err - } @@ -17541,15 +20396,6 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ - } - } - } -- // Finally, add errors for any unused dependencies. -- onlyDiagnostic := len(diagnostics) == 0 && len(unused) == 1 -- for _, req := range unused { -- srcErr, err := unusedDiagnostic(pm.Mapper, req, onlyDiagnostic) -- if err != nil { -- return nil, err -- } -- diagnostics = append(diagnostics, srcErr) -- } - return diagnostics, nil -} - @@ -17720,7 +20566,7 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ -// -// TODO(rfindley): this should key off source.ImportPath. -func parseImports(ctx context.Context, s *snapshot, files []source.FileHandle) (map[string]bool, error) { -- pgfs, _, err := s.parseCache.parseFiles(ctx, source.ParseHeader, files...) +- pgfs, err := s.view.parseCache.parseFiles(ctx, token.NewFileSet(), source.ParseHeader, false, files...) - if err != nil { // e.g. context cancellation - return nil, err - } @@ -17736,8 +20582,8 @@ diff -urN a/gopls/internal/lsp/cache/mod_tidy.go b/gopls/internal/lsp/cache/mod_ -} diff -urN a/gopls/internal/lsp/cache/mod_vuln.go b/gopls/internal/lsp/cache/mod_vuln.go --- a/gopls/internal/lsp/cache/mod_vuln.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/mod_vuln.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,75 +0,0 @@ ++++ b/gopls/internal/lsp/cache/mod_vuln.go 1970-01-01 08:00:00 +@@ -1,48 +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. @@ -17746,45 +20592,29 @@ diff -urN a/gopls/internal/lsp/cache/mod_vuln.go b/gopls/internal/lsp/cache/mod_ - -import ( - "context" -- "os" - -- "golang.org/x/tools/gopls/internal/govulncheck" -- "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" - "golang.org/x/tools/gopls/internal/vulncheck" +- "golang.org/x/tools/gopls/internal/vulncheck/scan" - "golang.org/x/tools/internal/memoize" -) - -// ModVuln returns import vulnerability analysis for the given go.mod URI. -// Concurrent requests are combined into a single command. --func (s *snapshot) ModVuln(ctx context.Context, modURI span.URI) (*govulncheck.Result, error) { +-func (s *snapshot) ModVuln(ctx context.Context, modURI span.URI) (*vulncheck.Result, error) { - s.mu.Lock() - entry, hit := s.modVulnHandles.Get(modURI) - s.mu.Unlock() - - type modVuln struct { -- result *govulncheck.Result +- result *vulncheck.Result - err error - } - - // Cache miss? - if !hit { -- // If the file handle is an overlay, it may not be written to disk. -- // The go.mod file has to be on disk for vulncheck to work. -- // -- // TODO(hyangah): use overlays for vulncheck. -- fh, err := s.GetFile(ctx, modURI) -- if err != nil { -- return nil, err -- } -- if _, ok := fh.(*Overlay); ok { -- if info, _ := os.Stat(modURI.Filename()); info == nil { -- return nil, source.ErrNoModOnDisk -- } -- } -- - handle := memoize.NewPromise("modVuln", func(ctx context.Context, arg interface{}) interface{} { -- result, err := modVulnImpl(ctx, arg.(*snapshot), modURI) +- result, err := scan.VulnerablePackages(ctx, arg.(*snapshot)) - return modVuln{result, err} - }) - @@ -17795,27 +20625,16 @@ diff -urN a/gopls/internal/lsp/cache/mod_vuln.go b/gopls/internal/lsp/cache/mod_ - } - - // Await result. -- v, err := s.awaitPromise(ctx, entry.(*memoize.Promise)) +- v, err := s.awaitPromise(ctx, entry) - if err != nil { - return nil, err - } - res := v.(modVuln) - return res.result, res.err -} -- --func modVulnImpl(ctx context.Context, s *snapshot, uri span.URI) (*govulncheck.Result, error) { -- if vulncheck.VulnerablePackages == nil { -- return &govulncheck.Result{}, nil -- } -- fh, err := s.GetFile(ctx, uri) -- if err != nil { -- return nil, err -- } -- return vulncheck.VulnerablePackages(ctx, s, fh) --} diff -urN a/gopls/internal/lsp/cache/os_darwin.go b/gopls/internal/lsp/cache/os_darwin.go --- a/gopls/internal/lsp/cache/os_darwin.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/os_darwin.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cache/os_darwin.go 1970-01-01 08:00:00 @@ -1,59 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -17878,7 +20697,7 @@ diff -urN a/gopls/internal/lsp/cache/os_darwin.go b/gopls/internal/lsp/cache/os_ -} diff -urN a/gopls/internal/lsp/cache/os_windows.go b/gopls/internal/lsp/cache/os_windows.go --- a/gopls/internal/lsp/cache/os_windows.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/os_windows.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cache/os_windows.go 1970-01-01 08:00:00 @@ -1,56 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -17936,458 +20755,10 @@ diff -urN a/gopls/internal/lsp/cache/os_windows.go b/gopls/internal/lsp/cache/os - } - return nil -} -diff -urN a/gopls/internal/lsp/cache/parse_cache.go b/gopls/internal/lsp/cache/parse_cache.go ---- a/gopls/internal/lsp/cache/parse_cache.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/parse_cache.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,298 +0,0 @@ --// Copyright 2023 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 cache -- --import ( -- "container/heap" -- "context" -- "go/token" -- "runtime" -- "sort" -- "sync" -- -- "golang.org/x/sync/errgroup" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/internal/memoize" --) -- --// This file contains an implementation of a bounded-size parse cache, that --// offsets the base token.Pos value of each cached file so that they may be --// later described by a single dedicated FileSet. --// --// This is achieved by tracking a monotonic offset in the token.Pos space, that --// is incremented before parsing allow room for the resulting parsed file. -- --// Keep 200 recently parsed files, based on the following rationale: --// - One of the most important benefits of caching is avoiding re-parsing --// everything in a package when working on a single file. No packages in --// Kubernetes have > 200 files (only one has > 100). --// - Experience has shown that ~1000 parsed files can use noticeable space. --// 200 feels like a sweet spot between limiting cache size and optimizing --// cache hits for low-latency operations. --const parseCacheMaxFiles = 200 -- --// parsePadding is additional padding allocated between entries in the parse --// cache to allow for increases in length (such as appending missing braces) --// caused by fixAST. --// --// This is used to mitigate a chicken and egg problem: we must know the base --// offset of the file we're about to parse, before we start parsing, and yet --// src fixups may affect the actual size of the parsed content (and therefore --// the offsets of subsequent files). --// --// When we encounter a file that no longer fits in its allocated space in the --// fileset, we have no choice but to re-parse it. Leaving a generous padding --// reduces the likelihood of this "slow path". --// --// This value is mutable for testing, so that we can exercise the slow path. --var parsePadding = 1000 // mutable for testing -- --// A parseCache holds a bounded number of recently accessed parsed Go files. As --// new files are stored, older files may be evicted from the cache. --// --// The parseCache.parseFiles method exposes a batch API for parsing (and --// caching) multiple files. This is necessary for type-checking, where files --// must be parsed in a common fileset. --type parseCache struct { -- mu sync.Mutex -- m map[parseKey]*parseCacheEntry -- lru queue // min-atime priority queue of *parseCacheEntry -- clock uint64 // clock time, incremented when the cache is updated -- nextOffset token.Pos // token.Pos offset for the next parsed file --} -- --// parseKey uniquely identifies a parsed Go file. --type parseKey struct { -- file source.FileIdentity -- mode source.ParseMode --} -- --type parseCacheEntry struct { -- key parseKey -- promise *memoize.Promise // memoize.Promise[*source.ParsedGoFile] -- atime uint64 // clock time of last access -- lruIndex int --} -- --// startParse prepares a parsing pass, using the following steps: --// - search for cache hits --// - create new promises for cache misses --// - store as many new promises in the cache as space will allow --// --// The resulting slice has an entry for every given file handle, though some --// entries may be nil if there was an error reading the file (in which case the --// resulting error will be non-nil). --func (c *parseCache) startParse(mode source.ParseMode, fhs ...source.FileHandle) ([]*memoize.Promise, error) { -- c.mu.Lock() -- defer c.mu.Unlock() -- -- // Any parsing pass increments the clock, as we'll update access times. -- // (technically, if fhs is empty this isn't necessary, but that's a degenerate case). -- // -- // All entries parsed from a single call get the same access time. -- c.clock++ -- -- // Read file data and collect cacheable files. -- var ( -- data = make([][]byte, len(fhs)) // file content for each readable file -- promises = make([]*memoize.Promise, len(fhs)) -- firstReadError error // first error from fh.Read, or nil -- ) -- for i, fh := range fhs { -- src, err := fh.Read() -- if err != nil { -- if firstReadError == nil { -- firstReadError = err -- } -- continue -- } -- data[i] = src -- -- key := parseKey{ -- file: fh.FileIdentity(), -- mode: mode, -- } -- -- // Check for a cache hit. -- if e, ok := c.m[key]; ok { -- e.atime = c.clock -- heap.Fix(&c.lru, e.lruIndex) -- promises[i] = e.promise -- continue -- } -- -- // ...otherwise, create a new promise to parse with a non-overlapping offset -- fset := token.NewFileSet() -- if c.nextOffset > 0 { -- // Add a dummy file so that this parsed file does not overlap with others. -- fset.AddFile("", 1, int(c.nextOffset)) -- } -- c.nextOffset += token.Pos(len(src) + parsePadding + 1) // leave room for src fixes -- fh := fh -- promise := memoize.NewPromise(string(fh.URI()), func(ctx context.Context, _ interface{}) interface{} { -- return parseGoSrc(ctx, fset, fh.URI(), src, mode) -- }) -- promises[i] = promise -- -- var e *parseCacheEntry -- if len(c.lru) < parseCacheMaxFiles { -- // add new entry -- e = new(parseCacheEntry) -- if c.m == nil { -- c.m = make(map[parseKey]*parseCacheEntry) -- } -- } else { -- // evict oldest entry -- e = heap.Pop(&c.lru).(*parseCacheEntry) -- delete(c.m, e.key) -- } -- e.key = key -- e.promise = promise -- e.atime = c.clock -- c.m[e.key] = e -- heap.Push(&c.lru, e) -- } -- -- if len(c.m) != len(c.lru) { -- panic("map and LRU are inconsistent") -- } -- -- return promises, firstReadError --} -- --// parseFiles returns a ParsedGoFile for the given file handles in the --// requested parse mode. --// --// If parseFiles returns an error, it still returns a slice, --// but with a nil entry for each file that could not be parsed. --// --// The second result is a FileSet describing all resulting parsed files. --// --// For parsed files that already exists in the cache, access time will be --// updated. For others, parseFiles will parse and store as many results in the --// cache as space allows. --func (c *parseCache) parseFiles(ctx context.Context, mode source.ParseMode, fhs ...source.FileHandle) ([]*source.ParsedGoFile, *token.FileSet, error) { -- promises, firstReadError := c.startParse(mode, fhs...) -- -- // Await all parsing. -- var g errgroup.Group -- g.SetLimit(runtime.GOMAXPROCS(-1)) // parsing is CPU-bound. -- pgfs := make([]*source.ParsedGoFile, len(fhs)) -- for i, promise := range promises { -- if promise == nil { -- continue -- } -- i := i -- promise := promise -- g.Go(func() error { -- result, err := promise.Get(ctx, nil) -- if err != nil { -- return err -- } -- pgfs[i] = result.(*source.ParsedGoFile) -- return nil -- }) -- } -- if err := g.Wait(); err != nil { -- return nil, nil, err -- } -- -- // Construct a token.FileSet mapping all parsed files, and update their -- // Tok to the corresponding file in the new fileset. -- // -- // In the unlikely event that a parsed file no longer fits in its allocated -- // space in the FileSet range, it will need to be re-parsed. -- -- var tokenFiles []*token.File -- fileIndex := make(map[*token.File]int) // to look up original indexes after sorting -- for i, pgf := range pgfs { -- if pgf == nil { -- continue -- } -- fileIndex[pgf.Tok] = i -- tokenFiles = append(tokenFiles, pgf.Tok) -- } -- -- sort.Slice(tokenFiles, func(i, j int) bool { -- return tokenFiles[i].Base() < tokenFiles[j].Base() -- }) -- -- var needReparse []int // files requiring reparsing -- out := tokenFiles[:0] -- for i, f := range tokenFiles { -- if i < len(tokenFiles)-1 && f.Base()+f.Size() >= tokenFiles[i+1].Base() { -- if f != tokenFiles[i+1] { // no need to re-parse duplicates -- needReparse = append(needReparse, fileIndex[f]) -- } -- } else { -- out = append(out, f) -- } -- } -- fset := source.FileSetFor(out...) -- -- // Re-parse any remaining files using the stitched fileSet. -- for _, i := range needReparse { -- // Start from scratch, rather than using ParsedGoFile.Src, so that source -- // fixing operates exactly the same (note that fixing stops after a limited -- // number of tries). -- fh := fhs[i] -- src, err := fh.Read() -- if err != nil { -- if firstReadError == nil { -- firstReadError = err -- } -- continue -- } -- pgfs[i] = parseGoSrc(ctx, fset, fh.URI(), src, mode) -- } -- -- // Ensure each PGF refers to a token.File from the new FileSet. -- for i, pgf := range pgfs { -- if pgf == nil { -- continue -- } -- newTok := fset.File(token.Pos(pgf.Tok.Base())) -- if newTok == nil { -- panic("internal error: missing tok for " + pgf.URI) -- } -- if newTok.Base() != pgf.Tok.Base() || newTok.Size() != pgf.Tok.Size() { -- panic("internal error: mismatching token.File in synthetic FileSet") -- } -- pgf2 := *pgf -- pgf2.Tok = newTok -- pgfs[i] = &pgf2 -- } -- -- return pgfs, fset, firstReadError --} -- --// -- priority queue boilerplate -- -- --// queue is a min-atime prority queue of cache entries. --type queue []*parseCacheEntry -- --func (q queue) Len() int { return len(q) } -- --func (q queue) Less(i, j int) bool { return q[i].atime < q[j].atime } -- --func (q queue) Swap(i, j int) { -- q[i], q[j] = q[j], q[i] -- q[i].lruIndex = i -- q[j].lruIndex = j --} -- --func (q *queue) Push(x interface{}) { -- e := x.(*parseCacheEntry) -- e.lruIndex = len(*q) -- *q = append(*q, e) --} -- --func (q *queue) Pop() interface{} { -- last := len(*q) - 1 -- e := (*q)[last] -- (*q)[last] = nil // aid GC -- *q = (*q)[:last] -- return e --} -diff -urN a/gopls/internal/lsp/cache/parse_cache_test.go b/gopls/internal/lsp/cache/parse_cache_test.go ---- a/gopls/internal/lsp/cache/parse_cache_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/parse_cache_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,142 +0,0 @@ --// Copyright 2023 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 cache -- --import ( -- "context" -- "fmt" -- "go/token" -- "testing" -- -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/span" --) -- --func TestParseCache(t *testing.T) { -- ctx := context.Background() -- uri := span.URI("file:///myfile") -- fh := makeFakeFileHandle(uri, []byte("package p\n\nconst _ = \"foo\"")) -- -- var cache parseCache -- pgfs1, _, err := cache.parseFiles(ctx, source.ParseFull, fh) -- if err != nil { -- t.Fatal(err) -- } -- pgf1 := pgfs1[0] -- pgfs2, _, err := cache.parseFiles(ctx, source.ParseFull, fh) -- pgf2 := pgfs2[0] -- if err != nil { -- t.Fatal(err) -- } -- if pgf1.File != pgf2.File { -- t.Errorf("parseFiles(%q): unexpected cache miss on repeated call", uri) -- } -- -- // Fill up the cache with other files, but don't evict the file above. -- files := []source.FileHandle{fh} -- files = append(files, dummyFileHandles(parseCacheMaxFiles-1)...) -- pgfs3, fset, err := cache.parseFiles(ctx, source.ParseFull, files...) -- pgf3 := pgfs3[0] -- if pgf3.File != pgf1.File { -- t.Errorf("parseFiles(%q, ...): unexpected cache miss", uri) -- } -- if pgf3.Tok == pgf1.Tok { -- t.Errorf("parseFiles(%q, ...): unexpectedly matching token file", uri) -- } -- if pgf3.Tok.Base() != pgf1.Tok.Base() || pgf3.Tok.Size() != pgf1.Tok.Size() { -- t.Errorf("parseFiles(%q, ...): result.Tok has base: %d, size: %d, want (%d, %d)", uri, pgf3.Tok.Base(), pgf3.Tok.Size(), pgf1.Tok.Base(), pgf1.Tok.Size()) -- } -- if tok := fset.File(token.Pos(pgf3.Tok.Base())); tok != pgf3.Tok { -- t.Errorf("parseFiles(%q, ...): result.Tok not contained in FileSet", uri) -- } -- -- // Now overwrite the cache, after which we should get new results. -- files = dummyFileHandles(parseCacheMaxFiles) -- _, _, err = cache.parseFiles(ctx, source.ParseFull, files...) -- if err != nil { -- t.Fatal(err) -- } -- pgfs4, _, err := cache.parseFiles(ctx, source.ParseFull, fh) -- if err != nil { -- t.Fatal(err) -- } -- if pgfs4[0].File == pgf1.File { -- t.Errorf("parseFiles(%q): unexpected cache hit after overwriting cache", uri) -- } --} -- --func TestParseCache_Reparsing(t *testing.T) { -- defer func(padding int) { -- parsePadding = padding -- }(parsePadding) -- parsePadding = 0 -- -- files := dummyFileHandles(parseCacheMaxFiles) -- danglingSelector := []byte("package p\nfunc _() {\n\tx.\n}") -- files = append(files, makeFakeFileHandle("file:///bad1", danglingSelector)) -- files = append(files, makeFakeFileHandle("file:///bad2", danglingSelector)) -- -- // Parsing should succeed even though we overflow the padding. -- var cache parseCache -- _, _, err := cache.parseFiles(context.Background(), source.ParseFull, files...) -- if err != nil { -- t.Fatal(err) -- } --} -- --func TestParseCache_Duplicates(t *testing.T) { -- ctx := context.Background() -- uri := span.URI("file:///myfile") -- fh := makeFakeFileHandle(uri, []byte("package p\n\nconst _ = \"foo\"")) -- -- var cache parseCache -- pgfs, _, err := cache.parseFiles(ctx, source.ParseFull, fh, fh) -- if err != nil { -- t.Fatal(err) -- } -- if pgfs[0].File != pgfs[1].File { -- t.Errorf("parseFiles(fh, fh): = [%p, %p], want duplicate files", pgfs[0].File, pgfs[1].File) -- } --} -- --func dummyFileHandles(n int) []source.FileHandle { -- var fhs []source.FileHandle -- for i := 0; i < n; i++ { -- uri := span.URI(fmt.Sprintf("file:///_%d", i)) -- src := []byte(fmt.Sprintf("package p\nvar _ = %d", i)) -- fhs = append(fhs, makeFakeFileHandle(uri, src)) -- } -- return fhs --} -- --func makeFakeFileHandle(uri span.URI, src []byte) fakeFileHandle { -- return fakeFileHandle{ -- uri: uri, -- data: src, -- hash: source.HashOf(src), -- } --} -- --type fakeFileHandle struct { -- source.FileHandle -- uri span.URI -- data []byte -- hash source.Hash --} -- --func (h fakeFileHandle) URI() span.URI { -- return h.uri --} -- --func (h fakeFileHandle) Read() ([]byte, error) { -- return h.data, nil --} -- --func (h fakeFileHandle) FileIdentity() source.FileIdentity { -- return source.FileIdentity{ -- URI: h.uri, -- Hash: h.hash, -- } --} diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.go --- a/gopls/internal/lsp/cache/parse.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/parse.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,900 +0,0 @@ ++++ b/gopls/internal/lsp/cache/parse.go 1970-01-01 08:00:00 +@@ -1,969 +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. @@ -18405,6 +20776,7 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - "path/filepath" - "reflect" - +- goplsastutil "golang.org/x/tools/gopls/internal/astutil" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/lsp/source" @@ -18416,8 +20788,8 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - -// ParseGo parses the file whose contents are provided by fh, using a cache. -// The resulting tree may have beeen fixed up. --func (s *snapshot) ParseGo(ctx context.Context, fh source.FileHandle, mode source.ParseMode) (*source.ParsedGoFile, error) { -- pgfs, _, err := s.parseCache.parseFiles(ctx, mode, fh) +-func (s *snapshot) ParseGo(ctx context.Context, fh source.FileHandle, mode parser.Mode) (*source.ParsedGoFile, error) { +- pgfs, err := s.view.parseCache.parseFiles(ctx, token.NewFileSet(), mode, false, fh) - if err != nil { - return nil, err - } @@ -18425,29 +20797,34 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g -} - -// parseGoImpl parses the Go source file whose content is provided by fh. --func parseGoImpl(ctx context.Context, fset *token.FileSet, fh source.FileHandle, mode source.ParseMode) (*source.ParsedGoFile, error) { -- ctx, done := event.Start(ctx, "cache.parseGo", tag.File.Of(fh.URI().Filename())) -- defer done() -- +-func parseGoImpl(ctx context.Context, fset *token.FileSet, fh source.FileHandle, mode parser.Mode, purgeFuncBodies bool) (*source.ParsedGoFile, error) { - ext := filepath.Ext(fh.URI().Filename()) - if ext != ".go" && ext != "" { // files generated by cgo have no extension - return nil, fmt.Errorf("cannot parse non-Go file %s", fh.URI()) - } -- src, err := fh.Read() +- content, err := fh.Content() - if err != nil { - return nil, err - } -- return parseGoSrc(ctx, fset, fh.URI(), src, mode), nil +- // Check for context cancellation before actually doing the parse. +- if ctx.Err() != nil { +- return nil, ctx.Err() +- } +- pgf, _ := ParseGoSrc(ctx, fset, fh.URI(), content, mode, purgeFuncBodies) +- return pgf, nil -} - --// parseGoSrc parses a buffer of Go source, repairing the tree if necessary. --func parseGoSrc(ctx context.Context, fset *token.FileSet, uri span.URI, src []byte, mode source.ParseMode) (res *source.ParsedGoFile) { -- parserMode := parser.AllErrors | parser.ParseComments -- if mode == source.ParseHeader { -- parserMode = parser.ImportsOnly | parser.ParseComments +-// ParseGoSrc parses a buffer of Go source, repairing the tree if necessary. +-// +-// The provided ctx is used only for logging. +-func ParseGoSrc(ctx context.Context, fset *token.FileSet, uri span.URI, src []byte, mode parser.Mode, purgeFuncBodies bool) (res *source.ParsedGoFile, fixes []fixType) { +- if purgeFuncBodies { +- src = goplsastutil.PurgeFuncBodies(src) - } +- ctx, done := event.Start(ctx, "cache.ParseGoSrc", tag.File.Of(uri.Filename())) +- defer done() - -- file, err := parser.ParseFile(fset, uri.Filename(), src, parserMode) +- file, err := parser.ParseFile(fset, uri.Filename(), src, mode) - var parseErr scanner.ErrorList - if err != nil { - // We passed a byte slice, so the only possible error is a parse error. @@ -18463,15 +20840,20 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - tok.SetLinesForContent(src) - } - -- fixed := false +- fixedSrc := false +- fixedAST := false - // If there were parse errors, attempt to fix them up. - if parseErr != nil { - // Fix any badly parsed parts of the AST. -- fixed = fixAST(file, tok, src) +- astFixes := fixAST(file, tok, src) +- fixedAST = len(fixes) > 0 +- if fixedAST { +- fixes = append(fixes, astFixes...) +- } - - for i := 0; i < 10; i++ { - // Fix certain syntax errors that render the file unparseable. -- newSrc := fixSrc(file, tok, src) +- newSrc, srcFix := fixSrc(file, tok, src) - if newSrc == nil { - break - } @@ -18484,14 +20866,30 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - event.Log(ctx, fmt.Sprintf("fixSrc loop - last diff:\n%v", unified), tag.File.Of(tok.Name())) - } - -- newFile, _ := parser.ParseFile(fset, uri.Filename(), newSrc, parserMode) -- if newFile != nil { -- // Maintain the original parseError so we don't try formatting the doctored file. -- file = newFile -- src = newSrc -- tok = fset.File(file.Pos()) +- newFile, newErr := parser.ParseFile(fset, uri.Filename(), newSrc, mode) +- if newFile == nil { +- break // no progress +- } +- +- // Maintain the original parseError so we don't try formatting the +- // doctored file. +- file = newFile +- src = newSrc +- tok = fset.File(file.Pos()) - -- fixed = fixAST(file, tok, src) +- // Only now that we accept the fix do we record the src fix from above. +- fixes = append(fixes, srcFix) +- fixedSrc = true +- +- if newErr == nil { +- break // nothing to fix +- } +- +- // Note that fixedAST is reset after we fix src. +- astFixes = fixAST(file, tok, src) +- fixedAST = len(astFixes) > 0 +- if fixedAST { +- fixes = append(fixes, astFixes...) - } - } - } @@ -18500,12 +20898,13 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - URI: uri, - Mode: mode, - Src: src, -- Fixed: fixed, +- FixedSrc: fixedSrc, +- FixedAST: fixedAST, - File: file, - Tok: tok, - Mapper: protocol.NewMapper(uri, src), - ParseErr: parseErr, -- } +- }, fixes -} - -// fixAST inspects the AST and potentially modifies any *ast.BadStmts so that it can be @@ -18513,22 +20912,26 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g -// -// If fixAST returns true, the resulting AST is considered "fixed", meaning -// positions have been mangled, and type checker errors may not make sense. --func fixAST(n ast.Node, tok *token.File, src []byte) (fixed bool) { +-func fixAST(n ast.Node, tok *token.File, src []byte) (fixes []fixType) { - var err error - walkASTWithParent(n, func(n, parent ast.Node) bool { - switch n := n.(type) { - case *ast.BadStmt: -- if fixed = fixDeferOrGoStmt(n, parent, tok, src); fixed { +- if fixDeferOrGoStmt(n, parent, tok, src) { +- fixes = append(fixes, fixedDeferOrGo) - // Recursively fix in our fixed node. -- _ = fixAST(parent, tok, src) +- moreFixes := fixAST(parent, tok, src) +- fixes = append(fixes, moreFixes...) - } else { - err = fmt.Errorf("unable to parse defer or go from *ast.BadStmt: %v", err) - } - return false - case *ast.BadExpr: -- if fixed = fixArrayType(n, parent, tok, src); fixed { +- if fixArrayType(n, parent, tok, src) { +- fixes = append(fixes, fixedArrayType) - // Recursively fix in our fixed node. -- _ = fixAST(parent, tok, src) +- moreFixes := fixAST(parent, tok, src) +- fixes = append(fixes, moreFixes...) - return false - } - @@ -18538,15 +20941,18 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - // // "i := foo" is init statement, not condition. - // for i := foo - // -- fixInitStmt(n, parent, tok, src) -- +- if fixInitStmt(n, parent, tok, src) { +- fixes = append(fixes, fixedInit) +- } - return false - case *ast.SelectorExpr: - // Fix cases where a keyword prefix results in a phantom "_" selector, e.g.: - // - // foo.var<> // want to complete to "foo.variance" - // -- fixPhantomSelector(n, tok, src) +- if fixPhantomSelector(n, tok, src) { +- fixes = append(fixes, fixedPhantomSelector) +- } - return true - - case *ast.BlockStmt: @@ -18554,7 +20960,9 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt: - // Adjust closing curly brace of empty switch/select - // statements so we can complete inside them. -- fixEmptySwitch(n, tok, src) +- if fixEmptySwitch(n, tok, src) { +- fixes = append(fixes, fixedEmptySwitch) +- } - } - - return true @@ -18562,7 +20970,7 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - return true - } - }) -- return fixed +- return fixes -} - -// walkASTWithParent walks the AST rooted at n. The semantics are @@ -18590,9 +20998,26 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - }) -} - +-// TODO(rfindley): revert this intrumentation once we're certain the crash in +-// #59097 is fixed. +-type fixType int +- +-const ( +- noFix fixType = iota +- fixedCurlies +- fixedDanglingSelector +- fixedDeferOrGo +- fixedArrayType +- fixedInit +- fixedPhantomSelector +- fixedEmptySwitch +-) +- -// fixSrc attempts to modify the file's source code to fix certain -// syntax errors that leave the rest of the file unparsed. --func fixSrc(f *ast.File, tf *token.File, src []byte) (newSrc []byte) { +-// +-// fixSrc returns a non-nil result if and only if a fix was applied. +-func fixSrc(f *ast.File, tf *token.File, src []byte) (newSrc []byte, fix fixType) { - walkASTWithParent(f, func(n, parent ast.Node) bool { - if newSrc != nil { - return false @@ -18601,14 +21026,20 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - switch n := n.(type) { - case *ast.BlockStmt: - newSrc = fixMissingCurlies(f, n, parent, tf, src) +- if newSrc != nil { +- fix = fixedCurlies +- } - case *ast.SelectorExpr: - newSrc = fixDanglingSelector(n, tf, src) +- if newSrc != nil { +- fix = fixedDanglingSelector +- } - } - - return newSrc == nil - }) - -- return newSrc +- return newSrc, fix -} - -// fixMissingCurlies adds in curly braces for block statements that @@ -18632,7 +21063,7 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - } - } - -- parentLine := tok.Line(parent.Pos()) +- parentLine := safetoken.Line(tok, parent.Pos()) - - if parentLine >= tok.LineCount() { - // If we are the last line in the file, no need to fix anything. @@ -18727,30 +21158,33 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g -// switch { -// -// } --func fixEmptySwitch(body *ast.BlockStmt, tok *token.File, src []byte) { +-// +-// The resulting bool reports whether any fixing occurred. +-func fixEmptySwitch(body *ast.BlockStmt, tok *token.File, src []byte) bool { - // We only care about empty switch statements. - if len(body.List) > 0 || !body.Rbrace.IsValid() { -- return +- return false - } - - // If the right brace is actually in the source code at the - // specified position, don't mess with it. - braceOffset, err := safetoken.Offset(tok, body.Rbrace) - if err != nil { -- return +- return false - } - if braceOffset < len(src) && src[braceOffset] == '}' { -- return +- return false - } - -- braceLine := tok.Line(body.Rbrace) +- braceLine := safetoken.Line(tok, body.Rbrace) - if braceLine >= tok.LineCount() { - // If we are the last line in the file, no need to fix anything. -- return +- return false - } - - // Move the right brace down one line. - body.Rbrace = tok.LineStart(braceLine + 1) +- return true -} - -// fixDanglingSelector inserts real "_" selector expressions in place @@ -18801,9 +21235,11 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g -// yields a "_" selector instead of "var" since "var" is a keyword. -// -// TODO(rfindley): should this constitute an ast 'fix'? --func fixPhantomSelector(sel *ast.SelectorExpr, tf *token.File, src []byte) { +-// +-// The resulting bool reports whether any fixing occurred. +-func fixPhantomSelector(sel *ast.SelectorExpr, tf *token.File, src []byte) bool { - if !isPhantomUnderscore(sel.Sel, tf, src) { -- return +- return false - } - - // Only consider selectors directly abutting the selector ".". This @@ -18813,15 +21249,15 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - // var bar = 123 - // - if sel.Sel.Pos() != sel.X.End()+1 { -- return +- return false - } - - maybeKeyword := readKeyword(sel.Sel.Pos(), tf, src) - if maybeKeyword == "" { -- return +- return false - } - -- replaceNode(sel, sel.Sel, &ast.Ident{ +- return replaceNode(sel, sel.Sel, &ast.Ident{ - Name: maybeKeyword, - NamePos: sel.Sel.Pos(), - }) @@ -18850,21 +21286,21 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g -// parser is looking for the conditional expression. However, "i := 0" -// are not valid expressions, so we get a BadExpr. -// --// fixInitStmt returns valid AST for the original source. --func fixInitStmt(bad *ast.BadExpr, parent ast.Node, tok *token.File, src []byte) { +-// The resulting bool reports whether any fixing occurred. +-func fixInitStmt(bad *ast.BadExpr, parent ast.Node, tok *token.File, src []byte) bool { - if !bad.Pos().IsValid() || !bad.End().IsValid() { -- return +- return false - } - - // Try to extract a statement from the BadExpr. - start, end, err := safetoken.Offsets(tok, bad.Pos(), bad.End()-1) - if err != nil { -- return +- return false - } - stmtBytes := src[start : end+1] - stmt, err := parseStmt(bad.Pos(), stmtBytes) - if err != nil { -- return +- return false - } - - // If the parent statement doesn't already have an "init" statement, @@ -18873,29 +21309,33 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - switch p := parent.(type) { - case *ast.IfStmt: - if p.Init != nil { -- return +- return false - } - p.Init = stmt - p.Cond = &ast.Ident{ - Name: "_", - NamePos: stmt.End(), - } +- return true - case *ast.ForStmt: - if p.Init != nil { -- return +- return false - } - p.Init = stmt - p.Cond = &ast.Ident{ - Name: "_", - NamePos: stmt.End(), - } +- return true - case *ast.SwitchStmt: - if p.Init != nil { -- return +- return false - } - p.Init = stmt - p.Tag = nil +- return true - } +- return false -} - -// readKeyword reads the keyword starting at pos, if any. @@ -19073,7 +21513,7 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - // the period is likely a dangling selector and needs a phantom - // "_". Likewise if the current token is on a different line than - // the period, the period is likely a dangling selector. -- if lastToken == token.PERIOD && (tkn == token.RBRACE || tok.Line(to) > tok.Line(last)) { +- if lastToken == token.PERIOD && (tkn == token.RBRACE || safetoken.Line(tok, to) > safetoken.Line(tok, last)) { - // Insert phantom "_" selector after the dangling ".". - phantomSelectors = append(phantomSelectors, last+1) - // If we aren't in a block then end the expression after the ".". @@ -19288,41 +21728,669 @@ diff -urN a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.g - - return false -} -diff -urN a/gopls/internal/lsp/cache/parsemode_go116.go b/gopls/internal/lsp/cache/parsemode_go116.go ---- a/gopls/internal/lsp/cache/parsemode_go116.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/parsemode_go116.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --// Copyright 2022 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/cache/parse_cache.go b/gopls/internal/lsp/cache/parse_cache.go +--- a/gopls/internal/lsp/cache/parse_cache.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cache/parse_cache.go 1970-01-01 08:00:00 +@@ -1,418 +0,0 @@ +-// Copyright 2023 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 cache - --// The parser.SkipObjectResolution mode flag is not supported before Go 1.17. --const skipObjectResolution = 0 -diff -urN a/gopls/internal/lsp/cache/parsemode_go117.go b/gopls/internal/lsp/cache/parsemode_go117.go ---- a/gopls/internal/lsp/cache/parsemode_go117.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/parsemode_go117.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ --// Copyright 2022 The Go Authors. All rights reserved. +-import ( +- "bytes" +- "container/heap" +- "context" +- "fmt" +- "go/parser" +- "go/token" +- "math/bits" +- "runtime" +- "sync" +- "time" +- +- "golang.org/x/sync/errgroup" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/memoize" +- "golang.org/x/tools/internal/tokeninternal" +-) +- +-// This file contains an implementation of an LRU parse cache, that offsets the +-// base token.Pos value of each cached file so that they may be later described +-// by a single dedicated FileSet. +-// +-// This is achieved by tracking a monotonic offset in the token.Pos space, that +-// is incremented before parsing allow room for the resulting parsed file. +- +-// reservedForParsing defines the room in the token.Pos space reserved for +-// cached parsed files. +-// +-// Files parsed through the parseCache are guaranteed not to have overlapping +-// spans: the parseCache tracks a monotonic base for newly parsed files. +-// +-// By offsetting the initial base of a FileSet, we can allow other operations +-// accepting the FileSet (such as the gcimporter) to add new files using the +-// normal FileSet APIs without overlapping with cached parsed files. +-// +-// Note that 1<<60 represents an exabyte of parsed data, more than any gopls +-// process can ever parse. +-// +-// On 32-bit systems we don't cache parse results (see parseFiles). +-const reservedForParsing = 1 << (bits.UintSize - 4) +- +-// fileSetWithBase returns a new token.FileSet with Base() equal to the +-// requested base. +-// +-// If base < 1, fileSetWithBase panics. +-// (1 is the smallest permitted FileSet base). +-func fileSetWithBase(base int) *token.FileSet { +- fset := token.NewFileSet() +- if base > 1 { +- // Add a dummy file to set the base of fset. We won't ever use the +- // resulting FileSet, so it doesn't matter how we achieve this. +- // +- // FileSets leave a 1-byte padding between files, so we set the base by +- // adding a zero-length file at base-1. +- fset.AddFile("", base-1, 0) +- } +- if fset.Base() != base { +- panic("unexpected FileSet.Base") +- } +- return fset +-} +- +-const ( +- // Always keep 100 recent files, independent of their wall-clock age, to +- // optimize the case where the user resumes editing after a delay. +- parseCacheMinFiles = 100 +-) +- +-// parsePadding is additional padding allocated to allow for increases in +-// length (such as appending missing braces) caused by fixAST. +-// +-// This is used to mitigate a chicken and egg problem: we must know the base +-// offset of the file we're about to parse, before we start parsing, and yet +-// src fixups may affect the actual size of the parsed content (and therefore +-// the offsets of subsequent files). +-// +-// When we encounter a file that no longer fits in its allocated space in the +-// fileset, we have no choice but to re-parse it. Leaving a generous padding +-// reduces the likelihood of this "slow path". +-// +-// This value is mutable for testing, so that we can exercise the slow path. +-var parsePadding = 1000 // mutable for testing +- +-// A parseCache holds recently accessed parsed Go files. After new files are +-// stored, older files may be evicted from the cache via garbage collection. +-// +-// The parseCache.parseFiles method exposes a batch API for parsing (and +-// caching) multiple files. This is necessary for type-checking, where files +-// must be parsed in a common fileset. +-type parseCache struct { +- expireAfter time.Duration // interval at which to collect expired cache entries +- done chan struct{} // closed when GC is stopped +- +- mu sync.Mutex +- m map[parseKey]*parseCacheEntry +- lru queue // min-atime priority queue of *parseCacheEntry +- clock uint64 // clock time, incremented when the cache is updated +- nextBase int // base offset for the next parsed file +-} +- +-// newParseCache creates a new parse cache and starts a goroutine to garbage +-// collect entries whose age is at least expireAfter. +-// +-// Callers must call parseCache.stop when the parse cache is no longer in use. +-func newParseCache(expireAfter time.Duration) *parseCache { +- c := &parseCache{ +- expireAfter: expireAfter, +- m: make(map[parseKey]*parseCacheEntry), +- done: make(chan struct{}), +- } +- go c.gc() +- return c +-} +- +-// stop causes the GC goroutine to exit. +-func (c *parseCache) stop() { +- close(c.done) +-} +- +-// parseKey uniquely identifies a parsed Go file. +-type parseKey struct { +- uri span.URI +- mode parser.Mode +- purgeFuncBodies bool +-} +- +-type parseCacheEntry struct { +- key parseKey +- hash source.Hash +- promise *memoize.Promise // memoize.Promise[*source.ParsedGoFile] +- atime uint64 // clock time of last access, for use in LRU sorting +- walltime time.Time // actual time of last access, for use in time-based eviction; too coarse for LRU on some systems +- lruIndex int // owned by the queue implementation +-} +- +-// startParse prepares a parsing pass, creating new promises in the cache for +-// any cache misses. +-// +-// The resulting slice has an entry for every given file handle, though some +-// entries may be nil if there was an error reading the file (in which case the +-// resulting error will be non-nil). +-func (c *parseCache) startParse(mode parser.Mode, purgeFuncBodies bool, fhs ...source.FileHandle) ([]*memoize.Promise, error) { +- c.mu.Lock() +- defer c.mu.Unlock() +- +- // Any parsing pass increments the clock, as we'll update access times. +- // (technically, if fhs is empty this isn't necessary, but that's a degenerate case). +- // +- // All entries parsed from a single call get the same access time. +- c.clock++ +- walltime := time.Now() +- +- // Read file data and collect cacheable files. +- var ( +- data = make([][]byte, len(fhs)) // file content for each readable file +- promises = make([]*memoize.Promise, len(fhs)) +- firstReadError error // first error from fh.Read, or nil +- ) +- for i, fh := range fhs { +- content, err := fh.Content() +- if err != nil { +- if firstReadError == nil { +- firstReadError = err +- } +- continue +- } +- data[i] = content +- +- key := parseKey{ +- uri: fh.URI(), +- mode: mode, +- purgeFuncBodies: purgeFuncBodies, +- } +- +- if e, ok := c.m[key]; ok { +- if e.hash == fh.FileIdentity().Hash { // cache hit +- e.atime = c.clock +- e.walltime = walltime +- heap.Fix(&c.lru, e.lruIndex) +- promises[i] = e.promise +- continue +- } else { +- // A cache hit, for a different version. Delete it. +- delete(c.m, e.key) +- heap.Remove(&c.lru, e.lruIndex) +- } +- } +- +- uri := fh.URI() +- promise := memoize.NewPromise("parseCache.parse", func(ctx context.Context, _ interface{}) interface{} { +- // Allocate 2*len(content)+parsePadding to allow for re-parsing once +- // inside of parseGoSrc without exceeding the allocated space. +- base, nextBase := c.allocateSpace(2*len(content) + parsePadding) +- +- pgf, fixes1 := ParseGoSrc(ctx, fileSetWithBase(base), uri, content, mode, purgeFuncBodies) +- file := pgf.Tok +- if file.Base()+file.Size()+1 > nextBase { +- // The parsed file exceeds its allocated space, likely due to multiple +- // passes of src fixing. In this case, we have no choice but to re-do +- // the operation with the correct size. +- // +- // Even though the final successful parse requires only file.Size() +- // bytes of Pos space, we need to accommodate all the missteps to get +- // there, as parseGoSrc will repeat them. +- actual := file.Base() + file.Size() - base // actual size consumed, after re-parsing +- base2, nextBase2 := c.allocateSpace(actual) +- pgf2, fixes2 := ParseGoSrc(ctx, fileSetWithBase(base2), uri, content, mode, purgeFuncBodies) +- +- // In golang/go#59097 we observed that this panic condition was hit. +- // One bug was found and fixed, but record more information here in +- // case there is still a bug here. +- if end := pgf2.Tok.Base() + pgf2.Tok.Size(); end != nextBase2-1 { +- var errBuf bytes.Buffer +- fmt.Fprintf(&errBuf, "internal error: non-deterministic parsing result:\n") +- fmt.Fprintf(&errBuf, "\t%q (%d-%d) does not span %d-%d\n", uri, pgf2.Tok.Base(), base2, end, nextBase2-1) +- fmt.Fprintf(&errBuf, "\tfirst %q (%d-%d)\n", pgf.URI, pgf.Tok.Base(), pgf.Tok.Base()+pgf.Tok.Size()) +- fmt.Fprintf(&errBuf, "\tfirst space: (%d-%d), second space: (%d-%d)\n", base, nextBase, base2, nextBase2) +- fmt.Fprintf(&errBuf, "\tfirst mode: %v, second mode: %v", pgf.Mode, pgf2.Mode) +- fmt.Fprintf(&errBuf, "\tfirst err: %v, second err: %v", pgf.ParseErr, pgf2.ParseErr) +- fmt.Fprintf(&errBuf, "\tfirst fixes: %v, second fixes: %v", fixes1, fixes2) +- panic(errBuf.String()) +- } +- pgf = pgf2 +- } +- return pgf +- }) +- promises[i] = promise +- +- // add new entry; entries are gc'ed asynchronously +- e := &parseCacheEntry{ +- key: key, +- hash: fh.FileIdentity().Hash, +- promise: promise, +- atime: c.clock, +- walltime: walltime, +- } +- c.m[e.key] = e +- heap.Push(&c.lru, e) +- } +- +- if len(c.m) != len(c.lru) { +- panic("map and LRU are inconsistent") +- } +- +- return promises, firstReadError +-} +- +-func (c *parseCache) gc() { +- const period = 10 * time.Second // gc period +- timer := time.NewTicker(period) +- defer timer.Stop() +- +- for { +- select { +- case <-c.done: +- return +- case <-timer.C: +- } +- +- c.gcOnce() +- } +-} +- +-func (c *parseCache) gcOnce() { +- now := time.Now() +- c.mu.Lock() +- defer c.mu.Unlock() +- +- for len(c.m) > parseCacheMinFiles { +- e := heap.Pop(&c.lru).(*parseCacheEntry) +- if now.Sub(e.walltime) >= c.expireAfter { +- delete(c.m, e.key) +- } else { +- heap.Push(&c.lru, e) +- break +- } +- } +-} +- +-// allocateSpace reserves the next n bytes of token.Pos space in the +-// cache. +-// +-// It returns the resulting file base, next base, and an offset FileSet to use +-// for parsing. +-func (c *parseCache) allocateSpace(size int) (int, int) { +- c.mu.Lock() +- defer c.mu.Unlock() +- +- if c.nextBase == 0 { +- // FileSet base values must be at least 1. +- c.nextBase = 1 +- } +- base := c.nextBase +- c.nextBase += size + 1 +- return base, c.nextBase +-} +- +-// parseFiles returns a ParsedGoFile for each file handle in fhs, in the +-// requested parse mode. +-// +-// For parsed files that already exists in the cache, access time will be +-// updated. For others, parseFiles will parse and store as many results in the +-// cache as space allows. +-// +-// The token.File for each resulting parsed file will be added to the provided +-// FileSet, using the tokeninternal.AddExistingFiles API. Consequently, the +-// given fset should only be used in other APIs if its base is >= +-// reservedForParsing. +-// +-// If parseFiles returns an error, it still returns a slice, +-// but with a nil entry for each file that could not be parsed. +-func (c *parseCache) parseFiles(ctx context.Context, fset *token.FileSet, mode parser.Mode, purgeFuncBodies bool, fhs ...source.FileHandle) ([]*source.ParsedGoFile, error) { +- pgfs := make([]*source.ParsedGoFile, len(fhs)) +- +- // Temporary fall-back for 32-bit systems, where reservedForParsing is too +- // small to be viable. We don't actually support 32-bit systems, so this +- // workaround is only for tests and can be removed when we stop running +- // 32-bit TryBots for gopls. +- if bits.UintSize == 32 { +- for i, fh := range fhs { +- var err error +- pgfs[i], err = parseGoImpl(ctx, fset, fh, mode, purgeFuncBodies) +- if err != nil { +- return pgfs, err +- } +- } +- return pgfs, nil +- } +- +- promises, firstErr := c.startParse(mode, purgeFuncBodies, fhs...) +- +- // Await all parsing. +- var g errgroup.Group +- g.SetLimit(runtime.GOMAXPROCS(-1)) // parsing is CPU-bound. +- for i, promise := range promises { +- if promise == nil { +- continue +- } +- i := i +- promise := promise +- g.Go(func() error { +- result, err := promise.Get(ctx, nil) +- if err != nil { +- return err +- } +- pgfs[i] = result.(*source.ParsedGoFile) +- return nil +- }) +- } +- +- if err := g.Wait(); err != nil && firstErr == nil { +- firstErr = err +- } +- +- // Augment the FileSet to map all parsed files. +- var tokenFiles []*token.File +- for _, pgf := range pgfs { +- if pgf == nil { +- continue +- } +- tokenFiles = append(tokenFiles, pgf.Tok) +- } +- tokeninternal.AddExistingFiles(fset, tokenFiles) +- +- const debugIssue59080 = true +- if debugIssue59080 { +- for _, f := range tokenFiles { +- pos := token.Pos(f.Base()) +- f2 := fset.File(pos) +- if f2 != f { +- panic(fmt.Sprintf("internal error: File(%d (start)) = %v, not %v", pos, f2, f)) +- } +- pos = token.Pos(f.Base() + f.Size()) +- f2 = fset.File(pos) +- if f2 != f { +- panic(fmt.Sprintf("internal error: File(%d (end)) = %v, not %v", pos, f2, f)) +- } +- } +- } +- +- return pgfs, firstErr +-} +- +-// -- priority queue boilerplate -- +- +-// queue is a min-atime prority queue of cache entries. +-type queue []*parseCacheEntry +- +-func (q queue) Len() int { return len(q) } +- +-func (q queue) Less(i, j int) bool { return q[i].atime < q[j].atime } +- +-func (q queue) Swap(i, j int) { +- q[i], q[j] = q[j], q[i] +- q[i].lruIndex = i +- q[j].lruIndex = j +-} +- +-func (q *queue) Push(x interface{}) { +- e := x.(*parseCacheEntry) +- e.lruIndex = len(*q) +- *q = append(*q, e) +-} +- +-func (q *queue) Pop() interface{} { +- last := len(*q) - 1 +- e := (*q)[last] +- (*q)[last] = nil // aid GC +- *q = (*q)[:last] +- return e +-} +diff -urN a/gopls/internal/lsp/cache/parse_cache_test.go b/gopls/internal/lsp/cache/parse_cache_test.go +--- a/gopls/internal/lsp/cache/parse_cache_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cache/parse_cache_test.go 1970-01-01 08:00:00 +@@ -1,233 +0,0 @@ +-// Copyright 2023 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 cache - --import "go/parser" +-import ( +- "context" +- "fmt" +- "go/token" +- "math/bits" +- "testing" +- "time" +- +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" +-) +- +-func skipIfNoParseCache(t *testing.T) { +- if bits.UintSize == 32 { +- t.Skip("the parse cache is not supported on 32-bit systems") +- } +-} +- +-func TestParseCache(t *testing.T) { +- skipIfNoParseCache(t) +- +- ctx := context.Background() +- uri := span.URI("file:///myfile") +- fh := makeFakeFileHandle(uri, []byte("package p\n\nconst _ = \"foo\"")) +- fset := token.NewFileSet() +- +- cache := newParseCache(0) +- pgfs1, err := cache.parseFiles(ctx, fset, source.ParseFull, false, fh) +- if err != nil { +- t.Fatal(err) +- } +- pgf1 := pgfs1[0] +- pgfs2, err := cache.parseFiles(ctx, fset, source.ParseFull, false, fh) +- pgf2 := pgfs2[0] +- if err != nil { +- t.Fatal(err) +- } +- if pgf1 != pgf2 { +- t.Errorf("parseFiles(%q): unexpected cache miss on repeated call", uri) +- } +- +- // Fill up the cache with other files, but don't evict the file above. +- cache.gcOnce() +- files := []source.FileHandle{fh} +- files = append(files, dummyFileHandles(parseCacheMinFiles-1)...) +- +- pgfs3, err := cache.parseFiles(ctx, fset, source.ParseFull, false, files...) +- if err != nil { +- t.Fatal(err) +- } +- pgf3 := pgfs3[0] +- if pgf3 != pgf1 { +- t.Errorf("parseFiles(%q, ...): unexpected cache miss", uri) +- } +- if pgf3.Tok.Base() != pgf1.Tok.Base() || pgf3.Tok.Size() != pgf1.Tok.Size() { +- t.Errorf("parseFiles(%q, ...): result.Tok has base: %d, size: %d, want (%d, %d)", uri, pgf3.Tok.Base(), pgf3.Tok.Size(), pgf1.Tok.Base(), pgf1.Tok.Size()) +- } +- if tok := fset.File(token.Pos(pgf3.Tok.Base())); tok != pgf3.Tok { +- t.Errorf("parseFiles(%q, ...): result.Tok not contained in FileSet", uri) +- } +- +- // Now overwrite the cache, after which we should get new results. +- cache.gcOnce() +- files = dummyFileHandles(parseCacheMinFiles) +- _, err = cache.parseFiles(ctx, fset, source.ParseFull, false, files...) +- if err != nil { +- t.Fatal(err) +- } +- // force a GC, which should collect the recently parsed files +- cache.gcOnce() +- pgfs4, err := cache.parseFiles(ctx, fset, source.ParseFull, false, fh) +- if err != nil { +- t.Fatal(err) +- } +- if pgfs4[0] == pgf1 { +- t.Errorf("parseFiles(%q): unexpected cache hit after overwriting cache", uri) +- } +-} +- +-func TestParseCache_Reparsing(t *testing.T) { +- skipIfNoParseCache(t) +- +- defer func(padding int) { +- parsePadding = padding +- }(parsePadding) +- parsePadding = 0 +- +- files := dummyFileHandles(parseCacheMinFiles) +- danglingSelector := []byte("package p\nfunc _() {\n\tx.\n}") +- files = append(files, makeFakeFileHandle("file:///bad1", danglingSelector)) +- files = append(files, makeFakeFileHandle("file:///bad2", danglingSelector)) +- +- // Parsing should succeed even though we overflow the padding. +- cache := newParseCache(0) +- _, err := cache.parseFiles(context.Background(), token.NewFileSet(), source.ParseFull, false, files...) +- if err != nil { +- t.Fatal(err) +- } +-} +- +-// Re-parsing the first file should not panic. +-func TestParseCache_Issue59097(t *testing.T) { +- skipIfNoParseCache(t) +- +- defer func(padding int) { +- parsePadding = padding +- }(parsePadding) +- parsePadding = 0 +- +- danglingSelector := []byte("package p\nfunc _() {\n\tx.\n}") +- files := []source.FileHandle{makeFakeFileHandle("file:///bad", danglingSelector)} +- +- // Parsing should succeed even though we overflow the padding. +- cache := newParseCache(0) +- _, err := cache.parseFiles(context.Background(), token.NewFileSet(), source.ParseFull, false, files...) +- if err != nil { +- t.Fatal(err) +- } +-} +- +-func TestParseCache_TimeEviction(t *testing.T) { +- skipIfNoParseCache(t) +- +- ctx := context.Background() +- fset := token.NewFileSet() +- uri := span.URI("file:///myfile") +- fh := makeFakeFileHandle(uri, []byte("package p\n\nconst _ = \"foo\"")) +- +- const gcDuration = 10 * time.Millisecond +- cache := newParseCache(gcDuration) +- cache.stop() // we'll manage GC manually, for testing. +- +- pgfs0, err := cache.parseFiles(ctx, fset, source.ParseFull, false, fh, fh) +- if err != nil { +- t.Fatal(err) +- } +- +- files := dummyFileHandles(parseCacheMinFiles) +- _, err = cache.parseFiles(ctx, fset, source.ParseFull, false, files...) +- if err != nil { +- t.Fatal(err) +- } +- +- // Even after filling up the 'min' files, we get a cache hit for our original file. +- pgfs1, err := cache.parseFiles(ctx, fset, source.ParseFull, false, fh, fh) +- if err != nil { +- t.Fatal(err) +- } +- +- if pgfs0[0] != pgfs1[0] { +- t.Errorf("before GC, got unexpected cache miss") +- } +- +- // But after GC, we get a cache miss. +- _, err = cache.parseFiles(ctx, fset, source.ParseFull, false, files...) // mark dummy files as newer +- if err != nil { +- t.Fatal(err) +- } +- time.Sleep(gcDuration) +- cache.gcOnce() +- +- pgfs2, err := cache.parseFiles(ctx, fset, source.ParseFull, false, fh, fh) +- if err != nil { +- t.Fatal(err) +- } +- +- if pgfs0[0] == pgfs2[0] { +- t.Errorf("after GC, got unexpected cache hit for %s", pgfs0[0].URI) +- } +-} +- +-func TestParseCache_Duplicates(t *testing.T) { +- skipIfNoParseCache(t) +- +- ctx := context.Background() +- uri := span.URI("file:///myfile") +- fh := makeFakeFileHandle(uri, []byte("package p\n\nconst _ = \"foo\"")) +- +- cache := newParseCache(0) +- pgfs, err := cache.parseFiles(ctx, token.NewFileSet(), source.ParseFull, false, fh, fh) +- if err != nil { +- t.Fatal(err) +- } +- if pgfs[0] != pgfs[1] { +- t.Errorf("parseFiles(fh, fh): = [%p, %p], want duplicate files", pgfs[0].File, pgfs[1].File) +- } +-} +- +-func dummyFileHandles(n int) []source.FileHandle { +- var fhs []source.FileHandle +- for i := 0; i < n; i++ { +- uri := span.URI(fmt.Sprintf("file:///_%d", i)) +- src := []byte(fmt.Sprintf("package p\nvar _ = %d", i)) +- fhs = append(fhs, makeFakeFileHandle(uri, src)) +- } +- return fhs +-} +- +-func makeFakeFileHandle(uri span.URI, src []byte) fakeFileHandle { +- return fakeFileHandle{ +- uri: uri, +- data: src, +- hash: source.HashOf(src), +- } +-} +- +-type fakeFileHandle struct { +- source.FileHandle +- uri span.URI +- data []byte +- hash source.Hash +-} +- +-func (h fakeFileHandle) URI() span.URI { +- return h.uri +-} +- +-func (h fakeFileHandle) Content() ([]byte, error) { +- return h.data, nil +-} - --const skipObjectResolution = parser.SkipObjectResolution +-func (h fakeFileHandle) FileIdentity() source.FileIdentity { +- return source.FileIdentity{ +- URI: h.uri, +- Hash: h.hash, +- } +-} diff -urN a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go --- a/gopls/internal/lsp/cache/pkg.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/pkg.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,165 +0,0 @@ ++++ b/gopls/internal/lsp/cache/pkg.go 1970-01-01 08:00:00 +@@ -1,177 +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. @@ -19336,11 +22404,12 @@ diff -urN a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go - "go/scanner" - "go/token" - "go/types" +- "sync" - - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/lsp/source/methodsets" +- "golang.org/x/tools/gopls/internal/lsp/source/xrefs" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/memoize" -) - -// Convenient local aliases for typed strings. @@ -19351,11 +22420,10 @@ diff -urN a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go - ImportPath = source.ImportPath -) - --// A Package is the union of snapshot-local information (Metadata) and shared --// type-checking information (a syntaxPackage). +-// A Package is the union of package metadata and type checking results. -// -// TODO(rfindley): for now, we do not persist the post-processing of --// loadDiagnostics, because the value of the snapshot.packages map is just the +-// loadDiagnostics, because the value of the snapshot.packages map is just the -// package handle. Fix this. -type Package struct { - m *source.Metadata @@ -19365,8 +22433,7 @@ diff -urN a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go -// syntaxPackage contains parse trees and type information for a package. -type syntaxPackage struct { - // -- identifiers -- -- id PackageID -- mode source.ParseMode +- id PackageID - - // -- outputs -- - fset *token.FileSet // for now, same as the snapshot's FileSet @@ -19377,11 +22444,28 @@ diff -urN a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go - typeErrors []types.Error - types *types.Package - typesInfo *types.Info -- importMap map[string]*types.Package // keys are PackagePaths -- hasFixedFiles bool // if true, AST was sufficiently mangled that we should hide type errors -- analyses memoize.Store // maps analyzer.Name to Promise[actionResult] -- xrefs []byte -- methodsets *methodsets.Index +- importMap map[PackagePath]*types.Package +- hasFixedFiles bool // if true, AST was sufficiently mangled that we should hide type errors +- +- xrefsOnce sync.Once +- _xrefs []byte // only used by the xrefs method +- +- methodsetsOnce sync.Once +- _methodsets *methodsets.Index // only used by the methodsets method +-} +- +-func (p *syntaxPackage) xrefs() []byte { +- p.xrefsOnce.Do(func() { +- p._xrefs = xrefs.Index(p.compiledGoFiles, p.types, p.typesInfo) +- }) +- return p._xrefs +-} +- +-func (p *syntaxPackage) methodsets() *methodsets.Index { +- p.methodsetsOnce.Do(func() { +- p._methodsets = methodsets.NewIndex(p.fset, p.types) +- }) +- return p._methodsets -} - -func (p *Package) String() string { return string(p.m.ID) } @@ -19398,8 +22482,11 @@ diff -urN a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go -type ( - fileLoadScope span.URI // load packages containing a file (including command-line-arguments) - packageLoadScope string // load a specific package (the value is its PackageID) -- moduleLoadScope string // load packages in a specific module -- viewLoadScope span.URI // load the workspace +- moduleLoadScope struct { +- dir string // dir containing the go.mod file +- modulePath string // parsed module path +- } +- viewLoadScope span.URI // load the workspace -) - -// Implement the loadScope interface. @@ -19408,10 +22495,6 @@ diff -urN a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go -func (moduleLoadScope) aScope() {} -func (viewLoadScope) aScope() {} - --func (p *Package) ParseMode() source.ParseMode { -- return p.pkg.mode --} -- -func (p *Package) CompiledGoFiles() []*source.ParsedGoFile { - return p.pkg.compiledGoFiles -} @@ -19459,18 +22542,15 @@ diff -urN a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go -// dependencies of p, or if no symbols from that package were -// referenced during the type-checking of p. -func (p *Package) DependencyTypes(path source.PackagePath) *types.Package { -- if path == p.m.PkgPath { -- return p.pkg.types -- } -- return p.pkg.importMap[string(path)] +- return p.pkg.importMap[path] -} - --func (p *Package) HasParseErrors() bool { -- return len(p.pkg.parseErrors) != 0 +-func (p *Package) GetParseErrors() []scanner.ErrorList { +- return p.pkg.parseErrors -} - --func (p *Package) HasTypeErrors() bool { -- return len(p.pkg.typeErrors) != 0 +-func (p *Package) GetTypeErrors() []types.Error { +- return p.pkg.typeErrors -} - -func (p *Package) DiagnosticsForFile(ctx context.Context, s source.Snapshot, uri span.URI) ([]*source.Diagnostic, error) { @@ -19490,8 +22570,8 @@ diff -urN a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go -} diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/session.go --- a/gopls/internal/lsp/cache/session.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/session.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,730 +0,0 @@ ++++ b/gopls/internal/lsp/cache/session.go 1970-01-01 08:00:00 +@@ -1,715 +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. @@ -19506,13 +22586,15 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - "sync" - "sync/atomic" - -- "golang.org/x/tools/gopls/internal/govulncheck" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/lsp/source/typerefs" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" +- "golang.org/x/tools/gopls/internal/vulncheck" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/gocommand" - "golang.org/x/tools/internal/imports" +- "golang.org/x/tools/internal/memoize" - "golang.org/x/tools/internal/persistent" - "golang.org/x/tools/internal/xcontext" -) @@ -19525,12 +22607,11 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - cache *Cache // shared cache - gocmdRunner *gocommand.Runner // limits go command concurrency - -- optionsMu sync.Mutex -- options *source.Options -- - viewMu sync.Mutex - views []*View -- viewMap map[span.URI]*View // map of URI->best view +- viewMap map[span.URI]*View // file->best view +- +- parseCache *parseCache - - *overlayFS -} @@ -19539,18 +22620,9 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi -func (s *Session) ID() string { return s.id } -func (s *Session) String() string { return s.id } - --// Options returns a copy of the SessionOptions for this session. --func (s *Session) Options() *source.Options { -- s.optionsMu.Lock() -- defer s.optionsMu.Unlock() -- return s.options --} -- --// SetOptions sets the options of this session to new values. --func (s *Session) SetOptions(options *source.Options) { -- s.optionsMu.Lock() -- defer s.optionsMu.Unlock() -- s.options = options +-// GoCommandRunner returns the gocommand Runner for this session. +-func (s *Session) GoCommandRunner() *gocommand.Runner { +- return s.gocmdRunner -} - -// Shutdown the session and all views it has created. @@ -19564,6 +22636,7 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - for _, view := range views { - view.shutdown() - } +- s.parseCache.stop() - event.Log(ctx, "Shutdown session", KeyShutdownSession.Of(s)) -} - @@ -19597,16 +22670,18 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - -// TODO(rfindley): clarify that createView can never be cancelled (with the -// possible exception of server shutdown). +-// On success, the caller becomes responsible for calling the release function once. -func (s *Session) createView(ctx context.Context, name string, folder span.URI, options *source.Options, seqID uint64) (*View, *snapshot, func(), error) { - index := atomic.AddInt64(&viewIndex, 1) - - // Get immutable workspace information. - info, err := s.getWorkspaceInformation(ctx, folder, options) - if err != nil { -- return nil, nil, func() {}, err +- return nil, nil, nil, err - } - -- wsModFiles, wsModFilesErr := computeWorkspaceModFiles(ctx, info.gomod, info.effectiveGOWORK(), info.effectiveGO111MODULE(), s) +- gowork, _ := info.GOWORK() +- wsModFiles, wsModFilesErr := computeWorkspaceModFiles(ctx, info.gomod, gowork, info.effectiveGO111MODULE(), s) - - // We want a true background context and not a detached context here - // the spans need to be unrelated and no tag values should pollute it. @@ -19616,14 +22691,14 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - v := &View{ - id: strconv.FormatInt(index, 10), - gocmdRunner: s.gocmdRunner, +- lastOptions: options, - initialWorkspaceLoad: make(chan struct{}), - initializationSema: make(chan struct{}, 1), -- options: options, - baseCtx: baseCtx, - name: name, -- folder: folder, - moduleUpgrades: map[span.URI]map[string]string{}, -- vulns: map[span.URI]*govulncheck.Result{}, +- vulns: map[span.URI]*vulncheck.Result{}, +- parseCache: s.parseCache, - fs: s.overlayFS, - workspaceInformation: info, - } @@ -19651,23 +22726,22 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - backgroundCtx: backgroundCtx, - cancel: cancel, - store: s.cache.store, -- packages: persistent.NewMap(packageIDLessInterface), +- packages: new(persistent.Map[PackageID, *packageHandle]), - meta: new(metadataGraph), -- files: newFilesMap(), -- parseCache: new(parseCache), -- activePackages: persistent.NewMap(packageIDLessInterface), -- symbolizeHandles: persistent.NewMap(uriLessInterface), -- analyses: persistent.NewMap(analysisKeyLessInterface), +- files: newFileMap(), +- activePackages: new(persistent.Map[PackageID, *Package]), +- symbolizeHandles: new(persistent.Map[span.URI, *memoize.Promise]), - workspacePackages: make(map[PackageID]PackagePath), -- unloadableFiles: make(map[span.URI]struct{}), -- parseModHandles: persistent.NewMap(uriLessInterface), -- parseWorkHandles: persistent.NewMap(uriLessInterface), -- modTidyHandles: persistent.NewMap(uriLessInterface), -- modVulnHandles: persistent.NewMap(uriLessInterface), -- modWhyHandles: persistent.NewMap(uriLessInterface), -- knownSubdirs: newKnownDirsSet(), +- unloadableFiles: new(persistent.Set[span.URI]), +- parseModHandles: new(persistent.Map[span.URI, *memoize.Promise]), +- parseWorkHandles: new(persistent.Map[span.URI, *memoize.Promise]), +- modTidyHandles: new(persistent.Map[span.URI, *memoize.Promise]), +- modVulnHandles: new(persistent.Map[span.URI, *memoize.Promise]), +- modWhyHandles: new(persistent.Map[span.URI, *memoize.Promise]), - workspaceModFiles: wsModFiles, - workspaceModFilesErr: wsModFilesErr, +- pkgIndex: typerefs.NewPackageIndex(), +- options: options, - } - // Save one reference in the view. - v.releaseSnapshot = v.snapshot.Acquire() @@ -19691,8 +22765,8 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - return v, snapshot, snapshot.Acquire(), nil -} - --// View returns a view with a matching name, if the session has one. --func (s *Session) View(name string) *View { +-// ViewByName returns a view with a matching name, if the session has one. +-func (s *Session) ViewByName(name string) *View { - s.viewMu.Lock() - defer s.viewMu.Unlock() - for _, view := range s.views { @@ -19703,6 +22777,18 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - return nil -} - +-// View returns the view with a matching id, if present. +-func (s *Session) View(id string) (*View, error) { +- s.viewMu.Lock() +- defer s.viewMu.Unlock() +- for _, view := range s.views { +- if view.ID() == id { +- return view, nil +- } +- } +- return nil, fmt.Errorf("no view with ID %q", id) +-} +- -// ViewOf returns a view corresponding to the given URI. -// If the file is not already associated with a view, pick one using some heuristics. -func (s *Session) ViewOf(uri span.URI) (*View, error) { @@ -19744,9 +22830,15 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - } - // TODO(rfindley): this should consider the workspace layout (i.e. - // go.work). -- if view.contains(uri) { +- snapshot, release, err := view.getSnapshot() +- if err != nil { +- // view is shutdown +- continue +- } +- if snapshot.contains(uri) { - longest = view - } +- release() - } - if longest != nil { - return longest @@ -19765,6 +22857,7 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi -func (s *Session) RemoveView(view *View) { - s.viewMu.Lock() - defer s.viewMu.Unlock() +- - i := s.dropView(view) - if i == -1 { // error reported elsewhere - return @@ -19774,18 +22867,11 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - s.views = removeElement(s.views, i) -} - --// updateView recreates the view with the given options. +-// updateViewLocked recreates the view with the given options. -// -// If the resulting error is non-nil, the view may or may not have already been -// dropped from the session. --func (s *Session) updateView(ctx context.Context, view *View, options *source.Options) (*View, error) { -- s.viewMu.Lock() -- defer s.viewMu.Unlock() -- -- return s.updateViewLocked(ctx, view, options) --} -- --func (s *Session) updateViewLocked(ctx context.Context, view *View, options *source.Options) (*View, error) { +-func (s *Session) updateViewLocked(ctx context.Context, view *View, options *source.Options) error { - // Preserve the snapshot ID if we are recreating the view. - view.snapshotMu.Lock() - if view.snapshot == nil { @@ -19797,22 +22883,32 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - - i := s.dropView(view) - if i == -1 { -- return nil, fmt.Errorf("view %q not found", view.id) +- return fmt.Errorf("view %q not found", view.id) - } - -- v, _, release, err := s.createView(ctx, view.name, view.folder, options, seqID) -- release() -- +- v, snapshot, release, err := s.createView(ctx, view.name, view.folder, options, seqID) - if err != nil { - // we have dropped the old view, but could not create the new one - // this should not happen and is very bad, but we still need to clean - // up the view array if it happens - s.views = removeElement(s.views, i) -- return nil, err +- return err +- } +- defer release() +- +- // The new snapshot has lost the history of the previous view. As a result, +- // it may not see open files that aren't in its build configuration (as it +- // would have done via didOpen notifications). This can lead to inconsistent +- // behavior when configuration is changed mid-session. +- // +- // Ensure the new snapshot observes all open files. +- for _, o := range v.fs.Overlays() { +- _, _ = snapshot.ReadFile(ctx, o.URI()) - } +- - // substitute the new view into the array where the old view was - s.views[i] = v -- return v, nil +- return nil -} - -// removeElement removes the ith element from the slice replacing it with the last element. @@ -19850,19 +22946,6 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - return err -} - --// TODO(rfindley): fileChange seems redundant with source.FileModification. --// De-dupe into a common representation for changes. --type fileChange struct { -- content []byte -- exists bool -- fileHandle source.FileHandle -- -- // isUnchanged indicates whether the file action is one that does not -- // change the actual contents of the file. Opens and closes should not -- // be treated like other changes, since the file content doesn't change. -- isUnchanged bool --} -- -// DidModifyFiles reports a file modification to the session. It returns -// the new snapshots after the modifications have been applied, paired with -// the affected file URIs for those snapshots. @@ -19896,6 +22979,8 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - checkViews := false - - for _, c := range changes { +- // TODO(rfindley): go.work files need not be named "go.work" -- we need to +- // check each view's source. - if isGoMod(c.URI) || isGoWork(c.URI) { - // Change, InvalidateMetadata, and UnknownFileAction actions do not cause - // us to re-evaluate views. @@ -19916,7 +23001,7 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - // synchronously to change processing? Can we assume that the env did not - // change, and derive go.work using a combination of the configured - // GOWORK value and filesystem? -- info, err := s.getWorkspaceInformation(ctx, view.folder, view.Options()) +- info, err := s.getWorkspaceInformation(ctx, view.folder, view.lastOptions) - if err != nil { - // Catastrophic failure, equivalent to a failure of session - // initialization and therefore should almost never happen. One @@ -19930,8 +23015,7 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - } - - if info != view.workspaceInformation { -- _, err := s.updateViewLocked(ctx, view, view.Options()) -- if err != nil { +- if err := s.updateViewLocked(ctx, view, view.lastOptions); err != nil { - // More catastrophic failure. The view may or may not still exist. - // The best we can do is log and move on. - event.Error(ctx, "recreating view", err) @@ -19941,7 +23025,7 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - } - - // Collect information about views affected by these changes. -- views := make(map[*View]map[span.URI]*fileChange) +- views := make(map[*View]map[span.URI]source.FileHandle) - affectedViews := map[span.URI][]*View{} - // forceReloadMetadata records whether any change is the magic - // source.InvalidateMetadata action. @@ -19974,37 +23058,22 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - } - affectedViews[c.URI] = changedViews - -- isUnchanged := c.Action == source.Open || c.Action == source.Close -- - // Apply the changes to all affected views. +- fh := mustReadFile(ctx, s, c.URI) - for _, view := range changedViews { - // Make sure that the file is added to the view's seenFiles set. - view.markKnown(c.URI) - if _, ok := views[view]; !ok { -- views[view] = make(map[span.URI]*fileChange) -- } -- fh, err := s.GetFile(ctx, c.URI) -- if err != nil { -- return nil, nil, err -- } -- content, err := fh.Read() -- if err != nil { -- // Ignore the error: the file may be deleted. -- content = nil -- } -- views[view][c.URI] = &fileChange{ -- content: content, -- exists: err == nil, -- fileHandle: fh, -- isUnchanged: isUnchanged, +- views[view] = make(map[span.URI]source.FileHandle) - } +- views[view][c.URI] = fh - } - } - - var releases []func() - viewToSnapshot := map[*View]*snapshot{} - for view, changed := range views { -- snapshot, release := view.invalidateContent(ctx, changed, forceReloadMetadata) +- snapshot, release := view.invalidateContent(ctx, changed, nil, forceReloadMetadata) - releases = append(releases, release) - viewToSnapshot[view] = snapshot - } @@ -20054,61 +23123,36 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - } - s.viewMu.Unlock() - -- knownDirs := knownDirectories(ctx, snapshots) -- defer knownDirs.Destroy() -- +- // Expand the modification to any file we could care about, which we define +- // to be any file observed by any of the snapshots. +- // +- // There may be other files in the directory, but if we haven't read them yet +- // we don't need to invalidate them. - var result []source.FileModification - for _, c := range changes { -- if !knownDirs.Contains(c.URI) { -- result = append(result, c) -- continue +- expanded := make(map[span.URI]bool) +- for _, snapshot := range snapshots { +- for _, uri := range snapshot.filesInDir(c.URI) { +- expanded[uri] = true +- } - } -- affectedFiles := knownFilesInDir(ctx, snapshots, c.URI) -- var fileChanges []source.FileModification -- for uri := range affectedFiles { -- fileChanges = append(fileChanges, source.FileModification{ -- URI: uri, -- Action: c.Action, -- LanguageID: "", -- OnDisk: c.OnDisk, -- // changes to directories cannot include text or versions -- }) -- } -- result = append(result, fileChanges...) -- } -- return result --} -- --// knownDirectories returns all of the directories known to the given --// snapshots, including workspace directories and their subdirectories. --// It is responsibility of the caller to destroy the returned set. --func knownDirectories(ctx context.Context, snapshots []*snapshot) knownDirsSet { -- result := newKnownDirsSet() -- for _, snapshot := range snapshots { -- dirs := snapshot.dirs(ctx) -- for _, dir := range dirs { -- result.Insert(dir) +- if len(expanded) == 0 { +- result = append(result, c) +- } else { +- for uri := range expanded { +- result = append(result, source.FileModification{ +- URI: uri, +- Action: c.Action, +- LanguageID: "", +- OnDisk: c.OnDisk, +- // changes to directories cannot include text or versions +- }) +- } - } -- knownSubdirs := snapshot.getKnownSubdirs(dirs) -- result.SetAll(knownSubdirs) -- knownSubdirs.Destroy() - } - return result -} - --// knownFilesInDir returns the files known to the snapshots in the session. --// It does not respect symlinks. --func knownFilesInDir(ctx context.Context, snapshots []*snapshot, dir span.URI) map[span.URI]struct{} { -- files := map[span.URI]struct{}{} -- -- for _, snapshot := range snapshots { -- for _, uri := range snapshot.knownFilesInDir(ctx, dir) { -- files[uri] = struct{}{} -- } -- } -- return files --} -- -// Precondition: caller holds s.viewMu lock. -// TODO(rfindley): move this to fs_overlay.go. -func (fs *overlayFS) updateOverlays(ctx context.Context, changes []source.FileModification) error { @@ -20178,11 +23222,8 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - } - sameContentOnDisk = true - default: -- fh, err := fs.delegate.GetFile(ctx, c.URI) -- if err != nil { -- return err -- } -- _, readErr := fh.Read() +- fh := mustReadFile(ctx, fs.delegate, c.URI) +- _, readErr := fh.Content() - sameContentOnDisk = (readErr == nil && fh.FileIdentity().Hash == hash) - } - o = &Overlay{ @@ -20203,9 +23244,33 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi - return nil -} - --// FileWatchingGlobPatterns returns glob patterns to watch every directory --// known by the view. For views within a module, this is the module root, --// any directory in the module root, and any replace targets. +-func mustReadFile(ctx context.Context, fs source.FileSource, uri span.URI) source.FileHandle { +- ctx = xcontext.Detach(ctx) +- fh, err := fs.ReadFile(ctx, uri) +- if err != nil { +- // ReadFile cannot fail with an uncancellable context. +- bug.Reportf("reading file failed unexpectedly: %v", err) +- return brokenFile{uri, err} +- } +- return fh +-} +- +-// A brokenFile represents an unexpected failure to read a file. +-type brokenFile struct { +- uri span.URI +- err error +-} +- +-func (b brokenFile) URI() span.URI { return b.uri } +-func (b brokenFile) FileIdentity() source.FileIdentity { return source.FileIdentity{URI: b.uri} } +-func (b brokenFile) SameContentsOnDisk() bool { return false } +-func (b brokenFile) Version() int32 { return 0 } +-func (b brokenFile) Content() ([]byte, error) { return nil, b.err } +- +-// FileWatchingGlobPatterns returns a new set of glob patterns to +-// watch every directory known by the view. For views within a module, +-// this is the module root, any directory in the module root, and any +-// replace targets. -func (s *Session) FileWatchingGlobPatterns(ctx context.Context) map[string]struct{} { - s.viewMu.Lock() - defer s.viewMu.Unlock() @@ -20224,8 +23289,8 @@ diff -urN a/gopls/internal/lsp/cache/session.go b/gopls/internal/lsp/cache/sessi -} diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go --- a/gopls/internal/lsp/cache/snapshot.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/snapshot.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,2214 +0,0 @@ ++++ b/gopls/internal/lsp/cache/snapshot.go 1970-01-01 08:00:00 +@@ -1,2472 +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. @@ -20238,10 +23303,10 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - "errors" - "fmt" - "go/ast" +- "go/build/constraint" - "go/token" - "go/types" - "io" -- "io/ioutil" - "log" - "os" - "path/filepath" @@ -20257,12 +23322,15 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - "golang.org/x/sync/errgroup" - "golang.org/x/tools/go/packages" - "golang.org/x/tools/go/types/objectpath" +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/filecache" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/lsp/source/methodsets" +- "golang.org/x/tools/gopls/internal/lsp/source/typerefs" - "golang.org/x/tools/gopls/internal/lsp/source/xrefs" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/event/tag" - "golang.org/x/tools/internal/gocommand" @@ -20300,14 +23368,17 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // view.initializationSema. - initialized bool - // initializedErr holds the last error resulting from initialization. If -- // initialization fails, we only retry when the the workspace modules change, +- // initialization fails, we only retry when the workspace modules change, - // to avoid too many go/packages calls. - initializedErr *source.CriticalError - - // mu guards all of the maps in the snapshot, as well as the builtin URI. - mu sync.Mutex - -- // builtin pins the AST and package for builtin.go in memory. +- // builtin is the location of builtin.go in GOROOT. +- // +- // TODO(rfindley): would it make more sense to eagerly parse builtin, and +- // instead store a *ParsedGoFile here? - builtin span.URI - - // meta holds loaded metadata. @@ -20319,14 +23390,11 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - - // files maps file URIs to their corresponding FileHandles. - // It may invalidated when a file's content changes. -- files filesMap -- -- // parseCache holds an LRU cache of recently parsed files. -- parseCache *parseCache +- files *fileMap - - // symbolizeHandles maps each file URI to a handle for the future - // result of computing the symbols declared in that file. -- symbolizeHandles *persistent.Map // from span.URI to *memoize.Promise[symbolizeResult] +- symbolizeHandles *persistent.Map[span.URI, *memoize.Promise] // *memoize.Promise[symbolizeResult] - - // packages maps a packageKey to a *packageHandle. - // It may be invalidated when a file's content changes. @@ -20335,21 +23403,17 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // - packages.Get(id).meta == meta.metadata[id] for all ids - // - if a package is in packages, then all of its dependencies should also - // be in packages, unless there is a missing import -- packages *persistent.Map // from packageID to *packageHandle +- packages *persistent.Map[PackageID, *packageHandle] - - // activePackages maps a package ID to a memoized active package, or nil if - // the package is known not to be open. - // - // IDs not contained in the map are not known to be open or not open. -- activePackages *persistent.Map // from packageID to *Package -- -- // analyses maps an analysisKey (which identifies a package -- // and a set of analyzers) to the handle for the future result -- // of loading the package and analyzing it. -- analyses *persistent.Map // from analysisKey to analysisPromise +- activePackages *persistent.Map[PackageID, *Package] - - // workspacePackages contains the workspace's packages, which are loaded -- // when the view is created. +- // when the view is created. It contains no intermediate test variants. +- // TODO(rfindley): use a persistent.Map. - workspacePackages map[PackageID]PackagePath - - // shouldLoad tracks packages that need to be reloaded, mapping a PackageID @@ -20360,34 +23424,25 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - shouldLoad map[PackageID][]PackagePath - - // unloadableFiles keeps track of files that we've failed to load. -- unloadableFiles map[span.URI]struct{} +- unloadableFiles *persistent.Set[span.URI] - - // TODO(rfindley): rename the handles below to "promises". A promise is - // different from a handle (we mutate the package handle.) - - // parseModHandles keeps track of any parseModHandles for the snapshot. - // The handles need not refer to only the view's go.mod file. -- parseModHandles *persistent.Map // from span.URI to *memoize.Promise[parseModResult] +- parseModHandles *persistent.Map[span.URI, *memoize.Promise] // *memoize.Promise[parseModResult] - - // parseWorkHandles keeps track of any parseWorkHandles for the snapshot. - // The handles need not refer to only the view's go.work file. -- parseWorkHandles *persistent.Map // from span.URI to *memoize.Promise[parseWorkResult] +- parseWorkHandles *persistent.Map[span.URI, *memoize.Promise] // *memoize.Promise[parseWorkResult] - - // Preserve go.mod-related handles to avoid garbage-collecting the results - // of various calls to the go command. The handles need not refer to only - // the view's go.mod file. -- modTidyHandles *persistent.Map // from span.URI to *memoize.Promise[modTidyResult] -- modWhyHandles *persistent.Map // from span.URI to *memoize.Promise[modWhyResult] -- modVulnHandles *persistent.Map // from span.URI to *memoize.Promise[modVulnResult] -- -- // knownSubdirs is the set of subdirectories in the workspace, used to -- // create glob patterns for file watching. -- knownSubdirs knownDirsSet -- knownSubdirsPatternCache string -- // unprocessedSubdirChanges are any changes that might affect the set of -- // subdirectories in the workspace. They are not reflected to knownSubdirs -- // during the snapshot cloning step as it can slow down cloning. -- unprocessedSubdirChanges []*fileChange +- modTidyHandles *persistent.Map[span.URI, *memoize.Promise] // *memoize.Promise[modTidyResult] +- modWhyHandles *persistent.Map[span.URI, *memoize.Promise] // *memoize.Promise[modWhyResult] +- modVulnHandles *persistent.Map[span.URI, *memoize.Promise] // *memoize.Promise[modVulnResult] - - // workspaceModFiles holds the set of mod files active in this snapshot. - // @@ -20397,6 +23452,26 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // This set is immutable inside the snapshot, and therefore is not guarded by mu. - workspaceModFiles map[span.URI]struct{} - workspaceModFilesErr error // error encountered computing workspaceModFiles +- +- // importGraph holds a shared import graph to use for type-checking. Adding +- // more packages to this import graph can speed up type checking, at the +- // expense of in-use memory. +- // +- // See getImportGraph for additional documentation. +- importGraphDone chan struct{} // closed when importGraph is set; may be nil +- importGraph *importGraph // copied from preceding snapshot and re-evaluated +- +- // pkgIndex is an index of package IDs, for efficient storage of typerefs. +- pkgIndex *typerefs.PackageIndex +- +- // Only compute module prefixes once, as they are used with high frequency to +- // detect ignored files. +- ignoreFilterOnce sync.Once +- ignoreFilter *ignoreFilter +- +- // options holds the user configuration at the time this snapshot was +- // created. +- options *source.Options -} - -var globalSnapshotID uint64 @@ -20441,7 +23516,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -// The destroyedBy argument is used for debugging. -// -// v.snapshotMu must be held while calling this function, in order to preserve --// the invariants described by the the docstring for v.snapshot. +-// the invariants described by the docstring for v.snapshot. -func (v *View) destroy(s *snapshot, destroyedBy string) { - v.snapshotWG.Add(1) - go func() { @@ -20463,15 +23538,14 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - - s.packages.Destroy() - s.activePackages.Destroy() -- s.analyses.Destroy() - s.files.Destroy() -- s.knownSubdirs.Destroy() - s.symbolizeHandles.Destroy() - s.parseModHandles.Destroy() - s.parseWorkHandles.Destroy() - s.modTidyHandles.Destroy() - s.modVulnHandles.Destroy() - s.modWhyHandles.Destroy() +- s.unloadableFiles.Destroy() -} - -func (s *snapshot) SequenceID() uint64 { @@ -20486,6 +23560,41 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - return s.view -} - +-func (s *snapshot) FileKind(fh source.FileHandle) source.FileKind { +- // The kind of an unsaved buffer comes from the +- // TextDocumentItem.LanguageID field in the didChange event, +- // not from the file name. They may differ. +- if o, ok := fh.(*Overlay); ok { +- if o.kind != source.UnknownKind { +- return o.kind +- } +- } +- +- fext := filepath.Ext(fh.URI().Filename()) +- switch fext { +- case ".go": +- return source.Go +- case ".mod": +- return source.Mod +- case ".sum": +- return source.Sum +- case ".work": +- return source.Work +- } +- exts := s.options.TemplateExtensions +- for _, ext := range exts { +- if fext == ext || fext == "."+ext { +- return source.Tmpl +- } +- } +- // and now what? This should never happen, but it does for cgo before go1.15 +- return source.Go +-} +- +-func (s *snapshot) Options() *source.Options { +- return s.options // temporarily return view options. +-} +- -func (s *snapshot) BackgroundContext() context.Context { - return s.backgroundCtx -} @@ -20499,7 +23608,8 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -} - -func (s *snapshot) WorkFile() span.URI { -- return s.view.effectiveGOWORK() +- gowork, _ := s.view.GOWORK() +- return gowork -} - -func (s *snapshot) Templates() map[span.URI]source.FileHandle { @@ -20508,60 +23618,32 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - - tmpls := map[span.URI]source.FileHandle{} - s.files.Range(func(k span.URI, fh source.FileHandle) { -- if s.view.FileKind(fh) == source.Tmpl { +- if s.FileKind(fh) == source.Tmpl { - tmpls[k] = fh - } - }) - return tmpls -} - --func (s *snapshot) ValidBuildConfiguration() bool { +-func (s *snapshot) validBuildConfiguration() bool { - // Since we only really understand the `go` command, if the user has a - // different GOPACKAGESDRIVER, assume that their configuration is valid. - if s.view.hasGopackagesDriver { - return true - } +- - // Check if the user is working within a module or if we have found - // multiple modules in the workspace. - if len(s.workspaceModFiles) > 0 { - return true - } -- // The user may have a multiple directories in their GOPATH. -- // Check if the workspace is within any of them. -- // TODO(rfindley): this should probably be subject to "if GO111MODULES = off {...}". -- for _, gp := range filepath.SplitList(s.view.gopath) { -- if source.InDir(filepath.Join(gp, "src"), s.view.folder.Filename()) { -- return true -- } -- } -- return false --} - --// moduleMode reports whether the current snapshot uses Go modules. --// --// From https://go.dev/ref/mod, module mode is active if either of the --// following hold: --// - GO111MODULE=on --// - GO111MODULE=auto and we are inside a module or have a GOWORK value. --// --// Additionally, this method returns false if GOPACKAGESDRIVER is set. --// --// TODO(rfindley): use this more widely. --func (s *snapshot) moduleMode() bool { -- // Since we only really understand the `go` command, if the user has a -- // different GOPACKAGESDRIVER, assume that their configuration is valid. -- if s.view.hasGopackagesDriver { -- return false -- } -- -- switch s.view.effectiveGO111MODULE() { -- case on: +- // TODO(rfindley): this should probably be subject to "if GO111MODULES = off {...}". +- if s.view.inGOPATH { - return true -- case off: -- return false -- default: -- return len(s.workspaceModFiles) > 0 || s.view.gowork != "" - } +- +- return false -} - -// workspaceMode describes the way in which the snapshot's workspace should @@ -20573,7 +23655,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - - // If the view has an invalid configuration, don't build the workspace - // module. -- validBuildConfiguration := s.ValidBuildConfiguration() +- validBuildConfiguration := s.validBuildConfiguration() - if !validBuildConfiguration { - return mode - } @@ -20584,8 +23666,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - return mode - } - mode |= moduleMode -- options := s.view.Options() -- if options.TempModfile { +- if s.options.TempModfile { - mode |= tempModfile - } - return mode @@ -20598,9 +23679,6 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -// multiple modules in on config, so buildOverlay needs to filter overlays by -// module. -func (s *snapshot) config(ctx context.Context, inv *gocommand.Invocation) *packages.Config { -- s.view.optionsMu.Lock() -- verboseOutput := s.view.options.VerboseOutput -- s.view.optionsMu.Unlock() - - cfg := &packages.Config{ - Context: ctx, @@ -20623,7 +23701,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - panic("go/packages must not be used to parse files") - }, - Logf: func(format string, args ...interface{}) { -- if verboseOutput { +- if s.options.VerboseOutput { - event.Log(ctx, fmt.Sprintf(format, args...)) - } - }, @@ -20685,11 +23763,11 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - return false, nil, nil, nil - } - var modBytes, sumBytes []byte -- modBytes, err = ioutil.ReadFile(tmpURI.Filename()) +- modBytes, err = os.ReadFile(tmpURI.Filename()) - if err != nil && !os.IsNotExist(err) { - return false, nil, nil, err - } -- sumBytes, err = ioutil.ReadFile(strings.TrimSuffix(tmpURI.Filename(), ".mod") + ".sum") +- sumBytes, err = os.ReadFile(strings.TrimSuffix(tmpURI.Filename(), ".mod") + ".sum") - if err != nil && !os.IsNotExist(err) { - return false, nil, nil, err - } @@ -20705,18 +23783,16 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -// it used only after call to tempModFile. Clarify that it is only -// non-nil on success. -func (s *snapshot) goCommandInvocation(ctx context.Context, flags source.InvocationFlags, inv *gocommand.Invocation) (tmpURI span.URI, updatedInv *gocommand.Invocation, cleanup func(), err error) { -- s.view.optionsMu.Lock() -- allowModfileModificationOption := s.view.options.AllowModfileModifications -- allowNetworkOption := s.view.options.AllowImplicitNetworkAccess +- allowModfileModificationOption := s.options.AllowModfileModifications +- allowNetworkOption := s.options.AllowImplicitNetworkAccess - - // TODO(rfindley): this is very hard to follow, and may not even be doing the - // right thing: should inv.Env really trample view.options? Do we ever invoke - // this with a non-empty inv.Env? - // - // We should refactor to make it clearer that the correct env is being used. -- inv.Env = append(append(append(os.Environ(), s.view.options.EnvSlice()...), inv.Env...), "GO111MODULE="+s.view.GO111MODULE()) -- inv.BuildFlags = append([]string{}, s.view.options.BuildFlags...) -- s.view.optionsMu.Unlock() +- inv.Env = append(append(append(os.Environ(), s.options.EnvSlice()...), inv.Env...), "GO111MODULE="+s.view.GO111MODULE()) +- inv.BuildFlags = append([]string{}, s.options.BuildFlags...) - cleanup = func() {} // fallback - - // All logic below is for module mode. @@ -20750,7 +23826,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // the main (workspace) module. Otherwise, we should use the module for - // the passed-in working dir. - if mode == source.LoadWorkspace { -- if s.view.effectiveGOWORK() == "" && s.view.gomod != "" { +- if gowork, _ := s.view.GOWORK(); gowork == "" && s.view.gomod != "" { - modURI = s.view.gomod - } - } else { @@ -20759,11 +23835,11 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - - var modContent []byte - if modURI != "" { -- modFH, err := s.GetFile(ctx, modURI) +- modFH, err := s.ReadFile(ctx, modURI) - if err != nil { - return "", nil, cleanup, err - } -- modContent, err = modFH.Read() +- modContent, err = modFH.Content() - if err != nil { - return "", nil, cleanup, err - } @@ -20816,7 +23892,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - if modURI == "" { - return "", nil, cleanup, fmt.Errorf("no go.mod file found in %s", inv.WorkingDir) - } -- modFH, err := s.GetFile(ctx, modURI) +- modFH, err := s.ReadFile(ctx, modURI) - if err != nil { - return "", nil, cleanup, err - } @@ -20833,24 +23909,26 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -} - -func (s *snapshot) buildOverlay() map[string][]byte { -- s.mu.Lock() -- defer s.mu.Unlock() -- - overlays := make(map[string][]byte) -- s.files.Range(func(uri span.URI, fh source.FileHandle) { -- overlay, ok := fh.(*Overlay) -- if !ok { -- return -- } +- for _, overlay := range s.overlays() { - if overlay.saved { -- return +- continue - } -- // TODO(rstambler): Make sure not to send overlays outside of the current view. -- overlays[uri.Filename()] = overlay.content -- }) +- // TODO(rfindley): previously, there was a todo here to make sure we don't +- // send overlays outside of the current view. IMO we should instead make +- // sure this doesn't matter. +- overlays[overlay.URI().Filename()] = overlay.content +- } - return overlays -} - +-func (s *snapshot) overlays() []*Overlay { +- s.mu.Lock() +- defer s.mu.Unlock() +- +- return s.files.Overlays() +-} +- -// Package data kinds, identifying various package data that may be stored in -// the file cache. -const ( @@ -20858,37 +23936,59 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - methodSetsKind = "methodsets" - exportDataKind = "export" - diagnosticsKind = "diagnostics" +- typerefsKind = "typerefs" -) - -func (s *snapshot) PackageDiagnostics(ctx context.Context, ids ...PackageID) (map[span.URI][]*source.Diagnostic, error) { -- // TODO(rfindley): opt: avoid unnecessary encode->decode after type-checking. -- data, err := s.getPackageData(ctx, diagnosticsKind, ids, func(p *syntaxPackage) []byte { -- return encodeDiagnostics(p.diagnostics) -- }) +- ctx, done := event.Start(ctx, "cache.snapshot.PackageDiagnostics") +- defer done() +- +- var mu sync.Mutex - perFile := make(map[span.URI][]*source.Diagnostic) -- for _, data := range data { -- if data != nil { -- for _, diag := range data.m.Diagnostics { -- perFile[diag.URI] = append(perFile[diag.URI], diag) -- } -- diags := decodeDiagnostics(data.data) -- for _, diag := range diags { -- perFile[diag.URI] = append(perFile[diag.URI], diag) -- } +- collect := func(diags []*source.Diagnostic) { +- mu.Lock() +- defer mu.Unlock() +- for _, diag := range diags { +- perFile[diag.URI] = append(perFile[diag.URI], diag) +- } +- } +- pre := func(i int, ph *packageHandle) bool { +- data, err := filecache.Get(diagnosticsKind, ph.key) +- if err == nil { // hit +- collect(ph.m.Diagnostics) +- collect(decodeDiagnostics(data)) +- return false +- } else if err != filecache.ErrNotFound { +- event.Error(ctx, "reading diagnostics from filecache", err) - } +- return true - } -- return perFile, err +- post := func(_ int, pkg *Package) { +- collect(pkg.m.Diagnostics) +- collect(pkg.pkg.diagnostics) +- } +- return perFile, s.forEachPackage(ctx, ids, pre, post) -} - -func (s *snapshot) References(ctx context.Context, ids ...PackageID) ([]source.XrefIndex, error) { -- data, err := s.getPackageData(ctx, xrefsKind, ids, func(p *syntaxPackage) []byte { return p.xrefs }) +- ctx, done := event.Start(ctx, "cache.snapshot.References") +- defer done() +- - indexes := make([]source.XrefIndex, len(ids)) -- for i, data := range data { -- if data != nil { -- indexes[i] = XrefIndex{m: data.m, data: data.data} +- pre := func(i int, ph *packageHandle) bool { +- data, err := filecache.Get(xrefsKind, ph.key) +- if err == nil { // hit +- indexes[i] = XrefIndex{m: ph.m, data: data} +- return false +- } else if err != filecache.ErrNotFound { +- event.Error(ctx, "reading xrefs from filecache", err) - } +- return true - } -- return indexes, err +- post := func(i int, pkg *Package) { +- indexes[i] = XrefIndex{m: pkg.m, data: pkg.pkg.xrefs()} +- } +- return indexes, s.forEachPackage(ctx, ids, pre, post) -} - -// An XrefIndex is a helper for looking up a package in a given package. @@ -20902,24 +24002,39 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -} - -func (s *snapshot) MethodSets(ctx context.Context, ids ...PackageID) ([]*methodsets.Index, error) { -- // TODO(rfindley): opt: avoid unnecessary encode->decode after type-checking. -- data, err := s.getPackageData(ctx, methodSetsKind, ids, func(p *syntaxPackage) []byte { -- return p.methodsets.Encode() -- }) +- ctx, done := event.Start(ctx, "cache.snapshot.MethodSets") +- defer done() +- - indexes := make([]*methodsets.Index, len(ids)) -- for i, data := range data { -- if data != nil { -- indexes[i] = methodsets.Decode(data.data) -- } else if ids[i] == "unsafe" { -- indexes[i] = &methodsets.Index{} -- } else { -- panic(fmt.Sprintf("nil data for %s", ids[i])) +- pre := func(i int, ph *packageHandle) bool { +- data, err := filecache.Get(methodSetsKind, ph.key) +- if err == nil { // hit +- indexes[i] = methodsets.Decode(data) +- return false +- } else if err != filecache.ErrNotFound { +- event.Error(ctx, "reading methodsets from filecache", err) - } +- return true +- } +- post := func(i int, pkg *Package) { +- indexes[i] = pkg.pkg.methodsets() - } -- return indexes, err +- return indexes, s.forEachPackage(ctx, ids, pre, post) -} - -func (s *snapshot) MetadataForFile(ctx context.Context, uri span.URI) ([]*source.Metadata, error) { +- if s.view.ViewType() == AdHocView { +- // As described in golang/go#57209, in ad-hoc workspaces (where we load ./ +- // rather than ./...), preempting the directory load with file loads can +- // lead to an inconsistent outcome, where certain files are loaded with +- // command-line-arguments packages and others are loaded only in the ad-hoc +- // package. Therefore, ensure that the workspace is loaded before doing any +- // file loads. +- if err := s.awaitLoaded(ctx); err != nil { +- return nil, err +- } +- } +- - s.mu.Lock() - - // Start with the set of package associations derived from the last load. @@ -20933,7 +24048,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - } - - // Check if uri is known to be unloadable. -- _, unloadable := s.unloadableFiles[uri] +- unloadable := s.unloadableFiles.Contains(uri) - - s.mu.Unlock() - @@ -20945,12 +24060,22 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - scope := fileLoadScope(uri) - err := s.load(ctx, false, scope) - -- // Guard against failed loads due to context cancellation. - // - // Return the context error here as the current operation is no longer - // valid. -- if ctxErr := ctx.Err(); ctxErr != nil { -- return nil, ctxErr +- if err != nil { +- // Guard against failed loads due to context cancellation. We don't want +- // to mark loads as completed if they failed due to context cancellation. +- if ctx.Err() != nil { +- return nil, ctx.Err() +- } +- +- // Don't return an error here, as we may still return stale IDs. +- // Furthermore, the result of MetadataForFile should be consistent upon +- // subsequent calls, even if the file is marked as unloadable. +- if !errors.Is(err, errNoPackages) { +- event.Error(ctx, "MetadataForFile", err) +- } - } - - // We must clear scopes after loading. @@ -20959,13 +24084,6 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // packages as loaded. We could do this from snapshot.load and avoid - // raciness. - s.clearShouldLoad(scope) -- -- // Don't return an error here, as we may still return stale IDs. -- // Furthermore, the result of MetadataForFile should be consistent upon -- // subsequent calls, even if the file is marked as unloadable. -- if err != nil && !errors.Is(err, errNoPackages) { -- event.Error(ctx, "MetadataForFile", err) -- } - } - - // Retrieve the metadata. @@ -20983,17 +24101,27 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // so if we get here and still have - // no IDs, uri is unloadable. - if !unloadable && len(ids) == 0 { -- s.unloadableFiles[uri] = struct{}{} +- s.unloadableFiles.Add(uri) - } - -- // Sort packages "narrowest" to "widest" (in practice: non-tests before tests). +- // Sort packages "narrowest" to "widest" (in practice: +- // non-tests before tests), and regular packages before +- // their intermediate test variants (which have the same +- // files but different imports). - sort.Slice(metas, func(i, j int) bool { -- return len(metas[i].CompiledGoFiles) < len(metas[j].CompiledGoFiles) +- x, y := metas[i], metas[j] +- xfiles, yfiles := len(x.CompiledGoFiles), len(y.CompiledGoFiles) +- if xfiles != yfiles { +- return xfiles < yfiles +- } +- return boolLess(x.IsIntermediateTestVariant(), y.IsIntermediateTestVariant()) - }) - - return metas, nil -} - +-func boolLess(x, y bool) bool { return !x && y } // false < true +- -func (s *snapshot) ReverseDependencies(ctx context.Context, id PackageID, transitive bool) (map[PackageID]*source.Metadata, error) { - if err := s.awaitLoaded(ctx); err != nil { - return nil, err @@ -21024,21 +24152,15 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - return rdeps, nil -} - --func (s *snapshot) workspaceMetadata() (meta []*source.Metadata) { -- s.mu.Lock() -- defer s.mu.Unlock() -- -- for id := range s.workspacePackages { -- meta = append(meta, s.meta.metadata[id]) -- } -- return meta --} -- -// -- Active package tracking -- -// --// We say a package is "active" if any of its files are open. After --// type-checking we keep active packages in memory. The activePackages --// peristent map does bookkeeping for the set of active packages. +-// We say a package is "active" if any of its files are open. +-// This is an optimization: the "active" concept is an +-// implementation detail of the cache and is not exposed +-// in the source or Snapshot API. +-// After type-checking we keep active packages in memory. +-// The activePackages persistent map does bookkeeping for +-// the set of active packages. - -// getActivePackage returns a the memoized active package for id, if it exists. -// If id is not active or has not yet been type-checked, it returns nil. @@ -21047,49 +24169,36 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - defer s.mu.Unlock() - - if value, ok := s.activePackages.Get(id); ok { -- return value.(*Package) // possibly nil, if we have already checked this id. +- return value - } - return nil -} - --// memoizeActivePackage checks if pkg is active, and if so either records it in +-// setActivePackage checks if pkg is active, and if so either records it in -// the active packages map or returns the existing memoized active package for id. --// --// The resulting package is non-nil if and only if the specified package is open. --func (s *snapshot) memoizeActivePackage(id PackageID, pkg *Package) (active *Package) { +-func (s *snapshot) setActivePackage(id PackageID, pkg *Package) { - s.mu.Lock() - defer s.mu.Unlock() - -- if value, ok := s.activePackages.Get(id); ok { -- return value.(*Package) // possibly nil, if we have already checked this id. +- if _, ok := s.activePackages.Get(id); ok { +- return // already memoized - } - -- defer func() { -- s.activePackages.Set(id, active, nil) // store the result either way: remember that pkg is not open -- }() -- for _, cgf := range pkg.Metadata().GoFiles { -- if s.isOpenLocked(cgf) { -- return pkg -- } -- } -- for _, cgf := range pkg.Metadata().CompiledGoFiles { -- if s.isOpenLocked(cgf) { -- return pkg -- } +- if containsOpenFileLocked(s, pkg.Metadata()) { +- s.activePackages.Set(id, pkg, nil) +- } else { +- s.activePackages.Set(id, (*Package)(nil), nil) // remember that pkg is not open - } -- return nil -} - -func (s *snapshot) resetActivePackagesLocked() { - s.activePackages.Destroy() -- s.activePackages = persistent.NewMap(packageIDLessInterface) +- s.activePackages = new(persistent.Map[PackageID, *Package]) -} - --const fileExtensions = "go,mod,sum,work" -- -func (s *snapshot) fileWatchingGlobPatterns(ctx context.Context) map[string]struct{} { -- extensions := fileExtensions -- for _, ext := range s.View().Options().TemplateExtensions { +- extensions := "go,mod,sum,work" +- for _, ext := range s.Options().TemplateExtensions { - extensions += "," + ext - } - // Work-around microsoft/vscode#100870 by making sure that we are, @@ -21100,194 +24209,177 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - } - - // If GOWORK is outside the folder, ensure we are watching it. -- gowork := s.view.effectiveGOWORK() +- gowork, _ := s.view.GOWORK() - if gowork != "" && !source.InDir(s.view.folder.Filename(), gowork.Filename()) { - patterns[gowork.Filename()] = struct{}{} - } - - // Add a pattern for each Go module in the workspace that is not within the view. -- dirs := s.dirs(ctx) +- dirs := s.workspaceDirs(ctx) - for _, dir := range dirs { -- dirName := dir.Filename() -- - // If the directory is within the view's folder, we're already watching - // it with the first pattern above. -- if source.InDir(s.view.folder.Filename(), dirName) { +- if source.InDir(s.view.folder.Filename(), dir) { - continue - } - // TODO(rstambler): If microsoft/vscode#3025 is resolved before - // microsoft/vscode#101042, we will need a work-around for Windows - // drive letter casing. -- patterns[fmt.Sprintf("%s/**/*.{%s}", dirName, extensions)] = struct{}{} +- patterns[fmt.Sprintf("%s/**/*.{%s}", dir, extensions)] = struct{}{} - } - -- // Some clients do not send notifications for changes to directories that -- // contain Go code (golang/go#42348). To handle this, explicitly watch all -- // of the directories in the workspace. We find them by adding the -- // directories of every file in the snapshot's workspace directories. -- // There may be thousands. -- if pattern := s.getKnownSubdirsPattern(dirs); pattern != "" { -- patterns[pattern] = struct{}{} +- if s.watchSubdirs() { +- // Some clients (e.g. VS Code) do not send notifications for changes to +- // directories that contain Go code (golang/go#42348). To handle this, +- // explicitly watch all of the directories in the workspace. We find them +- // by adding the directories of every file in the snapshot's workspace +- // directories. There may be thousands of patterns, each a single +- // directory. +- // +- // We compute this set by looking at files that we've previously observed. +- // This may miss changed to directories that we haven't observed, but that +- // shouldn't matter as there is nothing to invalidate (if a directory falls +- // in forest, etc). +- // +- // (A previous iteration created a single glob pattern holding a union of +- // all the directories, but this was found to cause VS Code to get stuck +- // for several minutes after a buffer was saved twice in a workspace that +- // had >8000 watched directories.) +- // +- // Some clients (notably coc.nvim, which uses watchman for globs) perform +- // poorly with a large list of individual directories. +- s.addKnownSubdirs(patterns, dirs) - } - - return patterns -} - --func (s *snapshot) getKnownSubdirsPattern(wsDirs []span.URI) string { +-func (s *snapshot) addKnownSubdirs(patterns map[string]unit, wsDirs []string) { - s.mu.Lock() - defer s.mu.Unlock() - -- // First, process any pending changes and update the set of known -- // subdirectories. -- // It may change list of known subdirs and therefore invalidate the cache. -- s.applyKnownSubdirsChangesLocked(wsDirs) -- -- if s.knownSubdirsPatternCache == "" { -- var builder strings.Builder -- s.knownSubdirs.Range(func(uri span.URI) { -- if builder.Len() == 0 { -- builder.WriteString("{") -- } else { -- builder.WriteString(",") +- s.files.Dirs().Range(func(dir string) { +- for _, wsDir := range wsDirs { +- if source.InDir(wsDir, dir) { +- patterns[dir] = unit{} - } -- builder.WriteString(uri.Filename()) -- }) -- if builder.Len() > 0 { -- builder.WriteString("}") -- s.knownSubdirsPatternCache = builder.String() - } -- } -- -- return s.knownSubdirsPatternCache --} -- --// collectAllKnownSubdirs collects all of the subdirectories within the --// snapshot's workspace directories. None of the workspace directories are --// included. --func (s *snapshot) collectAllKnownSubdirs(ctx context.Context) { -- dirs := s.dirs(ctx) -- -- s.mu.Lock() -- defer s.mu.Unlock() -- -- s.knownSubdirs.Destroy() -- s.knownSubdirs = newKnownDirsSet() -- s.knownSubdirsPatternCache = "" -- s.files.Range(func(uri span.URI, fh source.FileHandle) { -- s.addKnownSubdirLocked(uri, dirs) - }) -} - --func (s *snapshot) getKnownSubdirs(wsDirs []span.URI) knownDirsSet { -- s.mu.Lock() -- defer s.mu.Unlock() -- -- // First, process any pending changes and update the set of known -- // subdirectories. -- s.applyKnownSubdirsChangesLocked(wsDirs) -- -- return s.knownSubdirs.Clone() --} +-// workspaceDirs returns the workspace directories for the loaded modules. +-// +-// A workspace directory is, roughly speaking, a directory for which we care +-// about file changes. +-func (s *snapshot) workspaceDirs(ctx context.Context) []string { +- dirSet := make(map[string]unit) - --func (s *snapshot) applyKnownSubdirsChangesLocked(wsDirs []span.URI) { -- for _, c := range s.unprocessedSubdirChanges { -- if c.isUnchanged { -- continue -- } -- if !c.exists { -- s.removeKnownSubdirLocked(c.fileHandle.URI()) -- } else { -- s.addKnownSubdirLocked(c.fileHandle.URI(), wsDirs) -- } -- } -- s.unprocessedSubdirChanges = nil --} +- // Dirs should, at the very least, contain the working directory and folder. +- dirSet[s.view.goCommandDir.Filename()] = unit{} +- dirSet[s.view.folder.Filename()] = unit{} - --func (s *snapshot) addKnownSubdirLocked(uri span.URI, dirs []span.URI) { -- dir := filepath.Dir(uri.Filename()) -- // First check if the directory is already known, because then we can -- // return early. -- if s.knownSubdirs.Contains(span.URIFromPath(dir)) { -- return -- } -- var matched span.URI -- for _, wsDir := range dirs { -- if source.InDir(wsDir.Filename(), dir) { -- matched = wsDir -- break +- // Additionally, if e.g. go.work indicates other workspace modules, we should +- // include their directories too. +- if s.workspaceModFilesErr == nil { +- for modFile := range s.workspaceModFiles { +- dir := filepath.Dir(modFile.Filename()) +- dirSet[dir] = unit{} - } - } -- // Don't watch any directory outside of the workspace directories. -- if matched == "" { -- return -- } -- for { -- if dir == "" || dir == matched.Filename() { -- break -- } -- uri := span.URIFromPath(dir) -- if s.knownSubdirs.Contains(uri) { -- break -- } -- s.knownSubdirs.Insert(uri) -- dir = filepath.Dir(dir) -- s.knownSubdirsPatternCache = "" +- var dirs []string +- for d := range dirSet { +- dirs = append(dirs, d) - } +- sort.Strings(dirs) +- return dirs -} - --func (s *snapshot) removeKnownSubdirLocked(uri span.URI) { -- dir := filepath.Dir(uri.Filename()) -- for dir != "" { -- uri := span.URIFromPath(dir) -- if !s.knownSubdirs.Contains(uri) { -- break -- } -- if info, _ := os.Stat(dir); info == nil { -- s.knownSubdirs.Remove(uri) -- s.knownSubdirsPatternCache = "" +-// watchSubdirs reports whether gopls should request separate file watchers for +-// each relevant subdirectory. This is necessary only for clients (namely VS +-// Code) that do not send notifications for individual files in a directory +-// when the entire directory is deleted. +-func (s *snapshot) watchSubdirs() bool { +- switch p := s.options.SubdirWatchPatterns; p { +- case source.SubdirWatchPatternsOn: +- return true +- case source.SubdirWatchPatternsOff: +- return false +- case source.SubdirWatchPatternsAuto: +- // See the documentation of InternalOptions.SubdirWatchPatterns for an +- // explanation of why VS Code gets a different default value here. +- // +- // Unfortunately, there is no authoritative list of client names, nor any +- // requirements that client names do not change. We should update the VS +- // Code extension to set a default value of "subdirWatchPatterns" to "on", +- // so that this workaround is only temporary. +- if s.options.ClientInfo != nil && s.options.ClientInfo.Name == "Visual Studio Code" { +- return true - } -- dir = filepath.Dir(dir) +- return false +- default: +- bug.Reportf("invalid subdirWatchPatterns: %q", p) +- return false - } -} - --// knownFilesInDir returns the files known to the given snapshot that are in --// the given directory. It does not respect symlinks. --func (s *snapshot) knownFilesInDir(ctx context.Context, dir span.URI) []span.URI { -- var files []span.URI +-// filesInDir returns all files observed by the snapshot that are contained in +-// a directory with the provided URI. +-func (s *snapshot) filesInDir(uri span.URI) []span.URI { - s.mu.Lock() - defer s.mu.Unlock() - -- s.files.Range(func(uri span.URI, fh source.FileHandle) { -- if source.InDir(dir.Filename(), uri.Filename()) { +- dir := uri.Filename() +- if !s.files.Dirs().Contains(dir) { +- return nil +- } +- var files []span.URI +- s.files.Range(func(uri span.URI, _ source.FileHandle) { +- if source.InDir(dir, uri.Filename()) { - files = append(files, uri) - } - }) - return files -} - --func (s *snapshot) ActiveMetadata(ctx context.Context) ([]*source.Metadata, error) { +-func (s *snapshot) WorkspaceMetadata(ctx context.Context) ([]*source.Metadata, error) { - if err := s.awaitLoaded(ctx); err != nil { - return nil, err - } -- return s.workspaceMetadata(), nil +- +- s.mu.Lock() +- defer s.mu.Unlock() +- +- meta := make([]*source.Metadata, 0, len(s.workspacePackages)) +- for id := range s.workspacePackages { +- meta = append(meta, s.meta.metadata[id]) +- } +- return meta, nil -} - -// Symbols extracts and returns symbol information for every file contained in -// a loaded package. It awaits snapshot loading. -// -// TODO(rfindley): move this to the top of cache/symbols.go --func (s *snapshot) Symbols(ctx context.Context) (map[span.URI][]source.Symbol, error) { +-func (s *snapshot) Symbols(ctx context.Context, workspaceOnly bool) (map[span.URI][]source.Symbol, error) { - if err := s.awaitLoaded(ctx); err != nil { - return nil, err - } - -- // Build symbols for all loaded Go files. -- s.mu.Lock() -- meta := s.meta -- s.mu.Unlock() +- var ( +- meta []*source.Metadata +- err error +- ) +- if workspaceOnly { +- meta, err = s.WorkspaceMetadata(ctx) +- } else { +- meta, err = s.AllMetadata(ctx) +- } +- if err != nil { +- return nil, fmt.Errorf("loading metadata: %v", err) +- } - - goFiles := make(map[span.URI]struct{}) -- for _, m := range meta.metadata { +- for _, m := range meta { - for _, uri := range m.GoFiles { - goFiles[uri] = struct{}{} - } @@ -21350,7 +24442,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -func moduleForURI(modFiles map[span.URI]struct{}, uri span.URI) span.URI { - var match span.URI - for modURI := range modFiles { -- if !source.InDir(span.Dir(modURI).Filename(), uri.Filename()) { +- if !source.InDir(filepath.Dir(modURI.Filename()), uri.Filename()) { - continue - } - if len(modURI) > len(match) { @@ -21365,7 +24457,6 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -// -// The given uri must be a file, not a directory. -func nearestModFile(ctx context.Context, uri span.URI, fs source.FileSource) (span.URI, error) { -- // TODO(rfindley) - dir := filepath.Dir(uri.Filename()) - mod, err := findRootPattern(ctx, dir, "go.mod", fs) - if err != nil { @@ -21411,25 +24502,6 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - } -} - --// noValidMetadataForURILocked reports whether there is any valid metadata for --// the given URI. --func (s *snapshot) noValidMetadataForURILocked(uri span.URI) bool { -- for _, id := range s.meta.ids[uri] { -- if _, ok := s.meta.metadata[id]; ok { -- return false -- } -- } -- return true --} -- --func (s *snapshot) isWorkspacePackage(id PackageID) bool { -- s.mu.Lock() -- defer s.mu.Unlock() -- -- _, ok := s.workspacePackages[id] -- return ok --} -- -func (s *snapshot) FindFile(uri span.URI) source.FileHandle { - s.view.markKnown(uri) - @@ -21440,29 +24512,65 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - return result -} - --// GetFile returns a File for the given URI. If the file is unknown it is added +-// ReadFile returns a File for the given URI. If the file is unknown it is added -// to the managed set. -// --// GetFile succeeds even if the file does not exist. A non-nil error return +-// ReadFile succeeds even if the file does not exist. A non-nil error return -// indicates some type of internal error, for example if ctx is cancelled. --func (s *snapshot) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) { +-func (s *snapshot) ReadFile(ctx context.Context, uri span.URI) (source.FileHandle, error) { - s.mu.Lock() - defer s.mu.Unlock() - -- return lockedSnapshot{s}.GetFile(ctx, uri) +- return lockedSnapshot{s}.ReadFile(ctx, uri) +-} +- +-// preloadFiles delegates to the view FileSource to read the requested uris in +-// parallel, without holding the snapshot lock. +-func (s *snapshot) preloadFiles(ctx context.Context, uris []span.URI) { +- files := make([]source.FileHandle, len(uris)) +- var wg sync.WaitGroup +- iolimit := make(chan struct{}, 20) // I/O concurrency limiting semaphore +- for i, uri := range uris { +- wg.Add(1) +- iolimit <- struct{}{} +- go func(i int, uri span.URI) { +- defer wg.Done() +- fh, err := s.view.fs.ReadFile(ctx, uri) +- <-iolimit +- if err != nil && ctx.Err() == nil { +- event.Error(ctx, fmt.Sprintf("reading %s", uri), err) +- return +- } +- files[i] = fh +- }(i, uri) +- } +- wg.Wait() +- +- s.mu.Lock() +- defer s.mu.Unlock() +- +- for i, fh := range files { +- if fh == nil { +- continue // error logged above +- } +- uri := uris[i] +- if _, ok := s.files.Get(uri); !ok { +- s.files.Set(uri, fh) +- } +- } -} - -// A lockedSnapshot implements the source.FileSource interface while holding -// the lock for the wrapped snapshot. -type lockedSnapshot struct{ wrapped *snapshot } - --func (s lockedSnapshot) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) { +-func (s lockedSnapshot) ReadFile(ctx context.Context, uri span.URI) (source.FileHandle, error) { - s.wrapped.view.markKnown(uri) - if fh, ok := s.wrapped.files.Get(uri); ok { - return fh, nil - } - -- fh, err := s.wrapped.view.fs.GetFile(ctx, uri) // read the file +- fh, err := s.wrapped.view.fs.ReadFile(ctx, uri) - if err != nil { - return nil, err - } @@ -21473,33 +24581,13 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -func (s *snapshot) IsOpen(uri span.URI) bool { - s.mu.Lock() - defer s.mu.Unlock() -- return s.isOpenLocked(uri) -- --} -- --func (s *snapshot) openFiles() []source.FileHandle { -- s.mu.Lock() -- defer s.mu.Unlock() -- -- var open []source.FileHandle -- s.files.Range(func(uri span.URI, fh source.FileHandle) { -- if isFileOpen(fh) { -- open = append(open, fh) -- } -- }) -- return open --} - --func (s *snapshot) isOpenLocked(uri span.URI) bool { - fh, _ := s.files.Get(uri) -- return isFileOpen(fh) --} -- --func isFileOpen(fh source.FileHandle) bool { - _, open := fh.(*Overlay) - return open -} - +-// TODO(rfindley): it would make sense for awaitLoaded to return metadata. -func (s *snapshot) awaitLoaded(ctx context.Context) error { - loadErr := s.awaitLoadedAllErrors(ctx) - @@ -21511,7 +24599,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - return nil -} - --func (s *snapshot) GetCriticalError(ctx context.Context) *source.CriticalError { +-func (s *snapshot) CriticalError(ctx context.Context) *source.CriticalError { - // If we couldn't compute workspace mod files, then the load below is - // invalid. - // @@ -21528,7 +24616,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // Even if packages didn't fail to load, we still may want to show - // additional warnings. - if loadErr == nil { -- active, _ := s.ActiveMetadata(ctx) +- active, _ := s.WorkspaceMetadata(ctx) - if msg := shouldShowAdHocPackagesWarning(s, active); msg != "" { - return &source.CriticalError{ - MainError: errors.New(msg), @@ -21579,8 +24667,8 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -If you are using modules, please open your editor to a directory in your module. -If you believe this warning is incorrect, please file an issue: https://github.com/golang/go/issues/new.` - --func shouldShowAdHocPackagesWarning(snapshot source.Snapshot, active []*source.Metadata) string { -- if !snapshot.ValidBuildConfiguration() { +-func shouldShowAdHocPackagesWarning(snapshot *snapshot, active []*source.Metadata) string { +- if !snapshot.validBuildConfiguration() { - for _, m := range active { - // A blank entry in DepsByImpPath - // indicates a missing dependency. @@ -21690,7 +24778,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - - // If the view's build configuration is invalid, we cannot reload by - // package path. Just reload the directory instead. -- if !s.ValidBuildConfiguration() { +- if !s.validBuildConfiguration() { - scopes = []loadScope{viewLoadScope("LOAD_INVALID_VIEW")} - } - @@ -21705,183 +24793,330 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - return err -} - +-// reloadOrphanedOpenFiles attempts to load a package for each open file that +-// does not yet have an associated package. If loading finishes without being +-// canceled, any files still not contained in a package are marked as unloadable. +-// +-// An error is returned if the load is canceled. -func (s *snapshot) reloadOrphanedOpenFiles(ctx context.Context) error { +- s.mu.Lock() +- meta := s.meta +- s.mu.Unlock() - // When we load ./... or a package path directly, we may not get packages - // that exist only in overlays. As a workaround, we search all of the files - // available in the snapshot and reload their metadata individually using a - // file= query if the metadata is unavailable. -- files := s.orphanedOpenFiles() -- -- // Files without a valid package declaration can't be loaded. Don't try. -- var scopes []loadScope -- for _, file := range files { -- pgf, err := s.ParseGo(ctx, file, source.ParseHeader) -- if err != nil { +- open := s.overlays() +- var files []*Overlay +- for _, o := range open { +- uri := o.URI() +- if s.IsBuiltin(uri) || s.FileKind(o) != source.Go { - continue - } -- if !pgf.File.Package.IsValid() { -- continue +- if len(meta.ids[uri]) == 0 { +- files = append(files, o) - } +- } - -- scopes = append(scopes, fileLoadScope(file.URI())) +- // Filter to files that are not known to be unloadable. +- s.mu.Lock() +- loadable := files[:0] +- for _, file := range files { +- if !s.unloadableFiles.Contains(file.URI()) { +- loadable = append(loadable, file) +- } - } +- files = loadable +- s.mu.Unlock() - -- if len(scopes) == 0 { +- if len(files) == 0 { - return nil - } - -- // The regtests match this exact log message, keep them in sync. -- event.Log(ctx, "reloadOrphanedFiles reloading", tag.Query.Of(scopes)) -- err := s.load(ctx, false, scopes...) +- var uris []span.URI +- for _, file := range files { +- uris = append(uris, file.URI()) +- } +- +- event.Log(ctx, "reloadOrphanedFiles reloading", tag.Files.Of(uris)) +- +- var g errgroup.Group +- +- cpulimit := runtime.GOMAXPROCS(0) +- g.SetLimit(cpulimit) +- +- // Load files one-at-a-time. go/packages can return at most one +- // command-line-arguments package per query. +- for _, file := range files { +- file := file +- g.Go(func() error { +- return s.load(ctx, false, fileLoadScope(file.URI())) +- }) +- } - - // If we failed to load some files, i.e. they have no metadata, - // mark the failures so we don't bother retrying until the file's - // content changes. - // -- // TODO(rstambler): This may be an overestimate if the load stopped -- // early for an unrelated errors. Add a fallback? -- // -- // Check for context cancellation so that we don't incorrectly mark files -- // as unloadable, but don't return before setting all workspace packages. -- if ctx.Err() == nil && err != nil { -- event.Error(ctx, "reloadOrphanedFiles: failed to load", err, tag.Query.Of(scopes)) -- s.mu.Lock() -- for _, scope := range scopes { -- uri := span.URI(scope.(fileLoadScope)) -- if s.noValidMetadataForURILocked(uri) { -- s.unloadableFiles[uri] = struct{}{} -- } +- // TODO(rfindley): is it possible that the load stopped early for an +- // unrelated errors? If so, add a fallback? +- +- if err := g.Wait(); err != nil { +- // Check for context cancellation so that we don't incorrectly mark files +- // as unloadable, but don't return before setting all workspace packages. +- if ctx.Err() != nil { +- return ctx.Err() +- } +- +- if !errors.Is(err, errNoPackages) { +- event.Error(ctx, "reloadOrphanedFiles: failed to load", err, tag.Files.Of(uris)) - } -- s.mu.Unlock() - } -- return nil --} - --func (s *snapshot) orphanedOpenFiles() []source.FileHandle { +- // If the context was not canceled, we assume that the result of loading +- // packages is deterministic (though as we saw in golang/go#59318, it may not +- // be in the presence of bugs). Marking all unloaded files as unloadable here +- // prevents us from falling into recursive reloading where we only make a bit +- // of progress each time. - s.mu.Lock() - defer s.mu.Unlock() -- -- var files []source.FileHandle -- s.files.Range(func(uri span.URI, fh source.FileHandle) { -- // Only consider open files, which will be represented as overlays. -- if _, isOverlay := fh.(*Overlay); !isOverlay { -- return -- } -- // Don't try to reload metadata for go.mod files. -- if s.view.FileKind(fh) != source.Go { -- return -- } -- // If the URI doesn't belong to this view, then it's not in a workspace -- // package and should not be reloaded directly. -- if !source.InDir(s.view.folder.Filename(), uri.Filename()) { -- return -- } -- // Don't reload metadata for files we've already deemed unloadable. -- if _, ok := s.unloadableFiles[uri]; ok { -- return -- } -- if s.noValidMetadataForURILocked(uri) { -- files = append(files, fh) +- for _, file := range files { +- // TODO(rfindley): instead of locking here, we should have load return the +- // metadata graph that resulted from loading. +- uri := file.URI() +- if len(s.meta.ids[uri]) == 0 { +- s.unloadableFiles.Add(uri) - } -- }) -- return files --} +- } - --// TODO(golang/go#53756): this function needs to consider more than just the --// absolute URI, for example: --// - the position of /vendor/ with respect to the relevant module root --// - whether or not go.work is in use (as vendoring isn't supported in workspace mode) --// --// Most likely, each call site of inVendor needs to be reconsidered to --// understand and correctly implement the desired behavior. --func inVendor(uri span.URI) bool { -- _, after, found := cut(string(uri), "/vendor/") -- // Only subdirectories of /vendor/ are considered vendored -- // (/vendor/a/foo.go is vendored, /vendor/foo.go is not). -- return found && strings.Contains(after, "/") +- return nil -} - --// TODO(adonovan): replace with strings.Cut when we can assume go1.18. --func cut(s, sep string) (before, after string, found bool) { -- if i := strings.Index(s, sep); i >= 0 { -- return s[:i], s[i+len(sep):], true +-// OrphanedFileDiagnostics reports diagnostics describing why open files have +-// no packages or have only command-line-arguments packages. +-// +-// If the resulting diagnostic is nil, the file is either not orphaned or we +-// can't produce a good diagnostic. +-// +-// TODO(rfindley): reconcile the definition of "orphaned" here with +-// reloadOrphanedFiles. The latter does not include files with +-// command-line-arguments packages. +-func (s *snapshot) OrphanedFileDiagnostics(ctx context.Context) (map[span.URI]*source.Diagnostic, error) { +- if err := s.awaitLoaded(ctx); err != nil { +- return nil, err - } -- return s, "", false --} - --// unappliedChanges is a file source that handles an uncloned snapshot. --type unappliedChanges struct { -- originalSnapshot *snapshot -- changes map[span.URI]*fileChange --} +- var files []*Overlay - --func (ac *unappliedChanges) GetFile(ctx context.Context, uri span.URI) (source.FileHandle, error) { -- if c, ok := ac.changes[uri]; ok { -- return c.fileHandle, nil +-searchOverlays: +- for _, o := range s.overlays() { +- uri := o.URI() +- if s.IsBuiltin(uri) || s.FileKind(o) != source.Go { +- continue +- } +- md, err := s.MetadataForFile(ctx, uri) +- if err != nil { +- return nil, err +- } +- for _, m := range md { +- if !source.IsCommandLineArguments(m.ID) || m.Standalone { +- continue searchOverlays +- } +- } +- files = append(files, o) +- } +- if len(files) == 0 { +- return nil, nil - } -- return ac.originalSnapshot.GetFile(ctx, uri) --} - --func (s *snapshot) clone(ctx, bgCtx context.Context, changes map[span.URI]*fileChange, forceReloadMetadata bool) (*snapshot, func()) { -- ctx, done := event.Start(ctx, "snapshot.clone") -- defer done() +- loadedModFiles := make(map[span.URI]struct{}) // all mod files, including dependencies +- ignoredFiles := make(map[span.URI]bool) // files reported in packages.Package.IgnoredFiles - -- reinit := false -- wsModFiles, wsModFilesErr := s.workspaceModFiles, s.workspaceModFilesErr +- meta, err := s.AllMetadata(ctx) +- if err != nil { +- return nil, err +- } - -- if workURI := s.view.effectiveGOWORK(); workURI != "" { -- if change, ok := changes[workURI]; ok { -- wsModFiles, wsModFilesErr = computeWorkspaceModFiles(ctx, s.view.gomod, workURI, s.view.effectiveGO111MODULE(), &unappliedChanges{ -- originalSnapshot: s, -- changes: changes, -- }) -- // TODO(rfindley): don't rely on 'isUnchanged' here. Use a content hash instead. -- reinit = change.fileHandle.Saved() && !change.isUnchanged +- for _, meta := range meta { +- if meta.Module != nil && meta.Module.GoMod != "" { +- gomod := span.URIFromPath(meta.Module.GoMod) +- loadedModFiles[gomod] = struct{}{} +- } +- for _, ignored := range meta.IgnoredFiles { +- ignoredFiles[ignored] = true - } - } - -- // Reinitialize if any workspace mod file has changed on disk. -- for uri, change := range changes { -- if _, ok := wsModFiles[uri]; ok && change.fileHandle.Saved() && !change.isUnchanged { -- reinit = true +- diagnostics := make(map[span.URI]*source.Diagnostic) +- for _, fh := range files { +- // Only warn about orphaned files if the file is well-formed enough to +- // actually be part of a package. +- // +- // Use ParseGo as for open files this is likely to be a cache hit (we'll have ) +- pgf, err := s.ParseGo(ctx, fh, source.ParseHeader) +- if err != nil { +- continue +- } +- if !pgf.File.Name.Pos().IsValid() { +- continue +- } +- rng, err := pgf.PosRange(pgf.File.Name.Pos(), pgf.File.Name.End()) +- if err != nil { +- continue - } -- } - -- // Finally, process sumfile changes that may affect loading. -- for uri, change := range changes { -- if !change.fileHandle.Saved() { -- continue // like with go.mod files, we only reinit when things are saved +- var ( +- msg string // if non-empty, report a diagnostic with this message +- suggestedFixes []source.SuggestedFix // associated fixes, if any +- ) +- +- // If we have a relevant go.mod file, check whether the file is orphaned +- // due to its go.mod file being inactive. We could also offer a +- // prescriptive diagnostic in the case that there is no go.mod file, but it +- // is harder to be precise in that case, and less important. +- if goMod, err := nearestModFile(ctx, fh.URI(), s); err == nil && goMod != "" { +- if _, ok := loadedModFiles[goMod]; !ok { +- modDir := filepath.Dir(goMod.Filename()) +- viewDir := s.view.folder.Filename() +- +- // When the module is underneath the view dir, we offer +- // "use all modules" quick-fixes. +- inDir := source.InDir(viewDir, modDir) +- +- if rel, err := filepath.Rel(viewDir, modDir); err == nil { +- modDir = rel +- } +- +- var fix string +- if s.view.goversion >= 18 { +- if s.view.gowork != "" { +- fix = fmt.Sprintf("To fix this problem, you can add this module to your go.work file (%s)", s.view.gowork) +- if cmd, err := command.NewRunGoWorkCommandCommand("Run `go work use`", command.RunGoWorkArgs{ +- ViewID: s.view.ID(), +- Args: []string{"use", modDir}, +- }); err == nil { +- suggestedFixes = append(suggestedFixes, source.SuggestedFix{ +- Title: "Use this module in your go.work file", +- Command: &cmd, +- ActionKind: protocol.QuickFix, +- }) +- } +- +- if inDir { +- if cmd, err := command.NewRunGoWorkCommandCommand("Run `go work use -r`", command.RunGoWorkArgs{ +- ViewID: s.view.ID(), +- Args: []string{"use", "-r", "."}, +- }); err == nil { +- suggestedFixes = append(suggestedFixes, source.SuggestedFix{ +- Title: "Use all modules in your workspace", +- Command: &cmd, +- ActionKind: protocol.QuickFix, +- }) +- } +- } +- } else { +- fix = "To fix this problem, you can add a go.work file that uses this directory." +- +- if cmd, err := command.NewRunGoWorkCommandCommand("Run `go work init && go work use`", command.RunGoWorkArgs{ +- ViewID: s.view.ID(), +- InitFirst: true, +- Args: []string{"use", modDir}, +- }); err == nil { +- suggestedFixes = []source.SuggestedFix{ +- { +- Title: "Add a go.work file using this module", +- Command: &cmd, +- ActionKind: protocol.QuickFix, +- }, +- } +- } +- +- if inDir { +- if cmd, err := command.NewRunGoWorkCommandCommand("Run `go work init && go work use -r`", command.RunGoWorkArgs{ +- ViewID: s.view.ID(), +- InitFirst: true, +- Args: []string{"use", "-r", "."}, +- }); err == nil { +- suggestedFixes = append(suggestedFixes, source.SuggestedFix{ +- Title: "Add a go.work file using all modules in your workspace", +- Command: &cmd, +- ActionKind: protocol.QuickFix, +- }) +- } +- } +- } +- } else { +- fix = `To work with multiple modules simultaneously, please upgrade to Go 1.18 or +-later, reinstall gopls, and use a go.work file.` +- } +- msg = fmt.Sprintf(`This file is within module %q, which is not included in your workspace. +-%s +-See the documentation for more information on setting up your workspace: +-https://github.com/golang/tools/blob/master/gopls/doc/workspace.md.`, modDir, fix) +- } - } -- if filepath.Base(uri.Filename()) == "go.work.sum" && s.view.gowork != "" { -- if filepath.Dir(uri.Filename()) == filepath.Dir(s.view.gowork) { -- reinit = true +- +- if msg == "" && ignoredFiles[fh.URI()] { +- // TODO(rfindley): use the constraint package to check if the file +- // _actually_ satisfies the current build context. +- hasConstraint := false +- walkConstraints(pgf.File, func(constraint.Expr) bool { +- hasConstraint = true +- return false +- }) +- var fix string +- if hasConstraint { +- fix = `This file may be excluded due to its build tags; try adding "-tags=" to your gopls "buildFlags" configuration +-See the documentation for more information on working with build tags: +-https://github.com/golang/tools/blob/master/gopls/doc/settings.md#buildflags-string.` +- } else if strings.Contains(filepath.Base(fh.URI().Filename()), "_") { +- fix = `This file may be excluded due to its GOOS/GOARCH, or other build constraints.` +- } else { +- fix = `This file is ignored by your gopls build.` // we don't know why - } +- msg = fmt.Sprintf("No packages found for open file %s.\n%s", fh.URI().Filename(), fix) - } -- if filepath.Base(uri.Filename()) == "go.sum" { -- dir := filepath.Dir(uri.Filename()) -- modURI := span.URIFromPath(filepath.Join(dir, "go.mod")) -- if _, active := wsModFiles[modURI]; active { -- reinit = true +- +- if msg != "" { +- d := &source.Diagnostic{ +- URI: fh.URI(), +- Range: rng, +- Severity: protocol.SeverityWarning, +- Source: source.ListError, +- Message: msg, +- SuggestedFixes: suggestedFixes, - } +- if ok := source.BundleQuickFixes(d); !ok { +- bug.Reportf("failed to bundle quick fixes for %v", d) +- } +- // Only report diagnostics if we detect an actual exclusion. +- diagnostics[fh.URI()] = d - } - } +- return diagnostics, nil +-} +- +-// TODO(golang/go#53756): this function needs to consider more than just the +-// absolute URI, for example: +-// - the position of /vendor/ with respect to the relevant module root +-// - whether or not go.work is in use (as vendoring isn't supported in workspace mode) +-// +-// Most likely, each call site of inVendor needs to be reconsidered to +-// understand and correctly implement the desired behavior. +-func inVendor(uri span.URI) bool { +- _, after, found := strings.Cut(string(uri), "/vendor/") +- // Only subdirectories of /vendor/ are considered vendored +- // (/vendor/a/foo.go is vendored, /vendor/foo.go is not). +- return found && strings.Contains(after, "/") +-} +- +-func (s *snapshot) clone(ctx, bgCtx context.Context, changes map[span.URI]source.FileHandle, newOptions *source.Options, forceReloadMetadata bool) (*snapshot, func()) { +- ctx, done := event.Start(ctx, "cache.snapshot.clone") +- defer done() - - s.mu.Lock() - defer s.mu.Unlock() - -- // Changes to vendor tree may require reinitialization, -- // either because of an initialization error -- // (e.g. "inconsistent vendoring detected"), or because -- // one or more modules may have moved into or out of the -- // vendor tree after 'go mod vendor' or 'rm -fr vendor/'. -- for uri := range changes { -- if inVendor(uri) && s.initializedErr != nil || -- strings.HasSuffix(string(uri), "/vendor/modules.txt") { -- reinit = true -- break -- } -- } -- - bgCtx, cancel := context.WithCancel(bgCtx) - result := &snapshot{ - sequenceID: s.sequenceID + 1, @@ -21895,26 +25130,24 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - initializedErr: s.initializedErr, - packages: s.packages.Clone(), - activePackages: s.activePackages.Clone(), -- analyses: s.analyses.Clone(), -- files: s.files.Clone(), -- parseCache: s.parseCache, -- symbolizeHandles: s.symbolizeHandles.Clone(), +- files: s.files.Clone(changes), +- symbolizeHandles: cloneWithout(s.symbolizeHandles, changes), - workspacePackages: make(map[PackageID]PackagePath, len(s.workspacePackages)), -- unloadableFiles: make(map[span.URI]struct{}, len(s.unloadableFiles)), -- parseModHandles: s.parseModHandles.Clone(), -- parseWorkHandles: s.parseWorkHandles.Clone(), -- modTidyHandles: s.modTidyHandles.Clone(), -- modWhyHandles: s.modWhyHandles.Clone(), -- modVulnHandles: s.modVulnHandles.Clone(), -- knownSubdirs: s.knownSubdirs.Clone(), -- workspaceModFiles: wsModFiles, -- workspaceModFilesErr: wsModFilesErr, +- unloadableFiles: s.unloadableFiles.Clone(), // not cloneWithout: typing in a file doesn't necessarily make it loadable +- parseModHandles: cloneWithout(s.parseModHandles, changes), +- parseWorkHandles: cloneWithout(s.parseWorkHandles, changes), +- modTidyHandles: cloneWithout(s.modTidyHandles, changes), +- modWhyHandles: cloneWithout(s.modWhyHandles, changes), +- modVulnHandles: cloneWithout(s.modVulnHandles, changes), +- workspaceModFiles: s.workspaceModFiles, +- workspaceModFilesErr: s.workspaceModFilesErr, +- importGraph: s.importGraph, +- pkgIndex: s.pkgIndex, +- options: s.options, - } - -- // The snapshot should be initialized if either s was uninitialized, or we've -- // detected a change that triggers reinitialization. -- if reinit { -- result.initialized = false +- if newOptions != nil { +- result.options = newOptions - } - - // Create a lease on the new snapshot. @@ -21922,24 +25155,87 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // incref/decref operation that might destroy it prematurely.) - release := result.Acquire() - -- // Copy the set of unloadable files. +- reinit := false +- +- // Changes to vendor tree may require reinitialization, +- // either because of an initialization error +- // (e.g. "inconsistent vendoring detected"), or because +- // one or more modules may have moved into or out of the +- // vendor tree after 'go mod vendor' or 'rm -fr vendor/'. - // -- // TODO(rfindley): this looks wrong. Shouldn't we clear unloadableFiles on -- // changes to environment or workspace layout, or more generally on any -- // metadata change? +- // TODO(rfindley): revisit the location of this check. +- for uri := range changes { +- if inVendor(uri) && s.initializedErr != nil || +- strings.HasSuffix(string(uri), "/vendor/modules.txt") { +- reinit = true +- break +- } +- } +- +- // Collect observed file handles for changed URIs from the old snapshot, if +- // they exist. Importantly, we don't call ReadFile here: consider the case +- // where a file is added on disk; we don't want to read the newly added file +- // into the old snapshot, as that will break our change detection below. +- oldFiles := make(map[span.URI]source.FileHandle) +- for uri := range changes { +- if fh, ok := s.files.Get(uri); ok { +- oldFiles[uri] = fh +- } +- } +- // changedOnDisk determines if the new file handle may have changed on disk. +- // It over-approximates, returning true if the new file is saved and either +- // the old file wasn't saved, or the on-disk contents changed. - // -- // Maybe not, as major configuration changes cause a new view. -- for k, v := range s.unloadableFiles { -- result.unloadableFiles[k] = v +- // oldFH may be nil. +- changedOnDisk := func(oldFH, newFH source.FileHandle) bool { +- if !newFH.SameContentsOnDisk() { +- return false +- } +- if oe, ne := (oldFH != nil && fileExists(oldFH)), fileExists(newFH); !oe || !ne { +- return oe != ne +- } +- return !oldFH.SameContentsOnDisk() || oldFH.FileIdentity() != newFH.FileIdentity() - } - -- // Add all of the known subdirectories, but don't update them for the -- // changed files. We need to rebuild the workspace module to know the -- // true set of known subdirectories, but we don't want to do that in clone. -- result.knownSubdirs = s.knownSubdirs.Clone() -- result.knownSubdirsPatternCache = s.knownSubdirsPatternCache -- for _, c := range changes { -- result.unprocessedSubdirChanges = append(result.unprocessedSubdirChanges, c) +- if workURI, _ := s.view.GOWORK(); workURI != "" { +- if newFH, ok := changes[workURI]; ok { +- result.workspaceModFiles, result.workspaceModFilesErr = computeWorkspaceModFiles(ctx, s.view.gomod, workURI, s.view.effectiveGO111MODULE(), result) +- if changedOnDisk(oldFiles[workURI], newFH) { +- reinit = true +- } +- } +- } +- +- // Reinitialize if any workspace mod file has changed on disk. +- for uri, newFH := range changes { +- if _, ok := result.workspaceModFiles[uri]; ok && changedOnDisk(oldFiles[uri], newFH) { +- reinit = true +- } +- } +- +- // Finally, process sumfile changes that may affect loading. +- for uri, newFH := range changes { +- if !changedOnDisk(oldFiles[uri], newFH) { +- continue // like with go.mod files, we only reinit when things change on disk +- } +- dir, base := filepath.Split(uri.Filename()) +- if base == "go.work.sum" && s.view.gowork != "" { +- if dir == filepath.Dir(s.view.gowork) { +- reinit = true +- } +- } +- if base == "go.sum" { +- modURI := span.URIFromPath(filepath.Join(dir, "go.mod")) +- if _, active := result.workspaceModFiles[modURI]; active { +- reinit = true +- } +- } +- } +- +- // The snapshot should be initialized if either s was uninitialized, or we've +- // detected a change that triggers reinitialization. +- if reinit { +- result.initialized = false - } - - // directIDs keeps track of package IDs that have directly changed. @@ -21949,6 +25245,7 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // Invalidate all package metadata if the workspace module has changed. - if reinit { - for k := range s.meta.metadata { +- // TODO(rfindley): this seems brittle; can we just start over? - directIDs[k] = true - } - } @@ -21958,22 +25255,14 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - anyFileOpenedOrClosed := false // opened files affect workspace packages - anyFileAdded := false // adding a file can resolve missing dependencies - -- for uri, change := range changes { -- // Invalidate go.mod-related handles. -- result.modTidyHandles.Delete(uri) -- result.modWhyHandles.Delete(uri) -- result.modVulnHandles.Delete(uri) -- -- // Invalidate handles for cached symbols. -- result.symbolizeHandles.Delete(uri) -- +- for uri, newFH := range changes { - // The original FileHandle for this URI is cached on the snapshot. -- originalFH, _ := s.files.Get(uri) -- var originalOpen, newOpen bool -- _, originalOpen = originalFH.(*Overlay) -- _, newOpen = change.fileHandle.(*Overlay) -- anyFileOpenedOrClosed = anyFileOpenedOrClosed || (originalOpen != newOpen) -- anyFileAdded = anyFileAdded || (originalFH == nil && change.fileHandle != nil) +- oldFH, _ := oldFiles[uri] // may be nil +- _, oldOpen := oldFH.(*Overlay) +- _, newOpen := newFH.(*Overlay) +- +- anyFileOpenedOrClosed = anyFileOpenedOrClosed || (oldOpen != newOpen) +- anyFileAdded = anyFileAdded || (oldFH == nil || !fileExists(oldFH)) && fileExists(newFH) - - // If uri is a Go file, check if it has changed in a way that would - // invalidate metadata. Note that we can't use s.view.FileKind here, @@ -21981,7 +25270,11 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // but what the Go command sees. - var invalidateMetadata, pkgFileChanged, importDeleted bool - if strings.HasSuffix(uri.Filename(), ".go") { -- invalidateMetadata, pkgFileChanged, importDeleted = metadataChanges(ctx, s, originalFH, change.fileHandle) +- invalidateMetadata, pkgFileChanged, importDeleted = metadataChanges(ctx, s, oldFH, newFH) +- } +- if invalidateMetadata { +- // If this is a metadata-affecting change, perhaps a reload will succeed. +- result.unloadableFiles.Remove(uri) - } - - invalidateMetadata = invalidateMetadata || forceReloadMetadata || reinit @@ -21995,26 +25288,45 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - - // Invalidate the previous modTidyHandle if any of the files have been - // saved or if any of the metadata has been invalidated. -- if invalidateMetadata || fileWasSaved(originalFH, change.fileHandle) { -- // TODO(maybe): Only delete mod handles for -- // which the withoutURI is relevant. -- // Requires reverse-engineering the go command. (!) -- result.modTidyHandles.Clear() +- // +- // TODO(rfindley): this seems like too-aggressive invalidation of mod +- // results. We should instead thread through overlays to the Go command +- // invocation and only run this if invalidateMetadata (and perhaps then +- // still do it less frequently). +- if invalidateMetadata || fileWasSaved(oldFH, newFH) { +- // Only invalidate mod tidy results for the most relevant modfile in the +- // workspace. This is a potentially lossy optimization for workspaces +- // with many modules (such as google-cloud-go, which has 145 modules as +- // of writing). +- // +- // While it is theoretically possible that a change in workspace module A +- // could affect the mod-tidiness of workspace module B (if B transitively +- // requires A), such changes are probably unlikely and not worth the +- // penalty of re-running go mod tidy for everything. Note that mod tidy +- // ignores GOWORK, so the two modules would have to be related by a chain +- // of replace directives. +- // +- // We could improve accuracy by inspecting replace directives, using +- // overlays in go mod tidy, and/or checking for metadata changes from the +- // on-disk content. +- // +- // Note that we iterate the modTidyHandles map here, rather than e.g. +- // using nearestModFile, because we don't have access to an accurate +- // FileSource at this point in the snapshot clone. +- const onlyInvalidateMostRelevant = true +- if onlyInvalidateMostRelevant { +- deleteMostRelevantModFile(result.modTidyHandles, uri) +- } else { +- result.modTidyHandles.Clear() +- } +- +- // TODO(rfindley): should we apply the above heuristic to mod vuln or mod +- // why handles as well? +- // +- // TODO(rfindley): no tests fail if I delete the line below. - result.modWhyHandles.Clear() - result.modVulnHandles.Clear() - } -- -- result.parseModHandles.Delete(uri) -- result.parseWorkHandles.Delete(uri) -- // Handle the invalidated file; it may have new contents or not exist. -- if !change.exists { -- result.files.Delete(uri) -- } else { -- result.files.Set(uri, change.fileHandle) -- } -- -- // Make sure to remove the changed file from the unloadable set. -- delete(result.unloadableFiles, uri) - } - - // Deleting an import can cause list errors due to import cycles to be @@ -22078,41 +25390,17 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - addRevDeps(id, invalidateMetadata) - } - -- // Delete invalidated package type information. -- for id := range idsToInvalidate { -- result.packages.Delete(id) -- result.activePackages.Delete(id) -- } -- -- // Delete invalidated analysis actions. -- var actionsToDelete []analysisKey -- result.analyses.Range(func(k, _ interface{}) { -- key := k.(analysisKey) -- if _, ok := idsToInvalidate[key.pkgid]; ok { -- actionsToDelete = append(actionsToDelete, key) -- } -- }) -- for _, key := range actionsToDelete { -- result.analyses.Delete(key) -- } -- -- // If a file has been deleted, we must delete metadata for all packages -- // containing that file. -- // -- // TODO(rfindley): why not keep invalid metadata in this case? If we -- // otherwise allow operate on invalid metadata, why not continue to do so, -- // skipping the missing file? -- skipID := map[PackageID]bool{} -- for _, c := range changes { -- if c.exists { -- continue -- } -- // The file has been deleted. -- if ids, ok := s.meta.ids[c.fileHandle.URI()]; ok { -- for _, id := range ids { -- skipID[id] = true +- // Invalidated package information. +- for id, invalidateMetadata := range idsToInvalidate { +- if _, ok := directIDs[id]; ok || invalidateMetadata { +- result.packages.Delete(id) +- } else { +- if entry, hit := result.packages.Get(id); hit { +- ph := entry.clone(false) +- result.packages.Set(id, ph, nil) - } - } +- result.activePackages.Delete(id) - } - - // Any packages that need loading in s still need loading in the new @@ -22151,10 +25439,11 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - } - - // Check whether the metadata should be deleted. -- if skipID[k] || invalidateMetadata { +- if invalidateMetadata { - metadataUpdates[k] = nil - continue - } +- - } - - // Update metadata, if necessary. @@ -22180,10 +25469,41 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - if workspaceModeChanged { - result.workspacePackages = map[PackageID]PackagePath{} - } -- result.dumpWorkspace("clone") - return result, release -} - +-func cloneWithout[V any](m *persistent.Map[span.URI, V], changes map[span.URI]source.FileHandle) *persistent.Map[span.URI, V] { +- m2 := m.Clone() +- for k := range changes { +- m2.Delete(k) +- } +- return m2 +-} +- +-// deleteMostRelevantModFile deletes the mod file most likely to be the mod +-// file for the changed URI, if it exists. +-// +-// Specifically, this is the longest mod file path in a directory containing +-// changed. This might not be accurate if there is another mod file closer to +-// changed that happens not to be present in the map, but that's OK: the goal +-// of this function is to guarantee that IF the nearest mod file is present in +-// the map, it is invalidated. +-func deleteMostRelevantModFile(m *persistent.Map[span.URI, *memoize.Promise], changed span.URI) { +- var mostRelevant span.URI +- changedFile := changed.Filename() +- +- m.Range(func(modURI span.URI, _ *memoize.Promise) { +- if len(modURI) > len(mostRelevant) { +- if source.InDir(filepath.Dir(modURI.Filename()), changedFile) { +- mostRelevant = modURI +- } +- } +- }) +- if mostRelevant != "" { +- m.Delete(mostRelevant) +- } +-} +- -// invalidatedPackageIDs returns all packages invalidated by a change to uri. -// If we haven't seen this URI before, we guess based on files in the same -// directory. This is of course incorrect in build systems where packages are @@ -22277,9 +25597,9 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap -// - importDeleted means that an import has been deleted, or we can't -// determine if an import was deleted due to errors. -func metadataChanges(ctx context.Context, lockedSnapshot *snapshot, oldFH, newFH source.FileHandle) (invalidate, pkgFileChanged, importDeleted bool) { -- if oldFH == nil || newFH == nil { // existential changes -- changed := (oldFH == nil) != (newFH == nil) -- return changed, changed, (newFH == nil) // we don't know if an import was deleted +- if oe, ne := oldFH != nil && fileExists(oldFH), fileExists(newFH); !oe || !ne { // existential changes +- changed := oe != ne +- return changed, changed, !ne // we don't know if an import was deleted - } - - // If the file hasn't changed, there's no need to reload. @@ -22287,16 +25607,12 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - return false, false, false - } - +- fset := token.NewFileSet() - // Parse headers to compare package names and imports. -- oldHeads, _, oldErr := lockedSnapshot.parseCache.parseFiles(ctx, source.ParseHeader, oldFH) -- newHeads, _, newErr := lockedSnapshot.parseCache.parseFiles(ctx, source.ParseHeader, newFH) +- oldHeads, oldErr := lockedSnapshot.view.parseCache.parseFiles(ctx, fset, source.ParseHeader, false, oldFH) +- newHeads, newErr := lockedSnapshot.view.parseCache.parseFiles(ctx, fset, source.ParseHeader, false, newFH) - - if oldErr != nil || newErr != nil { -- // TODO(rfindley): we can get here if newFH does not exist. There is -- // asymmetry, in that newFH may be non-nil even if the underlying file does -- // not exist. -- // -- // We should not produce a non-nil filehandle for a file that does not exist. - errChanged := (oldErr == nil) != (newErr == nil) - return errChanged, errChanged, (newErr != nil) // we don't know if an import was deleted - } @@ -22340,8 +25656,8 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - // Note: if this affects performance we can probably avoid parsing in the - // common case by first scanning the source for potential comments. - if !invalidate { -- origFulls, _, oldErr := lockedSnapshot.parseCache.parseFiles(ctx, source.ParseFull, oldFH) -- newFulls, _, newErr := lockedSnapshot.parseCache.parseFiles(ctx, source.ParseFull, newFH) +- origFulls, oldErr := lockedSnapshot.view.parseCache.parseFiles(ctx, fset, source.ParseFull, false, oldFH) +- newFulls, newErr := lockedSnapshot.view.parseCache.parseFiles(ctx, fset, source.ParseFull, false, newFH) - if oldErr == nil && newErr == nil { - invalidate = magicCommentsChanged(origFulls[0].File, newFulls[0].File) - } else { @@ -22419,14 +25735,21 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - return nil, fmt.Errorf("no builtin package for view %s", s.view.name) - } - -- fh, err := s.GetFile(ctx, builtin) +- fh, err := s.ReadFile(ctx, builtin) - if err != nil { - return nil, err - } -- return s.ParseGo(ctx, fh, source.ParseFull) +- // For the builtin file only, we need syntactic object resolution +- // (since we can't type check). +- mode := source.ParseFull &^ source.SkipObjectResolution +- pgfs, err := s.view.parseCache.parseFiles(ctx, token.NewFileSet(), mode, false, fh) +- if err != nil { +- return nil, err +- } +- return pgfs[0], nil -} - --func (s *snapshot) IsBuiltin(ctx context.Context, uri span.URI) bool { +-func (s *snapshot) IsBuiltin(uri span.URI) bool { - s.mu.Lock() - defer s.mu.Unlock() - // We should always get the builtin URI in a canonical form, so use simple @@ -22440,182 +25763,10 @@ diff -urN a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snap - - s.builtin = span.URIFromPath(path) -} -diff -urN a/gopls/internal/lsp/cache/standalone_go115.go b/gopls/internal/lsp/cache/standalone_go115.go ---- a/gopls/internal/lsp/cache/standalone_go115.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/standalone_go115.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +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.16 --// +build !go1.16 -- --package cache -- --// isStandaloneFile returns false, as the 'standaloneTags' setting is --// unsupported on Go 1.15 and earlier. --func isStandaloneFile(src []byte, standaloneTags []string) bool { -- return false --} -diff -urN a/gopls/internal/lsp/cache/standalone_go116.go b/gopls/internal/lsp/cache/standalone_go116.go ---- a/gopls/internal/lsp/cache/standalone_go116.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/standalone_go116.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,50 +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.16 --// +build go1.16 -- --package cache -- --import ( -- "go/build/constraint" -- "go/parser" -- "go/token" --) -- --// isStandaloneFile reports whether a file with the given contents should be --// considered a 'standalone main file', meaning a package that consists of only --// a single file. --func isStandaloneFile(src []byte, standaloneTags []string) bool { -- f, err := parser.ParseFile(token.NewFileSet(), "", src, parser.PackageClauseOnly|parser.ParseComments) -- if err != nil { -- return false -- } -- -- if f.Name == nil || f.Name.Name != "main" { -- return false -- } -- -- for _, cg := range f.Comments { -- // Even with PackageClauseOnly the parser consumes the semicolon following -- // the package clause, so we must guard against comments that come after -- // the package name. -- if cg.Pos() > f.Name.Pos() { -- continue -- } -- for _, comment := range cg.List { -- if c, err := constraint.Parse(comment.Text); err == nil { -- if tag, ok := c.(*constraint.TagExpr); ok { -- for _, t := range standaloneTags { -- if t == tag.Tag { -- return true -- } -- } -- } -- } -- } -- } -- -- return false --} -diff -urN a/gopls/internal/lsp/cache/standalone_go116_test.go b/gopls/internal/lsp/cache/standalone_go116_test.go ---- a/gopls/internal/lsp/cache/standalone_go116_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/standalone_go116_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,96 +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.16 --// +build go1.16 -- --package cache -- --import ( -- "testing" --) -- --func TestIsStandaloneFile(t *testing.T) { -- tests := []struct { -- desc string -- contents string -- standaloneTags []string -- want bool -- }{ -- { -- "new syntax", -- "//go:build ignore\n\npackage main\n", -- []string{"ignore"}, -- true, -- }, -- { -- "legacy syntax", -- "// +build ignore\n\npackage main\n", -- []string{"ignore"}, -- true, -- }, -- { -- "multiple tags", -- "//go:build ignore\n\npackage main\n", -- []string{"exclude", "ignore"}, -- true, -- }, -- { -- "invalid tag", -- "// +build ignore\n\npackage main\n", -- []string{"script"}, -- false, -- }, -- { -- "non-main package", -- "//go:build ignore\n\npackage p\n", -- []string{"ignore"}, -- false, -- }, -- { -- "alternate tag", -- "// +build script\n\npackage main\n", -- []string{"script"}, -- true, -- }, -- { -- "both syntax", -- "//go:build ignore\n// +build ignore\n\npackage main\n", -- []string{"ignore"}, -- true, -- }, -- { -- "after comments", -- "// A non-directive comment\n//go:build ignore\n\npackage main\n", -- []string{"ignore"}, -- true, -- }, -- { -- "after package decl", -- "package main //go:build ignore\n", -- []string{"ignore"}, -- false, -- }, -- { -- "on line after package decl", -- "package main\n\n//go:build ignore\n", -- []string{"ignore"}, -- false, -- }, -- { -- "combined with other expressions", -- "\n\n//go:build ignore || darwin\n\npackage main\n", -- []string{"ignore"}, -- false, -- }, -- } -- -- for _, test := range tests { -- t.Run(test.desc, func(t *testing.T) { -- if got := isStandaloneFile([]byte(test.contents), test.standaloneTags); got != test.want { -- t.Errorf("isStandaloneFile(%q, %v) = %t, want %t", test.contents, test.standaloneTags, got, test.want) -- } -- }) -- } --} diff -urN a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbols.go --- a/gopls/internal/lsp/cache/symbols.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/symbols.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,213 +0,0 @@ ++++ b/gopls/internal/lsp/cache/symbols.go 1970-01-01 08:00:00 +@@ -1,192 +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. @@ -22629,10 +25780,10 @@ diff -urN a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbo - "go/types" - "strings" - +- "golang.org/x/tools/gopls/internal/astutil" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/memoize" -) - -// symbolize returns the result of symbolizing the file identified by uri, using a cache. @@ -22649,7 +25800,7 @@ diff -urN a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbo - - // Cache miss? - if !hit { -- fh, err := s.GetFile(ctx, uri) +- fh, err := s.ReadFile(ctx, uri) - if err != nil { - return nil, err - } @@ -22668,7 +25819,7 @@ diff -urN a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbo - } - - // Await result. -- v, err := s.awaitPromise(ctx, entry.(*memoize.Promise)) +- v, err := s.awaitPromise(ctx, entry) - if err != nil { - return nil, err - } @@ -22677,10 +25828,8 @@ diff -urN a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbo -} - -// symbolizeImpl reads and parses a file and extracts symbols from it. --// It may use a parsed file already present in the cache but --// otherwise does not populate the cache. -func symbolizeImpl(ctx context.Context, snapshot *snapshot, fh source.FileHandle) ([]source.Symbol, error) { -- pgfs, _, err := snapshot.parseCache.parseFiles(ctx, source.ParseFull, fh) +- pgfs, err := snapshot.view.parseCache.parseFiles(ctx, token.NewFileSet(), source.ParseFull, false, fh) - if err != nil { - return nil, err - } @@ -22740,7 +25889,7 @@ diff -urN a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbo - var recv *ast.Ident - if decl.Recv.NumFields() > 0 { - kind = protocol.Method -- recv = unpackRecv(decl.Recv.List[0].Type) +- _, recv, _ = astutil.UnpackRecv(decl.Recv.List[0].Type) - } - w.atNode(decl.Name, decl.Name.Name, kind, recv) - case *ast.GenDecl: @@ -22776,25 +25925,6 @@ diff -urN a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbo - return protocol.Class -} - --func unpackRecv(rtyp ast.Expr) *ast.Ident { -- // Extract the receiver identifier. Lifted from go/types/resolver.go --L: -- for { -- switch t := rtyp.(type) { -- case *ast.ParenExpr: -- rtyp = t.X -- case *ast.StarExpr: -- rtyp = t.X -- default: -- break L -- } -- } -- if name, _ := rtyp.(*ast.Ident); name != nil { -- return name -- } -- return nil --} -- -// walkType processes symbols related to a type expression. path is path of -// nested type identifiers to the type expression. -func (w *symbolWalker) walkType(typ ast.Expr, path ...*ast.Ident) { @@ -22831,8 +25961,8 @@ diff -urN a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbo -} diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go --- a/gopls/internal/lsp/cache/view.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/view.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1142 +0,0 @@ ++++ b/gopls/internal/lsp/cache/view.go 1970-01-01 08:00:00 +@@ -1,1286 +0,0 @@ -// Copyright 2018 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. @@ -22846,7 +25976,6 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - "encoding/json" - "errors" - "fmt" -- "io/ioutil" - "os" - "path" - "path/filepath" @@ -22860,10 +25989,10 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - "golang.org/x/mod/modfile" - "golang.org/x/mod/semver" - exec "golang.org/x/sys/execabs" -- "golang.org/x/tools/gopls/internal/govulncheck" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/gopls/internal/vulncheck" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/gocommand" - "golang.org/x/tools/internal/imports" @@ -22882,14 +26011,16 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - // name is the user-specified name of this view. - name string - -- optionsMu sync.Mutex -- options *source.Options +- // lastOptions holds the most recent options on this view, used for detecting +- // major changes. +- // +- // Guarded by Session.viewMu. +- lastOptions *source.Options - - // Workspace information. The fields below are immutable, and together with - // options define the build list. Any change to these fields results in a new - // View. -- folder span.URI // user-specified workspace folder -- workspaceInformation // Go environment information +- workspaceInformation // Go environment information - - importsState *importsState - @@ -22900,12 +26031,15 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - - // vulns maps each go.mod file's URI to its known vulnerabilities. - vulnsMu sync.Mutex -- vulns map[span.URI]*govulncheck.Result +- vulns map[span.URI]*vulncheck.Result +- +- // parseCache holds an LRU cache of recently parsed files. +- parseCache *parseCache - - // fs is the file source used to populate this view. -- fs source.FileSource +- fs *overlayFS - -- // seenFiles tracks files that the view has accessed. +- // knownFiles tracks files that the view has accessed. - // TODO(golang/go#57558): this notion is fundamentally problematic, and - // should be removed. - knownFilesMu sync.Mutex @@ -22947,6 +26081,9 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go -// -// This type is compared to see if the View needs to be reconstructed. -type workspaceInformation struct { +- // folder is the LSP workspace folder. +- folder span.URI +- - // `go env` variables that need to be tracked by gopls. - goEnv - @@ -22965,6 +26102,17 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - // GOPACKAGESDRIVER environment variable or a gopackagesdriver binary on - // their machine. - hasGopackagesDriver bool +- +- // inGOPATH reports whether the workspace directory is contained in a GOPATH +- // directory. +- inGOPATH bool +- +- // goCommandDir is the dir to use for running go commands. +- // +- // The only case where this should matter is if we've narrowed the workspace to +- // a single nested module. In that case, the go command won't be able to find +- // the module unless we tell it the nested directory. +- goCommandDir span.URI -} - -// effectiveGO111MODULE reports the value of GO111MODULE effective in the go @@ -22980,13 +26128,89 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - } -} - --// effectiveGOWORK returns the effective GOWORK value for this workspace, if +-// A ViewType describes how we load package information for a view. +-// +-// This is used for constructing the go/packages.Load query, and for +-// interpreting missing packages, imports, or errors. +-// +-// Each view has a ViewType which is derived from its immutable workspace +-// information -- any environment change that would affect the view type +-// results in a new view. +-type ViewType int +- +-const ( +- // GoPackagesDriverView is a view with a non-empty GOPACKAGESDRIVER +- // environment variable. +- GoPackagesDriverView ViewType = iota +- +- // GOPATHView is a view in GOPATH mode. +- // +- // I.e. in GOPATH, with GO111MODULE=off, or GO111MODULE=auto with no +- // go.mod file. +- GOPATHView +- +- // GoModuleView is a view in module mode with a single Go module. +- GoModuleView +- +- // GoWorkView is a view in module mode with a go.work file. +- GoWorkView +- +- // An AdHocView is a collection of files in a given directory, not in GOPATH +- // or a module. +- AdHocView +-) +- +-// ViewType derives the type of the view from its workspace information. +-// +-// TODO(rfindley): this logic is overlapping and slightly inconsistent with +-// validBuildConfiguration. As part of zero-config-gopls (golang/go#57979), fix +-// this inconsistency and consolidate on the ViewType abstraction. +-func (w workspaceInformation) ViewType() ViewType { +- if w.hasGopackagesDriver { +- return GoPackagesDriverView +- } +- go111module := w.effectiveGO111MODULE() +- if w.gowork != "" && go111module != off { +- return GoWorkView +- } +- if w.gomod != "" && go111module != off { +- return GoModuleView +- } +- if w.inGOPATH && go111module != on { +- return GOPATHView +- } +- return AdHocView +-} +- +-// moduleMode reports whether the current snapshot uses Go modules. +-// +-// From https://go.dev/ref/mod, module mode is active if either of the +-// following hold: +-// - GO111MODULE=on +-// - GO111MODULE=auto and we are inside a module or have a GOWORK value. +-// +-// Additionally, this method returns false if GOPACKAGESDRIVER is set. +-// +-// TODO(rfindley): use this more widely. +-func (w workspaceInformation) moduleMode() bool { +- switch w.ViewType() { +- case GoModuleView, GoWorkView: +- return true +- default: +- return false +- } +-} +- +-// GOWORK returns the effective GOWORK value for this workspace, if -// any, in URI form. --func (w workspaceInformation) effectiveGOWORK() span.URI { +-// +-// The second result reports whether the effective GOWORK value is "" because +-// GOWORK=off. +-func (w workspaceInformation) GOWORK() (span.URI, bool) { - if w.gowork == "off" || w.gowork == "" { -- return "" +- return "", w.gowork == "off" - } -- return span.URIFromPath(w.gowork) +- return span.URIFromPath(w.gowork), false -} - -// GO111MODULE returns the value of GO111MODULE to use for running the go @@ -23103,7 +26327,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go -// longer needed. -func tempModFile(modFh source.FileHandle, gosum []byte) (tmpURI span.URI, cleanup func(), err error) { - filenameHash := source.Hashf("%s", modFh.URI().Filename()) -- tmpMod, err := ioutil.TempFile("", fmt.Sprintf("go.%s.*.mod", filenameHash)) +- tmpMod, err := os.CreateTemp("", fmt.Sprintf("go.%s.*.mod", filenameHash)) - if err != nil { - return "", nil, err - } @@ -23112,7 +26336,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - tmpURI = span.URIFromPath(tmpMod.Name()) - tmpSumName := sumFilename(tmpURI) - -- content, err := modFh.Read() +- content, err := modFh.Content() - if err != nil { - return "", nil, err - } @@ -23138,7 +26362,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - - // Create an analogous go.sum, if one exists. - if gosum != nil { -- if err := ioutil.WriteFile(tmpSumName, gosum, 0655); err != nil { +- if err := os.WriteFile(tmpSumName, gosum, 0655); err != nil { - return "", nil, err - } - } @@ -23156,44 +26380,19 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - return v.folder -} - --func (v *View) Options() *source.Options { -- v.optionsMu.Lock() -- defer v.optionsMu.Unlock() -- return v.options --} -- --func (v *View) FileKind(fh source.FileHandle) source.FileKind { -- // The kind of an unsaved buffer comes from the -- // TextDocumentItem.LanguageID field in the didChange event, -- // not from the file name. They may differ. -- if o, ok := fh.(*Overlay); ok { -- if o.kind != source.UnknownKind { -- return o.kind -- } -- } -- -- fext := filepath.Ext(fh.URI().Filename()) -- switch fext { -- case ".go": -- return source.Go -- case ".mod": -- return source.Mod -- case ".sum": -- return source.Sum -- case ".work": -- return source.Work -- } -- exts := v.Options().TemplateExtensions -- for _, ext := range exts { -- if fext == ext || fext == "."+ext { -- return source.Tmpl -- } -- } -- // and now what? This should never happen, but it does for cgo before go1.15 -- return source.Go --} -- -func minorOptionsChange(a, b *source.Options) bool { +- // TODO(rfindley): this function detects whether a view should be recreated, +- // but this is also checked by the getWorkspaceInformation logic. +- // +- // We should eliminate this redundancy. +- // +- // Additionally, this function has existed for a long time, but git history +- // suggests that it was added arbitrarily, not due to an actual performance +- // problem. +- // +- // Especially now that we have optimized reinitialization of the session, we +- // should consider just always creating a new view on any options change. +- - // Check if any of the settings that modify our understanding of files have - // been changed. - if !reflect.DeepEqual(a.Env, b.Env) { @@ -23221,29 +26420,42 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - return reflect.DeepEqual(aBuildFlags, bBuildFlags) -} - --// SetViewOptions sets the options of the given view to new values. Calling --// this may cause the view to be invalidated and a replacement view added to --// the session. If so the new view will be returned, otherwise the original one --// will be returned. --func (s *Session) SetViewOptions(ctx context.Context, v *View, options *source.Options) (*View, error) { +-// SetFolderOptions updates the options of each View associated with the folder +-// of the given URI. +-// +-// Calling this may cause each related view to be invalidated and a replacement +-// view added to the session. +-func (s *Session) SetFolderOptions(ctx context.Context, uri span.URI, options *source.Options) error { +- s.viewMu.Lock() +- defer s.viewMu.Unlock() +- +- for _, v := range s.views { +- if v.folder == uri { +- if err := s.setViewOptions(ctx, v, options); err != nil { +- return err +- } +- } +- } +- return nil +-} +- +-func (s *Session) setViewOptions(ctx context.Context, v *View, options *source.Options) error { - // no need to rebuild the view if the options were not materially changed -- v.optionsMu.Lock() -- if minorOptionsChange(v.options, options) { -- v.options = options -- v.optionsMu.Unlock() -- return v, nil +- if minorOptionsChange(v.lastOptions, options) { +- _, release := v.invalidateContent(ctx, nil, options, false) +- release() +- v.lastOptions = options +- return nil - } -- v.optionsMu.Unlock() -- newView, err := s.updateView(ctx, v, options) -- return newView, err +- return s.updateViewLocked(ctx, v, options) -} - -// viewEnv returns a string describing the environment of a newly created view. -// -// It must not be called concurrently with any other view methods. -func viewEnv(v *View) string { -- env := v.options.EnvSlice() -- buildFlags := append([]string{}, v.options.BuildFlags...) +- env := v.snapshot.options.EnvSlice() +- buildFlags := append([]string{}, v.snapshot.options.BuildFlags...) - - var buf bytes.Buffer - fmt.Fprintf(&buf, `go info for %v @@ -23254,9 +26466,9 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go -(selected go env: %v) -`, - v.folder.Filename(), -- v.workingDir().Filename(), +- v.goCommandDir.Filename(), - strings.TrimRight(v.workspaceInformation.goversionOutput, "\n"), -- v.snapshot.ValidBuildConfiguration(), +- v.snapshot.validBuildConfiguration(), - buildFlags, - v.goEnv, - ) @@ -23271,7 +26483,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - return buf.String() -} - --func (s *snapshot) RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error { +-func (s *snapshot) RunProcessEnvFunc(ctx context.Context, fn func(context.Context, *imports.Options) error) error { - return s.view.importsState.runProcessEnvFunc(ctx, s, fn) -} - @@ -23292,13 +26504,13 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go -// locateTemplateFiles ensures that the snapshot has mapped template files -// within the workspace folder. -func (s *snapshot) locateTemplateFiles(ctx context.Context) { -- if len(s.view.Options().TemplateExtensions) == 0 { +- if len(s.options.TemplateExtensions) == 0 { - return - } -- suffixes := s.view.Options().TemplateExtensions +- suffixes := s.options.TemplateExtensions - - searched := 0 -- filterFunc := s.view.filterFunc() +- filterFunc := s.filterFunc() - err := filepath.WalkDir(s.view.folder.Filename(), func(path string, entry os.DirEntry, err error) error { - if err != nil { - return err @@ -23324,7 +26536,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - // - // Furthermore, this operation must ignore errors, including context - // cancellation, or risk leaving the snapshot in an undefined state. -- s.GetFile(ctx, uri) +- s.ReadFile(ctx, uri) - return nil - }) - if err != nil { @@ -23332,33 +26544,33 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - } -} - --func (v *View) contains(uri span.URI) bool { +-func (s *snapshot) contains(uri span.URI) bool { - // If we've expanded the go dir to a parent directory, consider if the - // expanded dir contains the uri. - // TODO(rfindley): should we ignore the root here? It is not provided by the - // user. It would be better to explicitly consider the set of active modules - // wherever relevant. - inGoDir := false -- if source.InDir(v.workingDir().Filename(), v.folder.Filename()) { -- inGoDir = source.InDir(v.workingDir().Filename(), uri.Filename()) +- if source.InDir(s.view.goCommandDir.Filename(), s.view.folder.Filename()) { +- inGoDir = source.InDir(s.view.goCommandDir.Filename(), uri.Filename()) - } -- inFolder := source.InDir(v.folder.Filename(), uri.Filename()) +- inFolder := source.InDir(s.view.folder.Filename(), uri.Filename()) - - if !inGoDir && !inFolder { - return false - } - -- return !v.filterFunc()(uri) +- return !s.filterFunc()(uri) -} - -// filterFunc returns a func that reports whether uri is filtered by the currently configured -// directoryFilters. --func (v *View) filterFunc() func(span.URI) bool { -- filterer := buildFilterer(v.folder.Filename(), v.gomodcache, v.Options()) +-func (s *snapshot) filterFunc() func(span.URI) bool { +- filterer := buildFilterer(s.view.folder.Filename(), s.view.gomodcache, s.options) - return func(uri span.URI) bool { - // Only filter relative to the configured root directory. -- if source.InDir(v.folder.Filename(), uri.Filename()) { -- return pathExcludedByFilter(strings.TrimPrefix(uri.Filename(), v.folder.Filename()), filterer) +- if source.InDir(s.view.folder.Filename(), uri.Filename()) { +- return pathExcludedByFilter(strings.TrimPrefix(uri.Filename(), s.view.folder.Filename()), filterer) - } - return false - } @@ -23375,7 +26587,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - // - // TODO(rfindley): Make sure the go.work files are always known - // to the view. -- if c.URI == v.effectiveGOWORK() { +- if gowork, _ := v.GOWORK(); gowork == c.URI { - return true - } - @@ -23385,7 +26597,12 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - // had neither test nor associated issue, and cited only emacs behavior, this - // logic was deleted. - -- return v.contains(c.URI) +- snapshot, release, err := v.getSnapshot() +- if err != nil { +- return false // view was shut down +- } +- defer release() +- return snapshot.contains(c.URI) -} - -func (v *View) markKnown(uri span.URI) { @@ -23412,6 +26629,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - - v.snapshotMu.Lock() - if v.snapshot != nil { +- v.snapshot.cancel() - v.releaseSnapshot() - v.destroy(v.snapshot, "View.shutdown") - v.snapshot = nil @@ -23422,22 +26640,57 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - v.snapshotWG.Wait() -} - +-// While go list ./... skips directories starting with '.', '_', or 'testdata', +-// gopls may still load them via file queries. Explicitly filter them out. -func (s *snapshot) IgnoredFile(uri span.URI) bool { -- filename := uri.Filename() -- var prefixes []string -- if len(s.workspaceModFiles) == 0 { -- for _, entry := range filepath.SplitList(s.view.gopath) { -- prefixes = append(prefixes, filepath.Join(entry, "src")) +- // Fast path: if uri doesn't contain '.', '_', or 'testdata', it is not +- // possible that it is ignored. +- { +- uriStr := string(uri) +- if !strings.Contains(uriStr, ".") && !strings.Contains(uriStr, "_") && !strings.Contains(uriStr, "testdata") { +- return false - } -- } else { -- prefixes = append(prefixes, s.view.gomodcache) -- for m := range s.workspaceModFiles { -- prefixes = append(prefixes, span.Dir(m).Filename()) +- } +- +- s.ignoreFilterOnce.Do(func() { +- var dirs []string +- if len(s.workspaceModFiles) == 0 { +- for _, entry := range filepath.SplitList(s.view.gopath) { +- dirs = append(dirs, filepath.Join(entry, "src")) +- } +- } else { +- dirs = append(dirs, s.view.gomodcache) +- for m := range s.workspaceModFiles { +- dirs = append(dirs, filepath.Dir(m.Filename())) +- } - } +- s.ignoreFilter = newIgnoreFilter(dirs) +- }) +- +- return s.ignoreFilter.ignored(uri.Filename()) +-} +- +-// An ignoreFilter implements go list's exclusion rules via its 'ignored' method. +-type ignoreFilter struct { +- prefixes []string // root dirs, ending in filepath.Separator +-} +- +-// newIgnoreFilter returns a new ignoreFilter implementing exclusion rules +-// relative to the provided directories. +-func newIgnoreFilter(dirs []string) *ignoreFilter { +- f := new(ignoreFilter) +- for _, d := range dirs { +- f.prefixes = append(f.prefixes, filepath.Clean(d)+string(filepath.Separator)) - } -- for _, prefix := range prefixes { -- if strings.HasPrefix(filename, prefix) { -- return checkIgnored(filename[len(prefix):]) +- return f +-} +- +-func (f *ignoreFilter) ignored(filename string) bool { +- for _, prefix := range f.prefixes { +- if suffix := strings.TrimPrefix(filename, prefix); suffix != filename { +- if checkIgnored(suffix) { +- return true +- } - } - } - return false @@ -23449,6 +26702,8 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go -// Directory and file names that begin with "." or "_" are ignored -// by the go tool, as are directories named "testdata". -func checkIgnored(suffix string) bool { +- // Note: this could be further optimized by writing a HasSegment helper, a +- // segment-boundary respecting variant of strings.Contains. - for _, component := range strings.Split(suffix, string(filepath.Separator)) { - if len(component) == 0 { - continue @@ -23493,7 +26748,6 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - } - - s.loadWorkspace(ctx, firstAttempt) -- s.collectAllKnownSubdirs(ctx) -} - -func (s *snapshot) loadWorkspace(ctx context.Context, firstAttempt bool) (loadErr error) { @@ -23534,30 +26788,43 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - }) - } - +- // TODO(rfindley): this should be predicated on the s.view.moduleMode(). +- // There is no point loading ./... if we have an empty go.work. - if len(s.workspaceModFiles) > 0 { - for modURI := range s.workspaceModFiles { +- // Verify that the modfile is valid before trying to load it. +- // +- // TODO(rfindley): now that we no longer need to parse the modfile in +- // order to load scope, we could move these diagnostics to a more general +- // location where we diagnose problems with modfiles or the workspace. +- // - // Be careful not to add context cancellation errors as critical module - // errors. -- fh, err := s.GetFile(ctx, modURI) +- fh, err := s.ReadFile(ctx, modURI) - if err != nil { -- if ctx.Err() == nil { -- addError(modURI, err) +- if ctx.Err() != nil { +- return ctx.Err() - } +- addError(modURI, err) - continue - } - parsed, err := s.ParseMod(ctx, fh) - if err != nil { -- if ctx.Err() == nil { -- addError(modURI, err) +- if ctx.Err() != nil { +- return ctx.Err() - } +- addError(modURI, err) - continue - } - if parsed.File == nil || parsed.File.Module == nil { - addError(modURI, fmt.Errorf("no module path for %s", modURI)) - continue - } -- path := parsed.File.Module.Mod.Path -- scopes = append(scopes, moduleLoadScope(path)) +- moduleDir := filepath.Dir(modURI.Filename()) +- // Previously, we loaded /... for each module path, but that +- // is actually incorrect when the pattern may match packages in more than +- // one module. See golang/go#59458 for more details. +- scopes = append(scopes, moduleLoadScope{dir: moduleDir, modulePath: parsed.File.Module.Mod.Path}) - } - } else { - scopes = append(scopes, viewLoadScope("LOAD_VIEW")) @@ -23613,7 +26880,9 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go -// -// invalidateContent returns a non-nil snapshot for the new content, along with -// a callback which the caller must invoke to release that snapshot. --func (v *View) invalidateContent(ctx context.Context, changes map[span.URI]*fileChange, forceReloadMetadata bool) (*snapshot, func()) { +-// +-// newOptions may be nil, in which case options remain unchanged. +-func (v *View) invalidateContent(ctx context.Context, changes map[span.URI]source.FileHandle, newOptions *source.Options, forceReloadMetadata bool) (*snapshot, func()) { - // Detach the context so that content invalidation cannot be canceled. - ctx = xcontext.Detach(ctx) - @@ -23635,7 +26904,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - prevSnapshot.AwaitInitialized(ctx) - - // Save one lease of the cloned snapshot in the view. -- v.snapshot, v.releaseSnapshot = prevSnapshot.clone(ctx, v.baseCtx, changes, forceReloadMetadata) +- v.snapshot, v.releaseSnapshot = prevSnapshot.clone(ctx, v.baseCtx, changes, newOptions, forceReloadMetadata) - - prevReleaseSnapshot() - v.destroy(prevSnapshot, "View.invalidateContent") @@ -23649,7 +26918,9 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - return workspaceInformation{}, fmt.Errorf("invalid workspace folder path: %w; check that the casing of the configured workspace folder path agrees with the casing reported by the operating system", err) - } - var err error -- var info workspaceInformation +- info := workspaceInformation{ +- folder: folder, +- } - inv := gocommand.Invocation{ - WorkingDir: folder.Filename(), - Env: options.EnvSlice(), @@ -23662,7 +26933,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - if err != nil { - return info, err - } -- if err := info.goEnv.load(ctx, folder.Filename(), options.EnvSlice(), s.gocmdRunner); err != nil { +- if err := info.load(ctx, folder.Filename(), options.EnvSlice(), s.gocmdRunner); err != nil { - return info, err - } - // The value of GOPACKAGESDRIVER is not returned through the go command. @@ -23680,6 +26951,27 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - return info, err - } - +- // Check if the workspace is within any GOPATH directory. +- for _, gp := range filepath.SplitList(info.gopath) { +- if source.InDir(filepath.Join(gp, "src"), folder.Filename()) { +- info.inGOPATH = true +- break +- } +- } +- +- // Compute the "working directory", which is where we run go commands. +- // +- // Note: if gowork is in use, this will default to the workspace folder. In +- // the past, we would instead use the folder containing go.work. This should +- // not make a difference, and in fact may improve go list error messages. +- // +- // TODO(golang/go#57514): eliminate the expandWorkspaceToModule setting +- // entirely. +- if options.ExpandWorkspaceToModule && info.gomod != "" { +- info.goCommandDir = span.URIFromPath(filepath.Dir(info.gomod.Filename())) +- } else { +- info.goCommandDir = folder +- } - return info, nil -} - @@ -23721,24 +27013,6 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - return "", nil -} - --// workingDir returns the directory from which to run Go commands. --// --// The only case where this should matter is if we've narrowed the workspace to --// a singular nested module. In that case, the go command won't be able to find --// the module unless we tell it the nested directory. --func (v *View) workingDir() span.URI { -- // Note: if gowork is in use, this will default to the workspace folder. In -- // the past, we would instead use the folder containing go.work. This should -- // not make a difference, and in fact may improve go list error messages. -- // -- // TODO(golang/go#57514): eliminate the expandWorkspaceToModule setting -- // entirely. -- if v.Options().ExpandWorkspaceToModule && v.gomod != "" { -- return span.Dir(v.gomod) -- } -- return v.folder --} -- -// findRootPattern looks for files with the given basename in dir or any parent -// directory of dir, using the provided FileSource. It returns the first match, -// starting from dir and search parents. @@ -23748,11 +27022,11 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go -func findRootPattern(ctx context.Context, dir, basename string, fs source.FileSource) (string, error) { - for dir != "" { - target := filepath.Join(dir, basename) -- exists, err := fileExists(ctx, span.URIFromPath(target), fs) +- fh, err := fs.ReadFile(ctx, span.URIFromPath(target)) - if err != nil { -- return "", err // not readable or context cancelled +- return "", err // context cancelled - } -- if exists { +- if fileExists(fh) { - return target, nil - } - // Trailing separators must be trimmed, otherwise filepath.Split is a noop. @@ -23816,8 +27090,8 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go -const maxGovulncheckResultAge = 1 * time.Hour // Invalidate results older than this limit. -var timeNow = time.Now // for testing - --func (v *View) Vulnerabilities(modfiles ...span.URI) map[span.URI]*govulncheck.Result { -- m := make(map[span.URI]*govulncheck.Result) +-func (v *View) Vulnerabilities(modfiles ...span.URI) map[span.URI]*vulncheck.Result { +- m := make(map[span.URI]*vulncheck.Result) - now := timeNow() - v.vulnsMu.Lock() - defer v.vulnsMu.Unlock() @@ -23838,7 +27112,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - return m -} - --func (v *View) SetVulnerabilities(modfile span.URI, vulns *govulncheck.Result) { +-func (v *View) SetVulnerabilities(modfile span.URI, vulns *vulncheck.Result) { - v.vulnsMu.Lock() - defer v.vulnsMu.Unlock() - @@ -23927,7 +27201,7 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go - // No vendor directory? - // TODO(golang/go#57514): this is wrong if the working dir is not the module - // root. -- if fi, err := os.Stat(filepath.Join(s.view.workingDir().Filename(), "vendor")); err != nil || !fi.IsDir() { +- if fi, err := os.Stat(filepath.Join(s.view.goCommandDir.Filename(), "vendor")); err != nil || !fi.IsDir() { - return false, nil - } - @@ -23977,8 +27251,8 @@ diff -urN a/gopls/internal/lsp/cache/view.go b/gopls/internal/lsp/cache/view.go -} diff -urN a/gopls/internal/lsp/cache/view_test.go b/gopls/internal/lsp/cache/view_test.go --- a/gopls/internal/lsp/cache/view_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/view_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,278 +0,0 @@ ++++ b/gopls/internal/lsp/cache/view_test.go 1970-01-01 08:00:00 +@@ -1,303 +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. @@ -23987,31 +27261,27 @@ diff -urN a/gopls/internal/lsp/cache/view_test.go b/gopls/internal/lsp/cache/vie -import ( - "context" - "encoding/json" -- "io/ioutil" - "os" - "path/filepath" - "testing" - "time" - - "github.com/google/go-cmp/cmp" -- "golang.org/x/tools/gopls/internal/govulncheck" - "golang.org/x/tools/gopls/internal/lsp/fake" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/gopls/internal/vulncheck" -) - -func TestCaseInsensitiveFilesystem(t *testing.T) { -- base, err := ioutil.TempDir("", t.Name()) -- if err != nil { -- t.Fatal(err) -- } +- base := t.TempDir() - - inner := filepath.Join(base, "a/B/c/DEFgh") - if err := os.MkdirAll(inner, 0777); err != nil { - t.Fatal(err) - } - file := filepath.Join(inner, "f.go") -- if err := ioutil.WriteFile(file, []byte("hi"), 0777); err != nil { +- if err := os.WriteFile(file, []byte("hi"), 0777); err != nil { - t.Fatal(err) - } - if _, err := os.Stat(filepath.Join(inner, "F.go")); err != nil { @@ -24201,19 +27471,19 @@ diff -urN a/gopls/internal/lsp/cache/view_test.go b/gopls/internal/lsp/cache/vie - now := time.Now() - - view := &View{ -- vulns: make(map[span.URI]*govulncheck.Result), +- vulns: make(map[span.URI]*vulncheck.Result), - } - file1, file2 := span.URIFromPath("f1/go.mod"), span.URIFromPath("f2/go.mod") - -- vuln1 := &govulncheck.Result{AsOf: now.Add(-(maxGovulncheckResultAge * 3) / 4)} // already ~3/4*maxGovulncheckResultAge old +- vuln1 := &vulncheck.Result{AsOf: now.Add(-(maxGovulncheckResultAge * 3) / 4)} // already ~3/4*maxGovulncheckResultAge old - view.SetVulnerabilities(file1, vuln1) - -- vuln2 := &govulncheck.Result{AsOf: now} // fresh. +- vuln2 := &vulncheck.Result{AsOf: now} // fresh. - view.SetVulnerabilities(file2, vuln2) - - t.Run("fresh", func(t *testing.T) { - got := view.Vulnerabilities() -- want := map[span.URI]*govulncheck.Result{ +- want := map[span.URI]*vulncheck.Result{ - file1: vuln1, - file2: vuln2, - } @@ -24227,7 +27497,7 @@ diff -urN a/gopls/internal/lsp/cache/view_test.go b/gopls/internal/lsp/cache/vie - timeNow = func() time.Time { return now.Add(maxGovulncheckResultAge / 2) } - t.Run("after30min", func(t *testing.T) { - got := view.Vulnerabilities() -- want := map[span.URI]*govulncheck.Result{ +- want := map[span.URI]*vulncheck.Result{ - file1: nil, // expired. - file2: vuln2, - } @@ -24242,7 +27512,7 @@ diff -urN a/gopls/internal/lsp/cache/view_test.go b/gopls/internal/lsp/cache/vie - - t.Run("after1hr", func(t *testing.T) { - got := view.Vulnerabilities() -- want := map[span.URI]*govulncheck.Result{ +- want := map[span.URI]*vulncheck.Result{ - file1: nil, - file2: nil, - } @@ -24257,10 +27527,39 @@ diff -urN a/gopls/internal/lsp/cache/view_test.go b/gopls/internal/lsp/cache/vie - b, _ := json.MarshalIndent(x, "", " ") - return string(b) -} +- +-func TestIgnoreFilter(t *testing.T) { +- tests := []struct { +- dirs []string +- path string +- want bool +- }{ +- {[]string{"a"}, "a/testdata/foo", true}, +- {[]string{"a"}, "a/_ignore/foo", true}, +- {[]string{"a"}, "a/.ignore/foo", true}, +- {[]string{"a"}, "b/testdata/foo", false}, +- {[]string{"a"}, "testdata/foo", false}, +- {[]string{"a", "b"}, "b/testdata/foo", true}, +- {[]string{"a"}, "atestdata/foo", false}, +- } +- +- for _, test := range tests { +- // convert to filepaths, for convenience +- for i, dir := range test.dirs { +- test.dirs[i] = filepath.FromSlash(dir) +- } +- test.path = filepath.FromSlash(test.path) +- +- f := newIgnoreFilter(test.dirs) +- if got := f.ignored(test.path); got != test.want { +- t.Errorf("newIgnoreFilter(%q).ignore(%q) = %t, want %t", test.dirs, test.path, got, test.want) +- } +- } +-} diff -urN a/gopls/internal/lsp/cache/workspace.go b/gopls/internal/lsp/cache/workspace.go --- a/gopls/internal/lsp/cache/workspace.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cache/workspace.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,177 +0,0 @@ ++++ b/gopls/internal/lsp/cache/workspace.go 1970-01-01 08:00:00 +@@ -1,133 +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. @@ -24271,9 +27570,8 @@ diff -urN a/gopls/internal/lsp/cache/workspace.go b/gopls/internal/lsp/cache/wor - "context" - "errors" - "fmt" -- "os" +- "io/fs" - "path/filepath" -- "sort" - "strings" - - "golang.org/x/mod/modfile" @@ -24291,11 +27589,11 @@ diff -urN a/gopls/internal/lsp/cache/workspace.go b/gopls/internal/lsp/cache/wor - return nil, nil - } - if gowork != "" { -- fh, err := fs.GetFile(ctx, gowork) +- fh, err := fs.ReadFile(ctx, gowork) - if err != nil { - return nil, err - } -- content, err := fh.Read() +- content, err := fh.Content() - if err != nil { - return nil, err - } @@ -24322,38 +27620,6 @@ diff -urN a/gopls/internal/lsp/cache/workspace.go b/gopls/internal/lsp/cache/wor - return nil, nil -} - --// dirs returns the workspace directories for the loaded modules. --// --// A workspace directory is, roughly speaking, a directory for which we care --// about file changes. This is used for the purpose of registering file --// watching patterns, and expanding directory modifications to their adjacent --// files. --// --// TODO(rfindley): move this to snapshot.go. --// TODO(rfindley): can we make this abstraction simpler and/or more accurate? --func (s *snapshot) dirs(ctx context.Context) []span.URI { -- dirSet := make(map[span.URI]struct{}) -- -- // Dirs should, at the very least, contain the working directory and folder. -- dirSet[s.view.workingDir()] = struct{}{} -- dirSet[s.view.folder] = struct{}{} -- -- // Additionally, if e.g. go.work indicates other workspace modules, we should -- // include their directories too. -- if s.workspaceModFilesErr == nil { -- for modFile := range s.workspaceModFiles { -- dir := filepath.Dir(modFile.Filename()) -- dirSet[span.URIFromPath(dir)] = struct{}{} -- } -- } -- var dirs []span.URI -- for d := range dirSet { -- dirs = append(dirs, d) -- } -- sort.Slice(dirs, func(i, j int) bool { return dirs[i] < dirs[j] }) -- return dirs --} -- -// isGoMod reports if uri is a go.mod file. -func isGoMod(uri span.URI) bool { - return filepath.Base(uri.Filename()) == "go.mod" @@ -24364,25 +27630,11 @@ diff -urN a/gopls/internal/lsp/cache/workspace.go b/gopls/internal/lsp/cache/wor - return filepath.Base(uri.Filename()) == "go.work" -} - --// fileExists reports if the file uri exists within source. --func fileExists(ctx context.Context, uri span.URI, source source.FileSource) (bool, error) { -- fh, err := source.GetFile(ctx, uri) -- if err != nil { -- return false, err -- } -- return fileHandleExists(fh) --} -- --// fileHandleExists reports if the file underlying fh actually exits. --func fileHandleExists(fh source.FileHandle) (bool, error) { -- _, err := fh.Read() -- if err == nil { -- return true, nil -- } -- if os.IsNotExist(err) { -- return false, nil -- } -- return false, err +-// fileExists reports whether the file has a Content (which may be empty). +-// An overlay exists even if it is not reflected in the file system. +-func fileExists(fh source.FileHandle) bool { +- _, err := fh.Content() +- return err == nil -} - -// errExhausted is returned by findModules if the file scan limit is reached. @@ -24390,7 +27642,10 @@ diff -urN a/gopls/internal/lsp/cache/workspace.go b/gopls/internal/lsp/cache/wor - -// Limit go.mod search to 1 million files. As a point of reference, -// Kubernetes has 22K files (as of 2020-11-24). --const fileLimit = 1000000 +-// +-// Note: per golang/go#56496, the previous limit of 1M files was too slow, at +-// which point this limit was decreased to 100K. +-const fileLimit = 100_000 - -// findModules recursively walks the root directory looking for go.mod files, -// returning the set of modules it discovers. If modLimit is non-zero, @@ -24402,7 +27657,7 @@ diff -urN a/gopls/internal/lsp/cache/workspace.go b/gopls/internal/lsp/cache/wor - modFiles := make(map[span.URI]struct{}) - searched := 0 - errDone := errors.New("done") -- err := filepath.Walk(root.Filename(), func(path string, info os.FileInfo, err error) error { +- err := filepath.WalkDir(root.Filename(), func(path string, info fs.DirEntry, err error) error { - if err != nil { - // Probably a permission error. Keep looking. - return filepath.SkipDir @@ -24440,8 +27695,8 @@ diff -urN a/gopls/internal/lsp/cache/workspace.go b/gopls/internal/lsp/cache/wor -} diff -urN a/gopls/internal/lsp/call_hierarchy.go b/gopls/internal/lsp/call_hierarchy.go --- a/gopls/internal/lsp/call_hierarchy.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/call_hierarchy.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,42 +0,0 @@ ++++ b/gopls/internal/lsp/call_hierarchy.go 1970-01-01 08:00:00 +@@ -1,52 +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. @@ -24453,9 +27708,13 @@ diff -urN a/gopls/internal/lsp/call_hierarchy.go b/gopls/internal/lsp/call_hiera - - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/internal/event" -) - -func (s *Server) prepareCallHierarchy(ctx context.Context, params *protocol.CallHierarchyPrepareParams) ([]protocol.CallHierarchyItem, error) { +- ctx, done := event.Start(ctx, "lsp.Server.prepareCallHierarchy") +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go) - defer release() - if !ok { @@ -24466,6 +27725,9 @@ diff -urN a/gopls/internal/lsp/call_hierarchy.go b/gopls/internal/lsp/call_hiera -} - -func (s *Server) incomingCalls(ctx context.Context, params *protocol.CallHierarchyIncomingCallsParams) ([]protocol.CallHierarchyIncomingCall, error) { +- ctx, done := event.Start(ctx, "lsp.Server.incomingCalls") +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.Item.URI, source.Go) - defer release() - if !ok { @@ -24476,6 +27738,9 @@ diff -urN a/gopls/internal/lsp/call_hierarchy.go b/gopls/internal/lsp/call_hiera -} - -func (s *Server) outgoingCalls(ctx context.Context, params *protocol.CallHierarchyOutgoingCallsParams) ([]protocol.CallHierarchyOutgoingCall, error) { +- ctx, done := event.Start(ctx, "lsp.Server.outgoingCalls") +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.Item.URI, source.Go) - defer release() - if !ok { @@ -24486,8 +27751,8 @@ diff -urN a/gopls/internal/lsp/call_hierarchy.go b/gopls/internal/lsp/call_hiera -} diff -urN a/gopls/internal/lsp/cmd/call_hierarchy.go b/gopls/internal/lsp/cmd/call_hierarchy.go --- a/gopls/internal/lsp/cmd/call_hierarchy.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/call_hierarchy.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,142 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/call_hierarchy.go 1970-01-01 08:00:00 +@@ -1,144 +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. @@ -24530,16 +27795,16 @@ diff -urN a/gopls/internal/lsp/cmd/call_hierarchy.go b/gopls/internal/lsp/cmd/ca - return tool.CommandLineErrorf("call_hierarchy expects 1 argument (position)") - } - -- conn, err := c.app.connect(ctx) +- conn, err := c.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - - from := span.Parse(args[0]) -- file := conn.openFile(ctx, from.URI()) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, from.URI()) +- if err != nil { +- return err - } - - loc, err := file.mapper.SpanLocation(from) @@ -24601,27 +27866,29 @@ diff -urN a/gopls/internal/lsp/cmd/call_hierarchy.go b/gopls/internal/lsp/cmd/ca -// callItemPrintString returns a protocol.CallHierarchyItem object represented as a string. -// item and call ranges (protocol.Range) are converted to user friendly spans (1-indexed). -func callItemPrintString(ctx context.Context, conn *connection, item protocol.CallHierarchyItem, callsURI protocol.DocumentURI, calls []protocol.Range) (string, error) { -- itemFile := conn.openFile(ctx, item.URI.SpanURI()) -- if itemFile.err != nil { -- return "", itemFile.err +- itemFile, err := conn.openFile(ctx, item.URI.SpanURI()) +- if err != nil { +- return "", err - } -- itemSpan, err := itemFile.mapper.LocationSpan(protocol.Location{URI: item.URI, Range: item.Range}) +- itemSpan, err := itemFile.mapper.RangeSpan(item.Range) - if err != nil { - return "", err - } - -- callsFile := conn.openFile(ctx, callsURI.SpanURI()) -- if callsURI != "" && callsFile.err != nil { -- return "", callsFile.err -- } - var callRanges []string -- for _, rng := range calls { -- call, err := callsFile.mapper.RangeSpan(rng) +- if callsURI != "" { +- callsFile, err := conn.openFile(ctx, callsURI.SpanURI()) - if err != nil { - return "", err - } -- callRange := fmt.Sprintf("%d:%d-%d", call.Start().Line(), call.Start().Column(), call.End().Column()) -- callRanges = append(callRanges, callRange) +- for _, rng := range calls { +- call, err := callsFile.mapper.RangeSpan(rng) +- if err != nil { +- return "", err +- } +- callRange := fmt.Sprintf("%d:%d-%d", call.Start().Line(), call.Start().Column(), call.End().Column()) +- callRanges = append(callRanges, callRange) +- } - } - - printString := fmt.Sprintf("function %s in %v", item.Name, itemSpan) @@ -24632,8 +27899,8 @@ diff -urN a/gopls/internal/lsp/cmd/call_hierarchy.go b/gopls/internal/lsp/cmd/ca -} diff -urN a/gopls/internal/lsp/cmd/capabilities_test.go b/gopls/internal/lsp/cmd/capabilities_test.go --- a/gopls/internal/lsp/cmd/capabilities_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/capabilities_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,166 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/capabilities_test.go 1970-01-01 08:00:00 +@@ -1,176 +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. @@ -24643,7 +27910,6 @@ diff -urN a/gopls/internal/lsp/cmd/capabilities_test.go b/gopls/internal/lsp/cmd -import ( - "context" - "fmt" -- "io/ioutil" - "os" - "path/filepath" - "testing" @@ -24651,36 +27917,44 @@ diff -urN a/gopls/internal/lsp/cmd/capabilities_test.go b/gopls/internal/lsp/cmd - "golang.org/x/tools/gopls/internal/lsp" - "golang.org/x/tools/gopls/internal/lsp/cache" - "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/internal/testenv" -) - -// TestCapabilities does some minimal validation of the server's adherence to the LSP. -// The checks in the test are added as changes are made and errors noticed. -func TestCapabilities(t *testing.T) { -- tmpDir, err := ioutil.TempDir("", "fake") +- // TODO(bcmills): This test fails on js/wasm, which is not unexpected, but the +- // failure mode is that the DidOpen call below reports "no views in session", +- // which seems a little too cryptic. +- // Is there some missing error reporting somewhere? +- testenv.NeedsTool(t, "go") +- +- tmpDir, err := os.MkdirTemp("", "fake") - if err != nil { - t.Fatal(err) - } - tmpFile := filepath.Join(tmpDir, "fake.go") -- if err := ioutil.WriteFile(tmpFile, []byte(""), 0775); err != nil { +- if err := os.WriteFile(tmpFile, []byte(""), 0775); err != nil { - t.Fatal(err) - } -- if err := ioutil.WriteFile(filepath.Join(tmpDir, "go.mod"), []byte("module fake\n\ngo 1.12\n"), 0775); err != nil { +- if err := os.WriteFile(filepath.Join(tmpDir, "go.mod"), []byte("module fake\n\ngo 1.12\n"), 0775); err != nil { - t.Fatal(err) - } - defer os.RemoveAll(tmpDir) - - app := New("gopls-test", tmpDir, os.Environ(), nil) -- c := newConnection(app) -- ctx := context.Background() -- defer c.terminate(ctx) - - params := &protocol.ParamInitialize{} -- params.RootURI = protocol.URIFromPath(c.Client.app.wd) +- params.RootURI = protocol.URIFromPath(app.wd) - params.Capabilities.Workspace.Configuration = true - - // Send an initialize request to the server. -- c.Server = lsp.NewServer(cache.NewSession(ctx, cache.New(nil), app.options), c.Client) -- result, err := c.Server.Initialize(ctx, params) +- ctx := context.Background() +- client := newClient(app, nil) +- options := source.DefaultOptions(app.options) +- server := lsp.NewServer(cache.NewSession(ctx, cache.New(nil)), client, options) +- result, err := server.Initialize(ctx, params) - if err != nil { - t.Fatal(err) - } @@ -24689,10 +27963,13 @@ diff -urN a/gopls/internal/lsp/cmd/capabilities_test.go b/gopls/internal/lsp/cmd - t.Error(err) - } - // Complete initialization of server. -- if err := c.Server.Initialized(ctx, &protocol.InitializedParams{}); err != nil { +- if err := server.Initialized(ctx, &protocol.InitializedParams{}); err != nil { - t.Fatal(err) - } - +- c := newConnection(server, client) +- defer c.terminate(ctx) +- - // Open the file on the server side. - uri := protocol.URIFromPath(tmpFile) - if err := c.Server.DidOpen(ctx, &protocol.DidOpenTextDocumentParams{ @@ -24802,7 +28079,7 @@ diff -urN a/gopls/internal/lsp/cmd/capabilities_test.go b/gopls/internal/lsp/cmd -} diff -urN a/gopls/internal/lsp/cmd/check.go b/gopls/internal/lsp/cmd/check.go --- a/gopls/internal/lsp/cmd/check.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/check.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/check.go 1970-01-01 08:00:00 @@ -1,73 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -24846,7 +28123,7 @@ diff -urN a/gopls/internal/lsp/cmd/check.go b/gopls/internal/lsp/cmd/check.go - checking := map[span.URI]*cmdFile{} - var uris []span.URI - // now we ready to kick things off -- conn, err := c.app.connect(ctx) +- conn, err := c.app.connect(ctx, nil) - if err != nil { - return err - } @@ -24854,17 +28131,17 @@ diff -urN a/gopls/internal/lsp/cmd/check.go b/gopls/internal/lsp/cmd/check.go - for _, arg := range args { - uri := span.URIFromPath(arg) - uris = append(uris, uri) -- file := conn.openFile(ctx, uri) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, uri) +- if err != nil { +- return err - } - checking[uri] = file - } - if err := conn.diagnoseFiles(ctx, uris); err != nil { - return err - } -- conn.Client.filesMu.Lock() -- defer conn.Client.filesMu.Unlock() +- conn.client.filesMu.Lock() +- defer conn.client.filesMu.Unlock() - - for _, file := range checking { - for _, d := range file.diagnostics { @@ -24879,8 +28156,8 @@ diff -urN a/gopls/internal/lsp/cmd/check.go b/gopls/internal/lsp/cmd/check.go -} diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go --- a/gopls/internal/lsp/cmd/cmd.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/cmd.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,640 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/cmd.go 1970-01-01 08:00:00 +@@ -1,801 +0,0 @@ -// Copyright 2018 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. @@ -24894,7 +28171,6 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - "context" - "flag" - "fmt" -- "io/ioutil" - "log" - "os" - "reflect" @@ -24905,12 +28181,15 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - "time" - - "golang.org/x/tools/gopls/internal/lsp" +- "golang.org/x/tools/gopls/internal/lsp/browser" - "golang.org/x/tools/gopls/internal/lsp/cache" - "golang.org/x/tools/gopls/internal/lsp/debug" +- "golang.org/x/tools/gopls/internal/lsp/filecache" - "golang.org/x/tools/gopls/internal/lsp/lsprpc" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/diff" - "golang.org/x/tools/internal/jsonrpc2" - "golang.org/x/tools/internal/tool" - "golang.org/x/tools/internal/xcontext" @@ -24956,6 +28235,26 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - // PrepareOptions is called to update the options when a new view is built. - // It is primarily to allow the behavior of gopls to be modified by hooks. - PrepareOptions func(*source.Options) +- +- // editFlags holds flags that control how file edit operations +- // are applied, in particular when the server makes an ApplyEdits +- // downcall to the client. Present only for commands that apply edits. +- editFlags *EditFlags +-} +- +-// EditFlags defines flags common to {fix,format,imports,rename} +-// that control how edits are applied to the client's files. +-// +-// The type is exported for flag reflection. +-// +-// The -write, -diff, and -list flags are orthogonal but any +-// of them suppresses the default behavior, which is to print +-// the edited file contents. +-type EditFlags struct { +- Write bool `flag:"w,write" help:"write edited content to source files"` +- Preserve bool `flag:"preserve" help:"with -write, make copies of original files"` +- Diff bool `flag:"d,diff" help:"display diffs instead of edited file content"` +- List bool `flag:"l,list" help:"display names of edited files"` -} - -func (app *Application) verbose() bool { @@ -25019,6 +28318,12 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - for _, c := range app.featureCommands() { - fmt.Fprintf(w, " %s\t%s\n", c.Name(), c.ShortHelp()) - } +- if app.verbose() { +- fmt.Fprint(w, "\t\nInternal Use Only\t\n") +- for _, c := range app.internalCommands() { +- fmt.Fprintf(w, " %s\t%s\n", c.Name(), c.ShortHelp()) +- } +- } - fmt.Fprint(w, "\nflags:\n") - printFlagDefaults(f) -} @@ -25097,6 +28402,11 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go -// If no arguments are passed it will invoke the server sub command, as a -// temporary measure for compatibility. -func (app *Application) Run(ctx context.Context, args ...string) error { +- // In the category of "things we can do while waiting for the Go command": +- // Pre-initialize the filecache, which takes ~50ms to hash the gopls +- // executable, and immediately runs a gc. +- filecache.Start() +- - ctx = debug.WithInstance(ctx, app.wd, app.OCAgent) - if len(args) == 0 { - s := flag.NewFlagSet(app.Name(), flag.ExitOnError) @@ -25119,6 +28429,7 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - var commands []tool.Application - commands = append(commands, app.mainCommands()...) - commands = append(commands, app.featureCommands()...) +- commands = append(commands, app.internalCommands()...) - return commands -} - @@ -25133,6 +28444,12 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - } -} - +-func (app *Application) internalCommands() []tool.Application { +- return []tool.Application{ +- &vulncheck{app: app}, +- } +-} +- -func (app *Application) featureCommands() []tool.Application { - return []tool.Application{ - &callHierarchy{app: app}, @@ -25151,10 +28468,11 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - &rename{app: app}, - &semtok{app: app}, - &signature{app: app}, +- &stats{app: app}, - &suggestedFix{app: app}, - &symbols{app: app}, +- - &workspaceSymbol{app: app}, -- &vulncheck{app: app}, - } -} - @@ -25163,20 +28481,25 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - internalConnections = make(map[string]*connection) -) - --func (app *Application) connect(ctx context.Context) (*connection, error) { +-// connect creates and initializes a new in-process gopls session. +-// +-// If onProgress is set, it is called for each new progress notification. +-func (app *Application) connect(ctx context.Context, onProgress func(*protocol.ProgressParams)) (*connection, error) { - switch { - case app.Remote == "": -- connection := newConnection(app) -- connection.Server = lsp.NewServer(cache.NewSession(ctx, cache.New(nil), app.options), connection.Client) -- ctx = protocol.WithClient(ctx, connection.Client) -- return connection, connection.initialize(ctx, app.options) +- client := newClient(app, onProgress) +- options := source.DefaultOptions(app.options) +- server := lsp.NewServer(cache.NewSession(ctx, cache.New(nil)), client, options) +- conn := newConnection(server, client) +- if err := conn.initialize(protocol.WithClient(ctx, client), app.options); err != nil { +- return nil, err +- } +- return conn, nil +- - case strings.HasPrefix(app.Remote, "internal@"): - internalMu.Lock() - defer internalMu.Unlock() -- opts := source.DefaultOptions().Clone() -- if app.options != nil { -- app.options(opts) -- } +- opts := source.DefaultOptions(app.options) - key := fmt.Sprintf("%s %v %v %v", app.wd, opts.PreferredContentFormat, opts.HierarchicalDocumentSymbolSupport, opts.SymbolMatcher) - if c := internalConnections[key]; c != nil { - return c, nil @@ -25194,29 +28517,20 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - } -} - --// CloseTestConnections terminates shared connections used in command tests. It --// should only be called from tests. --func CloseTestConnections(ctx context.Context) { -- for _, c := range internalConnections { -- c.Shutdown(ctx) -- c.Exit(ctx) -- } --} -- -func (app *Application) connectRemote(ctx context.Context, remote string) (*connection, error) { -- connection := newConnection(app) - conn, err := lsprpc.ConnectToRemote(ctx, remote) - if err != nil { - return nil, err - } - stream := jsonrpc2.NewHeaderStream(conn) - cc := jsonrpc2.NewConn(stream) -- connection.Server = protocol.ServerDispatcher(cc) -- ctx = protocol.WithClient(ctx, connection.Client) +- server := protocol.ServerDispatcher(cc) +- client := newClient(app, nil) +- connection := newConnection(server, client) +- ctx = protocol.WithClient(ctx, connection.client) - cc.Go(ctx, - protocol.Handlers( -- protocol.ClientHandler(connection.Client, -- jsonrpc2.MethodNotFound))) +- protocol.ClientHandler(client, jsonrpc2.MethodNotFound))) - return connection, connection.initialize(ctx, app.options) -} - @@ -25228,14 +28542,11 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - -func (c *connection) initialize(ctx context.Context, options func(*source.Options)) error { - params := &protocol.ParamInitialize{} -- params.RootURI = protocol.URIFromPath(c.Client.app.wd) +- params.RootURI = protocol.URIFromPath(c.client.app.wd) - params.Capabilities.Workspace.Configuration = true - - // Make sure to respect configured options when sending initialize request. -- opts := source.DefaultOptions().Clone() -- if options != nil { -- options(opts) -- } +- opts := source.DefaultOptions(options) - // If you add an additional option here, you must update the map key in connect. - params.Capabilities.TextDocument.Hover = &protocol.HoverClientCapabilities{ - ContentFormat: []protocol.MarkupKind{opts.PreferredContentFormat}, @@ -25247,6 +28558,13 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - params.Capabilities.TextDocument.SemanticTokens.Requests.Full.Value = true - params.Capabilities.TextDocument.SemanticTokens.TokenTypes = lsp.SemanticTypes() - params.Capabilities.TextDocument.SemanticTokens.TokenModifiers = lsp.SemanticModifiers() +- +- // If the subcommand has registered a progress handler, report the progress +- // capability. +- if c.client.onProgress != nil { +- params.Capabilities.Window.WorkDoneProgress = true +- } +- - params.InitializationOptions = map[string]interface{}{ - "symbolMatcher": matcherString[opts.SymbolMatcher], - } @@ -25261,17 +28579,18 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - -type connection struct { - protocol.Server -- Client *cmdClient +- client *cmdClient -} - +-// cmdClient defines the protocol.Client interface behavior of the gopls CLI tool. -type cmdClient struct { -- protocol.Server -- app *Application +- app *Application +- onProgress func(*protocol.ProgressParams) - - diagnosticsMu sync.Mutex - diagnosticsDone chan struct{} - -- filesMu sync.Mutex +- filesMu sync.Mutex // guards files map and each cmdFile.diagnostics - files map[span.URI]*cmdFile -} - @@ -25279,16 +28598,21 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - uri span.URI - mapper *protocol.Mapper - err error -- open bool - diagnostics []protocol.Diagnostic -} - --func newConnection(app *Application) *connection { +-func newClient(app *Application, onProgress func(*protocol.ProgressParams)) *cmdClient { +- return &cmdClient{ +- app: app, +- onProgress: onProgress, +- files: make(map[span.URI]*cmdFile), +- } +-} +- +-func newConnection(server protocol.Server, client *cmdClient) *connection { - return &connection{ -- Client: &cmdClient{ -- app: app, -- files: make(map[span.URI]*cmdFile), -- }, +- Server: server, +- client: client, - } -} - @@ -25379,7 +28703,87 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go -} - -func (c *cmdClient) ApplyEdit(ctx context.Context, p *protocol.ApplyWorkspaceEditParams) (*protocol.ApplyWorkspaceEditResult, error) { -- return &protocol.ApplyWorkspaceEditResult{Applied: false, FailureReason: "not implemented"}, nil +- if err := c.applyWorkspaceEdit(&p.Edit); err != nil { +- return &protocol.ApplyWorkspaceEditResult{FailureReason: err.Error()}, nil +- } +- return &protocol.ApplyWorkspaceEditResult{Applied: true}, nil +-} +- +-// applyWorkspaceEdit applies a complete WorkspaceEdit to the client's +-// files, honoring the preferred edit mode specified by cli.app.editMode. +-// (Used by rename and by ApplyEdit downcalls.) +-func (cli *cmdClient) applyWorkspaceEdit(edit *protocol.WorkspaceEdit) error { +- var orderedURIs []string +- edits := map[span.URI][]protocol.TextEdit{} +- for _, c := range edit.DocumentChanges { +- if c.TextDocumentEdit != nil { +- uri := fileURI(c.TextDocumentEdit.TextDocument.URI) +- edits[uri] = append(edits[uri], c.TextDocumentEdit.Edits...) +- orderedURIs = append(orderedURIs, string(uri)) +- } +- if c.RenameFile != nil { +- return fmt.Errorf("client does not support file renaming (%s -> %s)", +- c.RenameFile.OldURI, +- c.RenameFile.NewURI) +- } +- } +- sort.Strings(orderedURIs) +- for _, u := range orderedURIs { +- uri := span.URIFromURI(u) +- f := cli.openFile(uri) +- if f.err != nil { +- return f.err +- } +- if err := applyTextEdits(f.mapper, edits[uri], cli.app.editFlags); err != nil { +- return err +- } +- } +- return nil +-} +- +-// applyTextEdits applies a list of edits to the mapper file content, +-// using the preferred edit mode. It is a no-op if there are no edits. +-func applyTextEdits(mapper *protocol.Mapper, edits []protocol.TextEdit, flags *EditFlags) error { +- if len(edits) == 0 { +- return nil +- } +- newContent, renameEdits, err := source.ApplyProtocolEdits(mapper, edits) +- if err != nil { +- return err +- } +- +- filename := mapper.URI.Filename() +- +- if flags.List { +- fmt.Println(filename) +- } +- +- if flags.Write { +- if flags.Preserve { +- if err := os.Rename(filename, filename+".orig"); err != nil { +- return err +- } +- } +- if err := os.WriteFile(filename, newContent, 0644); err != nil { +- return err +- } +- } +- +- if flags.Diff { +- unified, err := diff.ToUnified(filename+".orig", filename, string(mapper.Content), renameEdits) +- if err != nil { +- return err +- } +- fmt.Print(unified) +- } +- +- // No flags: just print edited file content. +- // TODO(adonovan): how is this ever useful with multiple files? +- if !(flags.List || flags.Write || flags.Diff) { +- os.Stdout.Write(newContent) +- } +- +- return nil -} - -func (c *cmdClient) PublishDiagnostics(ctx context.Context, p *protocol.PublishDiagnosticsParams) error { @@ -25394,17 +28798,52 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - c.filesMu.Lock() - defer c.filesMu.Unlock() - -- file := c.getFile(ctx, fileURI(p.URI)) -- file.diagnostics = p.Diagnostics +- file := c.getFile(fileURI(p.URI)) +- file.diagnostics = append(file.diagnostics, p.Diagnostics...) +- +- // Perform a crude in-place deduplication. +- // TODO(golang/go#60122): replace the ad-hoc gopls/diagnoseFiles +- // non-standard request with support for textDocument/diagnostic, +- // so that we don't need to do this de-duplication. +- type key [6]interface{} +- seen := make(map[key]bool) +- out := file.diagnostics[:0] +- for _, d := range file.diagnostics { +- var codeHref string +- if desc := d.CodeDescription; desc != nil { +- codeHref = desc.Href +- } +- k := key{d.Range, d.Severity, d.Code, codeHref, d.Source, d.Message} +- if !seen[k] { +- seen[k] = true +- out = append(out, d) +- } +- } +- file.diagnostics = out +- - return nil -} - --func (c *cmdClient) Progress(context.Context, *protocol.ProgressParams) error { +-func (c *cmdClient) Progress(_ context.Context, params *protocol.ProgressParams) error { +- if c.onProgress != nil { +- c.onProgress(params) +- } - return nil -} - --func (c *cmdClient) ShowDocument(context.Context, *protocol.ShowDocumentParams) (*protocol.ShowDocumentResult, error) { -- return nil, nil +-func (c *cmdClient) ShowDocument(ctx context.Context, params *protocol.ShowDocumentParams) (*protocol.ShowDocumentResult, error) { +- var success bool +- if params.External { +- // Open URI in external browser. +- success = browser.Open(string(params.URI)) +- } else { +- // Open file in editor, optionally taking focus and selecting a range. +- // (cmdClient has no editor. Should it fork+exec $EDITOR?) +- log.Printf("Server requested that client editor open %q (takeFocus=%t, selection=%+v)", +- params.URI, params.TakeFocus, params.Selection) +- success = true +- } +- return &protocol.ShowDocumentResult{Success: success}, nil -} - -func (c *cmdClient) WorkDoneProgressCreate(context.Context, *protocol.WorkDoneProgressCreateParams) error { @@ -25427,7 +28866,7 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - return nil -} - --func (c *cmdClient) getFile(ctx context.Context, uri span.URI) *cmdFile { +-func (c *cmdClient) getFile(uri span.URI) *cmdFile { - file, found := c.files[uri] - if !found || file.err != nil { - file = &cmdFile{ @@ -25436,7 +28875,7 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - c.files[uri] = file - } - if file.mapper == nil { -- content, err := ioutil.ReadFile(uri.Filename()) +- content, err := os.ReadFile(uri.Filename()) - if err != nil { - file.err = fmt.Errorf("getFile: %v: %v", uri, err) - return file @@ -25446,22 +28885,19 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - return file -} - --func (c *cmdClient) openFile(ctx context.Context, uri span.URI) *cmdFile { +-func (c *cmdClient) openFile(uri span.URI) *cmdFile { - c.filesMu.Lock() - defer c.filesMu.Unlock() -- -- file := c.getFile(ctx, uri) -- if file.err != nil || file.open { -- return file -- } -- file.open = true -- return file +- return c.getFile(uri) -} - --func (c *connection) openFile(ctx context.Context, uri span.URI) *cmdFile { -- file := c.Client.openFile(ctx, uri) +-// TODO(adonovan): provide convenience helpers to: +-// - map a (URI, protocol.Range) to a MappedRange; +-// - parse a command-line argument to a MappedRange. +-func (c *connection) openFile(ctx context.Context, uri span.URI) (*cmdFile, error) { +- file := c.client.openFile(uri) - if file.err != nil { -- return file +- return nil, file.err - } - - p := &protocol.DidOpenTextDocumentParams{ @@ -25473,9 +28909,11 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - }, - } - if err := c.Server.DidOpen(ctx, p); err != nil { +- // TODO(adonovan): is this assignment concurrency safe? - file.err = fmt.Errorf("%v: %v", uri, err) +- return nil, file.err - } -- return file +- return file, nil -} - -func (c *connection) semanticTokens(ctx context.Context, p *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) { @@ -25492,22 +28930,22 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go - for _, file := range files { - untypedFiles = append(untypedFiles, string(file)) - } -- c.Client.diagnosticsMu.Lock() -- defer c.Client.diagnosticsMu.Unlock() +- c.client.diagnosticsMu.Lock() +- defer c.client.diagnosticsMu.Unlock() - -- c.Client.diagnosticsDone = make(chan struct{}) +- c.client.diagnosticsDone = make(chan struct{}) - _, err := c.Server.NonstandardRequest(ctx, "gopls/diagnoseFiles", map[string]interface{}{"files": untypedFiles}) - if err != nil { -- close(c.Client.diagnosticsDone) +- close(c.client.diagnosticsDone) - return err - } - -- <-c.Client.diagnosticsDone +- <-c.client.diagnosticsDone - return nil -} - -func (c *connection) terminate(ctx context.Context) { -- if strings.HasPrefix(c.Client.app.Remote, "internal@") { +- if strings.HasPrefix(c.client.app.Remote, "internal@") { - // internal connections need to be left alive for the next test - return - } @@ -25523,8 +28961,8 @@ diff -urN a/gopls/internal/lsp/cmd/cmd.go b/gopls/internal/lsp/cmd/cmd.go -} diff -urN a/gopls/internal/lsp/cmd/definition.go b/gopls/internal/lsp/cmd/definition.go --- a/gopls/internal/lsp/cmd/definition.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/definition.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,132 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/definition.go 1970-01-01 08:00:00 +@@ -1,138 +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. @@ -25601,15 +29039,15 @@ diff -urN a/gopls/internal/lsp/cmd/definition.go b/gopls/internal/lsp/cmd/defini - o.PreferredContentFormat = protocol.Markdown - } - } -- conn, err := d.app.connect(ctx) +- conn, err := d.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - from := span.Parse(args[0]) -- file := conn.openFile(ctx, from.URI()) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, from.URI()) +- if err != nil { +- return err - } - loc, err := file.mapper.SpanLocation(from) - if err != nil { @@ -25626,25 +29064,27 @@ diff -urN a/gopls/internal/lsp/cmd/definition.go b/gopls/internal/lsp/cmd/defini - if len(locs) == 0 { - return fmt.Errorf("%v: not an identifier", from) - } -- q := protocol.HoverParams{ -- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), -- } -- hover, err := conn.Hover(ctx, &q) +- file, err = conn.openFile(ctx, fileURI(locs[0].URI)) - if err != nil { - return fmt.Errorf("%v: %v", from, err) - } -- if hover == nil { -- return fmt.Errorf("%v: not an identifier", from) +- definition, err := file.mapper.LocationSpan(locs[0]) +- if err != nil { +- return fmt.Errorf("%v: %v", from, err) - } -- file = conn.openFile(ctx, fileURI(locs[0].URI)) -- if file.err != nil { -- return fmt.Errorf("%v: %v", from, file.err) +- +- q := protocol.HoverParams{ +- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), - } -- definition, err := file.mapper.LocationSpan(locs[0]) +- hover, err := conn.Hover(ctx, &q) - if err != nil { - return fmt.Errorf("%v: %v", from, err) - } -- description := strings.TrimSpace(hover.Contents.Value) +- var description string +- if hover != nil { +- description = strings.TrimSpace(hover.Contents.Value) +- } +- - result := &Definition{ - Span: definition, - Description: description, @@ -25654,13 +29094,17 @@ diff -urN a/gopls/internal/lsp/cmd/definition.go b/gopls/internal/lsp/cmd/defini - enc.SetIndent("", "\t") - return enc.Encode(result) - } -- fmt.Printf("%v: defined here as %s", result.Span, result.Description) +- fmt.Printf("%v", result.Span) +- if len(result.Description) > 0 { +- fmt.Printf(": defined here as %s", result.Description) +- } +- fmt.Printf("\n") - return nil -} diff -urN a/gopls/internal/lsp/cmd/folding_range.go b/gopls/internal/lsp/cmd/folding_range.go --- a/gopls/internal/lsp/cmd/folding_range.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/folding_range.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,73 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/folding_range.go 1970-01-01 08:00:00 +@@ -1,72 +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. @@ -25700,16 +29144,15 @@ diff -urN a/gopls/internal/lsp/cmd/folding_range.go b/gopls/internal/lsp/cmd/fol - return tool.CommandLineErrorf("folding_ranges expects 1 argument (file)") - } - -- conn, err := r.app.connect(ctx) +- conn, err := r.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - - from := span.Parse(args[0]) -- file := conn.openFile(ctx, from.URI()) -- if file.err != nil { -- return file.err +- if _, err := conn.openFile(ctx, from.URI()); err != nil { +- return err - } - - p := protocol.FoldingRangeParams{ @@ -25736,8 +29179,8 @@ diff -urN a/gopls/internal/lsp/cmd/folding_range.go b/gopls/internal/lsp/cmd/fol -} diff -urN a/gopls/internal/lsp/cmd/format.go b/gopls/internal/lsp/cmd/format.go --- a/gopls/internal/lsp/cmd/format.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/format.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,110 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/format.go 1970-01-01 08:00:00 +@@ -1,76 +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. @@ -25748,21 +29191,14 @@ diff -urN a/gopls/internal/lsp/cmd/format.go b/gopls/internal/lsp/cmd/format.go - "context" - "flag" - "fmt" -- "io/ioutil" -- "os" - - "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/diff" -) - -// format implements the format verb for gopls. -type format struct { -- Diff bool `flag:"d,diff" help:"display diffs instead of rewriting files"` -- Write bool `flag:"w,write" help:"write result to (source) file instead of stdout"` -- List bool `flag:"l,list" help:"list files whose formatting differs from gofmt's"` -- +- EditFlags - app *Application -} - @@ -25787,22 +29223,20 @@ diff -urN a/gopls/internal/lsp/cmd/format.go b/gopls/internal/lsp/cmd/format.go -// results to stdout. -func (c *format) Run(ctx context.Context, args ...string) error { - if len(args) == 0 { -- // no files, so no results - return nil - } -- // now we ready to kick things off -- conn, err := c.app.connect(ctx) +- c.app.editFlags = &c.EditFlags +- conn, err := c.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - for _, arg := range args { - spn := span.Parse(arg) -- file := conn.openFile(ctx, spn.URI()) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, spn.URI()) +- if err != nil { +- return err - } -- filename := spn.URI().Filename() - loc, err := file.mapper.SpanLocation(spn) - if err != nil { - return err @@ -25817,41 +29251,16 @@ diff -urN a/gopls/internal/lsp/cmd/format.go b/gopls/internal/lsp/cmd/format.go - if err != nil { - return fmt.Errorf("%v: %v", spn, err) - } -- formatted, sedits, err := source.ApplyProtocolEdits(file.mapper, edits) -- if err != nil { -- return fmt.Errorf("%v: %v", spn, err) -- } -- printIt := true -- if c.List { -- printIt = false -- if len(edits) > 0 { -- fmt.Println(filename) -- } -- } -- if c.Write { -- printIt = false -- if len(edits) > 0 { -- ioutil.WriteFile(filename, formatted, 0644) -- } -- } -- if c.Diff { -- printIt = false -- unified, err := diff.ToUnified(filename+".orig", filename, string(file.mapper.Content), sedits) -- if err != nil { -- return err -- } -- fmt.Print(unified) -- } -- if printIt { -- os.Stdout.Write(formatted) +- if err := applyTextEdits(file.mapper, edits, c.app.editFlags); err != nil { +- return err - } - } - return nil -} diff -urN a/gopls/internal/lsp/cmd/help_test.go b/gopls/internal/lsp/cmd/help_test.go --- a/gopls/internal/lsp/cmd/help_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/help_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,58 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/help_test.go 1970-01-01 08:00:00 +@@ -1,84 +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. @@ -25862,7 +29271,7 @@ diff -urN a/gopls/internal/lsp/cmd/help_test.go b/gopls/internal/lsp/cmd/help_te - "bytes" - "context" - "flag" -- "io/ioutil" +- "os" - "path/filepath" - "testing" - @@ -25895,12 +29304,12 @@ diff -urN a/gopls/internal/lsp/cmd/help_test.go b/gopls/internal/lsp/cmd/help_te - helpFile := filepath.Join("usage", name+".hlp") - got := buf.Bytes() - if *updateHelpFiles { -- if err := ioutil.WriteFile(helpFile, got, 0666); err != nil { +- if err := os.WriteFile(helpFile, got, 0666); err != nil { - t.Errorf("Failed writing %v: %v", helpFile, err) - } - return - } -- want, err := ioutil.ReadFile(helpFile) +- want, err := os.ReadFile(helpFile) - if err != nil { - t.Fatalf("Missing help file %q", helpFile) - } @@ -25910,9 +29319,35 @@ diff -urN a/gopls/internal/lsp/cmd/help_test.go b/gopls/internal/lsp/cmd/help_te - }) - } -} +- +-func TestVerboseHelp(t *testing.T) { +- testenv.NeedsGoBuild(t) // This is a lie. We actually need the source code. +- app := cmd.New(appName, "", nil, nil) +- ctx := context.Background() +- var buf bytes.Buffer +- s := flag.NewFlagSet(appName, flag.ContinueOnError) +- s.SetOutput(&buf) +- tool.Run(ctx, s, app, []string{"-v", "-h"}) +- got := buf.Bytes() +- +- helpFile := filepath.Join("usage", "usage-v.hlp") +- if *updateHelpFiles { +- if err := os.WriteFile(helpFile, got, 0666); err != nil { +- t.Errorf("Failed writing %v: %v", helpFile, err) +- } +- return +- } +- want, err := os.ReadFile(helpFile) +- if err != nil { +- t.Fatalf("Missing help file %q", helpFile) +- } +- if diff := cmp.Diff(string(want), string(got)); diff != "" { +- t.Errorf("Help file %q did not match, run with -update-help-files to fix (-want +got)\n%s", helpFile, diff) +- } +-} diff -urN a/gopls/internal/lsp/cmd/highlight.go b/gopls/internal/lsp/cmd/highlight.go --- a/gopls/internal/lsp/cmd/highlight.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/highlight.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/highlight.go 1970-01-01 08:00:00 @@ -1,82 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -25955,16 +29390,16 @@ diff -urN a/gopls/internal/lsp/cmd/highlight.go b/gopls/internal/lsp/cmd/highlig - return tool.CommandLineErrorf("highlight expects 1 argument (position)") - } - -- conn, err := r.app.connect(ctx) +- conn, err := r.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - - from := span.Parse(args[0]) -- file := conn.openFile(ctx, from.URI()) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, from.URI()) +- if err != nil { +- return err - } - - loc, err := file.mapper.SpanLocation(from) @@ -25998,8 +29433,8 @@ diff -urN a/gopls/internal/lsp/cmd/highlight.go b/gopls/internal/lsp/cmd/highlig -} diff -urN a/gopls/internal/lsp/cmd/implementation.go b/gopls/internal/lsp/cmd/implementation.go --- a/gopls/internal/lsp/cmd/implementation.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/implementation.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,84 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/implementation.go 1970-01-01 08:00:00 +@@ -1,87 +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. @@ -26042,16 +29477,16 @@ diff -urN a/gopls/internal/lsp/cmd/implementation.go b/gopls/internal/lsp/cmd/im - return tool.CommandLineErrorf("implementation expects 1 argument (position)") - } - -- conn, err := i.app.connect(ctx) +- conn, err := i.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - - from := span.Parse(args[0]) -- file := conn.openFile(ctx, from.URI()) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, from.URI()) +- if err != nil { +- return err - } - - loc, err := file.mapper.SpanLocation(from) @@ -26069,7 +29504,10 @@ diff -urN a/gopls/internal/lsp/cmd/implementation.go b/gopls/internal/lsp/cmd/im - - var spans []string - for _, impl := range implementations { -- f := conn.openFile(ctx, fileURI(impl.URI)) +- f, err := conn.openFile(ctx, fileURI(impl.URI)) +- if err != nil { +- return err +- } - span, err := f.mapper.LocationSpan(impl) - if err != nil { - return err @@ -26086,8 +29524,8 @@ diff -urN a/gopls/internal/lsp/cmd/implementation.go b/gopls/internal/lsp/cmd/im -} diff -urN a/gopls/internal/lsp/cmd/imports.go b/gopls/internal/lsp/cmd/imports.go --- a/gopls/internal/lsp/cmd/imports.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/imports.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,105 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/imports.go 1970-01-01 08:00:00 +@@ -1,81 +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. @@ -26098,21 +29536,15 @@ diff -urN a/gopls/internal/lsp/cmd/imports.go b/gopls/internal/lsp/cmd/imports.g - "context" - "flag" - "fmt" -- "io/ioutil" -- "os" - - "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/diff" - "golang.org/x/tools/internal/tool" -) - -// imports implements the import verb for gopls. -type imports struct { -- Diff bool `flag:"d,diff" help:"display diffs instead of rewriting files"` -- Write bool `flag:"w,write" help:"write result to (source) file instead of stdout"` -- +- EditFlags - app *Application -} - @@ -26139,7 +29571,8 @@ diff -urN a/gopls/internal/lsp/cmd/imports.go b/gopls/internal/lsp/cmd/imports.g - if len(args) != 1 { - return tool.CommandLineErrorf("imports expects 1 argument") - } -- conn, err := t.app.connect(ctx) +- t.app.editFlags = &t.EditFlags +- conn, err := t.app.connect(ctx, nil) - if err != nil { - return err - } @@ -26147,9 +29580,9 @@ diff -urN a/gopls/internal/lsp/cmd/imports.go b/gopls/internal/lsp/cmd/imports.g - - from := span.Parse(args[0]) - uri := from.URI() -- file := conn.openFile(ctx, uri) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, uri) +- if err != nil { +- return err - } - actions, err := conn.CodeAction(ctx, &protocol.CodeActionParams{ - TextDocument: protocol.TextDocumentIdentifier{ @@ -26172,31 +29605,12 @@ diff -urN a/gopls/internal/lsp/cmd/imports.go b/gopls/internal/lsp/cmd/imports.g - } - } - } -- newContent, sedits, err := source.ApplyProtocolEdits(file.mapper, edits) -- if err != nil { -- return fmt.Errorf("%v: %v", edits, err) -- } -- filename := file.uri.Filename() -- switch { -- case t.Write: -- if len(edits) > 0 { -- ioutil.WriteFile(filename, newContent, 0644) -- } -- case t.Diff: -- unified, err := diff.ToUnified(filename+".orig", filename, string(file.mapper.Content), sedits) -- if err != nil { -- return err -- } -- fmt.Print(unified) -- default: -- os.Stdout.Write(newContent) -- } -- return nil +- return applyTextEdits(file.mapper, edits, t.app.editFlags) -} diff -urN a/gopls/internal/lsp/cmd/info.go b/gopls/internal/lsp/cmd/info.go --- a/gopls/internal/lsp/cmd/info.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/info.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,246 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/info.go 1970-01-01 08:00:00 +@@ -1,311 +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. @@ -26211,10 +29625,13 @@ diff -urN a/gopls/internal/lsp/cmd/info.go b/gopls/internal/lsp/cmd/info.go - "fmt" - "net/url" - "os" +- "sort" - "strings" - +- goplsbug "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/browser" - "golang.org/x/tools/gopls/internal/lsp/debug" +- "golang.org/x/tools/gopls/internal/lsp/filecache" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/internal/tool" -) @@ -26327,10 +29744,50 @@ diff -urN a/gopls/internal/lsp/cmd/info.go b/gopls/internal/lsp/cmd/info.go -// Run collects some basic information and then prepares an issue ready to -// be reported. -func (b *bug) Run(ctx context.Context, args ...string) error { -- buf := &bytes.Buffer{} -- fmt.Fprint(buf, goplsBugHeader) -- debug.PrintVersionInfo(ctx, buf, true, debug.Markdown) -- body := buf.String() +- // This undocumented environment variable allows +- // the cmd integration test (and maintainers) to +- // trigger a call to bug.Report. +- if msg := os.Getenv("TEST_GOPLS_BUG"); msg != "" { +- filecache.Start() // register bug handler +- goplsbug.Report(msg) +- return nil +- } +- +- // Enumerate bug reports, grouped and sorted. +- _, reports := filecache.BugReports() +- sort.Slice(reports, func(i, j int) bool { +- x, y := reports[i], reports[i] +- if x.Key != y.Key { +- return x.Key < y.Key // ascending key order +- } +- return y.AtTime.Before(x.AtTime) // most recent first +- }) +- keyDenom := make(map[string]int) // key is "file:line" +- for _, report := range reports { +- keyDenom[report.Key]++ +- } +- +- // Privacy: the content of 'public' will be posted to GitHub +- // to populate an issue textarea. Even though the user must +- // submit the form to share the information with the world, +- // merely populating the form causes us to share the +- // information with GitHub itself. +- // +- // For that reason, we cannot write private information to +- // public, such as bug reports, which may quote source code. +- public := &bytes.Buffer{} +- fmt.Fprint(public, goplsBugHeader) +- if len(reports) > 0 { +- fmt.Fprintf(public, "#### Internal errors\n\n") +- fmt.Fprintf(public, "Gopls detected %d internal errors, %d distinct:\n", +- len(reports), len(keyDenom)) +- for key, denom := range keyDenom { +- fmt.Fprintf(public, "- %s (%d)\n", key, denom) +- } +- fmt.Fprintf(public, "\nPlease copy the full information printed by `gopls bug` here, if you are comfortable sharing it.\n\n") +- } +- debug.PrintVersionInfo(ctx, public, true, debug.Markdown) +- body := public.String() - title := strings.Join(args, " ") - if !strings.HasPrefix(title, goplsBugPrefix) { - title = goplsBugPrefix + title @@ -26339,6 +29796,29 @@ diff -urN a/gopls/internal/lsp/cmd/info.go b/gopls/internal/lsp/cmd/info.go - fmt.Print("Please file a new issue at golang.org/issue/new using this template:\n\n") - fmt.Print(body) - } +- +- // Print bug reports to stdout (not GitHub). +- keyNum := make(map[string]int) +- for _, report := range reports { +- fmt.Printf("-- %v -- \n", report.AtTime) +- +- // Append seq number (e.g. " (1/2)") for repeated keys. +- var seq string +- if denom := keyDenom[report.Key]; denom > 1 { +- keyNum[report.Key]++ +- seq = fmt.Sprintf(" (%d/%d)", keyNum[report.Key], denom) +- } +- +- // Privacy: +- // - File and Stack may contain the name of the user that built gopls. +- // - Description may contain names of the user's packages/files/symbols. +- fmt.Printf("%s:%d: %s%s\n\n", report.File, report.Line, report.Description, seq) +- fmt.Printf("%s\n\n", report.Stack) +- } +- if len(reports) > 0 { +- fmt.Printf("Please copy the above information into the GitHub issue, if you are comfortable sharing it.\n") +- } +- - return nil -} - @@ -26432,8 +29912,7 @@ diff -urN a/gopls/internal/lsp/cmd/info.go b/gopls/internal/lsp/cmd/info.go -` - -func (l *licenses) Run(ctx context.Context, args ...string) error { -- opts := source.DefaultOptions() -- l.app.options(opts) +- opts := source.DefaultOptions(l.app.options) - txt := licensePreamble - if opts.LicensesText == "" { - txt += "(development gopls, license information not available)" @@ -26445,7 +29924,7 @@ diff -urN a/gopls/internal/lsp/cmd/info.go b/gopls/internal/lsp/cmd/info.go -} diff -urN a/gopls/internal/lsp/cmd/links.go b/gopls/internal/lsp/cmd/links.go --- a/gopls/internal/lsp/cmd/links.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/links.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/links.go 1970-01-01 08:00:00 @@ -1,77 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -26494,7 +29973,7 @@ diff -urN a/gopls/internal/lsp/cmd/links.go b/gopls/internal/lsp/cmd/links.go - if len(args) != 1 { - return tool.CommandLineErrorf("links expects 1 argument") - } -- conn, err := l.app.connect(ctx) +- conn, err := l.app.connect(ctx, nil) - if err != nil { - return err - } @@ -26502,9 +29981,9 @@ diff -urN a/gopls/internal/lsp/cmd/links.go b/gopls/internal/lsp/cmd/links.go - - from := span.Parse(args[0]) - uri := from.URI() -- file := conn.openFile(ctx, uri) -- if file.err != nil { -- return file.err +- +- if _, err := conn.openFile(ctx, uri); err != nil { +- return err - } - results, err := conn.DocumentLink(ctx, &protocol.DocumentLinkParams{ - TextDocument: protocol.TextDocumentIdentifier{ @@ -26520,13 +29999,13 @@ diff -urN a/gopls/internal/lsp/cmd/links.go b/gopls/internal/lsp/cmd/links.go - return enc.Encode(results) - } - for _, v := range results { -- fmt.Println(v.Target) +- fmt.Println(*v.Target) - } - return nil -} diff -urN a/gopls/internal/lsp/cmd/prepare_rename.go b/gopls/internal/lsp/cmd/prepare_rename.go --- a/gopls/internal/lsp/cmd/prepare_rename.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/prepare_rename.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/prepare_rename.go 1970-01-01 08:00:00 @@ -1,80 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -26574,16 +30053,16 @@ diff -urN a/gopls/internal/lsp/cmd/prepare_rename.go b/gopls/internal/lsp/cmd/pr - return tool.CommandLineErrorf("prepare_rename expects 1 argument (file)") - } - -- conn, err := r.app.connect(ctx) +- conn, err := r.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - - from := span.Parse(args[0]) -- file := conn.openFile(ctx, from.URI()) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, from.URI()) +- if err != nil { +- return err - } - loc, err := file.mapper.SpanLocation(from) - if err != nil { @@ -26610,8 +30089,8 @@ diff -urN a/gopls/internal/lsp/cmd/prepare_rename.go b/gopls/internal/lsp/cmd/pr -} diff -urN a/gopls/internal/lsp/cmd/references.go b/gopls/internal/lsp/cmd/references.go --- a/gopls/internal/lsp/cmd/references.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/references.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,89 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/references.go 1970-01-01 08:00:00 +@@ -1,92 +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. @@ -26658,16 +30137,16 @@ diff -urN a/gopls/internal/lsp/cmd/references.go b/gopls/internal/lsp/cmd/refere - return tool.CommandLineErrorf("references expects 1 argument (position)") - } - -- conn, err := r.app.connect(ctx) +- conn, err := r.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - - from := span.Parse(args[0]) -- file := conn.openFile(ctx, from.URI()) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, from.URI()) +- if err != nil { +- return err - } - loc, err := file.mapper.SpanLocation(from) - if err != nil { @@ -26685,7 +30164,10 @@ diff -urN a/gopls/internal/lsp/cmd/references.go b/gopls/internal/lsp/cmd/refere - } - var spans []string - for _, l := range locations { -- f := conn.openFile(ctx, fileURI(l.URI)) +- f, err := conn.openFile(ctx, fileURI(l.URI)) +- if err != nil { +- return err +- } - // convert location to span for user-friendly 1-indexed line - // and column numbers - span, err := f.mapper.LocationSpan(l) @@ -26703,7 +30185,7 @@ diff -urN a/gopls/internal/lsp/cmd/references.go b/gopls/internal/lsp/cmd/refere -} diff -urN a/gopls/internal/lsp/cmd/remote.go b/gopls/internal/lsp/cmd/remote.go --- a/gopls/internal/lsp/cmd/remote.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/remote.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/remote.go 1970-01-01 08:00:00 @@ -1,164 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -26871,8 +30353,8 @@ diff -urN a/gopls/internal/lsp/cmd/remote.go b/gopls/internal/lsp/cmd/remote.go -} diff -urN a/gopls/internal/lsp/cmd/rename.go b/gopls/internal/lsp/cmd/rename.go --- a/gopls/internal/lsp/cmd/rename.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/rename.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,130 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/rename.go 1970-01-01 08:00:00 +@@ -1,74 +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. @@ -26883,24 +30365,15 @@ diff -urN a/gopls/internal/lsp/cmd/rename.go b/gopls/internal/lsp/cmd/rename.go - "context" - "flag" - "fmt" -- "io/ioutil" -- "os" -- "path/filepath" -- "sort" - - "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/diff" - "golang.org/x/tools/internal/tool" -) - -// rename implements the rename verb for gopls. -type rename struct { -- Diff bool `flag:"d,diff" help:"display diffs instead of rewriting files"` -- Write bool `flag:"w,write" help:"write result to (source) file instead of stdout"` -- Preserve bool `flag:"preserve" help:"preserve original files"` -- +- EditFlags - app *Application -} - @@ -26929,16 +30402,17 @@ diff -urN a/gopls/internal/lsp/cmd/rename.go b/gopls/internal/lsp/cmd/rename.go - if len(args) != 2 { - return tool.CommandLineErrorf("definition expects 2 arguments (position, new name)") - } -- conn, err := r.app.connect(ctx) +- r.app.editFlags = &r.EditFlags +- conn, err := r.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - - from := span.Parse(args[0]) -- file := conn.openFile(ctx, from.URI()) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, from.URI()) +- if err != nil { +- return err - } - loc, err := file.mapper.SpanLocation(from) - if err != nil { @@ -26953,60 +30427,12 @@ diff -urN a/gopls/internal/lsp/cmd/rename.go b/gopls/internal/lsp/cmd/rename.go - if err != nil { - return err - } -- var orderedURIs []string -- edits := map[span.URI][]protocol.TextEdit{} -- for _, c := range edit.DocumentChanges { -- if c.TextDocumentEdit != nil { -- uri := fileURI(c.TextDocumentEdit.TextDocument.URI) -- edits[uri] = append(edits[uri], c.TextDocumentEdit.Edits...) -- orderedURIs = append(orderedURIs, string(uri)) -- } -- } -- sort.Strings(orderedURIs) -- changeCount := len(orderedURIs) -- -- for _, u := range orderedURIs { -- uri := span.URIFromURI(u) -- cmdFile := conn.openFile(ctx, uri) -- filename := cmdFile.uri.Filename() -- -- newContent, renameEdits, err := source.ApplyProtocolEdits(cmdFile.mapper, edits[uri]) -- if err != nil { -- return fmt.Errorf("%v: %v", edits, err) -- } -- -- switch { -- case r.Write: -- fmt.Fprintln(os.Stderr, filename) -- if r.Preserve { -- if err := os.Rename(filename, filename+".orig"); err != nil { -- return fmt.Errorf("%v: %v", edits, err) -- } -- } -- ioutil.WriteFile(filename, newContent, 0644) -- case r.Diff: -- unified, err := diff.ToUnified(filename+".orig", filename, string(cmdFile.mapper.Content), renameEdits) -- if err != nil { -- return err -- } -- fmt.Print(unified) -- default: -- if len(orderedURIs) > 1 { -- fmt.Printf("%s:\n", filepath.Base(filename)) -- } -- os.Stdout.Write(newContent) -- if changeCount > 1 { // if this wasn't last change, print newline -- fmt.Println() -- } -- changeCount -= 1 -- } -- } -- return nil +- return conn.client.applyWorkspaceEdit(edit) -} diff -urN a/gopls/internal/lsp/cmd/semantictokens.go b/gopls/internal/lsp/cmd/semantictokens.go --- a/gopls/internal/lsp/cmd/semantictokens.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/semantictokens.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,225 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/semantictokens.go 1970-01-01 08:00:00 +@@ -1,224 +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. @@ -27020,7 +30446,6 @@ diff -urN a/gopls/internal/lsp/cmd/semantictokens.go b/gopls/internal/lsp/cmd/se - "fmt" - "go/parser" - "go/token" -- "io/ioutil" - "log" - "os" - "unicode/utf8" @@ -27085,18 +30510,18 @@ diff -urN a/gopls/internal/lsp/cmd/semantictokens.go b/gopls/internal/lsp/cmd/se - origOptions(opts) - opts.SemanticTokens = true - } -- conn, err := c.app.connect(ctx) +- conn, err := c.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - uri := span.URIFromPath(args[0]) -- file := conn.openFile(ctx, uri) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, uri) +- if err != nil { +- return err - } - -- buf, err := ioutil.ReadFile(args[0]) +- buf, err := os.ReadFile(args[0]) - if err != nil { - return err - } @@ -27171,7 +30596,7 @@ diff -urN a/gopls/internal/lsp/cmd/semantictokens.go b/gopls/internal/lsp/cmd/se -} - -func decorate(file string, result []uint32) error { -- buf, err := ioutil.ReadFile(file) +- buf, err := os.ReadFile(file) - if err != nil { - return err - } @@ -27234,8 +30659,8 @@ diff -urN a/gopls/internal/lsp/cmd/semantictokens.go b/gopls/internal/lsp/cmd/se -} diff -urN a/gopls/internal/lsp/cmd/serve.go b/gopls/internal/lsp/cmd/serve.go --- a/gopls/internal/lsp/cmd/serve.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/serve.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,130 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/serve.go 1970-01-01 08:00:00 +@@ -1,146 +0,0 @@ -// Copyright 2018 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. @@ -27328,7 +30753,6 @@ diff -urN a/gopls/internal/lsp/cmd/serve.go b/gopls/internal/lsp/cmd/serve.go - } - defer closeLog() - di.ServerAddress = s.Address -- di.MonitorMemory(ctx) - di.Serve(ctx, s.Debug) - } - var ss jsonrpc2.StreamServer @@ -27348,6 +30772,23 @@ diff -urN a/gopls/internal/lsp/cmd/serve.go b/gopls/internal/lsp/cmd/serve.go - } - if s.Port != 0 { - network = "tcp" +- // TODO(adonovan): should gopls ever be listening on network +- // sockets, or only local ones? +- // +- // Ian says this was added in anticipation of +- // something related to "VS Code remote" that turned +- // out to be unnecessary. So I propose we limit it to +- // localhost, if only so that we avoid the macOS +- // firewall prompt. +- // +- // Hana says: "s.Address is for the remote access (LSP) +- // and s.Port is for debugging purpose (according to +- // the Server type documentation). I am not sure why the +- // existing code here is mixing up and overwriting addr. +- // For debugging endpoint, I think localhost makes perfect sense." +- // +- // TODO(adonovan): disentangle Address and Port, +- // and use only localhost for the latter. - addr = fmt.Sprintf(":%v", s.Port) - } - if addr != "" { @@ -27368,7 +30809,7 @@ diff -urN a/gopls/internal/lsp/cmd/serve.go b/gopls/internal/lsp/cmd/serve.go -} diff -urN a/gopls/internal/lsp/cmd/signature.go b/gopls/internal/lsp/cmd/signature.go --- a/gopls/internal/lsp/cmd/signature.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/signature.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/signature.go 1970-01-01 08:00:00 @@ -1,88 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -27411,16 +30852,16 @@ diff -urN a/gopls/internal/lsp/cmd/signature.go b/gopls/internal/lsp/cmd/signatu - return tool.CommandLineErrorf("signature expects 1 argument (position)") - } - -- conn, err := r.app.connect(ctx) +- conn, err := r.app.connect(ctx, nil) - if err != nil { - return err - } - defer conn.terminate(ctx) - - from := span.Parse(args[0]) -- file := conn.openFile(ctx, from.URI()) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, from.URI()) +- if err != nil { +- return err - } - - loc, err := file.mapper.SpanLocation(from) @@ -27458,9 +30899,284 @@ diff -urN a/gopls/internal/lsp/cmd/signature.go b/gopls/internal/lsp/cmd/signatu - - return nil -} +diff -urN a/gopls/internal/lsp/cmd/stats.go b/gopls/internal/lsp/cmd/stats.go +--- a/gopls/internal/lsp/cmd/stats.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cmd/stats.go 1970-01-01 08:00:00 +@@ -1,271 +0,0 @@ +-// Copyright 2023 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 cmd +- +-import ( +- "context" +- "encoding/json" +- "flag" +- "fmt" +- "go/token" +- "io/fs" +- "os" +- "path/filepath" +- "reflect" +- "runtime" +- "strings" +- "sync" +- "time" +- +- goplsbug "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp" +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/debug" +- "golang.org/x/tools/gopls/internal/lsp/filecache" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/internal/event" +-) +- +-type stats struct { +- app *Application +- +- Anon bool `flag:"anon" help:"hide any fields that may contain user names, file names, or source code"` +-} +- +-func (s *stats) Name() string { return "stats" } +-func (r *stats) Parent() string { return r.app.Name() } +-func (s *stats) Usage() string { return "" } +-func (s *stats) ShortHelp() string { return "print workspace statistics" } +- +-func (s *stats) DetailedHelp(f *flag.FlagSet) { +- fmt.Fprint(f.Output(), ` +-Load the workspace for the current directory, and output a JSON summary of +-workspace information relevant to performance. As a side effect, this command +-populates the gopls file cache for the current workspace. +- +-By default, this command may include output that refers to the location or +-content of user code. When the -anon flag is set, fields that may refer to user +-code are hidden. +- +-Example: +- $ gopls stats -anon +-`) +- printFlagDefaults(f) +-} +- +-func (s *stats) Run(ctx context.Context, args ...string) error { +- if s.app.Remote != "" { +- // stats does not work with -remote. +- // Other sessions on the daemon may interfere with results. +- // Additionally, the type assertions in below only work if progress +- // notifications bypass jsonrpc2 serialization. +- return fmt.Errorf("the stats subcommand does not work with -remote") +- } +- +- if !s.app.Verbose { +- event.SetExporter(nil) // don't log errors to stderr +- } +- +- stats := GoplsStats{ +- GOOS: runtime.GOOS, +- GOARCH: runtime.GOARCH, +- GOPLSCACHE: os.Getenv("GOPLSCACHE"), +- GoVersion: runtime.Version(), +- GoplsVersion: debug.Version(), +- } +- +- opts := s.app.options +- s.app.options = func(o *source.Options) { +- if opts != nil { +- opts(o) +- } +- o.VerboseWorkDoneProgress = true +- } +- var ( +- iwlMu sync.Mutex +- iwlToken protocol.ProgressToken +- iwlDone = make(chan struct{}) +- ) +- +- onProgress := func(p *protocol.ProgressParams) { +- switch v := p.Value.(type) { +- case *protocol.WorkDoneProgressBegin: +- if v.Title == lsp.DiagnosticWorkTitle(lsp.FromInitialWorkspaceLoad) { +- iwlMu.Lock() +- iwlToken = p.Token +- iwlMu.Unlock() +- } +- case *protocol.WorkDoneProgressEnd: +- iwlMu.Lock() +- tok := iwlToken +- iwlMu.Unlock() +- +- if p.Token == tok { +- close(iwlDone) +- } +- } +- } +- +- // do executes a timed section of the stats command. +- do := func(name string, f func() error) (time.Duration, error) { +- start := time.Now() +- fmt.Fprintf(os.Stderr, "%-30s", name+"...") +- if err := f(); err != nil { +- return time.Since(start), err +- } +- d := time.Since(start) +- fmt.Fprintf(os.Stderr, "done (%v)\n", d) +- return d, nil +- } +- +- var conn *connection +- iwlDuration, err := do("Initializing workspace", func() error { +- var err error +- conn, err = s.app.connect(ctx, onProgress) +- if err != nil { +- return err +- } +- select { +- case <-iwlDone: +- case <-ctx.Done(): +- return ctx.Err() +- } +- return nil +- }) +- stats.InitialWorkspaceLoadDuration = fmt.Sprint(iwlDuration) +- if err != nil { +- return err +- } +- defer conn.terminate(ctx) +- +- // Gather bug reports produced by any process using +- // this executable and persisted in the cache. +- do("Gathering bug reports", func() error { +- stats.CacheDir, stats.BugReports = filecache.BugReports() +- if stats.BugReports == nil { +- stats.BugReports = []goplsbug.Bug{} // non-nil for JSON +- } +- return nil +- }) +- +- if _, err := do("Querying memstats", func() error { +- memStats, err := conn.ExecuteCommand(ctx, &protocol.ExecuteCommandParams{ +- Command: command.MemStats.ID(), +- }) +- if err != nil { +- return err +- } +- stats.MemStats = memStats.(command.MemStatsResult) +- return nil +- }); err != nil { +- return err +- } +- +- if _, err := do("Querying workspace stats", func() error { +- wsStats, err := conn.ExecuteCommand(ctx, &protocol.ExecuteCommandParams{ +- Command: command.WorkspaceStats.ID(), +- }) +- if err != nil { +- return err +- } +- stats.WorkspaceStats = wsStats.(command.WorkspaceStatsResult) +- return nil +- }); err != nil { +- return err +- } +- +- if _, err := do("Collecting directory info", func() error { +- var err error +- stats.DirStats, err = findDirStats(ctx) +- if err != nil { +- return err +- } +- return nil +- }); err != nil { +- return err +- } +- +- // Filter JSON output to fields that are consistent with s.Anon. +- okFields := make(map[string]interface{}) +- { +- v := reflect.ValueOf(stats) +- t := v.Type() +- for i := 0; i < t.NumField(); i++ { +- f := t.Field(i) +- if !token.IsExported(f.Name) { +- continue +- } +- if s.Anon && f.Tag.Get("anon") != "ok" { +- // Fields that can be served with -anon must be explicitly marked as OK. +- continue +- } +- vf := v.FieldByName(f.Name) +- okFields[f.Name] = vf.Interface() +- } +- } +- data, err := json.MarshalIndent(okFields, "", " ") +- if err != nil { +- return err +- } +- +- os.Stdout.Write(data) +- fmt.Println() +- return nil +-} +- +-// GoplsStats holds information extracted from a gopls session in the current +-// workspace. +-// +-// Fields that should be printed with the -anon flag should be explicitly +-// marked as `anon:"ok"`. Only fields that cannot refer to user files or code +-// should be marked as such. +-type GoplsStats struct { +- GOOS, GOARCH string `anon:"ok"` +- GOPLSCACHE string +- GoVersion string `anon:"ok"` +- GoplsVersion string `anon:"ok"` +- InitialWorkspaceLoadDuration string `anon:"ok"` // in time.Duration string form +- CacheDir string +- BugReports []goplsbug.Bug +- MemStats command.MemStatsResult `anon:"ok"` +- WorkspaceStats command.WorkspaceStatsResult `anon:"ok"` +- DirStats dirStats `anon:"ok"` +-} +- +-type dirStats struct { +- Files int +- TestdataFiles int +- GoFiles int +- ModFiles int +- Dirs int +-} +- +-// findDirStats collects information about the current directory and its +-// subdirectories. +-func findDirStats(ctx context.Context) (dirStats, error) { +- var ds dirStats +- filepath.WalkDir(".", func(path string, d fs.DirEntry, err error) error { +- if err != nil { +- return err +- } +- if d.IsDir() { +- ds.Dirs++ +- } else { +- ds.Files++ +- slashed := filepath.ToSlash(path) +- switch { +- case strings.Contains(slashed, "/testdata/") || strings.HasPrefix(slashed, "testdata/"): +- ds.TestdataFiles++ +- case strings.HasSuffix(path, ".go"): +- ds.GoFiles++ +- case strings.HasSuffix(path, ".mod"): +- ds.ModFiles++ +- } +- } +- return nil +- }) +- return ds, nil +-} diff -urN a/gopls/internal/lsp/cmd/subcommands.go b/gopls/internal/lsp/cmd/subcommands.go --- a/gopls/internal/lsp/cmd/subcommands.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/subcommands.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/subcommands.go 1970-01-01 08:00:00 @@ -1,59 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -27523,8 +31239,8 @@ diff -urN a/gopls/internal/lsp/cmd/subcommands.go b/gopls/internal/lsp/cmd/subco -} diff -urN a/gopls/internal/lsp/cmd/suggested_fix.go b/gopls/internal/lsp/cmd/suggested_fix.go --- a/gopls/internal/lsp/cmd/suggested_fix.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/suggested_fix.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,167 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/suggested_fix.go 1970-01-01 08:00:00 +@@ -1,193 +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. @@ -27535,21 +31251,21 @@ diff -urN a/gopls/internal/lsp/cmd/suggested_fix.go b/gopls/internal/lsp/cmd/sug - "context" - "flag" - "fmt" -- "io/ioutil" -- "os" - - "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/diff" - "golang.org/x/tools/internal/tool" -) - +-// TODO(adonovan): this command has a very poor user interface. It +-// should have a way to query the available fixes for a file (without +-// a span), enumerate the valid fix kinds, enable all fixes, and not +-// require the pointless -all flag. See issue #60290. +- -// suggestedFix implements the fix verb for gopls. -type suggestedFix struct { -- Diff bool `flag:"d,diff" help:"display diffs instead of rewriting files"` -- Write bool `flag:"w,write" help:"write result to (source) file instead of stdout"` -- All bool `flag:"a,all" help:"apply all fixes, not just preferred fixes"` +- EditFlags +- All bool `flag:"a,all" help:"apply all fixes, not just preferred fixes"` - - app *Application -} @@ -27560,8 +31276,33 @@ diff -urN a/gopls/internal/lsp/cmd/suggested_fix.go b/gopls/internal/lsp/cmd/sug -func (s *suggestedFix) ShortHelp() string { return "apply suggested fixes" } -func (s *suggestedFix) DetailedHelp(f *flag.FlagSet) { - fmt.Fprintf(f.Output(), ` --Example: apply suggested fixes for this file -- $ gopls fix -w internal/lsp/cmd/check.go +-Example: apply fixes to this file, rewriting it: +- +- $ gopls fix -a -w internal/lsp/cmd/check.go +- +-The -a (-all) flag causes all fixes, not just preferred ones, to be +-applied, but since no fixes are currently preferred, this flag is +-essentially mandatory. +- +-Arguments after the filename are interpreted as LSP CodeAction kinds +-to be applied; the default set is {"quickfix"}, but valid kinds include: +- +- quickfix +- refactor +- refactor.extract +- refactor.inline +- refactor.rewrite +- source.organizeImports +- source.fixAll +- +-CodeAction kinds are hierarchical, so "refactor" includes +-"refactor.inline". There is currently no way to enable or even +-enumerate all kinds. +- +-Example: apply any "refactor.rewrite" fixes at the specific byte +-offset within this file: +- +- $ gopls fix -a internal/lsp/cmd/check.go:#43 refactor.rewrite - -fix-flags: -`) @@ -27576,7 +31317,8 @@ diff -urN a/gopls/internal/lsp/cmd/suggested_fix.go b/gopls/internal/lsp/cmd/sug - if len(args) < 1 { - return tool.CommandLineErrorf("fix expects at least 1 argument") - } -- conn, err := s.app.connect(ctx) +- s.app.editFlags = &s.EditFlags +- conn, err := s.app.connect(ctx, nil) - if err != nil { - return err - } @@ -27584,17 +31326,25 @@ diff -urN a/gopls/internal/lsp/cmd/suggested_fix.go b/gopls/internal/lsp/cmd/sug - - from := span.Parse(args[0]) - uri := from.URI() -- file := conn.openFile(ctx, uri) -- if file.err != nil { -- return file.err +- file, err := conn.openFile(ctx, uri) +- if err != nil { +- return err +- } +- rng, err := file.mapper.SpanRange(from) +- if err != nil { +- return err - } - +- // Get diagnostics. - if err := conn.diagnoseFiles(ctx, []span.URI{uri}); err != nil { - return err - } -- conn.Client.filesMu.Lock() -- defer conn.Client.filesMu.Unlock() +- diagnostics := []protocol.Diagnostic{} // LSP wants non-nil slice +- conn.client.filesMu.Lock() +- diagnostics = append(diagnostics, file.diagnostics...) +- conn.client.filesMu.Unlock() - +- // Request code actions - codeActionKinds := []protocol.CodeActionKind{protocol.QuickFix} - if len(args) > 1 { - codeActionKinds = []protocol.CodeActionKind{} @@ -27602,18 +31352,13 @@ diff -urN a/gopls/internal/lsp/cmd/suggested_fix.go b/gopls/internal/lsp/cmd/sug - codeActionKinds = append(codeActionKinds, protocol.CodeActionKind(k)) - } - } -- -- rng, err := file.mapper.SpanRange(from) -- if err != nil { -- return err -- } - p := protocol.CodeActionParams{ - TextDocument: protocol.TextDocumentIdentifier{ - URI: protocol.URIFromSpanURI(uri), - }, - Context: protocol.CodeActionContext{ - Only: codeActionKinds, -- Diagnostics: file.diagnostics, +- Diagnostics: diagnostics, - }, - Range: rng, - } @@ -27621,14 +31366,34 @@ diff -urN a/gopls/internal/lsp/cmd/suggested_fix.go b/gopls/internal/lsp/cmd/sug - if err != nil { - return fmt.Errorf("%v: %v", from, err) - } +- +- // Gather edits from matching code actions. - var edits []protocol.TextEdit - for _, a := range actions { -- if a.Command != nil { -- return fmt.Errorf("ExecuteCommand is not yet supported on the command line") -- } +- // Without -all, apply only "preferred" fixes. - if !a.IsPreferred && !s.All { - continue - } +- +- // Execute any command. +- // This may cause the server to make +- // an ApplyEdit downcall to the client. +- if a.Command != nil { +- if _, err := conn.ExecuteCommand(ctx, &protocol.ExecuteCommandParams{ +- Command: a.Command.Command, +- Arguments: a.Command.Arguments, +- }); err != nil { +- return err +- } +- // The specification says that commands should +- // be executed _after_ edits are applied, not +- // instead of them, but we don't want to +- // duplicate edits. +- continue +- } +- +- // Partially apply CodeAction.Edit, a WorkspaceEdit. +- // (See also conn.Client.applyWorkspaceEdit(a.Edit)). - if !from.HasPosition() { - for _, c := range a.Edit.DocumentChanges { - if c.TextDocumentEdit != nil { @@ -27639,14 +31404,11 @@ diff -urN a/gopls/internal/lsp/cmd/suggested_fix.go b/gopls/internal/lsp/cmd/sug - } - continue - } -- // If the span passed in has a position, then we need to find -- // the codeaction that has the same range as the passed in span. +- +- // The provided span has a position (not just offsets). +- // Find the code action that has the same range as it. - for _, diag := range a.Diagnostics { -- spn, err := file.mapper.RangeSpan(diag.Range) -- if err != nil { -- continue -- } -- if span.ComparePoint(from.Start(), spn.Start()) == 0 { +- if diag.Range.Start == rng.Start { - for _, c := range a.Edit.DocumentChanges { - if c.TextDocumentEdit != nil { - if fileURI(c.TextDocumentEdit.TextDocument.URI) == uri { @@ -27670,31 +31432,11 @@ diff -urN a/gopls/internal/lsp/cmd/suggested_fix.go b/gopls/internal/lsp/cmd/sug - } - } - -- newContent, sedits, err := source.ApplyProtocolEdits(file.mapper, edits) -- if err != nil { -- return fmt.Errorf("%v: %v", edits, err) -- } -- -- filename := file.uri.Filename() -- switch { -- case s.Write: -- if len(edits) > 0 { -- ioutil.WriteFile(filename, newContent, 0644) -- } -- case s.Diff: -- diffs, err := diff.ToUnified(filename+".orig", filename, string(file.mapper.Content), sedits) -- if err != nil { -- return err -- } -- fmt.Print(diffs) -- default: -- os.Stdout.Write(newContent) -- } -- return nil +- return applyTextEdits(file.mapper, edits, s.app.editFlags) -} diff -urN a/gopls/internal/lsp/cmd/symbols.go b/gopls/internal/lsp/cmd/symbols.go --- a/gopls/internal/lsp/cmd/symbols.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/symbols.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/symbols.go 1970-01-01 08:00:00 @@ -1,116 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -27735,7 +31477,7 @@ diff -urN a/gopls/internal/lsp/cmd/symbols.go b/gopls/internal/lsp/cmd/symbols.g - return tool.CommandLineErrorf("symbols expects 1 argument (position)") - } - -- conn, err := r.app.connect(ctx) +- conn, err := r.app.connect(ctx, nil) - if err != nil { - return err - } @@ -27812,23 +31554,15 @@ diff -urN a/gopls/internal/lsp/cmd/symbols.go b/gopls/internal/lsp/cmd/symbols.g - r.End.Character+1, - ) -} -diff -urN a/gopls/internal/lsp/cmd/test/cmdtest.go b/gopls/internal/lsp/cmd/test/cmdtest.go ---- a/gopls/internal/lsp/cmd/test/cmdtest.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/test/cmdtest.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --// Copyright 2023 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 cmdtest contains the test suite for the command line behavior of gopls. --package cmdtest diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp/cmd/test/integration_test.go --- a/gopls/internal/lsp/cmd/test/integration_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/test/integration_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,898 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/test/integration_test.go 1970-01-01 08:00:00 +@@ -1,1024 +0,0 @@ -// Copyright 2023 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 cmdtest contains the test suite for the command line behavior of gopls. -package cmdtest - -// This file defines integration tests of each gopls subcommand that @@ -27845,9 +31579,9 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp -// -// TODO(adonovan): -// - Use markers to represent positions in the input and in assertions. --// - Coverage of cross-cutting things like cwd, enviro, span parsing, etc. --// - Subcommands that accept -write and -diff flags should implement --// them consistently wrt the default behavior; factor their tests. +-// - Coverage of cross-cutting things like cwd, environ, span parsing, etc. +-// - Subcommands that accept -write and -diff flags implement them +-// consistently; factor their tests. -// - Add missing test for 'vulncheck' subcommand. -// - Add tests for client-only commands: serve, bug, help, api-json, licenses. - @@ -27856,6 +31590,7 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp - "context" - "encoding/json" - "fmt" +- "math/rand" - "os" - "path/filepath" - "regexp" @@ -27863,11 +31598,11 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp - "testing" - - exec "golang.org/x/sys/execabs" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/hooks" - "golang.org/x/tools/gopls/internal/lsp/cmd" - "golang.org/x/tools/gopls/internal/lsp/debug" - "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/testenv" - "golang.org/x/tools/internal/tool" - "golang.org/x/tools/txtar" @@ -27880,7 +31615,7 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp - tree := writeTree(t, "") - - // There's not much we can robustly assert about the actual version. -- const want = debug.Version // e.g. "master" +- want := debug.Version() // e.g. "master" - - // basic - { @@ -28221,7 +31956,7 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp - res := gopls(t, tree, "imports", "a.go") - res.checkExit(true) - if res.stdout != want { -- t.Errorf("format: got <<%s>>, want <<%s>>", res.stdout, want) +- t.Errorf("imports: got <<%s>>, want <<%s>>", res.stdout, want) - } - } - // -diff: show a unified diff @@ -28506,6 +32241,95 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp - } -} - +-func TestStats(t *testing.T) { +- t.Parallel() +- +- tree := writeTree(t, ` +--- go.mod -- +-module example.com +-go 1.18 +- +--- a.go -- +-package a +--- b/b.go -- +-package b +--- testdata/foo.go -- +-package foo +-`) +- +- // Trigger a bug report with a distinctive string +- // and check that it was durably recorded. +- oops := fmt.Sprintf("oops-%d", rand.Int()) +- { +- env := []string{"TEST_GOPLS_BUG=" + oops} +- res := goplsWithEnv(t, tree, env, "bug") +- res.checkExit(true) +- } +- +- res := gopls(t, tree, "stats") +- res.checkExit(true) +- +- var stats cmd.GoplsStats +- if err := json.Unmarshal([]byte(res.stdout), &stats); err != nil { +- t.Fatalf("failed to unmarshal JSON output of stats command: %v", err) +- } +- +- // a few sanity checks +- checks := []struct { +- field string +- got int +- want int +- }{ +- { +- "WorkspaceStats.Views[0].WorkspaceModules", +- stats.WorkspaceStats.Views[0].WorkspacePackages.Modules, +- 1, +- }, +- { +- "WorkspaceStats.Views[0].WorkspacePackages", +- stats.WorkspaceStats.Views[0].WorkspacePackages.Packages, +- 2, +- }, +- {"DirStats.Files", stats.DirStats.Files, 4}, +- {"DirStats.GoFiles", stats.DirStats.GoFiles, 2}, +- {"DirStats.ModFiles", stats.DirStats.ModFiles, 1}, +- {"DirStats.TestdataFiles", stats.DirStats.TestdataFiles, 1}, +- } +- for _, check := range checks { +- if check.got != check.want { +- t.Errorf("stats.%s = %d, want %d", check.field, check.got, check.want) +- } +- } +- +- // Check that we got a BugReport with the expected message. +- { +- got := fmt.Sprint(stats.BugReports) +- wants := []string{ +- "cmd/info.go", // File containing call to bug.Report +- oops, // Description +- } +- for _, want := range wants { +- if !strings.Contains(got, want) { +- t.Errorf("BugReports does not contain %q. Got:<<%s>>", want, got) +- break +- } +- } +- } +- +- // Check that -anon suppresses fields containing user information. +- { +- res2 := gopls(t, tree, "stats", "-anon") +- res2.checkExit(true) +- var stats2 cmd.GoplsStats +- if err := json.Unmarshal([]byte(res2.stdout), &stats2); err != nil { +- t.Fatalf("failed to unmarshal JSON output of stats command: %v", err) +- } +- if got := len(stats2.BugReports); got > 0 { +- t.Errorf("Got %d bug reports with -anon, want 0. Reports:%+v", got, stats2.BugReports) +- } +- } +-} +- -// TestFix tests the 'fix' subcommand (../suggested_fix.go). -func TestFix(t *testing.T) { - t.Parallel() @@ -28517,16 +32341,15 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp - --- a.go -- -package a --var _ error = T(0) -type T int -func f() (int, string) { return } --`) -- want := ` +- +--- b.go -- -package a --var _ error = T(0) --type T int --func f() (int, string) { return 0, "" } --`[1:] +-import "io" +-var _ io.Reader = C{} +-type C struct{} +-`) - - // no arguments - { @@ -28534,20 +32357,45 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp - res.checkExit(false) - res.checkStderr("expects at least 1 argument") - } -- // success (-a enables fillreturns) +- // success with default kinds, {quickfix}. +- // -a is always required because no fix is currently "preferred" (!) - { - res := gopls(t, tree, "fix", "-a", "a.go") - res.checkExit(true) - got := res.stdout +- want := ` +-package a +-type T int +-func f() (int, string) { return 0, "" } +- +-`[1:] - if got != want { -- t.Errorf("fix: got <<%s>>, want <<%s>>", got, want) +- t.Errorf("fix: got <<%s>>, want <<%s>>\nstderr:\n%s", got, want, res.stderr) +- } +- } +- // success, with explicit CodeAction kind and diagnostics span. +- { +- res := gopls(t, tree, "fix", "-a", "b.go:#40", "quickfix") +- res.checkExit(true) +- got := res.stdout +- want := ` +-package a +- +-import "io" +- +-var _ io.Reader = C{} +- +-type C struct{} +- +-// Read implements io.Reader. +-func (C) Read(p []byte) (n int, err error) { +- panic("unimplemented") +-} +-`[1:] +- if got != want { +- t.Errorf("fix: got <<%s>>, want <<%s>>\nstderr:\n%s", got, want, res.stderr) - } - } -- // TODO(adonovan): more tests: -- // - -write, -diff: factor with imports, format, rename. -- // - without -all flag -- // - args[2:] is an optional list of protocol.CodeActionKind enum values. -- // - a span argument with a range causes filtering. -} - -// TestWorkspaceSymbol tests the 'workspace_symbol' subcommand (../workspace_symbol.go). @@ -28590,7 +32438,12 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp - -// This function is a stand-in for gopls.main in ../../../../main.go. -func goplsMain() { -- bug.PanicOnBugs = true // (not in the production command) +- // Panic on bugs (unlike the production gopls command), +- // except in tests that inject calls to bug.Report. +- if os.Getenv("TEST_GOPLS_BUG") == "" { +- bug.PanicOnBugs = true +- } +- - tool.Main(context.Background(), cmd.New("gopls", "", nil, hooks.Options), os.Args[1:]) -} - @@ -28620,6 +32473,10 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp - -// gopls executes gopls in a child process. -func gopls(t *testing.T, dir string, args ...string) *result { +- return goplsWithEnv(t, dir, nil, args...) +-} +- +-func goplsWithEnv(t *testing.T, dir string, env []string, args ...string) *result { - testenv.NeedsTool(t, "go") - - // Catch inadvertent use of dir=".", which would make @@ -28628,16 +32485,17 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp - t.Fatalf("dir is not absolute: %s", dir) - } - -- cmd := exec.Command(os.Args[0], args...) -- cmd.Env = append(os.Environ(), "ENTRYPOINT=goplsMain") -- cmd.Dir = dir -- cmd.Stdout = new(bytes.Buffer) -- cmd.Stderr = new(bytes.Buffer) +- goplsCmd := exec.Command(os.Args[0], args...) +- goplsCmd.Env = append(os.Environ(), "ENTRYPOINT=goplsMain") +- goplsCmd.Env = append(goplsCmd.Env, env...) +- goplsCmd.Dir = dir +- goplsCmd.Stdout = new(bytes.Buffer) +- goplsCmd.Stderr = new(bytes.Buffer) - -- cmdErr := cmd.Run() +- cmdErr := goplsCmd.Run() - -- stdout := strings.ReplaceAll(fmt.Sprint(cmd.Stdout), dir, ".") -- stderr := strings.ReplaceAll(fmt.Sprint(cmd.Stderr), dir, ".") +- stdout := strings.ReplaceAll(fmt.Sprint(goplsCmd.Stdout), dir, ".") +- stderr := strings.ReplaceAll(fmt.Sprint(goplsCmd.Stderr), dir, ".") - exitcode := 0 - if cmdErr != nil { - if exitErr, ok := cmdErr.(*exec.ExitError); ok { @@ -28726,7 +32584,7 @@ diff -urN a/gopls/internal/lsp/cmd/test/integration_test.go b/gopls/internal/lsp -} diff -urN a/gopls/internal/lsp/cmd/usage/api-json.hlp b/gopls/internal/lsp/cmd/usage/api-json.hlp --- a/gopls/internal/lsp/cmd/usage/api-json.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/api-json.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/api-json.hlp 1970-01-01 08:00:00 @@ -1,4 +0,0 @@ -print json describing gopls API - @@ -28734,7 +32592,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/api-json.hlp b/gopls/internal/lsp/cmd/u - gopls [flags] api-json diff -urN a/gopls/internal/lsp/cmd/usage/bug.hlp b/gopls/internal/lsp/cmd/usage/bug.hlp --- a/gopls/internal/lsp/cmd/usage/bug.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/bug.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/bug.hlp 1970-01-01 08:00:00 @@ -1,4 +0,0 @@ -report a bug in gopls - @@ -28742,7 +32600,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/bug.hlp b/gopls/internal/lsp/cmd/usage/ - gopls [flags] bug diff -urN a/gopls/internal/lsp/cmd/usage/call_hierarchy.hlp b/gopls/internal/lsp/cmd/usage/call_hierarchy.hlp --- a/gopls/internal/lsp/cmd/usage/call_hierarchy.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/call_hierarchy.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/call_hierarchy.hlp 1970-01-01 08:00:00 @@ -1,10 +0,0 @@ -display selected identifier's call hierarchy - @@ -28756,7 +32614,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/call_hierarchy.hlp b/gopls/internal/lsp - $ gopls call_hierarchy helper/helper.go:#53 diff -urN a/gopls/internal/lsp/cmd/usage/check.hlp b/gopls/internal/lsp/cmd/usage/check.hlp --- a/gopls/internal/lsp/cmd/usage/check.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/check.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/check.hlp 1970-01-01 08:00:00 @@ -1,8 +0,0 @@ -show diagnostic results for the specified file - @@ -28768,7 +32626,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/check.hlp b/gopls/internal/lsp/cmd/usag - $ gopls check internal/lsp/cmd/check.go diff -urN a/gopls/internal/lsp/cmd/usage/definition.hlp b/gopls/internal/lsp/cmd/usage/definition.hlp --- a/gopls/internal/lsp/cmd/usage/definition.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/definition.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/definition.hlp 1970-01-01 08:00:00 @@ -1,15 +0,0 @@ -show declaration of selected identifier - @@ -28787,26 +32645,55 @@ diff -urN a/gopls/internal/lsp/cmd/usage/definition.hlp b/gopls/internal/lsp/cmd - support markdown in responses diff -urN a/gopls/internal/lsp/cmd/usage/fix.hlp b/gopls/internal/lsp/cmd/usage/fix.hlp --- a/gopls/internal/lsp/cmd/usage/fix.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/fix.hlp 1970-01-01 00:00:00.000000000 +0000 -@@ -1,15 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/usage/fix.hlp 1970-01-01 08:00:00 +@@ -1,44 +0,0 @@ -apply suggested fixes - -Usage: - gopls [flags] fix [fix-flags] - --Example: apply suggested fixes for this file -- $ gopls fix -w internal/lsp/cmd/check.go +-Example: apply fixes to this file, rewriting it: +- +- $ gopls fix -a -w internal/lsp/cmd/check.go +- +-The -a (-all) flag causes all fixes, not just preferred ones, to be +-applied, but since no fixes are currently preferred, this flag is +-essentially mandatory. +- +-Arguments after the filename are interpreted as LSP CodeAction kinds +-to be applied; the default set is {"quickfix"}, but valid kinds include: +- +- quickfix +- refactor +- refactor.extract +- refactor.inline +- refactor.rewrite +- source.organizeImports +- source.fixAll +- +-CodeAction kinds are hierarchical, so "refactor" includes +-"refactor.inline". There is currently no way to enable or even +-enumerate all kinds. +- +-Example: apply any "refactor.rewrite" fixes at the specific byte +-offset within this file: +- +- $ gopls fix -a internal/lsp/cmd/check.go:#43 refactor.rewrite - -fix-flags: - -a,-all - apply all fixes, not just preferred fixes - -d,-diff -- display diffs instead of rewriting files +- display diffs instead of edited file content +- -l,-list +- display names of edited files +- -preserve +- with -write, make copies of original files - -w,-write -- write result to (source) file instead of stdout +- write edited content to source files diff -urN a/gopls/internal/lsp/cmd/usage/folding_ranges.hlp b/gopls/internal/lsp/cmd/usage/folding_ranges.hlp --- a/gopls/internal/lsp/cmd/usage/folding_ranges.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/folding_ranges.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/folding_ranges.hlp 1970-01-01 08:00:00 @@ -1,8 +0,0 @@ -display selected file's folding ranges - @@ -28818,8 +32705,8 @@ diff -urN a/gopls/internal/lsp/cmd/usage/folding_ranges.hlp b/gopls/internal/lsp - $ gopls folding_ranges helper/helper.go diff -urN a/gopls/internal/lsp/cmd/usage/format.hlp b/gopls/internal/lsp/cmd/usage/format.hlp --- a/gopls/internal/lsp/cmd/usage/format.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/format.hlp 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/usage/format.hlp 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ -format the code according to the go standard - -Usage: @@ -28833,14 +32720,16 @@ diff -urN a/gopls/internal/lsp/cmd/usage/format.hlp b/gopls/internal/lsp/cmd/usa - -format-flags: - -d,-diff -- display diffs instead of rewriting files +- display diffs instead of edited file content - -l,-list -- list files whose formatting differs from gofmt's +- display names of edited files +- -preserve +- with -write, make copies of original files - -w,-write -- write result to (source) file instead of stdout +- write edited content to source files diff -urN a/gopls/internal/lsp/cmd/usage/help.hlp b/gopls/internal/lsp/cmd/usage/help.hlp --- a/gopls/internal/lsp/cmd/usage/help.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/help.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/help.hlp 1970-01-01 08:00:00 @@ -1,10 +0,0 @@ -print usage information for subcommands - @@ -28854,7 +32743,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/help.hlp b/gopls/internal/lsp/cmd/usage -$ gopls help remote sessions # help on 'remote sessions' subcommand diff -urN a/gopls/internal/lsp/cmd/usage/highlight.hlp b/gopls/internal/lsp/cmd/usage/highlight.hlp --- a/gopls/internal/lsp/cmd/usage/highlight.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/highlight.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/highlight.hlp 1970-01-01 08:00:00 @@ -1,10 +0,0 @@ -display selected identifier's highlights - @@ -28868,7 +32757,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/highlight.hlp b/gopls/internal/lsp/cmd/ - $ gopls highlight helper/helper.go:#53 diff -urN a/gopls/internal/lsp/cmd/usage/implementation.hlp b/gopls/internal/lsp/cmd/usage/implementation.hlp --- a/gopls/internal/lsp/cmd/usage/implementation.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/implementation.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/implementation.hlp 1970-01-01 08:00:00 @@ -1,10 +0,0 @@ -display selected identifier's implementation - @@ -28882,8 +32771,8 @@ diff -urN a/gopls/internal/lsp/cmd/usage/implementation.hlp b/gopls/internal/lsp - $ gopls implementation helper/helper.go:#53 diff -urN a/gopls/internal/lsp/cmd/usage/imports.hlp b/gopls/internal/lsp/cmd/usage/imports.hlp --- a/gopls/internal/lsp/cmd/usage/imports.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/imports.hlp 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/usage/imports.hlp 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ -updates import statements - -Usage: @@ -28895,12 +32784,16 @@ diff -urN a/gopls/internal/lsp/cmd/usage/imports.hlp b/gopls/internal/lsp/cmd/us - -imports-flags: - -d,-diff -- display diffs instead of rewriting files +- display diffs instead of edited file content +- -l,-list +- display names of edited files +- -preserve +- with -write, make copies of original files - -w,-write -- write result to (source) file instead of stdout +- write edited content to source files diff -urN a/gopls/internal/lsp/cmd/usage/inspect.hlp b/gopls/internal/lsp/cmd/usage/inspect.hlp --- a/gopls/internal/lsp/cmd/usage/inspect.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/inspect.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/inspect.hlp 1970-01-01 08:00:00 @@ -1,8 +0,0 @@ -interact with the gopls daemon (deprecated: use 'remote') - @@ -28912,7 +32805,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/inspect.hlp b/gopls/internal/lsp/cmd/us - debug start the debug server diff -urN a/gopls/internal/lsp/cmd/usage/licenses.hlp b/gopls/internal/lsp/cmd/usage/licenses.hlp --- a/gopls/internal/lsp/cmd/usage/licenses.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/licenses.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/licenses.hlp 1970-01-01 08:00:00 @@ -1,4 +0,0 @@ -print licenses of included software - @@ -28920,7 +32813,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/licenses.hlp b/gopls/internal/lsp/cmd/u - gopls [flags] licenses diff -urN a/gopls/internal/lsp/cmd/usage/links.hlp b/gopls/internal/lsp/cmd/usage/links.hlp --- a/gopls/internal/lsp/cmd/usage/links.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/links.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/links.hlp 1970-01-01 08:00:00 @@ -1,12 +0,0 @@ -list links in a file - @@ -28936,7 +32829,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/links.hlp b/gopls/internal/lsp/cmd/usag - emit document links in JSON format diff -urN a/gopls/internal/lsp/cmd/usage/prepare_rename.hlp b/gopls/internal/lsp/cmd/usage/prepare_rename.hlp --- a/gopls/internal/lsp/cmd/usage/prepare_rename.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/prepare_rename.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/prepare_rename.hlp 1970-01-01 08:00:00 @@ -1,10 +0,0 @@ -test validity of a rename operation at location - @@ -28950,7 +32843,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/prepare_rename.hlp b/gopls/internal/lsp - $ gopls prepare_rename helper/helper.go:#53 diff -urN a/gopls/internal/lsp/cmd/usage/references.hlp b/gopls/internal/lsp/cmd/usage/references.hlp --- a/gopls/internal/lsp/cmd/usage/references.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/references.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/references.hlp 1970-01-01 08:00:00 @@ -1,14 +0,0 @@ -display selected identifier's references - @@ -28968,7 +32861,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/references.hlp b/gopls/internal/lsp/cmd - include the declaration of the specified identifier in the results diff -urN a/gopls/internal/lsp/cmd/usage/remote.hlp b/gopls/internal/lsp/cmd/usage/remote.hlp --- a/gopls/internal/lsp/cmd/usage/remote.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/remote.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/remote.hlp 1970-01-01 08:00:00 @@ -1,8 +0,0 @@ -interact with the gopls daemon - @@ -28980,8 +32873,8 @@ diff -urN a/gopls/internal/lsp/cmd/usage/remote.hlp b/gopls/internal/lsp/cmd/usa - debug start the debug server diff -urN a/gopls/internal/lsp/cmd/usage/rename.hlp b/gopls/internal/lsp/cmd/usage/rename.hlp --- a/gopls/internal/lsp/cmd/usage/rename.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/rename.hlp 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/usage/rename.hlp 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ -rename selected identifier - -Usage: @@ -28995,14 +32888,16 @@ diff -urN a/gopls/internal/lsp/cmd/usage/rename.hlp b/gopls/internal/lsp/cmd/usa - -rename-flags: - -d,-diff -- display diffs instead of rewriting files +- display diffs instead of edited file content +- -l,-list +- display names of edited files - -preserve -- preserve original files +- with -write, make copies of original files - -w,-write -- write result to (source) file instead of stdout +- write edited content to source files diff -urN a/gopls/internal/lsp/cmd/usage/semtok.hlp b/gopls/internal/lsp/cmd/usage/semtok.hlp --- a/gopls/internal/lsp/cmd/usage/semtok.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/semtok.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/semtok.hlp 1970-01-01 08:00:00 @@ -1,8 +0,0 @@ -show semantic tokens for the specified file - @@ -29014,7 +32909,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/semtok.hlp b/gopls/internal/lsp/cmd/usa - $ gopls semtok internal/lsp/cmd/semtok.go diff -urN a/gopls/internal/lsp/cmd/usage/serve.hlp b/gopls/internal/lsp/cmd/usage/serve.hlp --- a/gopls/internal/lsp/cmd/usage/serve.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/serve.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/serve.hlp 1970-01-01 08:00:00 @@ -1,30 +0,0 @@ -run a server for Go code using the Language Server Protocol - @@ -29048,7 +32943,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/serve.hlp b/gopls/internal/lsp/cmd/usag - print the full rpc trace in lsp inspector format diff -urN a/gopls/internal/lsp/cmd/usage/signature.hlp b/gopls/internal/lsp/cmd/usage/signature.hlp --- a/gopls/internal/lsp/cmd/usage/signature.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/signature.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/signature.hlp 1970-01-01 08:00:00 @@ -1,10 +0,0 @@ -display selected identifier's signature - @@ -29060,9 +32955,30 @@ diff -urN a/gopls/internal/lsp/cmd/usage/signature.hlp b/gopls/internal/lsp/cmd/ - $ # 1-indexed location (:line:column or :#offset) of the target identifier - $ gopls signature helper/helper.go:8:6 - $ gopls signature helper/helper.go:#53 +diff -urN a/gopls/internal/lsp/cmd/usage/stats.hlp b/gopls/internal/lsp/cmd/usage/stats.hlp +--- a/gopls/internal/lsp/cmd/usage/stats.hlp 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cmd/usage/stats.hlp 1970-01-01 08:00:00 +@@ -1,17 +0,0 @@ +-print workspace statistics +- +-Usage: +- gopls [flags] stats +- +-Load the workspace for the current directory, and output a JSON summary of +-workspace information relevant to performance. As a side effect, this command +-populates the gopls file cache for the current workspace. +- +-By default, this command may include output that refers to the location or +-content of user code. When the -anon flag is set, fields that may refer to user +-code are hidden. +- +-Example: +- $ gopls stats -anon +- -anon +- hide any fields that may contain user names, file names, or source code diff -urN a/gopls/internal/lsp/cmd/usage/symbols.hlp b/gopls/internal/lsp/cmd/usage/symbols.hlp --- a/gopls/internal/lsp/cmd/usage/symbols.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/symbols.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/symbols.hlp 1970-01-01 08:00:00 @@ -1,7 +0,0 @@ -display selected file's symbols - @@ -29071,10 +32987,96 @@ diff -urN a/gopls/internal/lsp/cmd/usage/symbols.hlp b/gopls/internal/lsp/cmd/us - -Example: - $ gopls symbols helper/helper.go +diff -urN a/gopls/internal/lsp/cmd/usage/usage-v.hlp b/gopls/internal/lsp/cmd/usage/usage-v.hlp +--- a/gopls/internal/lsp/cmd/usage/usage-v.hlp 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/cmd/usage/usage-v.hlp 1970-01-01 08:00:00 +@@ -1,82 +0,0 @@ +- +-gopls is a Go language server. +- +-It is typically used with an editor to provide language features. When no +-command is specified, gopls will default to the 'serve' command. The language +-features can also be accessed via the gopls command-line interface. +- +-Usage: +- gopls help [] +- +-Command: +- +-Main +- serve run a server for Go code using the Language Server Protocol +- version print the gopls version information +- bug report a bug in gopls +- help print usage information for subcommands +- api-json print json describing gopls API +- licenses print licenses of included software +- +-Features +- call_hierarchy display selected identifier's call hierarchy +- check show diagnostic results for the specified file +- definition show declaration of selected identifier +- folding_ranges display selected file's folding ranges +- format format the code according to the go standard +- highlight display selected identifier's highlights +- implementation display selected identifier's implementation +- imports updates import statements +- remote interact with the gopls daemon +- inspect interact with the gopls daemon (deprecated: use 'remote') +- links list links in a file +- prepare_rename test validity of a rename operation at location +- references display selected identifier's references +- rename rename selected identifier +- semtok show semantic tokens for the specified file +- signature display selected identifier's signature +- stats print workspace statistics +- fix apply suggested fixes +- symbols display selected file's symbols +- workspace_symbol search symbols in workspace +- +-Internal Use Only +- vulncheck run vulncheck analysis (internal-use only) +- +-flags: +- -debug=string +- serve debug information on the supplied address +- -listen=string +- address on which to listen for remote connections. If prefixed by 'unix;', the subsequent address is assumed to be a unix domain socket. Otherwise, TCP is used. +- -listen.timeout=duration +- when used with -listen, shut down the server when there are no connected clients for this duration +- -logfile=string +- filename to log to. if value is "auto", then logging to a default output file is enabled +- -mode=string +- no effect +- -ocagent=string +- the address of the ocagent (e.g. http://localhost:55678), or off (default "off") +- -port=int +- port on which to run gopls for debugging purposes +- -profile.alloc=string +- write alloc profile to this file +- -profile.cpu=string +- write CPU profile to this file +- -profile.mem=string +- write memory profile to this file +- -profile.trace=string +- write trace log to this file +- -remote=string +- forward all commands to a remote lsp specified by this flag. With no special prefix, this is assumed to be a TCP address. If prefixed by 'unix;', the subsequent address is assumed to be a unix domain socket. If 'auto', or prefixed by 'auto;', the remote address is automatically resolved based on the executing environment. +- -remote.debug=string +- when used with -remote=auto, the -debug value used to start the daemon +- -remote.listen.timeout=duration +- when used with -remote=auto, the -listen.timeout value used to start the daemon (default 1m0s) +- -remote.logfile=string +- when used with -remote=auto, the -logfile value used to start the daemon +- -rpc.trace +- print the full rpc trace in lsp inspector format +- -v,-verbose +- verbose output +- -vv,-veryverbose +- very verbose output diff -urN a/gopls/internal/lsp/cmd/usage/usage.hlp b/gopls/internal/lsp/cmd/usage/usage.hlp --- a/gopls/internal/lsp/cmd/usage/usage.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/usage.hlp 1970-01-01 00:00:00.000000000 +0000 -@@ -1,77 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/usage/usage.hlp 1970-01-01 08:00:00 +@@ -1,79 +0,0 @@ - -gopls is a Go language server. - @@ -29112,10 +33114,10 @@ diff -urN a/gopls/internal/lsp/cmd/usage/usage.hlp b/gopls/internal/lsp/cmd/usag - rename rename selected identifier - semtok show semantic tokens for the specified file - signature display selected identifier's signature +- stats print workspace statistics - fix apply suggested fixes - symbols display selected file's symbols - workspace_symbol search symbols in workspace -- vulncheck run experimental vulncheck analysis (experimental: under development) - -flags: - -debug=string @@ -29132,6 +33134,8 @@ diff -urN a/gopls/internal/lsp/cmd/usage/usage.hlp b/gopls/internal/lsp/cmd/usag - the address of the ocagent (e.g. http://localhost:55678), or off (default "off") - -port=int - port on which to run gopls for debugging purposes +- -profile.alloc=string +- write alloc profile to this file - -profile.cpu=string - write CPU profile to this file - -profile.mem=string @@ -29154,7 +33158,7 @@ diff -urN a/gopls/internal/lsp/cmd/usage/usage.hlp b/gopls/internal/lsp/cmd/usag - very verbose output diff -urN a/gopls/internal/lsp/cmd/usage/version.hlp b/gopls/internal/lsp/cmd/usage/version.hlp --- a/gopls/internal/lsp/cmd/usage/version.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/version.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/version.hlp 1970-01-01 08:00:00 @@ -1,6 +0,0 @@ -print the gopls version information - @@ -29164,14 +33168,14 @@ diff -urN a/gopls/internal/lsp/cmd/usage/version.hlp b/gopls/internal/lsp/cmd/us - outputs in json format. diff -urN a/gopls/internal/lsp/cmd/usage/vulncheck.hlp b/gopls/internal/lsp/cmd/usage/vulncheck.hlp --- a/gopls/internal/lsp/cmd/usage/vulncheck.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/vulncheck.hlp 1970-01-01 00:00:00.000000000 +0000 -@@ -1,17 +0,0 @@ --run experimental vulncheck analysis (experimental: under development) ++++ b/gopls/internal/lsp/cmd/usage/vulncheck.hlp 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-run vulncheck analysis (internal-use only) - -Usage: - gopls [flags] vulncheck - -- WARNING: this command is experimental. +- WARNING: this command is for internal-use only. - - By default, the command outputs a JSON-encoded - golang.org/x/tools/gopls/internal/lsp/command.VulncheckResult @@ -29179,13 +33183,9 @@ diff -urN a/gopls/internal/lsp/cmd/usage/vulncheck.hlp b/gopls/internal/lsp/cmd/ - Example: - $ gopls vulncheck - -- -config -- If true, the command reads a JSON-encoded package load configuration from stdin -- -summary -- If true, outputs a JSON-encoded govulnchecklib.Summary JSON diff -urN a/gopls/internal/lsp/cmd/usage/workspace_symbol.hlp b/gopls/internal/lsp/cmd/usage/workspace_symbol.hlp --- a/gopls/internal/lsp/cmd/usage/workspace_symbol.hlp 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/usage/workspace_symbol.hlp 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/cmd/usage/workspace_symbol.hlp 1970-01-01 08:00:00 @@ -1,13 +0,0 @@ -search symbols in workspace - @@ -29198,12 +33198,12 @@ diff -urN a/gopls/internal/lsp/cmd/usage/workspace_symbol.hlp b/gopls/internal/l - -workspace_symbol-flags: - -matcher=string -- specifies the type of matcher: fuzzy, caseSensitive, or caseInsensitive. -- The default is caseInsensitive. +- specifies the type of matcher: fuzzy, fastfuzzy, casesensitive, or caseinsensitive. +- The default is caseinsensitive. diff -urN a/gopls/internal/lsp/cmd/vulncheck.go b/gopls/internal/lsp/cmd/vulncheck.go --- a/gopls/internal/lsp/cmd/vulncheck.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/vulncheck.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,84 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/vulncheck.go 1970-01-01 08:00:00 +@@ -1,47 +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. @@ -29212,43 +33212,28 @@ diff -urN a/gopls/internal/lsp/cmd/vulncheck.go b/gopls/internal/lsp/cmd/vulnche - -import ( - "context" -- "encoding/json" - "flag" - "fmt" - "os" - -- "golang.org/x/tools/go/packages" -- vulnchecklib "golang.org/x/tools/gopls/internal/vulncheck" -- "golang.org/x/tools/internal/tool" +- "golang.org/x/tools/gopls/internal/vulncheck/scan" -) - -// vulncheck implements the vulncheck command. +-// TODO(hakim): hide from the public. -type vulncheck struct { -- Config bool `flag:"config" help:"If true, the command reads a JSON-encoded package load configuration from stdin"` -- AsSummary bool `flag:"summary" help:"If true, outputs a JSON-encoded govulnchecklib.Summary JSON"` -- app *Application --} -- --type pkgLoadConfig struct { -- // BuildFlags is a list of command-line flags to be passed through to -- // the build system's query tool. -- BuildFlags []string -- -- // If Tests is set, the loader includes related test packages. -- Tests bool +- app *Application -} - --// TODO(hyangah): document pkgLoadConfig -- -func (v *vulncheck) Name() string { return "vulncheck" } -func (v *vulncheck) Parent() string { return v.app.Name() } -func (v *vulncheck) Usage() string { return "" } -func (v *vulncheck) ShortHelp() string { -- return "run experimental vulncheck analysis (experimental: under development)" +- return "run vulncheck analysis (internal-use only)" -} -func (v *vulncheck) DetailedHelp(f *flag.FlagSet) { - fmt.Fprint(f.Output(), ` -- WARNING: this command is experimental. +- WARNING: this command is for internal-use only. - - By default, the command outputs a JSON-encoded - golang.org/x/tools/gopls/internal/lsp/command.VulncheckResult @@ -29257,32 +33242,10 @@ diff -urN a/gopls/internal/lsp/cmd/vulncheck.go b/gopls/internal/lsp/cmd/vulnche - $ gopls vulncheck - -`) -- printFlagDefaults(f) -} - -func (v *vulncheck) Run(ctx context.Context, args ...string) error { -- if vulnchecklib.Main == nil { -- return fmt.Errorf("vulncheck command is available only in gopls compiled with go1.18 or newer") -- } -- -- // TODO(hyangah): what's wrong with allowing multiple targets? -- if len(args) > 1 { -- return tool.CommandLineErrorf("vulncheck accepts at most one package pattern") -- } -- var cfg pkgLoadConfig -- if v.Config { -- if err := json.NewDecoder(os.Stdin).Decode(&cfg); err != nil { -- return tool.CommandLineErrorf("failed to parse cfg: %v", err) -- } -- } -- loadCfg := packages.Config{ -- Context: ctx, -- Tests: cfg.Tests, -- BuildFlags: cfg.BuildFlags, -- // inherit the current process's cwd and env. -- } -- -- if err := vulnchecklib.Main(loadCfg, args...); err != nil { +- if err := scan.Main(ctx, args...); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } @@ -29290,8 +33253,8 @@ diff -urN a/gopls/internal/lsp/cmd/vulncheck.go b/gopls/internal/lsp/cmd/vulnche -} diff -urN a/gopls/internal/lsp/cmd/workspace_symbol.go b/gopls/internal/lsp/cmd/workspace_symbol.go --- a/gopls/internal/lsp/cmd/workspace_symbol.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/cmd/workspace_symbol.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,85 +0,0 @@ ++++ b/gopls/internal/lsp/cmd/workspace_symbol.go 1970-01-01 08:00:00 +@@ -1,89 +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. @@ -29302,6 +33265,7 @@ diff -urN a/gopls/internal/lsp/cmd/workspace_symbol.go b/gopls/internal/lsp/cmd/ - "context" - "flag" - "fmt" +- "strings" - - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" @@ -29310,7 +33274,7 @@ diff -urN a/gopls/internal/lsp/cmd/workspace_symbol.go b/gopls/internal/lsp/cmd/ - -// workspaceSymbol implements the workspace_symbol verb for gopls. -type workspaceSymbol struct { -- Matcher string `flag:"matcher" help:"specifies the type of matcher: fuzzy, caseSensitive, or caseInsensitive.\nThe default is caseInsensitive."` +- Matcher string `flag:"matcher" help:"specifies the type of matcher: fuzzy, fastfuzzy, casesensitive, or caseinsensitive.\nThe default is caseinsensitive."` - - app *Application -} @@ -29340,10 +33304,10 @@ diff -urN a/gopls/internal/lsp/cmd/workspace_symbol.go b/gopls/internal/lsp/cmd/ - if opts != nil { - opts(o) - } -- switch r.Matcher { +- switch strings.ToLower(r.Matcher) { - case "fuzzy": - o.SymbolMatcher = source.SymbolFuzzy -- case "caseSensitive": +- case "casesensitive": - o.SymbolMatcher = source.SymbolCaseSensitive - case "fastfuzzy": - o.SymbolMatcher = source.SymbolFastFuzzy @@ -29352,7 +33316,7 @@ diff -urN a/gopls/internal/lsp/cmd/workspace_symbol.go b/gopls/internal/lsp/cmd/ - } - } - -- conn, err := r.app.connect(ctx) +- conn, err := r.app.connect(ctx, nil) - if err != nil { - return err - } @@ -29367,7 +33331,10 @@ diff -urN a/gopls/internal/lsp/cmd/workspace_symbol.go b/gopls/internal/lsp/cmd/ - return err - } - for _, s := range symbols { -- f := conn.openFile(ctx, fileURI(s.Location.URI)) +- f, err := conn.openFile(ctx, fileURI(s.Location.URI)) +- if err != nil { +- return err +- } - span, err := f.mapper.LocationSpan(s.Location) - if err != nil { - return err @@ -29379,8 +33346,8 @@ diff -urN a/gopls/internal/lsp/cmd/workspace_symbol.go b/gopls/internal/lsp/cmd/ -} diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.go --- a/gopls/internal/lsp/code_action.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/code_action.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,481 +0,0 @@ ++++ b/gopls/internal/lsp/code_action.go 1970-01-01 08:00:00 +@@ -1,655 +0,0 @@ -// Copyright 2018 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. @@ -29390,9 +33357,15 @@ diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.g -import ( - "context" - "fmt" +- "go/ast" - "sort" - "strings" - +- "golang.org/x/tools/go/ast/inspector" +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/analysis/fillstruct" +- "golang.org/x/tools/gopls/internal/lsp/analysis/infertypeargs" +- "golang.org/x/tools/gopls/internal/lsp/analysis/stubmethods" - "golang.org/x/tools/gopls/internal/lsp/command" - "golang.org/x/tools/gopls/internal/lsp/mod" - "golang.org/x/tools/gopls/internal/lsp/protocol" @@ -29404,6 +33377,9 @@ diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.g -) - -func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) { +- ctx, done := event.Start(ctx, "lsp.Server.codeAction") +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) - defer release() - if !ok { @@ -29412,224 +33388,268 @@ diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.g - uri := fh.URI() - - // Determine the supported actions for this file kind. -- kind := snapshot.View().FileKind(fh) -- supportedCodeActions, ok := snapshot.View().Options().SupportedCodeActions[kind] +- kind := snapshot.FileKind(fh) +- supportedCodeActions, ok := snapshot.Options().SupportedCodeActions[kind] - if !ok { - return nil, fmt.Errorf("no supported code actions for %v file kind", kind) - } +- if len(supportedCodeActions) == 0 { +- return nil, nil // not an error if there are none supported +- } - - // The Only field of the context specifies which code actions the client wants. - // If Only is empty, assume that the client wants all of the non-explicit code actions. -- var wanted map[protocol.CodeActionKind]bool -- -- // Explicit Code Actions are opt-in and shouldn't be returned to the client unless -- // requested using Only. -- // TODO: Add other CodeLenses such as GoGenerate, RegenerateCgo, etc.. -- explicit := map[protocol.CodeActionKind]bool{ -- protocol.GoTest: true, -- } +- var want map[protocol.CodeActionKind]bool +- { +- // Explicit Code Actions are opt-in and shouldn't be returned to the client unless +- // requested using Only. +- // TODO: Add other CodeLenses such as GoGenerate, RegenerateCgo, etc.. +- explicit := map[protocol.CodeActionKind]bool{ +- protocol.GoTest: true, +- } - -- if len(params.Context.Only) == 0 { -- wanted = supportedCodeActions -- } else { -- wanted = make(map[protocol.CodeActionKind]bool) -- for _, only := range params.Context.Only { -- for k, v := range supportedCodeActions { -- if only == k || strings.HasPrefix(string(k), string(only)+".") { -- wanted[k] = wanted[k] || v +- if len(params.Context.Only) == 0 { +- want = supportedCodeActions +- } else { +- want = make(map[protocol.CodeActionKind]bool) +- for _, only := range params.Context.Only { +- for k, v := range supportedCodeActions { +- if only == k || strings.HasPrefix(string(k), string(only)+".") { +- want[k] = want[k] || v +- } - } +- want[only] = want[only] || explicit[only] - } -- wanted[only] = wanted[only] || explicit[only] - } - } -- if len(supportedCodeActions) == 0 { -- return nil, nil // not an error if there are none supported -- } -- if len(wanted) == 0 { +- if len(want) == 0 { - return nil, fmt.Errorf("no supported code action to execute for %s, wanted %v", uri, params.Context.Only) - } - -- var codeActions []protocol.CodeAction - switch kind { - case source.Mod: -- if diagnostics := params.Context.Diagnostics; len(diagnostics) > 0 { -- diags, err := mod.ModDiagnostics(ctx, snapshot, fh) -- if source.IsNonFatalGoModError(err) { -- return nil, nil -- } -- if err != nil { -- return nil, err -- } -- udiags, err := mod.ModUpgradeDiagnostics(ctx, snapshot, fh) -- if err != nil { -- return nil, err -- } -- quickFixes, err := codeActionsMatchingDiagnostics(ctx, snapshot, diagnostics, append(diags, udiags...)) -- if err != nil { -- return nil, err -- } -- codeActions = append(codeActions, quickFixes...) +- var actions []protocol.CodeAction - -- vdiags, err := mod.ModVulnerabilityDiagnostics(ctx, snapshot, fh) -- if err != nil { -- return nil, err -- } -- // Group vulnerabilities by location and then limit which code actions we return -- // for each location. -- m := make(map[protocol.Range][]*source.Diagnostic) -- for _, v := range vdiags { -- m[v.Range] = append(m[v.Range], v) -- } -- for _, sdiags := range m { -- quickFixes, err = codeActionsMatchingDiagnostics(ctx, snapshot, diagnostics, sdiags) -- if err != nil { -- return nil, err +- fixes, err := s.codeActionsMatchingDiagnostics(ctx, fh.URI(), snapshot, params.Context.Diagnostics, want) +- if err != nil { +- return nil, err +- } +- +- // Group vulnerability fixes by their range, and select only the most +- // appropriate upgrades. +- // +- // TODO(rfindley): can this instead be accomplished on the diagnosis side, +- // so that code action handling remains uniform? +- vulnFixes := make(map[protocol.Range][]protocol.CodeAction) +- searchFixes: +- for _, fix := range fixes { +- for _, diag := range fix.Diagnostics { +- if diag.Source == string(source.Govulncheck) || diag.Source == string(source.Vulncheck) { +- vulnFixes[diag.Range] = append(vulnFixes[diag.Range], fix) +- continue searchFixes - } -- quickFixes = mod.SelectUpgradeCodeActions(quickFixes) -- codeActions = append(codeActions, quickFixes...) - } +- actions = append(actions, fix) +- } +- +- for _, fixes := range vulnFixes { +- fixes = mod.SelectUpgradeCodeActions(fixes) +- actions = append(actions, fixes...) - } +- +- return actions, nil +- - case source.Go: +- diagnostics := params.Context.Diagnostics +- - // Don't suggest fixes for generated files, since they are generally - // not useful and some editors may apply them automatically on save. - if source.IsGenerated(ctx, snapshot, uri) { - return nil, nil - } -- diagnostics := params.Context.Diagnostics - -- // First, process any missing imports and pair them with the -- // diagnostics they fix. -- if wantQuickFixes := wanted[protocol.QuickFix] && len(diagnostics) > 0; wantQuickFixes || wanted[protocol.SourceOrganizeImports] { -- importEdits, importEditsPerFix, err := source.AllImportsFixes(ctx, snapshot, fh) +- actions, err := s.codeActionsMatchingDiagnostics(ctx, uri, snapshot, diagnostics, want) +- if err != nil { +- return nil, err +- } +- +- // Only compute quick fixes if there are any diagnostics to fix. +- wantQuickFixes := want[protocol.QuickFix] && len(diagnostics) > 0 +- +- // Code actions requiring syntax information alone. +- if wantQuickFixes || want[protocol.SourceOrganizeImports] || want[protocol.RefactorExtract] { +- pgf, err := snapshot.ParseGo(ctx, fh, source.ParseFull) - if err != nil { -- event.Error(ctx, "imports fixes", err, tag.File.Of(fh.URI().Filename())) -- } -- // Separate this into a set of codeActions per diagnostic, where -- // each action is the addition, removal, or renaming of one import. -- if wantQuickFixes { -- for _, importFix := range importEditsPerFix { -- fixes := importDiagnostics(importFix.Fix, diagnostics) -- if len(fixes) == 0 { -- continue +- return nil, err +- } +- +- // Process any missing imports and pair them with the diagnostics they +- // fix. +- if wantQuickFixes || want[protocol.SourceOrganizeImports] { +- importEdits, importEditsPerFix, err := source.AllImportsFixes(ctx, snapshot, pgf) +- if err != nil { +- event.Error(ctx, "imports fixes", err, tag.File.Of(fh.URI().Filename())) +- importEdits = nil +- importEditsPerFix = nil +- } +- +- // Separate this into a set of codeActions per diagnostic, where +- // each action is the addition, removal, or renaming of one import. +- if wantQuickFixes { +- for _, importFix := range importEditsPerFix { +- fixed := fixedByImportFix(importFix.Fix, diagnostics) +- if len(fixed) == 0 { +- continue +- } +- actions = append(actions, protocol.CodeAction{ +- Title: importFixTitle(importFix.Fix), +- Kind: protocol.QuickFix, +- Edit: &protocol.WorkspaceEdit{ +- DocumentChanges: documentChanges(fh, importFix.Edits), +- }, +- Diagnostics: fixed, +- }) - } -- codeActions = append(codeActions, protocol.CodeAction{ -- Title: importFixTitle(importFix.Fix), -- Kind: protocol.QuickFix, +- } +- +- // Send all of the import edits as one code action if the file is +- // being organized. +- if want[protocol.SourceOrganizeImports] && len(importEdits) > 0 { +- actions = append(actions, protocol.CodeAction{ +- Title: "Organize Imports", +- Kind: protocol.SourceOrganizeImports, - Edit: &protocol.WorkspaceEdit{ -- DocumentChanges: documentChanges(fh, importFix.Edits), +- DocumentChanges: documentChanges(fh, importEdits), - }, -- Diagnostics: fixes, - }) - } - } - -- // Send all of the import edits as one code action if the file is -- // being organized. -- if wanted[protocol.SourceOrganizeImports] && len(importEdits) > 0 { -- codeActions = append(codeActions, protocol.CodeAction{ -- Title: "Organize Imports", -- Kind: protocol.SourceOrganizeImports, -- Edit: &protocol.WorkspaceEdit{ -- DocumentChanges: documentChanges(fh, importEdits), -- }, -- }) +- if want[protocol.RefactorExtract] { +- extractions, err := refactorExtract(ctx, snapshot, pgf, params.Range) +- if err != nil { +- return nil, err +- } +- actions = append(actions, extractions...) - } - } -- if ctx.Err() != nil { -- return nil, ctx.Err() -- } -- -- // Type-check the package and also run analysis, -- // then combine their diagnostics. -- pkg, _, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.NarrowestPackage) -- if err != nil { -- return nil, err -- } -- pkgDiags, err := pkg.DiagnosticsForFile(ctx, snapshot, uri) -- if err != nil { -- return nil, err -- } -- analysisDiags, err := source.Analyze(ctx, snapshot, pkg.Metadata().ID, true) -- if err != nil { -- return nil, err -- } -- var fileDiags []*source.Diagnostic -- source.CombineDiagnostics(pkgDiags, analysisDiags[uri], &fileDiags, &fileDiags) - -- // Split diagnostics into fixes, which must match incoming diagnostics, -- // and non-fixes, which must match the requested range. Build actions -- // for all of them. -- var fixDiags, nonFixDiags []*source.Diagnostic -- for _, d := range fileDiags { -- if len(d.SuggestedFixes) == 0 { -- continue -- } -- var isFix bool -- for _, fix := range d.SuggestedFixes { -- if fix.ActionKind == protocol.QuickFix || fix.ActionKind == protocol.SourceFixAll { -- isFix = true -- break +- var stubMethodsDiagnostics []protocol.Diagnostic +- if wantQuickFixes && snapshot.Options().IsAnalyzerEnabled(stubmethods.Analyzer.Name) { +- for _, pd := range diagnostics { +- if stubmethods.MatchesMessage(pd.Message) { +- stubMethodsDiagnostics = append(stubMethodsDiagnostics, pd) - } - } -- if isFix { -- fixDiags = append(fixDiags, d) -- } else { -- nonFixDiags = append(nonFixDiags, d) -- } -- } -- -- fixActions, err := codeActionsMatchingDiagnostics(ctx, snapshot, diagnostics, fixDiags) -- if err != nil { -- return nil, err - } -- codeActions = append(codeActions, fixActions...) - -- for _, nonfix := range nonFixDiags { -- // For now, only show diagnostics for matching lines. Maybe we should -- // alter this behavior in the future, depending on the user experience. -- if !protocol.Intersect(nonfix.Range, params.Range) { -- continue -- } -- actions, err := codeActionsForDiagnostic(ctx, snapshot, nonfix, nil) +- // Code actions requiring type information. +- if len(stubMethodsDiagnostics) > 0 || +- want[protocol.RefactorRewrite] || +- want[protocol.RefactorInline] || +- want[protocol.GoTest] { +- pkg, pgf, err := source.NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, err - } -- codeActions = append(codeActions, actions...) -- } +- for _, pd := range stubMethodsDiagnostics { +- start, end, err := pgf.RangePos(pd.Range) +- if err != nil { +- return nil, err +- } +- action, ok, err := func() (_ protocol.CodeAction, _ bool, rerr error) { +- // golang/go#61693: code actions were refactored to run outside of the +- // analysis framework, but as a result they lost their panic recovery. +- // +- // Stubmethods "should never fail"", but put back the panic recovery as a +- // defensive measure. +- defer func() { +- if r := recover(); r != nil { +- rerr = bug.Errorf("stubmethods panicked: %v", r) +- } +- }() +- d, ok := stubmethods.DiagnosticForError(pkg.FileSet(), pgf.File, start, end, pd.Message, pkg.GetTypesInfo()) +- if !ok { +- return protocol.CodeAction{}, false, nil +- } +- cmd, err := command.NewApplyFixCommand(d.Message, command.ApplyFixArgs{ +- URI: protocol.URIFromSpanURI(pgf.URI), +- Fix: source.StubMethods, +- Range: pd.Range, +- }) +- if err != nil { +- return protocol.CodeAction{}, false, err +- } +- return protocol.CodeAction{ +- Title: d.Message, +- Kind: protocol.QuickFix, +- Command: &cmd, +- Diagnostics: []protocol.Diagnostic{pd}, +- }, true, nil +- }() +- if err != nil { +- return nil, err +- } +- if ok { +- actions = append(actions, action) +- } +- } - -- if wanted[protocol.RefactorExtract] { -- fixes, err := extractionFixes(ctx, snapshot, uri, params.Range) -- if err != nil { -- return nil, err +- if want[protocol.RefactorRewrite] { +- rewrites, err := refactorRewrite(ctx, snapshot, pkg, pgf, fh, params.Range) +- if err != nil { +- return nil, err +- } +- actions = append(actions, rewrites...) - } -- codeActions = append(codeActions, fixes...) -- } - -- if wanted[protocol.GoTest] { -- fixes, err := goTest(ctx, snapshot, uri, params.Range) -- if err != nil { -- return nil, err +- if want[protocol.RefactorInline] { +- rewrites, err := refactorInline(ctx, snapshot, pkg, pgf, fh, params.Range) +- if err != nil { +- return nil, err +- } +- actions = append(actions, rewrites...) +- } +- +- if want[protocol.GoTest] { +- fixes, err := goTest(ctx, snapshot, pkg, pgf, params.Range) +- if err != nil { +- return nil, err +- } +- actions = append(actions, fixes...) - } -- codeActions = append(codeActions, fixes...) - } - +- return actions, nil +- - default: - // Unsupported file kind for a code action. - return nil, nil - } +-} +- +-func (s *Server) findMatchingDiagnostics(uri span.URI, pd protocol.Diagnostic) []*source.Diagnostic { +- s.diagnosticsMu.Lock() +- defer s.diagnosticsMu.Unlock() - -- var filtered []protocol.CodeAction -- for _, action := range codeActions { -- if wanted[action.Kind] { -- filtered = append(filtered, action) +- var sds []*source.Diagnostic +- for _, report := range s.diagnostics[uri].reports { +- for _, sd := range report.diags { +- sameDiagnostic := (pd.Message == strings.TrimSpace(sd.Message) && // extra space may have been trimmed when converting to protocol.Diagnostic +- protocol.CompareRange(pd.Range, sd.Range) == 0 && +- pd.Source == string(sd.Source)) +- +- if sameDiagnostic { +- sds = append(sds, sd) +- } - } - } -- return filtered, nil +- return sds -} - -func (s *Server) getSupportedCodeActions() []protocol.CodeActionKind { - allCodeActionKinds := make(map[protocol.CodeActionKind]struct{}) -- for _, kinds := range s.session.Options().SupportedCodeActions { +- for _, kinds := range s.Options().SupportedCodeActions { - for kind := range kinds { - allCodeActionKinds[kind] = struct{}{} - } @@ -29657,7 +33677,10 @@ diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.g - return str -} - --func importDiagnostics(fix *imports.ImportFix, diagnostics []protocol.Diagnostic) (results []protocol.Diagnostic) { +-// fixedByImportFix filters the provided slice of diagnostics to those that +-// would be fixed by the provided imports fix. +-func fixedByImportFix(fix *imports.ImportFix, diagnostics []protocol.Diagnostic) []protocol.Diagnostic { +- var results []protocol.Diagnostic - for _, diagnostic := range diagnostics { - switch { - // "undeclared name: X" may be an unresolved import. @@ -29691,23 +33714,16 @@ diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.g - return results -} - --func extractionFixes(ctx context.Context, snapshot source.Snapshot, uri span.URI, rng protocol.Range) ([]protocol.CodeAction, error) { +-func refactorExtract(ctx context.Context, snapshot source.Snapshot, pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.CodeAction, error) { - if rng.Start == rng.End { - return nil, nil - } -- fh, err := snapshot.GetFile(ctx, uri) -- if err != nil { -- return nil, err -- } -- pgf, err := snapshot.ParseGo(ctx, fh, source.ParseFull) -- if err != nil { -- return nil, fmt.Errorf("getting file for Identifier: %w", err) -- } +- - start, end, err := pgf.RangePos(rng) - if err != nil { - return nil, err - } -- puri := protocol.URIFromSpanURI(uri) +- puri := protocol.URIFromSpanURI(pgf.URI) - var commands []protocol.Command - if _, ok, methodOk, _ := source.CanExtractFunction(pgf.Tok, start, end, pgf.Src, pgf.File); ok { - cmd, err := command.NewApplyFixCommand("Extract function", command.ApplyFixArgs{ @@ -29753,6 +33769,126 @@ diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.g - return actions, nil -} - +-func refactorRewrite(ctx context.Context, snapshot source.Snapshot, pkg source.Package, pgf *source.ParsedGoFile, fh source.FileHandle, rng protocol.Range) (_ []protocol.CodeAction, rerr error) { +- // golang/go#61693: code actions were refactored to run outside of the +- // analysis framework, but as a result they lost their panic recovery. +- // +- // These code actions should never fail, but put back the panic recovery as a +- // defensive measure. +- defer func() { +- if r := recover(); r != nil { +- rerr = bug.Errorf("refactor.rewrite code actions panicked: %v", r) +- } +- }() +- start, end, err := pgf.RangePos(rng) +- if err != nil { +- return nil, err +- } +- +- var commands []protocol.Command +- if _, ok, _ := source.CanInvertIfCondition(pgf.File, start, end); ok { +- cmd, err := command.NewApplyFixCommand("Invert if condition", command.ApplyFixArgs{ +- URI: protocol.URIFromSpanURI(pgf.URI), +- Fix: source.InvertIfCondition, +- Range: rng, +- }) +- if err != nil { +- return nil, err +- } +- commands = append(commands, cmd) +- } +- +- // N.B.: an inspector only pays for itself after ~5 passes, which means we're +- // currently not getting a good deal on this inspection. +- // +- // TODO: Consider removing the inspection after convenienceAnalyzers are removed. +- inspect := inspector.New([]*ast.File{pgf.File}) +- if snapshot.Options().IsAnalyzerEnabled(fillstruct.Analyzer.Name) { +- for _, d := range fillstruct.DiagnoseFillableStructs(inspect, start, end, pkg.GetTypes(), pkg.GetTypesInfo()) { +- rng, err := pgf.Mapper.PosRange(pgf.Tok, d.Pos, d.End) +- if err != nil { +- return nil, err +- } +- cmd, err := command.NewApplyFixCommand(d.Message, command.ApplyFixArgs{ +- URI: protocol.URIFromSpanURI(pgf.URI), +- Fix: source.FillStruct, +- Range: rng, +- }) +- if err != nil { +- return nil, err +- } +- commands = append(commands, cmd) +- } +- } +- +- var actions []protocol.CodeAction +- for i := range commands { +- actions = append(actions, protocol.CodeAction{ +- Title: commands[i].Title, +- Kind: protocol.RefactorRewrite, +- Command: &commands[i], +- }) +- } +- +- if snapshot.Options().IsAnalyzerEnabled(infertypeargs.Analyzer.Name) { +- for _, d := range infertypeargs.DiagnoseInferableTypeArgs(pkg.FileSet(), inspect, start, end, pkg.GetTypes(), pkg.GetTypesInfo()) { +- if len(d.SuggestedFixes) != 1 { +- panic(fmt.Sprintf("unexpected number of suggested fixes from infertypeargs: %v", len(d.SuggestedFixes))) +- } +- fix := d.SuggestedFixes[0] +- var edits []protocol.TextEdit +- for _, analysisEdit := range fix.TextEdits { +- rng, err := pgf.Mapper.PosRange(pgf.Tok, analysisEdit.Pos, analysisEdit.End) +- if err != nil { +- return nil, err +- } +- edits = append(edits, protocol.TextEdit{ +- Range: rng, +- NewText: string(analysisEdit.NewText), +- }) +- } +- actions = append(actions, protocol.CodeAction{ +- Title: "Simplify type arguments", +- Kind: protocol.RefactorRewrite, +- Edit: &protocol.WorkspaceEdit{ +- DocumentChanges: documentChanges(fh, edits), +- }, +- }) +- } +- } +- +- return actions, nil +-} +- +-// refactorInline returns inline actions available at the specified range. +-func refactorInline(ctx context.Context, snapshot source.Snapshot, pkg source.Package, pgf *source.ParsedGoFile, fh source.FileHandle, rng protocol.Range) ([]protocol.CodeAction, error) { +- var commands []protocol.Command +- +- // If range is within call expression, offer inline action. +- if _, fn, err := source.EnclosingStaticCall(pkg, pgf, rng); err == nil { +- cmd, err := command.NewApplyFixCommand(fmt.Sprintf("Inline call to %s", fn.Name()), command.ApplyFixArgs{ +- URI: protocol.URIFromSpanURI(pgf.URI), +- Fix: source.InlineCall, +- Range: rng, +- }) +- if err != nil { +- return nil, err +- } +- commands = append(commands, cmd) +- } +- +- // Convert commands to actions. +- var actions []protocol.CodeAction +- for i := range commands { +- actions = append(actions, protocol.CodeAction{ +- Title: commands[i].Title, +- Kind: protocol.RefactorInline, +- Command: &commands[i], +- }) +- } +- return actions, nil +-} +- -func documentChanges(fh source.FileHandle, edits []protocol.TextEdit) []protocol.DocumentChanges { - return []protocol.DocumentChanges{ - { @@ -29763,41 +33899,55 @@ diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.g - URI: protocol.URIFromSpanURI(fh.URI()), - }, - }, -- Edits: edits, +- Edits: nonNilSliceTextEdit(edits), - }, - }, - } -} - --func codeActionsMatchingDiagnostics(ctx context.Context, snapshot source.Snapshot, pdiags []protocol.Diagnostic, sdiags []*source.Diagnostic) ([]protocol.CodeAction, error) { +-// codeActionsMatchingDiagnostics fetches code actions for the provided +-// diagnostics, by first attempting to unmarshal code actions directly from the +-// bundled protocol.Diagnostic.Data field, and failing that by falling back on +-// fetching a matching source.Diagnostic from the set of stored diagnostics for +-// this file. +-func (s *Server) codeActionsMatchingDiagnostics(ctx context.Context, uri span.URI, snapshot source.Snapshot, pds []protocol.Diagnostic, want map[protocol.CodeActionKind]bool) ([]protocol.CodeAction, error) { - var actions []protocol.CodeAction -- for _, sd := range sdiags { -- var diag *protocol.Diagnostic -- for _, pd := range pdiags { -- if sameDiagnostic(pd, sd) { -- diag = &pd -- break +- var unbundled []protocol.Diagnostic // diagnostics without bundled code actions in their Data field +- for _, pd := range pds { +- bundled := source.BundledQuickFixes(pd) +- if len(bundled) > 0 { +- for _, fix := range bundled { +- if want[fix.Kind] { +- actions = append(actions, fix) +- } - } +- } else { +- // No bundled actions: keep searching for a match. +- unbundled = append(unbundled, pd) - } -- if diag == nil { -- continue -- } -- diagActions, err := codeActionsForDiagnostic(ctx, snapshot, sd, diag) -- if err != nil { -- return nil, err -- } -- actions = append(actions, diagActions...) +- } - +- for _, pd := range unbundled { +- for _, sd := range s.findMatchingDiagnostics(uri, pd) { +- diagActions, err := codeActionsForDiagnostic(ctx, snapshot, sd, &pd, want) +- if err != nil { +- return nil, err +- } +- actions = append(actions, diagActions...) +- } - } - return actions, nil -} - --func codeActionsForDiagnostic(ctx context.Context, snapshot source.Snapshot, sd *source.Diagnostic, pd *protocol.Diagnostic) ([]protocol.CodeAction, error) { +-func codeActionsForDiagnostic(ctx context.Context, snapshot source.Snapshot, sd *source.Diagnostic, pd *protocol.Diagnostic, want map[protocol.CodeActionKind]bool) ([]protocol.CodeAction, error) { - var actions []protocol.CodeAction - for _, fix := range sd.SuggestedFixes { -- var changes []protocol.DocumentChanges +- if !want[fix.ActionKind] { +- continue +- } +- changes := []protocol.DocumentChanges{} // must be a slice - for uri, edits := range fix.Edits { -- fh, err := snapshot.GetFile(ctx, uri) +- fh, err := snapshot.ReadFile(ctx, uri) - if err != nil { - return nil, err - } @@ -29811,25 +33961,14 @@ diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.g - }, - Command: fix.Command, - } -- if pd != nil { -- action.Diagnostics = []protocol.Diagnostic{*pd} -- } +- action.Diagnostics = []protocol.Diagnostic{*pd} - actions = append(actions, action) - } - return actions, nil -} - --func sameDiagnostic(pd protocol.Diagnostic, sd *source.Diagnostic) bool { -- return pd.Message == strings.TrimSpace(sd.Message) && // extra space may have been trimmed when converting to protocol.Diagnostic -- protocol.CompareRange(pd.Range, sd.Range) == 0 && pd.Source == string(sd.Source) --} -- --func goTest(ctx context.Context, snapshot source.Snapshot, uri span.URI, rng protocol.Range) ([]protocol.CodeAction, error) { -- fh, err := snapshot.GetFile(ctx, uri) -- if err != nil { -- return nil, err -- } -- fns, err := source.TestsAndBenchmarks(ctx, snapshot, fh) +-func goTest(ctx context.Context, snapshot source.Snapshot, pkg source.Package, pgf *source.ParsedGoFile, rng protocol.Range) ([]protocol.CodeAction, error) { +- fns, err := source.TestsAndBenchmarks(ctx, snapshot, pkg, pgf) - if err != nil { - return nil, err - } @@ -29852,7 +33991,7 @@ diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.g - return nil, nil - } - -- cmd, err := command.NewTestCommand("Run tests and benchmarks", protocol.URIFromSpanURI(uri), tests, benchmarks) +- cmd, err := command.NewTestCommand("Run tests and benchmarks", protocol.URIFromSpanURI(pgf.URI), tests, benchmarks) - if err != nil { - return nil, err - } @@ -29862,10 +34001,12 @@ diff -urN a/gopls/internal/lsp/code_action.go b/gopls/internal/lsp/code_action.g - Command: &cmd, - }}, nil -} +- +-type unit = struct{} diff -urN a/gopls/internal/lsp/code_lens.go b/gopls/internal/lsp/code_lens.go --- a/gopls/internal/lsp/code_lens.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/code_lens.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,57 +0,0 @@ ++++ b/gopls/internal/lsp/code_lens.go 1970-01-01 08:00:00 +@@ -1,61 +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. @@ -29882,16 +34023,20 @@ diff -urN a/gopls/internal/lsp/code_lens.go b/gopls/internal/lsp/code_lens.go - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" -) - -func (s *Server) codeLens(ctx context.Context, params *protocol.CodeLensParams) ([]protocol.CodeLens, error) { +- ctx, done := event.Start(ctx, "lsp.Server.codeLens", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) - defer release() - if !ok { - return nil, err - } - var lenses map[command.Command]source.LensFunc -- switch snapshot.View().FileKind(fh) { +- switch snapshot.FileKind(fh) { - case source.Mod: - lenses = mod.LensFuncs() - case source.Go: @@ -29902,7 +34047,7 @@ diff -urN a/gopls/internal/lsp/code_lens.go b/gopls/internal/lsp/code_lens.go - } - var result []protocol.CodeLens - for cmd, lf := range lenses { -- if !snapshot.View().Options().Codelenses[string(cmd)] { +- if !snapshot.Options().Codelenses[string(cmd)] { - continue - } - added, err := lf(ctx, snapshot, fh) @@ -29925,8 +34070,8 @@ diff -urN a/gopls/internal/lsp/code_lens.go b/gopls/internal/lsp/code_lens.go -} diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/command/command_gen.go --- a/gopls/internal/lsp/command/command_gen.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/command/command_gen.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,509 +0,0 @@ ++++ b/gopls/internal/lsp/command/command_gen.go 1970-01-01 08:00:00 +@@ -1,621 +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. @@ -29936,10 +34081,10 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma -//go:build !generate -// +build !generate - --package command -- -// Code generated by generate.go. DO NOT EDIT. - +-package command +- -import ( - "context" - "fmt" @@ -29948,35 +34093,42 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma -) - -const ( -- AddDependency Command = "add_dependency" -- AddImport Command = "add_import" -- ApplyFix Command = "apply_fix" -- CheckUpgrades Command = "check_upgrades" -- EditGoDirective Command = "edit_go_directive" -- FetchVulncheckResult Command = "fetch_vulncheck_result" -- GCDetails Command = "gc_details" -- Generate Command = "generate" -- GoGetPackage Command = "go_get_package" -- ListImports Command = "list_imports" -- ListKnownPackages Command = "list_known_packages" -- MemStats Command = "mem_stats" -- RegenerateCgo Command = "regenerate_cgo" -- RemoveDependency Command = "remove_dependency" -- ResetGoModDiagnostics Command = "reset_go_mod_diagnostics" -- RunGovulncheck Command = "run_govulncheck" -- RunTests Command = "run_tests" -- StartDebugging Command = "start_debugging" -- Test Command = "test" -- Tidy Command = "tidy" -- ToggleGCDetails Command = "toggle_gc_details" -- UpdateGoSum Command = "update_go_sum" -- UpgradeDependency Command = "upgrade_dependency" -- Vendor Command = "vendor" +- AddDependency Command = "add_dependency" +- AddImport Command = "add_import" +- AddTelemetryCounters Command = "add_telemetry_counters" +- ApplyFix Command = "apply_fix" +- CheckUpgrades Command = "check_upgrades" +- EditGoDirective Command = "edit_go_directive" +- FetchVulncheckResult Command = "fetch_vulncheck_result" +- GCDetails Command = "gc_details" +- Generate Command = "generate" +- GoGetPackage Command = "go_get_package" +- ListImports Command = "list_imports" +- ListKnownPackages Command = "list_known_packages" +- MaybePromptForTelemetry Command = "maybe_prompt_for_telemetry" +- MemStats Command = "mem_stats" +- RegenerateCgo Command = "regenerate_cgo" +- RemoveDependency Command = "remove_dependency" +- ResetGoModDiagnostics Command = "reset_go_mod_diagnostics" +- RunGoWorkCommand Command = "run_go_work_command" +- RunGovulncheck Command = "run_govulncheck" +- RunTests Command = "run_tests" +- StartDebugging Command = "start_debugging" +- StartProfile Command = "start_profile" +- StopProfile Command = "stop_profile" +- Test Command = "test" +- Tidy Command = "tidy" +- ToggleGCDetails Command = "toggle_gc_details" +- UpdateGoSum Command = "update_go_sum" +- UpgradeDependency Command = "upgrade_dependency" +- Vendor Command = "vendor" +- WorkspaceStats Command = "workspace_stats" -) - -var Commands = []Command{ - AddDependency, - AddImport, +- AddTelemetryCounters, - ApplyFix, - CheckUpgrades, - EditGoDirective, @@ -29986,19 +34138,24 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - GoGetPackage, - ListImports, - ListKnownPackages, +- MaybePromptForTelemetry, - MemStats, - RegenerateCgo, - RemoveDependency, - ResetGoModDiagnostics, +- RunGoWorkCommand, - RunGovulncheck, - RunTests, - StartDebugging, +- StartProfile, +- StopProfile, - Test, - Tidy, - ToggleGCDetails, - UpdateGoSum, - UpgradeDependency, - Vendor, +- WorkspaceStats, -} - -func Dispatch(ctx context.Context, params *protocol.ExecuteCommandParams, s Interface) (interface{}, error) { @@ -30015,6 +34172,12 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - return nil, err - } - return nil, s.AddImport(ctx, a0) +- case "gopls.add_telemetry_counters": +- var a0 AddTelemetryCountersArgs +- if err := UnmarshalArgs(params.Arguments, &a0); err != nil { +- return nil, err +- } +- return nil, s.AddTelemetryCounters(ctx, a0) - case "gopls.apply_fix": - var a0 ApplyFixArgs - if err := UnmarshalArgs(params.Arguments, &a0); err != nil { @@ -30069,6 +34232,8 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - return nil, err - } - return s.ListKnownPackages(ctx, a0) +- case "gopls.maybe_prompt_for_telemetry": +- return nil, s.MaybePromptForTelemetry(ctx) - case "gopls.mem_stats": - return s.MemStats(ctx) - case "gopls.regenerate_cgo": @@ -30089,6 +34254,12 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - return nil, err - } - return nil, s.ResetGoModDiagnostics(ctx, a0) +- case "gopls.run_go_work_command": +- var a0 RunGoWorkArgs +- if err := UnmarshalArgs(params.Arguments, &a0); err != nil { +- return nil, err +- } +- return nil, s.RunGoWorkCommand(ctx, a0) - case "gopls.run_govulncheck": - var a0 VulncheckArgs - if err := UnmarshalArgs(params.Arguments, &a0); err != nil { @@ -30107,6 +34278,18 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - return nil, err - } - return s.StartDebugging(ctx, a0) +- case "gopls.start_profile": +- var a0 StartProfileArgs +- if err := UnmarshalArgs(params.Arguments, &a0); err != nil { +- return nil, err +- } +- return s.StartProfile(ctx, a0) +- case "gopls.stop_profile": +- var a0 StopProfileArgs +- if err := UnmarshalArgs(params.Arguments, &a0); err != nil { +- return nil, err +- } +- return s.StopProfile(ctx, a0) - case "gopls.test": - var a0 protocol.DocumentURI - var a1 []string @@ -30145,6 +34328,8 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - return nil, err - } - return nil, s.Vendor(ctx, a0) +- case "gopls.workspace_stats": +- return s.WorkspaceStats(ctx) - } - return nil, fmt.Errorf("unsupported command %q", params.Command) -} @@ -30173,6 +34358,18 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - }, nil -} - +-func NewAddTelemetryCountersCommand(title string, a0 AddTelemetryCountersArgs) (protocol.Command, error) { +- args, err := MarshalArgs(a0) +- if err != nil { +- return protocol.Command{}, err +- } +- return protocol.Command{ +- Title: title, +- Command: "gopls.add_telemetry_counters", +- Arguments: args, +- }, nil +-} +- -func NewApplyFixCommand(title string, a0 ApplyFixArgs) (protocol.Command, error) { - args, err := MarshalArgs(a0) - if err != nil { @@ -30281,6 +34478,18 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - }, nil -} - +-func NewMaybePromptForTelemetryCommand(title string) (protocol.Command, error) { +- args, err := MarshalArgs() +- if err != nil { +- return protocol.Command{}, err +- } +- return protocol.Command{ +- Title: title, +- Command: "gopls.maybe_prompt_for_telemetry", +- Arguments: args, +- }, nil +-} +- -func NewMemStatsCommand(title string) (protocol.Command, error) { - args, err := MarshalArgs() - if err != nil { @@ -30329,6 +34538,18 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - }, nil -} - +-func NewRunGoWorkCommandCommand(title string, a0 RunGoWorkArgs) (protocol.Command, error) { +- args, err := MarshalArgs(a0) +- if err != nil { +- return protocol.Command{}, err +- } +- return protocol.Command{ +- Title: title, +- Command: "gopls.run_go_work_command", +- Arguments: args, +- }, nil +-} +- -func NewRunGovulncheckCommand(title string, a0 VulncheckArgs) (protocol.Command, error) { - args, err := MarshalArgs(a0) - if err != nil { @@ -30365,6 +34586,30 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - }, nil -} - +-func NewStartProfileCommand(title string, a0 StartProfileArgs) (protocol.Command, error) { +- args, err := MarshalArgs(a0) +- if err != nil { +- return protocol.Command{}, err +- } +- return protocol.Command{ +- Title: title, +- Command: "gopls.start_profile", +- Arguments: args, +- }, nil +-} +- +-func NewStopProfileCommand(title string, a0 StopProfileArgs) (protocol.Command, error) { +- args, err := MarshalArgs(a0) +- if err != nil { +- return protocol.Command{}, err +- } +- return protocol.Command{ +- Title: title, +- Command: "gopls.stop_profile", +- Arguments: args, +- }, nil +-} +- -func NewTestCommand(title string, a0 protocol.DocumentURI, a1 []string, a2 []string) (protocol.Command, error) { - args, err := MarshalArgs(a0, a1, a2) - if err != nil { @@ -30436,9 +34681,21 @@ diff -urN a/gopls/internal/lsp/command/command_gen.go b/gopls/internal/lsp/comma - Arguments: args, - }, nil -} +- +-func NewWorkspaceStatsCommand(title string) (protocol.Command, error) { +- args, err := MarshalArgs() +- if err != nil { +- return protocol.Command{}, err +- } +- return protocol.Command{ +- Title: title, +- Command: "gopls.workspace_stats", +- Arguments: args, +- }, nil +-} diff -urN a/gopls/internal/lsp/command/commandmeta/meta.go b/gopls/internal/lsp/command/commandmeta/meta.go --- a/gopls/internal/lsp/command/commandmeta/meta.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/command/commandmeta/meta.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/command/commandmeta/meta.go 1970-01-01 08:00:00 @@ -1,259 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -30701,7 +34958,7 @@ diff -urN a/gopls/internal/lsp/command/commandmeta/meta.go b/gopls/internal/lsp/ -} diff -urN a/gopls/internal/lsp/command/gen/gen.go b/gopls/internal/lsp/command/gen/gen.go --- a/gopls/internal/lsp/command/gen/gen.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/command/gen/gen.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/command/gen/gen.go 1970-01-01 08:00:00 @@ -1,155 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -30717,8 +34974,8 @@ diff -urN a/gopls/internal/lsp/command/gen/gen.go b/gopls/internal/lsp/command/g - "go/types" - "text/template" - -- "golang.org/x/tools/internal/imports" - "golang.org/x/tools/gopls/internal/lsp/command/commandmeta" +- "golang.org/x/tools/internal/imports" -) - -const src = `// Copyright 2021 The Go Authors. All rights reserved. @@ -30730,10 +34987,10 @@ diff -urN a/gopls/internal/lsp/command/gen/gen.go b/gopls/internal/lsp/command/g -//go:build !generate -// +build !generate - --package command -- -// Code generated by generate.go. DO NOT EDIT. - +-package command +- -import ( - {{range $k, $v := .Imports -}} - "{{$k}}" @@ -30860,7 +35117,7 @@ diff -urN a/gopls/internal/lsp/command/gen/gen.go b/gopls/internal/lsp/command/g -} diff -urN a/gopls/internal/lsp/command/generate.go b/gopls/internal/lsp/command/generate.go --- a/gopls/internal/lsp/command/generate.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/command/generate.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/command/generate.go 1970-01-01 08:00:00 @@ -1,25 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -30889,8 +35146,8 @@ diff -urN a/gopls/internal/lsp/command/generate.go b/gopls/internal/lsp/command/ -} diff -urN a/gopls/internal/lsp/command/interface.go b/gopls/internal/lsp/command/interface.go --- a/gopls/internal/lsp/command/interface.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/command/interface.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,410 +0,0 @@ ++++ b/gopls/internal/lsp/command/interface.go 1970-01-01 08:00:00 +@@ -1,521 +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. @@ -30910,8 +35167,8 @@ diff -urN a/gopls/internal/lsp/command/interface.go b/gopls/internal/lsp/command -import ( - "context" - -- "golang.org/x/tools/gopls/internal/govulncheck" - "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/vulncheck" -) - -// Interface defines the interface gopls exposes for the @@ -30930,6 +35187,7 @@ diff -urN a/gopls/internal/lsp/command/interface.go b/gopls/internal/lsp/command - // - // Applies a fix to a region of source code. - ApplyFix(context.Context, ApplyFixArgs) error +- - // Test: Run test(s) (legacy) - // - // Runs `go test` for a specific set of test or benchmark functions. @@ -31038,7 +35296,22 @@ diff -urN a/gopls/internal/lsp/command/interface.go b/gopls/internal/lsp/command - // address. - StartDebugging(context.Context, DebuggingArgs) (DebuggingResult, error) - -- // RunGovulncheck: Run govulncheck. +- // StartProfile: start capturing a profile of gopls' execution. +- // +- // Start a new pprof profile. Before using the resulting file, profiling must +- // be stopped with a corresponding call to StopProfile. +- // +- // This command is intended for internal use only, by the gopls benchmark +- // runner. +- StartProfile(context.Context, StartProfileArgs) (StartProfileResult, error) +- +- // StopProfile: stop an ongoing profile. +- // +- // This command is intended for internal use only, by the gopls benchmark +- // runner. +- StopProfile(context.Context, StopProfileArgs) (StopProfileResult, error) +- +- // RunGovulncheck: Run vulncheck. - // - // Run vulnerability check (`govulncheck`). - RunGovulncheck(context.Context, VulncheckArgs) (RunVulncheckResult, error) @@ -31046,7 +35319,7 @@ diff -urN a/gopls/internal/lsp/command/interface.go b/gopls/internal/lsp/command - // FetchVulncheckResult: Get known vulncheck result - // - // Fetch the result of latest vulnerability check (`govulncheck`). -- FetchVulncheckResult(context.Context, URIArg) (map[protocol.DocumentURI]*govulncheck.Result, error) +- FetchVulncheckResult(context.Context, URIArg) (map[protocol.DocumentURI]*vulncheck.Result, error) - - // MemStats: fetch memory statistics - // @@ -31055,6 +35328,29 @@ diff -urN a/gopls/internal/lsp/command/interface.go b/gopls/internal/lsp/command - // - // This command is used for benchmarking, and may change in the future. - MemStats(context.Context) (MemStatsResult, error) +- +- // WorkspaceStats: fetch workspace statistics +- // +- // Query statistics about workspace builds, modules, packages, and files. +- // +- // This command is intended for internal use only, by the gopls stats +- // command. +- WorkspaceStats(context.Context) (WorkspaceStatsResult, error) +- +- // RunGoWorkCommand: run `go work [args...]`, and apply the resulting go.work +- // edits to the current go.work file. +- RunGoWorkCommand(context.Context, RunGoWorkArgs) error +- +- // AddTelemetryCounters: update the given telemetry counters. +- // +- // Gopls will prepend "fwd/" to all the counters updated using this command +- // to avoid conflicts with other counters gopls collects. +- AddTelemetryCounters(context.Context, AddTelemetryCountersArgs) error +- +- // MaybePromptForTelemetry: checks for the right conditions, and then prompts +- // the user to ask if they want to enable Go telemetry uploading. If the user +- // responds 'Yes', the telemetry mode is set to "on". +- MaybePromptForTelemetry(context.Context) error -} - -type RunTestsArgs struct { @@ -31117,7 +35413,10 @@ diff -urN a/gopls/internal/lsp/command/interface.go b/gopls/internal/lsp/command - // The go.mod file URI. - URI protocol.DocumentURI - // The module path to remove. -- ModulePath string +- ModulePath string +- // If the module is tidied apart from the one unused diagnostic, we can +- // run `go get module@none`, and then run `go mod tidy`. Otherwise, we +- // must make textual edits. - OnlyDiagnostic bool -} - @@ -31205,6 +35504,30 @@ diff -urN a/gopls/internal/lsp/command/interface.go b/gopls/internal/lsp/command - URLs []string -} - +-// StartProfileArgs holds the arguments to the StartProfile command. +-// +-// It is a placeholder for future compatibility. +-type StartProfileArgs struct { +-} +- +-// StartProfileResult holds the result of the StartProfile command. +-// +-// It is a placeholder for future compatibility. +-type StartProfileResult struct { +-} +- +-// StopProfileArgs holds the arguments to the StopProfile command. +-// +-// It is a placeholder for future compatibility. +-type StopProfileArgs struct { +-} +- +-// StopProfileResult holds the result to the StopProfile command. +-type StopProfileResult struct { +- // File is the profile file name. +- File string +-} +- -type ResetGoModDiagnosticsArgs struct { - URIArg - @@ -31298,13 +35621,58 @@ diff -urN a/gopls/internal/lsp/command/interface.go b/gopls/internal/lsp/command - -// MemStatsResult holds selected fields from runtime.MemStats. -type MemStatsResult struct { -- HeapAlloc uint64 -- HeapInUse uint64 +- HeapAlloc uint64 +- HeapInUse uint64 +- TotalAlloc uint64 +-} +- +-// WorkspaceStatsResult returns information about the size and shape of the +-// workspace. +-type WorkspaceStatsResult struct { +- Files FileStats // file stats for the cache +- Views []ViewStats // stats for each view in the session +-} +- +-// FileStats holds information about a set of files. +-type FileStats struct { +- Total int // total number of files +- Largest int // number of bytes in the largest file +- Errs int // number of files that could not be read +-} +- +-// ViewStats holds information about a single View in the session. +-type ViewStats struct { +- GoCommandVersion string // version of the Go command resolved for this view +- AllPackages PackageStats // package info for all packages (incl. dependencies) +- WorkspacePackages PackageStats // package info for workspace packages +- Diagnostics int // total number of diagnostics in the workspace +-} +- +-// PackageStats holds information about a collection of packages. +-type PackageStats struct { +- Packages int // total number of packages +- LargestPackage int // number of files in the largest package +- CompiledGoFiles int // total number of compiled Go files across all packages +- Modules int // total number of unique modules +-} +- +-type RunGoWorkArgs struct { +- ViewID string // ID of the view to run the command from +- InitFirst bool // Whether to run `go work init` first +- Args []string // Args to pass to `go work` +-} +- +-// AddTelemetryCountersArgs holds the arguments to the AddCounters command +-// that updates the telemetry counters. +-type AddTelemetryCountersArgs struct { +- // Names and Values must have the same length. +- Names []string // Name of counters. +- Values []int64 // Values added to the corresponding counters. Must be non-negative. -} diff -urN a/gopls/internal/lsp/command/interface_test.go b/gopls/internal/lsp/command/interface_test.go --- a/gopls/internal/lsp/command/interface_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/command/interface_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,31 +0,0 @@ ++++ b/gopls/internal/lsp/command/interface_test.go 1970-01-01 08:00:00 +@@ -1,32 +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. @@ -31312,7 +35680,7 @@ diff -urN a/gopls/internal/lsp/command/interface_test.go b/gopls/internal/lsp/co -package command_test - -import ( -- "io/ioutil" +- "os" - "testing" - - "github.com/google/go-cmp/cmp" @@ -31321,9 +35689,10 @@ diff -urN a/gopls/internal/lsp/command/interface_test.go b/gopls/internal/lsp/co -) - -func TestGenerated(t *testing.T) { -- testenv.NeedsGoBuild(t) // This is a lie. We actually need the source code. +- testenv.NeedsGoPackages(t) +- testenv.NeedsLocalXTools(t) - -- onDisk, err := ioutil.ReadFile("command_gen.go") +- onDisk, err := os.ReadFile("command_gen.go") - if err != nil { - t.Fatal(err) - } @@ -31338,8 +35707,8 @@ diff -urN a/gopls/internal/lsp/command/interface_test.go b/gopls/internal/lsp/co -} diff -urN a/gopls/internal/lsp/command/util.go b/gopls/internal/lsp/command/util.go --- a/gopls/internal/lsp/command/util.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/command/util.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,63 +0,0 @@ ++++ b/gopls/internal/lsp/command/util.go 1970-01-01 08:00:00 +@@ -1,64 +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. @@ -31358,6 +35727,7 @@ diff -urN a/gopls/internal/lsp/command/util.go b/gopls/internal/lsp/command/util - -type Command string - +-// ID returns the command identifier to use in the executeCommand request. -func (c Command) ID() string { - return ID(string(c)) -} @@ -31405,8 +35775,8 @@ diff -urN a/gopls/internal/lsp/command/util.go b/gopls/internal/lsp/command/util -} diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go --- a/gopls/internal/lsp/command.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/command.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,964 +0,0 @@ ++++ b/gopls/internal/lsp/command.go 1970-01-01 08:00:00 +@@ -1,1212 +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. @@ -31420,33 +35790,38 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - "errors" - "fmt" - "io" -- "io/ioutil" - "os" -- "os/exec" - "path/filepath" - "runtime" +- "runtime/pprof" - "sort" - "strings" -- "time" - - "golang.org/x/mod/modfile" - "golang.org/x/tools/go/ast/astutil" -- "golang.org/x/tools/gopls/internal/govulncheck" +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/cache" - "golang.org/x/tools/gopls/internal/lsp/command" - "golang.org/x/tools/gopls/internal/lsp/debug" - "golang.org/x/tools/gopls/internal/lsp/progress" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/gopls/internal/telemetry" - "golang.org/x/tools/gopls/internal/vulncheck" +- "golang.org/x/tools/gopls/internal/vulncheck/scan" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/gocommand" +- "golang.org/x/tools/internal/tokeninternal" - "golang.org/x/tools/internal/xcontext" -) - -func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error) { +- ctx, done := event.Start(ctx, "lsp.Server.executeCommand") +- defer done() +- - var found bool -- for _, name := range s.session.Options().SupportedCommands { +- for _, name := range s.Options().SupportedCommands { - if name == params.Command { - found = true - break @@ -31468,11 +35843,26 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - params *protocol.ExecuteCommandParams -} - +-func (h *commandHandler) MaybePromptForTelemetry(ctx context.Context) error { +- go h.s.maybePromptForTelemetry(ctx, true) +- return nil +-} +- +-func (*commandHandler) AddTelemetryCounters(_ context.Context, args command.AddTelemetryCountersArgs) error { +- if len(args.Names) != len(args.Values) { +- return fmt.Errorf("Names and Values must have the same length") +- } +- // invalid counter update requests will be silently dropped. (no audience) +- telemetry.AddForwardedCounters(args.Names, args.Values) +- return nil +-} +- -// commandConfig configures common command set-up and execution. -type commandConfig struct { - async bool // whether to run the command asynchronously. Async commands can only return errors. - requireSave bool // whether all files must be saved for the command to work - progress string // title to use for progress reporting. If empty, no progress will be reported. +- forView string // view to resolve to a snapshot; incompatible with forURI - forURI protocol.DocumentURI // URI to resolve to a snapshot. If unset, snapshot will be nil. -} - @@ -31498,7 +35888,7 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - if cfg.requireSave { - var unsaved []string - for _, overlay := range c.s.session.Overlays() { -- if !overlay.Saved() { +- if !overlay.SameContentsOnDisk() { - unsaved = append(unsaved, overlay.URI().Filename()) - } - } @@ -31507,6 +35897,9 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - } - } - var deps commandDeps +- if cfg.forURI != "" && cfg.forView != "" { +- return bug.Errorf("internal error: forURI=%q, forView=%q", cfg.forURI, cfg.forView) +- } - if cfg.forURI != "" { - var ok bool - var release func() @@ -31518,6 +35911,17 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - } - return fmt.Errorf("invalid file URL: %v", cfg.forURI) - } +- } else if cfg.forView != "" { +- view, err := c.s.session.View(cfg.forView) +- if err != nil { +- return err +- } +- var release func() +- deps.snapshot, release, err = view.Snapshot() +- if err != nil { +- return err +- } +- defer release() - } - ctx, cancel := context.WithCancel(xcontext.Detach(ctx)) - if cfg.progress != "" { @@ -31564,7 +35968,7 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - if err != nil { - return err - } -- var changes []protocol.DocumentChanges +- changes := []protocol.DocumentChanges{} // must be a slice - for _, edit := range edits { - edit := edit - changes = append(changes, protocol.DocumentChanges{ @@ -31609,7 +36013,7 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - } - deps.snapshot.View().RegisterModuleUpgrades(args.URI.SpanURI(), upgrades) - // Re-diagnose the snapshot to publish the new module diagnostics. -- c.s.diagnoseSnapshot(deps.snapshot, nil, false) +- c.s.diagnoseSnapshot(deps.snapshot, nil, false, 0) - return nil - }) -} @@ -31640,7 +36044,7 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - } - - // Re-diagnose the snapshot to remove the diagnostics. -- c.s.diagnoseSnapshot(deps.snapshot, nil, false) +- c.s.diagnoseSnapshot(deps.snapshot, nil, false, 0) - return nil - }) -} @@ -31748,10 +36152,9 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - progress: "Removing dependency", - forURI: args.URI, - }, func(ctx context.Context, deps commandDeps) error { -- // If the module is tidied apart from the one unused diagnostic, we can -- // run `go get module@none`, and then run `go mod tidy`. Otherwise, we -- // must make textual edits. -- // TODO(rstambler): In Go 1.17+, we will be able to use the go command +- // See the documentation for OnlyDiagnostic. +- // +- // TODO(rfindley): In Go 1.17+, we will be able to use the go command - // without checking if the module is tidy. - if args.OnlyDiagnostic { - return c.s.runGoModUpdateCommands(ctx, deps.snapshot, args.URI.SpanURI(), func(invoke func(...string) (*bytes.Buffer, error)) error { @@ -31781,7 +36184,7 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - URI: protocol.URIFromSpanURI(deps.fh.URI()), - }, - }, -- Edits: edits, +- Edits: nonNilSliceTextEdit(edits), - }, - }, - }, @@ -31815,7 +36218,7 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - return nil, err - } - // Calculate the edits to be made due to the change. -- diff := snapshot.View().Options().ComputeEdits(string(pm.Mapper.Content), string(newContent)) +- diff := snapshot.Options().ComputeEdits(string(pm.Mapper.Content), string(newContent)) - return source.ToProtocolEdits(pm.Mapper, diff) -} - @@ -31843,15 +36246,11 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - -func (c *commandHandler) runTests(ctx context.Context, snapshot source.Snapshot, work *progress.WorkDone, uri protocol.DocumentURI, tests, benchmarks []string) error { - // TODO: fix the error reporting when this runs async. -- metas, err := snapshot.MetadataForFile(ctx, uri.SpanURI()) +- meta, err := source.NarrowestMetadataForFile(ctx, snapshot, uri.SpanURI()) - if err != nil { - return err - } -- metas = source.RemoveIntermediateTestVariants(metas) -- if len(metas) == 0 { -- return fmt.Errorf("package could not be found for file: %s", uri.SpanURI().Filename()) -- } -- pkgPath := string(metas[0].ForTest) +- pkgPath := string(meta.ForTest) - - // create output - buf := &bytes.Buffer{} @@ -31984,48 +36383,35 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - } - modURI := snapshot.GoModForFile(uri) - sumURI := span.URIFromPath(strings.TrimSuffix(modURI.Filename(), ".mod") + ".sum") -- modEdits, err := applyFileEdits(ctx, snapshot, modURI, newModBytes) +- modEdits, err := collectFileEdits(ctx, snapshot, modURI, newModBytes) - if err != nil { - return err - } -- sumEdits, err := applyFileEdits(ctx, snapshot, sumURI, newSumBytes) +- sumEdits, err := collectFileEdits(ctx, snapshot, sumURI, newSumBytes) - if err != nil { - return err - } -- changes := append(sumEdits, modEdits...) -- if len(changes) == 0 { -- return nil -- } -- var documentChanges []protocol.DocumentChanges -- for _, change := range changes { -- change := change -- documentChanges = append(documentChanges, protocol.DocumentChanges{ -- TextDocumentEdit: &change, -- }) -- } -- response, err := s.client.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{ -- Edit: protocol.WorkspaceEdit{ -- DocumentChanges: documentChanges, -- }, -- }) -- if err != nil { -- return err -- } -- if !response.Applied { -- return fmt.Errorf("edits not applied because of %s", response.FailureReason) -- } -- return nil +- return applyFileEdits(ctx, s.client, append(sumEdits, modEdits...)) -} - --func applyFileEdits(ctx context.Context, snapshot source.Snapshot, uri span.URI, newContent []byte) ([]protocol.TextDocumentEdit, error) { -- fh, err := snapshot.GetFile(ctx, uri) +-// collectFileEdits collects any file edits required to transform the snapshot +-// file specified by uri to the provided new content. +-// +-// If the file is not open, collectFileEdits simply writes the new content to +-// disk. +-// +-// TODO(rfindley): fix this API asymmetry. It should be up to the caller to +-// write the file or apply the edits. +-func collectFileEdits(ctx context.Context, snapshot source.Snapshot, uri span.URI, newContent []byte) ([]protocol.TextDocumentEdit, error) { +- fh, err := snapshot.ReadFile(ctx, uri) - if err != nil { - return nil, err - } -- oldContent, err := fh.Read() +- oldContent, err := fh.Content() - if err != nil && !os.IsNotExist(err) { - return nil, err - } +- - if bytes.Equal(oldContent, newContent) { - return nil, nil - } @@ -32034,12 +36420,12 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - // file and leave it unsaved. We would rather apply the changes directly, - // especially to go.sum, which should be mostly invisible to the user. - if !snapshot.IsOpen(uri) { -- err := ioutil.WriteFile(uri.Filename(), newContent, 0666) +- err := os.WriteFile(uri.Filename(), newContent, 0666) - return nil, err - } - - m := protocol.NewMapper(fh.URI(), oldContent) -- diff := snapshot.View().Options().ComputeEdits(string(oldContent), string(newContent)) +- diff := snapshot.Options().ComputeEdits(string(oldContent), string(newContent)) - edits, err := source.ToProtocolEdits(m, diff) - if err != nil { - return nil, err @@ -32055,6 +36441,31 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - }}, nil -} - +-func applyFileEdits(ctx context.Context, cli protocol.Client, edits []protocol.TextDocumentEdit) error { +- if len(edits) == 0 { +- return nil +- } +- documentChanges := []protocol.DocumentChanges{} // must be a slice +- for _, change := range edits { +- change := change +- documentChanges = append(documentChanges, protocol.DocumentChanges{ +- TextDocumentEdit: &change, +- }) +- } +- response, err := cli.ApplyEdit(ctx, &protocol.ApplyWorkspaceEditParams{ +- Edit: protocol.WorkspaceEdit{ +- DocumentChanges: documentChanges, +- }, +- }) +- if err != nil { +- return err +- } +- if !response.Applied { +- return fmt.Errorf("edits not applied because of %s", response.FailureReason) +- } +- return nil +-} +- -func runGoGetModule(invoke func(...string) (*bytes.Buffer, error), addRequire bool, args []string) error { - if addRequire { - if err := addModuleRequire(invoke, args); err != nil { @@ -32109,20 +36520,19 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - progress: "Toggling GC Details", - forURI: args.URI, - }, func(ctx context.Context, deps commandDeps) error { -- metas, err := deps.snapshot.MetadataForFile(ctx, deps.fh.URI()) +- meta, err := source.NarrowestMetadataForFile(ctx, deps.snapshot, deps.fh.URI()) - if err != nil { - return err - } -- id := metas[0].ID // 0 => narrowest package - c.s.gcOptimizationDetailsMu.Lock() -- if _, ok := c.s.gcOptimizationDetails[id]; ok { -- delete(c.s.gcOptimizationDetails, id) +- if _, ok := c.s.gcOptimizationDetails[meta.ID]; ok { +- delete(c.s.gcOptimizationDetails, meta.ID) - c.s.clearDiagnosticSource(gcDetailsSource) - } else { -- c.s.gcOptimizationDetails[id] = struct{}{} +- c.s.gcOptimizationDetails[meta.ID] = struct{}{} - } - c.s.gcOptimizationDetailsMu.Unlock() -- c.s.diagnoseSnapshot(deps.snapshot, nil, false) +- c.s.diagnoseSnapshot(deps.snapshot, nil, false, 0) - return nil - }) -} @@ -32147,7 +36557,7 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - err := c.run(ctx, commandConfig{ - forURI: args.URI, - }, func(ctx context.Context, deps commandDeps) error { -- fh, err := deps.snapshot.GetFile(ctx, args.URI.SpanURI()) +- fh, err := deps.snapshot.ReadFile(ctx, args.URI.SpanURI()) - if err != nil { - return err - } @@ -32155,7 +36565,7 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - if err != nil { - return err - } -- fset := source.FileSetFor(pgf.Tok) +- fset := tokeninternal.FileSetFor(pgf.Tok) - for _, group := range astutil.Imports(fset, pgf.File) { - for _, imp := range group { - if imp.Path == nil { @@ -32171,14 +36581,11 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - }) - } - } -- metas, err := deps.snapshot.MetadataForFile(ctx, args.URI.SpanURI()) +- meta, err := source.NarrowestMetadataForFile(ctx, deps.snapshot, args.URI.SpanURI()) - if err != nil { - return err // e.g. cancelled - } -- if len(metas) == 0 { -- return fmt.Errorf("no package containing %v", args.URI.SpanURI()) -- } -- for pkgPath := range metas[0].DepsByPkgPath { // 0 => narrowest package +- for pkgPath := range meta.DepsByPkgPath { - result.PackageImports = append(result.PackageImports, - command.PackageImport{Path: string(pkgPath)}) - } @@ -32224,6 +36631,49 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - return result, fmt.Errorf("starting debug server: %w", err) - } - result.URLs = []string{"http://" + listenedAddr} +- openClientBrowser(ctx, c.s.client, result.URLs[0]) +- return result, nil +-} +- +-func (c *commandHandler) StartProfile(ctx context.Context, args command.StartProfileArgs) (result command.StartProfileResult, _ error) { +- file, err := os.CreateTemp("", "gopls-profile-*") +- if err != nil { +- return result, fmt.Errorf("creating temp profile file: %v", err) +- } +- +- c.s.ongoingProfileMu.Lock() +- defer c.s.ongoingProfileMu.Unlock() +- +- if c.s.ongoingProfile != nil { +- file.Close() // ignore error +- return result, fmt.Errorf("profile already started (for %q)", c.s.ongoingProfile.Name()) +- } +- +- if err := pprof.StartCPUProfile(file); err != nil { +- file.Close() // ignore error +- return result, fmt.Errorf("starting profile: %v", err) +- } +- +- c.s.ongoingProfile = file +- return result, nil +-} +- +-func (c *commandHandler) StopProfile(ctx context.Context, args command.StopProfileArgs) (result command.StopProfileResult, _ error) { +- c.s.ongoingProfileMu.Lock() +- defer c.s.ongoingProfileMu.Unlock() +- +- prof := c.s.ongoingProfile +- c.s.ongoingProfile = nil +- +- if prof == nil { +- return result, fmt.Errorf("no ongoing profile") +- } +- +- pprof.StopCPUProfile() +- if err := prof.Close(); err != nil { +- return result, fmt.Errorf("closing profile file: %v", err) +- } +- result.File = prof.Name() - return result, nil -} - @@ -32238,10 +36688,10 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - Tests bool -} - --func (c *commandHandler) FetchVulncheckResult(ctx context.Context, arg command.URIArg) (map[protocol.DocumentURI]*govulncheck.Result, error) { -- ret := map[protocol.DocumentURI]*govulncheck.Result{} +-func (c *commandHandler) FetchVulncheckResult(ctx context.Context, arg command.URIArg) (map[protocol.DocumentURI]*vulncheck.Result, error) { +- ret := map[protocol.DocumentURI]*vulncheck.Result{} - err := c.run(ctx, commandConfig{forURI: arg.URI}, func(ctx context.Context, deps commandDeps) error { -- if deps.snapshot.View().Options().Vulncheck == source.ModeVulncheckImports { +- if deps.snapshot.Options().Vulncheck == source.ModeVulncheckImports { - for _, modfile := range deps.snapshot.ModFiles() { - res, err := deps.snapshot.ModVuln(ctx, modfile) - if err != nil { @@ -32278,60 +36728,22 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - }, func(ctx context.Context, deps commandDeps) error { - tokenChan <- deps.work.Token() - -- view := deps.snapshot.View() -- opts := view.Options() -- // quickly test if gopls is compiled to support govulncheck -- // by checking vulncheck.Main. Alternatively, we can continue and -- // let the `gopls vulncheck` command fail. This is lighter-weight. -- if vulncheck.Main == nil { -- return errors.New("vulncheck feature is not available") -- } -- -- cmd := exec.CommandContext(ctx, os.Args[0], "vulncheck", "-config", args.Pattern) -- cmd.Dir = filepath.Dir(args.URI.SpanURI().Filename()) -- -- var viewEnv []string -- if e := opts.EnvSlice(); e != nil { -- viewEnv = append(os.Environ(), e...) -- } -- cmd.Env = viewEnv -- -- // stdin: gopls vulncheck expects JSON-encoded configuration from STDIN when -config flag is set. -- var stdin bytes.Buffer -- cmd.Stdin = &stdin +- workDoneWriter := progress.NewWorkDoneWriter(ctx, deps.work) +- dir := filepath.Dir(args.URI.SpanURI().Filename()) +- pattern := args.Pattern - -- if err := json.NewEncoder(&stdin).Encode(pkgLoadConfig{ -- BuildFlags: opts.BuildFlags, -- // TODO(hyangah): add `tests` flag in command.VulncheckArgs -- }); err != nil { -- return fmt.Errorf("failed to pass package load config: %v", err) -- } -- -- // stderr: stream gopls vulncheck's STDERR as progress reports -- er := progress.NewEventWriter(ctx, "vulncheck") -- stderr := io.MultiWriter(er, progress.NewWorkDoneWriter(ctx, deps.work)) -- cmd.Stderr = stderr -- // TODO: can we stream stdout? -- stdout, err := cmd.Output() +- result, err := scan.RunGovulncheck(ctx, pattern, deps.snapshot, dir, workDoneWriter) - if err != nil { -- return fmt.Errorf("failed to run govulncheck: %v", err) +- return err - } - -- var result govulncheck.Result -- if err := json.Unmarshal(stdout, &result); err != nil { -- // TODO: for easy debugging, log the failed stdout somewhere? -- return fmt.Errorf("failed to parse govulncheck output: %v", err) -- } -- result.Mode = govulncheck.ModeGovulncheck -- result.AsOf = time.Now() -- deps.snapshot.View().SetVulnerabilities(args.URI.SpanURI(), &result) +- deps.snapshot.View().SetVulnerabilities(args.URI.SpanURI(), result) +- c.s.diagnoseSnapshot(deps.snapshot, nil, false, 0) - -- c.s.diagnoseSnapshot(deps.snapshot, nil, false) -- vulns := result.Vulns -- affecting := make([]string, 0, len(vulns)) -- for _, v := range vulns { -- if v.IsCalled() { -- affecting = append(affecting, v.OSV.ID) +- affecting := make(map[string]bool, len(result.Entries)) +- for _, finding := range result.Findings { +- if len(finding.Trace) > 1 { // at least 2 frames if callstack exists (vulnerability, entry) +- affecting[finding.OSV] = true - } - } - if len(affecting) == 0 { @@ -32340,10 +36752,14 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - Message: "No vulnerabilities found", - }) - } -- sort.Strings(affecting) +- affectingOSVs := make([]string, 0, len(affecting)) +- for id := range affecting { +- affectingOSVs = append(affectingOSVs, id) +- } +- sort.Strings(affectingOSVs) - return c.s.client.ShowMessage(ctx, &protocol.ShowMessageParams{ - Type: protocol.Warning, -- Message: fmt.Sprintf("Found %v", strings.Join(affecting, ", ")), +- Message: fmt.Sprintf("Found %v", strings.Join(affectingOSVs, ", ")), - }) - }) - if err != nil { @@ -32367,14 +36783,216 @@ diff -urN a/gopls/internal/lsp/command.go b/gopls/internal/lsp/command.go - var m runtime.MemStats - runtime.ReadMemStats(&m) - return command.MemStatsResult{ -- HeapAlloc: m.HeapAlloc, -- HeapInUse: m.HeapInuse, +- HeapAlloc: m.HeapAlloc, +- HeapInUse: m.HeapInuse, +- TotalAlloc: m.TotalAlloc, - }, nil -} +- +-// WorkspaceStats implements the WorkspaceStats command, reporting information +-// about the current state of the loaded workspace for the current session. +-func (c *commandHandler) WorkspaceStats(ctx context.Context) (command.WorkspaceStatsResult, error) { +- var res command.WorkspaceStatsResult +- res.Files.Total, res.Files.Largest, res.Files.Errs = c.s.session.Cache().FileStats() +- +- for _, view := range c.s.session.Views() { +- vs, err := collectViewStats(ctx, view) +- if err != nil { +- return res, err +- } +- res.Views = append(res.Views, vs) +- } +- return res, nil +-} +- +-func collectViewStats(ctx context.Context, view *cache.View) (command.ViewStats, error) { +- s, release, err := view.Snapshot() +- if err != nil { +- return command.ViewStats{}, err +- } +- defer release() +- +- allMD, err := s.AllMetadata(ctx) +- if err != nil { +- return command.ViewStats{}, err +- } +- allPackages := collectPackageStats(allMD) +- +- wsMD, err := s.WorkspaceMetadata(ctx) +- if err != nil { +- return command.ViewStats{}, err +- } +- workspacePackages := collectPackageStats(wsMD) +- +- var ids []source.PackageID +- for _, m := range wsMD { +- ids = append(ids, m.ID) +- } +- +- diags, err := s.PackageDiagnostics(ctx, ids...) +- if err != nil { +- return command.ViewStats{}, err +- } +- +- ndiags := 0 +- for _, d := range diags { +- ndiags += len(d) +- } +- +- return command.ViewStats{ +- GoCommandVersion: view.GoVersionString(), +- AllPackages: allPackages, +- WorkspacePackages: workspacePackages, +- Diagnostics: ndiags, +- }, nil +-} +- +-func collectPackageStats(md []*source.Metadata) command.PackageStats { +- var stats command.PackageStats +- stats.Packages = len(md) +- modules := make(map[string]bool) +- +- for _, m := range md { +- n := len(m.CompiledGoFiles) +- stats.CompiledGoFiles += n +- if n > stats.LargestPackage { +- stats.LargestPackage = n +- } +- if m.Module != nil { +- modules[m.Module.Path] = true +- } +- } +- stats.Modules = len(modules) +- +- return stats +-} +- +-// RunGoWorkCommand invokes `go work ` with the provided arguments. +-// +-// args.InitFirst controls whether to first run `go work init`. This allows a +-// single command to both create and recursively populate a go.work file -- as +-// of writing there is no `go work init -r`. +-// +-// Some thought went into implementing this command. Unlike the go.mod commands +-// above, this command simply invokes the go command and relies on the client +-// to notify gopls of file changes via didChangeWatchedFile notifications. +-// We could instead run these commands with GOWORK set to a temp file, but that +-// poses the following problems: +-// - directory locations in the resulting temp go.work file will be computed +-// relative to the directory containing that go.work. If the go.work is in a +-// tempdir, the directories will need to be translated to/from that dir. +-// - it would be simpler to use a temp go.work file in the workspace +-// directory, or whichever directory contains the real go.work file, but +-// that sets a bad precedent of writing to a user-owned directory. We +-// shouldn't start doing that. +-// - Sending workspace edits to create a go.work file would require using +-// the CreateFile resource operation, which would need to be tested in every +-// client as we haven't used it before. We don't have time for that right +-// now. +-// +-// Therefore, we simply require that the current go.work file is saved (if it +-// exists), and delegate to the go command. +-func (c *commandHandler) RunGoWorkCommand(ctx context.Context, args command.RunGoWorkArgs) error { +- return c.run(ctx, commandConfig{ +- progress: "Running go work command", +- forView: args.ViewID, +- }, func(ctx context.Context, deps commandDeps) (runErr error) { +- snapshot := deps.snapshot +- view := snapshot.View().(*cache.View) +- viewDir := view.Folder().Filename() +- +- // If the user has explicitly set GOWORK=off, we should warn them +- // explicitly and avoid potentially misleading errors below. +- goworkURI, off := view.GOWORK() +- if off { +- return fmt.Errorf("cannot modify go.work files when GOWORK=off") +- } +- gowork := goworkURI.Filename() +- +- if goworkURI != "" { +- fh, err := snapshot.ReadFile(ctx, goworkURI) +- if err != nil { +- return fmt.Errorf("reading current go.work file: %v", err) +- } +- if !fh.SameContentsOnDisk() { +- return fmt.Errorf("must save workspace file %s before running go work commands", goworkURI) +- } +- } else { +- if !args.InitFirst { +- // If go.work does not exist, we should have detected that and asked +- // for InitFirst. +- return bug.Errorf("internal error: cannot run go work command: required go.work file not found") +- } +- gowork = filepath.Join(viewDir, "go.work") +- if err := c.invokeGoWork(ctx, viewDir, gowork, []string{"init"}); err != nil { +- return fmt.Errorf("running `go work init`: %v", err) +- } +- } +- +- return c.invokeGoWork(ctx, viewDir, gowork, args.Args) +- }) +-} +- +-func (c *commandHandler) invokeGoWork(ctx context.Context, viewDir, gowork string, args []string) error { +- inv := gocommand.Invocation{ +- Verb: "work", +- Args: args, +- WorkingDir: viewDir, +- Env: append(os.Environ(), fmt.Sprintf("GOWORK=%s", gowork)), +- } +- if _, err := c.s.session.GoCommandRunner().Run(ctx, inv); err != nil { +- return fmt.Errorf("running go work command: %v", err) +- } +- return nil +-} +- +-// openClientBrowser causes the LSP client to open the specified URL +-// in an external browser. +-func openClientBrowser(ctx context.Context, cli protocol.Client, url protocol.URI) { +- showDocumentImpl(ctx, cli, url, nil) +-} +- +-// openClientEditor causes the LSP client to open the specified document +-// and select the indicated range. +-func openClientEditor(ctx context.Context, cli protocol.Client, loc protocol.Location) { +- showDocumentImpl(ctx, cli, protocol.URI(loc.URI), &loc.Range) +-} +- +-func showDocumentImpl(ctx context.Context, cli protocol.Client, url protocol.URI, rangeOpt *protocol.Range) { +- // In principle we shouldn't send a showDocument request to a +- // client that doesn't support it, as reported by +- // ShowDocumentClientCapabilities. But even clients that do +- // support it may defer the real work of opening the document +- // asynchronously, to avoid deadlocks due to rentrancy. +- // +- // For example: client sends request to server; server sends +- // showDocument to client; client opens editor; editor causes +- // new RPC to be sent to server, which is still busy with +- // previous request. (This happens in eglot.) +- // +- // So we can't rely on the success/failure information. +- // That's the reason this function doesn't return an error. +- +- // "External" means run the system-wide handler (e.g. open(1) +- // on macOS or xdg-open(1) on Linux) for this URL, ignoring +- // TakeFocus and Selection. Note that this may still end up +- // opening the same editor (e.g. VSCode) for a file: URL. +- res, err := cli.ShowDocument(ctx, &protocol.ShowDocumentParams{ +- URI: url, +- External: rangeOpt == nil, +- TakeFocus: true, +- Selection: rangeOpt, // optional +- }) +- if err != nil { +- event.Error(ctx, "client.showDocument: %v", err) +- } else if res != nil && !res.Success { +- event.Log(ctx, fmt.Sprintf("client declined to open document %v", url)) +- } +-} diff -urN a/gopls/internal/lsp/completion.go b/gopls/internal/lsp/completion.go --- a/gopls/internal/lsp/completion.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/completion.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,140 +0,0 @@ ++++ b/gopls/internal/lsp/completion.go 1970-01-01 08:00:00 +@@ -1,149 +0,0 @@ -// Copyright 2018 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. @@ -32391,11 +37009,20 @@ diff -urN a/gopls/internal/lsp/completion.go b/gopls/internal/lsp/completion.go - "golang.org/x/tools/gopls/internal/lsp/source/completion" - "golang.org/x/tools/gopls/internal/lsp/template" - "golang.org/x/tools/gopls/internal/lsp/work" +- "golang.org/x/tools/gopls/internal/telemetry" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/event/tag" -) - --func (s *Server) completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) { +-func (s *Server) completion(ctx context.Context, params *protocol.CompletionParams) (_ *protocol.CompletionList, rerr error) { +- recordLatency := telemetry.StartLatencyTimer("completion") +- defer func() { +- recordLatency(ctx, rerr) +- }() +- +- ctx, done := event.Start(ctx, "lsp.Server.completion", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) - defer release() - if !ok { @@ -32403,7 +37030,7 @@ diff -urN a/gopls/internal/lsp/completion.go b/gopls/internal/lsp/completion.go - } - var candidates []completion.CompletionItem - var surrounding *completion.Selection -- switch snapshot.View().FileKind(fh) { +- switch snapshot.FileKind(fh) { - case source.Go: - candidates, surrounding, err = completion.Completion(ctx, snapshot, fh, params.Position, params.Context) - case source.Mod: @@ -32439,7 +37066,7 @@ diff -urN a/gopls/internal/lsp/completion.go b/gopls/internal/lsp/completion.go - - // When using deep completions/fuzzy matching, report results as incomplete so - // client fetches updated completions after every key stroke. -- options := snapshot.View().Options() +- options := snapshot.Options() - incompleteResults := options.DeepCompletion || options.Matcher == source.Fuzzy - - items := toProtocolCompletionItems(candidates, rng, options) @@ -32508,7 +37135,7 @@ diff -urN a/gopls/internal/lsp/completion.go b/gopls/internal/lsp/completion.go - - Preselect: i == 0, - Documentation: doc, -- Tags: candidate.Tags, +- Tags: nonNilSliceCompletionItemTag(candidate.Tags), - Deprecated: candidate.Deprecated, - } - items = append(items, item) @@ -32517,8 +37144,8 @@ diff -urN a/gopls/internal/lsp/completion.go b/gopls/internal/lsp/completion.go -} diff -urN a/gopls/internal/lsp/completion_test.go b/gopls/internal/lsp/completion_test.go --- a/gopls/internal/lsp/completion_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/completion_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,176 +0,0 @@ ++++ b/gopls/internal/lsp/completion_test.go 1970-01-01 08:00:00 +@@ -1,173 +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. @@ -32570,15 +37197,6 @@ diff -urN a/gopls/internal/lsp/completion_test.go b/gopls/internal/lsp/completio - } -} - --func (r *runner) UnimportedCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { -- got := r.callCompletion(t, src, func(opts *source.Options) {}) -- got = tests.FilterBuiltins(src, got) -- want := expected(t, test, items) -- if diff := tests.CheckCompletionOrder(want, got, false); diff != "" { -- t.Errorf("%s", diff) -- } --} -- -func (r *runner) DeepCompletion(t *testing.T, src span.Span, test tests.Completion, items tests.CompletionItems) { - got := r.callCompletion(t, src, func(opts *source.Options) { - opts.DeepCompletion = true @@ -32639,6 +37257,7 @@ diff -urN a/gopls/internal/lsp/completion_test.go b/gopls/internal/lsp/completio - Label: item.Label, - Kind: item.Kind, - Detail: item.Detail, +- Tags: []protocol.CompletionItemTag{}, // must be a slice - Documentation: &protocol.Or_CompletionItem_documentation{ - Value: item.Documentation, - }, @@ -32664,20 +37283,8 @@ diff -urN a/gopls/internal/lsp/completion_test.go b/gopls/internal/lsp/completio - -func (r *runner) callCompletion(t *testing.T, src span.Span, options func(*source.Options)) []protocol.CompletionItem { - t.Helper() -- -- view, err := r.server.session.ViewOf(src.URI()) -- if err != nil { -- t.Fatal(err) -- } -- original := view.Options() -- modified := view.Options().Clone() -- options(modified) -- view, err = r.server.session.SetViewOptions(r.ctx, view, modified) -- if err != nil { -- t.Error(err) -- return nil -- } -- defer r.server.session.SetViewOptions(r.ctx, view, original) +- cleanup := r.toggleOptions(t, src.URI(), options) +- defer cleanup() - - list, err := r.server.Completion(r.ctx, &protocol.CompletionParams{ - TextDocumentPositionParams: protocol.TextDocumentPositionParams{ @@ -32695,226 +37302,27 @@ diff -urN a/gopls/internal/lsp/completion_test.go b/gopls/internal/lsp/completio - } - return list.Items -} -diff -urN a/gopls/internal/lsp/debounce.go b/gopls/internal/lsp/debounce.go ---- a/gopls/internal/lsp/debounce.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debounce.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,71 +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 lsp -- --import ( -- "sync" -- "time" --) -- --type debounceEvent struct { -- order uint64 -- done chan struct{} --} -- --type debouncer struct { -- mu sync.Mutex -- events map[string]*debounceEvent --} -- --func newDebouncer() *debouncer { -- return &debouncer{ -- events: make(map[string]*debounceEvent), -- } --} - --// debounce returns a channel that receives a boolean reporting whether, --// by the time the delay channel receives a value, this call is (or will be) --// the most recent call with the highest order number for its key. --func (d *debouncer) debounce(key string, order uint64, delay <-chan time.Time) <-chan bool { -- okc := make(chan bool, 1) -- -- d.mu.Lock() -- if prev, ok := d.events[key]; ok { -- if prev.order > order { -- // If we have a logical ordering of events (as is the case for snapshots), -- // don't overwrite a later event with an earlier event. -- d.mu.Unlock() -- okc <- false -- return okc -- } -- close(prev.done) -- } -- done := make(chan struct{}) -- next := &debounceEvent{ -- order: order, -- done: done, -- } -- d.events[key] = next -- d.mu.Unlock() -- -- go func() { -- ok := false -- select { -- case <-delay: -- d.mu.Lock() -- if d.events[key] == next { -- ok = true -- delete(d.events, key) -- } else { -- // The event was superseded before we acquired d.mu. -- } -- d.mu.Unlock() -- case <-done: -- } -- okc <- ok -- }() -- -- return okc --} -diff -urN a/gopls/internal/lsp/debounce_test.go b/gopls/internal/lsp/debounce_test.go ---- a/gopls/internal/lsp/debounce_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debounce_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,81 +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 lsp -- --import ( -- "testing" -- "time" --) -- --func TestDebouncer(t *testing.T) { -- t.Parallel() -- -- type event struct { -- key string -- order uint64 -- wantFired bool -- } -- tests := []struct { -- label string -- events []*event -- }{ -- { -- label: "overridden", -- events: []*event{ -- {key: "a", order: 1, wantFired: false}, -- {key: "a", order: 2, wantFired: true}, -- }, -- }, -- { -- label: "distinct labels", -- events: []*event{ -- {key: "a", order: 1, wantFired: true}, -- {key: "b", order: 2, wantFired: true}, -- }, -- }, -- { -- label: "reverse order", -- events: []*event{ -- {key: "a", order: 2, wantFired: true}, -- {key: "a", order: 1, wantFired: false}, -- }, -- }, -- { -- label: "multiple overrides", -- events: []*event{ -- {key: "a", order: 1, wantFired: false}, -- {key: "a", order: 2, wantFired: false}, -- {key: "a", order: 3, wantFired: false}, -- {key: "a", order: 4, wantFired: false}, -- {key: "a", order: 5, wantFired: true}, -- }, -- }, +-func (r *runner) toggleOptions(t *testing.T, uri span.URI, options func(*source.Options)) (reset func()) { +- view, err := r.server.session.ViewOf(uri) +- if err != nil { +- t.Fatal(err) - } -- for _, test := range tests { -- test := test -- t.Run(test.label, func(t *testing.T) { -- d := newDebouncer() -- -- delays := make([]chan time.Time, len(test.events)) -- okcs := make([]<-chan bool, len(test.events)) +- folder := view.Folder() - -- // Register the events in deterministic order, synchronously. -- for i, e := range test.events { -- delays[i] = make(chan time.Time, 1) -- okcs[i] = d.debounce(e.key, e.order, delays[i]) -- } -- -- // Now see which event fired. -- for i, okc := range okcs { -- event := test.events[i] -- delays[i] <- time.Now() -- fired := <-okc -- if fired != event.wantFired { -- t.Errorf("[key: %q, order: %d]: fired = %t, want %t", event.key, event.order, fired, event.wantFired) -- } -- } -- }) +- modified := r.server.Options().Clone() +- options(modified) +- if err = r.server.session.SetFolderOptions(r.ctx, folder, modified); err != nil { +- t.Fatal(err) - } --} -diff -urN a/gopls/internal/lsp/debug/buildinfo_go1.12.go b/gopls/internal/lsp/debug/buildinfo_go1.12.go ---- a/gopls/internal/lsp/debug/buildinfo_go1.12.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debug/buildinfo_go1.12.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,29 +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 debug -- --import ( -- "runtime" -- "runtime/debug" --) -- --type BuildInfo struct { -- debug.BuildInfo -- GoVersion string // Version of Go that produced this binary --} -- --func readBuildInfo() (*BuildInfo, bool) { -- rinfo, ok := debug.ReadBuildInfo() -- if !ok { -- return nil, false +- return func() { +- r.server.session.SetFolderOptions(r.ctx, folder, r.server.Options()) - } -- return &BuildInfo{ -- GoVersion: runtime.Version(), -- BuildInfo: *rinfo, -- }, true --} -diff -urN a/gopls/internal/lsp/debug/buildinfo_go1.18.go b/gopls/internal/lsp/debug/buildinfo_go1.18.go ---- a/gopls/internal/lsp/debug/buildinfo_go1.18.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debug/buildinfo_go1.18.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,19 +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 debug -- --import ( -- "runtime/debug" --) -- --type BuildInfo debug.BuildInfo -- --func readBuildInfo() (*BuildInfo, bool) { -- info, ok := debug.ReadBuildInfo() -- return (*BuildInfo)(info), ok -} diff -urN a/gopls/internal/lsp/debug/info.go b/gopls/internal/lsp/debug/info.go --- a/gopls/internal/lsp/debug/info.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debug/info.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,254 +0,0 @@ ++++ b/gopls/internal/lsp/debug/info.go 1970-01-01 08:00:00 +@@ -1,258 +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. @@ -32927,6 +37335,7 @@ diff -urN a/gopls/internal/lsp/debug/info.go b/gopls/internal/lsp/debug/info.go - "encoding/json" - "fmt" - "io" +- "os" - "reflect" - "runtime" - "runtime/debug" @@ -32946,12 +37355,19 @@ diff -urN a/gopls/internal/lsp/debug/info.go b/gopls/internal/lsp/debug/info.go -) - -// Version is a manually-updated mechanism for tracking versions. --const Version = "master" +-func Version() string { +- if info, ok := debug.ReadBuildInfo(); ok { +- if info.Main.Version != "" { +- return info.Main.Version +- } +- } +- return "(unknown)" +-} - -// ServerVersion is the format used by gopls to report its version to the -// client. This format is structured so that the client can parse it easily. -type ServerVersion struct { -- *BuildInfo +- *debug.BuildInfo - Version string -} - @@ -32959,23 +37375,18 @@ diff -urN a/gopls/internal/lsp/debug/info.go b/gopls/internal/lsp/debug/info.go -// built in module mode, we return a GOPATH-specific message with the -// hardcoded version. -func VersionInfo() *ServerVersion { -- if info, ok := readBuildInfo(); ok { -- return getVersion(info) -- } -- buildInfo := &BuildInfo{} -- // go1.17 or earlier, part of s.BuildInfo are embedded fields. -- buildInfo.Path = "gopls, built in GOPATH mode" -- buildInfo.GoVersion = runtime.Version() -- return &ServerVersion{ -- Version: Version, -- BuildInfo: buildInfo, +- if info, ok := debug.ReadBuildInfo(); ok { +- return &ServerVersion{ +- Version: Version(), +- BuildInfo: info, +- } - } --} -- --func getVersion(info *BuildInfo) *ServerVersion { - return &ServerVersion{ -- Version: Version, -- BuildInfo: info, +- Version: Version(), +- BuildInfo: &debug.BuildInfo{ +- Path: "gopls, built in GOPATH mode", +- GoVersion: runtime.Version(), +- }, - } -} - @@ -32984,6 +37395,7 @@ diff -urN a/gopls/internal/lsp/debug/info.go b/gopls/internal/lsp/debug/info.go - section(w, HTML, "Server Instance", func() { - fmt.Fprintf(w, "Start time: %v\n", i.StartTime) - fmt.Fprintf(w, "LogFile: %s\n", i.Logfile) +- fmt.Fprintf(w, "pid: %d\n", os.Getpid()) - fmt.Fprintf(w, "Working directory: %s\n", i.Workdir) - fmt.Fprintf(w, "Address: %s\n", i.ServerAddress) - fmt.Fprintf(w, "Debug address: %s\n", i.DebugAddress()) @@ -33040,7 +37452,7 @@ diff -urN a/gopls/internal/lsp/debug/info.go b/gopls/internal/lsp/debug/info.go -} - -func printBuildInfo(w io.Writer, info *ServerVersion, verbose bool, mode PrintMode) { -- fmt.Fprintf(w, "%v %v\n", info.Path, Version) +- fmt.Fprintf(w, "%v %v\n", info.Path, Version()) - printModuleInfo(w, info.Main, mode) - if !verbose { - return @@ -33171,8 +37583,8 @@ diff -urN a/gopls/internal/lsp/debug/info.go b/gopls/internal/lsp/debug/info.go -} diff -urN a/gopls/internal/lsp/debug/info_test.go b/gopls/internal/lsp/debug/info_test.go --- a/gopls/internal/lsp/debug/info_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debug/info_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,47 +0,0 @@ ++++ b/gopls/internal/lsp/debug/info_test.go 1970-01-01 08:00:00 +@@ -1,48 +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. @@ -33202,7 +37614,7 @@ diff -urN a/gopls/internal/lsp/debug/info_test.go b/gopls/internal/lsp/debug/inf - if g, w := got.GoVersion, runtime.Version(); g != w { - t.Errorf("go version = %v, want %v", g, w) - } -- if g, w := got.Version, Version; g != w { +- if g, w := got.Version, Version(); g != w { - t.Errorf("gopls version = %v, want %v", g, w) - } - // Other fields of BuildInfo may not be available during test. @@ -33216,13 +37628,14 @@ diff -urN a/gopls/internal/lsp/debug/info_test.go b/gopls/internal/lsp/debug/inf - res := buf.Bytes() - - // Other fields of BuildInfo may not be available during test. -- if !bytes.Contains(res, []byte(Version)) || !bytes.Contains(res, []byte(runtime.Version())) { -- t.Errorf("plaintext output = %q,\nwant (version: %v, go: %v)", res, Version, runtime.Version()) +- wantGoplsVersion, wantGoVersion := Version(), runtime.Version() +- if !bytes.Contains(res, []byte(wantGoplsVersion)) || !bytes.Contains(res, []byte(wantGoVersion)) { +- t.Errorf("plaintext output = %q,\nwant (version: %v, go: %v)", res, wantGoplsVersion, wantGoVersion) - } -} diff -urN a/gopls/internal/lsp/debug/log/log.go b/gopls/internal/lsp/debug/log/log.go --- a/gopls/internal/lsp/debug/log/log.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debug/log/log.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/debug/log/log.go 1970-01-01 08:00:00 @@ -1,43 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -33269,7 +37682,7 @@ diff -urN a/gopls/internal/lsp/debug/log/log.go b/gopls/internal/lsp/debug/log/l -} diff -urN a/gopls/internal/lsp/debug/metrics.go b/gopls/internal/lsp/debug/metrics.go --- a/gopls/internal/lsp/debug/metrics.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debug/metrics.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/debug/metrics.go 1970-01-01 08:00:00 @@ -1,58 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -33331,7 +37744,7 @@ diff -urN a/gopls/internal/lsp/debug/metrics.go b/gopls/internal/lsp/debug/metri -} diff -urN a/gopls/internal/lsp/debug/rpc.go b/gopls/internal/lsp/debug/rpc.go --- a/gopls/internal/lsp/debug/rpc.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debug/rpc.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/debug/rpc.go 1970-01-01 08:00:00 @@ -1,239 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -33574,8 +37987,8 @@ diff -urN a/gopls/internal/lsp/debug/rpc.go b/gopls/internal/lsp/debug/rpc.go -} diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.go --- a/gopls/internal/lsp/debug/serve.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debug/serve.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,909 +0,0 @@ ++++ b/gopls/internal/lsp/debug/serve.go 1970-01-01 08:00:00 +@@ -1,864 +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. @@ -33583,7 +37996,6 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g -package debug - -import ( -- "archive/zip" - "bytes" - "context" - "errors" @@ -33598,16 +38010,15 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g - "path" - "path/filepath" - "runtime" -- rpprof "runtime/pprof" - "strconv" - "strings" - "sync" - "time" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/cache" - "golang.org/x/tools/gopls/internal/lsp/debug/log" - "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/event/core" - "golang.org/x/tools/internal/event/export" @@ -33685,6 +38096,13 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g - return nil -} - +-// Analysis returns the global Analysis template value. +-func (st *State) Analysis() (_ analysisTmpl) { return } +- +-type analysisTmpl struct{} +- +-func (analysisTmpl) AnalyzerRunTimes() []cache.LabelDuration { return cache.AnalyzerRunTimes() } +- -// Sessions returns the set of Session objects currently being served. -func (st *State) Sessions() []*cache.Session { - var sessions []*cache.Session @@ -33858,6 +38276,10 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g - return i.State.Cache(path.Base(r.URL.Path)) -} - +-func (i *Instance) getAnalysis(r *http.Request) interface{} { +- return i.State.Analysis() +-} +- -func (i *Instance) getSession(r *http.Request) interface{} { - return i.State.Session(path.Base(r.URL.Path)) -} @@ -34030,6 +38452,7 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g - if i.traces != nil { - mux.HandleFunc("/trace/", render(TraceTmpl, i.traces.getData)) - } +- mux.HandleFunc("/analysis/", render(AnalysisTmpl, i.getAnalysis)) - mux.HandleFunc("/cache/", render(CacheTmpl, i.getCache)) - mux.HandleFunc("/session/", render(SessionTmpl, i.getSession)) - mux.HandleFunc("/view/", render(ViewTmpl, i.getView)) @@ -34040,14 +38463,14 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g - mux.HandleFunc("/memory", render(MemoryTmpl, getMemory)) - - // Internal debugging helpers. -- mux.HandleFunc("/_dogc", func(w http.ResponseWriter, r *http.Request) { +- mux.HandleFunc("/gc", func(w http.ResponseWriter, r *http.Request) { - runtime.GC() - runtime.GC() - runtime.GC() -- http.Error(w, "OK", 200) +- http.Redirect(w, r, "/memory", http.StatusTemporaryRedirect) - }) - mux.HandleFunc("/_makeabug", func(w http.ResponseWriter, r *http.Request) { -- bug.Report("bug here", nil) +- bug.Report("bug here") - http.Error(w, "made a bug", http.StatusOK) - }) - @@ -34072,65 +38495,6 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g - return i.listenedDebugAddress -} - --// MonitorMemory starts recording memory statistics each second. --func (i *Instance) MonitorMemory(ctx context.Context) { -- tick := time.NewTicker(time.Second) -- nextThresholdGiB := uint64(1) -- go func() { -- for { -- <-tick.C -- var mem runtime.MemStats -- runtime.ReadMemStats(&mem) -- if mem.HeapAlloc < nextThresholdGiB*1<<30 { -- continue -- } -- if err := i.writeMemoryDebug(nextThresholdGiB, true); err != nil { -- event.Error(ctx, "writing memory debug info", err) -- } -- if err := i.writeMemoryDebug(nextThresholdGiB, false); err != nil { -- event.Error(ctx, "writing memory debug info", err) -- } -- event.Log(ctx, fmt.Sprintf("Wrote memory usage debug info to %v", os.TempDir())) -- nextThresholdGiB++ -- } -- }() --} -- --func (i *Instance) writeMemoryDebug(threshold uint64, withNames bool) error { -- suffix := "withnames" -- if !withNames { -- suffix = "nonames" -- } -- -- filename := fmt.Sprintf("gopls.%d-%dGiB-%s.zip", os.Getpid(), threshold, suffix) -- zipf, err := os.OpenFile(filepath.Join(os.TempDir(), filename), os.O_CREATE|os.O_RDWR, 0644) -- if err != nil { -- return err -- } -- zipw := zip.NewWriter(zipf) -- -- f, err := zipw.Create("heap.pb.gz") -- if err != nil { -- return err -- } -- if err := rpprof.Lookup("heap").WriteTo(f, 0); err != nil { -- return err -- } -- -- f, err = zipw.Create("goroutines.txt") -- if err != nil { -- return err -- } -- if err := rpprof.Lookup("goroutine").WriteTo(f, 1); err != nil { -- return err -- } -- -- if err := zipw.Close(); err != nil { -- return err -- } -- return zipf.Close() --} -- -func makeGlobalExporter(stderr io.Writer) event.Exporter { - p := export.Printer{} - var pMu sync.Mutex @@ -34275,10 +38639,10 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g -td.value { - text-align: right; -} --ul.events { -- list-style-type: none; +-ul.spans { +- font-family: monospace; +- font-size: 85%; -} -- - -{{block "head" .}}{{end}} - @@ -34286,9 +38650,11 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g -Main -Info -Memory +-Profiling -Metrics -RPC -Trace +-Analysis -
-

{{template "title" .}}

-{{block "body" .}} @@ -34326,9 +38692,10 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g - } - return s - }, -- "options": func(s *cache.Session) []sessionOption { -- return showOptions(s.Options()) -- }, +- // TODO(rfindley): re-enable option inspection. +- // "options": func(s *cache.Session) []sessionOption { +- // return showOptions(s.Options()) +- // }, -}) - -var MainTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(` @@ -34355,9 +38722,10 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g -`)) - -var MemoryTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(` --{{define "title"}}GoPls memory usage{{end}} +-{{define "title"}}Gopls memory usage{{end}} -{{define "head"}}{{end}} -{{define "body"}} +-
-

Stats

- - @@ -34399,6 +38767,14 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g -{{end}} -`)) - +-var AnalysisTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(` +-{{define "title"}}Analysis{{end}} +-{{define "body"}} +-

Analyzer.Run times

+-
    {{range .AnalyzerRunTimes}}
  • {{.Duration}} {{.Label}}
  • {{end}}
+-{{end}} +-`)) +- -var ClientTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(` -{{define "title"}}Client {{.Session.ID}}{{end}} -{{define "body"}} @@ -34453,12 +38829,6 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g -
  • -{{.FileIdentity.URI}} -
  • {{end}} --

    Options

    --{{range options .}} --

    {{.Name}} {{.Type}}

    --

    default: {{.Default}}

    --{{if ne .Default .Current}}

    current: {{.Current}}

    {{end}} --{{end}} -{{end}} -`)) - @@ -34467,8 +38837,6 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g -{{define "body"}} -Name: {{.Name}}
    -Folder: {{.Folder}}
    --

    Environment

    --
      {{range .Options.Env}}
    • {{.}}
    • {{end}}
    -{{end}} -`)) - @@ -34482,13 +38850,13 @@ diff -urN a/gopls/internal/lsp/debug/serve.go b/gopls/internal/lsp/debug/serve.g - Kind: {{.Kind}}
    -{{end}} -

    Contents

    --
    {{fcontent .Read}}
    +-
    {{fcontent .Content}}
    -{{end}} -`)) diff -urN a/gopls/internal/lsp/debug/trace.go b/gopls/internal/lsp/debug/trace.go --- a/gopls/internal/lsp/debug/trace.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/debug/trace.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,233 +0,0 @@ ++++ b/gopls/internal/lsp/debug/trace.go 1970-01-01 08:00:00 +@@ -1,320 +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. @@ -34513,60 +38881,117 @@ diff -urN a/gopls/internal/lsp/debug/trace.go b/gopls/internal/lsp/debug/trace.g - "golang.org/x/tools/internal/event/label" -) - +-// TraceTmpl extends BaseTemplate and renders a TraceResults, e.g. from getData(). -var TraceTmpl = template.Must(template.Must(BaseTemplate.Clone()).Parse(` -{{define "title"}}Trace Information{{end}} -{{define "body"}} - {{range .Traces}}{{.Name}} last: {{.Last.Duration}}, longest: {{.Longest.Duration}}
    {{end}} - {{if .Selected}} -

    {{.Selected.Name}}

    -- {{if .Selected.Last}}

    Last

      {{template "details" .Selected.Last}}
    {{end}} -- {{if .Selected.Longest}}

    Longest

      {{template "details" .Selected.Longest}}
    {{end}} +- {{if .Selected.Last}}

    Last

      {{template "completeSpan" .Selected.Last}}
    {{end}} +- {{if .Selected.Longest}}

    Longest

      {{template "completeSpan" .Selected.Longest}}
    {{end}} - {{end}} +- +-

    Recent spans (oldest first)

    +-

    +- A finite number of recent span start/end times are shown below. +- The nesting represents the children of a parent span (and the log events within a span). +- A span may appear twice: chronologically at toplevel, and nested within its parent. +-

    +-
      {{range .Recent}}{{template "spanStartEnd" .}}{{end}}
    -{{end}} --{{define "details"}} --
  • {{.Offset}} {{.Name}} {{.Duration}} {{.Tags}}
  • -- {{if .Events}}
      {{range .Events}}
    • {{.Offset}} {{.Tags}}
    • {{end}}
    {{end}} -- {{if .Children}}
      {{range .Children}}{{template "details" .}}{{end}}
    {{end}} +-{{define "spanStartEnd"}} +- {{if .Start}} +-
  • {{.Span.Header .Start}}
  • +- {{else}} +- {{template "completeSpan" .Span}} +- {{end}} +-{{end}} +-{{define "completeSpan"}} +-
  • {{.Header false}}
  • +- {{if .Events}}
      {{range .Events}}
    • {{.Header}}
    • {{end}}
    {{end}} +- {{if .ChildStartEnd}}
      {{range .ChildStartEnd}}{{template "spanStartEnd" .}}{{end}}
    {{end}} -{{end}} -`)) - -type traces struct { -- mu sync.Mutex -- sets map[string]*traceSet -- unfinished map[export.SpanContext]*traceData +- mu sync.Mutex +- sets map[string]*traceSet +- unfinished map[export.SpanContext]*traceSpan +- recent []spanStartEnd +- recentEvictions int -} - +-// A spanStartEnd records the start or end of a span. +-// If Start, the span may be unfinished, so some fields (e.g. Finish) +-// may be unset and others (e.g. Events) may be being actively populated. +-type spanStartEnd struct { +- Start bool +- Span *traceSpan +-} +- +-func (ev spanStartEnd) Time() time.Time { +- if ev.Start { +- return ev.Span.Start +- } else { +- return ev.Span.Finish +- } +-} +- +-// A TraceResults is the subject for the /trace HTML template. -type TraceResults struct { // exported for testing - Traces []*traceSet - Selected *traceSet +- Recent []spanStartEnd -} - +-// A traceSet holds two representative spans of a given span name. -type traceSet struct { - Name string -- Last *traceData -- Longest *traceData --} -- --type traceData struct { -- TraceID export.TraceID -- SpanID export.SpanID -- ParentID export.SpanID -- Name string -- Start time.Time -- Finish time.Time -- Offset time.Duration -- Duration time.Duration -- Tags string -- Events []traceEvent -- Children []*traceData +- Last *traceSpan +- Longest *traceSpan +-} +- +-// A traceSpan holds information about a single span. +-type traceSpan struct { +- TraceID export.TraceID +- SpanID export.SpanID +- ParentID export.SpanID +- Name string +- Start time.Time +- Finish time.Time // set at end +- Duration time.Duration // set at end +- Tags string +- Events []traceEvent // set at end +- ChildStartEnd []spanStartEnd // populated while active +- +- parent *traceSpan +-} +- +-const timeFormat = "15:04:05.000" +- +-// Header renders the time, name, tags, and (if !start), +-// duration of a span start or end event. +-func (span *traceSpan) Header(start bool) string { +- if start { +- return fmt.Sprintf("%s start %s %s", +- span.Start.Format(timeFormat), span.Name, span.Tags) +- } else { +- return fmt.Sprintf("%s end %s (+%s) %s", +- span.Finish.Format(timeFormat), span.Name, span.Duration, span.Tags) +- } -} - -type traceEvent struct { - Time time.Time -- Offset time.Duration +- Offset time.Duration // relative to start of span - Tags string -} - +-func (ev traceEvent) Header() string { +- return fmt.Sprintf("%s event (+%s) %s", ev.Time.Format(timeFormat), ev.Offset, ev.Tags) +-} +- -func StdTrace(exporter event.Exporter) event.Exporter { - return func(ctx context.Context, ev core.Event, lm label.Map) context.Context { - span := export.GetSpan(ctx) @@ -34619,7 +39044,7 @@ diff -urN a/gopls/internal/lsp/debug/trace.go b/gopls/internal/lsp/debug/trace.g - case event.IsStart(ev): - // Just starting: add it to the unfinished map. - // Allocate before the critical section. -- td := &traceData{ +- td := &traceSpan{ - TraceID: span.ID.TraceID, - SpanID: span.ID.SpanID, - ParentID: span.ParentID, @@ -34630,22 +39055,23 @@ diff -urN a/gopls/internal/lsp/debug/trace.go b/gopls/internal/lsp/debug/trace.g - - t.mu.Lock() - defer t.mu.Unlock() +- +- t.addRecentLocked(td, true) // add start event +- - if t.sets == nil { - t.sets = make(map[string]*traceSet) -- t.unfinished = make(map[export.SpanContext]*traceData) +- t.unfinished = make(map[export.SpanContext]*traceSpan) - } - t.unfinished[span.ID] = td -- // and wire up parents if we have them -- if !span.ParentID.IsValid() { -- return ctx -- } -- parentID := export.SpanContext{TraceID: span.ID.TraceID, SpanID: span.ParentID} -- parent, found := t.unfinished[parentID] -- if !found { -- // trace had an invalid parent, so it cannot itself be valid -- return ctx +- +- // Wire up parents if we have them. +- if span.ParentID.IsValid() { +- parentID := export.SpanContext{TraceID: span.ID.TraceID, SpanID: span.ParentID} +- if parent, ok := t.unfinished[parentID]; ok { +- td.parent = parent +- parent.ChildStartEnd = append(parent.ChildStartEnd, spanStartEnd{true, td}) +- } - } -- parent.Children = append(parent.Children, td) - - case event.IsEnd(ev): - // Finishing: must be already in the map. @@ -34666,10 +39092,10 @@ diff -urN a/gopls/internal/lsp/debug/trace.go b/gopls/internal/lsp/debug/trace.g - return ctx // if this happens we are in a bad place - } - delete(t.unfinished, span.ID) -- - td.Finish = span.Finish().At() - td.Duration = span.Finish().At().Sub(span.Start().At()) - td.Events = tdEvents +- t.addRecentLocked(td, false) // add end event - - set, ok := t.sets[span.Name] - if !ok { @@ -34680,43 +39106,72 @@ diff -urN a/gopls/internal/lsp/debug/trace.go b/gopls/internal/lsp/debug/trace.g - if set.Longest == nil || set.Last.Duration > set.Longest.Duration { - set.Longest = set.Last - } -- if !td.ParentID.IsValid() { +- if td.parent != nil { +- td.parent.ChildStartEnd = append(td.parent.ChildStartEnd, spanStartEnd{false, td}) +- } else { - fillOffsets(td, td.Start) - } - } - return ctx -} - --func (t *traces) getData(req *http.Request) interface{} { -- if len(t.sets) == 0 { -- return nil +-// addRecentLocked appends a start or end event to the "recent" log, +-// evicting an old entry if necessary. +-func (t *traces) addRecentLocked(span *traceSpan, start bool) { +- t.recent = append(t.recent, spanStartEnd{Start: start, Span: span}) +- +- const maxRecent = 100 // number of log entries before eviction +- for len(t.recent) > maxRecent { +- t.recent[0] = spanStartEnd{} // aid GC +- t.recent = t.recent[1:] +- t.recentEvictions++ +- +- // Using a slice as a FIFO queue leads to unbounded growth +- // as Go's GC cannot collect the ever-growing unused prefix. +- // So, compact it periodically. +- if t.recentEvictions%maxRecent == 0 { +- t.recent = append([]spanStartEnd(nil), t.recent...) +- } - } -- data := TraceResults{} -- data.Traces = make([]*traceSet, 0, len(t.sets)) +-} +- +-// getData returns the TraceResults rendered by TraceTmpl for the /trace[/name] endpoint. +-func (t *traces) getData(req *http.Request) interface{} { +- // TODO(adonovan): the HTTP request doesn't acquire the mutex +- // for t or for each span! Audit and fix. +- +- // Sort last/longest sets by name. +- traces := make([]*traceSet, 0, len(t.sets)) - for _, set := range t.sets { -- data.Traces = append(data.Traces, set) +- traces = append(traces, set) - } -- sort.Slice(data.Traces, func(i, j int) bool { return data.Traces[i].Name < data.Traces[j].Name }) -- if bits := strings.SplitN(req.URL.Path, "/trace/", 2); len(bits) > 1 { -- data.Selected = t.sets[bits[1]] +- sort.Slice(traces, func(i, j int) bool { +- return traces[i].Name < traces[j].Name +- }) +- +- return TraceResults{ +- Traces: traces, +- Selected: t.sets[strings.TrimPrefix(req.URL.Path, "/trace/")], // may be nil +- Recent: t.recent, - } -- return data -} - --func fillOffsets(td *traceData, start time.Time) { -- td.Offset = td.Start.Sub(start) +-func fillOffsets(td *traceSpan, start time.Time) { - for i := range td.Events { - td.Events[i].Offset = td.Events[i].Time.Sub(start) - } -- for _, child := range td.Children { -- fillOffsets(child, start) +- for _, child := range td.ChildStartEnd { +- if !child.Start { +- fillOffsets(child.Span, start) +- } - } -} - -func renderLabels(labels label.List) string { - buf := &bytes.Buffer{} - for index := 0; labels.Valid(index); index++ { -- if l := labels.Label(index); l.Valid() { +- // The 'start' label duplicates the span name, so discard it. +- if l := labels.Label(index); l.Valid() && l.Key().Name() != "start" { - fmt.Fprintf(buf, "%v ", l) - } - } @@ -34724,8 +39179,8 @@ diff -urN a/gopls/internal/lsp/debug/trace.go b/gopls/internal/lsp/debug/trace.g -} diff -urN a/gopls/internal/lsp/definition.go b/gopls/internal/lsp/definition.go --- a/gopls/internal/lsp/definition.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/definition.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,52 +0,0 @@ ++++ b/gopls/internal/lsp/definition.go 1970-01-01 08:00:00 +@@ -1,60 +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. @@ -34734,30 +39189,35 @@ diff -urN a/gopls/internal/lsp/definition.go b/gopls/internal/lsp/definition.go - -import ( - "context" -- "errors" - "fmt" - - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/lsp/template" +- "golang.org/x/tools/gopls/internal/telemetry" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" -) - --func (s *Server) definition(ctx context.Context, params *protocol.DefinitionParams) ([]protocol.Location, error) { +-func (s *Server) definition(ctx context.Context, params *protocol.DefinitionParams) (_ []protocol.Location, rerr error) { +- recordLatency := telemetry.StartLatencyTimer("definition") +- defer func() { +- recordLatency(ctx, rerr) +- }() +- +- ctx, done := event.Start(ctx, "lsp.Server.definition", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - // TODO(rfindley): definition requests should be multiplexed across all views. - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) - defer release() - if !ok { - return nil, err - } -- switch kind := snapshot.View().FileKind(fh); kind { +- switch kind := snapshot.FileKind(fh); kind { - case source.Tmpl: - return template.Definition(snapshot, fh, params.Position) - case source.Go: -- // Partial support for jumping from linkname directive (position at 2nd argument). -- locations, err := source.LinknameDefinition(ctx, snapshot, fh, params.Position) -- if !errors.Is(err, source.ErrNoLinkname) { -- return locations, err -- } - return source.Definition(ctx, snapshot, fh, params.Position) - default: - return nil, fmt.Errorf("can't find definitions for file type %s", kind) @@ -34765,13 +39225,16 @@ diff -urN a/gopls/internal/lsp/definition.go b/gopls/internal/lsp/definition.go -} - -func (s *Server) typeDefinition(ctx context.Context, params *protocol.TypeDefinitionParams) ([]protocol.Location, error) { +- ctx, done := event.Start(ctx, "lsp.Server.typeDefinition", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - // TODO(rfindley): type definition requests should be multiplexed across all views. - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go) - defer release() - if !ok { - return nil, err - } -- switch kind := snapshot.View().FileKind(fh); kind { +- switch kind := snapshot.FileKind(fh); kind { - case source.Go: - return source.TypeDefinition(ctx, snapshot, fh, params.Position) - default: @@ -34780,8 +39243,8 @@ diff -urN a/gopls/internal/lsp/definition.go b/gopls/internal/lsp/definition.go -} diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.go --- a/gopls/internal/lsp/diagnostics.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/diagnostics.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,764 +0,0 @@ ++++ b/gopls/internal/lsp/diagnostics.go 1970-01-01 08:00:00 +@@ -1,862 +0,0 @@ -// Copyright 2018 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. @@ -34795,12 +39258,12 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - "fmt" - "os" - "path/filepath" +- "sort" - "strings" - "sync" - "time" - -- "golang.org/x/sync/errgroup" -- "golang.org/x/tools/gopls/internal/lsp/debug/log" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/mod" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" @@ -34809,14 +39272,23 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - "golang.org/x/tools/gopls/internal/span" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/event/tag" -- "golang.org/x/tools/internal/xcontext" -) - +-// TODO(rfindley): simplify this very complicated logic for publishing +-// diagnostics. While doing so, ensure that we can test subtle logic such as +-// for multi-pass diagnostics. +- -// diagnosticSource differentiates different sources of diagnostics. +-// +-// Diagnostics from the same source overwrite each other, whereas diagnostics +-// from different sources do not. Conceptually, the server state is a mapping +-// from diagnostics source to a set of diagnostics, and each storeDiagnostics +-// operation updates one entry of that mapping. -type diagnosticSource int - -const ( -- modSource diagnosticSource = iota +- modParseSource diagnosticSource = iota +- modTidySource - gcDetailsSource - analysisSource - typeCheckSource @@ -34863,13 +39335,15 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - mustPublish bool - - // The last stored diagnostics for each diagnostic source. -- reports map[diagnosticSource]diagnosticReport +- reports map[diagnosticSource]*diagnosticReport -} - -func (d diagnosticSource) String() string { - switch d { -- case modSource: -- return "FromSource" +- case modParseSource: +- return "FromModParse" +- case modTidySource: +- return "FromModTidy" - case gcDetailsSource: - return "FromGCDetails" - case analysisSource: @@ -34890,46 +39364,69 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g -} - -// hashDiagnostics computes a hash to identify diags. +-// +-// hashDiagnostics mutates its argument (via sorting). -func hashDiagnostics(diags ...*source.Diagnostic) string { +- if len(diags) == 0 { +- return emptyDiagnosticsHash +- } +- return computeDiagnosticHash(diags...) +-} +- +-// opt: pre-computed hash for empty diagnostics +-var emptyDiagnosticsHash = computeDiagnosticHash() +- +-// computeDiagnosticHash should only be called from hashDiagnostics. +-// +-// TODO(rfindley): this should use source.Hash. +-func computeDiagnosticHash(diags ...*source.Diagnostic) string { - source.SortDiagnostics(diags) - h := sha256.New() - for _, d := range diags { - for _, t := range d.Tags { -- fmt.Fprintf(h, "%s", t) +- fmt.Fprintf(h, "tag: %s\n", t) - } - for _, r := range d.Related { -- fmt.Fprintf(h, "%s%s%s", r.Location.URI.SpanURI(), r.Message, r.Location.Range) +- fmt.Fprintf(h, "related: %s %s %s\n", r.Location.URI.SpanURI(), r.Message, r.Location.Range) +- } +- fmt.Fprintf(h, "code: %s\n", d.Code) +- fmt.Fprintf(h, "codeHref: %s\n", d.CodeHref) +- fmt.Fprintf(h, "message: %s\n", d.Message) +- fmt.Fprintf(h, "range: %s\n", d.Range) +- fmt.Fprintf(h, "severity: %s\n", d.Severity) +- fmt.Fprintf(h, "source: %s\n", d.Source) +- if d.BundledFixes != nil { +- fmt.Fprintf(h, "fixes: %s\n", *d.BundledFixes) - } -- fmt.Fprintf(h, "%s%s%s%s", d.Message, d.Range, d.Severity, d.Source) - } - return fmt.Sprintf("%x", h.Sum(nil)) -} - --func (s *Server) diagnoseDetached(snapshot source.Snapshot) { -- ctx := snapshot.BackgroundContext() -- ctx = xcontext.Detach(ctx) -- s.diagnose(ctx, snapshot, false) -- s.publishDiagnostics(ctx, true, snapshot) --} -- -func (s *Server) diagnoseSnapshots(snapshots map[source.Snapshot][]span.URI, onDisk bool) { - var diagnosticWG sync.WaitGroup - for snapshot, uris := range snapshots { - diagnosticWG.Add(1) - go func(snapshot source.Snapshot, uris []span.URI) { - defer diagnosticWG.Done() -- s.diagnoseSnapshot(snapshot, uris, onDisk) +- s.diagnoseSnapshot(snapshot, uris, onDisk, snapshot.Options().DiagnosticsDelay) - }(snapshot, uris) - } - diagnosticWG.Wait() -} - --func (s *Server) diagnoseSnapshot(snapshot source.Snapshot, changedURIs []span.URI, onDisk bool) { +-// diagnoseSnapshot computes and publishes diagnostics for the given snapshot. +-// +-// If delay is non-zero, computing diagnostics does not start until after this +-// delay has expired, to allow work to be cancelled by subsequent changes. +-// +-// If changedURIs is non-empty, it is a set of recently changed files that +-// should be diagnosed immediately, and onDisk reports whether these file +-// changes came from a change to on-disk files. +-func (s *Server) diagnoseSnapshot(snapshot source.Snapshot, changedURIs []span.URI, onDisk bool, delay time.Duration) { - ctx := snapshot.BackgroundContext() - ctx, done := event.Start(ctx, "Server.diagnoseSnapshot", source.SnapshotLabels(snapshot)...) - defer done() - -- delay := snapshot.View().Options().DiagnosticsDelay - if delay > 0 { - // 2-phase diagnostics. - // @@ -34937,25 +39434,36 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - // does not analyze) packages directly affected by - // file modifications. - // -- // The second phase runs analysis on the entire snapshot, -- // and is debounced by the configured delay. +- // The second phase runs after the delay, and does everything. +- // +- // We wait a brief delay before the first phase, to allow higher priority +- // work such as autocompletion to acquire the type checking mutex (though +- // typically both diagnosing changed files and performing autocompletion +- // will be doing the same work: recomputing active packages). +- const minDelay = 20 * time.Millisecond +- select { +- case <-time.After(minDelay): +- case <-ctx.Done(): +- return +- } +- - s.diagnoseChangedFiles(ctx, snapshot, changedURIs, onDisk) - s.publishDiagnostics(ctx, false, snapshot) - -- // We debounce diagnostics separately for each view, using the snapshot -- // local ID as logical ordering. -- // -- // TODO(rfindley): it would be cleaner to simply put the diagnostic -- // debouncer on the view, and remove the "key" argument to debouncing. -- if ok := <-s.diagDebouncer.debounce(snapshot.View().Name(), snapshot.SequenceID(), time.After(delay)); ok { -- s.diagnose(ctx, snapshot, false) -- s.publishDiagnostics(ctx, true, snapshot) +- if delay < minDelay { +- delay = 0 +- } else { +- delay -= minDelay +- } +- +- select { +- case <-time.After(delay): +- case <-ctx.Done(): +- return - } -- return - } - -- // Ignore possible workspace configuration warnings in the normal flow. -- s.diagnose(ctx, snapshot, false) +- s.diagnose(ctx, snapshot, analyzeOpenPackages) - s.publishDiagnostics(ctx, true, snapshot) -} - @@ -34963,11 +39471,7 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - ctx, done := event.Start(ctx, "Server.diagnoseChangedFiles", source.SnapshotLabels(snapshot)...) - defer done() - -- // TODO(adonovan): safety: refactor so that group.Go is called -- // in a second loop, so that if we should later add an early -- // return to the first loop, we don't leak goroutines. -- var group errgroup.Group -- seen := make(map[*source.Metadata]bool) +- toDiagnose := make(map[source.PackageID]*source.Metadata) - for _, uri := range uris { - // If the change is only on-disk and the file is not open, don't - // directly request its package. It may not be a workspace package. @@ -34981,45 +39485,50 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - } - - // Don't request type-checking for builtin.go: it's not a real package. -- if snapshot.IsBuiltin(ctx, uri) { +- if snapshot.IsBuiltin(uri) { +- continue +- } +- +- // Don't diagnose files that are ignored by `go list` (e.g. testdata). +- if snapshot.IgnoredFile(uri) { - continue - } - - // Find all packages that include this file and diagnose them in parallel. -- metas, err := snapshot.MetadataForFile(ctx, uri) +- meta, err := source.NarrowestMetadataForFile(ctx, snapshot, uri) - if err != nil { +- if ctx.Err() != nil { +- return +- } - // TODO(findleyr): we should probably do something with the error here, - // but as of now this can fail repeatedly if load fails, so can be too - // noisy to log (and we'll handle things later in the slow pass). - continue - } -- for _, m := range metas { -- if m.IsIntermediateTestVariant() { -- continue -- } -- if !seen[m] { -- seen[m] = true -- m := m -- group.Go(func() error { -- s.diagnosePkg(ctx, snapshot, m, false) -- return nil // error result is ignored -- }) -- } -- } +- toDiagnose[meta.ID] = meta - } -- group.Wait() // ignore error +- s.diagnosePkgs(ctx, snapshot, toDiagnose, nil) -} - +-// analysisMode parameterizes analysis behavior of a call to diagnosePkgs. +-type analysisMode int +- +-const ( +- analyzeNothing analysisMode = iota // don't run any analysis +- analyzeOpenPackages // run analysis on packages with open files +- analyzeEverything // run analysis on all packages +-) +- -// diagnose is a helper function for running diagnostics with a given context. -// Do not call it directly. forceAnalysis is only true for testing purposes. --func (s *Server) diagnose(ctx context.Context, snapshot source.Snapshot, forceAnalysis bool) { +-func (s *Server) diagnose(ctx context.Context, snapshot source.Snapshot, analyze analysisMode) { - ctx, done := event.Start(ctx, "Server.diagnose", source.SnapshotLabels(snapshot)...) - defer done() - - // Wait for a free diagnostics slot. - // TODO(adonovan): opt: shouldn't it be the analysis implementation's - // job to de-dup and limit resource consumption? In any case this -- // this function spends most its time waiting for awaitLoaded, at +- // function spends most its time waiting for awaitLoaded, at - // least initially. - select { - case <-ctx.Done(): @@ -35044,45 +39553,42 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - } - } - -- // Diagnose go.mod upgrades. -- upgradeReports, upgradeErr := mod.UpgradeDiagnostics(ctx, snapshot) -- if ctx.Err() != nil { -- log.Trace.Log(ctx, "diagnose cancelled") -- return -- } -- store(modCheckUpgradesSource, "diagnosing go.mod upgrades", upgradeReports, upgradeErr, true) +- // Diagnostics below are organized by increasing specificity: +- // go.work > mod > mod upgrade > mod vuln > package, etc. - - // Diagnose go.work file. - workReports, workErr := work.Diagnostics(ctx, snapshot) - if ctx.Err() != nil { -- log.Trace.Log(ctx, "diagnose cancelled") - return - } - store(workSource, "diagnosing go.work file", workReports, workErr, true) - - // Diagnose go.mod file. -- // (This step demands type checking of all active packages: -- // the bottleneck in the startup sequence for a big workspace.) - modReports, modErr := mod.Diagnostics(ctx, snapshot) - if ctx.Err() != nil { -- log.Trace.Log(ctx, "diagnose cancelled") - return - } -- store(modSource, "diagnosing go.mod file", modReports, modErr, true) +- store(modParseSource, "diagnosing go.mod file", modReports, modErr, true) +- +- // Diagnose go.mod upgrades. +- upgradeReports, upgradeErr := mod.UpgradeDiagnostics(ctx, snapshot) +- if ctx.Err() != nil { +- return +- } +- store(modCheckUpgradesSource, "diagnosing go.mod upgrades", upgradeReports, upgradeErr, true) - - // Diagnose vulnerabilities. - vulnReports, vulnErr := mod.VulnerabilityDiagnostics(ctx, snapshot) - if ctx.Err() != nil { -- log.Trace.Log(ctx, "diagnose cancelled") - return - } - store(modVulncheckSource, "diagnosing vulnerabilities", vulnReports, vulnErr, false) - -- activeMetas, activeErr := snapshot.ActiveMetadata(ctx) -- if s.shouldIgnoreError(ctx, snapshot, activeErr) { +- workspace, err := snapshot.WorkspaceMetadata(ctx) +- if s.shouldIgnoreError(ctx, snapshot, err) { - return - } -- criticalErr := snapshot.GetCriticalError(ctx) +- criticalErr := snapshot.CriticalError(ctx) - if ctx.Err() != nil { // must check ctx after GetCriticalError - return - } @@ -35101,103 +39607,200 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - - // If there are no workspace packages, there is nothing to diagnose and - // there are no orphaned files. -- if len(activeMetas) == 0 { +- if len(workspace) == 0 { - return - } - -- // Run go/analysis diagnosis of packages in parallel. -- // TODO(adonovan): opt: it may be more efficient to -- // have diagnosePkg take a set of packages. +- var wg sync.WaitGroup // for potentially slow operations below +- +- // Maybe run go mod tidy (if it has been invalidated). - // -- // TODO(adonovan): opt: since the new analysis driver does its -- // own type checking, we could strength-reduce pkg to -- // PackageID and get this step started as soon as the set of -- // active package IDs are known, without waiting for them to load. +- // Since go mod tidy can be slow, we run it concurrently to diagnostics. +- wg.Add(1) +- go func() { +- defer wg.Done() +- modTidyReports, err := mod.TidyDiagnostics(ctx, snapshot) +- store(modTidySource, "running go mod tidy", modTidyReports, err, true) +- }() +- +- // Run type checking and go/analysis diagnosis of packages in parallel. - var ( -- wg sync.WaitGroup -- seen = map[span.URI]struct{}{} +- seen = map[span.URI]struct{}{} +- toDiagnose = make(map[source.PackageID]*source.Metadata) +- toAnalyze = make(map[source.PackageID]unit) - ) -- for _, m := range activeMetas { +- for _, m := range workspace { +- var hasNonIgnored, hasOpenFile bool - for _, uri := range m.CompiledGoFiles { - seen[uri] = struct{}{} +- if !hasNonIgnored && !snapshot.IgnoredFile(uri) { +- hasNonIgnored = true +- } +- if !hasOpenFile && snapshot.IsOpen(uri) { +- hasOpenFile = true +- } +- } +- if hasNonIgnored { +- toDiagnose[m.ID] = m +- if analyze == analyzeEverything || analyze == analyzeOpenPackages && hasOpenFile { +- toAnalyze[m.ID] = unit{} +- } - } -- -- wg.Add(1) -- go func(m *source.Metadata) { -- defer wg.Done() -- s.diagnosePkg(ctx, snapshot, m, forceAnalysis) -- }(m) - } +- +- wg.Add(1) +- go func() { +- s.diagnosePkgs(ctx, snapshot, toDiagnose, toAnalyze) +- wg.Done() +- }() +- - wg.Wait() - - // Orphaned files. - // Confirm that every opened file belongs to a package (if any exist in - // the workspace). Otherwise, add a diagnostic to the file. -- for _, o := range s.session.Overlays() { -- if _, ok := seen[o.URI()]; ok { -- continue +- if diags, err := snapshot.OrphanedFileDiagnostics(ctx); err == nil { +- for uri, diag := range diags { +- s.storeDiagnostics(snapshot, uri, orphanedSource, []*source.Diagnostic{diag}, true) - } -- diagnostic := s.checkForOrphanedFile(ctx, snapshot, o) -- if diagnostic == nil { -- continue +- } else { +- if ctx.Err() == nil { +- event.Error(ctx, "computing orphaned file diagnostics", err, source.SnapshotLabels(snapshot)...) - } -- s.storeDiagnostics(snapshot, o.URI(), orphanedSource, []*source.Diagnostic{diagnostic}, true) - } -} - --func (s *Server) diagnosePkg(ctx context.Context, snapshot source.Snapshot, m *source.Metadata, alwaysAnalyze bool) { -- ctx, done := event.Start(ctx, "Server.diagnosePkg", append(source.SnapshotLabels(snapshot), tag.Package.Of(string(m.ID)))...) +-// diagnosePkgs type checks packages in toDiagnose, and analyzes packages in +-// toAnalyze, merging their diagnostics. Packages in toAnalyze must be a subset +-// of the packages in toDiagnose. +-// +-// It also implements gc_details diagnostics. +-// +-// TODO(rfindley): revisit handling of analysis gc_details. It may be possible +-// to merge this function with Server.diagnose, thereby avoiding the two layers +-// of concurrent dispatch: as of writing we concurrently run TidyDiagnostics +-// and diagnosePkgs, and diagnosePkgs concurrently runs PackageDiagnostics and +-// analysis. +-func (s *Server) diagnosePkgs(ctx context.Context, snapshot source.Snapshot, toDiagnose map[source.PackageID]*source.Metadata, toAnalyze map[source.PackageID]unit) { +- ctx, done := event.Start(ctx, "Server.diagnosePkgs", source.SnapshotLabels(snapshot)...) - defer done() -- enableDiagnostics := false -- includeAnalysis := alwaysAnalyze // only run analyses for packages with open files -- for _, uri := range m.CompiledGoFiles { -- enableDiagnostics = enableDiagnostics || !snapshot.IgnoredFile(uri) -- includeAnalysis = includeAnalysis || snapshot.IsOpen(uri) -- } -- // Don't show any diagnostics on ignored files. -- if !enableDiagnostics { -- return -- } - -- diags, err := snapshot.PackageDiagnostics(ctx, m.ID) -- if err != nil { -- event.Error(ctx, "warning: diagnostics failed", err, append(source.SnapshotLabels(snapshot), tag.Package.Of(string(m.ID)))...) -- return -- } +- // Analyze and type-check concurrently, since they are independent +- // operations. +- var ( +- wg sync.WaitGroup +- pkgDiags map[span.URI][]*source.Diagnostic +- analysisDiags = make(map[span.URI][]*source.Diagnostic) +- ) +- +- // Collect package diagnostics. +- wg.Add(1) +- go func() { +- defer wg.Done() +- var ids []source.PackageID +- for id := range toDiagnose { +- ids = append(ids, id) +- } +- var err error +- pkgDiags, err = snapshot.PackageDiagnostics(ctx, ids...) +- if err != nil { +- event.Error(ctx, "warning: diagnostics failed", err, source.SnapshotLabels(snapshot)...) +- } +- }() - - // Get diagnostics from analysis framework. - // This includes type-error analyzers, which suggest fixes to compiler errors. -- var analysisDiags map[span.URI][]*source.Diagnostic -- if includeAnalysis { -- diags, err := source.Analyze(ctx, snapshot, m.ID, false) +- wg.Add(1) +- go func() { +- defer wg.Done() +- diags, err := source.Analyze(ctx, snapshot, toAnalyze, s.progress) - if err != nil { -- event.Error(ctx, "warning: analyzing package", err, append(source.SnapshotLabels(snapshot), tag.Package.Of(string(m.ID)))...) +- var tagStr string // sorted comma-separated list of package IDs +- { +- // TODO(adonovan): replace with a generic map[S]any -> string +- // function in the tag package, and use maps.Keys + slices.Sort. +- keys := make([]string, 0, len(toDiagnose)) +- for id := range toDiagnose { +- keys = append(keys, string(id)) +- } +- sort.Strings(keys) +- tagStr = strings.Join(keys, ",") +- } +- event.Error(ctx, "warning: analyzing package", err, append(source.SnapshotLabels(snapshot), tag.Package.Of(tagStr))...) - return - } -- analysisDiags = diags -- } +- for uri, diags := range diags { +- analysisDiags[uri] = append(analysisDiags[uri], diags...) +- } +- }() - -- // For each file, update the server's diagnostics state. -- for _, uri := range m.CompiledGoFiles { -- // builtin.go exists only for documentation purposes and -- // is not valid Go code. Don't report distracting errors. -- if snapshot.IsBuiltin(ctx, uri) { +- wg.Wait() +- +- // TODO(rfindley): remove the guards against snapshot.IsBuiltin, after the +- // gopls@v0.12.0 release. Packages should not be producing diagnostics for +- // the builtin file: I do not know why this logic existed previously. +- +- // Merge analysis diagnostics with package diagnostics, and store the +- // resulting analysis diagnostics. +- for uri, adiags := range analysisDiags { +- if snapshot.IsBuiltin(uri) { +- bug.Reportf("go/analysis reported diagnostics for the builtin file: %v", adiags) - continue - } +- tdiags := pkgDiags[uri] +- var tdiags2, adiags2 []*source.Diagnostic +- source.CombineDiagnostics(tdiags, adiags, &tdiags2, &adiags2) +- pkgDiags[uri] = tdiags2 +- s.storeDiagnostics(snapshot, uri, analysisSource, adiags2, true) +- } - -- pkgDiags := diags[uri] -- var tdiags, adiags []*source.Diagnostic -- source.CombineDiagnostics(pkgDiags, analysisDiags[uri], &tdiags, &adiags) -- s.storeDiagnostics(snapshot, uri, typeCheckSource, tdiags, true) -- s.storeDiagnostics(snapshot, uri, analysisSource, adiags, true) +- // golang/go#59587: guarantee that we store type-checking diagnostics for every compiled +- // package file. +- // +- // Without explicitly storing empty diagnostics, the eager diagnostics +- // publication for changed files will not publish anything for files with +- // empty diagnostics. +- storedPkgDiags := make(map[span.URI]bool) +- for _, m := range toDiagnose { +- for _, uri := range m.CompiledGoFiles { +- s.storeDiagnostics(snapshot, uri, typeCheckSource, pkgDiags[uri], true) +- storedPkgDiags[uri] = true +- } +- } +- // Store the package diagnostics. +- for uri, diags := range pkgDiags { +- if storedPkgDiags[uri] { +- continue +- } +- // builtin.go exists only for documentation purposes, and is not valid Go code. +- // Don't report distracting errors +- if snapshot.IsBuiltin(uri) { +- bug.Reportf("type checking reported diagnostics for the builtin file: %v", diags) +- continue +- } +- s.storeDiagnostics(snapshot, uri, typeCheckSource, diags, true) - } - -- // If gc optimization details are requested, add them to the -- // diagnostic reports. +- // Process requested gc_details diagnostics. +- // +- // TODO(rfindley): this could be improved: +- // 1. This should memoize its results if the package has not changed. +- // 2. This should not even run gc_details if the package contains unsaved +- // files. +- // 3. See note below about using FindFile. +- var toGCDetail map[source.PackageID]*source.Metadata - s.gcOptimizationDetailsMu.Lock() -- _, enableGCDetails := s.gcOptimizationDetails[m.ID] +- for id := range s.gcOptimizationDetails { +- if m, ok := toDiagnose[id]; ok { +- if toGCDetail == nil { +- toGCDetail = make(map[source.PackageID]*source.Metadata) +- } +- toGCDetail[id] = m +- } +- } - s.gcOptimizationDetailsMu.Unlock() -- if enableGCDetails { +- +- for _, m := range toGCDetail { - gcReports, err := source.GCOptimizationDetails(ctx, snapshot, m) - if err != nil { - event.Error(ctx, "warning: gc details", err, append(source.SnapshotLabels(snapshot), tag.Package.Of(string(m.ID)))...) @@ -35211,10 +39814,13 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - // diagnostics does not race with storing the results here. - if enableGCDetails { - for uri, diags := range gcReports { +- // TODO(rfindley): remove the use of FindFile here, and use ReadFile +- // instead. Isn't it enough to know that the package came from the +- // snapshot? Any reports should apply to the snapshot. - fh := snapshot.FindFile(uri) - // Don't publish gc details for unsaved buffers, since the underlying - // logic operates on the file on disk. -- if fh == nil || !fh.Saved() { +- if fh == nil || !fh.SameContentsOnDisk() { - continue - } - s.storeDiagnostics(snapshot, uri, gcDetailsSource, diags, true) @@ -35236,7 +39842,7 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - if s.diagnostics[uri] == nil { - s.diagnostics[uri] = &fileReports{ - publishedHash: hashDiagnostics(), // Hash for 0 diagnostics. -- reports: map[diagnosticSource]diagnosticReport{}, +- reports: map[diagnosticSource]*diagnosticReport{}, - } - } - s.diagnostics[uri].mustPublish = true @@ -35245,6 +39851,8 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g -// storeDiagnostics stores results from a single diagnostic source. If merge is -// true, it merges results into any existing results for this snapshot. -// +-// Mutates (sorts) diags. +-// -// TODO(hyangah): investigate whether we can unconditionally overwrite previous report.diags -// with the new diags and eliminate the need for the `merge` flag. -func (s *Server) storeDiagnostics(snapshot source.Snapshot, uri span.URI, dsource diagnosticSource, diags []*source.Diagnostic, merge bool) { @@ -35260,10 +39868,14 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - if s.diagnostics[uri] == nil { - s.diagnostics[uri] = &fileReports{ - publishedHash: hashDiagnostics(), // Hash for 0 diagnostics. -- reports: map[diagnosticSource]diagnosticReport{}, +- reports: map[diagnosticSource]*diagnosticReport{}, - } - } - report := s.diagnostics[uri].reports[dsource] +- if report == nil { +- report = new(diagnosticReport) +- s.diagnostics[uri].reports[dsource] = report +- } - // Don't set obsolete diagnostics. - if report.snapshotID > snapshot.GlobalID() { - return @@ -35275,7 +39887,6 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - for _, d := range diags { - report.diags[hashDiagnostics(d)] = d - } -- s.diagnostics[uri].reports[dsource] = report -} - -// clearDiagnosticSource clears all diagnostics for a given source type. It is @@ -35303,7 +39914,7 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - if err != nil { - event.Error(ctx, "errors loading workspace", err.MainError, source.SnapshotLabels(snapshot)...) - for _, d := range err.Diagnostics { -- s.storeDiagnostics(snapshot, d.URI, modSource, []*source.Diagnostic{d}, true) +- s.storeDiagnostics(snapshot, d.URI, modParseSource, []*source.Diagnostic{d}, true) - } - errMsg = strings.ReplaceAll(err.MainError.Error(), "\n", " ") - } @@ -35325,66 +39936,6 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - } -} - --// checkForOrphanedFile checks that the given URIs can be mapped to packages. --// If they cannot and the workspace is not otherwise unloaded, it also surfaces --// a warning, suggesting that the user check the file for build tags. --func (s *Server) checkForOrphanedFile(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) *source.Diagnostic { -- // TODO(rfindley): this function may fail to produce a diagnostic for a -- // variety of reasons, some of which should probably not be ignored. For -- // example, should this function be tolerant of the case where fh does not -- // exist, or does not have a package name? -- // -- // It would be better to panic or report a bug in several of the cases below, -- // so that we can move toward guaranteeing we show the user a meaningful -- // error whenever it makes sense. -- if snapshot.View().FileKind(fh) != source.Go { -- return nil -- } -- // builtin files won't have a package, but they are never orphaned. -- if snapshot.IsBuiltin(ctx, fh.URI()) { -- return nil -- } -- -- // This call has the effect of inserting fh into snapshot.files, -- // where for better or worse (actually: just worse) it influences -- // the sets of open, known, and orphaned files. -- snapshot.GetFile(ctx, fh.URI()) -- -- metas, _ := snapshot.MetadataForFile(ctx, fh.URI()) -- if len(metas) > 0 || ctx.Err() != nil { -- return nil // no package, or cancelled -- } -- // Inv: file does not belong to a package we know about. -- pgf, err := snapshot.ParseGo(ctx, fh, source.ParseHeader) -- if err != nil { -- return nil -- } -- if !pgf.File.Name.Pos().IsValid() { -- return nil -- } -- rng, err := pgf.NodeRange(pgf.File.Name) -- if err != nil { -- return nil -- } -- // If the file no longer has a name ending in .go, this diagnostic is wrong -- if filepath.Ext(fh.URI().Filename()) != ".go" { -- return nil -- } -- // TODO(rstambler): We should be able to parse the build tags in the -- // file and show a more specific error message. For now, put the diagnostic -- // on the package declaration. -- return &source.Diagnostic{ -- URI: fh.URI(), -- Range: rng, -- Severity: protocol.SeverityWarning, -- Source: source.ListError, -- Message: fmt.Sprintf(`No packages found for open file %s: %v. --If this file contains build tags, try adding "-tags=" to your gopls "buildFlags" configuration (see (https://github.com/golang/tools/blob/master/gopls/doc/settings.md#buildflags-string). --Otherwise, see the troubleshooting guidelines for help investigating (https://github.com/golang/tools/blob/master/gopls/doc/troubleshooting.md). --`, fh.URI().Filename(), err), -- } --} -- -// publishDiagnostics collects and publishes any unpublished diagnostic reports. -func (s *Server) publishDiagnostics(ctx context.Context, final bool, snapshot source.Snapshot) { - ctx, done := event.Start(ctx, "Server.publishDiagnostics", source.SnapshotLabels(snapshot)...) @@ -35422,6 +39973,7 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - diags = append(diags, d) - reportDiags = append(reportDiags, d) - } +- - hash := hashDiagnostics(reportDiags...) - if hash != report.publishedHash { - anyReportsChanged = true @@ -35435,7 +39987,6 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - continue - } - -- source.SortDiagnostics(diags) - hash := hashDiagnostics(diags...) - if hash == r.publishedHash && !r.mustPublish { - // Update snapshotID to be the latest snapshot for which this diagnostic @@ -35455,15 +40006,22 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - r.publishedHash = hash - r.mustPublish = false // diagnostics have been successfully published - r.publishedSnapshotID = snapshot.GlobalID() -- for dsource, hash := range reportHashes { -- report := r.reports[dsource] -- report.publishedHash = hash -- r.reports[dsource] = report +- // When we publish diagnostics for a file, we must update the +- // publishedHash for every report, not just the reports that were +- // published. Eliding a report is equivalent to publishing empty +- // diagnostics. +- for dsource, report := range r.reports { +- if hash, ok := reportHashes[dsource]; ok { +- report.publishedHash = hash +- } else { +- // The report was not (yet) stored for this snapshot. Record that we +- // published no diagnostics from this source. +- report.publishedHash = hashDiagnostics() +- } - } - } else { - if ctx.Err() != nil { - // Publish may have failed due to a cancelled context. -- log.Trace.Log(ctx, "publish cancelled") - return - } - event.Error(ctx, "publishReports: failed to deliver diagnostic", err, tag.URI.Of(uri)) @@ -35480,8 +40038,9 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - Range: diag.Range, - Severity: diag.Severity, - Source: string(diag.Source), -- Tags: diag.Tags, +- Tags: emptySliceDiagnosticTag(diag.Tags), - RelatedInformation: diag.Related, +- Data: diag.BundledFixes, - } - if diag.Code != "" { - pdiag.Code = diag.Code @@ -35502,6 +40061,8 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - return true - } - // If the folder has no Go code in it, we shouldn't spam the user with a warning. +- // TODO(rfindley): surely it is not correct to walk the folder here just to +- // suppress diagnostics, every time we compute diagnostics. - var hasGo bool - _ = filepath.Walk(snapshot.View().Folder().Filename(), func(path string, info os.FileInfo, err error) error { - if err != nil { @@ -35537,7 +40098,7 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g - return ans -} - --func auxStr(v *source.Diagnostic, d diagnosticReport, typ diagnosticSource) string { +-func auxStr(v *source.Diagnostic, d *diagnosticReport, typ diagnosticSource) string { - // Tags? RelatedInformation? - msg := fmt.Sprintf("(%s)%q(source:%q,code:%q,severity:%s,snapshot:%d,type:%s)", - v.Range, v.Message, v.Source, v.Code, v.Severity, d.snapshotID, typ) @@ -35548,8 +40109,8 @@ diff -urN a/gopls/internal/lsp/diagnostics.go b/gopls/internal/lsp/diagnostics.g -} diff -urN a/gopls/internal/lsp/fake/client.go b/gopls/internal/lsp/fake/client.go --- a/gopls/internal/lsp/fake/client.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/client.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,187 +0,0 @@ ++++ b/gopls/internal/lsp/fake/client.go 1970-01-01 08:00:00 +@@ -1,193 +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. @@ -35566,7 +40127,7 @@ diff -urN a/gopls/internal/lsp/fake/client.go b/gopls/internal/lsp/fake/client.g -) - -// ClientHooks are a set of optional hooks called during handling of --// the corresponding client method (see protocol.Client for the the +-// the corresponding client method (see protocol.Client for the -// LSP server-to-client RPCs) in order to make test expectations -// awaitable. -type ClientHooks struct { @@ -35574,6 +40135,7 @@ diff -urN a/gopls/internal/lsp/fake/client.go b/gopls/internal/lsp/fake/client.g - OnDiagnostics func(context.Context, *protocol.PublishDiagnosticsParams) error - OnWorkDoneProgressCreate func(context.Context, *protocol.WorkDoneProgressCreateParams) error - OnProgress func(context.Context, *protocol.ProgressParams) error +- OnShowDocument func(context.Context, *protocol.ShowDocumentParams) error - OnShowMessage func(context.Context, *protocol.ShowMessageParams) error - OnShowMessageRequest func(context.Context, *protocol.ShowMessageRequestParams) error - OnRegisterCapability func(context.Context, *protocol.RegistrationParams) error @@ -35614,10 +40176,10 @@ diff -urN a/gopls/internal/lsp/fake/client.go b/gopls/internal/lsp/fake/client.g - return nil, err - } - } -- if len(params.Actions) == 0 || len(params.Actions) > 1 { -- return nil, fmt.Errorf("fake editor cannot handle multiple action items") +- if c.editor.config.MessageResponder != nil { +- return c.editor.config.MessageResponder(params) - } -- return ¶ms.Actions[0], nil +- return nil, nil // don't choose, which is effectively dismissing the message -} - -func (c *Client) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error { @@ -35646,9 +40208,8 @@ diff -urN a/gopls/internal/lsp/fake/client.go b/gopls/internal/lsp/fake/client.g - results := make([]interface{}, len(p.Items)) - for i, item := range p.Items { - if item.Section == "gopls" { -- c.editor.mu.Lock() -- results[i] = c.editor.settingsLocked() -- c.editor.mu.Unlock() +- config := c.editor.Config() +- results[i] = makeSettings(c.editor.sandbox, config) - } - } - return results, nil @@ -35715,7 +40276,13 @@ diff -urN a/gopls/internal/lsp/fake/client.go b/gopls/internal/lsp/fake/client.g - return nil -} - --func (c *Client) ShowDocument(context.Context, *protocol.ShowDocumentParams) (*protocol.ShowDocumentResult, error) { +-func (c *Client) ShowDocument(ctx context.Context, params *protocol.ShowDocumentParams) (*protocol.ShowDocumentResult, error) { +- if c.hooks.OnShowDocument != nil { +- if err := c.hooks.OnShowDocument(ctx, params); err != nil { +- return nil, err +- } +- return &protocol.ShowDocumentResult{Success: true}, nil +- } - return nil, nil -} - @@ -35739,7 +40306,7 @@ diff -urN a/gopls/internal/lsp/fake/client.go b/gopls/internal/lsp/fake/client.g -} diff -urN a/gopls/internal/lsp/fake/doc.go b/gopls/internal/lsp/fake/doc.go --- a/gopls/internal/lsp/fake/doc.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/doc.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/fake/doc.go 1970-01-01 08:00:00 @@ -1,19 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -35762,7 +40329,7 @@ diff -urN a/gopls/internal/lsp/fake/doc.go b/gopls/internal/lsp/fake/doc.go -package fake diff -urN a/gopls/internal/lsp/fake/edit.go b/gopls/internal/lsp/fake/edit.go --- a/gopls/internal/lsp/fake/edit.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/edit.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/fake/edit.go 1970-01-01 08:00:00 @@ -1,51 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -35815,10 +40382,110 @@ diff -urN a/gopls/internal/lsp/fake/edit.go b/gopls/internal/lsp/fake/edit.go - } - return patched, nil -} +diff -urN a/gopls/internal/lsp/fake/edit_test.go b/gopls/internal/lsp/fake/edit_test.go +--- a/gopls/internal/lsp/fake/edit_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/fake/edit_test.go 1970-01-01 08:00:00 +@@ -1,96 +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 fake +- +-import ( +- "testing" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +-) +- +-func TestApplyEdits(t *testing.T) { +- tests := []struct { +- label string +- content string +- edits []protocol.TextEdit +- want string +- wantErr bool +- }{ +- { +- label: "empty content", +- }, +- { +- label: "empty edit", +- content: "hello", +- edits: []protocol.TextEdit{}, +- want: "hello", +- }, +- { +- label: "unicode edit", +- content: "hello, 日本語", +- edits: []protocol.TextEdit{ +- NewEdit(0, 7, 0, 10, "world"), +- }, +- want: "hello, world", +- }, +- { +- label: "range edit", +- content: "ABC\nDEF\nGHI\nJKL", +- edits: []protocol.TextEdit{ +- NewEdit(1, 1, 2, 3, "12\n345"), +- }, +- want: "ABC\nD12\n345\nJKL", +- }, +- { +- label: "regression test for issue #57627", +- content: "go 1.18\nuse moda/a", +- edits: []protocol.TextEdit{ +- NewEdit(1, 0, 1, 0, "\n"), +- NewEdit(2, 0, 2, 0, "\n"), +- }, +- want: "go 1.18\n\nuse moda/a\n", +- }, +- { +- label: "end before start", +- content: "ABC\nDEF\nGHI\nJKL", +- edits: []protocol.TextEdit{ +- NewEdit(2, 3, 1, 1, "12\n345"), +- }, +- wantErr: true, +- }, +- { +- label: "out of bounds line", +- content: "ABC\nDEF\nGHI\nJKL", +- edits: []protocol.TextEdit{ +- NewEdit(1, 1, 4, 3, "12\n345"), +- }, +- wantErr: true, +- }, +- { +- label: "out of bounds column", +- content: "ABC\nDEF\nGHI\nJKL", +- edits: []protocol.TextEdit{ +- NewEdit(1, 4, 2, 3, "12\n345"), +- }, +- wantErr: true, +- }, +- } +- +- for _, test := range tests { +- test := test +- t.Run(test.label, func(t *testing.T) { +- got, err := applyEdits(protocol.NewMapper("", []byte(test.content)), test.edits, false) +- if (err != nil) != test.wantErr { +- t.Errorf("got err %v, want error: %t", err, test.wantErr) +- } +- if err != nil { +- return +- } +- if got := string(got); got != test.want { +- t.Errorf("got %q, want %q", got, test.want) +- } +- }) +- } +-} diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.go --- a/gopls/internal/lsp/fake/editor.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/editor.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1464 +0,0 @@ ++++ b/gopls/internal/lsp/fake/editor.go 1970-01-01 08:00:00 +@@ -1,1494 +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. @@ -35828,6 +40495,7 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g -import ( - "bytes" - "context" +- "encoding/json" - "errors" - "fmt" - "os" @@ -35858,7 +40526,6 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g - serverConn jsonrpc2.Conn - client *Client - sandbox *Sandbox -- defaultEnv map[string]string - - // TODO(adonovan): buffers should be keyed by protocol.DocumentURI. - mu sync.Mutex @@ -35877,7 +40544,7 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g - -// CallCounts tracks the number of protocol notifications of different types. -type CallCounts struct { -- DidOpen, DidChange, DidSave, DidChangeWatchedFiles, DidClose uint64 +- DidOpen, DidChange, DidSave, DidChangeWatchedFiles, DidClose, DidChangeConfiguration uint64 -} - -// buffer holds information about an open buffer in the editor. @@ -35896,8 +40563,14 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g -// source.UserOptions, but we use a separate type here so that we expose only -// that configuration which we support. -// --// The zero value for EditorConfig should correspond to its defaults. +-// The zero value for EditorConfig is the default configuration. -type EditorConfig struct { +- // ClientName sets the clientInfo.name for the LSP session (in the initialize request). +- // +- // Since this can only be set during initialization, changing this field via +- // Editor.ChangeConfiguration has no effect. +- ClientName string +- - // Env holds environment variables to apply on top of the default editor - // environment. When applying these variables, the special string - // $SANDBOX_WORKDIR is replaced by the absolute path to the sandbox working @@ -35925,15 +40598,25 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g - - // Settings holds user-provided configuration for the LSP server. - Settings map[string]interface{} +- +- // CapabilitiesJSON holds JSON client capabilities to overlay over the +- // editor's default client capabilities. +- // +- // Specifically, this JSON string will be unmarshalled into the editor's +- // client capabilities struct, before sending to the server. +- CapabilitiesJSON []byte +- +- // If non-nil, MessageResponder is used to respond to ShowMessageRequest +- // messages. +- MessageResponder func(params *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) -} - -// NewEditor creates a new Editor. -func NewEditor(sandbox *Sandbox, config EditorConfig) *Editor { - return &Editor{ -- buffers: make(map[string]buffer), -- sandbox: sandbox, -- defaultEnv: sandbox.GoEnv(), -- config: config, +- buffers: make(map[string]buffer), +- sandbox: sandbox, +- config: config, - } -} - @@ -36019,19 +40702,17 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g - return e.client -} - --// settingsLocked builds the settings map for use in LSP settings RPCs. --// --// e.mu must be held while calling this function. --func (e *Editor) settingsLocked() map[string]interface{} { +-// makeSettings builds the settings map for use in LSP settings RPCs. +-func makeSettings(sandbox *Sandbox, config EditorConfig) map[string]interface{} { - env := make(map[string]string) -- for k, v := range e.defaultEnv { +- for k, v := range sandbox.GoEnv() { - env[k] = v - } -- for k, v := range e.config.Env { +- for k, v := range config.Env { - env[k] = v - } - for k, v := range env { -- v = strings.ReplaceAll(v, "$SANDBOX_WORKDIR", e.sandbox.Workdir.RootURI().SpanURI().Filename()) +- v = strings.ReplaceAll(v, "$SANDBOX_WORKDIR", sandbox.Workdir.RootURI().SpanURI().Filename()) - env[k] = v - } - @@ -36042,16 +40723,12 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g - // asynchronous operations being completed (such as diagnosing a snapshot). - "verboseWorkDoneProgress": true, - -- // Set a generous completion budget, so that tests don't flake because +- // Set an unlimited completion budget, so that tests don't flake because - // completions are too slow. -- "completionBudget": "10s", -- -- // Shorten the diagnostic delay to speed up test execution (else we'd add -- // the default delay to each assertion about diagnostics) -- "diagnosticsDelay": "10ms", +- "completionBudget": "0s", - } - -- for k, v := range e.config.Settings { +- for k, v := range config.Settings { - if k == "env" { - panic("must not provide env via the EditorConfig.Settings field: use the EditorConfig.Env field instead") - } @@ -36062,23 +40739,23 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g -} - -func (e *Editor) initialize(ctx context.Context) error { +- config := e.Config() +- - params := &protocol.ParamInitialize{} -- params.ClientInfo = &protocol.Msg_XInitializeParams_clientInfo{} -- params.ClientInfo.Name = "fakeclient" -- params.ClientInfo.Version = "v1.0.0" -- e.mu.Lock() -- params.WorkspaceFolders = e.makeWorkspaceFoldersLocked() -- params.InitializationOptions = e.settingsLocked() -- e.mu.Unlock() -- params.Capabilities.Workspace.Configuration = true -- params.Capabilities.Window.WorkDoneProgress = true +- if e.config.ClientName != "" { +- params.ClientInfo = &protocol.Msg_XInitializeParams_clientInfo{} +- params.ClientInfo.Name = e.config.ClientName +- params.ClientInfo.Version = "v1.0.0" +- } +- params.InitializationOptions = makeSettings(e.sandbox, config) +- params.WorkspaceFolders = makeWorkspaceFolders(e.sandbox, config.WorkspaceFolders) - -- // TODO: set client capabilities +- // Set various client capabilities that are sought by gopls. +- params.Capabilities.Workspace.Configuration = true // support workspace/configuration +- params.Capabilities.Window.WorkDoneProgress = true // support window/workDoneProgress - params.Capabilities.TextDocument.Completion.CompletionItem.TagSupport.ValueSet = []protocol.CompletionItemTag{protocol.ComplDeprecated} -- - params.Capabilities.TextDocument.Completion.CompletionItem.SnippetSupport = true - params.Capabilities.TextDocument.SemanticTokens.Requests.Full.Value = true -- // copied from lsp/semantic.go to avoid import cycle in tests - params.Capabilities.TextDocument.SemanticTokens.TokenTypes = []string{ - "namespace", "type", "class", "enum", "interface", - "struct", "typeParameter", "parameter", "variable", "property", "enumMember", @@ -36089,17 +40766,25 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g - "declaration", "definition", "readonly", "static", - "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary", - } -- -- // This is a bit of a hack, since the fake editor doesn't actually support -- // watching changed files that match a specific glob pattern. However, the -- // editor does send didChangeWatchedFiles notifications, so set this to -- // true. +- // The LSP tests have historically enabled this flag, +- // but really we should test both ways for older editors. +- params.Capabilities.TextDocument.DocumentSymbol.HierarchicalDocumentSymbolSupport = true +- // Glob pattern watching is enabled. - params.Capabilities.Workspace.DidChangeWatchedFiles.DynamicRegistration = true +- // "rename" operations are used for package renaming. +- // +- // TODO(rfindley): add support for other resource operations (create, delete, ...) - params.Capabilities.Workspace.WorkspaceEdit = &protocol.WorkspaceEditClientCapabilities{ - ResourceOperations: []protocol.ResourceOperationKind{ - "rename", - }, - } +- // Apply capabilities overlay. +- if config.CapabilitiesJSON != nil { +- if err := json.Unmarshal(config.CapabilitiesJSON, ¶ms.Capabilities); err != nil { +- return fmt.Errorf("unmarshalling EditorConfig.CapabilitiesJSON: %v", err) +- } +- } - - trace := protocol.TraceValues("messages") - params.Trace = &trace @@ -36121,18 +40806,25 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g - return nil -} - --// makeWorkspaceFoldersLocked creates a slice of workspace folders to use for +-// HasCommand reports whether the connected server supports the command with the given ID. +-func (e *Editor) HasCommand(id string) bool { +- for _, command := range e.serverCapabilities.ExecuteCommandProvider.Commands { +- if command == id { +- return true +- } +- } +- return false +-} +- +-// makeWorkspaceFolders creates a slice of workspace folders to use for -// this editing session, based on the editor configuration. --// --// e.mu must be held while calling this function. --func (e *Editor) makeWorkspaceFoldersLocked() (folders []protocol.WorkspaceFolder) { -- paths := e.config.WorkspaceFolders +-func makeWorkspaceFolders(sandbox *Sandbox, paths []string) (folders []protocol.WorkspaceFolder) { - if len(paths) == 0 { -- paths = append(paths, string(e.sandbox.Workdir.RelativeTo)) +- paths = []string{string(sandbox.Workdir.RelativeTo)} - } - - for _, path := range paths { -- uri := string(e.sandbox.Workdir.URI(path)) +- uri := string(sandbox.Workdir.URI(path)) - folders = append(folders, protocol.WorkspaceFolder{ - URI: uri, - Name: filepath.Base(uri), @@ -36594,10 +41286,7 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g - -// GoToDefinition jumps to the definition of the symbol at the given position -// in an open buffer. It returns the location of the resulting jump. --// --// TODO(rfindley): rename to "Definition", to be consistent with LSP --// terminology. --func (e *Editor) GoToDefinition(ctx context.Context, loc protocol.Location) (protocol.Location, error) { +-func (e *Editor) Definition(ctx context.Context, loc protocol.Location) (protocol.Location, error) { - if err := e.checkBufferLocation(loc); err != nil { - return protocol.Location{}, err - } @@ -36612,9 +41301,9 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g - return e.extractFirstLocation(ctx, resp) -} - --// GoToTypeDefinition jumps to the type definition of the symbol at the given location --// in an open buffer. --func (e *Editor) GoToTypeDefinition(ctx context.Context, loc protocol.Location) (protocol.Location, error) { +-// TypeDefinition jumps to the type definition of the symbol at the given +-// location in an open buffer. +-func (e *Editor) TypeDefinition(ctx context.Context, loc protocol.Location) (protocol.Location, error) { - if err := e.checkBufferLocation(loc); err != nil { - return protocol.Location{}, err - } @@ -37150,19 +41839,26 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g - return e.config -} - +-func (e *Editor) SetConfig(cfg EditorConfig) { +- e.mu.Lock() +- e.config = cfg +- e.mu.Unlock() +-} +- -// ChangeConfiguration sets the new editor configuration, and if applicable -// sends a didChangeConfiguration notification. -// -// An error is returned if the change notification failed to send. -func (e *Editor) ChangeConfiguration(ctx context.Context, newConfig EditorConfig) error { -- e.mu.Lock() -- e.config = newConfig -- e.mu.Unlock() // don't hold e.mu during server calls +- e.SetConfig(newConfig) - if e.Server != nil { - var params protocol.DidChangeConfigurationParams // empty: gopls ignores the Settings field - if err := e.Server.DidChangeConfiguration(ctx, ¶ms); err != nil { - return err - } +- e.callsMu.Lock() +- e.calls.DidChangeConfiguration++ +- e.callsMu.Unlock() - } - return nil -} @@ -37172,12 +41868,13 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g -// -// The given folders must all be unique. -func (e *Editor) ChangeWorkspaceFolders(ctx context.Context, folders []string) error { +- config := e.Config() +- - // capture existing folders so that we can compute the change. -- e.mu.Lock() -- oldFolders := e.makeWorkspaceFoldersLocked() -- e.config.WorkspaceFolders = folders -- newFolders := e.makeWorkspaceFoldersLocked() -- e.mu.Unlock() +- oldFolders := makeWorkspaceFolders(e.sandbox, config.WorkspaceFolders) +- newFolders := makeWorkspaceFolders(e.sandbox, folders) +- config.WorkspaceFolders = folders +- e.SetConfig(config) - - if e.Server == nil { - return nil @@ -37285,7 +41982,7 @@ diff -urN a/gopls/internal/lsp/fake/editor.go b/gopls/internal/lsp/fake/editor.g -} diff -urN a/gopls/internal/lsp/fake/editor_test.go b/gopls/internal/lsp/fake/editor_test.go --- a/gopls/internal/lsp/fake/editor_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/editor_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/fake/editor_test.go 1970-01-01 08:00:00 @@ -1,61 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -37348,109 +42045,9 @@ diff -urN a/gopls/internal/lsp/fake/editor_test.go b/gopls/internal/lsp/fake/edi - t.Errorf("got text %q, want %q", got, want) - } -} -diff -urN a/gopls/internal/lsp/fake/edit_test.go b/gopls/internal/lsp/fake/edit_test.go ---- a/gopls/internal/lsp/fake/edit_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/edit_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,96 +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 fake -- --import ( -- "testing" -- -- "golang.org/x/tools/gopls/internal/lsp/protocol" --) -- --func TestApplyEdits(t *testing.T) { -- tests := []struct { -- label string -- content string -- edits []protocol.TextEdit -- want string -- wantErr bool -- }{ -- { -- label: "empty content", -- }, -- { -- label: "empty edit", -- content: "hello", -- edits: []protocol.TextEdit{}, -- want: "hello", -- }, -- { -- label: "unicode edit", -- content: "hello, 日本語", -- edits: []protocol.TextEdit{ -- NewEdit(0, 7, 0, 10, "world"), -- }, -- want: "hello, world", -- }, -- { -- label: "range edit", -- content: "ABC\nDEF\nGHI\nJKL", -- edits: []protocol.TextEdit{ -- NewEdit(1, 1, 2, 3, "12\n345"), -- }, -- want: "ABC\nD12\n345\nJKL", -- }, -- { -- label: "regression test for issue #57627", -- content: "go 1.18\nuse moda/a", -- edits: []protocol.TextEdit{ -- NewEdit(1, 0, 1, 0, "\n"), -- NewEdit(2, 0, 2, 0, "\n"), -- }, -- want: "go 1.18\n\nuse moda/a\n", -- }, -- { -- label: "end before start", -- content: "ABC\nDEF\nGHI\nJKL", -- edits: []protocol.TextEdit{ -- NewEdit(2, 3, 1, 1, "12\n345"), -- }, -- wantErr: true, -- }, -- { -- label: "out of bounds line", -- content: "ABC\nDEF\nGHI\nJKL", -- edits: []protocol.TextEdit{ -- NewEdit(1, 1, 4, 3, "12\n345"), -- }, -- wantErr: true, -- }, -- { -- label: "out of bounds column", -- content: "ABC\nDEF\nGHI\nJKL", -- edits: []protocol.TextEdit{ -- NewEdit(1, 4, 2, 3, "12\n345"), -- }, -- wantErr: true, -- }, -- } -- -- for _, test := range tests { -- test := test -- t.Run(test.label, func(t *testing.T) { -- got, err := applyEdits(protocol.NewMapper("", []byte(test.content)), test.edits, false) -- if (err != nil) != test.wantErr { -- t.Errorf("got err %v, want error: %t", err, test.wantErr) -- } -- if err != nil { -- return -- } -- if got := string(got); got != test.want { -- t.Errorf("got %q, want %q", got, test.want) -- } -- }) -- } --} diff -urN a/gopls/internal/lsp/fake/proxy.go b/gopls/internal/lsp/fake/proxy.go --- a/gopls/internal/lsp/fake/proxy.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/proxy.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/fake/proxy.go 1970-01-01 08:00:00 @@ -1,35 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -37489,7 +42086,7 @@ diff -urN a/gopls/internal/lsp/fake/proxy.go b/gopls/internal/lsp/fake/proxy.go -} diff -urN a/gopls/internal/lsp/fake/sandbox.go b/gopls/internal/lsp/fake/sandbox.go --- a/gopls/internal/lsp/fake/sandbox.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/sandbox.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/fake/sandbox.go 1970-01-01 08:00:00 @@ -1,299 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -37501,7 +42098,6 @@ diff -urN a/gopls/internal/lsp/fake/sandbox.go b/gopls/internal/lsp/fake/sandbox - "context" - "errors" - "fmt" -- "io/ioutil" - "os" - "path/filepath" - "strings" @@ -37585,7 +42181,7 @@ diff -urN a/gopls/internal/lsp/fake/sandbox.go b/gopls/internal/lsp/fake/sandbox - - rootDir := config.RootDir - if rootDir == "" { -- rootDir, err = ioutil.TempDir(config.RootDir, "gopls-sandbox-") +- rootDir, err = os.MkdirTemp(config.RootDir, "gopls-sandbox-") - if err != nil { - return nil, fmt.Errorf("creating temporary workdir: %v", err) - } @@ -37643,7 +42239,7 @@ diff -urN a/gopls/internal/lsp/fake/sandbox.go b/gopls/internal/lsp/fake/sandbox -// is the responsibility of the caller to call os.RemoveAll on the returned -// file path when it is no longer needed. -func Tempdir(files map[string][]byte) (string, error) { -- dir, err := ioutil.TempDir("", "gopls-tempdir-") +- dir, err := os.MkdirTemp("", "gopls-tempdir-") - if err != nil { - return "", err - } @@ -37747,10 +42343,11 @@ diff -urN a/gopls/internal/lsp/fake/sandbox.go b/gopls/internal/lsp/fake/sandbox -// RunGoCommand executes a go command in the sandbox. If checkForFileChanges is -// true, the sandbox scans the working directory and emits file change events -// for any file changes it finds. --func (sb *Sandbox) RunGoCommand(ctx context.Context, dir, verb string, args []string, checkForFileChanges bool) error { +-func (sb *Sandbox) RunGoCommand(ctx context.Context, dir, verb string, args, env []string, checkForFileChanges bool) error { - inv := sb.goCommandInvocation() - inv.Verb = verb - inv.Args = args +- inv.Env = append(inv.Env, env...) - if dir != "" { - inv.WorkingDir = sb.Workdir.AbsPath(dir) - } @@ -37759,7 +42356,7 @@ diff -urN a/gopls/internal/lsp/fake/sandbox.go b/gopls/internal/lsp/fake/sandbox - return fmt.Errorf("go command failed (stdout: %s) (stderr: %s): %v", stdout.String(), stderr.String(), err) - } - // Since running a go command may result in changes to workspace files, -- // check if we need to send any any "watched" file events. +- // check if we need to send any "watched" file events. - // - // TODO(rFindley): this side-effect can impact the usability of the sandbox - // for benchmarks. Consider refactoring. @@ -37782,7 +42379,7 @@ diff -urN a/gopls/internal/lsp/fake/sandbox.go b/gopls/internal/lsp/fake/sandbox -func (sb *Sandbox) Close() error { - var goCleanErr error - if sb.gopath != "" { -- goCleanErr = sb.RunGoCommand(context.Background(), "", "clean", []string{"-modcache"}, false) +- goCleanErr = sb.RunGoCommand(context.Background(), "", "clean", []string{"-modcache"}, nil, false) - } - err := robustio.RemoveAll(sb.rootdir) - if err != nil || goCleanErr != nil { @@ -37792,8 +42389,8 @@ diff -urN a/gopls/internal/lsp/fake/sandbox.go b/gopls/internal/lsp/fake/sandbox -} diff -urN a/gopls/internal/lsp/fake/workdir.go b/gopls/internal/lsp/fake/workdir.go --- a/gopls/internal/lsp/fake/workdir.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/workdir.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,438 +0,0 @@ ++++ b/gopls/internal/lsp/fake/workdir.go 1970-01-01 08:00:00 +@@ -1,430 +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. @@ -37806,7 +42403,6 @@ diff -urN a/gopls/internal/lsp/fake/workdir.go b/gopls/internal/lsp/fake/workdir - "crypto/sha256" - "fmt" - "io/fs" -- "io/ioutil" - "os" - "path/filepath" - "runtime" @@ -37853,7 +42449,7 @@ diff -urN a/gopls/internal/lsp/fake/workdir.go b/gopls/internal/lsp/fake/workdir - } - backoff := 1 * time.Millisecond - for { -- err := ioutil.WriteFile(fp, []byte(content), 0644) +- err := os.WriteFile(fp, []byte(content), 0644) - if err != nil { - // This lock file violation is not handled by the robustio package, as it - // indicates a real race condition that could be avoided. @@ -37916,7 +42512,7 @@ diff -urN a/gopls/internal/lsp/fake/workdir.go b/gopls/internal/lsp/fake/workdir -// fileID identifies a file version on disk. -type fileID struct { - mtime time.Time -- hash string // empty if mtime is old enough to be reliabe; otherwise a file digest +- hash string // empty if mtime is old enough to be reliable; otherwise a file digest -} - -func hashFile(data []byte) string { @@ -37956,7 +42552,7 @@ diff -urN a/gopls/internal/lsp/fake/workdir.go b/gopls/internal/lsp/fake/workdir -func (w *Workdir) ReadFile(path string) ([]byte, error) { - backoff := 1 * time.Millisecond - for { -- b, err := ioutil.ReadFile(w.AbsPath(path)) +- b, err := os.ReadFile(w.AbsPath(path)) - if err != nil { - if runtime.GOOS == "plan9" && strings.HasSuffix(err.Error(), " exclusive use file already open") { - // Plan 9 enforces exclusive access to locked files. @@ -38015,13 +42611,6 @@ diff -urN a/gopls/internal/lsp/fake/workdir.go b/gopls/internal/lsp/fake/workdir - return w.WriteFiles(ctx, map[string]string{path: content}) -} - --func (w *Workdir) fileEvent(path string, changeType protocol.FileChangeType) protocol.FileEvent { -- return protocol.FileEvent{ -- URI: w.URI(path), -- Type: changeType, -- } --} -- -// RenameFile performs an on disk-renaming of the workdir-relative oldPath to -// workdir-relative newPath, and notifies watchers of the changes. -// @@ -38166,14 +42755,14 @@ diff -urN a/gopls/internal/lsp/fake/workdir.go b/gopls/internal/lsp/fake/workdir - return nil - } - -- // Opt: avoid reading the file if mtime is sufficently old to be reliable. +- // Opt: avoid reading the file if mtime is sufficiently old to be reliable. - // - // If mtime is recent, it may not sufficiently identify the file contents: - // a subsequent write could result in the same mtime. For these cases, we - // must read the file contents. - id := fileID{mtime: info.ModTime()} - if time.Since(info.ModTime()) < 2*time.Second { -- data, err := ioutil.ReadFile(fp) +- data, err := os.ReadFile(fp) - if err != nil { - return err - } @@ -38200,7 +42789,7 @@ diff -urN a/gopls/internal/lsp/fake/workdir.go b/gopls/internal/lsp/fake/workdir - // In this case, read the content to check whether the file actually - // changed. - if oldID.mtime.Equal(id.mtime) && oldID.hash != "" && id.hash == "" { -- data, err := ioutil.ReadFile(fp) +- data, err := os.ReadFile(fp) - if err != nil { - return err - } @@ -38234,8 +42823,8 @@ diff -urN a/gopls/internal/lsp/fake/workdir.go b/gopls/internal/lsp/fake/workdir -} diff -urN a/gopls/internal/lsp/fake/workdir_test.go b/gopls/internal/lsp/fake/workdir_test.go --- a/gopls/internal/lsp/fake/workdir_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/workdir_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,220 +0,0 @@ ++++ b/gopls/internal/lsp/fake/workdir_test.go 1970-01-01 08:00:00 +@@ -1,219 +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. @@ -38244,7 +42833,6 @@ diff -urN a/gopls/internal/lsp/fake/workdir_test.go b/gopls/internal/lsp/fake/wo - -import ( - "context" -- "io/ioutil" - "os" - "sync" - "testing" @@ -38270,7 +42858,7 @@ diff -urN a/gopls/internal/lsp/fake/workdir_test.go b/gopls/internal/lsp/fake/wo -func newWorkdir(t *testing.T, txt string) (*Workdir, *eventBuffer, func()) { - t.Helper() - -- tmpdir, err := ioutil.TempDir("", "goplstest-workdir-") +- tmpdir, err := os.MkdirTemp("", "goplstest-workdir-") - if err != nil { - t.Fatal(err) - } @@ -38458,7 +43046,7 @@ diff -urN a/gopls/internal/lsp/fake/workdir_test.go b/gopls/internal/lsp/fake/wo -} diff -urN a/gopls/internal/lsp/fake/workdir_windows.go b/gopls/internal/lsp/fake/workdir_windows.go --- a/gopls/internal/lsp/fake/workdir_windows.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/fake/workdir_windows.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/fake/workdir_windows.go 1970-01-01 08:00:00 @@ -1,21 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -38483,8 +43071,8 @@ diff -urN a/gopls/internal/lsp/fake/workdir_windows.go b/gopls/internal/lsp/fake -} diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filecache/filecache.go --- a/gopls/internal/lsp/filecache/filecache.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/filecache/filecache.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,369 +0,0 @@ ++++ b/gopls/internal/lsp/filecache/filecache.go 1970-01-01 08:00:00 +@@ -1,608 +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. @@ -38510,60 +43098,114 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec -import ( - "bytes" - "crypto/sha256" -- "encoding/binary" +- "encoding/hex" +- "encoding/json" - "errors" - "fmt" - "io" +- "io/fs" - "log" - "os" - "path/filepath" - "sort" +- "strings" - "sync" - "sync/atomic" - "time" - -- "golang.org/x/tools/internal/lockedfile" +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/lru" -) - --// Get retrieves from the cache and returns a newly allocated --// copy of the value most recently supplied to Set(kind, key), --// possibly by another process. +-// Start causes the filecache to initialize and start garbage gollection. +-// +-// Start is automatically called by the first call to Get, but may be called +-// explicitly to pre-initialize the cache. +-func Start() { +- go getCacheDir() +-} +- +-// As an optimization, use a 100MB in-memory LRU cache in front of filecache +-// operations. This reduces I/O for operations such as diagnostics or +-// implementations that repeatedly access the same cache entries. +-var memCache = lru.New(100 * 1e6) +- +-type memKey struct { +- kind string +- key [32]byte +-} +- +-// Get retrieves from the cache and returns the value most recently +-// supplied to Set(kind, key), possibly by another process. -// Get returns ErrNotFound if the value was not found. +-// +-// Callers should not modify the returned array. -func Get(kind string, key [32]byte) ([]byte, error) { -- name := filename(kind, key) -- data, err := lockedfile.Read(name) +- // First consult the read-through memory cache. +- // Note that memory cache hits do not update the times +- // used for LRU eviction of the file-based cache. +- if value := memCache.Get(memKey{kind, key}); value != nil { +- return value.([]byte), nil +- } +- +- iolimit <- struct{}{} // acquire a token +- defer func() { <-iolimit }() // release a token +- +- // Read the index file, which provides the name of the CAS file. +- indexName, err := filename(kind, key) +- if err != nil { +- return nil, err +- } +- indexData, err := os.ReadFile(indexName) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - return nil, ErrNotFound - } - return nil, err - } +- var valueHash [32]byte +- if copy(valueHash[:], indexData) != len(valueHash) { +- return nil, ErrNotFound // index entry has wrong length +- } - -- // Verify that the Write was complete -- // by checking the recorded length. -- if len(data) < 8 { -- return nil, ErrNotFound // cache entry is incomplete +- // Read the CAS file and check its contents match. +- // +- // This ensures integrity in all cases (corrupt or truncated +- // file, short read, I/O error, wrong length, etc) except an +- // engineered hash collision, which is infeasible. +- casName, err := filename(casKind, valueHash) +- if err != nil { +- return nil, err - } -- if length := binary.LittleEndian.Uint64(data); int(length) != len(data)-8 { -- return nil, ErrNotFound // cache entry is incomplete (or too long!) +- value, _ := os.ReadFile(casName) // ignore error +- if sha256.Sum256(value) != valueHash { +- return nil, ErrNotFound // CAS file is missing or has wrong contents - } -- data = data[8:] - -- // Update file time for use by LRU eviction. -- // (This turns every read into a write operation. -- // If this is a performance problem, we should -- // touch the files aynchronously.) +- // Update file times used by LRU eviction. +- // +- // Because this turns a read into a write operation, +- // we follow the approach used in the go command's +- // cache and update the access time only if the +- // existing timestamp is older than one hour. - // - // (Traditionally the access time would be updated - // automatically, but for efficiency most POSIX systems have - // for many years set the noatime mount option to avoid every - // open or read operation entailing a metadata write.) - now := time.Now() -- if err := os.Chtimes(name, now, now); err != nil { -- return nil, fmt.Errorf("failed to update access time: %w", err) +- touch := func(filename string) { +- st, err := os.Stat(filename) +- if err == nil && now.Sub(st.ModTime()) > time.Hour { +- os.Chtimes(filename, now, now) // ignore error +- } - } +- touch(indexName) +- touch(casName) - -- return data, nil +- memCache.Set(memKey{kind, key}, value, len(value)) +- +- return value, nil -} - -// ErrNotFound is the distinguished error @@ -38572,37 +43214,97 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec - -// Set updates the value in the cache. -func Set(kind string, key [32]byte, value []byte) error { -- name := filename(kind, key) -- if err := os.MkdirAll(filepath.Dir(name), 0700); err != nil { +- memCache.Set(memKey{kind, key}, value, len(value)) +- +- // Set the active event to wake up the GC. +- select { +- case active <- struct{}{}: +- default: +- } +- +- iolimit <- struct{}{} // acquire a token +- defer func() { <-iolimit }() // release a token +- +- // First, add the value to the content- +- // addressable store (CAS), if not present. +- hash := sha256.Sum256(value) +- casName, err := filename(casKind, hash) +- if err != nil { - return err - } +- // Does CAS file exist and have correct (complete) content? +- // TODO(adonovan): opt: use mmap for this check. +- if prev, _ := os.ReadFile(casName); !bytes.Equal(prev, value) { +- if err := os.MkdirAll(filepath.Dir(casName), 0700); err != nil { +- return err +- } +- // Avoiding O_TRUNC here is merely an optimization to avoid +- // cache misses when two threads race to write the same file. +- if err := writeFileNoTrunc(casName, value, 0600); err != nil { +- os.Remove(casName) // ignore error +- return err // e.g. disk full +- } +- } - -- // In the unlikely event of a short write (e.g. ENOSPC) -- // followed by process termination (e.g. a power cut), we -- // don't want a reader to see a short file, so we record -- // the expected length first and verify it in Get. -- var length [8]byte -- binary.LittleEndian.PutUint64(length[:], uint64(len(value))) -- header := bytes.NewReader(length[:]) -- payload := bytes.NewReader(value) +- // Now write an index entry that refers to the CAS file. +- indexName, err := filename(kind, key) +- if err != nil { +- return err +- } +- if err := os.MkdirAll(filepath.Dir(indexName), 0700); err != nil { +- return err +- } +- if err := writeFileNoTrunc(indexName, hash[:], 0600); err != nil { +- os.Remove(indexName) // ignore error +- return err // e.g. disk full +- } +- +- return nil +-} - -- // Windows doesn't support atomic rename--we tried MoveFile, -- // MoveFileEx, ReplaceFileEx, and SetFileInformationByHandle -- // of RenameFileInfo, all to no avail--so instead we use -- // advisory file locking, which is only about 2x slower even -- // on POSIX platforms with atomic rename. -- return lockedfile.Write(name, io.MultiReader(header, payload), 0600) +-// The active 1-channel is a selectable resettable event +-// indicating recent cache activity. +-var active = make(chan struct{}, 1) +- +-// writeFileNoTrunc is like os.WriteFile but doesn't truncate until +-// after the write, so that racing writes of the same data are idempotent. +-func writeFileNoTrunc(filename string, data []byte, perm os.FileMode) error { +- f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, perm) +- if err != nil { +- return err +- } +- _, err = f.Write(data) +- if err == nil { +- err = f.Truncate(int64(len(data))) +- } +- if closeErr := f.Close(); err == nil { +- err = closeErr +- } +- return err -} - +-// reserved kind strings +-const ( +- casKind = "cas" // content-addressable store files +- bugKind = "bug" // gopls bug reports +-) +- +-var iolimit = make(chan struct{}, 128) // counting semaphore to limit I/O concurrency in Set. +- -var budget int64 = 1e9 // 1GB - --// SetBudget sets a soft limit on disk usage of the cache (in bytes) --// and returns the previous value. Supplying a negative value queries --// the current value without changing it. +-// SetBudget sets a soft limit on disk usage of regular files in the +-// cache (in bytes) and returns the previous value. Supplying a +-// negative value queries the current value without changing it. -// -// If two gopls processes have different budgets, the one with the -// lower budget will collect garbage more actively, but both will -// observe the effect. +-// +-// Even in the steady state, the storage usage reported by the 'du' +-// command may exceed the budget by as much as a factor of 3 due to +-// the overheads of directories and the effects of block quantization, +-// which are especially pronounced for the small index files. -func SetBudget(new int64) (old int64) { - if new < 0 { - return atomic.LoadInt64(&budget) @@ -38612,22 +43314,62 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec - -// --- implementation ---- - --// filename returns the cache entry of the specified kind and key. +-// filename returns the name of the cache file of the specified kind and key. -// --// A typical cache entry is a file name such as: +-// A typical cache file has a name such as: -// --// $HOME/Library/Caches / gopls / VVVVVVVV / kind / KK / KKKK...KKKK +-// $HOME/Library/Caches / gopls / VVVVVVVV / KK / KKKK...KKKK - kind -// -// The portions separated by spaces are as follows: -// - The user's preferred cache directory; the default value varies by OS. -// - The constant "gopls". -// - The "version", 32 bits of the digest of the gopls executable. --// - The kind or purpose of this cache subtree (e.g. "analysis"). -// - The first 8 bits of the key, to avoid huge directories. -// - The full 256 bits of the key. --// --// Once a file is written its contents are never modified, though it --// may be atomically replaced or removed. +-// - The kind or purpose of this cache file (e.g. "analysis"). +-// +-// The kind establishes a namespace for the keys. It is represented as +-// a suffix, not a segment, as this significantly reduces the number +-// of directories created, and thus the storage overhead. +-// +-// Previous iterations of the design aimed for the invariant that once +-// a file is written, its contents are never modified, though it may +-// be atomically replaced or removed. However, not all platforms have +-// an atomic rename operation (our first approach), and file locking +-// (our second) is a notoriously fickle mechanism. +-// +-// The current design instead exploits a trick from the cache +-// implementation used by the go command: writes of small files are in +-// practice atomic (all or nothing) on all platforms. +-// (See GOROOT/src/cmd/go/internal/cache/cache.go.) +-// +-// Russ Cox notes: "all file systems use an rwlock around every file +-// system block, including data blocks, so any writes or reads within +-// the same block are going to be handled atomically by the FS +-// implementation without any need to request file locking explicitly. +-// And since the files are so small, there's only one block. (A block +-// is at minimum 512 bytes, usually much more.)" And: "all modern file +-// systems protect against [partial writes due to power loss] with +-// journals." +-// +-// We use a two-level scheme consisting of an index and a +-// content-addressable store (CAS). A single cache entry consists of +-// two files. The value of a cache entry is written into the file at +-// filename("cas", sha256(value)). Since the value may be arbitrarily +-// large, this write is not atomic. That means we must check the +-// integrity of the contents read back from the CAS to make sure they +-// hash to the expected key. If the CAS file is incomplete or +-// inconsistent, we proceed as if it were missing. +-// +-// Once the CAS file has been written, we write a small fixed-size +-// index file at filename(kind, key), using the values supplied by the +-// caller. The index file contains the hash that identifies the value +-// file in the CAS. (We could add extra metadata to this file, up to +-// 512B, the minimum size of a disk block, if later desired, so long +-// as the total size remains fixed.) Because the index file is small, +-// concurrent writes to it are atomic in practice, even though this is +-// not guaranteed by any OS. The fixed size ensures that readers can't +-// see a palimpsest when a short new file overwrites a longer old one. -// -// New versions of gopls are free to reorganize the contents of the -// version directory as needs evolve. But all versions of gopls must @@ -38636,10 +43378,15 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec -// In particular, each gopls process attempts to garbage collect -// the entire gopls directory so that newer binaries can clean up -// after older ones: in the development cycle especially, new --// new versions may be created frequently. --func filename(kind string, key [32]byte) string { -- hex := fmt.Sprintf("%x", key) -- return filepath.Join(getCacheDir(), kind, hex[:2], hex) +-// versions may be created frequently. +-func filename(kind string, key [32]byte) (string, error) { +- base := fmt.Sprintf("%x-%s", key, kind) +- dir, err := getCacheDir() +- if err != nil { +- return "", err +- } +- // Keep the BugReports function consistent with this one. +- return filepath.Join(dir, base[:2], base), nil -} - -// getCacheDir returns the persistent cache directory of all processes @@ -38648,7 +43395,7 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec -// It must incorporate the hash of the executable so that we needn't -// worry about incompatible changes to the file format or changes to -// the algorithm that produced the index. --func getCacheDir() string { +-func getCacheDir() (string, error) { - cacheDirOnce.Do(func() { - // Use user's preferred cache directory. - userDir := os.Getenv("GOPLSCACHE") @@ -38681,21 +43428,22 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec - // Compute the hash of this executable (~20ms) and create a subdirectory. - hash, err := hashExecutable() - if err != nil { -- log.Fatalf("can't hash gopls executable: %v", err) +- cacheDirErr = fmt.Errorf("can't hash gopls executable: %v", err) - } - // Use only 32 bits of the digest to avoid unwieldy filenames. - // It's not an adversarial situation. - cacheDir = filepath.Join(goplsDir, fmt.Sprintf("%x", hash[:4])) - if err := os.MkdirAll(cacheDir, 0700); err != nil { -- log.Fatalf("can't create cache: %v", err) +- cacheDirErr = fmt.Errorf("can't create cache: %v", err) - } - }) -- return cacheDir +- return cacheDir, cacheDirErr -} - -var ( - cacheDirOnce sync.Once -- cacheDir string // only accessed by getCacheDir +- cacheDir string +- cacheDirErr error -) - -func hashExecutable() (hash [32]byte, err error) { @@ -38724,13 +43472,25 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec -// process, possibly running a different version of gopls, possibly -// running concurrently. -func gc(goplsDir string) { -- const period = 1 * time.Minute // period between collections +- // period between collections +- // +- // Originally the period was always 1 minute, but this +- // consumed 15% of a CPU core when idle (#61049). +- // +- // The reason for running collections even when idle is so +- // that long lived gopls sessions eventually clean up the +- // caches created by defunct executables. +- const ( +- minPeriod = 5 * time.Minute // when active +- maxPeriod = 6 * time.Hour // when idle +- ) +- - // Sleep statDelay*batchSize between stats to smooth out I/O. - // - // The constants below were chosen using the following heuristics: - // - 1GB of filecache is on the order of ~100-200k files, in which case -- // 100μs delay per file introduces 10-20s of additional walk time, less -- // than the 1m gc period. +- // 100μs delay per file introduces 10-20s of additional walk time, +- // less than the minPeriod. - // - Processing batches of stats at once is much more efficient than - // sleeping after every stat (due to OS optimizations). - const statDelay = 100 * time.Microsecond // average delay between stats, to smooth out I/O @@ -38756,8 +43516,9 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec - for { - // Enumerate all files in the cache. - type item struct { -- path string -- stat os.FileInfo +- path string +- mtime time.Time +- size int64 - } - var files []item - start := time.Now() @@ -38783,7 +43544,7 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec - } - os.Remove(path) // ignore error - } else { -- files = append(files, item{path, stat}) +- files = append(files, item{path, stat.ModTime(), stat.Size()}) - total += stat.Size() - if debug && len(files)%1000 == 0 { - log.Printf("filecache: checked %d files in %v", len(files), time.Since(start)) @@ -38798,7 +43559,7 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec - - // Sort oldest files first. - sort.Slice(files, func(i, j int) bool { -- return files[i].stat.ModTime().Before(files[j].stat.ModTime()) +- return files[i].mtime.Before(files[j].mtime) - }) - - // Delete oldest files until we're under budget. @@ -38808,15 +43569,17 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec - break - } - if debug { -- age := time.Since(file.stat.ModTime()) +- age := time.Since(file.mtime) - log.Printf("budget: deleting stale file %s (%dB, age %v)", -- file.path, file.stat.Size(), age) +- file.path, file.size, age) - } - os.Remove(file.path) // ignore error -- total -= file.stat.Size() +- total -= file.size - } +- files = nil // release memory before sleep - -- time.Sleep(period) +- // Wait unconditionally for the minimum period. +- time.Sleep(minPeriod) - - // Once only, delete all directories. - // This will succeed only for the empty ones, @@ -38852,12 +43615,76 @@ diff -urN a/gopls/internal/lsp/filecache/filecache.go b/gopls/internal/lsp/filec - log.Printf("deleted %d empty directories", deleted) - } - } +- +- // Wait up to the max period, +- // or for Set activity in this process. +- select { +- case <-active: +- case <-time.After(maxPeriod): +- } +- } +-} +- +-func init() { +- // Register a handler to durably record this process's first +- // assertion failure in the cache so that we can ask users to +- // share this information via the stats command. +- bug.Handle(func(bug bug.Bug) { +- // Wait for cache init (bugs in tests happen early). +- _, _ = getCacheDir() +- +- data, err := json.Marshal(bug) +- if err != nil { +- panic(fmt.Sprintf("error marshalling bug %+v: %v", bug, err)) +- } +- +- key := sha256.Sum256(data) +- _ = Set(bugKind, key, data) +- }) +-} +- +-// BugReports returns a new unordered array of the contents +-// of all cached bug reports produced by this executable. +-// It also returns the location of the cache directory +-// used by this process (or "" on initialization error). +-func BugReports() (string, []bug.Bug) { +- // To test this logic, run: +- // $ TEST_GOPLS_BUG=oops gopls bug # trigger a bug +- // $ gopls stats # list the bugs +- +- dir, err := getCacheDir() +- if err != nil { +- return "", nil // ignore initialization errors - } +- var result []bug.Bug +- _ = filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error { +- if err != nil { +- return nil // ignore readdir/stat errors +- } +- // Parse the key from each "XXXX-bug" cache file name. +- if !info.IsDir() && strings.HasSuffix(path, bugKind) { +- var key [32]byte +- n, err := hex.Decode(key[:], []byte(filepath.Base(path)[:len(key)*2])) +- if err != nil || n != len(key) { +- return nil // ignore malformed file names +- } +- content, err := Get(bugKind, key) +- if err == nil { // ignore read errors +- var b bug.Bug +- if err := json.Unmarshal(content, &b); err != nil { +- log.Printf("error marshalling bug %q: %v", string(content), err) +- } +- result = append(result, b) +- } +- } +- return nil +- }) +- return dir, result -} diff -urN a/gopls/internal/lsp/filecache/filecache_test.go b/gopls/internal/lsp/filecache/filecache_test.go --- a/gopls/internal/lsp/filecache/filecache_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/filecache/filecache_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,215 +0,0 @@ ++++ b/gopls/internal/lsp/filecache/filecache_test.go 1970-01-01 08:00:00 +@@ -1,265 +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. @@ -38878,10 +43705,12 @@ diff -urN a/gopls/internal/lsp/filecache/filecache_test.go b/gopls/internal/lsp/ - "os" - "os/exec" - "strconv" +- "strings" - "testing" - - "golang.org/x/sync/errgroup" - "golang.org/x/tools/gopls/internal/lsp/filecache" +- "golang.org/x/tools/internal/testenv" -) - -func TestBasics(t *testing.T) { @@ -38891,6 +43720,10 @@ diff -urN a/gopls/internal/lsp/filecache/filecache_test.go b/gopls/internal/lsp/ - - // Get of a never-seen key returns not found. - if _, err := filecache.Get(kind, key); err != filecache.ErrNotFound { +- if strings.Contains(err.Error(), "operation not supported") || +- strings.Contains(err.Error(), "not implemented") { +- t.Skipf("skipping: %v", err) +- } - t.Errorf("Get of random key returned err=%q, want not found", err) - } - @@ -38914,6 +43747,9 @@ diff -urN a/gopls/internal/lsp/filecache/filecache_test.go b/gopls/internal/lsp/ - -// TestConcurrency exercises concurrent access to the same entry. -func TestConcurrency(t *testing.T) { +- if os.Getenv("GO_BUILDER_NAME") == "plan9-arm" { +- t.Skip(`skipping on plan9-arm builder due to golang/go#58748: failing with 'mount rpc error'`) +- } - const kind = "TestConcurrency" - key := uniqueKey() - const N = 100 // concurrency level @@ -38956,6 +43792,10 @@ diff -urN a/gopls/internal/lsp/filecache/filecache_test.go b/gopls/internal/lsp/ - group.Go(func() error { return get(false) }) - } - if err := group.Wait(); err != nil { +- if strings.Contains(err.Error(), "operation not supported") || +- strings.Contains(err.Error(), "not implemented") { +- t.Skipf("skipping: %v", err) +- } - t.Fatal(err) - } - @@ -38975,12 +43815,17 @@ diff -urN a/gopls/internal/lsp/filecache/filecache_test.go b/gopls/internal/lsp/ -// It calls Set(A) in the parent, { Get(A); Set(B) } in the child -// process, then Get(B) in the parent. -func TestIPC(t *testing.T) { +- testenv.NeedsExec(t) +- - keyA := uniqueKey() - keyB := uniqueKey() - value := []byte(testIPCValueA) - - // Set keyA. - if err := filecache.Set(testIPCKind, keyA, value); err != nil { +- if strings.Contains(err.Error(), "operation not supported") { +- t.Skipf("skipping: %v", err) +- } - t.Fatalf("Set: %v", err) - } - @@ -39060,6 +43905,7 @@ diff -urN a/gopls/internal/lsp/filecache/filecache_test.go b/gopls/internal/lsp/ - b.Fatal(err) - } - b.ResetTimer() +- b.SetBytes(int64(len(value))) - - var group errgroup.Group - group.SetLimit(50) @@ -39073,10 +43919,41 @@ diff -urN a/gopls/internal/lsp/filecache/filecache_test.go b/gopls/internal/lsp/ - b.Fatal(err) - } -} +- +-// These two benchmarks are asymmetric: the one for Get imposes a +-// modest bound on concurrency (50) whereas the one for Set imposes a +-// much higher concurrency (1000) to test the implementation's +-// self-imposed bound. +- +-func BenchmarkUncontendedSet(b *testing.B) { +- const kind = "BenchmarkUncontendedSet" +- key := uniqueKey() +- var value [8192]byte +- +- const P = 1000 // parallelism +- b.SetBytes(P * int64(len(value))) +- +- for i := 0; i < b.N; i++ { +- // Perform P concurrent calls to Set. All must succeed. +- var group errgroup.Group +- for range [P]bool{} { +- group.Go(func() error { +- return filecache.Set(kind, key, value[:]) +- }) +- } +- if err := group.Wait(); err != nil { +- if strings.Contains(err.Error(), "operation not supported") || +- strings.Contains(err.Error(), "not implemented") { +- b.Skipf("skipping: %v", err) +- } +- b.Fatal(err) +- } +- } +-} diff -urN a/gopls/internal/lsp/folding_range.go b/gopls/internal/lsp/folding_range.go --- a/gopls/internal/lsp/folding_range.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/folding_range.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,41 +0,0 @@ ++++ b/gopls/internal/lsp/folding_range.go 1970-01-01 08:00:00 +@@ -1,46 +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. @@ -39088,16 +43965,21 @@ diff -urN a/gopls/internal/lsp/folding_range.go b/gopls/internal/lsp/folding_ran - - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" -) - -func (s *Server) foldingRange(ctx context.Context, params *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) { +- ctx, done := event.Start(ctx, "lsp.Server.foldingRange", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go) - defer release() - if !ok { - return nil, err - } - -- ranges, err := source.FoldingRange(ctx, snapshot, fh, snapshot.View().Options().LineFoldingOnly) +- ranges, err := source.FoldingRange(ctx, snapshot, fh, snapshot.Options().LineFoldingOnly) - if err != nil { - return nil, err - } @@ -39120,8 +44002,8 @@ diff -urN a/gopls/internal/lsp/folding_range.go b/gopls/internal/lsp/folding_ran -} diff -urN a/gopls/internal/lsp/format.go b/gopls/internal/lsp/format.go --- a/gopls/internal/lsp/format.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/format.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,31 +0,0 @@ ++++ b/gopls/internal/lsp/format.go 1970-01-01 08:00:00 +@@ -1,36 +0,0 @@ -// Copyright 2018 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. @@ -39135,15 +44017,20 @@ diff -urN a/gopls/internal/lsp/format.go b/gopls/internal/lsp/format.go - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/lsp/work" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" -) - -func (s *Server) formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) { +- ctx, done := event.Start(ctx, "lsp.Server.formatting", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) - defer release() - if !ok { - return nil, err - } -- switch snapshot.View().FileKind(fh) { +- switch snapshot.FileKind(fh) { - case source.Mod: - return mod.Format(ctx, snapshot, fh) - case source.Go: @@ -39153,10 +44040,576 @@ diff -urN a/gopls/internal/lsp/format.go b/gopls/internal/lsp/format.go - } - return nil, nil -} +diff -urN a/gopls/internal/lsp/frob/frob.go b/gopls/internal/lsp/frob/frob.go +--- a/gopls/internal/lsp/frob/frob.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/frob/frob.go 1970-01-01 08:00:00 +@@ -1,439 +0,0 @@ +-// Copyright 2023 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 frob is a fast restricted object encoder/decoder in the +-// spirit of encoding/gob. +-// +-// As with gob, types that recursively contain functions, channels, +-// and unsafe.Pointers cannot be encoded, but frob has these +-// additional restrictions: +-// +-// - Interface values are not supported; this avoids the need for +-// the encoding to describe types. +-// +-// - Types that recursively contain private struct fields are not +-// permitted. +-// +-// - The encoding is unspecified and subject to change, so the encoder +-// and decoder must exactly agree on their implementation and on the +-// definitions of the target types. +-// +-// - Lengths (of arrays, slices, and maps) are currently assumed to +-// fit in 32 bits. +-// +-// - There is no error handling. All errors are reported by panicking. +-// +-// - Values are serialized as trees, not graphs, so shared subgraphs +-// are encoded repeatedly. +-// +-// - No attempt is made to detect cyclic data structures. +-package frob +- +-import ( +- "encoding/binary" +- "fmt" +- "math" +- "reflect" +- "sync" +-) +- +-// A Codec[T] is an immutable encoder and decoder for values of type T. +-type Codec[T any] struct{ frob *frob } +- +-// CodecFor[T] returns a codec for values of type T. +-// It panics if type T is unsuitable. +-func CodecFor[T any]() Codec[T] { +- frobsMu.Lock() +- defer frobsMu.Unlock() +- return Codec[T]{frobFor(reflect.TypeOf((*T)(nil)).Elem())} +-} +- +-func (codec Codec[T]) Encode(v T) []byte { return codec.frob.Encode(v) } +-func (codec Codec[T]) Decode(data []byte, ptr *T) { codec.frob.Decode(data, ptr) } +- +-var ( +- frobsMu sync.Mutex +- frobs = make(map[reflect.Type]*frob) +-) +- +-// A frob is an encoder/decoder for a specific type. +-type frob struct { +- t reflect.Type +- kind reflect.Kind +- elems []*frob // elem (array/slice/ptr), key+value (map), fields (struct) +-} +- +-// frobFor returns the frob for a particular type. +-// Precondition: caller holds frobsMu. +-func frobFor(t reflect.Type) *frob { +- fr, ok := frobs[t] +- if !ok { +- fr = &frob{t: t, kind: t.Kind()} +- frobs[t] = fr +- +- switch fr.kind { +- case reflect.Bool, +- reflect.Int, +- reflect.Int8, +- reflect.Int16, +- reflect.Int32, +- reflect.Int64, +- reflect.Uint, +- reflect.Uint8, +- reflect.Uint16, +- reflect.Uint32, +- reflect.Uint64, +- reflect.Uintptr, +- reflect.Float32, +- reflect.Float64, +- reflect.Complex64, +- reflect.Complex128, +- reflect.String: +- +- case reflect.Array, +- reflect.Slice, +- reflect.Ptr: // TODO(adonovan): after go1.18, use Pointer +- fr.addElem(fr.t.Elem()) +- +- case reflect.Map: +- fr.addElem(fr.t.Key()) +- fr.addElem(fr.t.Elem()) +- +- case reflect.Struct: +- for i := 0; i < fr.t.NumField(); i++ { +- field := fr.t.Field(i) +- if field.PkgPath != "" { +- panic(fmt.Sprintf("unexported field %v", field)) +- } +- fr.addElem(field.Type) +- } +- +- default: +- // chan, func, interface, unsafe.Pointer +- panic(fmt.Sprintf("type %v is not supported by frob", fr.t)) +- } +- } +- return fr +-} +- +-func (fr *frob) addElem(t reflect.Type) { +- fr.elems = append(fr.elems, frobFor(t)) +-} +- +-const magic = "frob" +- +-func (fr *frob) Encode(v any) []byte { +- rv := reflect.ValueOf(v) +- if rv.Type() != fr.t { +- panic(fmt.Sprintf("got %v, want %v", rv.Type(), fr.t)) +- } +- w := &writer{} +- w.bytes([]byte(magic)) +- fr.encode(w, rv) +- if uint64(len(w.data))>>32 != 0 { +- panic("too large") // includes all cases where len doesn't fit in 32 bits +- } +- return w.data +-} +- +-// encode appends the encoding of value v, whose type must be fr.t. +-func (fr *frob) encode(out *writer, v reflect.Value) { +- switch fr.kind { +- case reflect.Bool: +- var b byte +- if v.Bool() { +- b = 1 +- } +- out.uint8(b) +- case reflect.Int: +- out.uint64(uint64(v.Int())) +- case reflect.Int8: +- out.uint8(uint8(v.Int())) +- case reflect.Int16: +- out.uint16(uint16(v.Int())) +- case reflect.Int32: +- out.uint32(uint32(v.Int())) +- case reflect.Int64: +- out.uint64(uint64(v.Int())) +- case reflect.Uint: +- out.uint64(v.Uint()) +- case reflect.Uint8: +- out.uint8(uint8(v.Uint())) +- case reflect.Uint16: +- out.uint16(uint16(v.Uint())) +- case reflect.Uint32: +- out.uint32(uint32(v.Uint())) +- case reflect.Uint64: +- out.uint64(v.Uint()) +- case reflect.Uintptr: +- out.uint64(uint64(v.Uint())) +- case reflect.Float32: +- out.uint32(math.Float32bits(float32(v.Float()))) +- case reflect.Float64: +- out.uint64(math.Float64bits(v.Float())) +- case reflect.Complex64: +- z := complex64(v.Complex()) +- out.uint32(uint32(math.Float32bits(real(z)))) +- out.uint32(uint32(math.Float32bits(imag(z)))) +- case reflect.Complex128: +- z := v.Complex() +- out.uint64(math.Float64bits(real(z))) +- out.uint64(math.Float64bits(imag(z))) +- +- case reflect.Array: +- len := v.Type().Len() +- elem := fr.elems[0] +- for i := 0; i < len; i++ { +- elem.encode(out, v.Index(i)) +- } +- +- case reflect.Slice: +- len := v.Len() +- out.uint32(uint32(len)) +- if len > 0 { +- elem := fr.elems[0] +- if elem.kind == reflect.Uint8 { +- // []byte fast path +- out.bytes(v.Bytes()) +- } else { +- for i := 0; i < len; i++ { +- elem.encode(out, v.Index(i)) +- } +- } +- } +- +- case reflect.Map: +- len := v.Len() +- out.uint32(uint32(len)) +- if len > 0 { +- kfrob, vfrob := fr.elems[0], fr.elems[1] +- for iter := v.MapRange(); iter.Next(); { +- kfrob.encode(out, iter.Key()) +- vfrob.encode(out, iter.Value()) +- } +- } +- +- case reflect.Ptr: // TODO(adonovan): after go1.18, use Pointer +- if v.IsNil() { +- out.uint8(0) +- } else { +- out.uint8(1) +- fr.elems[0].encode(out, v.Elem()) +- } +- +- case reflect.String: +- len := v.Len() +- out.uint32(uint32(len)) +- if len > 0 { +- out.data = append(out.data, v.String()...) +- } +- +- case reflect.Struct: +- for i, elem := range fr.elems { +- elem.encode(out, v.Field(i)) +- } +- +- default: +- panic(fr.t) +- } +-} +- +-func (fr *frob) Decode(data []byte, ptr any) { +- rv := reflect.ValueOf(ptr).Elem() +- if rv.Type() != fr.t { +- panic(fmt.Sprintf("got %v, want %v", rv.Type(), fr.t)) +- } +- rd := &reader{data} +- if string(rd.bytes(4)) != magic { +- panic("not a frob-encoded message") +- } +- fr.decode(rd, rv) +- if len(rd.data) > 0 { +- panic("surplus bytes") +- } +-} +- +-// decode reads from in, decodes a value, and sets addr to it. +-// addr must be a zero-initialized addressable variable of type fr.t. +-func (fr *frob) decode(in *reader, addr reflect.Value) { +- switch fr.kind { +- case reflect.Bool: +- addr.SetBool(in.uint8() != 0) +- case reflect.Int: +- addr.SetInt(int64(in.uint64())) +- case reflect.Int8: +- addr.SetInt(int64(in.uint8())) +- case reflect.Int16: +- addr.SetInt(int64(in.uint16())) +- case reflect.Int32: +- addr.SetInt(int64(in.uint32())) +- case reflect.Int64: +- addr.SetInt(int64(in.uint64())) +- case reflect.Uint: +- addr.SetUint(in.uint64()) +- case reflect.Uint8: +- addr.SetUint(uint64(in.uint8())) +- case reflect.Uint16: +- addr.SetUint(uint64(in.uint16())) +- case reflect.Uint32: +- addr.SetUint(uint64(in.uint32())) +- case reflect.Uint64: +- addr.SetUint(in.uint64()) +- case reflect.Uintptr: +- addr.SetUint(in.uint64()) +- case reflect.Float32: +- addr.SetFloat(float64(math.Float32frombits(in.uint32()))) +- case reflect.Float64: +- addr.SetFloat(math.Float64frombits(in.uint64())) +- case reflect.Complex64: +- addr.SetComplex(complex128(complex( +- math.Float32frombits(in.uint32()), +- math.Float32frombits(in.uint32()), +- ))) +- case reflect.Complex128: +- addr.SetComplex(complex( +- math.Float64frombits(in.uint64()), +- math.Float64frombits(in.uint64()), +- )) +- +- case reflect.Array: +- len := fr.t.Len() +- for i := 0; i < len; i++ { +- fr.elems[0].decode(in, addr.Index(i)) +- } +- +- case reflect.Slice: +- len := int(in.uint32()) +- if len > 0 { +- elem := fr.elems[0] +- if elem.kind == reflect.Uint8 { +- // []byte fast path +- // (Not addr.SetBytes: we must make a copy.) +- addr.Set(reflect.AppendSlice(addr, reflect.ValueOf(in.bytes(len)))) +- } else { +- addr.Set(reflect.MakeSlice(fr.t, len, len)) +- for i := 0; i < len; i++ { +- elem.decode(in, addr.Index(i)) +- } +- } +- } +- +- case reflect.Map: +- len := int(in.uint32()) +- if len > 0 { +- m := reflect.MakeMapWithSize(fr.t, len) +- addr.Set(m) +- kfrob, vfrob := fr.elems[0], fr.elems[1] +- k := reflect.New(kfrob.t).Elem() +- v := reflect.New(vfrob.t).Elem() +- kzero := reflect.Zero(kfrob.t) +- vzero := reflect.Zero(vfrob.t) +- for i := 0; i < len; i++ { +- // TODO(adonovan): use SetZero from go1.20. +- // k.SetZero() +- // v.SetZero() +- k.Set(kzero) +- v.Set(vzero) +- kfrob.decode(in, k) +- vfrob.decode(in, v) +- m.SetMapIndex(k, v) +- } +- } +- +- case reflect.Ptr: // TODO(adonovan): after go1.18, use Pointer +- isNil := in.uint8() == 0 +- if !isNil { +- ptr := reflect.New(fr.elems[0].t) +- addr.Set(ptr) +- fr.elems[0].decode(in, ptr.Elem()) +- } +- +- case reflect.String: +- len := int(in.uint32()) +- if len > 0 { +- addr.SetString(string(in.bytes(len))) +- } +- +- case reflect.Struct: +- for i, elem := range fr.elems { +- elem.decode(in, addr.Field(i)) +- } +- +- default: +- panic(fr.t) +- } +-} +- +-var le = binary.LittleEndian +- +-type reader struct{ data []byte } +- +-func (r *reader) uint8() uint8 { +- v := r.data[0] +- r.data = r.data[1:] +- return v +-} +- +-func (r *reader) uint16() uint16 { +- v := le.Uint16(r.data) +- r.data = r.data[2:] +- return v +-} +- +-func (r *reader) uint32() uint32 { +- v := le.Uint32(r.data) +- r.data = r.data[4:] +- return v +-} +- +-func (r *reader) uint64() uint64 { +- v := le.Uint64(r.data) +- r.data = r.data[8:] +- return v +-} +- +-func (r *reader) bytes(n int) []byte { +- v := r.data[:n] +- r.data = r.data[n:] +- return v +-} +- +-type writer struct{ data []byte } +- +-func (w *writer) uint8(v uint8) { w.data = append(w.data, v) } +-func (w *writer) uint16(v uint16) { w.data = appendUint16(w.data, v) } +-func (w *writer) uint32(v uint32) { w.data = appendUint32(w.data, v) } +-func (w *writer) uint64(v uint64) { w.data = appendUint64(w.data, v) } +-func (w *writer) bytes(v []byte) { w.data = append(w.data, v...) } +- +-// TODO(adonovan): delete these as in go1.19 they are methods on LittleEndian: +- +-func appendUint16(b []byte, v uint16) []byte { +- return append(b, +- byte(v), +- byte(v>>8), +- ) +-} +- +-func appendUint32(b []byte, v uint32) []byte { +- return append(b, +- byte(v), +- byte(v>>8), +- byte(v>>16), +- byte(v>>24), +- ) +-} +- +-func appendUint64(b []byte, v uint64) []byte { +- return append(b, +- byte(v), +- byte(v>>8), +- byte(v>>16), +- byte(v>>24), +- byte(v>>32), +- byte(v>>40), +- byte(v>>48), +- byte(v>>56), +- ) +-} +diff -urN a/gopls/internal/lsp/frob/frob_test.go b/gopls/internal/lsp/frob/frob_test.go +--- a/gopls/internal/lsp/frob/frob_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/frob/frob_test.go 1970-01-01 08:00:00 +@@ -1,119 +0,0 @@ +-// Copyright 2023 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 frob_test +- +-import ( +- "math" +- "reflect" +- "testing" +- +- "golang.org/x/tools/gopls/internal/lsp/frob" +-) +- +-func TestBasics(t *testing.T) { +- type Basics struct { +- A []*string +- B [2]int +- C *Basics +- D map[string]int +- E []byte +- F []string +- } +- codec := frob.CodecFor[Basics]() +- +- s1, s2 := "hello", "world" +- x := Basics{ +- A: []*string{&s1, nil, &s2}, +- B: [...]int{1, 2}, +- C: &Basics{ +- B: [...]int{3, 4}, +- D: map[string]int{"one": 1}, +- }, +- E: []byte("hello"), +- F: []string{s1, s2}, +- } +- var y Basics +- codec.Decode(codec.Encode(x), &y) +- if !reflect.DeepEqual(x, y) { +- t.Fatalf("bad roundtrip: got %#v, want %#v", y, x) +- } +-} +- +-func TestInts(t *testing.T) { +- type Ints struct { +- U uint +- U8 uint8 +- U16 uint16 +- U32 uint32 +- U64 uint64 +- UP uintptr +- I int +- I8 int8 +- I16 int16 +- I32 int32 +- I64 int64 +- F32 float32 +- F64 float64 +- C64 complex64 +- C128 complex128 +- } +- codec := frob.CodecFor[Ints]() +- +- // maxima +- max1 := Ints{ +- U: math.MaxUint, +- U8: math.MaxUint8, +- U16: math.MaxUint16, +- U32: math.MaxUint32, +- U64: math.MaxUint64, +- UP: math.MaxUint, +- I: math.MaxInt, +- I8: math.MaxInt8, +- I16: math.MaxInt16, +- I32: math.MaxInt32, +- I64: math.MaxInt64, +- F32: math.MaxFloat32, +- F64: math.MaxFloat64, +- C64: complex(math.MaxFloat32, math.MaxFloat32), +- C128: complex(math.MaxFloat64, math.MaxFloat64), +- } +- var max2 Ints +- codec.Decode(codec.Encode(max1), &max2) +- if !reflect.DeepEqual(max1, max2) { +- t.Fatalf("max: bad roundtrip: got %#v, want %#v", max2, max1) +- } +- +- // minima +- min1 := Ints{ +- I: math.MinInt, +- I8: math.MinInt8, +- I16: math.MinInt16, +- I32: math.MinInt32, +- I64: math.MinInt64, +- F32: -math.MaxFloat32, +- F64: -math.MaxFloat32, +- C64: complex(-math.MaxFloat32, -math.MaxFloat32), +- C128: complex(-math.MaxFloat64, -math.MaxFloat64), +- } +- var min2 Ints +- codec.Decode(codec.Encode(min1), &min2) +- if !reflect.DeepEqual(min1, min2) { +- t.Fatalf("min: bad roundtrip: got %#v, want %#v", min2, min1) +- } +- +- // negatives (other than MinInt), to exercise conversions +- neg1 := Ints{ +- I: -1, +- I8: -1, +- I16: -1, +- I32: -1, +- I64: -1, +- } +- var neg2 Ints +- codec.Decode(codec.Encode(neg1), &neg2) +- if !reflect.DeepEqual(neg1, neg2) { +- t.Fatalf("neg: bad roundtrip: got %#v, want %#v", neg2, neg1) +- } +-} diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go --- a/gopls/internal/lsp/general.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/general.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,619 +0,0 @@ ++++ b/gopls/internal/lsp/general.go 1970-01-01 08:00:00 +@@ -1,707 +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. @@ -39167,6 +44620,7 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - "context" - "encoding/json" - "fmt" +- "go/build" - "log" - "os" - "path" @@ -39175,16 +44629,22 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - "strings" - "sync" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/debug" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" +- "golang.org/x/tools/gopls/internal/telemetry" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/jsonrpc2" -) - -func (s *Server) initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) { +- ctx, done := event.Start(ctx, "lsp.Server.initialize") +- defer done() +- +- telemetry.RecordClientInfo(params) +- - s.stateMu.Lock() - if s.state >= serverInitializing { - defer s.stateMu.Unlock() @@ -39209,27 +44669,29 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - } - s.progress.SetSupportsWorkDoneProgress(params.Capabilities.Window.WorkDoneProgress) - -- options := s.session.Options() -- defer func() { s.session.SetOptions(options) }() +- options := s.Options().Clone() +- // TODO(rfindley): remove the error return from handleOptionResults, and +- // eliminate this defer. +- defer func() { s.SetOptions(options) }() - - if err := s.handleOptionResults(ctx, source.SetOptions(options, params.InitializationOptions)); err != nil { - return nil, err - } -- options.ForClientCapabilities(params.Capabilities) +- options.ForClientCapabilities(params.ClientInfo, params.Capabilities) - - if options.ShowBugReports { - // Report the next bug that occurs on the server. -- bugCh := bug.Notify() -- go func() { -- b := <-bugCh +- bug.Handle(func(b bug.Bug) { - msg := &protocol.ShowMessageParams{ - Type: protocol.Error, - Message: fmt.Sprintf("A bug occurred on the server: %s\nLocation:%s", b.Description, b.Key), - } -- if err := s.eventuallyShowMessage(context.Background(), msg); err != nil { -- log.Printf("error showing bug: %v", err) -- } -- }() +- go func() { +- if err := s.eventuallyShowMessage(context.Background(), msg); err != nil { +- log.Printf("error showing bug: %v", err) +- } +- }() +- }) - } - - folders := params.WorkspaceFolders @@ -39308,7 +44770,7 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - DocumentSymbolProvider: &protocol.Or_ServerCapabilities_documentSymbolProvider{Value: true}, - WorkspaceSymbolProvider: &protocol.Or_ServerCapabilities_workspaceSymbolProvider{Value: true}, - ExecuteCommandProvider: &protocol.ExecuteCommandOptions{ -- Commands: options.SupportedCommands, +- Commands: nonNilSliceString(options.SupportedCommands), - }, - FoldingRangeProvider: &protocol.Or_ServerCapabilities_foldingRangeProvider{Value: true}, - HoverProvider: &protocol.Or_ServerCapabilities_hoverProvider{Value: true}, @@ -39322,8 +44784,8 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - Range: &protocol.Or_SemanticTokensOptions_range{Value: true}, - Full: &protocol.Or_SemanticTokensOptions_full{Value: true}, - Legend: protocol.SemanticTokensLegend{ -- TokenTypes: s.session.Options().SemanticTypes, -- TokenModifiers: s.session.Options().SemanticMods, +- TokenTypes: nonNilSliceString(s.Options().SemanticTypes), +- TokenModifiers: nonNilSliceString(s.Options().SemanticMods), - }, - }, - SignatureHelpProvider: &protocol.SignatureHelpOptions{ @@ -39351,6 +44813,9 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go -} - -func (s *Server) initialized(ctx context.Context, params *protocol.InitializedParams) error { +- ctx, done := event.Start(ctx, "lsp.Server.initialized") +- defer done() +- - s.stateMu.Lock() - if s.state >= serverInitialized { - defer s.stateMu.Unlock() @@ -39364,9 +44829,7 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - } - s.notifications = nil - -- options := s.session.Options() -- defer func() { s.session.SetOptions(options) }() -- +- options := s.Options() - if err := s.addFolders(ctx, s.pendingFolders); err != nil { - return err - } @@ -39387,19 +44850,26 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - return err - } - } +- +- // Ask (maybe) about enabling telemetry. Do this asynchronously, as it's OK +- // for users to ignore or dismiss the question. +- go s.maybePromptForTelemetry(ctx, options.TelemetryPrompt) +- - return nil -} - -// GoVersionTable maps Go versions to the gopls version in which support will -// be deprecated, and the final gopls version supporting them without warnings. --// Keep this in sync with gopls/README.md +-// Keep this in sync with gopls/README.md. -// -// Must be sorted in ascending order of Go version. -// -// Mutable for testing. -var GoVersionTable = []GoVersionSupport{ - {12, "", "v0.7.5"}, -- {15, "v0.11.0", "v0.9.5"}, +- {15, "", "v0.9.5"}, +- {16, "v0.13.0", "v0.11.0"}, +- {17, "v0.13.0", "v0.11.0"}, -} - -// GoVersionSupport holds information about end-of-life Go version support. @@ -39415,11 +44885,13 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - return GoVersionTable[len(GoVersionTable)-1].GoVersion + 1 -} - --// versionMessage returns the warning/error message to display if the user is --// on the given Go version, if any. The goVersion variable is the X in Go 1.X. +-// versionMessage returns the warning/error message to display if the user has +-// the given Go version, if any. The goVersion variable is the X in Go 1.X. If +-// fromBuild is set, the Go version is the version used to build gopls. +-// Otherwise, it is the go command version. -// -// If goVersion is invalid (< 0), it returns "", 0. --func versionMessage(goVersion int) (string, protocol.MessageType) { +-func versionMessage(goVersion int, fromBuild bool) (string, protocol.MessageType) { - if goVersion < 0 { - return "", 0 - } @@ -39429,7 +44901,11 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - var msgBuilder strings.Builder - - mType := protocol.Error -- fmt.Fprintf(&msgBuilder, "Found Go version 1.%d", goVersion) +- if fromBuild { +- fmt.Fprintf(&msgBuilder, "Gopls was built with Go version 1.%d", goVersion) +- } else { +- fmt.Fprintf(&msgBuilder, "Found Go version 1.%d", goVersion) +- } - if v.DeprecatedVersion != "" { - // not deprecated yet, just a warning - fmt.Fprintf(&msgBuilder, ", which will be unsupported by gopls %s. ", v.DeprecatedVersion) @@ -39452,15 +44928,16 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go -// -// It should be called after views change. -func (s *Server) checkViewGoVersions() { -- oldestVersion := -1 +- oldestVersion, fromBuild := go1Point(), true - for _, view := range s.session.Views() { - viewVersion := view.GoVersion() - if oldestVersion == -1 || viewVersion < oldestVersion { -- oldestVersion = viewVersion +- oldestVersion, fromBuild = viewVersion, false - } +- telemetry.RecordViewGoVersion(viewVersion) - } - -- if msg, mType := versionMessage(oldestVersion); msg != "" { +- if msg, mType := versionMessage(oldestVersion, fromBuild); msg != "" { - s.eventuallyShowMessage(context.Background(), &protocol.ShowMessageParams{ - Type: mType, - Message: msg, @@ -39468,12 +44945,27 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - } -} - +-// go1Point returns the x in Go 1.x. If an error occurs extracting the go +-// version, it returns -1. +-// +-// Copied from the testenv package. +-func go1Point() int { +- for i := len(build.Default.ReleaseTags) - 1; i >= 0; i-- { +- var version int +- if _, err := fmt.Sscanf(build.Default.ReleaseTags[i], "go1.%d", &version); err != nil { +- continue +- } +- return version +- } +- return -1 +-} +- -func (s *Server) addFolders(ctx context.Context, folders []protocol.WorkspaceFolder) error { - originalViews := len(s.session.Views()) - viewErrors := make(map[span.URI]error) - - var ndiagnose sync.WaitGroup // number of unfinished diagnose calls -- if s.session.Options().VerboseWorkDoneProgress { +- if s.Options().VerboseWorkDoneProgress { - work := s.progress.Start(ctx, DiagnosticWorkTitle(FromInitialWorkspaceLoad), "Calculating diagnostics for initial workspace load...", nil, nil) - defer func() { - go func() { @@ -39515,7 +45007,7 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - // Diagnose the newly created view asynchronously. - ndiagnose.Add(1) - go func() { -- s.diagnoseDetached(snapshot) +- s.diagnoseSnapshot(snapshot, nil, false, 0) - <-initialized - release() - ndiagnose.Done() @@ -39598,14 +45090,13 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - -// registerWatchedDirectoriesLocked sends the workspace/didChangeWatchedFiles -// registrations to the client and updates s.watchedDirectories. +-// The caller must not subsequently mutate patterns. -func (s *Server) registerWatchedDirectoriesLocked(ctx context.Context, patterns map[string]struct{}) error { -- if !s.session.Options().DynamicWatchedFilesSupported { +- if !s.Options().DynamicWatchedFilesSupported { - return nil - } -- for k := range s.watchedGlobPatterns { -- delete(s.watchedGlobPatterns, k) -- } -- var watchers []protocol.FileSystemWatcher +- s.watchedGlobPatterns = patterns +- watchers := make([]protocol.FileSystemWatcher, 0, len(patterns)) // must be a slice - val := protocol.WatchChange | protocol.WatchDelete | protocol.WatchCreate - for pattern := range patterns { - watchers = append(watchers, protocol.FileSystemWatcher{ @@ -39626,16 +45117,30 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - return err - } - s.watchRegistrationCount++ -- -- for k, v := range patterns { -- s.watchedGlobPatterns[k] = v -- } - return nil -} - --func (s *Server) fetchConfig(ctx context.Context, name string, folder span.URI, o *source.Options) error { -- if !s.session.Options().ConfigurationSupported { -- return nil +-// Options returns the current server options. +-// +-// The caller must not modify the result. +-func (s *Server) Options() *source.Options { +- s.optionsMu.Lock() +- defer s.optionsMu.Unlock() +- return s.options +-} +- +-// SetOptions sets the current server options. +-// +-// The caller must not subsequently modify the options. +-func (s *Server) SetOptions(opts *source.Options) { +- s.optionsMu.Lock() +- defer s.optionsMu.Unlock() +- s.options = opts +-} +- +-func (s *Server) fetchFolderOptions(ctx context.Context, folder span.URI) (*source.Options, error) { +- if opts := s.Options(); !opts.ConfigurationSupported { +- return opts, nil - } - configs, err := s.client.Configuration(ctx, &protocol.ParamConfiguration{ - Items: []protocol.ConfigurationItem{{ @@ -39645,14 +45150,16 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - }, - ) - if err != nil { -- return fmt.Errorf("failed to get workspace configuration from client (%s): %v", folder, err) +- return nil, fmt.Errorf("failed to get workspace configuration from client (%s): %v", folder, err) - } +- +- folderOpts := s.Options().Clone() - for _, config := range configs { -- if err := s.handleOptionResults(ctx, source.SetOptions(o, config)); err != nil { -- return err +- if err := s.handleOptionResults(ctx, source.SetOptions(folderOpts, config)); err != nil { +- return nil, err - } - } -- return nil +- return folderOpts, nil -} - -func (s *Server) eventuallyShowMessage(ctx context.Context, msg *protocol.ShowMessageParams) error { @@ -39728,12 +45235,12 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - if err != nil { - return nil, nil, false, func() {}, err - } -- fh, err := snapshot.GetFile(ctx, uri) +- fh, err := snapshot.ReadFile(ctx, uri) - if err != nil { - release() - return nil, nil, false, func() {}, err - } -- if expectKind != source.UnknownKind && view.FileKind(fh) != expectKind { +- if expectKind != source.UnknownKind && snapshot.FileKind(fh) != expectKind { - // Wrong kind of file. Nothing to do. - release() - return nil, nil, false, func() {}, nil @@ -39744,6 +45251,9 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go -// shutdown implements the 'shutdown' LSP handler. It releases resources -// associated with the server and waits for all ongoing work to complete. -func (s *Server) shutdown(ctx context.Context) error { +- ctx, done := event.Start(ctx, "lsp.Server.shutdown") +- defer done() +- - s.stateMu.Lock() - defer s.stateMu.Unlock() - if s.state < serverInitialized { @@ -39763,6 +45273,9 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go -} - -func (s *Server) exit(ctx context.Context) error { +- ctx, done := event.Start(ctx, "lsp.Server.exit") +- defer done() +- - s.stateMu.Lock() - defer s.stateMu.Unlock() - @@ -39772,14 +45285,42 @@ diff -urN a/gopls/internal/lsp/general.go b/gopls/internal/lsp/general.go - // TODO: We should be able to do better than this. - os.Exit(1) - } -- // we don't terminate the process on a normal exit, we just allow it to +- // We don't terminate the process on a normal exit, we just allow it to - // close naturally if needed after the connection is closed. - return nil -} +- +-// TODO: when we can assume go1.18, replace with generic +-// (after retiring support for go1.17) +-func nonNilSliceString(x []string) []string { +- if x == nil { +- return []string{} +- } +- return x +-} +-func nonNilSliceTextEdit(x []protocol.TextEdit) []protocol.TextEdit { +- if x == nil { +- return []protocol.TextEdit{} +- } +- +- return x +-} +-func nonNilSliceCompletionItemTag(x []protocol.CompletionItemTag) []protocol.CompletionItemTag { +- if x == nil { +- return []protocol.CompletionItemTag{} +- } +- return x +-} +-func emptySliceDiagnosticTag(x []protocol.DiagnosticTag) []protocol.DiagnosticTag { +- if x == nil { +- return []protocol.DiagnosticTag{} +- } +- return x +-} diff -urN a/gopls/internal/lsp/general_test.go b/gopls/internal/lsp/general_test.go --- a/gopls/internal/lsp/general_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/general_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,44 +0,0 @@ ++++ b/gopls/internal/lsp/general_test.go 1970-01-01 08:00:00 +@@ -1,48 +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. @@ -39796,18 +45337,22 @@ diff -urN a/gopls/internal/lsp/general_test.go b/gopls/internal/lsp/general_test -func TestVersionMessage(t *testing.T) { - tests := []struct { - goVersion int +- fromBuild bool - wantContains []string // string fragments that we expect to see - wantType protocol.MessageType - }{ -- {-1, nil, 0}, -- {12, []string{"1.12", "not supported", "upgrade to Go 1.16", "install gopls v0.7.5"}, protocol.Error}, -- {13, []string{"1.13", "will be unsupported by gopls v0.11.0", "upgrade to Go 1.16", "install gopls v0.9.5"}, protocol.Warning}, -- {15, []string{"1.15", "will be unsupported by gopls v0.11.0", "upgrade to Go 1.16", "install gopls v0.9.5"}, protocol.Warning}, -- {16, nil, 0}, +- {-1, false, nil, 0}, +- {12, false, []string{"1.12", "not supported", "upgrade to Go 1.18", "install gopls v0.7.5"}, protocol.Error}, +- {13, false, []string{"1.13", "not supported", "upgrade to Go 1.18", "install gopls v0.9.5"}, protocol.Error}, +- {15, false, []string{"1.15", "not supported", "upgrade to Go 1.18", "install gopls v0.9.5"}, protocol.Error}, +- {15, true, []string{"Gopls was built with Go version 1.15", "not supported", "upgrade to Go 1.18", "install gopls v0.9.5"}, protocol.Error}, +- {16, false, []string{"1.16", "will be unsupported by gopls v0.13.0", "upgrade to Go 1.18", "install gopls v0.11.0"}, protocol.Warning}, +- {17, false, []string{"1.17", "will be unsupported by gopls v0.13.0", "upgrade to Go 1.18", "install gopls v0.11.0"}, protocol.Warning}, +- {17, true, []string{"Gopls was built with Go version 1.17", "will be unsupported by gopls v0.13.0", "upgrade to Go 1.18", "install gopls v0.11.0"}, protocol.Warning}, - } - - for _, test := range tests { -- gotMsg, gotType := versionMessage(test.goVersion) +- gotMsg, gotType := versionMessage(test.goVersion, test.fromBuild) - - if len(test.wantContains) == 0 && gotMsg != "" { - t.Errorf("versionMessage(%d) = %q, want \"\"", test.goVersion, gotMsg) @@ -39826,7 +45371,7 @@ diff -urN a/gopls/internal/lsp/general_test.go b/gopls/internal/lsp/general_test -} diff -urN a/gopls/internal/lsp/glob/glob.go b/gopls/internal/lsp/glob/glob.go --- a/gopls/internal/lsp/glob/glob.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/glob/glob.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/glob/glob.go 1970-01-01 08:00:00 @@ -1,349 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -40179,7 +45724,7 @@ diff -urN a/gopls/internal/lsp/glob/glob.go b/gopls/internal/lsp/glob/glob.go -} diff -urN a/gopls/internal/lsp/glob/glob_test.go b/gopls/internal/lsp/glob/glob_test.go --- a/gopls/internal/lsp/glob/glob_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/glob/glob_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/glob/glob_test.go 1970-01-01 08:00:00 @@ -1,118 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -40299,277 +45844,9 @@ diff -urN a/gopls/internal/lsp/glob/glob_test.go b/gopls/internal/lsp/glob/glob_ - } - } -} -diff -urN a/gopls/internal/lsp/helper/helper.go b/gopls/internal/lsp/helper/helper.go ---- a/gopls/internal/lsp/helper/helper.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/helper/helper.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,264 +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. -- --// The helper command generates the declaration of the concrete --// 'server' type that implements the abstract Server interface defined --// in protocol/tsserver.go (which is itself generated from the LSP --// protocol). --// --// To run, invoke "go generate" in the parent (lsp) directory. --// --// TODO(adonovan): merge this into the main LSP generator. --package main -- --import ( -- "bytes" -- "flag" -- "fmt" -- "go/ast" -- "go/format" -- "go/parser" -- "go/token" -- "log" -- "os" -- "sort" -- "strings" -- "text/template" --) -- --var ( -- typ = flag.String("t", "Server", "generate code for this type") -- def = flag.String("d", "", "the file the type is defined in") // this relies on punning -- use = flag.String("u", "", "look for uses in this package") -- out = flag.String("o", "", "where to write the generated file") --) -- --func main() { -- log.SetFlags(log.Lshortfile) -- flag.Parse() -- if *typ == "" || *def == "" || *use == "" || *out == "" { -- flag.PrintDefaults() -- os.Exit(1) -- } -- // read the type definition and see what methods we're looking for -- doTypes() -- -- // parse the package and see which methods are defined -- doUses() -- -- output() --} -- --// replace "\\\n" with nothing before using --var tmpl = `// 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 lsp -- --// code generated by helper. DO NOT EDIT. -- --import ( -- "context" -- -- "golang.org/x/tools/gopls/internal/lsp/protocol" --) -- --{{range $key, $v := .Stuff}} --func (s *{{$.Type}}) {{$v.Name}}({{.Param}}) {{.Result}} { -- {{if ne .Found ""}} return s.{{.Internal}}({{.Invoke}})\ -- {{else}}return {{if lt 1 (len .Results)}}nil, {{end}}notImplemented("{{.Name}}"){{end}} --} --{{end}} --` -- --func output() { -- // put in empty param names as needed -- for _, t := range types { -- if t.paramnames == nil { -- t.paramnames = make([]string, len(t.paramtypes)) -- } -- for i, p := range t.paramtypes { -- cm := "" -- if i > 0 { -- cm = ", " -- } -- t.Param += fmt.Sprintf("%s%s %s", cm, t.paramnames[i], p) -- this := t.paramnames[i] -- if this == "_" { -- this = "nil" -- } -- t.Invoke += fmt.Sprintf("%s%s", cm, this) -- } -- if len(t.Results) > 1 { -- t.Result = "(" -- } -- for i, r := range t.Results { -- cm := "" -- if i > 0 { -- cm = ", " -- } -- t.Result += fmt.Sprintf("%s%s", cm, r) -- } -- if len(t.Results) > 1 { -- t.Result += ")" -- } -- } -- -- fd, err := os.Create(*out) -- if err != nil { -- log.Fatal(err) -- } -- t, err := template.New("foo").Parse(tmpl) -- if err != nil { -- log.Fatal(err) -- } -- type par struct { -- Type string -- Stuff []*Function -- } -- p := par{*typ, types} -- if false { // debugging the template -- t.Execute(os.Stderr, &p) -- } -- buf := bytes.NewBuffer(nil) -- err = t.Execute(buf, &p) -- if err != nil { -- log.Fatal(err) -- } -- ans, err := format.Source(bytes.Replace(buf.Bytes(), []byte("\\\n"), []byte{}, -1)) -- if err != nil { -- log.Fatal(err) -- } -- fd.Write(ans) --} -- --func doUses() { -- fset := token.NewFileSet() -- pkgs, err := parser.ParseDir(fset, *use, nil, 0) -- if err != nil { -- log.Fatalf("%q:%v", *use, err) -- } -- pkg := pkgs["lsp"] // CHECK -- files := pkg.Files -- for fname, f := range files { -- for _, d := range f.Decls { -- fd, ok := d.(*ast.FuncDecl) -- if !ok { -- continue -- } -- nm := fd.Name.String() -- if ast.IsExported(nm) { -- // we're looking for things like didChange -- continue -- } -- if fx, ok := byname[nm]; ok { -- if fx.Found != "" { -- log.Fatalf("found %s in %s and %s", fx.Internal, fx.Found, fname) -- } -- fx.Found = fname -- // and the Paramnames -- ft := fd.Type -- for _, f := range ft.Params.List { -- nm := "" -- if len(f.Names) > 0 { -- nm = f.Names[0].String() -- if nm == "_" { -- nm = "_gen" -- } -- } -- fx.paramnames = append(fx.paramnames, nm) -- } -- } -- } -- } -- if false { -- for i, f := range types { -- log.Printf("%d %s %s", i, f.Internal, f.Found) -- } -- } --} -- --type Function struct { -- Name string -- Internal string // first letter lower case -- paramtypes []string -- paramnames []string -- Results []string -- Param string -- Result string // do it in code, easier than in a template -- Invoke string -- Found string // file it was found in --} -- --var types []*Function --var byname = map[string]*Function{} // internal names -- --func doTypes() { -- fset := token.NewFileSet() -- f, err := parser.ParseFile(fset, *def, nil, 0) -- if err != nil { -- log.Fatal(err) -- } -- fd, err := os.Create("/tmp/ast") -- if err != nil { -- log.Fatal(err) -- } -- ast.Fprint(fd, fset, f, ast.NotNilFilter) -- ast.Inspect(f, inter) -- sort.Slice(types, func(i, j int) bool { return types[i].Name < types[j].Name }) -- if false { -- for i, f := range types { -- log.Printf("%d %s(%v) %v", i, f.Name, f.paramtypes, f.Results) -- } -- } --} -- --func inter(n ast.Node) bool { -- x, ok := n.(*ast.TypeSpec) -- if !ok || x.Name.Name != *typ { -- return true -- } -- m := x.Type.(*ast.InterfaceType).Methods.List -- for _, fld := range m { -- fn := fld.Type.(*ast.FuncType) -- p := fn.Params.List -- r := fn.Results.List -- fx := &Function{ -- Name: fld.Names[0].String(), -- } -- fx.Internal = strings.ToLower(fx.Name[:1]) + fx.Name[1:] -- for _, f := range p { -- fx.paramtypes = append(fx.paramtypes, whatis(f.Type)) -- } -- for _, f := range r { -- fx.Results = append(fx.Results, whatis(f.Type)) -- } -- types = append(types, fx) -- byname[fx.Internal] = fx -- } -- return false --} -- --func whatis(x ast.Expr) string { -- switch n := x.(type) { -- case *ast.SelectorExpr: -- return whatis(n.X) + "." + n.Sel.String() -- case *ast.StarExpr: -- return "*" + whatis(n.X) -- case *ast.Ident: -- if ast.IsExported(n.Name) { -- // these are from package protocol -- return "protocol." + n.Name -- } -- return n.Name -- case *ast.ArrayType: -- return "[]" + whatis(n.Elt) -- case *ast.InterfaceType: -- return "interface{}" -- default: -- log.Fatalf("Fatal %T", x) -- return fmt.Sprintf("%T", x) -- } --} diff -urN a/gopls/internal/lsp/helper/README.md b/gopls/internal/lsp/helper/README.md --- a/gopls/internal/lsp/helper/README.md 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/helper/README.md 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/helper/README.md 1970-01-01 08:00:00 @@ -1,35 +0,0 @@ -# Generate server_gen.go - @@ -40606,10 +45883,278 @@ diff -urN a/gopls/internal/lsp/helper/README.md b/gopls/internal/lsp/helper/READ - -So to add a capability currently not implemented, just define it somewhere in `lsp`. -In this case, just define `func (s *Server) resolve(...)` and re-generate `server_gen.go`. +diff -urN a/gopls/internal/lsp/helper/helper.go b/gopls/internal/lsp/helper/helper.go +--- a/gopls/internal/lsp/helper/helper.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/helper/helper.go 1970-01-01 08:00:00 +@@ -1,264 +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. +- +-// The helper command generates the declaration of the concrete +-// 'server' type that implements the abstract Server interface defined +-// in protocol/tsserver.go (which is itself generated from the LSP +-// protocol). +-// +-// To run, invoke "go generate" in the parent (lsp) directory. +-// +-// TODO(adonovan): merge this into the main LSP generator. +-package main +- +-import ( +- "bytes" +- "flag" +- "fmt" +- "go/ast" +- "go/format" +- "go/parser" +- "go/token" +- "log" +- "os" +- "sort" +- "strings" +- "text/template" +-) +- +-var ( +- typ = flag.String("t", "Server", "generate code for this type") +- def = flag.String("d", "", "the file the type is defined in") // this relies on punning +- use = flag.String("u", "", "look for uses in this package") +- out = flag.String("o", "", "where to write the generated file") +-) +- +-func main() { +- log.SetFlags(log.Lshortfile) +- flag.Parse() +- if *typ == "" || *def == "" || *use == "" || *out == "" { +- flag.PrintDefaults() +- os.Exit(1) +- } +- // read the type definition and see what methods we're looking for +- doTypes() +- +- // parse the package and see which methods are defined +- doUses() +- +- output() +-} +- +-// replace "\\\n" with nothing before using +-var tmpl = `// 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 lsp +- +-// code generated by helper. DO NOT EDIT. +- +-import ( +- "context" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +-) +- +-{{range $key, $v := .Stuff}} +-func (s *{{$.Type}}) {{$v.Name}}({{.Param}}) {{.Result}} { +- {{if ne .Found ""}} return s.{{.Internal}}({{.Invoke}})\ +- {{else}}return {{if lt 1 (len .Results)}}nil, {{end}}notImplemented("{{.Name}}"){{end}} +-} +-{{end}} +-` +- +-func output() { +- // put in empty param names as needed +- for _, t := range types { +- if t.paramnames == nil { +- t.paramnames = make([]string, len(t.paramtypes)) +- } +- for i, p := range t.paramtypes { +- cm := "" +- if i > 0 { +- cm = ", " +- } +- t.Param += fmt.Sprintf("%s%s %s", cm, t.paramnames[i], p) +- this := t.paramnames[i] +- if this == "_" { +- this = "nil" +- } +- t.Invoke += fmt.Sprintf("%s%s", cm, this) +- } +- if len(t.Results) > 1 { +- t.Result = "(" +- } +- for i, r := range t.Results { +- cm := "" +- if i > 0 { +- cm = ", " +- } +- t.Result += fmt.Sprintf("%s%s", cm, r) +- } +- if len(t.Results) > 1 { +- t.Result += ")" +- } +- } +- +- fd, err := os.Create(*out) +- if err != nil { +- log.Fatal(err) +- } +- t, err := template.New("foo").Parse(tmpl) +- if err != nil { +- log.Fatal(err) +- } +- type par struct { +- Type string +- Stuff []*Function +- } +- p := par{*typ, types} +- if false { // debugging the template +- t.Execute(os.Stderr, &p) +- } +- buf := bytes.NewBuffer(nil) +- err = t.Execute(buf, &p) +- if err != nil { +- log.Fatal(err) +- } +- ans, err := format.Source(bytes.Replace(buf.Bytes(), []byte("\\\n"), []byte{}, -1)) +- if err != nil { +- log.Fatal(err) +- } +- fd.Write(ans) +-} +- +-func doUses() { +- fset := token.NewFileSet() +- pkgs, err := parser.ParseDir(fset, *use, nil, 0) +- if err != nil { +- log.Fatalf("%q:%v", *use, err) +- } +- pkg := pkgs["lsp"] // CHECK +- files := pkg.Files +- for fname, f := range files { +- for _, d := range f.Decls { +- fd, ok := d.(*ast.FuncDecl) +- if !ok { +- continue +- } +- nm := fd.Name.String() +- if ast.IsExported(nm) { +- // we're looking for things like didChange +- continue +- } +- if fx, ok := byname[nm]; ok { +- if fx.Found != "" { +- log.Fatalf("found %s in %s and %s", fx.Internal, fx.Found, fname) +- } +- fx.Found = fname +- // and the Paramnames +- ft := fd.Type +- for _, f := range ft.Params.List { +- nm := "" +- if len(f.Names) > 0 { +- nm = f.Names[0].String() +- if nm == "_" { +- nm = "_gen" +- } +- } +- fx.paramnames = append(fx.paramnames, nm) +- } +- } +- } +- } +- if false { +- for i, f := range types { +- log.Printf("%d %s %s", i, f.Internal, f.Found) +- } +- } +-} +- +-type Function struct { +- Name string +- Internal string // first letter lower case +- paramtypes []string +- paramnames []string +- Results []string +- Param string +- Result string // do it in code, easier than in a template +- Invoke string +- Found string // file it was found in +-} +- +-var types []*Function +-var byname = map[string]*Function{} // internal names +- +-func doTypes() { +- fset := token.NewFileSet() +- f, err := parser.ParseFile(fset, *def, nil, 0) +- if err != nil { +- log.Fatal(err) +- } +- fd, err := os.Create("/tmp/ast") +- if err != nil { +- log.Fatal(err) +- } +- ast.Fprint(fd, fset, f, ast.NotNilFilter) +- ast.Inspect(f, inter) +- sort.Slice(types, func(i, j int) bool { return types[i].Name < types[j].Name }) +- if false { +- for i, f := range types { +- log.Printf("%d %s(%v) %v", i, f.Name, f.paramtypes, f.Results) +- } +- } +-} +- +-func inter(n ast.Node) bool { +- x, ok := n.(*ast.TypeSpec) +- if !ok || x.Name.Name != *typ { +- return true +- } +- m := x.Type.(*ast.InterfaceType).Methods.List +- for _, fld := range m { +- fn := fld.Type.(*ast.FuncType) +- p := fn.Params.List +- r := fn.Results.List +- fx := &Function{ +- Name: fld.Names[0].String(), +- } +- fx.Internal = strings.ToLower(fx.Name[:1]) + fx.Name[1:] +- for _, f := range p { +- fx.paramtypes = append(fx.paramtypes, whatis(f.Type)) +- } +- for _, f := range r { +- fx.Results = append(fx.Results, whatis(f.Type)) +- } +- types = append(types, fx) +- byname[fx.Internal] = fx +- } +- return false +-} +- +-func whatis(x ast.Expr) string { +- switch n := x.(type) { +- case *ast.SelectorExpr: +- return whatis(n.X) + "." + n.Sel.String() +- case *ast.StarExpr: +- return "*" + whatis(n.X) +- case *ast.Ident: +- if ast.IsExported(n.Name) { +- // these are from package protocol +- return "protocol." + n.Name +- } +- return n.Name +- case *ast.ArrayType: +- return "[]" + whatis(n.Elt) +- case *ast.InterfaceType: +- return "interface{}" +- default: +- log.Fatalf("Fatal %T", x) +- return fmt.Sprintf("%T", x) +- } +-} diff -urN a/gopls/internal/lsp/highlight.go b/gopls/internal/lsp/highlight.go --- a/gopls/internal/lsp/highlight.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/highlight.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,45 +0,0 @@ ++++ b/gopls/internal/lsp/highlight.go 1970-01-01 08:00:00 +@@ -1,48 +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. @@ -40619,27 +46164,30 @@ diff -urN a/gopls/internal/lsp/highlight.go b/gopls/internal/lsp/highlight.go -import ( - "context" - -- "golang.org/x/tools/internal/event" -- "golang.org/x/tools/internal/event/tag" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/lsp/template" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" -) - -func (s *Server) documentHighlight(ctx context.Context, params *protocol.DocumentHighlightParams) ([]protocol.DocumentHighlight, error) { +- ctx, done := event.Start(ctx, "lsp.Server.documentHighlight", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go) - defer release() - if !ok { - return nil, err - } - -- if snapshot.View().FileKind(fh) == source.Tmpl { +- if snapshot.FileKind(fh) == source.Tmpl { - return template.Highlight(ctx, snapshot, fh, params.Position) - } - - rngs, err := source.Highlight(ctx, snapshot, fh, params.Position) - if err != nil { -- event.Error(ctx, "no highlight", err, tag.URI.Of(params.TextDocument.URI)) +- event.Error(ctx, "no highlight", err) - } - return toProtocolHighlight(rngs), nil -} @@ -40657,8 +46205,8 @@ diff -urN a/gopls/internal/lsp/highlight.go b/gopls/internal/lsp/highlight.go -} diff -urN a/gopls/internal/lsp/hover.go b/gopls/internal/lsp/hover.go --- a/gopls/internal/lsp/hover.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/hover.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,34 +0,0 @@ ++++ b/gopls/internal/lsp/hover.go 1970-01-01 08:00:00 +@@ -1,45 +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. @@ -40673,15 +46221,26 @@ diff -urN a/gopls/internal/lsp/hover.go b/gopls/internal/lsp/hover.go - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/lsp/template" - "golang.org/x/tools/gopls/internal/lsp/work" +- "golang.org/x/tools/gopls/internal/telemetry" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" -) - --func (s *Server) hover(ctx context.Context, params *protocol.HoverParams) (*protocol.Hover, error) { +-func (s *Server) hover(ctx context.Context, params *protocol.HoverParams) (_ *protocol.Hover, rerr error) { +- recordLatency := telemetry.StartLatencyTimer("hover") +- defer func() { +- recordLatency(ctx, rerr) +- }() +- +- ctx, done := event.Start(ctx, "lsp.Server.hover", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) - defer release() - if !ok { - return nil, err - } -- switch snapshot.View().FileKind(fh) { +- switch snapshot.FileKind(fh) { - case source.Mod: - return mod.Hover(ctx, snapshot, fh, params.Position) - case source.Go: @@ -40695,8 +46254,8 @@ diff -urN a/gopls/internal/lsp/hover.go b/gopls/internal/lsp/hover.go -} diff -urN a/gopls/internal/lsp/implementation.go b/gopls/internal/lsp/implementation.go --- a/gopls/internal/lsp/implementation.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/implementation.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,21 +0,0 @@ ++++ b/gopls/internal/lsp/implementation.go 1970-01-01 08:00:00 +@@ -1,32 +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. @@ -40708,9 +46267,20 @@ diff -urN a/gopls/internal/lsp/implementation.go b/gopls/internal/lsp/implementa - - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/telemetry" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" -) - --func (s *Server) implementation(ctx context.Context, params *protocol.ImplementationParams) ([]protocol.Location, error) { +-func (s *Server) implementation(ctx context.Context, params *protocol.ImplementationParams) (_ []protocol.Location, rerr error) { +- recordLatency := telemetry.StartLatencyTimer("implementation") +- defer func() { +- recordLatency(ctx, rerr) +- }() +- +- ctx, done := event.Start(ctx, "lsp.Server.implementation", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go) - defer release() - if !ok { @@ -40720,8 +46290,8 @@ diff -urN a/gopls/internal/lsp/implementation.go b/gopls/internal/lsp/implementa -} diff -urN a/gopls/internal/lsp/inlay_hint.go b/gopls/internal/lsp/inlay_hint.go --- a/gopls/internal/lsp/inlay_hint.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/inlay_hint.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,21 +0,0 @@ ++++ b/gopls/internal/lsp/inlay_hint.go 1970-01-01 08:00:00 +@@ -1,33 +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. @@ -40731,22 +46301,34 @@ diff -urN a/gopls/internal/lsp/inlay_hint.go b/gopls/internal/lsp/inlay_hint.go -import ( - "context" - +- "golang.org/x/tools/gopls/internal/lsp/mod" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" -) - -func (s *Server) inlayHint(ctx context.Context, params *protocol.InlayHintParams) ([]protocol.InlayHint, error) { -- snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go) +- ctx, done := event.Start(ctx, "lsp.Server.inlayHint", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- +- snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) - defer release() - if !ok { - return nil, err - } -- return source.InlayHint(ctx, snapshot, fh, params.Range) +- switch snapshot.FileKind(fh) { +- case source.Mod: +- return mod.InlayHint(ctx, snapshot, fh, params.Range) +- case source.Go: +- return source.InlayHint(ctx, snapshot, fh, params.Range) +- } +- return nil, nil -} diff -urN a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go --- a/gopls/internal/lsp/link.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/link.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,278 +0,0 @@ ++++ b/gopls/internal/lsp/link.go 1970-01-01 08:00:00 +@@ -1,280 +0,0 @@ -// Copyright 2018 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. @@ -40773,12 +46355,15 @@ diff -urN a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go -) - -func (s *Server) documentLink(ctx context.Context, params *protocol.DocumentLinkParams) (links []protocol.DocumentLink, err error) { +- ctx, done := event.Start(ctx, "lsp.Server.documentLink") +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) - defer release() - if !ok { - return nil, err - } -- switch snapshot.View().FileKind(fh) { +- switch snapshot.FileKind(fh) { - case source.Mod: - links, err = modLinks(ctx, snapshot, fh) - case source.Go: @@ -40815,7 +46400,7 @@ diff -urN a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go - } - // Shift the start position to the location of the - // dependency within the require statement. -- target := source.BuildLink(snapshot.View().Options().LinkTarget, "mod/"+req.Mod.String(), "") +- target := source.BuildLink(snapshot.Options().LinkTarget, "mod/"+req.Mod.String(), "") - l, err := toProtocolLink(pm.Mapper, target, start+i, start+i+len(dep)) - if err != nil { - return nil, err @@ -40828,7 +46413,7 @@ diff -urN a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go - } - - // Get all the links that are contained in the comments of the file. -- urlRegexp := snapshot.View().Options().URLRegexp +- urlRegexp := snapshot.Options().URLRegexp - for _, expr := range pm.File.Syntax.Stmt { - comments := expr.Comment() - if comments == nil { @@ -40849,7 +46434,6 @@ diff -urN a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go - -// goLinks returns the set of hyperlink annotations for the specified Go file. -func goLinks(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.DocumentLink, error) { -- view := snapshot.View() - - pgf, err := snapshot.ParseGo(ctx, fh, source.ParseFull) - if err != nil { @@ -40859,14 +46443,14 @@ diff -urN a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go - var links []protocol.DocumentLink - - // Create links for import specs. -- if view.Options().ImportShortcut.ShowLinks() { +- if snapshot.Options().ImportShortcut.ShowLinks() { - - // If links are to pkg.go.dev, append module version suffixes. - // This requires the import map from the package metadata. Ignore errors. - var depsByImpPath map[source.ImportPath]source.PackageID -- if strings.ToLower(view.Options().LinkTarget) == "pkg.go.dev" { -- if metas, _ := snapshot.MetadataForFile(ctx, fh.URI()); len(metas) > 0 { -- depsByImpPath = metas[0].DepsByImpPath // 0 => narrowest package +- if strings.ToLower(snapshot.Options().LinkTarget) == "pkg.go.dev" { +- if meta, err := source.NarrowestMetadataForFile(ctx, snapshot, fh.URI()); err == nil { +- depsByImpPath = meta.DepsByImpPath - } - } - @@ -40876,7 +46460,7 @@ diff -urN a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go - continue // bad import - } - // See golang/go#36998: don't link to modules matching GOPRIVATE. -- if view.IsGoPrivatePath(string(importPath)) { +- if snapshot.View().IsGoPrivatePath(string(importPath)) { - continue - } - @@ -40891,7 +46475,7 @@ diff -urN a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go - if err != nil { - return nil, err - } -- targetURL := source.BuildLink(view.Options().LinkTarget, urlPath, "") +- targetURL := source.BuildLink(snapshot.Options().LinkTarget, urlPath, "") - // Account for the quotation marks in the positions. - l, err := toProtocolLink(pgf.Mapper, targetURL, start+len(`"`), end-len(`"`)) - if err != nil { @@ -40901,7 +46485,7 @@ diff -urN a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go - } - } - -- urlRegexp := snapshot.View().Options().URLRegexp +- urlRegexp := snapshot.Options().URLRegexp - - // Gather links found in string literals. - var str []*ast.BasicLit @@ -41022,1389 +46606,1280 @@ diff -urN a/gopls/internal/lsp/link.go b/gopls/internal/lsp/link.go - } - return protocol.DocumentLink{ - Range: rng, -- Target: targetURL, +- Target: &targetURL, - }, nil -} -diff -urN a/gopls/internal/lsp/lsprpc/autostart_default.go b/gopls/internal/lsp/lsprpc/autostart_default.go ---- a/gopls/internal/lsp/lsprpc/autostart_default.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/autostart_default.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,39 +0,0 @@ --// Copyright 2020 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/lru/lru.go b/gopls/internal/lsp/lru/lru.go +--- a/gopls/internal/lsp/lru/lru.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lru/lru.go 1970-01-01 08:00:00 +@@ -1,151 +0,0 @@ +-// Copyright 2023 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 lsprpc +-// The lru package implements a fixed-size in-memory LRU cache. +-package lru - -import ( +- "container/heap" - "fmt" -- -- exec "golang.org/x/sys/execabs" +- "sync" -) - --var ( -- daemonize = func(*exec.Cmd) {} -- autoNetworkAddress = autoNetworkAddressDefault -- verifyRemoteOwnership = verifyRemoteOwnershipDefault --) +-// A Cache is a fixed-size in-memory LRU cache. +-type Cache struct { +- capacity int - --func runRemote(cmd *exec.Cmd) error { -- daemonize(cmd) -- if err := cmd.Start(); err != nil { -- return fmt.Errorf("starting remote gopls: %w", err) -- } -- return nil +- mu sync.Mutex +- used int // used capacity, in user-specified units +- m map[any]*entry // k/v lookup +- lru queue // min-atime priority queue of *entry +- clock int64 // clock time, incremented whenever the cache is updated -} - --// autoNetworkAddressDefault returns the default network and address for the --// automatically-started gopls remote. See autostart_posix.go for more --// information. --func autoNetworkAddressDefault(goplsPath, id string) (network string, address string) { -- if id != "" { -- panic("identified remotes are not supported on windows") -- } -- return "tcp", "localhost:37374" +-type entry struct { +- key any +- value any +- size int // caller-specified size +- atime int64 // last access / set time +- index int // index of entry in the heap slice -} - --func verifyRemoteOwnershipDefault(network, address string) (bool, error) { -- return true, nil --} -diff -urN a/gopls/internal/lsp/lsprpc/autostart_posix.go b/gopls/internal/lsp/lsprpc/autostart_posix.go ---- a/gopls/internal/lsp/lsprpc/autostart_posix.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/autostart_posix.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,97 +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. +-// New creates a new Cache with the given capacity, which must be positive. +-// +-// The cache capacity uses arbitrary units, which are specified during the Set +-// operation. +-func New(capacity int) *Cache { +- if capacity == 0 { +- panic("zero capacity") +- } - --//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris --// +build darwin dragonfly freebsd linux netbsd openbsd solaris +- return &Cache{ +- capacity: capacity, +- m: make(map[any]*entry), +- } +-} - --package lsprpc +-// Get retrieves the value for the specified key, or nil if the key is not +-// found. +-// +-// If the key is found, its access time is updated. +-func (c *Cache) Get(key any) any { +- c.mu.Lock() +- defer c.mu.Unlock() - --import ( -- "crypto/sha256" -- "errors" -- "fmt" -- "log" -- "os" -- "os/user" -- "path/filepath" -- "strconv" -- "syscall" +- c.clock++ // every access updates the clock - -- exec "golang.org/x/sys/execabs" --) +- if e, ok := c.m[key]; ok { // cache hit +- e.atime = c.clock +- heap.Fix(&c.lru, e.index) +- return e.value +- } - --func init() { -- daemonize = daemonizePosix -- autoNetworkAddress = autoNetworkAddressPosix -- verifyRemoteOwnership = verifyRemoteOwnershipPosix +- return nil -} - --func daemonizePosix(cmd *exec.Cmd) { -- cmd.SysProcAttr = &syscall.SysProcAttr{ -- Setsid: true, +-// Set stores a value for the specified key, using its given size to update the +-// current cache size, evicting old entries as necessary to fit in the cache +-// capacity. +-// +-// Size must be a non-negative value. If size is larger than the cache +-// capacity, the value is not stored and the cache is not modified. +-func (c *Cache) Set(key, value any, size int) { +- if size < 0 { +- panic(fmt.Sprintf("size must be non-negative, got %d", size)) +- } +- if size > c.capacity { +- return // uncacheable - } --} - --// autoNetworkAddressPosix resolves an id on the 'auto' pseduo-network to a --// real network and address. On unix, this uses unix domain sockets. --func autoNetworkAddressPosix(goplsPath, id string) (network string, address string) { -- // Especially when doing local development or testing, it's important that -- // the remote gopls instance we connect to is running the same binary as our -- // forwarder. So we encode a short hash of the binary path into the daemon -- // socket name. If possible, we also include the buildid in this hash, to -- // account for long-running processes where the binary has been subsequently -- // rebuilt. -- h := sha256.New() -- cmd := exec.Command("go", "tool", "buildid", goplsPath) -- cmd.Stdout = h -- var pathHash []byte -- if err := cmd.Run(); err == nil { -- pathHash = h.Sum(nil) -- } else { -- log.Printf("error getting current buildid: %v", err) -- sum := sha256.Sum256([]byte(goplsPath)) -- pathHash = sum[:] +- c.mu.Lock() +- defer c.mu.Unlock() +- +- c.clock++ +- +- // Remove the existing cache entry for key, if it exists. +- e, ok := c.m[key] +- if ok { +- c.used -= e.size +- heap.Remove(&c.lru, e.index) +- delete(c.m, key) - } -- shortHash := fmt.Sprintf("%x", pathHash)[:6] -- user := os.Getenv("USER") -- if user == "" { -- user = "shared" +- +- // Evict entries until the new value will fit. +- newUsed := c.used + size +- if newUsed < 0 { +- return // integer overflow; return silently - } -- basename := filepath.Base(goplsPath) -- idComponent := "" -- if id != "" { -- idComponent = "-" + id +- c.used = newUsed +- for c.used > c.capacity { +- // evict oldest entry +- e = heap.Pop(&c.lru).(*entry) +- c.used -= e.size +- delete(c.m, e.key) - } -- runtimeDir := os.TempDir() -- if xdg := os.Getenv("XDG_RUNTIME_DIR"); xdg != "" { -- runtimeDir = xdg +- +- // Store the new value. +- // Opt: e is evicted, so it can be reused to reduce allocation. +- if e == nil { +- e = new(entry) +- } +- e.key = key +- e.value = value +- e.size = size +- e.atime = c.clock +- c.m[e.key] = e +- heap.Push(&c.lru, e) +- +- if len(c.m) != len(c.lru) { +- panic("map and LRU are inconsistent") - } -- return "unix", filepath.Join(runtimeDir, fmt.Sprintf("%s-%s-daemon.%s%s", basename, shortHash, user, idComponent)) -} - --func verifyRemoteOwnershipPosix(network, address string) (bool, error) { -- if network != "unix" { -- return true, nil +-// -- priority queue boilerplate -- +- +-// queue is a min-atime priority queue of cache entries. +-type queue []*entry +- +-func (q queue) Len() int { return len(q) } +- +-func (q queue) Less(i, j int) bool { return q[i].atime < q[j].atime } +- +-func (q queue) Swap(i, j int) { +- q[i], q[j] = q[j], q[i] +- q[i].index = i +- q[j].index = j +-} +- +-func (q *queue) Push(x any) { +- e := x.(*entry) +- e.index = len(*q) +- *q = append(*q, e) +-} +- +-func (q *queue) Pop() any { +- last := len(*q) - 1 +- e := (*q)[last] +- (*q)[last] = nil // aid GC +- *q = (*q)[:last] +- return e +-} +diff -urN a/gopls/internal/lsp/lru/lru_fuzz_test.go b/gopls/internal/lsp/lru/lru_fuzz_test.go +--- a/gopls/internal/lsp/lru/lru_fuzz_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lru/lru_fuzz_test.go 1970-01-01 08:00:00 +@@ -1,41 +0,0 @@ +-// Copyright 2023 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 lru_test +- +-import ( +- "testing" +- +- "golang.org/x/tools/gopls/internal/lsp/lru" +-) +- +-// Simple fuzzing test for consistency. +-func FuzzCache(f *testing.F) { +- type op struct { +- set bool +- key, value byte - } -- fi, err := os.Stat(address) -- if err != nil { -- if os.IsNotExist(err) { -- return true, nil +- f.Fuzz(func(t *testing.T, data []byte) { +- var ops []op +- for len(data) >= 3 { +- ops = append(ops, op{data[0]%2 == 0, data[1], data[2]}) +- data = data[3:] - } -- return false, fmt.Errorf("checking socket owner: %w", err) -- } -- stat, ok := fi.Sys().(*syscall.Stat_t) -- if !ok { -- return false, errors.New("fi.Sys() is not a Stat_t") -- } -- user, err := user.Current() -- if err != nil { -- return false, fmt.Errorf("checking current user: %w", err) -- } -- uid, err := strconv.ParseUint(user.Uid, 10, 32) -- if err != nil { -- return false, fmt.Errorf("parsing current UID: %w", err) -- } -- return stat.Uid == uint32(uid), nil +- cache := lru.New(100) +- var reference [256]byte +- for _, op := range ops { +- if op.set { +- reference[op.key] = op.value +- cache.Set(op.key, op.value, 1) +- } else { +- if v := cache.Get(op.key); v != nil && v != reference[op.key] { +- t.Fatalf("cache.Get(%d) = %d, want %d", op.key, v, reference[op.key]) +- } +- } +- } +- }) -} -diff -urN a/gopls/internal/lsp/lsprpc/binder.go b/gopls/internal/lsp/lsprpc/binder.go ---- a/gopls/internal/lsp/lsprpc/binder.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/binder.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,148 +0,0 @@ --// Copyright 2021 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/lru/lru_test.go b/gopls/internal/lsp/lru/lru_test.go +--- a/gopls/internal/lsp/lru/lru_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lru/lru_test.go 1970-01-01 08:00:00 +@@ -1,154 +0,0 @@ +-// Copyright 2023 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 lsprpc +-package lru_test - -import ( -- "context" -- "encoding/json" +- "bytes" +- cryptorand "crypto/rand" - "fmt" +- "log" +- mathrand "math/rand" +- "strings" +- "testing" - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/internal/event" -- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" -- "golang.org/x/tools/internal/xcontext" +- "golang.org/x/sync/errgroup" +- "golang.org/x/tools/gopls/internal/lsp/lru" -) - --// The BinderFunc type adapts a bind function to implement the jsonrpc2.Binder --// interface. --type BinderFunc func(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions +-func TestCache(t *testing.T) { +- type get struct { +- key string +- want any +- } +- type set struct { +- key, value string +- } - --func (f BinderFunc) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { -- return f(ctx, conn) --} -- --// Middleware defines a transformation of jsonrpc2 Binders, that may be --// composed to build jsonrpc2 servers. --type Middleware func(jsonrpc2_v2.Binder) jsonrpc2_v2.Binder -- --// A ServerFunc is used to construct an LSP server for a given client. --type ServerFunc func(context.Context, protocol.ClientCloser) protocol.Server -- --// ServerBinder binds incoming connections to a new server. --type ServerBinder struct { -- newServer ServerFunc --} -- --func NewServerBinder(newServer ServerFunc) *ServerBinder { -- return &ServerBinder{newServer: newServer} --} -- --func (b *ServerBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { -- client := protocol.ClientDispatcherV2(conn) -- server := b.newServer(ctx, client) -- serverHandler := protocol.ServerHandlerV2(server) -- // Wrap the server handler to inject the client into each request context, so -- // that log events are reflected back to the client. -- wrapped := jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { -- ctx = protocol.WithClient(ctx, client) -- return serverHandler.Handle(ctx, req) -- }) -- preempter := &canceler{ -- conn: conn, -- } -- return jsonrpc2_v2.ConnectionOptions{ -- Handler: wrapped, -- Preempter: preempter, +- tests := []struct { +- label string +- steps []any +- }{ +- {"empty cache", []any{ +- get{"a", nil}, +- get{"b", nil}, +- }}, +- {"zero-length string", []any{ +- set{"a", ""}, +- get{"a", ""}, +- }}, +- {"under capacity", []any{ +- set{"a", "123"}, +- set{"b", "456"}, +- get{"a", "123"}, +- get{"b", "456"}, +- }}, +- {"over capacity", []any{ +- set{"a", "123"}, +- set{"b", "456"}, +- set{"c", "78901"}, +- get{"a", nil}, +- get{"b", "456"}, +- get{"c", "78901"}, +- }}, +- {"access ordering", []any{ +- set{"a", "123"}, +- set{"b", "456"}, +- get{"a", "123"}, +- set{"c", "78901"}, +- get{"a", "123"}, +- get{"b", nil}, +- get{"c", "78901"}, +- }}, - } --} -- --type canceler struct { -- conn *jsonrpc2_v2.Connection --} - --func (c *canceler) Preempt(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { -- if req.Method != "$/cancelRequest" { -- return nil, jsonrpc2_v2.ErrNotHandled -- } -- var params protocol.CancelParams -- if err := json.Unmarshal(req.Params, ¶ms); err != nil { -- return nil, fmt.Errorf("%w: %v", jsonrpc2_v2.ErrParse, err) -- } -- var id jsonrpc2_v2.ID -- switch raw := params.ID.(type) { -- case float64: -- id = jsonrpc2_v2.Int64ID(int64(raw)) -- case string: -- id = jsonrpc2_v2.StringID(raw) -- default: -- return nil, fmt.Errorf("%w: invalid ID type %T", jsonrpc2_v2.ErrParse, params.ID) +- for _, test := range tests { +- t.Run(test.label, func(t *testing.T) { +- c := lru.New(10) +- for i, step := range test.steps { +- switch step := step.(type) { +- case get: +- if got := c.Get(step.key); got != step.want { +- t.Errorf("#%d: c.Get(%q) = %q, want %q", i, step.key, got, step.want) +- } +- case set: +- c.Set(step.key, step.value, len(step.value)) +- } +- } +- }) - } -- c.conn.Cancel(id) -- return nil, nil -} - --type ForwardBinder struct { -- dialer jsonrpc2_v2.Dialer -- onBind func(*jsonrpc2_v2.Connection) --} +-// TestConcurrency exercises concurrent access to the same entry. +-// +-// It is a copy of TestConcurrency from the filecache package. +-func TestConcurrency(t *testing.T) { +- key := uniqueKey() +- const N = 100 // concurrency level - --func NewForwardBinder(dialer jsonrpc2_v2.Dialer) *ForwardBinder { -- return &ForwardBinder{ -- dialer: dialer, +- // Construct N distinct values, each larger +- // than a typical 4KB OS file buffer page. +- var values [N][8192]byte +- for i := range values { +- if _, err := mathrand.Read(values[i][:]); err != nil { +- t.Fatalf("rand: %v", err) +- } - } --} - --func (b *ForwardBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) (opts jsonrpc2_v2.ConnectionOptions) { -- client := protocol.ClientDispatcherV2(conn) -- clientBinder := NewClientBinder(func(context.Context, protocol.Server) protocol.Client { return client }) +- cache := lru.New(100 * 1e6) // 100MB cache - -- serverConn, err := jsonrpc2_v2.Dial(context.Background(), b.dialer, clientBinder) -- if err != nil { -- return jsonrpc2_v2.ConnectionOptions{ -- Handler: jsonrpc2_v2.HandlerFunc(func(context.Context, *jsonrpc2_v2.Request) (interface{}, error) { -- return nil, fmt.Errorf("%w: %v", jsonrpc2_v2.ErrInternal, err) -- }), +- // get calls Get and verifies that the cache entry +- // matches one of the values passed to Set. +- get := func(mustBeFound bool) error { +- got := cache.Get(key) +- if got == nil { +- if !mustBeFound { +- return nil +- } +- return fmt.Errorf("Get did not return a value") +- } +- gotBytes := got.([]byte) +- for _, want := range values { +- if bytes.Equal(want[:], gotBytes) { +- return nil // a match +- } - } +- return fmt.Errorf("Get returned a value that was never Set") - } - -- if b.onBind != nil { -- b.onBind(serverConn) -- } -- server := protocol.ServerDispatcherV2(serverConn) -- preempter := &canceler{ -- conn: conn, +- // Perform N concurrent calls to Set and Get. +- // All sets must succeed. +- // All gets must return nothing, or one of the Set values; +- // there is no third possibility. +- var group errgroup.Group +- for i := range values { +- i := i +- v := values[i][:] +- group.Go(func() error { +- cache.Set(key, v, len(v)) +- return nil +- }) +- group.Go(func() error { return get(false) }) - } -- detached := xcontext.Detach(ctx) -- go func() { -- conn.Wait() -- if err := serverConn.Close(); err != nil { -- event.Log(detached, fmt.Sprintf("closing remote connection: %v", err)) +- if err := group.Wait(); err != nil { +- if strings.Contains(err.Error(), "operation not supported") || +- strings.Contains(err.Error(), "not implemented") { +- t.Skipf("skipping: %v", err) - } -- }() -- return jsonrpc2_v2.ConnectionOptions{ -- Handler: protocol.ServerHandlerV2(server), -- Preempter: preempter, +- t.Fatal(err) - } --} -- --// A ClientFunc is used to construct an LSP client for a given server. --type ClientFunc func(context.Context, protocol.Server) protocol.Client -- --// ClientBinder binds an LSP client to an incoming connection. --type ClientBinder struct { -- newClient ClientFunc --} - --func NewClientBinder(newClient ClientFunc) *ClientBinder { -- return &ClientBinder{newClient} +- // A final Get must report one of the values that was Set. +- if err := get(true); err != nil { +- t.Fatalf("final Get failed: %v", err) +- } -} - --func (b *ClientBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { -- server := protocol.ServerDispatcherV2(conn) -- client := b.newClient(ctx, server) -- return jsonrpc2_v2.ConnectionOptions{ -- Handler: protocol.ClientHandlerV2(client), +-// uniqueKey returns a key that has never been used before. +-func uniqueKey() (key [32]byte) { +- if _, err := cryptorand.Read(key[:]); err != nil { +- log.Fatalf("rand: %v", err) - } +- return -} -diff -urN a/gopls/internal/lsp/lsprpc/binder_test.go b/gopls/internal/lsp/lsprpc/binder_test.go ---- a/gopls/internal/lsp/lsprpc/binder_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/binder_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,147 +0,0 @@ --// Copyright 2021 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/lsp_test.go b/gopls/internal/lsp/lsp_test.go +--- a/gopls/internal/lsp/lsp_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsp_test.go 1970-01-01 08:00:00 +@@ -1,760 +0,0 @@ +-// Copyright 2018 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 lsprpc_test +-package lsp - -import ( +- "bytes" - "context" -- "regexp" +- "fmt" +- "os" +- "path/filepath" +- "sort" - "strings" - "testing" -- "time" - +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/cache" +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/debug" - "golang.org/x/tools/gopls/internal/lsp/protocol" -- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" -- -- . "golang.org/x/tools/gopls/internal/lsp/lsprpc" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/lsp/tests" +- "golang.org/x/tools/gopls/internal/lsp/tests/compare" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/testenv" -) - --type TestEnv struct { -- Conns []*jsonrpc2_v2.Connection -- Servers []*jsonrpc2_v2.Server --} +-func TestMain(m *testing.M) { +- bug.PanicOnBugs = true +- testenv.ExitIfSmallMachine() - --func (e *TestEnv) Shutdown(t *testing.T) { -- for _, s := range e.Servers { -- s.Shutdown() -- } -- for _, c := range e.Conns { -- if err := c.Close(); err != nil { -- t.Error(err) -- } -- } -- for _, s := range e.Servers { -- if err := s.Wait(); err != nil { -- t.Error(err) -- } -- } +- os.Exit(m.Run()) -} - --func (e *TestEnv) serve(ctx context.Context, t *testing.T, server jsonrpc2_v2.Binder) (jsonrpc2_v2.Listener, *jsonrpc2_v2.Server) { -- l, err := jsonrpc2_v2.NetPipeListener(ctx) -- if err != nil { -- t.Fatal(err) -- } -- s := jsonrpc2_v2.NewServer(ctx, l, server) -- e.Servers = append(e.Servers, s) -- return l, s +-// TestLSP runs the marker tests in files beneath testdata/ using +-// implementations of each of the marker operations that make LSP RPCs to a +-// gopls server. +-func TestLSP(t *testing.T) { +- tests.RunTests(t, "testdata", true, testLSP) -} - --func (e *TestEnv) dial(ctx context.Context, t *testing.T, dialer jsonrpc2_v2.Dialer, client jsonrpc2_v2.Binder, forwarded bool) *jsonrpc2_v2.Connection { -- if forwarded { -- l, _ := e.serve(ctx, t, NewForwardBinder(dialer)) -- dialer = l.Dialer() -- } -- conn, err := jsonrpc2_v2.Dial(ctx, dialer, client) -- if err != nil { -- t.Fatal(err) -- } -- e.Conns = append(e.Conns, conn) -- return conn --} +-func testLSP(t *testing.T, datum *tests.Data) { +- ctx := tests.Context(t) - --func staticClientBinder(client protocol.Client) jsonrpc2_v2.Binder { -- f := func(context.Context, protocol.Server) protocol.Client { return client } -- return NewClientBinder(f) --} +- // Setting a debug instance suppresses logging to stderr, but ensures that we +- // still e.g. convert events into runtime/trace/instrumentation. +- // +- // Previously, we called event.SetExporter(nil), which turns off all +- // instrumentation. +- ctx = debug.WithInstance(ctx, "", "off") - --func staticServerBinder(server protocol.Server) jsonrpc2_v2.Binder { -- f := func(ctx context.Context, client protocol.ClientCloser) protocol.Server { -- return server +- session := cache.NewSession(ctx, cache.New(nil)) +- options := source.DefaultOptions(tests.DefaultOptions) +- options.SetEnvSlice(datum.Config.Env) +- view, snapshot, release, err := session.NewView(ctx, datum.Config.Dir, span.URIFromPath(datum.Config.Dir), options) +- if err != nil { +- t.Fatal(err) - } -- return NewServerBinder(f) --} - --func TestClientLoggingV2(t *testing.T) { -- ctx := context.Background() +- defer session.RemoveView(view) - -- for name, forwarded := range map[string]bool{ -- "forwarded": true, -- "standalone": false, -- } { -- t.Run(name, func(t *testing.T) { -- client := FakeClient{Logs: make(chan string, 10)} -- env := new(TestEnv) -- defer env.Shutdown(t) -- l, _ := env.serve(ctx, t, staticServerBinder(PingServer{})) -- conn := env.dial(ctx, t, l.Dialer(), staticClientBinder(client), forwarded) +- // Only run the -modfile specific tests in module mode with Go 1.14 or above. +- datum.ModfileFlagAvailable = len(snapshot.ModFiles()) > 0 && testenv.Go1Point() >= 14 +- release() - -- if err := protocol.ServerDispatcherV2(conn).DidOpen(ctx, &protocol.DidOpenTextDocumentParams{}); err != nil { -- t.Errorf("DidOpen: %v", err) +- // Open all files for performance reasons, because gopls only +- // keeps active packages (those with open files) in memory. +- // +- // In practice clients will only send document-oriented requests for open +- // files. +- var modifications []source.FileModification +- for _, module := range datum.Exported.Modules { +- for name := range module.Files { +- filename := datum.Exported.File(module.Name, name) +- if filepath.Ext(filename) != ".go" { +- continue - } -- select { -- case got := <-client.Logs: -- want := "ping" -- matched, err := regexp.MatchString(want, got) -- if err != nil { -- t.Fatal(err) -- } -- if !matched { -- t.Errorf("got log %q, want a log containing %q", got, want) -- } -- case <-time.After(1 * time.Second): -- t.Error("timeout waiting for client log") +- content, err := datum.Exported.FileContents(filename) +- if err != nil { +- t.Fatal(err) - } -- }) +- modifications = append(modifications, source.FileModification{ +- URI: span.URIFromPath(filename), +- Action: source.Open, +- Version: -1, +- Text: content, +- LanguageID: "go", +- }) +- } - } --} -- --func TestRequestCancellationV2(t *testing.T) { -- ctx := context.Background() -- -- for name, forwarded := range map[string]bool{ -- "forwarded": true, -- "standalone": false, -- } { -- t.Run(name, func(t *testing.T) { -- server := WaitableServer{ -- Started: make(chan struct{}), -- Completed: make(chan error), -- } -- env := new(TestEnv) -- defer env.Shutdown(t) -- l, _ := env.serve(ctx, t, staticServerBinder(server)) -- client := FakeClient{Logs: make(chan string, 10)} -- conn := env.dial(ctx, t, l.Dialer(), staticClientBinder(client), forwarded) -- -- sd := protocol.ServerDispatcherV2(conn) -- ctx, cancel := context.WithCancel(ctx) -- -- result := make(chan error) -- go func() { -- _, err := sd.Hover(ctx, &protocol.HoverParams{}) -- result <- err -- }() -- // Wait for the Hover request to start. -- <-server.Started -- cancel() -- if err := <-result; err == nil { -- t.Error("nil error for cancelled Hover(), want non-nil") -- } -- if err := <-server.Completed; err == nil || !strings.Contains(err.Error(), "cancelled hover") { -- t.Errorf("Hover(): unexpected server-side error %v", err) -- } +- for filename, content := range datum.Config.Overlay { +- if filepath.Ext(filename) != ".go" { +- continue +- } +- modifications = append(modifications, source.FileModification{ +- URI: span.URIFromPath(filename), +- Action: source.Open, +- Version: -1, +- Text: content, +- LanguageID: "go", - }) - } --} -diff -urN a/gopls/internal/lsp/lsprpc/commandinterceptor.go b/gopls/internal/lsp/lsprpc/commandinterceptor.go ---- a/gopls/internal/lsp/lsprpc/commandinterceptor.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/commandinterceptor.go 1970-01-01 00:00:00.000000000 +0000 -@@ -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 lsprpc -- --import ( -- "context" -- "encoding/json" -- -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" --) -- --// HandlerMiddleware is a middleware that only modifies the jsonrpc2 handler. --type HandlerMiddleware func(jsonrpc2_v2.Handler) jsonrpc2_v2.Handler +- if err := session.ModifyFiles(ctx, modifications); err != nil { +- t.Fatal(err) +- } +- r := &runner{ +- data: datum, +- ctx: ctx, +- editRecv: make(chan map[span.URI][]byte, 1), +- } - --// BindHandler transforms a HandlerMiddleware into a Middleware. --func BindHandler(hmw HandlerMiddleware) Middleware { -- return Middleware(func(binder jsonrpc2_v2.Binder) jsonrpc2_v2.Binder { -- return BinderFunc(func(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { -- opts := binder.Bind(ctx, conn) -- opts.Handler = hmw(opts.Handler) -- return opts -- }) -- }) +- r.server = NewServer(session, testClient{runner: r}, options) +- tests.Run(t, r, datum) -} - --func CommandInterceptor(command string, run func(*protocol.ExecuteCommandParams) (interface{}, error)) Middleware { -- return BindHandler(func(delegate jsonrpc2_v2.Handler) jsonrpc2_v2.Handler { -- return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { -- if req.Method == "workspace/executeCommand" { -- var params protocol.ExecuteCommandParams -- if err := json.Unmarshal(req.Params, ¶ms); err == nil { -- if params.Command == command { -- return run(¶ms) -- } -- } -- } -- -- return delegate.Handle(ctx, req) -- }) -- }) +-// runner implements tests.Tests by making LSP RPCs to a gopls server. +-type runner struct { +- server *Server +- data *tests.Data +- diagnostics map[span.URI][]*source.Diagnostic +- ctx context.Context +- editRecv chan map[span.URI][]byte -} -diff -urN a/gopls/internal/lsp/lsprpc/commandinterceptor_test.go b/gopls/internal/lsp/lsprpc/commandinterceptor_test.go ---- a/gopls/internal/lsp/lsprpc/commandinterceptor_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/commandinterceptor_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,42 +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 lsprpc_test +-// testClient stubs any client functions that may be called by LSP functions. +-type testClient struct { +- protocol.Client +- runner *runner +-} - --import ( -- "context" -- "testing" +-func (c testClient) Close() error { +- return nil +-} - -- "golang.org/x/tools/gopls/internal/lsp/protocol" +-// Trivially implement PublishDiagnostics so that we can call +-// server.publishReports below to de-dup sent diagnostics. +-func (c testClient) PublishDiagnostics(context.Context, *protocol.PublishDiagnosticsParams) error { +- return nil +-} - -- . "golang.org/x/tools/gopls/internal/lsp/lsprpc" --) +-func (c testClient) ShowMessage(context.Context, *protocol.ShowMessageParams) error { +- return nil +-} - --func TestCommandInterceptor(t *testing.T) { -- const command = "foo" -- caught := false -- intercept := func(_ *protocol.ExecuteCommandParams) (interface{}, error) { -- caught = true -- return map[string]interface{}{}, nil +-func (c testClient) ApplyEdit(ctx context.Context, params *protocol.ApplyWorkspaceEditParams) (*protocol.ApplyWorkspaceEditResult, error) { +- res, err := applyTextDocumentEdits(c.runner, params.Edit.DocumentChanges) +- if err != nil { +- return nil, err - } +- c.runner.editRecv <- res +- return &protocol.ApplyWorkspaceEditResult{Applied: true}, nil +-} - -- ctx := context.Background() -- env := new(TestEnv) -- defer env.Shutdown(t) -- mw := CommandInterceptor(command, intercept) -- l, _ := env.serve(ctx, t, mw(noopBinder)) -- conn := env.dial(ctx, t, l.Dialer(), noopBinder, false) +-func (r *runner) CallHierarchy(t *testing.T, spn span.Span, expectedCalls *tests.CallHierarchyResult) { +- mapper, err := r.data.Mapper(spn.URI()) +- if err != nil { +- t.Fatal(err) +- } +- loc, err := mapper.SpanLocation(spn) +- if err != nil { +- t.Fatalf("failed for %v: %v", spn, err) +- } - -- params := &protocol.ExecuteCommandParams{ -- Command: command, +- params := &protocol.CallHierarchyPrepareParams{ +- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), - } -- var res interface{} -- err := conn.Call(ctx, "workspace/executeCommand", params).Await(ctx, &res) +- +- items, err := r.server.PrepareCallHierarchy(r.ctx, params) - if err != nil { - t.Fatal(err) - } -- if !caught { -- t.Errorf("workspace/executeCommand was not intercepted") +- if len(items) == 0 { +- t.Fatalf("expected call hierarchy item to be returned for identifier at %v\n", loc.Range) - } --} -diff -urN a/gopls/internal/lsp/lsprpc/dialer.go b/gopls/internal/lsp/lsprpc/dialer.go ---- a/gopls/internal/lsp/lsprpc/dialer.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/dialer.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,114 +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 lsprpc +- callLocation := protocol.Location{ +- URI: items[0].URI, +- Range: items[0].Range, +- } +- if callLocation != loc { +- t.Fatalf("expected server.PrepareCallHierarchy to return identifier at %v but got %v\n", loc, callLocation) +- } - --import ( -- "context" -- "fmt" -- "io" -- "net" -- "os" -- "time" +- incomingCalls, err := r.server.IncomingCalls(r.ctx, &protocol.CallHierarchyIncomingCallsParams{Item: items[0]}) +- if err != nil { +- t.Error(err) +- } +- var incomingCallItems []protocol.CallHierarchyItem +- for _, item := range incomingCalls { +- incomingCallItems = append(incomingCallItems, item.From) +- } +- msg := tests.DiffCallHierarchyItems(incomingCallItems, expectedCalls.IncomingCalls) +- if msg != "" { +- t.Errorf("incoming calls: %s", msg) +- } - -- exec "golang.org/x/sys/execabs" -- "golang.org/x/tools/internal/event" --) -- --// AutoNetwork is the pseudo network type used to signal that gopls should use --// automatic discovery to resolve a remote address. --const AutoNetwork = "auto" -- --// An AutoDialer is a jsonrpc2 dialer that understands the 'auto' network. --type AutoDialer struct { -- network, addr string // the 'real' network and address -- isAuto bool // whether the server is on the 'auto' network -- -- executable string -- argFunc func(network, addr string) []string +- outgoingCalls, err := r.server.OutgoingCalls(r.ctx, &protocol.CallHierarchyOutgoingCallsParams{Item: items[0]}) +- if err != nil { +- t.Error(err) +- } +- var outgoingCallItems []protocol.CallHierarchyItem +- for _, item := range outgoingCalls { +- outgoingCallItems = append(outgoingCallItems, item.To) +- } +- msg = tests.DiffCallHierarchyItems(outgoingCallItems, expectedCalls.OutgoingCalls) +- if msg != "" { +- t.Errorf("outgoing calls: %s", msg) +- } -} - --func NewAutoDialer(rawAddr string, argFunc func(network, addr string) []string) (*AutoDialer, error) { -- d := AutoDialer{ -- argFunc: argFunc, +-func (r *runner) SemanticTokens(t *testing.T, spn span.Span) { +- uri := spn.URI() +- filename := uri.Filename() +- // this is called solely for coverage in semantic.go +- _, err := r.server.semanticTokensFull(r.ctx, &protocol.SemanticTokensParams{ +- TextDocument: protocol.TextDocumentIdentifier{ +- URI: protocol.URIFromSpanURI(uri), +- }, +- }) +- if err != nil { +- t.Errorf("%v for %s", err, filename) - } -- d.network, d.addr = ParseAddr(rawAddr) -- if d.network == AutoNetwork { -- d.isAuto = true -- bin, err := os.Executable() -- if err != nil { -- return nil, fmt.Errorf("getting executable: %w", err) -- } -- d.executable = bin -- d.network, d.addr = autoNetworkAddress(bin, d.addr) +- _, err = r.server.semanticTokensRange(r.ctx, &protocol.SemanticTokensRangeParams{ +- TextDocument: protocol.TextDocumentIdentifier{ +- URI: protocol.URIFromSpanURI(uri), +- }, +- // any legal range. Just to exercise the call. +- Range: protocol.Range{ +- Start: protocol.Position{ +- Line: 0, +- Character: 0, +- }, +- End: protocol.Position{ +- Line: 2, +- Character: 0, +- }, +- }, +- }) +- if err != nil { +- t.Errorf("%v for Range %s", err, filename) - } -- return &d, nil -} - --// Dial implements the jsonrpc2.Dialer interface. --func (d *AutoDialer) Dial(ctx context.Context) (io.ReadWriteCloser, error) { -- conn, err := d.dialNet(ctx) -- return conn, err --} +-func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []tests.SuggestedFix, expectedActions int) { +- uri := spn.URI() +- view, err := r.server.session.ViewOf(uri) +- if err != nil { +- t.Fatal(err) +- } - --// TODO(rFindley): remove this once we no longer need to integrate with v1 of --// the jsonrpc2 package. --func (d *AutoDialer) dialNet(ctx context.Context) (net.Conn, error) { -- // Attempt to verify that we own the remote. This is imperfect, but if we can -- // determine that the remote is owned by a different user, we should fail. -- ok, err := verifyRemoteOwnership(d.network, d.addr) +- m, err := r.data.Mapper(uri) - if err != nil { -- // If the ownership check itself failed, we fail open but log an error to -- // the user. -- event.Error(ctx, "unable to check daemon socket owner, failing open", err) -- } else if !ok { -- // We successfully checked that the socket is not owned by us, we fail -- // closed. -- return nil, fmt.Errorf("socket %q is owned by a different user", d.addr) +- t.Fatal(err) - } -- const dialTimeout = 1 * time.Second -- // Try dialing our remote once, in case it is already running. -- netConn, err := net.DialTimeout(d.network, d.addr, dialTimeout) -- if err == nil { -- return netConn, nil +- rng, err := m.SpanRange(spn) +- if err != nil { +- t.Fatal(err) - } -- if d.isAuto && d.argFunc != nil { -- if d.network == "unix" { -- // Sometimes the socketfile isn't properly cleaned up when the server -- // shuts down. Since we have already tried and failed to dial this -- // address, it should *usually* be safe to remove the socket before -- // binding to the address. -- // TODO(rfindley): there is probably a race here if multiple server -- // instances are simultaneously starting up. -- if _, err := os.Stat(d.addr); err == nil { -- if err := os.Remove(d.addr); err != nil { -- return nil, fmt.Errorf("removing remote socket file: %w", err) -- } +- // Get the diagnostics for this view if we have not done it before. +- r.collectDiagnostics(view) +- var diagnostics []protocol.Diagnostic +- for _, d := range r.diagnostics[uri] { +- // Compare the start positions rather than the entire range because +- // some diagnostics have a range with the same start and end position (8:1-8:1). +- // The current marker functionality prevents us from having a range of 0 length. +- if protocol.ComparePosition(d.Range.Start, rng.Start) == 0 { +- diagnostics = append(diagnostics, toProtocolDiagnostics([]*source.Diagnostic{d})...) +- break +- } +- } +- var codeActionKinds []protocol.CodeActionKind +- for _, k := range actionKinds { +- codeActionKinds = append(codeActionKinds, protocol.CodeActionKind(k.ActionKind)) +- } +- allActions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{ +- TextDocument: protocol.TextDocumentIdentifier{ +- URI: protocol.URIFromSpanURI(uri), +- }, +- Range: rng, +- Context: protocol.CodeActionContext{ +- Only: codeActionKinds, +- Diagnostics: diagnostics, +- }, +- }) +- if err != nil { +- t.Fatalf("CodeAction %s failed: %v", spn, err) +- } +- var actions []protocol.CodeAction +- for _, action := range allActions { +- for _, fix := range actionKinds { +- if strings.Contains(action.Title, fix.Title) { +- actions = append(actions, action) +- break - } - } -- args := d.argFunc(d.network, d.addr) -- cmd := exec.Command(d.executable, args...) -- if err := runRemote(cmd); err != nil { -- return nil, err +- +- } +- if len(actions) != expectedActions { +- var summaries []string +- for _, a := range actions { +- summaries = append(summaries, fmt.Sprintf("%q (%s)", a.Title, a.Kind)) - } +- t.Fatalf("CodeAction(...): got %d code actions (%v), want %d", len(actions), summaries, expectedActions) - } -- -- const retries = 5 -- // It can take some time for the newly started server to bind to our address, -- // so we retry for a bit. -- for retry := 0; retry < retries; retry++ { -- startDial := time.Now() -- netConn, err = net.DialTimeout(d.network, d.addr, dialTimeout) -- if err == nil { -- return netConn, nil +- action := actions[0] +- var match bool +- for _, k := range codeActionKinds { +- if action.Kind == k { +- match = true +- break - } -- event.Log(ctx, fmt.Sprintf("failed attempt #%d to connect to remote: %v\n", retry+2, err)) -- // In case our failure was a fast-failure, ensure we wait at least -- // f.dialTimeout before trying again. -- if retry != retries-1 { -- time.Sleep(dialTimeout - time.Since(startDial)) +- } +- if !match { +- t.Fatalf("unexpected kind for code action %s, got %v, want one of %v", action.Title, action.Kind, codeActionKinds) +- } +- var res map[span.URI][]byte +- if cmd := action.Command; cmd != nil { +- _, err := r.server.ExecuteCommand(r.ctx, &protocol.ExecuteCommandParams{ +- Command: action.Command.Command, +- Arguments: action.Command.Arguments, +- }) +- if err != nil { +- t.Fatalf("error converting command %q to edits: %v", action.Command.Command, err) +- } +- res = <-r.editRecv +- } else { +- res, err = applyTextDocumentEdits(r, action.Edit.DocumentChanges) +- if err != nil { +- t.Fatal(err) - } - } -- return nil, fmt.Errorf("dialing remote: %w", err) --} -diff -urN a/gopls/internal/lsp/lsprpc/goenv.go b/gopls/internal/lsp/lsprpc/goenv.go ---- a/gopls/internal/lsp/lsprpc/goenv.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/goenv.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,96 +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 lsprpc -- --import ( -- "context" -- "encoding/json" -- "fmt" -- "os" -- -- "golang.org/x/tools/internal/event" -- "golang.org/x/tools/internal/gocommand" -- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" -- "golang.org/x/tools/gopls/internal/lsp/protocol" --) -- --func GoEnvMiddleware() (Middleware, error) { -- return BindHandler(func(delegate jsonrpc2_v2.Handler) jsonrpc2_v2.Handler { -- return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { -- if req.Method == "initialize" { -- if err := addGoEnvToInitializeRequestV2(ctx, req); err != nil { -- event.Error(ctx, "adding go env to initialize", err) -- } -- } -- return delegate.Handle(ctx, req) +- for u, got := range res { +- want := r.data.Golden(t, "suggestedfix_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) { +- return got, nil - }) -- }), nil +- if diff := compare.Bytes(want, got); diff != "" { +- t.Errorf("suggested fixes failed for %s:\n%s", u.Filename(), diff) +- } +- } -} - --func addGoEnvToInitializeRequestV2(ctx context.Context, req *jsonrpc2_v2.Request) error { -- var params protocol.ParamInitialize -- if err := json.Unmarshal(req.Params, ¶ms); err != nil { -- return err -- } -- var opts map[string]interface{} -- switch v := params.InitializationOptions.(type) { -- case nil: -- opts = make(map[string]interface{}) -- case map[string]interface{}: -- opts = v -- default: -- return fmt.Errorf("unexpected type for InitializationOptions: %T", v) -- } -- envOpt, ok := opts["env"] -- if !ok { -- envOpt = make(map[string]interface{}) +-func (r *runner) MethodExtraction(t *testing.T, start span.Span, end span.Span) { +- uri := start.URI() +- m, err := r.data.Mapper(uri) +- if err != nil { +- t.Fatal(err) - } -- env, ok := envOpt.(map[string]interface{}) -- if !ok { -- return fmt.Errorf("env option is %T, expected a map", envOpt) +- spn := span.New(start.URI(), start.Start(), end.End()) +- rng, err := m.SpanRange(spn) +- if err != nil { +- t.Fatal(err) - } -- goenv, err := getGoEnv(ctx, env) +- actionsRaw, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{ +- TextDocument: protocol.TextDocumentIdentifier{ +- URI: protocol.URIFromSpanURI(uri), +- }, +- Range: rng, +- Context: protocol.CodeActionContext{ +- Only: []protocol.CodeActionKind{"refactor.extract"}, +- }, +- }) - if err != nil { -- return err +- t.Fatal(err) - } -- // We don't want to propagate GOWORK unless explicitly set since that could mess with -- // path inference during cmd/go invocations, see golang/go#51825. -- _, goworkSet := os.LookupEnv("GOWORK") -- for govar, value := range goenv { -- if govar == "GOWORK" && !goworkSet { -- continue +- var actions []protocol.CodeAction +- for _, action := range actionsRaw { +- if action.Command.Title == "Extract method" { +- actions = append(actions, action) - } -- env[govar] = value -- } -- opts["env"] = env -- params.InitializationOptions = opts -- raw, err := json.Marshal(params) -- if err != nil { -- return fmt.Errorf("marshaling updated options: %v", err) - } -- req.Params = json.RawMessage(raw) -- return nil --} -- --func getGoEnv(ctx context.Context, env map[string]interface{}) (map[string]string, error) { -- var runEnv []string -- for k, v := range env { -- runEnv = append(runEnv, fmt.Sprintf("%s=%s", k, v)) +- // Hack: We assume that we only get one matching code action per range. +- // TODO(rstambler): Support multiple code actions per test. +- if len(actions) == 0 || len(actions) > 1 { +- t.Fatalf("unexpected number of code actions, want 1, got %v", len(actions)) - } -- runner := gocommand.Runner{} -- output, err := runner.Run(ctx, gocommand.Invocation{ -- Verb: "env", -- Args: []string{"-json"}, -- Env: runEnv, +- _, err = r.server.ExecuteCommand(r.ctx, &protocol.ExecuteCommandParams{ +- Command: actions[0].Command.Command, +- Arguments: actions[0].Command.Arguments, - }) - if err != nil { -- return nil, err +- t.Fatal(err) - } -- envmap := make(map[string]string) -- if err := json.Unmarshal(output.Bytes(), &envmap); err != nil { -- return nil, err +- res := <-r.editRecv +- for u, got := range res { +- want := r.data.Golden(t, "methodextraction_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) { +- return got, nil +- }) +- if diff := compare.Bytes(want, got); diff != "" { +- t.Errorf("method extraction failed for %s:\n%s", u.Filename(), diff) +- } - } -- return envmap, nil -} -diff -urN a/gopls/internal/lsp/lsprpc/goenv_test.go b/gopls/internal/lsp/lsprpc/goenv_test.go ---- a/gopls/internal/lsp/lsprpc/goenv_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/goenv_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,65 +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 lsprpc_test -- --import ( -- "context" -- "testing" +-func (r *runner) InlayHints(t *testing.T, spn span.Span) { +- uri := spn.URI() +- filename := uri.Filename() - -- "golang.org/x/tools/gopls/internal/lsp/protocol" +- hints, err := r.server.InlayHint(r.ctx, &protocol.InlayHintParams{ +- TextDocument: protocol.TextDocumentIdentifier{ +- URI: protocol.URIFromSpanURI(uri), +- }, +- // TODO: add Range +- }) +- if err != nil { +- t.Fatal(err) +- } - -- . "golang.org/x/tools/gopls/internal/lsp/lsprpc" --) +- // Map inlay hints to text edits. +- edits := make([]protocol.TextEdit, len(hints)) +- for i, hint := range hints { +- var paddingLeft, paddingRight string +- if hint.PaddingLeft { +- paddingLeft = " " +- } +- if hint.PaddingRight { +- paddingRight = " " +- } +- edits[i] = protocol.TextEdit{ +- Range: protocol.Range{Start: hint.Position, End: hint.Position}, +- NewText: fmt.Sprintf("<%s%s%s>", paddingLeft, hint.Label[0].Value, paddingRight), +- } +- } - --type initServer struct { -- protocol.Server +- m, err := r.data.Mapper(uri) +- if err != nil { +- t.Fatal(err) +- } +- got, _, err := source.ApplyProtocolEdits(m, edits) +- if err != nil { +- t.Error(err) +- } - -- params *protocol.ParamInitialize --} +- withinlayHints := r.data.Golden(t, "inlayHint", filename, func() ([]byte, error) { +- return got, nil +- }) - --func (s *initServer) Initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) { -- s.params = params -- return &protocol.InitializeResult{}, nil +- if !bytes.Equal(withinlayHints, got) { +- t.Errorf("inlay hints failed for %s, expected:\n%s\ngot:\n%s", filename, withinlayHints, got) +- } -} - --func TestGoEnvMiddleware(t *testing.T) { -- ctx := context.Background() +-func (r *runner) Rename(t *testing.T, spn span.Span, newText string) { +- tag := fmt.Sprintf("%s-rename", newText) - -- server := &initServer{} -- env := new(TestEnv) -- defer env.Shutdown(t) -- l, _ := env.serve(ctx, t, staticServerBinder(server)) -- mw, err := GoEnvMiddleware() +- uri := spn.URI() +- filename := uri.Filename() +- sm, err := r.data.Mapper(uri) - if err != nil { - t.Fatal(err) - } -- binder := mw(NewForwardBinder(l.Dialer())) -- l, _ = env.serve(ctx, t, binder) -- conn := env.dial(ctx, t, l.Dialer(), noopBinder, true) -- dispatch := protocol.ServerDispatcherV2(conn) -- initParams := &protocol.ParamInitialize{} -- initParams.InitializationOptions = map[string]interface{}{ -- "env": map[string]interface{}{ -- "GONOPROXY": "example.com", -- }, +- loc, err := sm.SpanLocation(spn) +- if err != nil { +- t.Fatalf("failed for %v: %v", spn, err) - } -- if _, err := dispatch.Initialize(ctx, initParams); err != nil { +- +- wedit, err := r.server.Rename(r.ctx, &protocol.RenameParams{ +- TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI}, +- Position: loc.Range.Start, +- NewName: newText, +- }) +- if err != nil { +- renamed := string(r.data.Golden(t, tag, filename, func() ([]byte, error) { +- return []byte(err.Error()), nil +- })) +- if err.Error() != renamed { +- t.Errorf("%s: rename failed for %s, expected:\n%v\ngot:\n%v\n", spn, newText, renamed, err) +- } +- return +- } +- res, err := applyTextDocumentEdits(r, wedit.DocumentChanges) +- if err != nil { - t.Fatal(err) - } -- -- if server.params == nil { -- t.Fatalf("initialize params are unset") +- var orderedURIs []string +- for uri := range res { +- orderedURIs = append(orderedURIs, string(uri)) - } -- envOpts := server.params.InitializationOptions.(map[string]interface{})["env"].(map[string]interface{}) +- sort.Strings(orderedURIs) - -- // Check for an arbitrary Go variable. It should be set. -- if _, ok := envOpts["GOPRIVATE"]; !ok { -- t.Errorf("Go environment variable GOPRIVATE unset in initialization options") +- // Print the name and content of each modified file, +- // concatenated, and compare against the golden. +- var buf bytes.Buffer +- for i := 0; i < len(res); i++ { +- if i != 0 { +- buf.WriteByte('\n') +- } +- uri := span.URIFromURI(orderedURIs[i]) +- if len(res) > 1 { +- buf.WriteString(filepath.Base(uri.Filename())) +- buf.WriteString(":\n") +- } +- buf.Write(res[uri]) - } -- // Check that the variable present in our user config was not overwritten. -- if got, want := envOpts["GONOPROXY"], "example.com"; got != want { -- t.Errorf("GONOPROXY=%q, want %q", got, want) +- got := buf.Bytes() +- want := r.data.Golden(t, tag, filename, func() ([]byte, error) { +- return got, nil +- }) +- if diff := compare.Bytes(want, got); diff != "" { +- t.Errorf("rename failed for %s:\n%s", newText, diff) - } -} -diff -urN a/gopls/internal/lsp/lsprpc/lsprpc.go b/gopls/internal/lsp/lsprpc/lsprpc.go ---- a/gopls/internal/lsp/lsprpc/lsprpc.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/lsprpc.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,543 +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 lsprpc implements a jsonrpc2.StreamServer that may be used to --// serve the LSP on a jsonrpc2 channel. --package lsprpc +-func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) { +- m, err := r.data.Mapper(src.URI()) +- if err != nil { +- t.Fatal(err) +- } +- loc, err := m.SpanLocation(src) +- if err != nil { +- t.Fatalf("failed for %v: %v", src, err) +- } +- params := &protocol.PrepareRenameParams{ +- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), +- } +- got, err := r.server.PrepareRename(context.Background(), params) +- if err != nil { +- t.Errorf("prepare rename failed for %v: got error: %v", src, err) +- return +- } - --import ( -- "context" -- "encoding/json" -- "fmt" -- "log" -- "net" -- "os" -- "strconv" -- "strings" -- "sync" -- "sync/atomic" -- "time" +- // TODO(rfindley): can we consolidate on a single representation for +- // PrepareRename results, and use cmp.Diff here? - -- "golang.org/x/tools/gopls/internal/lsp" -- "golang.org/x/tools/gopls/internal/lsp/cache" -- "golang.org/x/tools/gopls/internal/lsp/command" -- "golang.org/x/tools/gopls/internal/lsp/debug" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/internal/event" -- "golang.org/x/tools/internal/event/tag" -- "golang.org/x/tools/internal/jsonrpc2" --) -- --// Unique identifiers for client/server. --var serverIndex int64 -- --// The StreamServer type is a jsonrpc2.StreamServer that handles incoming --// streams as a new LSP session, using a shared cache. --type StreamServer struct { -- cache *cache.Cache -- // daemon controls whether or not to log new connections. -- daemon bool -- -- // optionsOverrides is passed to newly created sessions. -- optionsOverrides func(*source.Options) -- -- // serverForTest may be set to a test fake for testing. -- serverForTest protocol.Server --} -- --// NewStreamServer creates a StreamServer using the shared cache. If --// withTelemetry is true, each session is instrumented with telemetry that --// records RPC statistics. --func NewStreamServer(cache *cache.Cache, daemon bool, optionsFunc func(*source.Options)) *StreamServer { -- return &StreamServer{cache: cache, daemon: daemon, optionsOverrides: optionsFunc} +- // PrepareRename may fail with no error if there was no object found at the +- // position. +- if got == nil { +- if want.Text != "" { // expected an ident. +- t.Errorf("prepare rename failed for %v: got nil", src) +- } +- return +- } +- if got.Range.Start == got.Range.End { +- // Special case for 0-length ranges. Marks can't specify a 0-length range, +- // so just compare the start. +- if got.Range.Start != want.Range.Start { +- t.Errorf("prepare rename failed: incorrect point, got %v want %v", got.Range.Start, want.Range.Start) +- } +- } else { +- if got.Range != want.Range { +- t.Errorf("prepare rename failed: incorrect range got %v want %v", got.Range, want.Range) +- } +- } +- if got.Placeholder != want.Text { +- t.Errorf("prepare rename failed: incorrect text got %v want %v", got.Placeholder, want.Text) +- } -} - --func (s *StreamServer) Binder() *ServerBinder { -- newServer := func(ctx context.Context, client protocol.ClientCloser) protocol.Server { -- session := cache.NewSession(ctx, s.cache, s.optionsOverrides) -- server := s.serverForTest -- if server == nil { -- server = lsp.NewServer(session, client) -- if instance := debug.GetInstance(ctx); instance != nil { -- instance.AddService(server, session) +-func applyTextDocumentEdits(r *runner, edits []protocol.DocumentChanges) (map[span.URI][]byte, error) { +- res := make(map[span.URI][]byte) +- for _, docEdits := range edits { +- if docEdits.TextDocumentEdit != nil { +- uri := docEdits.TextDocumentEdit.TextDocument.URI.SpanURI() +- var m *protocol.Mapper +- // If we have already edited this file, we use the edited version (rather than the +- // file in its original state) so that we preserve our initial changes. +- if content, ok := res[uri]; ok { +- m = protocol.NewMapper(uri, content) +- } else { +- var err error +- if m, err = r.data.Mapper(uri); err != nil { +- return nil, err +- } +- } +- patched, _, err := source.ApplyProtocolEdits(m, docEdits.TextDocumentEdit.Edits) +- if err != nil { +- return nil, err - } +- res[uri] = patched - } -- return server - } -- return NewServerBinder(newServer) +- return res, nil -} - --// ServeStream implements the jsonrpc2.StreamServer interface, by handling --// incoming streams using a new lsp server. --func (s *StreamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) error { -- client := protocol.ClientDispatcher(conn) -- session := cache.NewSession(ctx, s.cache, s.optionsOverrides) -- server := s.serverForTest -- if server == nil { -- server = lsp.NewServer(session, client) -- if instance := debug.GetInstance(ctx); instance != nil { -- instance.AddService(server, session) +-func (r *runner) SignatureHelp(t *testing.T, spn span.Span, want *protocol.SignatureHelp) { +- m, err := r.data.Mapper(spn.URI()) +- if err != nil { +- t.Fatal(err) +- } +- loc, err := m.SpanLocation(spn) +- if err != nil { +- t.Fatalf("failed for %v: %v", loc, err) +- } +- params := &protocol.SignatureHelpParams{ +- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), +- } +- got, err := r.server.SignatureHelp(r.ctx, params) +- if err != nil { +- // Only fail if we got an error we did not expect. +- if want != nil { +- t.Fatal(err) - } +- return - } -- // Clients may or may not send a shutdown message. Make sure the server is -- // shut down. -- // TODO(rFindley): this shutdown should perhaps be on a disconnected context. -- defer func() { -- if err := server.Shutdown(ctx); err != nil { -- event.Error(ctx, "error shutting down", err) +- if want == nil { +- if got != nil { +- t.Errorf("expected no signature, got %v", got) - } -- }() -- executable, err := os.Executable() -- if err != nil { -- log.Printf("error getting gopls path: %v", err) -- executable = "" +- return - } -- ctx = protocol.WithClient(ctx, client) -- conn.Go(ctx, -- protocol.Handlers( -- handshaker(session, executable, s.daemon, -- protocol.ServerHandler(server, -- jsonrpc2.MethodNotFound)))) -- if s.daemon { -- log.Printf("Session %s: connected", session.ID()) -- defer log.Printf("Session %s: exited", session.ID()) +- if got == nil { +- t.Fatalf("expected %v, got nil", want) +- } +- if diff := tests.DiffSignatures(spn, want, got); diff != "" { +- t.Error(diff) - } -- <-conn.Done() -- return conn.Err() --} -- --// A Forwarder is a jsonrpc2.StreamServer that handles an LSP stream by --// forwarding it to a remote. This is used when the gopls process started by --// the editor is in the `-remote` mode, which means it finds and connects to a --// separate gopls daemon. In these cases, we still want the forwarder gopls to --// be instrumented with telemetry, and want to be able to in some cases hijack --// the jsonrpc2 connection with the daemon. --type Forwarder struct { -- dialer *AutoDialer -- -- mu sync.Mutex -- // Hold on to the server connection so that we can redo the handshake if any -- // information changes. -- serverConn jsonrpc2.Conn -- serverID string -} - --// NewForwarder creates a new Forwarder, ready to forward connections to the --// remote server specified by rawAddr. If provided and rawAddr indicates an --// 'automatic' address (starting with 'auto;'), argFunc may be used to start a --// remote server for the auto-discovered address. --func NewForwarder(rawAddr string, argFunc func(network, address string) []string) (*Forwarder, error) { -- dialer, err := NewAutoDialer(rawAddr, argFunc) +-func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) { +- m, err := r.data.Mapper(uri) - if err != nil { -- return nil, err +- t.Fatal(err) - } -- fwd := &Forwarder{ -- dialer: dialer, +- got, err := r.server.DocumentLink(r.ctx, &protocol.DocumentLinkParams{ +- TextDocument: protocol.TextDocumentIdentifier{ +- URI: protocol.URIFromSpanURI(uri), +- }, +- }) +- if err != nil { +- t.Fatal(err) +- } +- if diff := tests.DiffLinks(m, wantLinks, got); diff != "" { +- t.Error(diff) - } -- return fwd, nil -} - --// QueryServerState queries the server state of the current server. --func QueryServerState(ctx context.Context, addr string) (*ServerState, error) { -- serverConn, err := dialRemote(ctx, addr) +-func (r *runner) AddImport(t *testing.T, uri span.URI, expectedImport string) { +- cmd, err := command.NewListKnownPackagesCommand("List Known Packages", command.URIArg{ +- URI: protocol.URIFromSpanURI(uri), +- }) - if err != nil { -- return nil, err +- t.Fatal(err) - } -- var state ServerState -- if err := protocol.Call(ctx, serverConn, sessionsMethod, nil, &state); err != nil { -- return nil, fmt.Errorf("querying server state: %w", err) +- resp, err := r.server.executeCommand(r.ctx, &protocol.ExecuteCommandParams{ +- Command: cmd.Command, +- Arguments: cmd.Arguments, +- }) +- if err != nil { +- t.Fatal(err) - } -- return &state, nil --} -- --// dialRemote is used for making calls into the gopls daemon. addr should be a --// URL, possibly on the synthetic 'auto' network (e.g. tcp://..., unix://..., --// or auto://...). --func dialRemote(ctx context.Context, addr string) (jsonrpc2.Conn, error) { -- network, address := ParseAddr(addr) -- if network == AutoNetwork { -- gp, err := os.Executable() -- if err != nil { -- return nil, fmt.Errorf("getting gopls path: %w", err) +- res := resp.(command.ListKnownPackagesResult) +- var hasPkg bool +- for _, p := range res.Packages { +- if p == expectedImport { +- hasPkg = true +- break - } -- network, address = autoNetworkAddress(gp, address) - } -- netConn, err := net.DialTimeout(network, address, 5*time.Second) -- if err != nil { -- return nil, fmt.Errorf("dialing remote: %w", err) +- if !hasPkg { +- t.Fatalf("%s: got %v packages\nwant contains %q", command.ListKnownPackages, res.Packages, expectedImport) - } -- serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn)) -- serverConn.Go(ctx, jsonrpc2.MethodNotFound) -- return serverConn, nil --} -- --func ExecuteCommand(ctx context.Context, addr string, id string, request, result interface{}) error { -- serverConn, err := dialRemote(ctx, addr) +- cmd, err = command.NewAddImportCommand("Add Imports", command.AddImportArgs{ +- URI: protocol.URIFromSpanURI(uri), +- ImportPath: expectedImport, +- }) - if err != nil { -- return err +- t.Fatal(err) - } -- args, err := command.MarshalArgs(request) +- _, err = r.server.executeCommand(r.ctx, &protocol.ExecuteCommandParams{ +- Command: cmd.Command, +- Arguments: cmd.Arguments, +- }) - if err != nil { -- return err +- t.Fatal(err) - } -- params := protocol.ExecuteCommandParams{ -- Command: id, -- Arguments: args, +- got := (<-r.editRecv)[uri] +- want := r.data.Golden(t, "addimport", uri.Filename(), func() ([]byte, error) { +- return []byte(got), nil +- }) +- if want == nil { +- t.Fatalf("golden file %q not found", uri.Filename()) +- } +- if diff := compare.Bytes(want, got); diff != "" { +- t.Errorf("%s mismatch\n%s", command.AddImport, diff) - } -- return protocol.Call(ctx, serverConn, "workspace/executeCommand", params, result) -} - --// ServeStream dials the forwarder remote and binds the remote to serve the LSP --// on the incoming stream. --func (f *Forwarder) ServeStream(ctx context.Context, clientConn jsonrpc2.Conn) error { -- client := protocol.ClientDispatcher(clientConn) +-func (r *runner) SelectionRanges(t *testing.T, spn span.Span) { +- uri := spn.URI() +- sm, err := r.data.Mapper(uri) +- if err != nil { +- t.Fatal(err) +- } +- loc, err := sm.SpanLocation(spn) +- if err != nil { +- t.Error(err) +- } - -- netConn, err := f.dialer.dialNet(ctx) +- ranges, err := r.server.selectionRange(r.ctx, &protocol.SelectionRangeParams{ +- TextDocument: protocol.TextDocumentIdentifier{ +- URI: protocol.URIFromSpanURI(uri), +- }, +- Positions: []protocol.Position{loc.Range.Start}, +- }) - if err != nil { -- return fmt.Errorf("forwarder: connecting to remote: %w", err) +- t.Fatal(err) - } -- serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn)) -- server := protocol.ServerDispatcher(serverConn) - -- // Forward between connections. -- serverConn.Go(ctx, -- protocol.Handlers( -- protocol.ClientHandler(client, -- jsonrpc2.MethodNotFound))) +- sb := &strings.Builder{} +- for i, path := range ranges { +- fmt.Fprintf(sb, "Ranges %d: ", i) +- rng := path +- for { +- s, e, err := sm.RangeOffsets(rng.Range) +- if err != nil { +- t.Error(err) +- } - -- // Don't run the clientConn yet, so that we can complete the handshake before -- // processing any client messages. +- var snippet string +- if e-s < 30 { +- snippet = string(sm.Content[s:e]) +- } else { +- snippet = string(sm.Content[s:s+15]) + "..." + string(sm.Content[e-15:e]) +- } - -- // Do a handshake with the server instance to exchange debug information. -- index := atomic.AddInt64(&serverIndex, 1) -- f.mu.Lock() -- f.serverConn = serverConn -- f.serverID = strconv.FormatInt(index, 10) -- f.mu.Unlock() -- f.handshake(ctx) -- clientConn.Go(ctx, -- protocol.Handlers( -- f.handler( -- protocol.ServerHandler(server, -- jsonrpc2.MethodNotFound)))) +- fmt.Fprintf(sb, "\n\t%v %q", rng.Range, strings.ReplaceAll(snippet, "\n", "\\n")) - -- select { -- case <-serverConn.Done(): -- clientConn.Close() -- case <-clientConn.Done(): -- serverConn.Close() +- if rng.Parent == nil { +- break +- } +- rng = *rng.Parent +- } +- sb.WriteRune('\n') - } +- got := sb.String() - -- err = nil -- if serverConn.Err() != nil { -- err = fmt.Errorf("remote disconnected: %v", serverConn.Err()) -- } else if clientConn.Err() != nil { -- err = fmt.Errorf("client disconnected: %v", clientConn.Err()) +- testName := "selectionrange_" + tests.SpanName(spn) +- want := r.data.Golden(t, testName, uri.Filename(), func() ([]byte, error) { +- return []byte(got), nil +- }) +- if want == nil { +- t.Fatalf("golden file %q not found", uri.Filename()) +- } +- if diff := compare.Text(got, string(want)); diff != "" { +- t.Errorf("%s mismatch\n%s", testName, diff) - } -- event.Log(ctx, fmt.Sprintf("forwarder: exited with error: %v", err)) -- return err -} - --// TODO(rfindley): remove this handshaking in favor of middleware. --func (f *Forwarder) handshake(ctx context.Context) { -- // This call to os.Executable is redundant, and will be eliminated by the -- // transition to the V2 API. -- goplsPath, err := os.Executable() +-func (r *runner) collectDiagnostics(view *cache.View) { +- if r.diagnostics != nil { +- return +- } +- r.diagnostics = make(map[span.URI][]*source.Diagnostic) +- +- snapshot, release, err := view.Snapshot() - if err != nil { -- event.Error(ctx, "getting executable for handshake", err) -- goplsPath = "" +- panic(err) - } -- var ( -- hreq = handshakeRequest{ -- ServerID: f.serverID, -- GoplsPath: goplsPath, +- defer release() +- +- // Always run diagnostics with analysis. +- r.server.diagnose(r.ctx, snapshot, analyzeEverything) +- for uri, reports := range r.server.diagnostics { +- for _, report := range reports.reports { +- for _, d := range report.diags { +- r.diagnostics[uri] = append(r.diagnostics[uri], d) +- } - } -- hresp handshakeResponse -- ) -- if di := debug.GetInstance(ctx); di != nil { -- hreq.Logfile = di.Logfile -- hreq.DebugAddr = di.ListenedDebugAddress() -- } -- if err := protocol.Call(ctx, f.serverConn, handshakeMethod, hreq, &hresp); err != nil { -- // TODO(rfindley): at some point in the future we should return an error -- // here. Handshakes have become functional in nature. -- event.Error(ctx, "forwarder: gopls handshake failed", err) -- } -- if hresp.GoplsPath != goplsPath { -- event.Error(ctx, "", fmt.Errorf("forwarder: gopls path mismatch: forwarder is %q, remote is %q", goplsPath, hresp.GoplsPath)) - } -- event.Log(ctx, "New server", -- tag.NewServer.Of(f.serverID), -- tag.Logfile.Of(hresp.Logfile), -- tag.DebugAddress.Of(hresp.DebugAddr), -- tag.GoplsPath.Of(hresp.GoplsPath), -- tag.ClientID.Of(hresp.SessionID), -- ) -} +diff -urN a/gopls/internal/lsp/lsprpc/autostart_default.go b/gopls/internal/lsp/lsprpc/autostart_default.go +--- a/gopls/internal/lsp/lsprpc/autostart_default.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/autostart_default.go 1970-01-01 08:00:00 +@@ -1,39 +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. - --func ConnectToRemote(ctx context.Context, addr string) (net.Conn, error) { -- dialer, err := NewAutoDialer(addr, nil) -- if err != nil { -- return nil, err +-package lsprpc +- +-import ( +- "fmt" +- +- exec "golang.org/x/sys/execabs" +-) +- +-var ( +- daemonize = func(*exec.Cmd) {} +- autoNetworkAddress = autoNetworkAddressDefault +- verifyRemoteOwnership = verifyRemoteOwnershipDefault +-) +- +-func runRemote(cmd *exec.Cmd) error { +- daemonize(cmd) +- if err := cmd.Start(); err != nil { +- return fmt.Errorf("starting remote gopls: %w", err) - } -- return dialer.dialNet(ctx) +- return nil -} - --// handler intercepts messages to the daemon to enrich them with local +-// autoNetworkAddressDefault returns the default network and address for the +-// automatically-started gopls remote. See autostart_posix.go for more -// information. --func (f *Forwarder) handler(handler jsonrpc2.Handler) jsonrpc2.Handler { -- return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error { -- // Intercept certain messages to add special handling. -- switch r.Method() { -- case "initialize": -- if newr, err := addGoEnvToInitializeRequest(ctx, r); err == nil { -- r = newr -- } else { -- log.Printf("unable to add local env to initialize request: %v", err) -- } -- case "workspace/executeCommand": -- var params protocol.ExecuteCommandParams -- if err := json.Unmarshal(r.Params(), ¶ms); err == nil { -- if params.Command == command.StartDebugging.ID() { -- var args command.DebuggingArgs -- if err := command.UnmarshalArgs(params.Arguments, &args); err == nil { -- reply = f.replyWithDebugAddress(ctx, reply, args) -- } else { -- event.Error(ctx, "unmarshaling debugging args", err) -- } -- } -- } else { -- event.Error(ctx, "intercepting executeCommand request", err) -- } -- } -- // The gopls workspace environment defaults to the process environment in -- // which gopls daemon was started. To avoid discrepancies in Go environment -- // between the editor and daemon, inject any unset variables in `go env` -- // into the options sent by initialize. -- // -- // See also golang.org/issue/37830. -- return handler(ctx, reply, r) +-func autoNetworkAddressDefault(goplsPath, id string) (network string, address string) { +- if id != "" { +- panic("identified remotes are not supported on windows") - } +- return "tcp", "localhost:37374" -} - --// addGoEnvToInitializeRequest builds a new initialize request in which we set --// any environment variables output by `go env` and not already present in the --// request. --// --// It returns an error if r is not an initialize request, or is otherwise --// malformed. --func addGoEnvToInitializeRequest(ctx context.Context, r jsonrpc2.Request) (jsonrpc2.Request, error) { -- var params protocol.ParamInitialize -- if err := json.Unmarshal(r.Params(), ¶ms); err != nil { -- return nil, err -- } -- var opts map[string]interface{} -- switch v := params.InitializationOptions.(type) { -- case nil: -- opts = make(map[string]interface{}) -- case map[string]interface{}: -- opts = v -- default: -- return nil, fmt.Errorf("unexpected type for InitializationOptions: %T", v) -- } -- envOpt, ok := opts["env"] -- if !ok { -- envOpt = make(map[string]interface{}) -- } -- env, ok := envOpt.(map[string]interface{}) -- if !ok { -- return nil, fmt.Errorf(`env option is %T, expected a map`, envOpt) -- } -- goenv, err := getGoEnv(ctx, env) -- if err != nil { -- return nil, err -- } -- // We don't want to propagate GOWORK unless explicitly set since that could mess with -- // path inference during cmd/go invocations, see golang/go#51825. -- _, goworkSet := os.LookupEnv("GOWORK") -- for govar, value := range goenv { -- if govar == "GOWORK" && !goworkSet { -- continue -- } -- env[govar] = value -- } -- opts["env"] = env -- params.InitializationOptions = opts -- call, ok := r.(*jsonrpc2.Call) -- if !ok { -- return nil, fmt.Errorf("%T is not a *jsonrpc2.Call", r) -- } -- return jsonrpc2.NewCall(call.ID(), "initialize", params) +-func verifyRemoteOwnershipDefault(network, address string) (bool, error) { +- return true, nil -} +diff -urN a/gopls/internal/lsp/lsprpc/autostart_posix.go b/gopls/internal/lsp/lsprpc/autostart_posix.go +--- a/gopls/internal/lsp/lsprpc/autostart_posix.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/autostart_posix.go 1970-01-01 08:00:00 +@@ -1,97 +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. - --func (f *Forwarder) replyWithDebugAddress(outerCtx context.Context, r jsonrpc2.Replier, args command.DebuggingArgs) jsonrpc2.Replier { -- di := debug.GetInstance(outerCtx) -- if di == nil { -- event.Log(outerCtx, "no debug instance to start") -- return r -- } -- return func(ctx context.Context, result interface{}, outerErr error) error { -- if outerErr != nil { -- return r(ctx, result, outerErr) -- } -- // Enrich the result with our own debugging information. Since we're an -- // intermediary, the jsonrpc2 package has deserialized the result into -- // maps, by default. Re-do the unmarshalling. -- raw, err := json.Marshal(result) -- if err != nil { -- event.Error(outerCtx, "marshaling intermediate command result", err) -- return r(ctx, result, err) -- } -- var modified command.DebuggingResult -- if err := json.Unmarshal(raw, &modified); err != nil { -- event.Error(outerCtx, "unmarshaling intermediate command result", err) -- return r(ctx, result, err) -- } -- addr := args.Addr -- if addr == "" { -- addr = "localhost:0" -- } -- addr, err = di.Serve(outerCtx, addr) -- if err != nil { -- event.Error(outerCtx, "starting debug server", err) -- return r(ctx, result, outerErr) -- } -- urls := []string{"http://" + addr} -- modified.URLs = append(urls, modified.URLs...) -- go f.handshake(ctx) -- return r(ctx, modified, nil) -- } --} -- --// A handshakeRequest identifies a client to the LSP server. --type handshakeRequest struct { -- // ServerID is the ID of the server on the client. This should usually be 0. -- ServerID string `json:"serverID"` -- // Logfile is the location of the clients log file. -- Logfile string `json:"logfile"` -- // DebugAddr is the client debug address. -- DebugAddr string `json:"debugAddr"` -- // GoplsPath is the path to the Gopls binary running the current client -- // process. -- GoplsPath string `json:"goplsPath"` --} -- --// A handshakeResponse is returned by the LSP server to tell the LSP client --// information about its session. --type handshakeResponse struct { -- // SessionID is the server session associated with the client. -- SessionID string `json:"sessionID"` -- // Logfile is the location of the server logs. -- Logfile string `json:"logfile"` -- // DebugAddr is the server debug address. -- DebugAddr string `json:"debugAddr"` -- // GoplsPath is the path to the Gopls binary running the current server -- // process. -- GoplsPath string `json:"goplsPath"` --} +-//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +-// +build darwin dragonfly freebsd linux netbsd openbsd solaris - --// ClientSession identifies a current client LSP session on the server. Note --// that it looks similar to handshakeResposne, but in fact 'Logfile' and --// 'DebugAddr' now refer to the client. --type ClientSession struct { -- SessionID string `json:"sessionID"` -- Logfile string `json:"logfile"` -- DebugAddr string `json:"debugAddr"` --} +-package lsprpc - --// ServerState holds information about the gopls daemon process, including its --// debug information and debug information of all of its current connected --// clients. --type ServerState struct { -- Logfile string `json:"logfile"` -- DebugAddr string `json:"debugAddr"` -- GoplsPath string `json:"goplsPath"` -- CurrentClientID string `json:"currentClientID"` -- Clients []ClientSession `json:"clients"` --} +-import ( +- "crypto/sha256" +- "errors" +- "fmt" +- "log" +- "os" +- "os/user" +- "path/filepath" +- "strconv" +- "syscall" - --const ( -- handshakeMethod = "gopls/handshake" -- sessionsMethod = "gopls/sessions" +- exec "golang.org/x/sys/execabs" -) - --func handshaker(session *cache.Session, goplsPath string, logHandshakes bool, handler jsonrpc2.Handler) jsonrpc2.Handler { -- return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error { -- switch r.Method() { -- case handshakeMethod: -- // We log.Printf in this handler, rather than event.Log when we want logs -- // to go to the daemon log rather than being reflected back to the -- // client. -- var req handshakeRequest -- if err := json.Unmarshal(r.Params(), &req); err != nil { -- if logHandshakes { -- log.Printf("Error processing handshake for session %s: %v", session.ID(), err) -- } -- sendError(ctx, reply, err) -- return nil -- } -- if logHandshakes { -- log.Printf("Session %s: got handshake. Logfile: %q, Debug addr: %q", session.ID(), req.Logfile, req.DebugAddr) -- } -- event.Log(ctx, "Handshake session update", -- cache.KeyUpdateSession.Of(session), -- tag.DebugAddress.Of(req.DebugAddr), -- tag.Logfile.Of(req.Logfile), -- tag.ServerID.Of(req.ServerID), -- tag.GoplsPath.Of(req.GoplsPath), -- ) -- resp := handshakeResponse{ -- SessionID: session.ID(), -- GoplsPath: goplsPath, -- } -- if di := debug.GetInstance(ctx); di != nil { -- resp.Logfile = di.Logfile -- resp.DebugAddr = di.ListenedDebugAddress() -- } -- return reply(ctx, resp, nil) +-func init() { +- daemonize = daemonizePosix +- autoNetworkAddress = autoNetworkAddressPosix +- verifyRemoteOwnership = verifyRemoteOwnershipPosix +-} - -- case sessionsMethod: -- resp := ServerState{ -- GoplsPath: goplsPath, -- CurrentClientID: session.ID(), -- } -- if di := debug.GetInstance(ctx); di != nil { -- resp.Logfile = di.Logfile -- resp.DebugAddr = di.ListenedDebugAddress() -- for _, c := range di.State.Clients() { -- resp.Clients = append(resp.Clients, ClientSession{ -- SessionID: c.Session.ID(), -- Logfile: c.Logfile, -- DebugAddr: c.DebugAddress, -- }) -- } -- } -- return reply(ctx, resp, nil) -- } -- return handler(ctx, reply, r) +-func daemonizePosix(cmd *exec.Cmd) { +- cmd.SysProcAttr = &syscall.SysProcAttr{ +- Setsid: true, - } -} - --func sendError(ctx context.Context, reply jsonrpc2.Replier, err error) { -- err = fmt.Errorf("%v: %w", err, jsonrpc2.ErrParse) -- if err := reply(ctx, nil, err); err != nil { -- event.Error(ctx, "", err) +-// autoNetworkAddressPosix resolves an id on the 'auto' pseduo-network to a +-// real network and address. On unix, this uses unix domain sockets. +-func autoNetworkAddressPosix(goplsPath, id string) (network string, address string) { +- // Especially when doing local development or testing, it's important that +- // the remote gopls instance we connect to is running the same binary as our +- // forwarder. So we encode a short hash of the binary path into the daemon +- // socket name. If possible, we also include the buildid in this hash, to +- // account for long-running processes where the binary has been subsequently +- // rebuilt. +- h := sha256.New() +- cmd := exec.Command("go", "tool", "buildid", goplsPath) +- cmd.Stdout = h +- var pathHash []byte +- if err := cmd.Run(); err == nil { +- pathHash = h.Sum(nil) +- } else { +- log.Printf("error getting current buildid: %v", err) +- sum := sha256.Sum256([]byte(goplsPath)) +- pathHash = sum[:] +- } +- shortHash := fmt.Sprintf("%x", pathHash)[:6] +- user := os.Getenv("USER") +- if user == "" { +- user = "shared" +- } +- basename := filepath.Base(goplsPath) +- idComponent := "" +- if id != "" { +- idComponent = "-" + id +- } +- runtimeDir := os.TempDir() +- if xdg := os.Getenv("XDG_RUNTIME_DIR"); xdg != "" { +- runtimeDir = xdg - } +- return "unix", filepath.Join(runtimeDir, fmt.Sprintf("%s-%s-daemon.%s%s", basename, shortHash, user, idComponent)) -} - --// ParseAddr parses the address of a gopls remote. --// TODO(rFindley): further document this syntax, and allow URI-style remote --// addresses such as "auto://...". --func ParseAddr(listen string) (network string, address string) { -- // Allow passing just -remote=auto, as a shorthand for using automatic remote -- // resolution. -- if listen == AutoNetwork { -- return AutoNetwork, "" +-func verifyRemoteOwnershipPosix(network, address string) (bool, error) { +- if network != "unix" { +- return true, nil - } -- if parts := strings.SplitN(listen, ";", 2); len(parts) == 2 { -- return parts[0], parts[1] +- fi, err := os.Stat(address) +- if err != nil { +- if os.IsNotExist(err) { +- return true, nil +- } +- return false, fmt.Errorf("checking socket owner: %w", err) - } -- return "tcp", listen +- stat, ok := fi.Sys().(*syscall.Stat_t) +- if !ok { +- return false, errors.New("fi.Sys() is not a Stat_t") +- } +- user, err := user.Current() +- if err != nil { +- return false, fmt.Errorf("checking current user: %w", err) +- } +- uid, err := strconv.ParseUint(user.Uid, 10, 32) +- if err != nil { +- return false, fmt.Errorf("parsing current UID: %w", err) +- } +- return stat.Uid == uint32(uid), nil -} -diff -urN a/gopls/internal/lsp/lsprpc/lsprpc_test.go b/gopls/internal/lsp/lsprpc/lsprpc_test.go ---- a/gopls/internal/lsp/lsprpc/lsprpc_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/lsprpc_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,345 +0,0 @@ --// Copyright 2020 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/lsprpc/binder.go b/gopls/internal/lsp/lsprpc/binder.go +--- a/gopls/internal/lsp/lsprpc/binder.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/binder.go 1970-01-01 08:00:00 +@@ -1,148 +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. - @@ -42412,161 +47887,279 @@ diff -urN a/gopls/internal/lsp/lsprpc/lsprpc_test.go b/gopls/internal/lsp/lsprpc - -import ( - "context" -- "errors" -- "regexp" -- "strings" -- "testing" -- "time" +- "encoding/json" +- "fmt" - -- "golang.org/x/tools/gopls/internal/lsp/cache" -- "golang.org/x/tools/gopls/internal/lsp/debug" -- "golang.org/x/tools/gopls/internal/lsp/fake" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/internal/event" -- "golang.org/x/tools/internal/jsonrpc2" -- "golang.org/x/tools/internal/jsonrpc2/servertest" +- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" +- "golang.org/x/tools/internal/xcontext" -) - --type FakeClient struct { -- protocol.Client +-// The BinderFunc type adapts a bind function to implement the jsonrpc2.Binder +-// interface. +-type BinderFunc func(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions - -- Logs chan string +-func (f BinderFunc) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { +- return f(ctx, conn) -} - --func (c FakeClient) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error { -- c.Logs <- params.Message -- return nil +-// Middleware defines a transformation of jsonrpc2 Binders, that may be +-// composed to build jsonrpc2 servers. +-type Middleware func(jsonrpc2_v2.Binder) jsonrpc2_v2.Binder +- +-// A ServerFunc is used to construct an LSP server for a given client. +-type ServerFunc func(context.Context, protocol.ClientCloser) protocol.Server +- +-// ServerBinder binds incoming connections to a new server. +-type ServerBinder struct { +- newServer ServerFunc -} - --// fakeServer is intended to be embedded in the test fakes below, to trivially --// implement Shutdown. --type fakeServer struct { -- protocol.Server +-func NewServerBinder(newServer ServerFunc) *ServerBinder { +- return &ServerBinder{newServer: newServer} -} - --func (fakeServer) Shutdown(ctx context.Context) error { -- return nil +-func (b *ServerBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { +- client := protocol.ClientDispatcherV2(conn) +- server := b.newServer(ctx, client) +- serverHandler := protocol.ServerHandlerV2(server) +- // Wrap the server handler to inject the client into each request context, so +- // that log events are reflected back to the client. +- wrapped := jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { +- ctx = protocol.WithClient(ctx, client) +- return serverHandler.Handle(ctx, req) +- }) +- preempter := &canceler{ +- conn: conn, +- } +- return jsonrpc2_v2.ConnectionOptions{ +- Handler: wrapped, +- Preempter: preempter, +- } -} - --type PingServer struct{ fakeServer } +-type canceler struct { +- conn *jsonrpc2_v2.Connection +-} - --func (s PingServer) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error { -- event.Log(ctx, "ping") -- return nil +-func (c *canceler) Preempt(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { +- if req.Method != "$/cancelRequest" { +- return nil, jsonrpc2_v2.ErrNotHandled +- } +- var params protocol.CancelParams +- if err := json.Unmarshal(req.Params, ¶ms); err != nil { +- return nil, fmt.Errorf("%w: %v", jsonrpc2_v2.ErrParse, err) +- } +- var id jsonrpc2_v2.ID +- switch raw := params.ID.(type) { +- case float64: +- id = jsonrpc2_v2.Int64ID(int64(raw)) +- case string: +- id = jsonrpc2_v2.StringID(raw) +- default: +- return nil, fmt.Errorf("%w: invalid ID type %T", jsonrpc2_v2.ErrParse, params.ID) +- } +- c.conn.Cancel(id) +- return nil, nil -} - --func TestClientLogging(t *testing.T) { -- ctx, cancel := context.WithCancel(context.Background()) -- defer cancel() +-type ForwardBinder struct { +- dialer jsonrpc2_v2.Dialer +- onBind func(*jsonrpc2_v2.Connection) +-} - -- server := PingServer{} -- client := FakeClient{Logs: make(chan string, 10)} +-func NewForwardBinder(dialer jsonrpc2_v2.Dialer) *ForwardBinder { +- return &ForwardBinder{ +- dialer: dialer, +- } +-} - -- ctx = debug.WithInstance(ctx, "", "") -- ss := NewStreamServer(cache.New(nil), false, nil) -- ss.serverForTest = server -- ts := servertest.NewPipeServer(ss, nil) -- defer checkClose(t, ts.Close) -- cc := ts.Connect(ctx) -- cc.Go(ctx, protocol.ClientHandler(client, jsonrpc2.MethodNotFound)) +-func (b *ForwardBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) (opts jsonrpc2_v2.ConnectionOptions) { +- client := protocol.ClientDispatcherV2(conn) +- clientBinder := NewClientBinder(func(context.Context, protocol.Server) protocol.Client { return client }) - -- if err := protocol.ServerDispatcher(cc).DidOpen(ctx, &protocol.DidOpenTextDocumentParams{}); err != nil { -- t.Errorf("DidOpen: %v", err) +- serverConn, err := jsonrpc2_v2.Dial(context.Background(), b.dialer, clientBinder) +- if err != nil { +- return jsonrpc2_v2.ConnectionOptions{ +- Handler: jsonrpc2_v2.HandlerFunc(func(context.Context, *jsonrpc2_v2.Request) (interface{}, error) { +- return nil, fmt.Errorf("%w: %v", jsonrpc2_v2.ErrInternal, err) +- }), +- } - } - -- select { -- case got := <-client.Logs: -- want := "ping" -- matched, err := regexp.MatchString(want, got) -- if err != nil { -- t.Fatal(err) -- } -- if !matched { -- t.Errorf("got log %q, want a log containing %q", got, want) +- if b.onBind != nil { +- b.onBind(serverConn) +- } +- server := protocol.ServerDispatcherV2(serverConn) +- preempter := &canceler{ +- conn: conn, +- } +- detached := xcontext.Detach(ctx) +- go func() { +- conn.Wait() +- if err := serverConn.Close(); err != nil { +- event.Log(detached, fmt.Sprintf("closing remote connection: %v", err)) - } -- case <-time.After(1 * time.Second): -- t.Error("timeout waiting for client log") +- }() +- return jsonrpc2_v2.ConnectionOptions{ +- Handler: protocol.ServerHandlerV2(server), +- Preempter: preempter, - } -} - --// WaitableServer instruments LSP request so that we can control their timing. --// The requests chosen are arbitrary: we simply needed one that blocks, and --// another that doesn't. --type WaitableServer struct { -- fakeServer +-// A ClientFunc is used to construct an LSP client for a given server. +-type ClientFunc func(context.Context, protocol.Server) protocol.Client - -- Started chan struct{} -- Completed chan error +-// ClientBinder binds an LSP client to an incoming connection. +-type ClientBinder struct { +- newClient ClientFunc -} - --func (s WaitableServer) Hover(ctx context.Context, _ *protocol.HoverParams) (_ *protocol.Hover, err error) { -- s.Started <- struct{}{} -- defer func() { -- s.Completed <- err -- }() -- select { -- case <-ctx.Done(): -- return nil, errors.New("cancelled hover") -- case <-time.After(10 * time.Second): +-func NewClientBinder(newClient ClientFunc) *ClientBinder { +- return &ClientBinder{newClient} +-} +- +-func (b *ClientBinder) Bind(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { +- server := protocol.ServerDispatcherV2(conn) +- client := b.newClient(ctx, server) +- return jsonrpc2_v2.ConnectionOptions{ +- Handler: protocol.ClientHandlerV2(client), - } -- return &protocol.Hover{}, nil -} +diff -urN a/gopls/internal/lsp/lsprpc/binder_test.go b/gopls/internal/lsp/lsprpc/binder_test.go +--- a/gopls/internal/lsp/lsprpc/binder_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/binder_test.go 1970-01-01 08:00:00 +@@ -1,147 +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. - --func (s WaitableServer) ResolveCompletionItem(_ context.Context, item *protocol.CompletionItem) (*protocol.CompletionItem, error) { -- return item, nil +-package lsprpc_test +- +-import ( +- "context" +- "regexp" +- "strings" +- "testing" +- "time" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" +- +- . "golang.org/x/tools/gopls/internal/lsp/lsprpc" +-) +- +-type TestEnv struct { +- Conns []*jsonrpc2_v2.Connection +- Servers []*jsonrpc2_v2.Server -} - --func checkClose(t *testing.T, closer func() error) { -- t.Helper() -- if err := closer(); err != nil { -- t.Errorf("closing: %v", err) +-func (e *TestEnv) Shutdown(t *testing.T) { +- for _, s := range e.Servers { +- s.Shutdown() +- } +- for _, c := range e.Conns { +- if err := c.Close(); err != nil { +- t.Error(err) +- } +- } +- for _, s := range e.Servers { +- if err := s.Wait(); err != nil { +- t.Error(err) +- } - } -} - --func setupForwarding(ctx context.Context, t *testing.T, s protocol.Server) (direct, forwarded servertest.Connector, cleanup func()) { -- t.Helper() -- serveCtx := debug.WithInstance(ctx, "", "") -- ss := NewStreamServer(cache.New(nil), false, nil) -- ss.serverForTest = s -- tsDirect := servertest.NewTCPServer(serveCtx, ss, nil) -- -- forwarder, err := NewForwarder("tcp;"+tsDirect.Addr, nil) +-func (e *TestEnv) serve(ctx context.Context, t *testing.T, server jsonrpc2_v2.Binder) (jsonrpc2_v2.Listener, *jsonrpc2_v2.Server) { +- l, err := jsonrpc2_v2.NetPipeListener(ctx) - if err != nil { - t.Fatal(err) - } -- tsForwarded := servertest.NewPipeServer(forwarder, nil) -- return tsDirect, tsForwarded, func() { -- checkClose(t, tsDirect.Close) -- checkClose(t, tsForwarded.Close) -- } +- s := jsonrpc2_v2.NewServer(ctx, l, server) +- e.Servers = append(e.Servers, s) +- return l, s -} - --func TestRequestCancellation(t *testing.T) { -- ctx := context.Background() -- server := WaitableServer{ -- Started: make(chan struct{}), -- Completed: make(chan error), +-func (e *TestEnv) dial(ctx context.Context, t *testing.T, dialer jsonrpc2_v2.Dialer, client jsonrpc2_v2.Binder, forwarded bool) *jsonrpc2_v2.Connection { +- if forwarded { +- l, _ := e.serve(ctx, t, NewForwardBinder(dialer)) +- dialer = l.Dialer() - } -- tsDirect, tsForwarded, cleanup := setupForwarding(ctx, t, server) -- defer cleanup() -- tests := []struct { -- serverType string -- ts servertest.Connector -- }{ -- {"direct", tsDirect}, -- {"forwarder", tsForwarded}, +- conn, err := jsonrpc2_v2.Dial(ctx, dialer, client) +- if err != nil { +- t.Fatal(err) - } +- e.Conns = append(e.Conns, conn) +- return conn +-} - -- for _, test := range tests { -- t.Run(test.serverType, func(t *testing.T) { -- cc := test.ts.Connect(ctx) -- sd := protocol.ServerDispatcher(cc) -- cc.Go(ctx, -- protocol.Handlers( -- jsonrpc2.MethodNotFound)) -- -- ctx := context.Background() -- ctx, cancel := context.WithCancel(ctx) +-func staticClientBinder(client protocol.Client) jsonrpc2_v2.Binder { +- f := func(context.Context, protocol.Server) protocol.Client { return client } +- return NewClientBinder(f) +-} +- +-func staticServerBinder(server protocol.Server) jsonrpc2_v2.Binder { +- f := func(ctx context.Context, client protocol.ClientCloser) protocol.Server { +- return server +- } +- return NewServerBinder(f) +-} +- +-func TestClientLoggingV2(t *testing.T) { +- ctx := context.Background() +- +- for name, forwarded := range map[string]bool{ +- "forwarded": true, +- "standalone": false, +- } { +- t.Run(name, func(t *testing.T) { +- client := FakeClient{Logs: make(chan string, 10)} +- env := new(TestEnv) +- defer env.Shutdown(t) +- l, _ := env.serve(ctx, t, staticServerBinder(PingServer{})) +- conn := env.dial(ctx, t, l.Dialer(), staticClientBinder(client), forwarded) +- +- if err := protocol.ServerDispatcherV2(conn).DidOpen(ctx, &protocol.DidOpenTextDocumentParams{}); err != nil { +- t.Errorf("DidOpen: %v", err) +- } +- select { +- case got := <-client.Logs: +- want := "ping" +- matched, err := regexp.MatchString(want, got) +- if err != nil { +- t.Fatal(err) +- } +- if !matched { +- t.Errorf("got log %q, want a log containing %q", got, want) +- } +- case <-time.After(1 * time.Second): +- t.Error("timeout waiting for client log") +- } +- }) +- } +-} +- +-func TestRequestCancellationV2(t *testing.T) { +- ctx := context.Background() +- +- for name, forwarded := range map[string]bool{ +- "forwarded": true, +- "standalone": false, +- } { +- t.Run(name, func(t *testing.T) { +- server := WaitableServer{ +- Started: make(chan struct{}), +- Completed: make(chan error), +- } +- env := new(TestEnv) +- defer env.Shutdown(t) +- l, _ := env.serve(ctx, t, staticServerBinder(server)) +- client := FakeClient{Logs: make(chan string, 10)} +- conn := env.dial(ctx, t, l.Dialer(), staticClientBinder(client), forwarded) +- +- sd := protocol.ServerDispatcherV2(conn) +- ctx, cancel := context.WithCancel(ctx) - - result := make(chan error) - go func() { @@ -42585,106 +48178,340 @@ diff -urN a/gopls/internal/lsp/lsprpc/lsprpc_test.go b/gopls/internal/lsp/lsprpc - }) - } -} +diff -urN a/gopls/internal/lsp/lsprpc/commandinterceptor.go b/gopls/internal/lsp/lsprpc/commandinterceptor.go +--- a/gopls/internal/lsp/lsprpc/commandinterceptor.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/commandinterceptor.go 1970-01-01 08:00:00 +@@ -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. - --const exampleProgram = ` ---- go.mod -- --module mod +-package lsprpc - --go 1.12 ---- main.go -- --package main +-import ( +- "context" +- "encoding/json" - --import "fmt" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" +-) - --func main() { -- fmt.Println("Hello World.") --}` +-// HandlerMiddleware is a middleware that only modifies the jsonrpc2 handler. +-type HandlerMiddleware func(jsonrpc2_v2.Handler) jsonrpc2_v2.Handler - --func TestDebugInfoLifecycle(t *testing.T) { -- sb, err := fake.NewSandbox(&fake.SandboxConfig{Files: fake.UnpackTxt(exampleProgram)}) +-// BindHandler transforms a HandlerMiddleware into a Middleware. +-func BindHandler(hmw HandlerMiddleware) Middleware { +- return Middleware(func(binder jsonrpc2_v2.Binder) jsonrpc2_v2.Binder { +- return BinderFunc(func(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { +- opts := binder.Bind(ctx, conn) +- opts.Handler = hmw(opts.Handler) +- return opts +- }) +- }) +-} +- +-func CommandInterceptor(command string, run func(*protocol.ExecuteCommandParams) (interface{}, error)) Middleware { +- return BindHandler(func(delegate jsonrpc2_v2.Handler) jsonrpc2_v2.Handler { +- return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { +- if req.Method == "workspace/executeCommand" { +- var params protocol.ExecuteCommandParams +- if err := json.Unmarshal(req.Params, ¶ms); err == nil { +- if params.Command == command { +- return run(¶ms) +- } +- } +- } +- +- return delegate.Handle(ctx, req) +- }) +- }) +-} +diff -urN a/gopls/internal/lsp/lsprpc/commandinterceptor_test.go b/gopls/internal/lsp/lsprpc/commandinterceptor_test.go +--- a/gopls/internal/lsp/lsprpc/commandinterceptor_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/commandinterceptor_test.go 1970-01-01 08:00:00 +@@ -1,42 +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 lsprpc_test +- +-import ( +- "context" +- "testing" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- +- . "golang.org/x/tools/gopls/internal/lsp/lsprpc" +-) +- +-func TestCommandInterceptor(t *testing.T) { +- const command = "foo" +- caught := false +- intercept := func(_ *protocol.ExecuteCommandParams) (interface{}, error) { +- caught = true +- return map[string]interface{}{}, nil +- } +- +- ctx := context.Background() +- env := new(TestEnv) +- defer env.Shutdown(t) +- mw := CommandInterceptor(command, intercept) +- l, _ := env.serve(ctx, t, mw(noopBinder)) +- conn := env.dial(ctx, t, l.Dialer(), noopBinder, false) +- +- params := &protocol.ExecuteCommandParams{ +- Command: command, +- } +- var res interface{} +- err := conn.Call(ctx, "workspace/executeCommand", params).Await(ctx, &res) - if err != nil { - t.Fatal(err) - } -- defer func() { -- if err := sb.Close(); err != nil { -- // TODO(golang/go#38490): we can't currently make this an error because -- // it fails on Windows: the workspace directory is still locked by a -- // separate Go process. -- // Once we have a reliable way to wait for proper shutdown, make this an -- // error. -- t.Logf("closing workspace failed: %v", err) -- } -- }() +- if !caught { +- t.Errorf("workspace/executeCommand was not intercepted") +- } +-} +diff -urN a/gopls/internal/lsp/lsprpc/dialer.go b/gopls/internal/lsp/lsprpc/dialer.go +--- a/gopls/internal/lsp/lsprpc/dialer.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/dialer.go 1970-01-01 08:00:00 +@@ -1,114 +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. - -- baseCtx, cancel := context.WithCancel(context.Background()) -- defer cancel() -- clientCtx := debug.WithInstance(baseCtx, "", "") -- serverCtx := debug.WithInstance(baseCtx, "", "") +-package lsprpc - -- cache := cache.New(nil) -- ss := NewStreamServer(cache, false, nil) -- tsBackend := servertest.NewTCPServer(serverCtx, ss, nil) +-import ( +- "context" +- "fmt" +- "io" +- "net" +- "os" +- "time" - -- forwarder, err := NewForwarder("tcp;"+tsBackend.Addr, nil) -- if err != nil { -- t.Fatal(err) +- exec "golang.org/x/sys/execabs" +- "golang.org/x/tools/internal/event" +-) +- +-// AutoNetwork is the pseudo network type used to signal that gopls should use +-// automatic discovery to resolve a remote address. +-const AutoNetwork = "auto" +- +-// An AutoDialer is a jsonrpc2 dialer that understands the 'auto' network. +-type AutoDialer struct { +- network, addr string // the 'real' network and address +- isAuto bool // whether the server is on the 'auto' network +- +- executable string +- argFunc func(network, addr string) []string +-} +- +-func NewAutoDialer(rawAddr string, argFunc func(network, addr string) []string) (*AutoDialer, error) { +- d := AutoDialer{ +- argFunc: argFunc, - } -- tsForwarder := servertest.NewPipeServer(forwarder, nil) +- d.network, d.addr = ParseAddr(rawAddr) +- if d.network == AutoNetwork { +- d.isAuto = true +- bin, err := os.Executable() +- if err != nil { +- return nil, fmt.Errorf("getting executable: %w", err) +- } +- d.executable = bin +- d.network, d.addr = autoNetworkAddress(bin, d.addr) +- } +- return &d, nil +-} - -- const skipApplyEdits = false -- ed1, err := fake.NewEditor(sb, fake.EditorConfig{}).Connect(clientCtx, tsForwarder, fake.ClientHooks{}, skipApplyEdits) +-// Dial implements the jsonrpc2.Dialer interface. +-func (d *AutoDialer) Dial(ctx context.Context) (io.ReadWriteCloser, error) { +- conn, err := d.dialNet(ctx) +- return conn, err +-} +- +-// TODO(rFindley): remove this once we no longer need to integrate with v1 of +-// the jsonrpc2 package. +-func (d *AutoDialer) dialNet(ctx context.Context) (net.Conn, error) { +- // Attempt to verify that we own the remote. This is imperfect, but if we can +- // determine that the remote is owned by a different user, we should fail. +- ok, err := verifyRemoteOwnership(d.network, d.addr) - if err != nil { -- t.Fatal(err) +- // If the ownership check itself failed, we fail open but log an error to +- // the user. +- event.Error(ctx, "unable to check daemon socket owner, failing open", err) +- } else if !ok { +- // We successfully checked that the socket is not owned by us, we fail +- // closed. +- return nil, fmt.Errorf("socket %q is owned by a different user", d.addr) - } -- defer ed1.Close(clientCtx) -- ed2, err := fake.NewEditor(sb, fake.EditorConfig{}).Connect(baseCtx, tsBackend, fake.ClientHooks{}, skipApplyEdits) -- if err != nil { -- t.Fatal(err) +- const dialTimeout = 1 * time.Second +- // Try dialing our remote once, in case it is already running. +- netConn, err := net.DialTimeout(d.network, d.addr, dialTimeout) +- if err == nil { +- return netConn, nil +- } +- if d.isAuto && d.argFunc != nil { +- if d.network == "unix" { +- // Sometimes the socketfile isn't properly cleaned up when the server +- // shuts down. Since we have already tried and failed to dial this +- // address, it should *usually* be safe to remove the socket before +- // binding to the address. +- // TODO(rfindley): there is probably a race here if multiple server +- // instances are simultaneously starting up. +- if _, err := os.Stat(d.addr); err == nil { +- if err := os.Remove(d.addr); err != nil { +- return nil, fmt.Errorf("removing remote socket file: %w", err) +- } +- } +- } +- args := d.argFunc(d.network, d.addr) +- cmd := exec.Command(d.executable, args...) +- if err := runRemote(cmd); err != nil { +- return nil, err +- } - } -- defer ed2.Close(baseCtx) - -- serverDebug := debug.GetInstance(serverCtx) -- if got, want := len(serverDebug.State.Clients()), 2; got != want { -- t.Errorf("len(server:Clients) = %d, want %d", got, want) +- const retries = 5 +- // It can take some time for the newly started server to bind to our address, +- // so we retry for a bit. +- for retry := 0; retry < retries; retry++ { +- startDial := time.Now() +- netConn, err = net.DialTimeout(d.network, d.addr, dialTimeout) +- if err == nil { +- return netConn, nil +- } +- event.Log(ctx, fmt.Sprintf("failed attempt #%d to connect to remote: %v\n", retry+2, err)) +- // In case our failure was a fast-failure, ensure we wait at least +- // f.dialTimeout before trying again. +- if retry != retries-1 { +- time.Sleep(dialTimeout - time.Since(startDial)) +- } - } -- if got, want := len(serverDebug.State.Sessions()), 2; got != want { -- t.Errorf("len(server:Sessions) = %d, want %d", got, want) +- return nil, fmt.Errorf("dialing remote: %w", err) +-} +diff -urN a/gopls/internal/lsp/lsprpc/goenv.go b/gopls/internal/lsp/lsprpc/goenv.go +--- a/gopls/internal/lsp/lsprpc/goenv.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/goenv.go 1970-01-01 08:00:00 +@@ -1,96 +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 lsprpc +- +-import ( +- "context" +- "encoding/json" +- "fmt" +- "os" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/gocommand" +- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" +-) +- +-func GoEnvMiddleware() (Middleware, error) { +- return BindHandler(func(delegate jsonrpc2_v2.Handler) jsonrpc2_v2.Handler { +- return jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { +- if req.Method == "initialize" { +- if err := addGoEnvToInitializeRequestV2(ctx, req); err != nil { +- event.Error(ctx, "adding go env to initialize", err) +- } +- } +- return delegate.Handle(ctx, req) +- }) +- }), nil +-} +- +-func addGoEnvToInitializeRequestV2(ctx context.Context, req *jsonrpc2_v2.Request) error { +- var params protocol.ParamInitialize +- if err := json.Unmarshal(req.Params, ¶ms); err != nil { +- return err - } -- clientDebug := debug.GetInstance(clientCtx) -- if got, want := len(clientDebug.State.Servers()), 1; got != want { -- t.Errorf("len(client:Servers) = %d, want %d", got, want) +- var opts map[string]interface{} +- switch v := params.InitializationOptions.(type) { +- case nil: +- opts = make(map[string]interface{}) +- case map[string]interface{}: +- opts = v +- default: +- return fmt.Errorf("unexpected type for InitializationOptions: %T", v) - } -- // Close one of the connections to verify that the client and session were -- // dropped. -- if err := ed1.Close(clientCtx); err != nil { -- t.Fatal(err) +- envOpt, ok := opts["env"] +- if !ok { +- envOpt = make(map[string]interface{}) - } -- /*TODO: at this point we have verified the editor is closed -- However there is no way currently to wait for all associated go routines to -- go away, and we need to wait for those to trigger the client drop -- for now we just give it a little bit of time, but we need to fix this -- in a principled way -- */ -- start := time.Now() -- delay := time.Millisecond -- const maxWait = time.Second -- for len(serverDebug.State.Clients()) > 1 { -- if time.Since(start) > maxWait { -- break +- env, ok := envOpt.(map[string]interface{}) +- if !ok { +- return fmt.Errorf("env option is %T, expected a map", envOpt) +- } +- goenv, err := getGoEnv(ctx, env) +- if err != nil { +- return err +- } +- // We don't want to propagate GOWORK unless explicitly set since that could mess with +- // path inference during cmd/go invocations, see golang/go#51825. +- _, goworkSet := os.LookupEnv("GOWORK") +- for govar, value := range goenv { +- if govar == "GOWORK" && !goworkSet { +- continue - } -- time.Sleep(delay) -- delay *= 2 +- env[govar] = value - } -- if got, want := len(serverDebug.State.Clients()), 1; got != want { -- t.Errorf("len(server:Clients) = %d, want %d", got, want) +- opts["env"] = env +- params.InitializationOptions = opts +- raw, err := json.Marshal(params) +- if err != nil { +- return fmt.Errorf("marshaling updated options: %v", err) - } -- if got, want := len(serverDebug.State.Sessions()), 1; got != want { -- t.Errorf("len(server:Sessions()) = %d, want %d", got, want) +- req.Params = json.RawMessage(raw) +- return nil +-} +- +-func getGoEnv(ctx context.Context, env map[string]interface{}) (map[string]string, error) { +- var runEnv []string +- for k, v := range env { +- runEnv = append(runEnv, fmt.Sprintf("%s=%s", k, v)) +- } +- runner := gocommand.Runner{} +- output, err := runner.Run(ctx, gocommand.Invocation{ +- Verb: "env", +- Args: []string{"-json"}, +- Env: runEnv, +- }) +- if err != nil { +- return nil, err +- } +- envmap := make(map[string]string) +- if err := json.Unmarshal(output.Bytes(), &envmap); err != nil { +- return nil, err - } +- return envmap, nil -} +diff -urN a/gopls/internal/lsp/lsprpc/goenv_test.go b/gopls/internal/lsp/lsprpc/goenv_test.go +--- a/gopls/internal/lsp/lsprpc/goenv_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/goenv_test.go 1970-01-01 08:00:00 +@@ -1,68 +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 lsprpc_test +- +-import ( +- "context" +- "testing" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/internal/testenv" +- +- . "golang.org/x/tools/gopls/internal/lsp/lsprpc" +-) - -type initServer struct { -- fakeServer +- protocol.Server - - params *protocol.ParamInitialize -} @@ -42694,1671 +48521,1222 @@ diff -urN a/gopls/internal/lsp/lsprpc/lsprpc_test.go b/gopls/internal/lsp/lsprpc - return &protocol.InitializeResult{}, nil -} - --func TestEnvForwarding(t *testing.T) { +-func TestGoEnvMiddleware(t *testing.T) { +- testenv.NeedsTool(t, "go") +- - ctx := context.Background() - - server := &initServer{} -- _, tsForwarded, cleanup := setupForwarding(ctx, t, server) -- defer cleanup() -- -- conn := tsForwarded.Connect(ctx) -- conn.Go(ctx, jsonrpc2.MethodNotFound) -- dispatch := protocol.ServerDispatcher(conn) +- env := new(TestEnv) +- defer env.Shutdown(t) +- l, _ := env.serve(ctx, t, staticServerBinder(server)) +- mw, err := GoEnvMiddleware() +- if err != nil { +- t.Fatal(err) +- } +- binder := mw(NewForwardBinder(l.Dialer())) +- l, _ = env.serve(ctx, t, binder) +- conn := env.dial(ctx, t, l.Dialer(), noopBinder, true) +- dispatch := protocol.ServerDispatcherV2(conn) - initParams := &protocol.ParamInitialize{} - initParams.InitializationOptions = map[string]interface{}{ - "env": map[string]interface{}{ - "GONOPROXY": "example.com", - }, - } -- _, err := dispatch.Initialize(ctx, initParams) -- if err != nil { +- if _, err := dispatch.Initialize(ctx, initParams); err != nil { - t.Fatal(err) - } +- - if server.params == nil { - t.Fatalf("initialize params are unset") - } -- env := server.params.InitializationOptions.(map[string]interface{})["env"].(map[string]interface{}) +- envOpts := server.params.InitializationOptions.(map[string]interface{})["env"].(map[string]interface{}) - - // Check for an arbitrary Go variable. It should be set. -- if _, ok := env["GOPRIVATE"]; !ok { +- if _, ok := envOpts["GOPRIVATE"]; !ok { - t.Errorf("Go environment variable GOPRIVATE unset in initialization options") - } - // Check that the variable present in our user config was not overwritten. -- if v := env["GONOPROXY"]; v != "example.com" { -- t.Errorf("GONOPROXY environment variable was overwritten") -- } --} -- --func TestListenParsing(t *testing.T) { -- tests := []struct { -- input, wantNetwork, wantAddr string -- }{ -- {"127.0.0.1:0", "tcp", "127.0.0.1:0"}, -- {"unix;/tmp/sock", "unix", "/tmp/sock"}, -- {"auto", "auto", ""}, -- {"auto;foo", "auto", "foo"}, -- } -- -- for _, test := range tests { -- gotNetwork, gotAddr := ParseAddr(test.input) -- if gotNetwork != test.wantNetwork { -- t.Errorf("network = %q, want %q", gotNetwork, test.wantNetwork) -- } -- if gotAddr != test.wantAddr { -- t.Errorf("addr = %q, want %q", gotAddr, test.wantAddr) -- } +- if got, want := envOpts["GONOPROXY"], "example.com"; got != want { +- t.Errorf("GONOPROXY=%q, want %q", got, want) - } -} -diff -urN a/gopls/internal/lsp/lsprpc/middleware.go b/gopls/internal/lsp/lsprpc/middleware.go ---- a/gopls/internal/lsp/lsprpc/middleware.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/middleware.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,142 +0,0 @@ --// Copyright 2021 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/lsprpc/lsprpc.go b/gopls/internal/lsp/lsprpc/lsprpc.go +--- a/gopls/internal/lsp/lsprpc/lsprpc.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/lsprpc.go 1970-01-01 08:00:00 +@@ -1,545 +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 lsprpc implements a jsonrpc2.StreamServer that may be used to +-// serve the LSP on a jsonrpc2 channel. -package lsprpc - -import ( - "context" - "encoding/json" - "fmt" +- "log" +- "net" +- "os" +- "strconv" +- "strings" - "sync" +- "sync/atomic" +- "time" - +- "golang.org/x/tools/gopls/internal/lsp" +- "golang.org/x/tools/gopls/internal/lsp/cache" +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/debug" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/internal/event" -- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" +- "golang.org/x/tools/internal/event/tag" +- "golang.org/x/tools/internal/jsonrpc2" -) - --// Metadata holds arbitrary data transferred between jsonrpc2 peers. --type Metadata map[string]interface{} -- --// PeerInfo holds information about a peering between jsonrpc2 servers. --type PeerInfo struct { -- // RemoteID is the identity of the current server on its peer. -- RemoteID int64 +-// Unique identifiers for client/server. +-var serverIndex int64 - -- // LocalID is the identity of the peer on the server. -- LocalID int64 +-// The StreamServer type is a jsonrpc2.StreamServer that handles incoming +-// streams as a new LSP session, using a shared cache. +-type StreamServer struct { +- cache *cache.Cache +- // daemon controls whether or not to log new connections. +- daemon bool - -- // IsClient reports whether the peer is a client. If false, the peer is a -- // server. -- IsClient bool +- // optionsOverrides is passed to newly created sessions. +- optionsOverrides func(*source.Options) - -- // Metadata holds arbitrary information provided by the peer. -- Metadata Metadata +- // serverForTest may be set to a test fake for testing. +- serverForTest protocol.Server -} - --// Handshaker handles both server and client handshaking over jsonrpc2. To --// instrument server-side handshaking, use Handshaker.Middleware. To instrument --// client-side handshaking, call Handshaker.ClientHandshake for any new --// client-side connections. --type Handshaker struct { -- // Metadata will be shared with peers via handshaking. -- Metadata Metadata -- -- mu sync.Mutex -- prevID int64 -- peers map[int64]PeerInfo +-// NewStreamServer creates a StreamServer using the shared cache. If +-// withTelemetry is true, each session is instrumented with telemetry that +-// records RPC statistics. +-func NewStreamServer(cache *cache.Cache, daemon bool, optionsFunc func(*source.Options)) *StreamServer { +- return &StreamServer{cache: cache, daemon: daemon, optionsOverrides: optionsFunc} -} - --// Peers returns the peer info this handshaker knows about by way of either the --// server-side handshake middleware, or client-side handshakes. --func (h *Handshaker) Peers() []PeerInfo { -- h.mu.Lock() -- defer h.mu.Unlock() -- -- var c []PeerInfo -- for _, v := range h.peers { -- c = append(c, v) +-func (s *StreamServer) Binder() *ServerBinder { +- newServer := func(ctx context.Context, client protocol.ClientCloser) protocol.Server { +- session := cache.NewSession(ctx, s.cache) +- server := s.serverForTest +- if server == nil { +- options := source.DefaultOptions(s.optionsOverrides) +- server = lsp.NewServer(session, client, options) +- if instance := debug.GetInstance(ctx); instance != nil { +- instance.AddService(server, session) +- } +- } +- return server - } -- return c +- return NewServerBinder(newServer) -} - --// Middleware is a jsonrpc2 middleware function to augment connection binding --// to handle the handshake method, and record disconnections. --func (h *Handshaker) Middleware(inner jsonrpc2_v2.Binder) jsonrpc2_v2.Binder { -- return BinderFunc(func(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { -- opts := inner.Bind(ctx, conn) -- -- localID := h.nextID() -- info := &PeerInfo{ -- RemoteID: localID, -- Metadata: h.Metadata, +-// ServeStream implements the jsonrpc2.StreamServer interface, by handling +-// incoming streams using a new lsp server. +-func (s *StreamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) error { +- client := protocol.ClientDispatcher(conn) +- session := cache.NewSession(ctx, s.cache) +- server := s.serverForTest +- if server == nil { +- options := source.DefaultOptions(s.optionsOverrides) +- server = lsp.NewServer(session, client, options) +- if instance := debug.GetInstance(ctx); instance != nil { +- instance.AddService(server, session) - } -- -- // Wrap the delegated handler to accept the handshake. -- delegate := opts.Handler -- opts.Handler = jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { -- if req.Method == handshakeMethod { -- var peerInfo PeerInfo -- if err := json.Unmarshal(req.Params, &peerInfo); err != nil { -- return nil, fmt.Errorf("%w: unmarshaling client info: %v", jsonrpc2_v2.ErrInvalidParams, err) -- } -- peerInfo.LocalID = localID -- peerInfo.IsClient = true -- h.recordPeer(peerInfo) -- return info, nil -- } -- return delegate.Handle(ctx, req) -- }) -- -- // Record the dropped client. -- go h.cleanupAtDisconnect(conn, localID) -- -- return opts -- }) --} -- --// ClientHandshake performs a client-side handshake with the server at the --// other end of conn, recording the server's peer info and watching for conn's --// disconnection. --func (h *Handshaker) ClientHandshake(ctx context.Context, conn *jsonrpc2_v2.Connection) { -- localID := h.nextID() -- info := &PeerInfo{ -- RemoteID: localID, -- Metadata: h.Metadata, - } -- -- call := conn.Call(ctx, handshakeMethod, info) -- var serverInfo PeerInfo -- if err := call.Await(ctx, &serverInfo); err != nil { -- event.Error(ctx, "performing handshake", err) -- return +- // Clients may or may not send a shutdown message. Make sure the server is +- // shut down. +- // TODO(rFindley): this shutdown should perhaps be on a disconnected context. +- defer func() { +- if err := server.Shutdown(ctx); err != nil { +- event.Error(ctx, "error shutting down", err) +- } +- }() +- executable, err := os.Executable() +- if err != nil { +- log.Printf("error getting gopls path: %v", err) +- executable = "" - } -- serverInfo.LocalID = localID -- h.recordPeer(serverInfo) -- -- go h.cleanupAtDisconnect(conn, localID) --} -- --func (h *Handshaker) nextID() int64 { -- h.mu.Lock() -- defer h.mu.Unlock() -- -- h.prevID++ -- return h.prevID +- ctx = protocol.WithClient(ctx, client) +- conn.Go(ctx, +- protocol.Handlers( +- handshaker(session, executable, s.daemon, +- protocol.ServerHandler(server, +- jsonrpc2.MethodNotFound)))) +- if s.daemon { +- log.Printf("Session %s: connected", session.ID()) +- defer log.Printf("Session %s: exited", session.ID()) +- } +- <-conn.Done() +- return conn.Err() -} - --func (h *Handshaker) cleanupAtDisconnect(conn *jsonrpc2_v2.Connection, peerID int64) { -- conn.Wait() +-// A Forwarder is a jsonrpc2.StreamServer that handles an LSP stream by +-// forwarding it to a remote. This is used when the gopls process started by +-// the editor is in the `-remote` mode, which means it finds and connects to a +-// separate gopls daemon. In these cases, we still want the forwarder gopls to +-// be instrumented with telemetry, and want to be able to in some cases hijack +-// the jsonrpc2 connection with the daemon. +-type Forwarder struct { +- dialer *AutoDialer - -- h.mu.Lock() -- defer h.mu.Unlock() -- delete(h.peers, peerID) +- mu sync.Mutex +- // Hold on to the server connection so that we can redo the handshake if any +- // information changes. +- serverConn jsonrpc2.Conn +- serverID string -} - --func (h *Handshaker) recordPeer(info PeerInfo) { -- h.mu.Lock() -- defer h.mu.Unlock() -- if h.peers == nil { -- h.peers = make(map[int64]PeerInfo) +-// NewForwarder creates a new Forwarder, ready to forward connections to the +-// remote server specified by rawAddr. If provided and rawAddr indicates an +-// 'automatic' address (starting with 'auto;'), argFunc may be used to start a +-// remote server for the auto-discovered address. +-func NewForwarder(rawAddr string, argFunc func(network, address string) []string) (*Forwarder, error) { +- dialer, err := NewAutoDialer(rawAddr, argFunc) +- if err != nil { +- return nil, err - } -- h.peers[info.LocalID] = info +- fwd := &Forwarder{ +- dialer: dialer, +- } +- return fwd, nil -} -diff -urN a/gopls/internal/lsp/lsprpc/middleware_test.go b/gopls/internal/lsp/lsprpc/middleware_test.go ---- a/gopls/internal/lsp/lsprpc/middleware_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsprpc/middleware_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,93 +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 lsprpc_test -- --import ( -- "context" -- "errors" -- "fmt" -- "testing" -- "time" -- -- . "golang.org/x/tools/gopls/internal/lsp/lsprpc" -- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" --) -- --var noopBinder = BinderFunc(func(context.Context, *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { -- return jsonrpc2_v2.ConnectionOptions{} --}) - --func TestHandshakeMiddleware(t *testing.T) { -- sh := &Handshaker{ -- Metadata: Metadata{ -- "answer": 42, -- }, +-// QueryServerState queries the server state of the current server. +-func QueryServerState(ctx context.Context, addr string) (*ServerState, error) { +- serverConn, err := dialRemote(ctx, addr) +- if err != nil { +- return nil, err - } -- ctx := context.Background() -- env := new(TestEnv) -- defer env.Shutdown(t) -- l, _ := env.serve(ctx, t, sh.Middleware(noopBinder)) -- conn := env.dial(ctx, t, l.Dialer(), noopBinder, false) -- ch := &Handshaker{ -- Metadata: Metadata{ -- "question": 6 * 9, -- }, +- var state ServerState +- if err := protocol.Call(ctx, serverConn, sessionsMethod, nil, &state); err != nil { +- return nil, fmt.Errorf("querying server state: %w", err) - } +- return &state, nil +-} - -- check := func(connected bool) error { -- clients := sh.Peers() -- servers := ch.Peers() -- want := 0 -- if connected { -- want = 1 -- } -- if got := len(clients); got != want { -- return fmt.Errorf("got %d clients on the server, want %d", got, want) -- } -- if got := len(servers); got != want { -- return fmt.Errorf("got %d servers on the client, want %d", got, want) -- } -- if !connected { -- return nil -- } -- client := clients[0] -- server := servers[0] -- if _, ok := client.Metadata["question"]; !ok { -- return errors.New("no client metadata") -- } -- if _, ok := server.Metadata["answer"]; !ok { -- return errors.New("no server metadata") -- } -- if client.LocalID != server.RemoteID { -- return fmt.Errorf("client.LocalID == %d, server.PeerID == %d", client.LocalID, server.RemoteID) -- } -- if client.RemoteID != server.LocalID { -- return fmt.Errorf("client.PeerID == %d, server.LocalID == %d", client.RemoteID, server.LocalID) +-// dialRemote is used for making calls into the gopls daemon. addr should be a +-// URL, possibly on the synthetic 'auto' network (e.g. tcp://..., unix://..., +-// or auto://...). +-func dialRemote(ctx context.Context, addr string) (jsonrpc2.Conn, error) { +- network, address := ParseAddr(addr) +- if network == AutoNetwork { +- gp, err := os.Executable() +- if err != nil { +- return nil, fmt.Errorf("getting gopls path: %w", err) - } -- return nil +- network, address = autoNetworkAddress(gp, address) +- } +- netConn, err := net.DialTimeout(network, address, 5*time.Second) +- if err != nil { +- return nil, fmt.Errorf("dialing remote: %w", err) - } +- serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn)) +- serverConn.Go(ctx, jsonrpc2.MethodNotFound) +- return serverConn, nil +-} - -- if err := check(false); err != nil { -- t.Fatalf("before handshake: %v", err) +-func ExecuteCommand(ctx context.Context, addr string, id string, request, result interface{}) error { +- serverConn, err := dialRemote(ctx, addr) +- if err != nil { +- return err - } -- ch.ClientHandshake(ctx, conn) -- if err := check(true); err != nil { -- t.Fatalf("after handshake: %v", err) +- args, err := command.MarshalArgs(request) +- if err != nil { +- return err - } -- conn.Close() -- // Wait for up to ~2s for connections to get cleaned up. -- delay := 25 * time.Millisecond -- for retries := 3; retries >= 0; retries-- { -- time.Sleep(delay) -- err := check(false) -- if err == nil { -- return -- } -- if retries == 0 { -- t.Fatalf("after closing connection: %v", err) -- } -- delay *= 4 +- params := protocol.ExecuteCommandParams{ +- Command: id, +- Arguments: args, - } +- return protocol.Call(ctx, serverConn, "workspace/executeCommand", params, result) -} -diff -urN a/gopls/internal/lsp/lsp_test.go b/gopls/internal/lsp/lsp_test.go ---- a/gopls/internal/lsp/lsp_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/lsp_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1360 +0,0 @@ --// Copyright 2018 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 lsp +-// ServeStream dials the forwarder remote and binds the remote to serve the LSP +-// on the incoming stream. +-func (f *Forwarder) ServeStream(ctx context.Context, clientConn jsonrpc2.Conn) error { +- client := protocol.ClientDispatcher(clientConn) - --import ( -- "bytes" -- "context" -- "fmt" -- "os" -- "os/exec" -- "path/filepath" -- "sort" -- "strings" -- "testing" +- netConn, err := f.dialer.dialNet(ctx) +- if err != nil { +- return fmt.Errorf("forwarder: connecting to remote: %w", err) +- } +- serverConn := jsonrpc2.NewConn(jsonrpc2.NewHeaderStream(netConn)) +- server := protocol.ServerDispatcher(serverConn) - -- "github.com/google/go-cmp/cmp" -- "github.com/google/go-cmp/cmp/cmpopts" -- "golang.org/x/tools/gopls/internal/lsp/cache" -- "golang.org/x/tools/gopls/internal/lsp/command" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/lsp/tests" -- "golang.org/x/tools/gopls/internal/lsp/tests/compare" -- "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" -- "golang.org/x/tools/internal/diff" -- "golang.org/x/tools/internal/event" -- "golang.org/x/tools/internal/testenv" --) +- // Forward between connections. +- serverConn.Go(ctx, +- protocol.Handlers( +- protocol.ClientHandler(client, +- jsonrpc2.MethodNotFound))) - --func TestMain(m *testing.M) { -- bug.PanicOnBugs = true -- testenv.ExitIfSmallMachine() +- // Don't run the clientConn yet, so that we can complete the handshake before +- // processing any client messages. - -- // Set the global exporter to nil so that we don't log to stderr. This avoids -- // a lot of misleading noise in test output. -- // -- // TODO(rfindley): investigate whether we can/should capture logs scoped to -- // individual tests by passing in a context with a local exporter. -- event.SetExporter(nil) +- // Do a handshake with the server instance to exchange debug information. +- index := atomic.AddInt64(&serverIndex, 1) +- f.mu.Lock() +- f.serverConn = serverConn +- f.serverID = strconv.FormatInt(index, 10) +- f.mu.Unlock() +- f.handshake(ctx) +- clientConn.Go(ctx, +- protocol.Handlers( +- f.handler( +- protocol.ServerHandler(server, +- jsonrpc2.MethodNotFound)))) - -- os.Exit(m.Run()) --} +- select { +- case <-serverConn.Done(): +- clientConn.Close() +- case <-clientConn.Done(): +- serverConn.Close() +- } - --// TestLSP runs the marker tests in files beneath testdata/ using --// implementations of each of the marker operations (e.g. @codelens) that --// make LSP RPCs (e.g. textDocument/codeLens) to a gopls server. --func TestLSP(t *testing.T) { -- tests.RunTests(t, "testdata", true, testLSP) +- err = nil +- if serverConn.Err() != nil { +- err = fmt.Errorf("remote disconnected: %v", serverConn.Err()) +- } else if clientConn.Err() != nil { +- err = fmt.Errorf("client disconnected: %v", clientConn.Err()) +- } +- event.Log(ctx, fmt.Sprintf("forwarder: exited with error: %v", err)) +- return err -} - --func testLSP(t *testing.T, datum *tests.Data) { -- ctx := tests.Context(t) -- -- session := cache.NewSession(ctx, cache.New(nil), nil) -- options := source.DefaultOptions().Clone() -- tests.DefaultOptions(options) -- session.SetOptions(options) -- options.SetEnvSlice(datum.Config.Env) -- view, snapshot, release, err := session.NewView(ctx, datum.Config.Dir, span.URIFromPath(datum.Config.Dir), options) +-// TODO(rfindley): remove this handshaking in favor of middleware. +-func (f *Forwarder) handshake(ctx context.Context) { +- // This call to os.Executable is redundant, and will be eliminated by the +- // transition to the V2 API. +- goplsPath, err := os.Executable() - if err != nil { -- t.Fatal(err) -- } -- -- defer session.RemoveView(view) -- -- // Enable type error analyses for tests. -- // TODO(golang/go#38212): Delete this once they are enabled by default. -- tests.EnableAllAnalyzers(options) -- session.SetViewOptions(ctx, view, options) -- -- // Enable all inlay hints for tests. -- tests.EnableAllInlayHints(options) -- -- // Only run the -modfile specific tests in module mode with Go 1.14 or above. -- datum.ModfileFlagAvailable = len(snapshot.ModFiles()) > 0 && testenv.Go1Point() >= 14 -- release() -- -- // Open all files for performance reasons. This is done because gopls only -- // keeps active packages in memory for open files. -- // -- // In practice clients will only send document-oriented requests for open -- // files. -- var modifications []source.FileModification -- for _, module := range datum.Exported.Modules { -- for name := range module.Files { -- filename := datum.Exported.File(module.Name, name) -- if filepath.Ext(filename) != ".go" { -- continue -- } -- content, err := datum.Exported.FileContents(filename) -- if err != nil { -- t.Fatal(err) -- } -- modifications = append(modifications, source.FileModification{ -- URI: span.URIFromPath(filename), -- Action: source.Open, -- Version: -1, -- Text: content, -- LanguageID: "go", -- }) -- } +- event.Error(ctx, "getting executable for handshake", err) +- goplsPath = "" - } -- for filename, content := range datum.Config.Overlay { -- if filepath.Ext(filename) != ".go" { -- continue +- var ( +- hreq = handshakeRequest{ +- ServerID: f.serverID, +- GoplsPath: goplsPath, - } -- modifications = append(modifications, source.FileModification{ -- URI: span.URIFromPath(filename), -- Action: source.Open, -- Version: -1, -- Text: content, -- LanguageID: "go", -- }) +- hresp handshakeResponse +- ) +- if di := debug.GetInstance(ctx); di != nil { +- hreq.Logfile = di.Logfile +- hreq.DebugAddr = di.ListenedDebugAddress() - } -- if err := session.ModifyFiles(ctx, modifications); err != nil { -- t.Fatal(err) +- if err := protocol.Call(ctx, f.serverConn, handshakeMethod, hreq, &hresp); err != nil { +- // TODO(rfindley): at some point in the future we should return an error +- // here. Handshakes have become functional in nature. +- event.Error(ctx, "forwarder: gopls handshake failed", err) - } -- r := &runner{ -- data: datum, -- ctx: ctx, -- normalizers: tests.CollectNormalizers(datum.Exported), -- editRecv: make(chan map[span.URI][]byte, 1), +- if hresp.GoplsPath != goplsPath { +- event.Error(ctx, "", fmt.Errorf("forwarder: gopls path mismatch: forwarder is %q, remote is %q", goplsPath, hresp.GoplsPath)) - } -- -- r.server = NewServer(session, testClient{runner: r}) -- tests.Run(t, r, datum) --} -- --// runner implements tests.Tests by making LSP RPCs to a gopls server. --type runner struct { -- server *Server -- data *tests.Data -- diagnostics map[span.URI][]*source.Diagnostic -- ctx context.Context -- normalizers []tests.Normalizer -- editRecv chan map[span.URI][]byte --} -- --// testClient stubs any client functions that may be called by LSP functions. --type testClient struct { -- protocol.Client -- runner *runner --} -- --func (c testClient) Close() error { -- return nil --} -- --// Trivially implement PublishDiagnostics so that we can call --// server.publishReports below to de-dup sent diagnostics. --func (c testClient) PublishDiagnostics(context.Context, *protocol.PublishDiagnosticsParams) error { -- return nil --} -- --func (c testClient) ShowMessage(context.Context, *protocol.ShowMessageParams) error { -- return nil +- event.Log(ctx, "New server", +- tag.NewServer.Of(f.serverID), +- tag.Logfile.Of(hresp.Logfile), +- tag.DebugAddress.Of(hresp.DebugAddr), +- tag.GoplsPath.Of(hresp.GoplsPath), +- tag.ClientID.Of(hresp.SessionID), +- ) -} - --func (c testClient) ApplyEdit(ctx context.Context, params *protocol.ApplyWorkspaceEditParams) (*protocol.ApplyWorkspaceEditResult, error) { -- res, err := applyTextDocumentEdits(c.runner, params.Edit.DocumentChanges) +-func ConnectToRemote(ctx context.Context, addr string) (net.Conn, error) { +- dialer, err := NewAutoDialer(addr, nil) - if err != nil { - return nil, err - } -- c.runner.editRecv <- res -- return &protocol.ApplyWorkspaceEditResult{Applied: true}, nil +- return dialer.dialNet(ctx) -} - --func (r *runner) CallHierarchy(t *testing.T, spn span.Span, expectedCalls *tests.CallHierarchyResult) { -- mapper, err := r.data.Mapper(spn.URI()) -- if err != nil { -- t.Fatal(err) -- } -- loc, err := mapper.SpanLocation(spn) -- if err != nil { -- t.Fatalf("failed for %v: %v", spn, err) -- } -- -- params := &protocol.CallHierarchyPrepareParams{ -- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), -- } -- -- items, err := r.server.PrepareCallHierarchy(r.ctx, params) -- if err != nil { -- t.Fatal(err) -- } -- if len(items) == 0 { -- t.Fatalf("expected call hierarchy item to be returned for identifier at %v\n", loc.Range) +-// handler intercepts messages to the daemon to enrich them with local +-// information. +-func (f *Forwarder) handler(handler jsonrpc2.Handler) jsonrpc2.Handler { +- return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error { +- // Intercept certain messages to add special handling. +- switch r.Method() { +- case "initialize": +- if newr, err := addGoEnvToInitializeRequest(ctx, r); err == nil { +- r = newr +- } else { +- log.Printf("unable to add local env to initialize request: %v", err) +- } +- case "workspace/executeCommand": +- var params protocol.ExecuteCommandParams +- if err := json.Unmarshal(r.Params(), ¶ms); err == nil { +- if params.Command == command.StartDebugging.ID() { +- var args command.DebuggingArgs +- if err := command.UnmarshalArgs(params.Arguments, &args); err == nil { +- reply = f.replyWithDebugAddress(ctx, reply, args) +- } else { +- event.Error(ctx, "unmarshaling debugging args", err) +- } +- } +- } else { +- event.Error(ctx, "intercepting executeCommand request", err) +- } +- } +- // The gopls workspace environment defaults to the process environment in +- // which gopls daemon was started. To avoid discrepancies in Go environment +- // between the editor and daemon, inject any unset variables in `go env` +- // into the options sent by initialize. +- // +- // See also golang.org/issue/37830. +- return handler(ctx, reply, r) - } +-} - -- callLocation := protocol.Location{ -- URI: items[0].URI, -- Range: items[0].Range, -- } -- if callLocation != loc { -- t.Fatalf("expected server.PrepareCallHierarchy to return identifier at %v but got %v\n", loc, callLocation) +-// addGoEnvToInitializeRequest builds a new initialize request in which we set +-// any environment variables output by `go env` and not already present in the +-// request. +-// +-// It returns an error if r is not an initialize request, or is otherwise +-// malformed. +-func addGoEnvToInitializeRequest(ctx context.Context, r jsonrpc2.Request) (jsonrpc2.Request, error) { +- var params protocol.ParamInitialize +- if err := json.Unmarshal(r.Params(), ¶ms); err != nil { +- return nil, err - } -- -- incomingCalls, err := r.server.IncomingCalls(r.ctx, &protocol.CallHierarchyIncomingCallsParams{Item: items[0]}) -- if err != nil { -- t.Error(err) +- var opts map[string]interface{} +- switch v := params.InitializationOptions.(type) { +- case nil: +- opts = make(map[string]interface{}) +- case map[string]interface{}: +- opts = v +- default: +- return nil, fmt.Errorf("unexpected type for InitializationOptions: %T", v) - } -- var incomingCallItems []protocol.CallHierarchyItem -- for _, item := range incomingCalls { -- incomingCallItems = append(incomingCallItems, item.From) +- envOpt, ok := opts["env"] +- if !ok { +- envOpt = make(map[string]interface{}) - } -- msg := tests.DiffCallHierarchyItems(incomingCallItems, expectedCalls.IncomingCalls) -- if msg != "" { -- t.Error(fmt.Sprintf("incoming calls: %s", msg)) +- env, ok := envOpt.(map[string]interface{}) +- if !ok { +- return nil, fmt.Errorf(`env option is %T, expected a map`, envOpt) - } -- -- outgoingCalls, err := r.server.OutgoingCalls(r.ctx, &protocol.CallHierarchyOutgoingCallsParams{Item: items[0]}) +- goenv, err := getGoEnv(ctx, env) - if err != nil { -- t.Error(err) +- return nil, err - } -- var outgoingCallItems []protocol.CallHierarchyItem -- for _, item := range outgoingCalls { -- outgoingCallItems = append(outgoingCallItems, item.To) +- // We don't want to propagate GOWORK unless explicitly set since that could mess with +- // path inference during cmd/go invocations, see golang/go#51825. +- _, goworkSet := os.LookupEnv("GOWORK") +- for govar, value := range goenv { +- if govar == "GOWORK" && !goworkSet { +- continue +- } +- env[govar] = value - } -- msg = tests.DiffCallHierarchyItems(outgoingCallItems, expectedCalls.OutgoingCalls) -- if msg != "" { -- t.Error(fmt.Sprintf("outgoing calls: %s", msg)) +- opts["env"] = env +- params.InitializationOptions = opts +- call, ok := r.(*jsonrpc2.Call) +- if !ok { +- return nil, fmt.Errorf("%T is not a *jsonrpc2.Call", r) - } +- return jsonrpc2.NewCall(call.ID(), "initialize", params) -} - --func (r *runner) CodeLens(t *testing.T, uri span.URI, want []protocol.CodeLens) { -- if !strings.HasSuffix(uri.Filename(), "go.mod") { -- return -- } -- got, err := r.server.codeLens(r.ctx, &protocol.CodeLensParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.DocumentURI(uri), -- }, -- }) -- if err != nil { -- t.Fatal(err) +-func (f *Forwarder) replyWithDebugAddress(outerCtx context.Context, r jsonrpc2.Replier, args command.DebuggingArgs) jsonrpc2.Replier { +- di := debug.GetInstance(outerCtx) +- if di == nil { +- event.Log(outerCtx, "no debug instance to start") +- return r - } -- if diff := tests.DiffCodeLens(uri, want, got); diff != "" { -- t.Errorf("%s: %s", uri, diff) +- return func(ctx context.Context, result interface{}, outerErr error) error { +- if outerErr != nil { +- return r(ctx, result, outerErr) +- } +- // Enrich the result with our own debugging information. Since we're an +- // intermediary, the jsonrpc2 package has deserialized the result into +- // maps, by default. Re-do the unmarshalling. +- raw, err := json.Marshal(result) +- if err != nil { +- event.Error(outerCtx, "marshaling intermediate command result", err) +- return r(ctx, result, err) +- } +- var modified command.DebuggingResult +- if err := json.Unmarshal(raw, &modified); err != nil { +- event.Error(outerCtx, "unmarshaling intermediate command result", err) +- return r(ctx, result, err) +- } +- addr := args.Addr +- if addr == "" { +- addr = "localhost:0" +- } +- addr, err = di.Serve(outerCtx, addr) +- if err != nil { +- event.Error(outerCtx, "starting debug server", err) +- return r(ctx, result, outerErr) +- } +- urls := []string{"http://" + addr} +- modified.URLs = append(urls, modified.URLs...) +- go f.handshake(ctx) +- return r(ctx, modified, nil) - } -} - --func (r *runner) Diagnostics(t *testing.T, uri span.URI, want []*source.Diagnostic) { -- // Get the diagnostics for this view if we have not done it before. -- v := r.server.session.View(r.data.Config.Dir) -- r.collectDiagnostics(v) -- tests.CompareDiagnostics(t, uri, want, r.diagnostics[uri]) +-// A handshakeRequest identifies a client to the LSP server. +-type handshakeRequest struct { +- // ServerID is the ID of the server on the client. This should usually be 0. +- ServerID string `json:"serverID"` +- // Logfile is the location of the clients log file. +- Logfile string `json:"logfile"` +- // DebugAddr is the client debug address. +- DebugAddr string `json:"debugAddr"` +- // GoplsPath is the path to the Gopls binary running the current client +- // process. +- GoplsPath string `json:"goplsPath"` -} - --func (r *runner) FoldingRanges(t *testing.T, spn span.Span) { -- uri := spn.URI() -- view, err := r.server.session.ViewOf(uri) -- if err != nil { -- t.Fatal(err) -- } -- original := view.Options() -- modified := original -- defer r.server.session.SetViewOptions(r.ctx, view, original) +-// A handshakeResponse is returned by the LSP server to tell the LSP client +-// information about its session. +-type handshakeResponse struct { +- // SessionID is the server session associated with the client. +- SessionID string `json:"sessionID"` +- // Logfile is the location of the server logs. +- Logfile string `json:"logfile"` +- // DebugAddr is the server debug address. +- DebugAddr string `json:"debugAddr"` +- // GoplsPath is the path to the Gopls binary running the current server +- // process. +- GoplsPath string `json:"goplsPath"` +-} - -- for _, test := range []struct { -- lineFoldingOnly bool -- prefix string -- }{ -- {false, "foldingRange"}, -- {true, "foldingRange-lineFolding"}, -- } { -- modified.LineFoldingOnly = test.lineFoldingOnly -- view, err = r.server.session.SetViewOptions(r.ctx, view, modified) -- if err != nil { -- t.Error(err) -- continue -- } -- ranges, err := r.server.FoldingRange(r.ctx, &protocol.FoldingRangeParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- }) -- if err != nil { -- t.Error(err) -- continue -- } -- r.foldingRanges(t, test.prefix, uri, ranges) -- } +-// ClientSession identifies a current client LSP session on the server. Note +-// that it looks similar to handshakeResposne, but in fact 'Logfile' and +-// 'DebugAddr' now refer to the client. +-type ClientSession struct { +- SessionID string `json:"sessionID"` +- Logfile string `json:"logfile"` +- DebugAddr string `json:"debugAddr"` -} - --func (r *runner) foldingRanges(t *testing.T, prefix string, uri span.URI, ranges []protocol.FoldingRange) { -- m, err := r.data.Mapper(uri) -- if err != nil { -- t.Fatal(err) -- } -- // Fold all ranges. -- nonOverlapping := nonOverlappingRanges(ranges) -- for i, rngs := range nonOverlapping { -- got, err := foldRanges(m, string(m.Content), rngs) -- if err != nil { -- t.Error(err) -- continue -- } -- tag := fmt.Sprintf("%s-%d", prefix, i) -- want := string(r.data.Golden(t, tag, uri.Filename(), func() ([]byte, error) { -- return []byte(got), nil -- })) +-// ServerState holds information about the gopls daemon process, including its +-// debug information and debug information of all of its current connected +-// clients. +-type ServerState struct { +- Logfile string `json:"logfile"` +- DebugAddr string `json:"debugAddr"` +- GoplsPath string `json:"goplsPath"` +- CurrentClientID string `json:"currentClientID"` +- Clients []ClientSession `json:"clients"` +-} - -- if want != got { -- t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got) -- } -- } +-const ( +- handshakeMethod = "gopls/handshake" +- sessionsMethod = "gopls/sessions" +-) - -- // Filter by kind. -- kinds := []protocol.FoldingRangeKind{protocol.Imports, protocol.Comment} -- for _, kind := range kinds { -- var kindOnly []protocol.FoldingRange -- for _, fRng := range ranges { -- if fRng.Kind == string(kind) { -- kindOnly = append(kindOnly, fRng) +-func handshaker(session *cache.Session, goplsPath string, logHandshakes bool, handler jsonrpc2.Handler) jsonrpc2.Handler { +- return func(ctx context.Context, reply jsonrpc2.Replier, r jsonrpc2.Request) error { +- switch r.Method() { +- case handshakeMethod: +- // We log.Printf in this handler, rather than event.Log when we want logs +- // to go to the daemon log rather than being reflected back to the +- // client. +- var req handshakeRequest +- if err := json.Unmarshal(r.Params(), &req); err != nil { +- if logHandshakes { +- log.Printf("Error processing handshake for session %s: %v", session.ID(), err) +- } +- sendError(ctx, reply, err) +- return nil - } -- } -- -- nonOverlapping := nonOverlappingRanges(kindOnly) -- for i, rngs := range nonOverlapping { -- got, err := foldRanges(m, string(m.Content), rngs) -- if err != nil { -- t.Error(err) -- continue +- if logHandshakes { +- log.Printf("Session %s: got handshake. Logfile: %q, Debug addr: %q", session.ID(), req.Logfile, req.DebugAddr) +- } +- event.Log(ctx, "Handshake session update", +- cache.KeyUpdateSession.Of(session), +- tag.DebugAddress.Of(req.DebugAddr), +- tag.Logfile.Of(req.Logfile), +- tag.ServerID.Of(req.ServerID), +- tag.GoplsPath.Of(req.GoplsPath), +- ) +- resp := handshakeResponse{ +- SessionID: session.ID(), +- GoplsPath: goplsPath, +- } +- if di := debug.GetInstance(ctx); di != nil { +- resp.Logfile = di.Logfile +- resp.DebugAddr = di.ListenedDebugAddress() - } -- tag := fmt.Sprintf("%s-%s-%d", prefix, kind, i) -- want := string(r.data.Golden(t, tag, uri.Filename(), func() ([]byte, error) { -- return []byte(got), nil -- })) +- return reply(ctx, resp, nil) - -- if want != got { -- t.Errorf("%s: foldingRanges failed for %s, expected:\n%v\ngot:\n%v", tag, uri.Filename(), want, got) +- case sessionsMethod: +- resp := ServerState{ +- GoplsPath: goplsPath, +- CurrentClientID: session.ID(), +- } +- if di := debug.GetInstance(ctx); di != nil { +- resp.Logfile = di.Logfile +- resp.DebugAddr = di.ListenedDebugAddress() +- for _, c := range di.State.Clients() { +- resp.Clients = append(resp.Clients, ClientSession{ +- SessionID: c.Session.ID(), +- Logfile: c.Logfile, +- DebugAddr: c.DebugAddress, +- }) +- } - } +- return reply(ctx, resp, nil) - } +- return handler(ctx, reply, r) +- } +-} - +-func sendError(ctx context.Context, reply jsonrpc2.Replier, err error) { +- err = fmt.Errorf("%v: %w", err, jsonrpc2.ErrParse) +- if err := reply(ctx, nil, err); err != nil { +- event.Error(ctx, "", err) - } -} - --func nonOverlappingRanges(ranges []protocol.FoldingRange) (res [][]protocol.FoldingRange) { -- for _, fRng := range ranges { -- setNum := len(res) -- for i := 0; i < len(res); i++ { -- canInsert := true -- for _, rng := range res[i] { -- if conflict(rng, fRng) { -- canInsert = false -- break -- } -- } -- if canInsert { -- setNum = i -- break -- } -- } -- if setNum == len(res) { -- res = append(res, []protocol.FoldingRange{}) -- } -- res[setNum] = append(res[setNum], fRng) +-// ParseAddr parses the address of a gopls remote. +-// TODO(rFindley): further document this syntax, and allow URI-style remote +-// addresses such as "auto://...". +-func ParseAddr(listen string) (network string, address string) { +- // Allow passing just -remote=auto, as a shorthand for using automatic remote +- // resolution. +- if listen == AutoNetwork { +- return AutoNetwork, "" - } -- return res +- if parts := strings.SplitN(listen, ";", 2); len(parts) == 2 { +- return parts[0], parts[1] +- } +- return "tcp", listen +-} +diff -urN a/gopls/internal/lsp/lsprpc/lsprpc_test.go b/gopls/internal/lsp/lsprpc/lsprpc_test.go +--- a/gopls/internal/lsp/lsprpc/lsprpc_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/lsprpc_test.go 1970-01-01 08:00:00 +@@ -1,376 +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 lsprpc +- +-import ( +- "context" +- "encoding/json" +- "errors" +- "regexp" +- "strings" +- "testing" +- "time" +- +- "golang.org/x/tools/gopls/internal/lsp/cache" +- "golang.org/x/tools/gopls/internal/lsp/debug" +- "golang.org/x/tools/gopls/internal/lsp/fake" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/jsonrpc2" +- "golang.org/x/tools/internal/jsonrpc2/servertest" +- "golang.org/x/tools/internal/testenv" +-) +- +-type FakeClient struct { +- protocol.Client +- +- Logs chan string +-} +- +-func (c FakeClient) LogMessage(ctx context.Context, params *protocol.LogMessageParams) error { +- c.Logs <- params.Message +- return nil +-} +- +-// fakeServer is intended to be embedded in the test fakes below, to trivially +-// implement Shutdown. +-type fakeServer struct { +- protocol.Server +-} +- +-func (fakeServer) Shutdown(ctx context.Context) error { +- return nil -} - --func conflict(a, b protocol.FoldingRange) bool { -- // a start position is <= b start positions -- return (a.StartLine < b.StartLine || (a.StartLine == b.StartLine && a.StartCharacter <= b.StartCharacter)) && -- (a.EndLine > b.StartLine || (a.EndLine == b.StartLine && a.EndCharacter > b.StartCharacter)) +-type PingServer struct{ fakeServer } +- +-func (s PingServer) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error { +- event.Log(ctx, "ping") +- return nil -} - --func foldRanges(m *protocol.Mapper, contents string, ranges []protocol.FoldingRange) (string, error) { -- foldedText := "<>" -- res := contents -- // Apply the edits from the end of the file forward -- // to preserve the offsets -- // TODO(adonovan): factor to use diff.ApplyEdits, which validates the input. -- for i := len(ranges) - 1; i >= 0; i-- { -- r := ranges[i] -- start, err := m.PositionPoint(protocol.Position{Line: r.StartLine, Character: r.StartCharacter}) +-func TestClientLogging(t *testing.T) { +- ctx, cancel := context.WithCancel(context.Background()) +- defer cancel() +- +- server := PingServer{} +- client := FakeClient{Logs: make(chan string, 10)} +- +- ctx = debug.WithInstance(ctx, "", "") +- ss := NewStreamServer(cache.New(nil), false, nil) +- ss.serverForTest = server +- ts := servertest.NewPipeServer(ss, nil) +- defer checkClose(t, ts.Close) +- cc := ts.Connect(ctx) +- cc.Go(ctx, protocol.ClientHandler(client, jsonrpc2.MethodNotFound)) +- +- if err := protocol.ServerDispatcher(cc).DidOpen(ctx, &protocol.DidOpenTextDocumentParams{}); err != nil { +- t.Errorf("DidOpen: %v", err) +- } +- +- select { +- case got := <-client.Logs: +- want := "ping" +- matched, err := regexp.MatchString(want, got) - if err != nil { -- return "", err +- t.Fatal(err) - } -- end, err := m.PositionPoint(protocol.Position{Line: r.EndLine, Character: r.EndCharacter}) -- if err != nil { -- return "", err +- if !matched { +- t.Errorf("got log %q, want a log containing %q", got, want) - } -- res = res[:start.Offset()] + foldedText + res[end.Offset():] +- case <-time.After(1 * time.Second): +- t.Error("timeout waiting for client log") - } -- return res, nil -} - --func (r *runner) Format(t *testing.T, spn span.Span) { -- uri := spn.URI() -- filename := uri.Filename() -- gofmted := r.data.Golden(t, "gofmt", filename, func() ([]byte, error) { -- cmd := exec.Command("gofmt", filename) -- out, _ := cmd.Output() // ignore error, sometimes we have intentionally ungofmt-able files -- return out, nil -- }) +-// WaitableServer instruments LSP request so that we can control their timing. +-// The requests chosen are arbitrary: we simply needed one that blocks, and +-// another that doesn't. +-type WaitableServer struct { +- fakeServer - -- edits, err := r.server.Formatting(r.ctx, &protocol.DocumentFormattingParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- }) -- if err != nil { -- if len(gofmted) > 0 { -- t.Error(err) -- } -- return -- } -- m, err := r.data.Mapper(uri) -- if err != nil { -- t.Fatal(err) -- } -- got, _, err := source.ApplyProtocolEdits(m, edits) -- if err != nil { -- t.Error(err) -- } -- if diff := compare.Bytes(gofmted, got); diff != "" { -- t.Errorf("format failed for %s (-want +got):\n%s", filename, diff) -- } +- Started chan struct{} +- Completed chan error -} - --func (r *runner) SemanticTokens(t *testing.T, spn span.Span) { -- uri := spn.URI() -- filename := uri.Filename() -- // this is called solely for coverage in semantic.go -- _, err := r.server.semanticTokensFull(r.ctx, &protocol.SemanticTokensParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- }) -- if err != nil { -- t.Errorf("%v for %s", err, filename) -- } -- _, err = r.server.semanticTokensRange(r.ctx, &protocol.SemanticTokensRangeParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- // any legal range. Just to exercise the call. -- Range: protocol.Range{ -- Start: protocol.Position{ -- Line: 0, -- Character: 0, -- }, -- End: protocol.Position{ -- Line: 2, -- Character: 0, -- }, -- }, -- }) -- if err != nil { -- t.Errorf("%v for Range %s", err, filename) +-func (s WaitableServer) Hover(ctx context.Context, _ *protocol.HoverParams) (_ *protocol.Hover, err error) { +- s.Started <- struct{}{} +- defer func() { +- s.Completed <- err +- }() +- select { +- case <-ctx.Done(): +- return nil, errors.New("cancelled hover") +- case <-time.After(10 * time.Second): - } +- return &protocol.Hover{}, nil -} - --func (r *runner) Import(t *testing.T, spn span.Span) { -- // Invokes textDocument/codeAction and applies all the "goimports" edits. +-func (s WaitableServer) ResolveCompletionItem(_ context.Context, item *protocol.CompletionItem) (*protocol.CompletionItem, error) { +- return item, nil +-} - -- uri := spn.URI() -- filename := uri.Filename() -- actions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- }) -- if err != nil { -- t.Fatal(err) +-func checkClose(t *testing.T, closer func() error) { +- t.Helper() +- if err := closer(); err != nil { +- t.Errorf("closing: %v", err) - } -- m, err := r.data.Mapper(uri) +-} +- +-func setupForwarding(ctx context.Context, t *testing.T, s protocol.Server) (direct, forwarded servertest.Connector, cleanup func()) { +- t.Helper() +- serveCtx := debug.WithInstance(ctx, "", "") +- ss := NewStreamServer(cache.New(nil), false, nil) +- ss.serverForTest = s +- tsDirect := servertest.NewTCPServer(serveCtx, ss, nil) +- +- forwarder, err := NewForwarder("tcp;"+tsDirect.Addr, nil) - if err != nil { - t.Fatal(err) - } -- got := m.Content -- if len(actions) > 0 { -- res, err := applyTextDocumentEdits(r, actions[0].Edit.DocumentChanges) -- if err != nil { -- t.Fatal(err) -- } -- got = res[uri] -- } -- want := r.data.Golden(t, "goimports", filename, func() ([]byte, error) { -- return got, nil -- }) -- if diff := compare.Bytes(want, got); diff != "" { -- t.Errorf("import failed for %s:\n%s", filename, diff) +- tsForwarded := servertest.NewPipeServer(forwarder, nil) +- return tsDirect, tsForwarded, func() { +- checkClose(t, tsDirect.Close) +- checkClose(t, tsForwarded.Close) - } -} - --func (r *runner) SuggestedFix(t *testing.T, spn span.Span, actionKinds []tests.SuggestedFix, expectedActions int) { -- uri := spn.URI() -- view, err := r.server.session.ViewOf(uri) -- if err != nil { -- t.Fatal(err) -- } -- -- m, err := r.data.Mapper(uri) -- if err != nil { -- t.Fatal(err) -- } -- rng, err := m.SpanRange(spn) -- if err != nil { -- t.Fatal(err) -- } -- // Get the diagnostics for this view if we have not done it before. -- r.collectDiagnostics(view) -- var diagnostics []protocol.Diagnostic -- for _, d := range r.diagnostics[uri] { -- // Compare the start positions rather than the entire range because -- // some diagnostics have a range with the same start and end position (8:1-8:1). -- // The current marker functionality prevents us from having a range of 0 length. -- if protocol.ComparePosition(d.Range.Start, rng.Start) == 0 { -- diagnostics = append(diagnostics, toProtocolDiagnostics([]*source.Diagnostic{d})...) -- break -- } -- } -- var codeActionKinds []protocol.CodeActionKind -- for _, k := range actionKinds { -- codeActionKinds = append(codeActionKinds, protocol.CodeActionKind(k.ActionKind)) +-func TestRequestCancellation(t *testing.T) { +- ctx := context.Background() +- server := WaitableServer{ +- Started: make(chan struct{}), +- Completed: make(chan error), - } -- allActions, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- Range: rng, -- Context: protocol.CodeActionContext{ -- Only: codeActionKinds, -- Diagnostics: diagnostics, -- }, -- }) -- if err != nil { -- t.Fatalf("CodeAction %s failed: %v", spn, err) +- tsDirect, tsForwarded, cleanup := setupForwarding(ctx, t, server) +- defer cleanup() +- tests := []struct { +- serverType string +- ts servertest.Connector +- }{ +- {"direct", tsDirect}, +- {"forwarder", tsForwarded}, - } -- var actions []protocol.CodeAction -- for _, action := range allActions { -- for _, fix := range actionKinds { -- if strings.Contains(action.Title, fix.Title) { -- actions = append(actions, action) -- break -- } -- } - -- } -- if len(actions) != expectedActions { -- var summaries []string -- for _, a := range actions { -- summaries = append(summaries, fmt.Sprintf("%q (%s)", a.Title, a.Kind)) -- } -- t.Fatalf("CodeAction(...): got %d code actions (%v), want %d", len(actions), summaries, expectedActions) -- } -- action := actions[0] -- var match bool -- for _, k := range codeActionKinds { -- if action.Kind == k { -- match = true -- break -- } -- } -- if !match { -- t.Fatalf("unexpected kind for code action %s, got %v, want one of %v", action.Title, action.Kind, codeActionKinds) -- } -- var res map[span.URI][]byte -- if cmd := action.Command; cmd != nil { -- _, err := r.server.ExecuteCommand(r.ctx, &protocol.ExecuteCommandParams{ -- Command: action.Command.Command, -- Arguments: action.Command.Arguments, -- }) -- if err != nil { -- t.Fatalf("error converting command %q to edits: %v", action.Command.Command, err) -- } -- res = <-r.editRecv -- } else { -- res, err = applyTextDocumentEdits(r, action.Edit.DocumentChanges) -- if err != nil { -- t.Fatal(err) -- } -- } -- for u, got := range res { -- want := r.data.Golden(t, "suggestedfix_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) { -- return got, nil +- for _, test := range tests { +- t.Run(test.serverType, func(t *testing.T) { +- cc := test.ts.Connect(ctx) +- sd := protocol.ServerDispatcher(cc) +- cc.Go(ctx, +- protocol.Handlers( +- jsonrpc2.MethodNotFound)) +- +- ctx := context.Background() +- ctx, cancel := context.WithCancel(ctx) +- +- result := make(chan error) +- go func() { +- _, err := sd.Hover(ctx, &protocol.HoverParams{}) +- result <- err +- }() +- // Wait for the Hover request to start. +- <-server.Started +- cancel() +- if err := <-result; err == nil { +- t.Error("nil error for cancelled Hover(), want non-nil") +- } +- if err := <-server.Completed; err == nil || !strings.Contains(err.Error(), "cancelled hover") { +- t.Errorf("Hover(): unexpected server-side error %v", err) +- } - }) -- if diff := compare.Bytes(want, got); diff != "" { -- t.Errorf("suggested fixes failed for %s:\n%s", u.Filename(), diff) -- } - } -} - --func (r *runner) FunctionExtraction(t *testing.T, start span.Span, end span.Span) { -- uri := start.URI() -- m, err := r.data.Mapper(uri) -- if err != nil { -- t.Fatal(err) -- } -- spn := span.New(start.URI(), start.Start(), end.End()) -- rng, err := m.SpanRange(spn) -- if err != nil { -- t.Fatal(err) -- } -- actionsRaw, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- Range: rng, -- Context: protocol.CodeActionContext{ -- Only: []protocol.CodeActionKind{"refactor.extract"}, -- }, -- }) -- if err != nil { -- t.Fatal(err) -- } -- var actions []protocol.CodeAction -- for _, action := range actionsRaw { -- if action.Command.Title == "Extract function" { -- actions = append(actions, action) -- } -- } -- // Hack: We assume that we only get one code action per range. -- // TODO(rstambler): Support multiple code actions per test. -- if len(actions) == 0 || len(actions) > 1 { -- t.Fatalf("unexpected number of code actions, want 1, got %v", len(actions)) -- } -- _, err = r.server.ExecuteCommand(r.ctx, &protocol.ExecuteCommandParams{ -- Command: actions[0].Command.Command, -- Arguments: actions[0].Command.Arguments, -- }) +-const exampleProgram = ` +--- go.mod -- +-module mod +- +-go 1.12 +--- main.go -- +-package main +- +-import "fmt" +- +-func main() { +- fmt.Println("Hello World.") +-}` +- +-func TestDebugInfoLifecycle(t *testing.T) { +- sb, err := fake.NewSandbox(&fake.SandboxConfig{Files: fake.UnpackTxt(exampleProgram)}) - if err != nil { - t.Fatal(err) - } -- res := <-r.editRecv -- for u, got := range res { -- want := r.data.Golden(t, "functionextraction_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) { -- return got, nil -- }) -- if diff := compare.Bytes(want, got); diff != "" { -- t.Errorf("function extraction failed for %s:\n%s", u.Filename(), diff) +- defer func() { +- if err := sb.Close(); err != nil { +- // TODO(golang/go#38490): we can't currently make this an error because +- // it fails on Windows: the workspace directory is still locked by a +- // separate Go process. +- // Once we have a reliable way to wait for proper shutdown, make this an +- // error. +- t.Logf("closing workspace failed: %v", err) - } -- } --} +- }() - --func (r *runner) MethodExtraction(t *testing.T, start span.Span, end span.Span) { -- uri := start.URI() -- m, err := r.data.Mapper(uri) -- if err != nil { -- t.Fatal(err) -- } -- spn := span.New(start.URI(), start.Start(), end.End()) -- rng, err := m.SpanRange(spn) +- baseCtx, cancel := context.WithCancel(context.Background()) +- defer cancel() +- clientCtx := debug.WithInstance(baseCtx, "", "") +- serverCtx := debug.WithInstance(baseCtx, "", "") +- +- cache := cache.New(nil) +- ss := NewStreamServer(cache, false, nil) +- tsBackend := servertest.NewTCPServer(serverCtx, ss, nil) +- +- forwarder, err := NewForwarder("tcp;"+tsBackend.Addr, nil) - if err != nil { - t.Fatal(err) - } -- actionsRaw, err := r.server.CodeAction(r.ctx, &protocol.CodeActionParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- Range: rng, -- Context: protocol.CodeActionContext{ -- Only: []protocol.CodeActionKind{"refactor.extract"}, -- }, -- }) +- tsForwarder := servertest.NewPipeServer(forwarder, nil) +- +- const skipApplyEdits = false +- ed1, err := fake.NewEditor(sb, fake.EditorConfig{}).Connect(clientCtx, tsForwarder, fake.ClientHooks{}, skipApplyEdits) - if err != nil { - t.Fatal(err) - } -- var actions []protocol.CodeAction -- for _, action := range actionsRaw { -- if action.Command.Title == "Extract method" { -- actions = append(actions, action) -- } -- } -- // Hack: We assume that we only get one matching code action per range. -- // TODO(rstambler): Support multiple code actions per test. -- if len(actions) == 0 || len(actions) > 1 { -- t.Fatalf("unexpected number of code actions, want 1, got %v", len(actions)) -- } -- _, err = r.server.ExecuteCommand(r.ctx, &protocol.ExecuteCommandParams{ -- Command: actions[0].Command.Command, -- Arguments: actions[0].Command.Arguments, -- }) +- defer ed1.Close(clientCtx) +- ed2, err := fake.NewEditor(sb, fake.EditorConfig{}).Connect(baseCtx, tsBackend, fake.ClientHooks{}, skipApplyEdits) - if err != nil { - t.Fatal(err) - } -- res := <-r.editRecv -- for u, got := range res { -- want := r.data.Golden(t, "methodextraction_"+tests.SpanName(spn), u.Filename(), func() ([]byte, error) { -- return got, nil -- }) -- if diff := compare.Bytes(want, got); diff != "" { -- t.Errorf("method extraction failed for %s:\n%s", u.Filename(), diff) -- } -- } --} +- defer ed2.Close(baseCtx) - --// TODO(rfindley): This handler needs more work. The output is still a bit hard --// to read (range diffs do not format nicely), and it is too entangled with hover. --func (r *runner) Definition(t *testing.T, _ span.Span, d tests.Definition) { -- sm, err := r.data.Mapper(d.Src.URI()) -- if err != nil { -- t.Fatal(err) -- } -- loc, err := sm.SpanLocation(d.Src) -- if err != nil { -- t.Fatalf("failed for %v: %v", d.Src, err) -- } -- tdpp := protocol.LocationTextDocumentPositionParams(loc) -- var got []protocol.Location -- var hover *protocol.Hover -- if d.IsType { -- params := &protocol.TypeDefinitionParams{ -- TextDocumentPositionParams: tdpp, -- } -- got, err = r.server.TypeDefinition(r.ctx, params) -- } else { -- params := &protocol.DefinitionParams{ -- TextDocumentPositionParams: tdpp, -- } -- got, err = r.server.Definition(r.ctx, params) -- if err != nil { -- t.Fatalf("failed for %v: %+v", d.Src, err) -- } -- v := &protocol.HoverParams{ -- TextDocumentPositionParams: tdpp, -- } -- hover, err = r.server.Hover(r.ctx, v) +- serverDebug := debug.GetInstance(serverCtx) +- if got, want := len(serverDebug.State.Clients()), 2; got != want { +- t.Errorf("len(server:Clients) = %d, want %d", got, want) - } -- if err != nil { -- t.Fatalf("failed for %v: %v", d.Src, err) +- if got, want := len(serverDebug.State.Sessions()), 2; got != want { +- t.Errorf("len(server:Sessions) = %d, want %d", got, want) - } -- dm, err := r.data.Mapper(d.Def.URI()) -- if err != nil { -- t.Fatal(err) +- clientDebug := debug.GetInstance(clientCtx) +- if got, want := len(clientDebug.State.Servers()), 1; got != want { +- t.Errorf("len(client:Servers) = %d, want %d", got, want) - } -- def, err := dm.SpanLocation(d.Def) -- if err != nil { +- // Close one of the connections to verify that the client and session were +- // dropped. +- if err := ed1.Close(clientCtx); err != nil { - t.Fatal(err) - } -- if !d.OnlyHover { -- want := []protocol.Location{def} -- if diff := cmp.Diff(want, got); diff != "" { -- t.Fatalf("Definition(%s) mismatch (-want +got):\n%s", d.Src, diff) -- } -- } -- didSomething := false -- if hover != nil { -- didSomething = true -- tag := fmt.Sprintf("%s-hoverdef", d.Name) -- want := string(r.data.Golden(t, tag, d.Src.URI().Filename(), func() ([]byte, error) { -- return []byte(hover.Contents.Value), nil -- })) -- got := hover.Contents.Value -- if diff := tests.DiffMarkdown(want, got); diff != "" { -- t.Errorf("%s: markdown mismatch:\n%s", d.Src, diff) +- /*TODO: at this point we have verified the editor is closed +- However there is no way currently to wait for all associated go routines to +- go away, and we need to wait for those to trigger the client drop +- for now we just give it a little bit of time, but we need to fix this +- in a principled way +- */ +- start := time.Now() +- delay := time.Millisecond +- const maxWait = time.Second +- for len(serverDebug.State.Clients()) > 1 { +- if time.Since(start) > maxWait { +- break - } +- time.Sleep(delay) +- delay *= 2 - } -- if !d.OnlyHover { -- didSomething = true -- locURI := got[0].URI.SpanURI() -- lm, err := r.data.Mapper(locURI) -- if err != nil { -- t.Fatal(err) -- } -- if def, err := lm.LocationSpan(got[0]); err != nil { -- t.Fatalf("failed for %v: %v", got[0], err) -- } else if def != d.Def { -- t.Errorf("for %v got %v want %v", d.Src, def, d.Def) -- } +- if got, want := len(serverDebug.State.Clients()), 1; got != want { +- t.Errorf("len(server:Clients) = %d, want %d", got, want) - } -- if !didSomething { -- t.Errorf("no tests ran for %s", d.Src.URI()) +- if got, want := len(serverDebug.State.Sessions()), 1; got != want { +- t.Errorf("len(server:Sessions()) = %d, want %d", got, want) - } -} - --func (r *runner) Implementation(t *testing.T, spn span.Span, wantSpans []span.Span) { -- sm, err := r.data.Mapper(spn.URI()) -- if err != nil { -- t.Fatal(err) -- } -- loc, err := sm.SpanLocation(spn) -- if err != nil { -- t.Fatal(err) -- } -- gotImpls, err := r.server.Implementation(r.ctx, &protocol.ImplementationParams{ -- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), -- }) -- if err != nil { -- t.Fatalf("Server.Implementation(%s): %v", spn, err) -- } -- gotLocs, err := tests.LocationsToSpans(r.data, gotImpls) -- if err != nil { -- t.Fatal(err) -- } -- sanitize := func(s string) string { -- return strings.ReplaceAll(s, r.data.Config.Dir, "gopls/internal/lsp/testdata") -- } -- want := sanitize(tests.SortAndFormatSpans(wantSpans)) -- got := sanitize(tests.SortAndFormatSpans(gotLocs)) -- if got != want { -- t.Errorf("implementations(%s):\n%s", sanitize(fmt.Sprint(spn)), diff.Unified("want", "got", want, got)) -- } +-type initServer struct { +- fakeServer +- +- params *protocol.ParamInitialize -} - --func (r *runner) Highlight(t *testing.T, src span.Span, spans []span.Span) { -- m, err := r.data.Mapper(src.URI()) -- if err != nil { -- t.Fatal(err) +-func (s *initServer) Initialize(ctx context.Context, params *protocol.ParamInitialize) (*protocol.InitializeResult, error) { +- s.params = params +- return &protocol.InitializeResult{}, nil +-} +- +-func TestEnvForwarding(t *testing.T) { +- testenv.NeedsTool(t, "go") +- +- ctx := context.Background() +- +- server := &initServer{} +- _, tsForwarded, cleanup := setupForwarding(ctx, t, server) +- defer cleanup() +- +- conn := tsForwarded.Connect(ctx) +- conn.Go(ctx, jsonrpc2.MethodNotFound) +- dispatch := protocol.ServerDispatcher(conn) +- initParams := &protocol.ParamInitialize{} +- initParams.InitializationOptions = map[string]interface{}{ +- "env": map[string]interface{}{ +- "GONOPROXY": "example.com", +- }, - } -- loc, err := m.SpanLocation(src) +- _, err := dispatch.Initialize(ctx, initParams) - if err != nil { - t.Fatal(err) - } -- params := &protocol.DocumentHighlightParams{ -- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), -- } -- highlights, err := r.server.DocumentHighlight(r.ctx, params) -- if err != nil { -- t.Fatalf("DocumentHighlight(%v) failed: %v", params, err) -- } -- var got []protocol.Range -- for _, h := range highlights { -- got = append(got, h.Range) +- if server.params == nil { +- t.Fatalf("initialize params are unset") - } +- env := server.params.InitializationOptions.(map[string]interface{})["env"].(map[string]interface{}) - -- var want []protocol.Range -- for _, s := range spans { -- rng, err := m.SpanRange(s) -- if err != nil { -- t.Fatalf("Mapper.SpanRange(%v) failed: %v", s, err) -- } -- want = append(want, rng) +- // Check for an arbitrary Go variable. It should be set. +- if _, ok := env["GOPRIVATE"]; !ok { +- t.Errorf("Go environment variable GOPRIVATE unset in initialization options") - } -- -- sortRanges := func(s []protocol.Range) { -- sort.Slice(s, func(i, j int) bool { -- return protocol.CompareRange(s[i], s[j]) < 0 -- }) +- // Check that the variable present in our user config was not overwritten. +- if v := env["GONOPROXY"]; v != "example.com" { +- t.Errorf("GONOPROXY environment variable was overwritten") - } +-} - -- sortRanges(got) -- sortRanges(want) +-func TestListenParsing(t *testing.T) { +- tests := []struct { +- input, wantNetwork, wantAddr string +- }{ +- {"127.0.0.1:0", "tcp", "127.0.0.1:0"}, +- {"unix;/tmp/sock", "unix", "/tmp/sock"}, +- {"auto", "auto", ""}, +- {"auto;foo", "auto", "foo"}, +- } - -- if diff := cmp.Diff(want, got); diff != "" { -- t.Errorf("DocumentHighlight(%v) mismatch (-want +got):\n%s", src, diff) +- for _, test := range tests { +- gotNetwork, gotAddr := ParseAddr(test.input) +- if gotNetwork != test.wantNetwork { +- t.Errorf("network = %q, want %q", gotNetwork, test.wantNetwork) +- } +- if gotAddr != test.wantAddr { +- t.Errorf("addr = %q, want %q", gotAddr, test.wantAddr) +- } - } -} - --func (r *runner) References(t *testing.T, src span.Span, itemList []span.Span) { -- // This test is substantially the same as (*runner).References in source/source_test.go. -- // TODO(adonovan): Factor (and remove fluff). Where should the common code live? -- -- sm, err := r.data.Mapper(src.URI()) -- if err != nil { -- t.Fatal(err) +-// For #59479, verify that empty slices are serialized as []. +-func TestEmptySlices(t *testing.T) { +- // The LSP would prefer that empty slices be sent as [] rather than null. +- const bad = `{"a":null}` +- const good = `{"a":[]}` +- var x struct { +- A []string `json:"a"` - } -- loc, err := sm.SpanLocation(src) -- if err != nil { -- t.Fatalf("failed for %v: %v", src, err) +- buf, _ := json.Marshal(x) +- if string(buf) != bad { +- // uninitialized is ezpected to give null +- t.Errorf("unexpectedly got %s, want %s", buf, bad) - } -- for _, includeDeclaration := range []bool{true, false} { -- t.Run(fmt.Sprintf("refs-declaration-%v", includeDeclaration), func(t *testing.T) { -- want := make(map[protocol.Location]bool) -- for i, pos := range itemList { -- // We don't want the first result if we aren't including the declaration. -- // TODO(adonovan): don't assume a single declaration: -- // there may be >1 if corresponding methods are considered. -- if i == 0 && !includeDeclaration { -- continue -- } -- m, err := r.data.Mapper(pos.URI()) -- if err != nil { -- t.Fatal(err) -- } -- loc, err := m.SpanLocation(pos) -- if err != nil { -- t.Fatalf("failed for %v: %v", src, err) -- } -- want[loc] = true -- } -- params := &protocol.ReferenceParams{ -- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), -- Context: protocol.ReferenceContext{ -- IncludeDeclaration: includeDeclaration, -- }, -- } -- got, err := r.server.References(r.ctx, params) -- if err != nil { -- t.Fatalf("failed for %v: %v", src, err) -- } -- -- sanitize := func(s string) string { -- // In practice, CONFIGDIR means "gopls/internal/lsp/testdata". -- return strings.ReplaceAll(s, r.data.Config.Dir, "CONFIGDIR") -- } -- formatLocation := func(loc protocol.Location) string { -- return fmt.Sprintf("%s:%d.%d-%d.%d", -- sanitize(string(loc.URI)), -- loc.Range.Start.Line+1, -- loc.Range.Start.Character+1, -- loc.Range.End.Line+1, -- loc.Range.End.Character+1) -- } -- toSlice := func(set map[protocol.Location]bool) []protocol.Location { -- // TODO(adonovan): use generic maps.Keys(), someday. -- list := make([]protocol.Location, 0, len(set)) -- for key := range set { -- list = append(list, key) -- } -- return list -- } -- toString := func(locs []protocol.Location) string { -- // TODO(adonovan): use generic JoinValues(locs, formatLocation). -- strs := make([]string, len(locs)) -- for i, loc := range locs { -- strs[i] = formatLocation(loc) -- } -- sort.Strings(strs) -- return strings.Join(strs, "\n") -- } -- gotStr := toString(got) -- wantStr := toString(toSlice(want)) -- if gotStr != wantStr { -- t.Errorf("incorrect references (got %d, want %d) at %s:\n%s", -- len(got), len(want), -- formatLocation(loc), -- diff.Unified("want", "got", wantStr, gotStr)) -- } -- }) +- x.A = make([]string, 0) +- buf, _ = json.Marshal(x) +- if string(buf) != good { +- // expect [] +- t.Errorf("unexpectedly got %s, want %s", buf, good) +- } +- x.A = []string{} +- buf, _ = json.Marshal(x) +- if string(buf) != good { +- // expect [] +- t.Errorf("unexpectedly got %s, want %s", buf, good) - } -} +diff -urN a/gopls/internal/lsp/lsprpc/middleware.go b/gopls/internal/lsp/lsprpc/middleware.go +--- a/gopls/internal/lsp/lsprpc/middleware.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/middleware.go 1970-01-01 08:00:00 +@@ -1,142 +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. - --func (r *runner) InlayHints(t *testing.T, spn span.Span) { -- uri := spn.URI() -- filename := uri.Filename() +-package lsprpc - -- hints, err := r.server.InlayHint(r.ctx, &protocol.InlayHintParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- // TODO: add Range -- }) -- if err != nil { -- t.Fatal(err) -- } +-import ( +- "context" +- "encoding/json" +- "fmt" +- "sync" - -- // Map inlay hints to text edits. -- edits := make([]protocol.TextEdit, len(hints)) -- for i, hint := range hints { -- var paddingLeft, paddingRight string -- if hint.PaddingLeft { -- paddingLeft = " " -- } -- if hint.PaddingRight { -- paddingRight = " " -- } -- edits[i] = protocol.TextEdit{ -- Range: protocol.Range{Start: hint.Position, End: hint.Position}, -- NewText: fmt.Sprintf("<%s%s%s>", paddingLeft, hint.Label[0].Value, paddingRight), -- } -- } +- "golang.org/x/tools/internal/event" +- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" +-) - -- m, err := r.data.Mapper(uri) -- if err != nil { -- t.Fatal(err) -- } -- got, _, err := source.ApplyProtocolEdits(m, edits) -- if err != nil { -- t.Error(err) -- } +-// Metadata holds arbitrary data transferred between jsonrpc2 peers. +-type Metadata map[string]interface{} - -- withinlayHints := r.data.Golden(t, "inlayHint", filename, func() ([]byte, error) { -- return got, nil -- }) +-// PeerInfo holds information about a peering between jsonrpc2 servers. +-type PeerInfo struct { +- // RemoteID is the identity of the current server on its peer. +- RemoteID int64 - -- if !bytes.Equal(withinlayHints, got) { -- t.Errorf("inlay hints failed for %s, expected:\n%s\ngot:\n%s", filename, withinlayHints, got) -- } --} +- // LocalID is the identity of the peer on the server. +- LocalID int64 - --func (r *runner) Rename(t *testing.T, spn span.Span, newText string) { -- tag := fmt.Sprintf("%s-rename", newText) +- // IsClient reports whether the peer is a client. If false, the peer is a +- // server. +- IsClient bool - -- uri := spn.URI() -- filename := uri.Filename() -- sm, err := r.data.Mapper(uri) -- if err != nil { -- t.Fatal(err) -- } -- loc, err := sm.SpanLocation(spn) -- if err != nil { -- t.Fatalf("failed for %v: %v", spn, err) -- } +- // Metadata holds arbitrary information provided by the peer. +- Metadata Metadata +-} - -- wedit, err := r.server.Rename(r.ctx, &protocol.RenameParams{ -- TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI}, -- Position: loc.Range.Start, -- NewName: newText, -- }) -- if err != nil { -- renamed := string(r.data.Golden(t, tag, filename, func() ([]byte, error) { -- return []byte(err.Error()), nil -- })) -- if err.Error() != renamed { -- t.Errorf("%s: rename failed for %s, expected:\n%v\ngot:\n%v\n", spn, newText, renamed, err) -- } -- return -- } -- res, err := applyTextDocumentEdits(r, wedit.DocumentChanges) -- if err != nil { -- t.Fatal(err) -- } -- var orderedURIs []string -- for uri := range res { -- orderedURIs = append(orderedURIs, string(uri)) -- } -- sort.Strings(orderedURIs) +-// Handshaker handles both server and client handshaking over jsonrpc2. To +-// instrument server-side handshaking, use Handshaker.Middleware. To instrument +-// client-side handshaking, call Handshaker.ClientHandshake for any new +-// client-side connections. +-type Handshaker struct { +- // Metadata will be shared with peers via handshaking. +- Metadata Metadata - -- // Print the name and content of each modified file, -- // concatenated, and compare against the golden. -- var buf bytes.Buffer -- for i := 0; i < len(res); i++ { -- if i != 0 { -- buf.WriteByte('\n') -- } -- uri := span.URIFromURI(orderedURIs[i]) -- if len(res) > 1 { -- buf.WriteString(filepath.Base(uri.Filename())) -- buf.WriteString(":\n") -- } -- buf.Write(res[uri]) -- } -- got := buf.Bytes() -- want := r.data.Golden(t, tag, filename, func() ([]byte, error) { -- return got, nil -- }) -- if diff := compare.Bytes(want, got); diff != "" { -- t.Errorf("rename failed for %s:\n%s", newText, diff) -- } +- mu sync.Mutex +- prevID int64 +- peers map[int64]PeerInfo -} - --func (r *runner) PrepareRename(t *testing.T, src span.Span, want *source.PrepareItem) { -- m, err := r.data.Mapper(src.URI()) -- if err != nil { -- t.Fatal(err) -- } -- loc, err := m.SpanLocation(src) -- if err != nil { -- t.Fatalf("failed for %v: %v", src, err) -- } -- params := &protocol.PrepareRenameParams{ -- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), -- } -- got, err := r.server.PrepareRename(context.Background(), params) -- if err != nil { -- t.Errorf("prepare rename failed for %v: got error: %v", src, err) -- return +-// Peers returns the peer info this handshaker knows about by way of either the +-// server-side handshake middleware, or client-side handshakes. +-func (h *Handshaker) Peers() []PeerInfo { +- h.mu.Lock() +- defer h.mu.Unlock() +- +- var c []PeerInfo +- for _, v := range h.peers { +- c = append(c, v) - } +- return c +-} - -- // TODO(rfindley): can we consolidate on a single representation for -- // PrepareRename results, and use cmp.Diff here? +-// Middleware is a jsonrpc2 middleware function to augment connection binding +-// to handle the handshake method, and record disconnections. +-func (h *Handshaker) Middleware(inner jsonrpc2_v2.Binder) jsonrpc2_v2.Binder { +- return BinderFunc(func(ctx context.Context, conn *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { +- opts := inner.Bind(ctx, conn) - -- // PrepareRename may fail with no error if there was no object found at the -- // position. -- if got == nil { -- if want.Text != "" { // expected an ident. -- t.Errorf("prepare rename failed for %v: got nil", src) -- } -- return -- } -- if got.Range.Start == got.Range.End { -- // Special case for 0-length ranges. Marks can't specify a 0-length range, -- // so just compare the start. -- if got.Range.Start != want.Range.Start { -- t.Errorf("prepare rename failed: incorrect point, got %v want %v", got.Range.Start, want.Range.Start) -- } -- } else { -- if got.Range != want.Range { -- t.Errorf("prepare rename failed: incorrect range got %v want %v", got.Range, want.Range) +- localID := h.nextID() +- info := &PeerInfo{ +- RemoteID: localID, +- Metadata: h.Metadata, - } -- } -- if got.Placeholder != want.Text { -- t.Errorf("prepare rename failed: incorrect text got %v want %v", got.Placeholder, want.Text) -- } --} - --func applyTextDocumentEdits(r *runner, edits []protocol.DocumentChanges) (map[span.URI][]byte, error) { -- res := make(map[span.URI][]byte) -- for _, docEdits := range edits { -- if docEdits.TextDocumentEdit != nil { -- uri := docEdits.TextDocumentEdit.TextDocument.URI.SpanURI() -- var m *protocol.Mapper -- // If we have already edited this file, we use the edited version (rather than the -- // file in its original state) so that we preserve our initial changes. -- if content, ok := res[uri]; ok { -- m = protocol.NewMapper(uri, content) -- } else { -- var err error -- if m, err = r.data.Mapper(uri); err != nil { -- return nil, err +- // Wrap the delegated handler to accept the handshake. +- delegate := opts.Handler +- opts.Handler = jsonrpc2_v2.HandlerFunc(func(ctx context.Context, req *jsonrpc2_v2.Request) (interface{}, error) { +- if req.Method == handshakeMethod { +- var peerInfo PeerInfo +- if err := json.Unmarshal(req.Params, &peerInfo); err != nil { +- return nil, fmt.Errorf("%w: unmarshaling client info: %v", jsonrpc2_v2.ErrInvalidParams, err) - } +- peerInfo.LocalID = localID +- peerInfo.IsClient = true +- h.recordPeer(peerInfo) +- return info, nil - } -- patched, _, err := source.ApplyProtocolEdits(m, docEdits.TextDocumentEdit.Edits) -- if err != nil { -- return nil, err -- } -- res[uri] = patched -- } -- } -- return res, nil --} +- return delegate.Handle(ctx, req) +- }) - --func (r *runner) Symbols(t *testing.T, uri span.URI, expectedSymbols []protocol.DocumentSymbol) { -- params := &protocol.DocumentSymbolParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- } -- got, err := r.server.DocumentSymbol(r.ctx, params) -- if err != nil { -- t.Fatal(err) -- } +- // Record the dropped client. +- go h.cleanupAtDisconnect(conn, localID) - -- symbols := make([]protocol.DocumentSymbol, len(got)) -- for i, s := range got { -- s, ok := s.(protocol.DocumentSymbol) -- if !ok { -- t.Fatalf("%v: wanted []DocumentSymbols but got %v", uri, got) -- } -- symbols[i] = s -- } +- return opts +- }) +-} - -- // Sort by position to make it easier to find errors. -- sortSymbols := func(s []protocol.DocumentSymbol) { -- sort.Slice(s, func(i, j int) bool { -- return protocol.CompareRange(s[i].SelectionRange, s[j].SelectionRange) < 0 -- }) +-// ClientHandshake performs a client-side handshake with the server at the +-// other end of conn, recording the server's peer info and watching for conn's +-// disconnection. +-func (h *Handshaker) ClientHandshake(ctx context.Context, conn *jsonrpc2_v2.Connection) { +- localID := h.nextID() +- info := &PeerInfo{ +- RemoteID: localID, +- Metadata: h.Metadata, - } -- sortSymbols(expectedSymbols) -- sortSymbols(symbols) - -- // Ignore 'Range' here as it is difficult (impossible?) to express -- // multi-line ranges in the packagestest framework. -- ignoreRange := cmpopts.IgnoreFields(protocol.DocumentSymbol{}, "Range") -- if diff := cmp.Diff(expectedSymbols, symbols, ignoreRange); diff != "" { -- t.Errorf("mismatching symbols (-want +got)\n%s", diff) +- call := conn.Call(ctx, handshakeMethod, info) +- var serverInfo PeerInfo +- if err := call.Await(ctx, &serverInfo); err != nil { +- event.Error(ctx, "performing handshake", err) +- return - } --} +- serverInfo.LocalID = localID +- h.recordPeer(serverInfo) - --func (r *runner) WorkspaceSymbols(t *testing.T, uri span.URI, query string, typ tests.WorkspaceSymbolsTestType) { -- matcher := tests.WorkspaceSymbolsTestTypeToMatcher(typ) +- go h.cleanupAtDisconnect(conn, localID) +-} - -- original := r.server.session.Options() -- modified := original -- modified.SymbolMatcher = matcher -- r.server.session.SetOptions(modified) -- defer r.server.session.SetOptions(original) +-func (h *Handshaker) nextID() int64 { +- h.mu.Lock() +- defer h.mu.Unlock() - -- params := &protocol.WorkspaceSymbolParams{ -- Query: query, -- } -- gotSymbols, err := r.server.Symbol(r.ctx, params) -- if err != nil { -- t.Fatal(err) -- } -- got, err := tests.WorkspaceSymbolsString(r.ctx, r.data, uri, gotSymbols) -- if err != nil { -- t.Fatal(err) -- } -- got = filepath.ToSlash(tests.Normalize(got, r.normalizers)) -- want := string(r.data.Golden(t, fmt.Sprintf("workspace_symbol-%s-%s", strings.ToLower(string(matcher)), query), uri.Filename(), func() ([]byte, error) { -- return []byte(got), nil -- })) -- if diff := compare.Text(want, got); diff != "" { -- t.Error(diff) -- } +- h.prevID++ +- return h.prevID -} - --func (r *runner) SignatureHelp(t *testing.T, spn span.Span, want *protocol.SignatureHelp) { -- m, err := r.data.Mapper(spn.URI()) -- if err != nil { -- t.Fatal(err) -- } -- loc, err := m.SpanLocation(spn) -- if err != nil { -- t.Fatalf("failed for %v: %v", loc, err) -- } -- params := &protocol.SignatureHelpParams{ -- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(loc), -- } -- got, err := r.server.SignatureHelp(r.ctx, params) -- if err != nil { -- // Only fail if we got an error we did not expect. -- if want != nil { -- t.Fatal(err) -- } -- return -- } -- if want == nil { -- if got != nil { -- t.Errorf("expected no signature, got %v", got) -- } -- return -- } -- if got == nil { -- t.Fatalf("expected %v, got nil", want) -- } -- if diff := tests.DiffSignatures(spn, want, got); diff != "" { -- t.Error(diff) -- } --} +-func (h *Handshaker) cleanupAtDisconnect(conn *jsonrpc2_v2.Connection, peerID int64) { +- conn.Wait() - --func (r *runner) Link(t *testing.T, uri span.URI, wantLinks []tests.Link) { -- m, err := r.data.Mapper(uri) -- if err != nil { -- t.Fatal(err) -- } -- got, err := r.server.DocumentLink(r.ctx, &protocol.DocumentLinkParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- }) -- if err != nil { -- t.Fatal(err) -- } -- if diff := tests.DiffLinks(m, wantLinks, got); diff != "" { -- t.Error(diff) -- } +- h.mu.Lock() +- defer h.mu.Unlock() +- delete(h.peers, peerID) -} - --func (r *runner) AddImport(t *testing.T, uri span.URI, expectedImport string) { -- cmd, err := command.NewListKnownPackagesCommand("List Known Packages", command.URIArg{ -- URI: protocol.URIFromSpanURI(uri), -- }) -- if err != nil { -- t.Fatal(err) -- } -- resp, err := r.server.executeCommand(r.ctx, &protocol.ExecuteCommandParams{ -- Command: cmd.Command, -- Arguments: cmd.Arguments, -- }) -- if err != nil { -- t.Fatal(err) -- } -- res := resp.(command.ListKnownPackagesResult) -- var hasPkg bool -- for _, p := range res.Packages { -- if p == expectedImport { -- hasPkg = true -- break -- } -- } -- if !hasPkg { -- t.Fatalf("%s: got %v packages\nwant contains %q", command.ListKnownPackages, res.Packages, expectedImport) -- } -- cmd, err = command.NewAddImportCommand("Add Imports", command.AddImportArgs{ -- URI: protocol.URIFromSpanURI(uri), -- ImportPath: expectedImport, -- }) -- if err != nil { -- t.Fatal(err) -- } -- _, err = r.server.executeCommand(r.ctx, &protocol.ExecuteCommandParams{ -- Command: cmd.Command, -- Arguments: cmd.Arguments, -- }) -- if err != nil { -- t.Fatal(err) -- } -- got := (<-r.editRecv)[uri] -- want := r.data.Golden(t, "addimport", uri.Filename(), func() ([]byte, error) { -- return []byte(got), nil -- }) -- if want == nil { -- t.Fatalf("golden file %q not found", uri.Filename()) -- } -- if diff := compare.Bytes(want, got); diff != "" { -- t.Errorf("%s mismatch\n%s", command.AddImport, diff) +-func (h *Handshaker) recordPeer(info PeerInfo) { +- h.mu.Lock() +- defer h.mu.Unlock() +- if h.peers == nil { +- h.peers = make(map[int64]PeerInfo) - } +- h.peers[info.LocalID] = info -} +diff -urN a/gopls/internal/lsp/lsprpc/middleware_test.go b/gopls/internal/lsp/lsprpc/middleware_test.go +--- a/gopls/internal/lsp/lsprpc/middleware_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/lsprpc/middleware_test.go 1970-01-01 08:00:00 +@@ -1,93 +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. - --func (r *runner) SelectionRanges(t *testing.T, spn span.Span) { -- uri := spn.URI() -- sm, err := r.data.Mapper(uri) -- if err != nil { -- t.Fatal(err) -- } -- loc, err := sm.SpanLocation(spn) -- if err != nil { -- t.Error(err) -- } -- -- ranges, err := r.server.selectionRange(r.ctx, &protocol.SelectionRangeParams{ -- TextDocument: protocol.TextDocumentIdentifier{ -- URI: protocol.URIFromSpanURI(uri), -- }, -- Positions: []protocol.Position{loc.Range.Start}, -- }) -- if err != nil { -- t.Fatal(err) -- } -- -- sb := &strings.Builder{} -- for i, path := range ranges { -- fmt.Fprintf(sb, "Ranges %d: ", i) -- rng := path -- for { -- s, e, err := sm.RangeOffsets(rng.Range) -- if err != nil { -- t.Error(err) -- } +-package lsprpc_test - -- var snippet string -- if e-s < 30 { -- snippet = string(sm.Content[s:e]) -- } else { -- snippet = string(sm.Content[s:s+15]) + "..." + string(sm.Content[e-15:e]) -- } +-import ( +- "context" +- "errors" +- "fmt" +- "testing" +- "time" - -- fmt.Fprintf(sb, "\n\t%v %q", rng.Range, strings.ReplaceAll(snippet, "\n", "\\n")) +- . "golang.org/x/tools/gopls/internal/lsp/lsprpc" +- jsonrpc2_v2 "golang.org/x/tools/internal/jsonrpc2_v2" +-) - -- if rng.Parent == nil { -- break -- } -- rng = *rng.Parent -- } -- sb.WriteRune('\n') -- } -- got := sb.String() +-var noopBinder = BinderFunc(func(context.Context, *jsonrpc2_v2.Connection) jsonrpc2_v2.ConnectionOptions { +- return jsonrpc2_v2.ConnectionOptions{} +-}) - -- testName := "selectionrange_" + tests.SpanName(spn) -- want := r.data.Golden(t, testName, uri.Filename(), func() ([]byte, error) { -- return []byte(got), nil -- }) -- if want == nil { -- t.Fatalf("golden file %q not found", uri.Filename()) +-func TestHandshakeMiddleware(t *testing.T) { +- sh := &Handshaker{ +- Metadata: Metadata{ +- "answer": 42, +- }, - } -- if diff := compare.Text(got, string(want)); diff != "" { -- t.Errorf("%s mismatch\n%s", testName, diff) +- ctx := context.Background() +- env := new(TestEnv) +- defer env.Shutdown(t) +- l, _ := env.serve(ctx, t, sh.Middleware(noopBinder)) +- conn := env.dial(ctx, t, l.Dialer(), noopBinder, false) +- ch := &Handshaker{ +- Metadata: Metadata{ +- "question": 6 * 9, +- }, - } --} - --func (r *runner) collectDiagnostics(view *cache.View) { -- if r.diagnostics != nil { -- return +- check := func(connected bool) error { +- clients := sh.Peers() +- servers := ch.Peers() +- want := 0 +- if connected { +- want = 1 +- } +- if got := len(clients); got != want { +- return fmt.Errorf("got %d clients on the server, want %d", got, want) +- } +- if got := len(servers); got != want { +- return fmt.Errorf("got %d servers on the client, want %d", got, want) +- } +- if !connected { +- return nil +- } +- client := clients[0] +- server := servers[0] +- if _, ok := client.Metadata["question"]; !ok { +- return errors.New("no client metadata") +- } +- if _, ok := server.Metadata["answer"]; !ok { +- return errors.New("no server metadata") +- } +- if client.LocalID != server.RemoteID { +- return fmt.Errorf("client.LocalID == %d, server.PeerID == %d", client.LocalID, server.RemoteID) +- } +- if client.RemoteID != server.LocalID { +- return fmt.Errorf("client.PeerID == %d, server.LocalID == %d", client.RemoteID, server.LocalID) +- } +- return nil - } -- r.diagnostics = make(map[span.URI][]*source.Diagnostic) - -- snapshot, release, err := view.Snapshot() -- if err != nil { -- panic(err) +- if err := check(false); err != nil { +- t.Fatalf("before handshake: %v", err) - } -- defer release() -- -- // Always run diagnostics with analysis. -- r.server.diagnose(r.ctx, snapshot, true) -- for uri, reports := range r.server.diagnostics { -- for _, report := range reports.reports { -- for _, d := range report.diags { -- r.diagnostics[uri] = append(r.diagnostics[uri], d) -- } +- ch.ClientHandshake(ctx, conn) +- if err := check(true); err != nil { +- t.Fatalf("after handshake: %v", err) +- } +- conn.Close() +- // Wait for up to ~2s for connections to get cleaned up. +- delay := 25 * time.Millisecond +- for retries := 3; retries >= 0; retries-- { +- time.Sleep(delay) +- err := check(false) +- if err == nil { +- return +- } +- if retries == 0 { +- t.Fatalf("after closing connection: %v", err) - } +- delay *= 4 - } -} diff -urN a/gopls/internal/lsp/mod/code_lens.go b/gopls/internal/lsp/mod/code_lens.go --- a/gopls/internal/lsp/mod/code_lens.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/mod/code_lens.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/mod/code_lens.go 1970-01-01 08:00:00 @@ -1,191 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -44553,8 +49931,8 @@ diff -urN a/gopls/internal/lsp/mod/code_lens.go b/gopls/internal/lsp/mod/code_le -} diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagnostics.go --- a/gopls/internal/lsp/mod/diagnostics.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/mod/diagnostics.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,561 +0,0 @@ ++++ b/gopls/internal/lsp/mod/diagnostics.go 1970-01-01 08:00:00 +@@ -1,559 +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. @@ -44566,28 +49944,36 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn -import ( - "context" - "fmt" +- "runtime" - "sort" - "strings" +- "sync" - - "golang.org/x/mod/modfile" - "golang.org/x/mod/semver" -- "golang.org/x/tools/gopls/internal/govulncheck" +- "golang.org/x/sync/errgroup" - "golang.org/x/tools/gopls/internal/lsp/command" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/gopls/internal/vulncheck/govulncheck" - "golang.org/x/tools/internal/event" -- "golang.org/x/vuln/osv" -) - --// Diagnostics returns diagnostics for the modules in the workspace. --// --// It waits for completion of type-checking of all active packages. +-// Diagnostics returns diagnostics from parsing the modules in the workspace. -func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[span.URI][]*source.Diagnostic, error) { - ctx, done := event.Start(ctx, "mod.Diagnostics", source.SnapshotLabels(snapshot)...) - defer done() - -- return collectDiagnostics(ctx, snapshot, ModDiagnostics) +- return collectDiagnostics(ctx, snapshot, ModParseDiagnostics) +-} +- +-// Diagnostics returns diagnostics from running go mod tidy. +-func TidyDiagnostics(ctx context.Context, snapshot source.Snapshot) (map[span.URI][]*source.Diagnostic, error) { +- ctx, done := event.Start(ctx, "mod.Diagnostics", source.SnapshotLabels(snapshot)...) +- defer done() +- +- return collectDiagnostics(ctx, snapshot, ModTidyDiagnostics) -} - -// UpgradeDiagnostics returns upgrade diagnostics for the modules in the @@ -44609,32 +49995,41 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn -} - -func collectDiagnostics(ctx context.Context, snapshot source.Snapshot, diagFn func(context.Context, source.Snapshot, source.FileHandle) ([]*source.Diagnostic, error)) (map[span.URI][]*source.Diagnostic, error) { +- g, ctx := errgroup.WithContext(ctx) +- cpulimit := runtime.GOMAXPROCS(0) +- g.SetLimit(cpulimit) +- +- var mu sync.Mutex - reports := make(map[span.URI][]*source.Diagnostic) +- - for _, uri := range snapshot.ModFiles() { -- fh, err := snapshot.GetFile(ctx, uri) -- if err != nil { -- return nil, err -- } -- reports[fh.URI()] = []*source.Diagnostic{} -- diagnostics, err := diagFn(ctx, snapshot, fh) -- if err != nil { -- return nil, err -- } -- for _, d := range diagnostics { -- fh, err := snapshot.GetFile(ctx, d.URI) +- uri := uri +- g.Go(func() error { +- fh, err := snapshot.ReadFile(ctx, uri) - if err != nil { -- return nil, err +- return err - } -- reports[fh.URI()] = append(reports[fh.URI()], d) -- } +- diagnostics, err := diagFn(ctx, snapshot, fh) +- if err != nil { +- return err +- } +- for _, d := range diagnostics { +- mu.Lock() +- reports[d.URI] = append(reports[fh.URI()], d) +- mu.Unlock() +- } +- return nil +- }) +- } +- +- if err := g.Wait(); err != nil { +- return nil, err - } - return reports, nil -} - --// ModDiagnostics waits for completion of type-checking of all active --// packages, then returns diagnostics from diagnosing the packages in --// the workspace and from tidying the go.mod file. --func ModDiagnostics(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) (diagnostics []*source.Diagnostic, err error) { +-// ModParseDiagnostics reports diagnostics from parsing the mod file. +-func ModParseDiagnostics(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) (diagnostics []*source.Diagnostic, err error) { - pm, err := snapshot.ParseMod(ctx, fh) - if err != nil { - if pm == nil || len(pm.ParseErrors) == 0 { @@ -44642,28 +50037,14 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - } - return pm.ParseErrors, nil - } +- return nil, nil +-} - -- // Packages in the workspace can contribute diagnostics to go.mod files. -- // TODO(rfindley): Try to avoid type checking all packages in the workspace here, -- // for every go.mod file. If gc_details is enabled, it looks like this could lead to extra -- // go command invocations (as gc details is not memoized). -- active, err := snapshot.ActiveMetadata(ctx) -- if err != nil && !source.IsNonFatalGoModError(err) { -- event.Error(ctx, fmt.Sprintf("workspace packages: diagnosing %s", pm.URI), err) -- } -- if err == nil { -- // Note: the call to PackageDiagnostics below may be the first operation -- // after the initial metadata load, and therefore result in type-checking -- // or loading many packages. -- ids := make([]source.PackageID, len(active)) -- for i, meta := range active { -- ids[i] = meta.ID -- } -- diags, err := snapshot.PackageDiagnostics(ctx, ids...) -- if err != nil { -- return nil, err -- } -- diagnostics = append(diagnostics, diags[fh.URI()]...) +-// ModTidyDiagnostics reports diagnostics from running go mod tidy. +-func ModTidyDiagnostics(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) (diagnostics []*source.Diagnostic, err error) { +- pm, err := snapshot.ParseMod(ctx, fh) // memoized +- if err != nil { +- return nil, nil // errors reported by ModDiagnostics above - } - - tidied, err := snapshot.ModTidy(ctx, pm) @@ -44744,14 +50125,14 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - - diagSource := source.Govulncheck - vs := snapshot.View().Vulnerabilities(fh.URI())[fh.URI()] -- if vs == nil && snapshot.View().Options().Vulncheck == source.ModeVulncheckImports { +- if vs == nil && snapshot.Options().Vulncheck == source.ModeVulncheckImports { - vs, err = snapshot.ModVuln(ctx, fh.URI()) - if err != nil { - return nil, err - } - diagSource = source.Vulncheck - } -- if vs == nil || len(vs.Vulns) == 0 { +- if vs == nil || len(vs.Findings) == 0 { - return nil, nil - } - @@ -44760,20 +50141,17 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - // must not happen - return nil, err // TODO: bug report - } -- type modVuln struct { -- mod *govulncheck.Module -- vuln *govulncheck.Vuln -- } -- vulnsByModule := make(map[string][]modVuln) -- for _, vuln := range vs.Vulns { -- for _, mod := range vuln.Modules { -- vulnsByModule[mod.Path] = append(vulnsByModule[mod.Path], modVuln{mod, vuln}) +- vulnsByModule := make(map[string][]*govulncheck.Finding) +- +- for _, finding := range vs.Findings { +- if vuln, typ := foundVuln(finding); typ == vulnCalled || typ == vulnImported { +- vulnsByModule[vuln.Module] = append(vulnsByModule[vuln.Module], finding) - } - } -- - for _, req := range pm.File.Require { -- vulns := vulnsByModule[req.Mod.Path] -- if len(vulns) == 0 { +- mod := req.Mod.Path +- findings := vulnsByModule[mod] +- if len(findings) == 0 { - continue - } - // note: req.Syntax is the line corresponding to 'require', which means @@ -44791,10 +50169,8 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - // others to 'info' level diagnostics. - // Fixes will include only the upgrades for warning level diagnostics. - var warningFixes, infoFixes []source.SuggestedFix -- var warning, info []string -- var relatedInfo []protocol.DiagnosticRelatedInformation -- for _, mv := range vulns { -- mod, vuln := mv.mod, mv.vuln +- var warningSet, infoSet = map[string]bool{}, map[string]bool{} +- for _, finding := range findings { - // It is possible that the source code was changed since the last - // govulncheck run and information in the `vulns` info is stale. - // For example, imagine that a user is in the middle of updating @@ -44811,33 +50187,42 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - // version in the require statement is equal to or higher than the - // fixed version, skip generating a diagnostic about the vulnerability. - // Eventually, the user has to rerun govulncheck. -- if mod.FixedVersion != "" && semver.IsValid(req.Mod.Version) && semver.Compare(mod.FixedVersion, req.Mod.Version) <= 0 { +- if finding.FixedVersion != "" && semver.IsValid(req.Mod.Version) && semver.Compare(finding.FixedVersion, req.Mod.Version) <= 0 { - continue - } -- if !vuln.IsCalled() { -- info = append(info, vuln.OSV.ID) -- } else { -- warning = append(warning, vuln.OSV.ID) -- relatedInfo = append(relatedInfo, listRelatedInfo(ctx, snapshot, vuln)...) +- switch _, typ := foundVuln(finding); typ { +- case vulnImported: +- infoSet[finding.OSV] = true +- case vulnCalled: +- warningSet[finding.OSV] = true - } - // Upgrade to the exact version we offer the user, not the most recent. -- if fixedVersion := mod.FixedVersion; semver.IsValid(fixedVersion) && semver.Compare(req.Mod.Version, fixedVersion) < 0 { +- if fixedVersion := finding.FixedVersion; semver.IsValid(fixedVersion) && semver.Compare(req.Mod.Version, fixedVersion) < 0 { - cmd, err := getUpgradeCodeAction(fh, req, fixedVersion) - if err != nil { - return nil, err // TODO: bug report - } - sf := source.SuggestedFixFromCommand(cmd, protocol.QuickFix) -- if !vuln.IsCalled() { +- switch _, typ := foundVuln(finding); typ { +- case vulnImported: - infoFixes = append(infoFixes, sf) -- } else { +- case vulnCalled: - warningFixes = append(warningFixes, sf) - } - } - } - -- if len(warning) == 0 && len(info) == 0 { +- if len(warningSet) == 0 && len(infoSet) == 0 { - continue - } +- // Remove affecting osvs from the non-affecting osv list if any. +- if len(warningSet) > 0 { +- for k := range infoSet { +- if warningSet[k] { +- delete(infoSet, k) +- } +- } +- } - // Add an upgrade for module@latest. - // TODO(suzmue): verify if latest is the same as fixedVersion. - latest, err := getUpgradeCodeAction(fh, req, "latest") @@ -44851,11 +50236,8 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - if len(infoFixes) > 0 { - infoFixes = append(infoFixes, sf) - } -- -- sort.Strings(warning) -- sort.Strings(info) -- -- if len(warning) > 0 { +- if len(warningSet) > 0 { +- warning := sortedKeys(warningSet) - warningFixes = append(warningFixes, suggestRunOrResetGovulncheck) - vulnDiagnostics = append(vulnDiagnostics, &source.Diagnostic{ - URI: fh.URI(), @@ -44864,10 +50246,10 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - Source: diagSource, - Message: getVulnMessage(req.Mod.Path, warning, true, diagSource == source.Govulncheck), - SuggestedFixes: warningFixes, -- Related: relatedInfo, - }) - } -- if len(info) > 0 { +- if len(infoSet) > 0 { +- info := sortedKeys(infoSet) - infoFixes = append(infoFixes, suggestRunOrResetGovulncheck) - vulnDiagnostics = append(vulnDiagnostics, &source.Diagnostic{ - URI: fh.URI(), @@ -44876,7 +50258,6 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - Source: diagSource, - Message: getVulnMessage(req.Mod.Path, info, false, diagSource == source.Govulncheck), - SuggestedFixes: infoFixes, -- Related: relatedInfo, - }) - } - } @@ -44884,7 +50265,13 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - // TODO(hyangah): place this diagnostic on the `go` directive or `toolchain` directive - // after https://go.dev/issue/57001. - const diagnoseStdLib = false -- if diagnoseStdLib { +- +- // If diagnosing the stdlib, add standard library vulnerability diagnostics +- // on the module declaration. +- // +- // Only proceed if we have a valid module declaration on which to position +- // the diagnostics. +- if diagnoseStdLib && pm.File.Module != nil && pm.File.Module.Syntax != nil { - // Add standard library vulnerabilities. - stdlibVulns := vulnsByModule["stdlib"] - if len(stdlibVulns) == 0 { @@ -44897,41 +50284,44 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - return vulnDiagnostics, nil // TODO: bug report - } - -- stdlib := stdlibVulns[0].mod.FoundVersion -- var warning, info []string -- var relatedInfo []protocol.DiagnosticRelatedInformation -- for _, mv := range stdlibVulns { -- vuln := mv.vuln -- stdlib = mv.mod.FoundVersion -- if !vuln.IsCalled() { -- info = append(info, vuln.OSV.ID) -- } else { -- warning = append(warning, vuln.OSV.ID) -- relatedInfo = append(relatedInfo, listRelatedInfo(ctx, snapshot, vuln)...) +- var warningSet, infoSet = map[string]bool{}, map[string]bool{} +- for _, finding := range stdlibVulns { +- switch _, typ := foundVuln(finding); typ { +- case vulnImported: +- infoSet[finding.OSV] = true +- case vulnCalled: +- warningSet[finding.OSV] = true - } - } -- if len(warning) > 0 { +- if len(warningSet) > 0 { +- warning := sortedKeys(warningSet) - fixes := []source.SuggestedFix{suggestRunOrResetGovulncheck} - vulnDiagnostics = append(vulnDiagnostics, &source.Diagnostic{ - URI: fh.URI(), - Range: rng, - Severity: protocol.SeverityWarning, - Source: diagSource, -- Message: getVulnMessage(stdlib, warning, true, diagSource == source.Govulncheck), +- Message: getVulnMessage("go", warning, true, diagSource == source.Govulncheck), - SuggestedFixes: fixes, -- Related: relatedInfo, - }) +- +- // remove affecting osvs from the non-affecting osv list if any. +- for k := range infoSet { +- if warningSet[k] { +- delete(infoSet, k) +- } +- } - } -- if len(info) > 0 { +- if len(infoSet) > 0 { +- info := sortedKeys(infoSet) - fixes := []source.SuggestedFix{suggestRunOrResetGovulncheck} - vulnDiagnostics = append(vulnDiagnostics, &source.Diagnostic{ - URI: fh.URI(), - Range: rng, - Severity: protocol.SeverityInformation, - Source: diagSource, -- Message: getVulnMessage(stdlib, info, false, diagSource == source.Govulncheck), +- Message: getVulnMessage("go", info, false, diagSource == source.Govulncheck), - SuggestedFixes: fixes, -- Related: relatedInfo, - }) - } - } @@ -44939,6 +50329,46 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - return vulnDiagnostics, nil -} - +-type vulnFindingType int +- +-const ( +- vulnUnknown vulnFindingType = iota +- vulnCalled +- vulnImported +- vulnRequired +-) +- +-// foundVuln returns the frame info describing discovered vulnerable symbol/package/module +-// and how this vulnerability affects the analyzed package or module. +-func foundVuln(finding *govulncheck.Finding) (*govulncheck.Frame, vulnFindingType) { +- // finding.Trace is sorted from the imported vulnerable symbol to +- // the entry point in the callstack. +- // If Function is set, then Package must be set. Module will always be set. +- // If Function is set it was found in the call graph, otherwise if Package is set +- // it was found in the import graph, otherwise it was found in the require graph. +- // See the documentation of govulncheck.Finding. +- if len(finding.Trace) == 0 { // this shouldn't happen, but just in case... +- return nil, vulnUnknown +- } +- vuln := finding.Trace[0] +- if vuln.Package == "" { +- return vuln, vulnRequired +- } +- if vuln.Function == "" { +- return vuln, vulnImported +- } +- return vuln, vulnCalled +-} +- +-func sortedKeys(m map[string]bool) []string { +- ret := make([]string, 0, len(m)) +- for k := range m { +- ret = append(ret, k) +- } +- sort.Strings(ret) +- return ret +-} +- -// suggestGovulncheckAction returns a code action that suggests either run govulncheck -// for more accurate investigation (if the present vulncheck diagnostics are based on -// analysis less accurate than govulncheck) or reset the existing govulncheck result @@ -44993,66 +50423,12 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - return b.String() -} - --func listRelatedInfo(ctx context.Context, snapshot source.Snapshot, vuln *govulncheck.Vuln) []protocol.DiagnosticRelatedInformation { -- var ri []protocol.DiagnosticRelatedInformation -- for _, m := range vuln.Modules { -- for _, p := range m.Packages { -- for _, c := range p.CallStacks { -- if len(c.Frames) == 0 { -- continue -- } -- entry := c.Frames[0] -- pos := entry.Position -- if pos.Filename == "" { -- continue // token.Position Filename is an optional field. -- } -- uri := span.URIFromPath(pos.Filename) -- startPos := protocol.Position{ -- Line: uint32(pos.Line) - 1, -- // We need to read the file contents to precisesly map -- // token.Position (pos) to the UTF16-based column offset -- // protocol.Position requires. That can be expensive. -- // We need this related info to just help users to open -- // the entry points of the callstack and once the file is -- // open, we will compute the precise location based on the -- // open file contents. So, use the beginning of the line -- // as the position here instead of precise UTF16-based -- // position computation. -- Character: 0, -- } -- ri = append(ri, protocol.DiagnosticRelatedInformation{ -- Location: protocol.Location{ -- URI: protocol.URIFromSpanURI(uri), -- Range: protocol.Range{ -- Start: startPos, -- End: startPos, -- }, -- }, -- Message: fmt.Sprintf("[%v] %v -> %v.%v", vuln.OSV.ID, entry.Name(), p.Path, c.Symbol), -- }) -- } -- } -- } -- return ri --} -- --func formatMessage(v *govulncheck.Vuln) string { -- details := []byte(v.OSV.Details) -- // Remove any new lines that are not preceded or followed by a new line. -- for i, r := range details { -- if r == '\n' && i > 0 && details[i-1] != '\n' && i+1 < len(details) && details[i+1] != '\n' { -- details[i] = ' ' -- } -- } -- return strings.TrimSpace(strings.Replace(string(details), "\n\n", "\n\n ", -1)) --} -- -// href returns the url for the vulnerability information. -// Eventually we should retrieve the url embedded in the osv.Entry. -// While vuln.go.dev is under development, this always returns -// the page in pkg.go.dev. --func href(vuln *osv.Entry) string { -- return fmt.Sprintf("https://pkg.go.dev/vuln/%s", vuln.ID) +-func href(vulnID string) string { +- return fmt.Sprintf("https://pkg.go.dev/vuln/%s", vulnID) -} - -func getUpgradeCodeAction(fh source.FileHandle, req *modfile.Require, version string) (protocol.Command, error) { @@ -45084,7 +50460,7 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - var chosenVersionedUpgrade string - var selected []protocol.CodeAction - -- seen := make(map[string]bool) +- seenTitles := make(map[string]bool) - - for _, action := range actions { - if strings.HasPrefix(action.Title, upgradeCodeActionPrefix) { @@ -45096,8 +50472,8 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn - } - } else if strings.HasPrefix(action.Title, "Reset govulncheck") { - resetAction = action -- } else if !seen[action.Command.Title] { -- seen[action.Command.Title] = true +- } else if !seenTitles[action.Command.Title] { +- seenTitles[action.Command.Title] = true - selected = append(selected, action) - } - } @@ -45118,7 +50494,7 @@ diff -urN a/gopls/internal/lsp/mod/diagnostics.go b/gopls/internal/lsp/mod/diagn -} diff -urN a/gopls/internal/lsp/mod/format.go b/gopls/internal/lsp/mod/format.go --- a/gopls/internal/lsp/mod/format.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/mod/format.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/mod/format.go 1970-01-01 08:00:00 @@ -1,30 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -45147,13 +50523,13 @@ diff -urN a/gopls/internal/lsp/mod/format.go b/gopls/internal/lsp/mod/format.go - return nil, err - } - // Calculate the edits to be made due to the change. -- diffs := snapshot.View().Options().ComputeEdits(string(pm.Mapper.Content), string(formatted)) +- diffs := snapshot.Options().ComputeEdits(string(pm.Mapper.Content), string(formatted)) - return source.ToProtocolEdits(pm.Mapper, diffs) -} diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go --- a/gopls/internal/lsp/mod/hover.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/mod/hover.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,358 +0,0 @@ ++++ b/gopls/internal/lsp/mod/hover.go 1970-01-01 08:00:00 +@@ -1,378 +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. @@ -45169,9 +50545,11 @@ diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go - - "golang.org/x/mod/modfile" - "golang.org/x/mod/semver" -- "golang.org/x/tools/gopls/internal/govulncheck" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/vulncheck" +- "golang.org/x/tools/gopls/internal/vulncheck/govulncheck" +- "golang.org/x/tools/gopls/internal/vulncheck/osv" - "golang.org/x/tools/internal/event" -) - @@ -45238,7 +50616,7 @@ diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go - // Get the vulnerability info. - fromGovulncheck := true - vs := snapshot.View().Vulnerabilities(fh.URI())[fh.URI()] -- if vs == nil && snapshot.View().Options().Vulncheck == source.ModeVulncheckImports { +- if vs == nil && snapshot.Options().Vulncheck == source.ModeVulncheckImports { - var err error - vs, err = snapshot.ModVuln(ctx, fh.URI()) - if err != nil { @@ -45246,7 +50624,7 @@ diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go - } - fromGovulncheck = false - } -- affecting, nonaffecting := lookupVulns(vs, req.Mod.Path, req.Mod.Version) +- affecting, nonaffecting, osvs := lookupVulns(vs, req.Mod.Path, req.Mod.Version) - - // Get the `go mod why` results for the given file. - why, err := snapshot.ModWhy(ctx, fh) @@ -45265,11 +50643,11 @@ diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go - if err != nil { - return nil, err - } -- options := snapshot.View().Options() +- options := snapshot.Options() - isPrivate := snapshot.View().IsGoPrivatePath(req.Mod.Path) - header := formatHeader(req.Mod.Path, options) - explanation = formatExplanation(explanation, req, options, isPrivate) -- vulns := formatVulnerabilities(req.Mod.Path, affecting, nonaffecting, options, fromGovulncheck) +- vulns := formatVulnerabilities(affecting, nonaffecting, osvs, options, fromGovulncheck) - - return &protocol.Hover{ - Contents: protocol.MarkupContent{ @@ -45296,7 +50674,7 @@ diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go - fromGovulncheck := true - vs := snapshot.View().Vulnerabilities(fh.URI())[fh.URI()] - -- if vs == nil && snapshot.View().Options().Vulncheck == source.ModeVulncheckImports { +- if vs == nil && snapshot.Options().Vulncheck == source.ModeVulncheckImports { - vs, err = snapshot.ModVuln(ctx, fh.URI()) - if err != nil { - return nil, false @@ -45305,9 +50683,9 @@ diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go - } - modpath := "stdlib" - goVersion := snapshot.View().GoVersionString() -- affecting, nonaffecting := lookupVulns(vs, modpath, goVersion) -- options := snapshot.View().Options() -- vulns := formatVulnerabilities(modpath, affecting, nonaffecting, options, fromGovulncheck) +- affecting, nonaffecting, osvs := lookupVulns(vs, modpath, goVersion) +- options := snapshot.Options() +- vulns := formatVulnerabilities(affecting, nonaffecting, osvs, options, fromGovulncheck) - - return &protocol.Hover{ - Contents: protocol.MarkupContent{ @@ -45330,50 +50708,86 @@ diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go - return b.String() -} - --func lookupVulns(vulns *govulncheck.Result, modpath, version string) (affecting, nonaffecting []*govulncheck.Vuln) { -- if vulns == nil { -- return nil, nil +-func lookupVulns(vulns *vulncheck.Result, modpath, version string) (affecting, nonaffecting []*govulncheck.Finding, osvs map[string]*osv.Entry) { +- if vulns == nil || len(vulns.Entries) == 0 { +- return nil, nil, nil - } -- for _, vuln := range vulns.Vulns { -- for _, mod := range vuln.Modules { -- if mod.Path != modpath { -- continue -- } -- // It is possible that the source code was changed since the last -- // govulncheck run and information in the `vulns` info is stale. -- // For example, imagine that a user is in the middle of updating -- // problematic modules detected by the govulncheck run by applying -- // quick fixes. Stale diagnostics can be confusing and prevent the -- // user from quickly locating the next module to fix. -- // Ideally we should rerun the analysis with the updated module -- // dependencies or any other code changes, but we are not yet -- // in the position of automatically triggering the analysis -- // (govulncheck can take a while). We also don't know exactly what -- // part of source code was changed since `vulns` was computed. -- // As a heuristic, we assume that a user upgrades the affecting -- // module to the version with the fix or the latest one, and if the -- // version in the require statement is equal to or higher than the -- // fixed version, skip the vulnerability information in the hover. -- // Eventually, the user has to rerun govulncheck. -- if mod.FixedVersion != "" && semver.IsValid(version) && semver.Compare(mod.FixedVersion, version) <= 0 { -- continue -- } -- if vuln.IsCalled() { -- affecting = append(affecting, vuln) -- } else { -- nonaffecting = append(nonaffecting, vuln) +- for _, finding := range vulns.Findings { +- vuln, typ := foundVuln(finding) +- if vuln.Module != modpath { +- continue +- } +- // It is possible that the source code was changed since the last +- // govulncheck run and information in the `vulns` info is stale. +- // For example, imagine that a user is in the middle of updating +- // problematic modules detected by the govulncheck run by applying +- // quick fixes. Stale diagnostics can be confusing and prevent the +- // user from quickly locating the next module to fix. +- // Ideally we should rerun the analysis with the updated module +- // dependencies or any other code changes, but we are not yet +- // in the position of automatically triggering the analysis +- // (govulncheck can take a while). We also don't know exactly what +- // part of source code was changed since `vulns` was computed. +- // As a heuristic, we assume that a user upgrades the affecting +- // module to the version with the fix or the latest one, and if the +- // version in the require statement is equal to or higher than the +- // fixed version, skip the vulnerability information in the hover. +- // Eventually, the user has to rerun govulncheck. +- if finding.FixedVersion != "" && semver.IsValid(version) && semver.Compare(finding.FixedVersion, version) <= 0 { +- continue +- } +- switch typ { +- case vulnCalled: +- affecting = append(affecting, finding) +- case vulnImported: +- nonaffecting = append(nonaffecting, finding) +- } +- } +- +- // Remove affecting elements from nonaffecting. +- // An OSV entry can appear in both lists if an OSV entry covers +- // multiple packages imported but not all vulnerable symbols are used. +- // The current wording of hover message doesn't clearly +- // present this case well IMO, so let's skip reporting nonaffecting. +- if len(affecting) > 0 && len(nonaffecting) > 0 { +- affectingSet := map[string]bool{} +- for _, f := range affecting { +- affectingSet[f.OSV] = true +- } +- n := 0 +- for _, v := range nonaffecting { +- if !affectingSet[v.OSV] { +- nonaffecting[n] = v +- n++ - } - } +- nonaffecting = nonaffecting[:n] - } -- sort.Slice(nonaffecting, func(i, j int) bool { return nonaffecting[i].OSV.ID < nonaffecting[j].OSV.ID }) -- sort.Slice(affecting, func(i, j int) bool { return affecting[i].OSV.ID < affecting[j].OSV.ID }) -- return affecting, nonaffecting +- sort.Slice(nonaffecting, func(i, j int) bool { return nonaffecting[i].OSV < nonaffecting[j].OSV }) +- sort.Slice(affecting, func(i, j int) bool { return affecting[i].OSV < affecting[j].OSV }) +- return affecting, nonaffecting, vulns.Entries -} - --func formatVulnerabilities(modPath string, affecting, nonaffecting []*govulncheck.Vuln, options *source.Options, fromGovulncheck bool) string { -- if len(affecting) == 0 && len(nonaffecting) == 0 { +-func fixedVersion(fixed string) string { +- if fixed == "" { +- return "No fix is available." +- } +- return "Fixed in " + fixed + "." +-} +- +-func formatVulnerabilities(affecting, nonaffecting []*govulncheck.Finding, osvs map[string]*osv.Entry, options *source.Options, fromGovulncheck bool) string { +- if len(osvs) == 0 || (len(affecting) == 0 && len(nonaffecting) == 0) { - return "" - } +- byOSV := func(findings []*govulncheck.Finding) map[string][]*govulncheck.Finding { +- m := make(map[string][]*govulncheck.Finding) +- for _, f := range findings { +- m[f.OSV] = append(m[f.OSV], f) +- } +- return m +- } +- affectingByOSV := byOSV(affecting) +- nonaffectingByOSV := byOSV(nonaffecting) - - // TODO(hyangah): can we use go templates to generate hover messages? - // Then, we can use a different template for markdown case. @@ -45381,22 +50795,23 @@ diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go - - var b strings.Builder - -- if len(affecting) > 0 { +- if len(affectingByOSV) > 0 { - // TODO(hyangah): make the message more eyecatching (icon/codicon/color) -- if len(affecting) == 1 { -- b.WriteString(fmt.Sprintf("\n**WARNING:** Found %d reachable vulnerability.\n", len(affecting))) +- if len(affectingByOSV) == 1 { +- fmt.Fprintf(&b, "\n**WARNING:** Found %d reachable vulnerability.\n", len(affectingByOSV)) - } else { -- b.WriteString(fmt.Sprintf("\n**WARNING:** Found %d reachable vulnerabilities.\n", len(affecting))) +- fmt.Fprintf(&b, "\n**WARNING:** Found %d reachable vulnerabilities.\n", len(affectingByOSV)) - } - } -- for _, v := range affecting { -- fix := fixedVersionInfo(v, modPath) -- pkgs := vulnerablePkgsInfo(v, modPath, useMarkdown) +- for id, findings := range affectingByOSV { +- fix := fixedVersion(findings[0].FixedVersion) +- pkgs := vulnerablePkgsInfo(findings, useMarkdown) +- osvEntry := osvs[id] - - if useMarkdown { -- fmt.Fprintf(&b, "- [**%v**](%v) %v%v%v\n", v.OSV.ID, href(v.OSV), formatMessage(v), pkgs, fix) +- fmt.Fprintf(&b, "- [**%v**](%v) %v%v\n%v\n", id, href(id), osvEntry.Summary, pkgs, fix) - } else { -- fmt.Fprintf(&b, " - [%v] %v (%v) %v%v\n", v.OSV.ID, formatMessage(v), href(v.OSV), pkgs, fix) +- fmt.Fprintf(&b, " - [%v] %v (%v) %v%v\n", id, osvEntry.Summary, href(id), pkgs, fix) - } - } - if len(nonaffecting) > 0 { @@ -45406,60 +50821,41 @@ diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go - fmt.Fprintf(&b, "\n**Note:** The project imports packages with known vulnerabilities. Use `govulncheck` to check if the project uses vulnerable symbols.\n") - } - } -- for _, v := range nonaffecting { -- fix := fixedVersionInfo(v, modPath) -- pkgs := vulnerablePkgsInfo(v, modPath, useMarkdown) +- for k, findings := range nonaffectingByOSV { +- fix := fixedVersion(findings[0].FixedVersion) +- pkgs := vulnerablePkgsInfo(findings, useMarkdown) +- osvEntry := osvs[k] +- - if useMarkdown { -- fmt.Fprintf(&b, "- [%v](%v) %v%v%v\n", v.OSV.ID, href(v.OSV), formatMessage(v), pkgs, fix) +- fmt.Fprintf(&b, "- [%v](%v) %v%v\n%v\n", k, href(k), osvEntry.Summary, pkgs, fix) - } else { -- fmt.Fprintf(&b, " - [%v] %v (%v) %v%v\n", v.OSV.ID, formatMessage(v), href(v.OSV), pkgs, fix) +- fmt.Fprintf(&b, " - [%v] %v (%v) %v\n%v\n", k, osvEntry.Summary, href(k), pkgs, fix) - } - } - b.WriteString("\n") - return b.String() -} - --func vulnerablePkgsInfo(v *govulncheck.Vuln, modPath string, useMarkdown bool) string { -- var b bytes.Buffer -- for _, m := range v.Modules { -- if m.Path != modPath { -- continue -- } -- if c := len(m.Packages); c == 1 { -- b.WriteString("\n Vulnerable package is:") -- } else if c > 1 { -- b.WriteString("\n Vulnerable packages are:") -- } -- for _, pkg := range m.Packages { +-func vulnerablePkgsInfo(findings []*govulncheck.Finding, useMarkdown bool) string { +- var b strings.Builder +- seen := map[string]bool{} +- for _, f := range findings { +- p := f.Trace[0].Package +- if !seen[p] { +- seen[p] = true - if useMarkdown { - b.WriteString("\n * `") - } else { - b.WriteString("\n ") - } -- b.WriteString(pkg.Path) +- b.WriteString(p) - if useMarkdown { - b.WriteString("`") - } - } - } -- if b.Len() == 0 { -- return "" -- } - return b.String() -} --func fixedVersionInfo(v *govulncheck.Vuln, modPath string) string { -- fix := "\n\n **No fix is available.**" -- for _, m := range v.Modules { -- if m.Path != modPath { -- continue -- } -- if m.FixedVersion != "" { -- fix = "\n\n Fixed in " + m.FixedVersion + "." -- } -- break -- } -- return fix --} - -func formatExplanation(text string, req *modfile.Require, options *source.Options, isPrivate bool) string { - text = strings.TrimSuffix(text, "\n") @@ -45512,86 +50908,114 @@ diff -urN a/gopls/internal/lsp/mod/hover.go b/gopls/internal/lsp/mod/hover.go - b.WriteString("\n```") - return b.String() -} -diff -urN a/gopls/internal/lsp/mod/mod_test.go b/gopls/internal/lsp/mod/mod_test.go ---- a/gopls/internal/lsp/mod/mod_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/mod/mod_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,57 +0,0 @@ --// Copyright 2019 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/mod/inlayhint.go b/gopls/internal/lsp/mod/inlayhint.go +--- a/gopls/internal/lsp/mod/inlayhint.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/mod/inlayhint.go 1970-01-01 08:00:00 +@@ -1,100 +0,0 @@ +-// Copyright 2023 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 mod - -import ( -- "io/ioutil" -- "os" -- "path/filepath" -- "testing" +- "context" +- "fmt" - -- "golang.org/x/tools/gopls/internal/lsp/cache" +- "golang.org/x/mod/modfile" +- "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/lsp/tests" -- "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/testenv" -) - --func TestMain(m *testing.M) { -- testenv.ExitIfSmallMachine() -- os.Exit(m.Run()) --} +-func InlayHint(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, rng protocol.Range) ([]protocol.InlayHint, error) { +- // Inlay hints are enabled if the client supports them. +- pm, err := snapshot.ParseMod(ctx, fh) +- if err != nil { +- return nil, err +- } - --func TestModfileRemainsUnchanged(t *testing.T) { -- ctx := tests.Context(t) -- session := cache.NewSession(ctx, cache.New(nil), nil) -- options := source.DefaultOptions().Clone() -- tests.DefaultOptions(options) -- options.TempModfile = true -- options.Env = map[string]string{"GOPACKAGESDRIVER": "off", "GOROOT": ""} +- // Compare the version of the module used in the snapshot's metadata with the +- // version requested by the module, in both cases, taking replaces into account. +- // Produce an InlayHint when the version is the module is not the one used. - -- // Make sure to copy the test directory to a temporary directory so we do not -- // modify the test code or add go.sum files when we run the tests. -- folder, err := tests.CopyFolderToTempDir(filepath.Join("testdata", "unchanged")) -- if err != nil { -- t.Fatal(err) +- replaces := make(map[string]*modfile.Replace) +- for _, x := range pm.File.Replace { +- replaces[x.Old.Path] = x +- } +- +- requires := make(map[string]*modfile.Require) +- for _, x := range pm.File.Require { +- requires[x.Mod.Path] = x - } -- defer os.RemoveAll(folder) - -- before, err := ioutil.ReadFile(filepath.Join(folder, "go.mod")) +- am, err := snapshot.AllMetadata(ctx) - if err != nil { -- t.Fatal(err) +- return nil, err +- } +- +- var ans []protocol.InlayHint +- seen := make(map[string]bool) +- for _, meta := range am { +- if meta.Module == nil || seen[meta.Module.Path] { +- continue +- } +- seen[meta.Module.Path] = true +- metaVersion := meta.Module.Version +- if meta.Module.Replace != nil { +- metaVersion = meta.Module.Replace.Version +- } +- // These versions can be blank, as in gopls/go.mod's local replace +- if oldrepl, ok := replaces[meta.Module.Path]; ok && oldrepl.New.Version != metaVersion { +- ih := genHint(oldrepl.Syntax, oldrepl.New.Version, metaVersion, pm.Mapper) +- if ih != nil { +- ans = append(ans, *ih) +- } +- } else if oldreq, ok := requires[meta.Module.Path]; ok && oldreq.Mod.Version != metaVersion { +- // maybe it was replaced: +- if _, ok := replaces[meta.Module.Path]; ok { +- continue +- } +- ih := genHint(oldreq.Syntax, oldreq.Mod.Version, metaVersion, pm.Mapper) +- if ih != nil { +- ans = append(ans, *ih) +- } +- } - } -- _, _, release, err := session.NewView(ctx, "diagnostics_test", span.URIFromPath(folder), options) +- return ans, nil +-} +- +-func genHint(mline *modfile.Line, oldVersion, newVersion string, m *protocol.Mapper) *protocol.InlayHint { +- x := mline.End.Byte // the parser has removed trailing whitespace and comments (see modfile_test.go) +- x -= len(mline.Token[len(mline.Token)-1]) +- line, err := m.OffsetPosition(x) - if err != nil { -- t.Fatal(err) +- return nil - } -- release() -- after, err := ioutil.ReadFile(filepath.Join(folder, "go.mod")) +- part := protocol.InlayHintLabelPart{ +- Value: newVersion, +- Tooltip: &protocol.OrPTooltipPLabel{ +- Value: fmt.Sprintf("used metadata's version %s rather than go.mod's version %s", newVersion, oldVersion), +- }, +- } +- rng, err := m.OffsetRange(x, mline.End.Byte) - if err != nil { -- t.Fatal(err) +- return nil - } -- if string(before) != string(after) { -- t.Errorf("the real go.mod file was changed even when tempModfile=true") +- te := protocol.TextEdit{ +- Range: rng, +- NewText: newVersion, +- } +- return &protocol.InlayHint{ +- Position: line, +- Label: []protocol.InlayHintLabelPart{part}, +- Kind: protocol.Parameter, +- PaddingRight: true, +- TextEdits: []protocol.TextEdit{te}, - } --} -diff -urN a/gopls/internal/lsp/mod/testdata/unchanged/go.mod b/gopls/internal/lsp/mod/testdata/unchanged/go.mod ---- a/gopls/internal/lsp/mod/testdata/unchanged/go.mod 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/mod/testdata/unchanged/go.mod 1970-01-01 00:00:00.000000000 +0000 -@@ -1 +0,0 @@ --module unchanged -diff -urN a/gopls/internal/lsp/mod/testdata/unchanged/main.go b/gopls/internal/lsp/mod/testdata/unchanged/main.go ---- a/gopls/internal/lsp/mod/testdata/unchanged/main.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/mod/testdata/unchanged/main.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --// Package unchanged does something --package unchanged -- --func Yo() { -- println("yo") -} diff -urN a/gopls/internal/lsp/progress/progress.go b/gopls/internal/lsp/progress/progress.go --- a/gopls/internal/lsp/progress/progress.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/progress/progress.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,271 +0,0 @@ ++++ b/gopls/internal/lsp/progress/progress.go 1970-01-01 08:00:00 +@@ -1,283 +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. @@ -45627,10 +51051,22 @@ diff -urN a/gopls/internal/lsp/progress/progress.go b/gopls/internal/lsp/progres - } -} - +-// SetSupportsWorkDoneProgress sets whether the client supports work done +-// progress reporting. It must be set before using the tracker. +-// +-// TODO(rfindley): fix this broken initialization pattern. +-// Also: do we actually need the fall-back progress behavior using ShowMessage? +-// Surely ShowMessage notifications are too noisy to be worthwhile. -func (tracker *Tracker) SetSupportsWorkDoneProgress(b bool) { - tracker.supportsWorkDoneProgress = b -} - +-// SupportsWorkDoneProgress reports whether the tracker supports work done +-// progress reporting. +-func (tracker *Tracker) SupportsWorkDoneProgress() bool { +- return tracker.supportsWorkDoneProgress +-} +- -// Start notifies the client of work being done on the server. It uses either -// ShowMessage RPCs or $/progress messages, depending on the capabilities of -// the client. The returned WorkDone handle may be used to report incremental @@ -45865,7 +51301,7 @@ diff -urN a/gopls/internal/lsp/progress/progress.go b/gopls/internal/lsp/progres -} diff -urN a/gopls/internal/lsp/progress/progress_test.go b/gopls/internal/lsp/progress/progress_test.go --- a/gopls/internal/lsp/progress/progress_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/progress/progress_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/progress/progress_test.go 1970-01-01 08:00:00 @@ -1,161 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -46028,9 +51464,416 @@ diff -urN a/gopls/internal/lsp/progress/progress_test.go b/gopls/internal/lsp/pr - } - } -} +diff -urN a/gopls/internal/lsp/prompt.go b/gopls/internal/lsp/prompt.go +--- a/gopls/internal/lsp/prompt.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/prompt.go 1970-01-01 08:00:00 +@@ -1,317 +0,0 @@ +-// Copyright 2023 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 lsp +- +-import ( +- "context" +- "fmt" +- "os" +- "path/filepath" +- "time" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/telemetry" +- "golang.org/x/tools/internal/event" +-) +- +-// promptTimeout is the amount of time we wait for an ongoing prompt before +-// prompting again. This gives the user time to reply. However, at some point +-// we must assume that the client is not displaying the prompt, the user is +-// ignoring it, or the prompt has been disrupted in some way (e.g. by a gopls +-// crash). +-const promptTimeout = 24 * time.Hour +- +-// The following constants are used for testing telemetry integration. +-const ( +- TelemetryPromptWorkTitle = "Checking telemetry prompt" // progress notification title, for awaiting in tests +- GoplsConfigDirEnvvar = "GOPLS_CONFIG_DIR" // overridden for testing +- FakeTelemetryModefileEnvvar = "GOPLS_FAKE_TELEMETRY_MODEFILE" // overridden for testing +- TelemetryYes = "Yes, I'd like to help." +- TelemetryNo = "No, thanks." +-) +- +-// getenv returns the effective environment variable value for the provided +-// key, looking up the key in the session environment before falling back on +-// the process environment. +-func (s *Server) getenv(key string) string { +- if v, ok := s.Options().Env[key]; ok { +- return v +- } +- return os.Getenv(key) +-} +- +-// configDir returns the root of the gopls configuration dir. By default this +-// is os.UserConfigDir/gopls, but it may be overridden for tests. +-func (s *Server) configDir() (string, error) { +- if d := s.getenv(GoplsConfigDirEnvvar); d != "" { +- return d, nil +- } +- userDir, err := os.UserConfigDir() +- if err != nil { +- return "", err +- } +- return filepath.Join(userDir, "gopls"), nil +-} +- +-// telemetryMode returns the current effective telemetry mode. +-// By default this is x/telemetry.Mode(), but it may be overridden for tests. +-func (s *Server) telemetryMode() string { +- if fake := s.getenv(FakeTelemetryModefileEnvvar); fake != "" { +- if data, err := os.ReadFile(fake); err == nil { +- return string(data) +- } +- return "off" +- } +- return telemetry.Mode() +-} +- +-// setTelemetryMode sets the current telemetry mode. +-// By default this calls x/telemetry.SetMode, but it may be overridden for +-// tests. +-func (s *Server) setTelemetryMode(mode string) error { +- if fake := s.getenv(FakeTelemetryModefileEnvvar); fake != "" { +- return os.WriteFile(fake, []byte(mode), 0666) +- } +- return telemetry.SetMode(mode) +-} +- +-// maybePromptForTelemetry checks for the right conditions, and then prompts +-// the user to ask if they want to enable Go telemetry uploading. If the user +-// responds 'Yes', the telemetry mode is set to "on". +-// +-// The actual conditions for prompting are defensive, erring on the side of not +-// prompting. +-// If enabled is false, this will not prompt the user in any condition, +-// but will send work progress reports to help testing. +-func (s *Server) maybePromptForTelemetry(ctx context.Context, enabled bool) { +- if s.Options().VerboseWorkDoneProgress { +- work := s.progress.Start(ctx, TelemetryPromptWorkTitle, "Checking if gopls should prompt about telemetry...", nil, nil) +- defer work.End(ctx, "Done.") +- } +- +- if !enabled { // check this after the work progress message for testing. +- return // prompt is disabled +- } +- +- if s.telemetryMode() == "on" { +- // Telemetry is already on -- nothing to ask about. +- return +- } +- +- errorf := func(format string, args ...any) { +- err := fmt.Errorf(format, args...) +- event.Error(ctx, "telemetry prompt failed", err) +- } +- +- // Only prompt if we can read/write the prompt config file. +- configDir, err := s.configDir() +- if err != nil { +- errorf("unable to determine config dir: %v", err) +- return +- } +- +- var ( +- promptDir = filepath.Join(configDir, "prompt") // prompt configuration directory +- promptFile = filepath.Join(promptDir, "telemetry") // telemetry prompt file +- ) +- +- // prompt states, to be written to the prompt file +- const ( +- pYes = "yes" // user said yes +- pNo = "no" // user said no +- pPending = "pending" // current prompt is still pending +- pFailed = "failed" // prompt was asked but failed +- ) +- validStates := map[string]bool{ +- pYes: true, +- pNo: true, +- pPending: true, +- pFailed: true, +- } +- +- // parse the current prompt file +- var ( +- state string +- attempts = 0 // number of times we've asked already +- ) +- if content, err := os.ReadFile(promptFile); err == nil { +- if _, err := fmt.Sscanf(string(content), "%s %d", &state, &attempts); err == nil && validStates[state] { +- if state == pYes || state == pNo { +- // Prompt has been answered. Nothing to do. +- return +- } +- } else { +- state, attempts = "", 0 +- errorf("malformed prompt result %q", string(content)) +- } +- } else if !os.IsNotExist(err) { +- errorf("reading prompt file: %v", err) +- // Something went wrong. Since we don't know how many times we've asked the +- // prompt, err on the side of not spamming. +- return +- } +- +- if attempts >= 5 { +- // We've tried asking enough; give up. +- return +- } +- if attempts == 0 { +- // First time asking the prompt; we may need to make the prompt dir. +- if err := os.MkdirAll(promptDir, 0777); err != nil { +- errorf("creating prompt dir: %v", err) +- return +- } +- } +- +- // Acquire the lock and write "pending" to the prompt file before actually +- // prompting. +- // +- // This ensures that the prompt file is writeable, and that we increment the +- // attempt counter before we prompt, so that we don't end up in a failure +- // mode where we keep prompting and then failing to record the response. +- +- release, ok, err := acquireLockFile(promptFile) +- if err != nil { +- errorf("acquiring prompt: %v", err) +- return +- } +- if !ok { +- // Another prompt is currently pending. +- return +- } +- defer release() +- +- attempts++ +- +- pendingContent := []byte(fmt.Sprintf("%s %d", pPending, attempts)) +- if err := os.WriteFile(promptFile, pendingContent, 0666); err != nil { +- errorf("writing pending state: %v", err) +- return +- } +- +- var prompt = `Go telemetry helps us improve Go by periodically sending anonymous metrics and crash reports to the Go team. Learn more at https://telemetry.go.dev/privacy. +- +-Would you like to enable Go telemetry? +-` +- if s.Options().LinkifyShowMessage { +- prompt = `Go telemetry helps us improve Go by periodically sending anonymous metrics and crash reports to the Go team. Learn more at [telemetry.go.dev/privacy](https://telemetry.go.dev/privacy). +- +-Would you like to enable Go telemetry? +-` +- } +- // TODO(rfindley): investigate a "tell me more" action in combination with ShowDocument. +- params := &protocol.ShowMessageRequestParams{ +- Type: protocol.Info, +- Message: prompt, +- Actions: []protocol.MessageActionItem{ +- {Title: TelemetryYes}, +- {Title: TelemetryNo}, +- }, +- } +- +- item, err := s.client.ShowMessageRequest(ctx, params) +- if err != nil { +- errorf("ShowMessageRequest failed: %v", err) +- // Defensive: ensure item == nil for the logic below. +- item = nil +- } +- +- message := func(typ protocol.MessageType, msg string) { +- if err := s.client.ShowMessage(ctx, &protocol.ShowMessageParams{ +- Type: typ, +- Message: msg, +- }); err != nil { +- errorf("ShowMessage(unrecognize) failed: %v", err) +- } +- } +- +- result := pFailed +- if item == nil { +- // e.g. dialog was dismissed +- errorf("no response") +- } else { +- // Response matches MessageActionItem.Title. +- switch item.Title { +- case TelemetryYes: +- result = pYes +- if err := s.setTelemetryMode("on"); err == nil { +- message(protocol.Info, telemetryOnMessage(s.Options().LinkifyShowMessage)) +- } else { +- errorf("enabling telemetry failed: %v", err) +- msg := fmt.Sprintf("Failed to enable Go telemetry: %v\nTo enable telemetry manually, please run `go run golang.org/x/telemetry/cmd/gotelemetry@latest on`", err) +- message(protocol.Error, msg) +- } +- +- case TelemetryNo: +- result = pNo +- default: +- errorf("unrecognized response %q", item.Title) +- message(protocol.Error, fmt.Sprintf("Unrecognized response %q", item.Title)) +- } +- } +- resultContent := []byte(fmt.Sprintf("%s %d", result, attempts)) +- if err := os.WriteFile(promptFile, resultContent, 0666); err != nil { +- errorf("error writing result state to prompt file: %v", err) +- } +-} +- +-func telemetryOnMessage(linkify bool) string { +- format := `Thank you. Telemetry uploading is now enabled. +- +-To disable telemetry uploading, run %s. +-` +- var runCmd = "`go run golang.org/x/telemetry/cmd/gotelemetry@latest off`" +- if linkify { +- runCmd = "[gotelemetry off](https://golang.org/x/telemetry/cmd/gotelemetry)" +- } +- return fmt.Sprintf(format, runCmd) +-} +- +-// acquireLockFile attempts to "acquire a lock" for writing to path. +-// +-// This is achieved by creating an exclusive lock file at .lock. Lock +-// files expire after a period, at which point acquireLockFile will remove and +-// recreate the lock file. +-// +-// acquireLockFile fails if path is in a directory that doesn't exist. +-func acquireLockFile(path string) (func(), bool, error) { +- lockpath := path + ".lock" +- fi, err := os.Stat(lockpath) +- if err == nil { +- if time.Since(fi.ModTime()) > promptTimeout { +- _ = os.Remove(lockpath) // ignore error +- } else { +- return nil, false, nil +- } +- } else if !os.IsNotExist(err) { +- return nil, false, fmt.Errorf("statting lockfile: %v", err) +- } +- +- f, err := os.OpenFile(lockpath, os.O_CREATE|os.O_EXCL, 0666) +- if err != nil { +- if os.IsExist(err) { +- return nil, false, nil +- } +- return nil, false, fmt.Errorf("creating lockfile: %v", err) +- } +- fi, err = f.Stat() +- if err != nil { +- return nil, false, err +- } +- release := func() { +- _ = f.Close() // ignore error +- fi2, err := os.Stat(lockpath) +- if err == nil && os.SameFile(fi, fi2) { +- // Only clean up the lockfile if it's the same file we created. +- // Otherwise, our lock has expired and something else has the lock. +- // +- // There's a race here, in that the file could have changed since the +- // stat above; but given that we've already waited 24h this is extremely +- // unlikely, and acceptable. +- _ = os.Remove(lockpath) +- } +- } +- return release, true, nil +-} +diff -urN a/gopls/internal/lsp/prompt_test.go b/gopls/internal/lsp/prompt_test.go +--- a/gopls/internal/lsp/prompt_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/prompt_test.go 1970-01-01 08:00:00 +@@ -1,82 +0,0 @@ +-// Copyright 2023 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 lsp +- +-import ( +- "path/filepath" +- "sync" +- "sync/atomic" +- "testing" +-) +- +-func TestAcquireFileLock(t *testing.T) { +- name := filepath.Join(t.TempDir(), "config.json") +- +- const concurrency = 100 +- var acquired int32 +- var releasers [concurrency]func() +- defer func() { +- for _, r := range releasers { +- if r != nil { +- r() +- } +- } +- }() +- +- var wg sync.WaitGroup +- for i := range releasers { +- i := i +- wg.Add(1) +- go func() { +- defer wg.Done() +- +- release, ok, err := acquireLockFile(name) +- if err != nil { +- t.Errorf("Acquire failed: %v", err) +- return +- } +- if ok { +- atomic.AddInt32(&acquired, 1) +- releasers[i] = release +- } +- }() +- } +- +- wg.Wait() +- +- if acquired != 1 { +- t.Errorf("Acquire succeeded %d times, expected exactly 1", acquired) +- } +-} +- +-func TestReleaseAndAcquireFileLock(t *testing.T) { +- name := filepath.Join(t.TempDir(), "config.json") +- +- acquire := func() (func(), bool) { +- t.Helper() +- release, ok, err := acquireLockFile(name) +- if err != nil { +- t.Fatal(err) +- } +- return release, ok +- } +- +- release, ok := acquire() +- if !ok { +- t.Fatal("failed to Acquire") +- } +- if release2, ok := acquire(); ok { +- release() +- release2() +- t.Fatalf("Acquire succeeded unexpectedly") +- } +- +- release() +- release3, ok := acquire() +- release3() +- if !ok { +- t.Fatalf("failed to Acquire") +- } +-} diff -urN a/gopls/internal/lsp/protocol/codeactionkind.go b/gopls/internal/lsp/protocol/codeactionkind.go --- a/gopls/internal/lsp/protocol/codeactionkind.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/codeactionkind.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/codeactionkind.go 1970-01-01 08:00:00 @@ -1,11 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -46045,8 +51888,8 @@ diff -urN a/gopls/internal/lsp/protocol/codeactionkind.go b/gopls/internal/lsp/p -) diff -urN a/gopls/internal/lsp/protocol/context.go b/gopls/internal/lsp/protocol/context.go --- a/gopls/internal/lsp/protocol/context.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/context.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,43 +0,0 @@ ++++ b/gopls/internal/lsp/protocol/context.go 1970-01-01 08:00:00 +@@ -1,45 +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. @@ -46087,12 +51930,14 @@ diff -urN a/gopls/internal/lsp/protocol/context.go b/gopls/internal/lsp/protocol - if event.IsError(ev) { - msg.Type = Error - } +- // TODO(adonovan): the goroutine here could cause log +- // messages to be delivered out of order! Use a queue. - go client.LogMessage(xcontext.Detach(ctx), msg) - return ctx -} diff -urN a/gopls/internal/lsp/protocol/doc.go b/gopls/internal/lsp/protocol/doc.go --- a/gopls/internal/lsp/protocol/doc.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/doc.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/doc.go 1970-01-01 08:00:00 @@ -1,18 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -46114,7 +51959,7 @@ diff -urN a/gopls/internal/lsp/protocol/doc.go b/gopls/internal/lsp/protocol/doc -package protocol diff -urN a/gopls/internal/lsp/protocol/enums.go b/gopls/internal/lsp/protocol/enums.go --- a/gopls/internal/lsp/protocol/enums.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/enums.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/enums.go 1970-01-01 08:00:00 @@ -1,231 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -46347,9 +52192,157 @@ diff -urN a/gopls/internal/lsp/protocol/enums.go b/gopls/internal/lsp/protocol/e -func ParseTextDocumentSaveReason(s string) TextDocumentSaveReason { - return TextDocumentSaveReason(parseEnum(s, namesTextDocumentSaveReason[:])) -} +diff -urN a/gopls/internal/lsp/protocol/generate/README.md b/gopls/internal/lsp/protocol/generate/README.md +--- a/gopls/internal/lsp/protocol/generate/README.md 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/protocol/generate/README.md 1970-01-01 08:00:00 +@@ -1,144 +0,0 @@ +-# LSP Support for gopls +- +-## The protocol +- +-The LSP protocol exchanges json-encoded messages between the client and the server. +-(gopls is the server.) The messages are either Requests, which require Responses, or +-Notifications, which generate no response. Each Request or Notification has a method name +-such as "textDocument/hover" that indicates its meaning and determines which function in the server will handle it. +-The protocol is described in a +-[web page](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/), +-in words, and in a json file (metaModel.json) available either linked towards the bottom of the +-web page, or in the vscode-languageserver-node repository. This code uses the latter so the +-exact version can be tied to a githash. By default, the command will download the `github.com/microsoft/vscode-languageserver-node` repository to a temporary directory. +- +-The specification has five sections +- +-1. Requests, which describe the Request and Response types for request methods (e.g., *textDocument/didChange*), +-2. Notifications, which describe the Request types for notification methods, +-3. Structures, which describe named struct-like types, +-4. TypeAliases, which describe type aliases, +-5. Enumerations, which describe named constants. +- +-Requests and Notifications are tagged with a Method (e.g., `"textDocument/hover"`). +-The specification does not specify the names of the functions that handle the messages. These +-names are specified by the `methodNames` map. Enumerations generate Go `const`s, but +-in Typescript they are scoped to namespaces, while in Go they are scoped to a package, so the Go names +-may need to be modified to avoid name collisions. (See the `disambiguate` map, and its use.) +- +-Finally, the specified types are Typescript types, which are quite different from Go types. +- +-### Optionality +- +-The specification can mark fields in structs as Optional. The client distinguishes between missing +-fields and `null` fields in some cases. The Go translation for an optional type +-should be making sure the field's value +-can be `nil`, and adding the json tag `,omitempty`. The former condition would be satisfied by +-adding `*` to the field's type if the type is not a reference type. +- +-### Types +- +-The specification uses a number of different types, only a few of which correspond directly to Go types. +-The specification's types are "base", "reference", "map", "literal", "stringLiteral", "tuple", "and", "or". +-The "base" types correspond directly to Go types, although some Go types needs to be chosen for `URI` and `DocumentUri`. (The "base" types`RegExp`, `BooleanLiteral`, `NumericLiteral` never occur.) +- +-"reference" types are the struct-like types in the Structures section of the specification. The given +-names are suitable for Go to use, except the code needs to change names like `_Initialze` to `XInitialize` so +-they are exported for json marshaling and unmarshaling. +- +-"map" types are just like Go. (The key type in all of them is `DocumentUri`.) +- +-"stringLiteral" types are types whose type name and value are a single string. The chosen Go equivalent +-is to make the type `string` and the value a constant. (The alternative would be to generate a new +-named type, which seemed redundant.) +- +-"literal" types are like Go anonymous structs, so they have to be given a name. (All instances +-of the remaining types have to be given names. One approach is to construct the name from the components +-of the type, but this leads to misleading punning, and is unstable if components are added. The other approach +-is to construct the name from the context of the definition, that is, from the types it is defined within. +-For instance `Lit__InitializeParams_clientInfo` is the "literal" type at the +-`clientInfo` field in the `_InitializeParams` +-struct. Although this choice is sensitive to the ordering of the components, the code uses this approach, +-presuming that reordering components is an unlikely protocol change.) +- +-"tuple" types are generated as Go structs. (There is only one, with two `uint32` fields.) +- +-"and" types are Go structs with embedded type names. (There is only one, `And_Param_workspace_configuration`.) +- +-"or" types are the most complicated. There are a lot of them and there is no simple Go equivalent. +-They are defined as structs with a single `Value interface{}` field and custom json marshaling +-and unmarshaling code. Users can assign anything to `Value` but the type will be checked, and +-correctly marshaled, by the custom marshaling code. The unmarshaling code checks types, so `Value` +-will have one of the permitted types. (`nil` is always allowed.) There are about 40 "or" types that +-have a single non-null component, and these are converted to the component type. +- +-## Processing +- +-The code parses the json specification file, and scans all the types. It assigns names, as described +-above, to the types that are unnamed in the specification, and constructs Go equivalents as required. +-(Most of this code is in typenames.go.) +- +-There are four output files. tsclient.go and tsserver.go contain the definition and implementation +-of the `protocol.Client` and `protocol.Server` types and the code that dispatches on the Method +-of the Request or Notification. tsjson.go contains the custom marshaling and unmarshaling code. +-And tsprotocol.go contains the type and const definitions. +- +-### Accommodating gopls +- +-As the code generates output, mostly in generateoutput.go and main.go, +-it makes adjustments so that no changes are required to the existing Go code. +-(Organizing the computation this way makes the code's structure simpler, but results in +-a lot of unused types.) +-There are three major classes of these adjustments, and leftover special cases. +- +-The first major +-adjustment is to change generated type names to the ones gopls expects. Some of these don't change the +-semantics of the type, just the name. +-But for historical reasons a lot of them replace "or" types by a single +-component of the type. (Until fairly recently Go only saw or used only one of components.) +-The `goplsType` map in tables.go controls this process. +- +-The second major adjustment is to the types of fields of structs, which is done using the +-`renameProp` map in tables.go. +- +-The third major adjustment handles optionality, controlling `*` and `,omitempty` placement when +-the default rules don't match what gopls is expecting. (The map is `goplsStar`, also in tables.go) +-(If the intermediate components in expressions of the form `A.B.C.S` were optional, the code would need +-a lot of useless checking for nils. Typescript has a language construct to avoid most checks.) +- +-Then there are some additional special cases. There are a few places with adjustments to avoid +-recursive types. For instance `LSPArray` is `[]LSPAny`, but `LSPAny` is an "or" type including `LSPArray`. +-The solution is to make `LSPAny` an `interface{}`. Another instance is `_InitializeParams.trace` +-whose type is an "or" of 3 stringLiterals, which just becomes a `string`. +- +-### Checking +- +-`TestAll(t *testing.T)` checks that there are no unexpected fields in the json specification. +- +-While the code is executing, it checks that all the entries in the maps in tables.go are used. +-It also checks that the entries in `renameProp` and `goplsStar` are not redundant. +- +-As a one-time check on the first release of this code, diff-ing the existing and generated tsclient.go +-and tsserver.go code results in only whitespace and comment diffs. The existing and generated +-tsprotocol.go differ in whitespace and comments, and in a substantial number of new type definitions +-that the older, more heuristic, code did not generate. (And the unused type `_InitializeParams` differs +-slightly between the new and the old, and is not worth fixing.) +- +-### Some history +- +-The original stub code was written by hand, but with the protocol under active development, that +-couldn't last. The web page existed before the json specification, but it lagged the implementation +-and was hard to process by machine. So the earlier version of the generating code was written in Typescript, and +-used the Typescript compiler's API to parse the protocol code in the repository. +-It then used a set of heuristics +-to pick out the elements of the protocol, and another set of overlapping heuristics to create the Go code. +-The output was functional, but idiosyncratic, and the code was fragile and barely maintainable. +- +-### The future +- +-Most of the adjustments using the maps in tables.go could be removed by making changes, mostly to names, +-in the gopls code. Using more "or" types in gopls requires more elaborate, but stereotyped, changes. +-But even without all the adjustments, making this its own module would face problems; a number of +-dependencies would have to be factored out. And, it is fragile. The custom unmarshaling code knows what +-types it expects. A design that return an 'any' on unexpected types would match the json +-'ignore unexpected values' philosophy better, but the Go code would need extra checking. diff -urN a/gopls/internal/lsp/protocol/generate/generate.go b/gopls/internal/lsp/protocol/generate/generate.go --- a/gopls/internal/lsp/protocol/generate/generate.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/generate/generate.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/generate/generate.go 1970-01-01 08:00:00 @@ -1,121 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -46474,8 +52467,8 @@ diff -urN a/gopls/internal/lsp/protocol/generate/generate.go b/gopls/internal/ls -} diff -urN a/gopls/internal/lsp/protocol/generate/main.go b/gopls/internal/lsp/protocol/generate/main.go --- a/gopls/internal/lsp/protocol/generate/main.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/generate/main.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,387 +0,0 @@ ++++ b/gopls/internal/lsp/protocol/generate/main.go 1970-01-01 08:00:00 +@@ -1,390 +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. @@ -46489,6 +52482,8 @@ diff -urN a/gopls/internal/lsp/protocol/generate/main.go b/gopls/internal/lsp/pr -// To run it, type 'go generate' in the parent (protocol) directory. -package main - +-// see https://github.com/golang/go/issues/61217 for discussion of an issue +- -import ( - "bytes" - "encoding/json" @@ -46509,14 +52504,15 @@ diff -urN a/gopls/internal/lsp/protocol/generate/main.go b/gopls/internal/lsp/pr -// For example, tag release/protocol/3.17.3 of the repo defines protocol version 3.17.0. -// (Point releases are reflected in the git tag version even when they are cosmetic -// and don't change the protocol.) --var lspGitRef = "release/protocol/3.17.3-next.6" +-var lspGitRef = "release/protocol/3.17.4-next.2" - -var ( - repodir = flag.String("d", "", "directory containing clone of "+vscodeRepo) - outputdir = flag.String("o", ".", "output directory") - // PJW: not for real code -- cmpdir = flag.String("c", "", "directory of earlier code") -- doboth = flag.String("b", "", "generate and compare") +- cmpdir = flag.String("c", "", "directory of earlier code") +- doboth = flag.String("b", "", "generate and compare") +- lineNumbers = flag.Bool("l", false, "add line numbers to generated output") -) - -func main() { @@ -46865,8 +52861,8 @@ diff -urN a/gopls/internal/lsp/protocol/generate/main.go b/gopls/internal/lsp/pr -} diff -urN a/gopls/internal/lsp/protocol/generate/main_test.go b/gopls/internal/lsp/protocol/generate/main_test.go --- a/gopls/internal/lsp/protocol/generate/main_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/generate/main_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,118 +0,0 @@ ++++ b/gopls/internal/lsp/protocol/generate/main_test.go 1970-01-01 08:00:00 +@@ -1,119 +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. @@ -46891,6 +52887,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/main_test.go b/gopls/internal/l -// (in vscode, just run the test with "go.coverOnSingleTest": true) -func TestAll(t *testing.T) { - t.Skip("needs vscode-languageserver-node repository") +- *lineNumbers = true - log.SetFlags(log.Lshortfile) - main() -} @@ -46987,8 +52984,8 @@ diff -urN a/gopls/internal/lsp/protocol/generate/main_test.go b/gopls/internal/l -} diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/protocol/generate/output.go --- a/gopls/internal/lsp/protocol/generate/output.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/generate/output.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,420 +0,0 @@ ++++ b/gopls/internal/lsp/protocol/generate/output.go 1970-01-01 08:00:00 +@@ -1,427 +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. @@ -47044,7 +53041,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/ -} - -func genDecl(method string, param, result *Type, dir string) { -- fname := methodNames[method] +- fname := methodName(method) - p := "" - if notNil(param) { - p = ", *" + goplsName(param) @@ -47083,7 +53080,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/ - out := new(bytes.Buffer) - fmt.Fprintf(out, "\tcase %q:\n", method) - var p string -- fname := methodNames[method] +- fname := methodName(method) - if notNil(param) { - nm := goplsName(param) - if method == "workspace/configuration" { // gopls compatibility @@ -47145,7 +53142,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/ - r = "([]LSPAny, error)" - goResult = "[]LSPAny" - } -- fname := methodNames[method] +- fname := methodName(method) - fmt.Fprintf(out, "func (s *%%sDispatcher) %s(ctx context.Context%s) %s {\n", - fname, p, r) - @@ -47208,7 +53205,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/ - // a weird case, and needed only so the generated code contains the old gopls code - nm = "DocumentDiagnosticParams" - } -- fmt.Fprintf(out, "type %s struct { // line %d\n", nm, s.Line) +- fmt.Fprintf(out, "type %s struct {%s\n", nm, linex(s.Line)) - // for gpls compatibilitye, embed most extensions, but expand the rest some day - props := append([]NameType{}, s.Properties...) - if s.Name == "SymbolInformation" { // but expand this one @@ -47278,7 +53275,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/ - switch nt.kind { - case "literal": - fmt.Fprintf(out, "// created for Literal (%s)\n", nt.name) -- fmt.Fprintf(out, "type %s struct { // line %d\n", nm, nt.line+1) +- fmt.Fprintf(out, "type %s struct {%s\n", nm, linex(nt.line+1)) - genProps(out, nt.properties, nt.name) // systematic name, not gopls name; is this a good choice? - case "or": - if !strings.HasPrefix(nm, "Or") { @@ -47293,18 +53290,18 @@ diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/ - } - sort.Strings(names) - fmt.Fprintf(out, "// created for Or %v\n", names) -- fmt.Fprintf(out, "type %s struct { // line %d\n", nm, nt.line+1) +- fmt.Fprintf(out, "type %s struct {%s\n", nm, linex(nt.line+1)) - fmt.Fprintf(out, "\tValue interface{} `json:\"value\"`\n") - case "and": - fmt.Fprintf(out, "// created for And\n") -- fmt.Fprintf(out, "type %s struct { // line %d\n", nm, nt.line+1) +- fmt.Fprintf(out, "type %s struct {%s\n", nm, linex(nt.line+1)) - for _, x := range nt.items { - nm := goplsName(x) - fmt.Fprintf(out, "\t%s\n", nm) - } - case "tuple": // there's only this one - nt.name = "UIntCommaUInt" -- fmt.Fprintf(out, "//created for Tuple\ntype %s struct { // line %d\n", nm, nt.line+1) +- fmt.Fprintf(out, "//created for Tuple\ntype %s struct {%s\n", nm, linex(nt.line+1)) - fmt.Fprintf(out, "\tFld0 uint32 `json:\"fld0\"`\n") - fmt.Fprintf(out, "\tFld1 uint32 `json:\"fld1\"`\n") - default: @@ -47320,7 +53317,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/ - generateDoc(out, e.Documentation) - tp := goplsName(e.Type) - nm := goName(e.Name) -- fmt.Fprintf(out, "type %s %s // line %d\n", nm, tp, e.Line) +- fmt.Fprintf(out, "type %s %s%s\n", nm, tp, linex(e.Line)) - types[nm] = out.String() - vals := new(bytes.Buffer) - generateDoc(vals, e.Documentation) @@ -47342,7 +53339,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/ - default: - log.Fatalf("impossible type %T", v) - } -- fmt.Fprintf(vals, "\t%s %s = %s // line %d\n", nm, e.Name, val, v.Line) +- fmt.Fprintf(vals, "\t%s %s = %s%s\n", nm, e.Name, val, linex(v.Line)) - } - consts[nm] = vals.String() - } @@ -47384,6 +53381,13 @@ diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/ - } -} - +-func linex(n int) string { +- if *lineNumbers { +- return fmt.Sprintf(" // line %d", n) +- } +- return "" +-} +- -func goplsName(t *Type) string { - nm := typeNames[t] - // translate systematic name to gopls name @@ -47409,150 +53413,10 @@ diff -urN a/gopls/internal/lsp/protocol/generate/output.go b/gopls/internal/lsp/ - // that's all the cases that occur currently - return false -} -diff -urN a/gopls/internal/lsp/protocol/generate/README.md b/gopls/internal/lsp/protocol/generate/README.md ---- a/gopls/internal/lsp/protocol/generate/README.md 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/generate/README.md 1970-01-01 00:00:00.000000000 +0000 -@@ -1,136 +0,0 @@ --# LSP Support for gopls -- --## The protocol -- --The LSP protocol exchanges json-encoded messages between the client and the server. --(gopls is the server.) The messages are either Requests, which require Responses, or --Notifications, which generate no response. Each Request or Notification has a method name --such as "textDocument/hover" that indicates its meaning and determines which function in the server will handle it. --The protocol is described in a --[web page](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/), --in words, and in a json file (metaModel.json) available either linked towards the bottom of the --web page, or in the vscode-languageserver-node repository. This code uses the latter so the --exact version can be tied to a githash. By default, the command will download the `github.com/microsoft/vscode-languageserver-node` repository to a temporary directory. -- --The specification has five sections --1. Requests, which describe the Request and Response types for request methods (e.g., *textDocument/didChange*), --2. Notifications, which describe the Request types for notification methods, --3. Structures, which describe named struct-like types, --4. TypeAliases, which describe type aliases, --5. Enumerations, which describe named constants. -- --Requests and Notifications are tagged with a Method (e.g., `"textDocument/hover"`). --The specification does not specify the names of the functions that handle the messages. These --names are specified by the `methodNames` map. Enumerations generate Go `const`s, but --in Typescript they are scoped to namespaces, while in Go they are scoped to a package, so the Go names --may need to be modified to avoid name collisions. (See the `disambiguate` map, and its use.) -- --Finally, the specified types are Typescript types, which are quite different from Go types. -- --### Optionality --The specification can mark fields in structs as Optional. The client distinguishes between missing --fields and `null` fields in some cases. The Go translation for an optional type --should be making sure the field's value --can be `nil`, and adding the json tag `,omitempty`. The former condition would be satisfied by --adding `*` to the field's type if the type is not a reference type. -- --### Types --The specification uses a number of different types, only a few of which correspond directly to Go types. --The specification's types are "base", "reference", "map", "literal", "stringLiteral", "tuple", "and", "or". --The "base" types correspond directly to Go types, although some Go types needs to be chosen for `URI` and `DocumentUri`. (The "base" types`RegExp`, `BooleanLiteral`, `NumericLiteral` never occur.) -- --"reference" types are the struct-like types in the Structures section of the specification. The given --names are suitable for Go to use, except the code needs to change names like `_Initialze` to `XInitialize` so --they are exported for json marshaling and unmarshaling. -- --"map" types are just like Go. (The key type in all of them is `DocumentUri`.) -- --"stringLiteral" types are types whose type name and value are a single string. The chosen Go equivalent --is to make the type `string` and the value a constant. (The alternative would be to generate a new --named type, which seemed redundant.) -- --"literal" types are like Go anonymous structs, so they have to be given a name. (All instances --of the remaining types have to be given names. One approach is to construct the name from the components --of the type, but this leads to misleading punning, and is unstable if components are added. The other approach --is to construct the name from the context of the definition, that is, from the types it is defined within. --For instance `Lit__InitializeParams_clientInfo` is the "literal" type at the --`clientInfo` field in the `_InitializeParams` --struct. Although this choice is sensitive to the ordering of the components, the code uses this approach, --presuming that reordering components is an unlikely protocol change.) -- --"tuple" types are generated as Go structs. (There is only one, with two `uint32` fields.) -- --"and" types are Go structs with embedded type names. (There is only one, `And_Param_workspace_configuration`.) -- --"or" types are the most complicated. There are a lot of them and there is no simple Go equivalent. --They are defined as structs with a single `Value interface{}` field and custom json marshaling --and unmarshaling code. Users can assign anything to `Value` but the type will be checked, and --correctly marshaled, by the custom marshaling code. The unmarshaling code checks types, so `Value` --will have one of the permitted types. (`nil` is always allowed.) There are about 40 "or" types that --have a single non-null component, and these are converted to the component type. -- --## Processing --The code parses the json specification file, and scans all the types. It assigns names, as described --above, to the types that are unnamed in the specification, and constructs Go equivalents as required. --(Most of this code is in typenames.go.) -- --There are four output files. tsclient.go and tsserver.go contain the definition and implementation --of the `protocol.Client` and `protocol.Server` types and the code that dispatches on the Method --of the Request or Notification. tsjson.go contains the custom marshaling and unmarshaling code. --And tsprotocol.go contains the type and const definitions. -- --### Accommodating gopls --As the code generates output, mostly in generateoutput.go and main.go, --it makes adjustments so that no changes are required to the existing Go code. --(Organizing the computation this way makes the code's structure simpler, but results in --a lot of unused types.) --There are three major classes of these adjustments, and leftover special cases. -- --The first major --adjustment is to change generated type names to the ones gopls expects. Some of these don't change the --semantics of the type, just the name. --But for historical reasons a lot of them replace "or" types by a single --component of the type. (Until fairly recently Go only saw or used only one of components.) --The `goplsType` map in tables.go controls this process. -- --The second major adjustment is to the types of fields of structs, which is done using the --`renameProp` map in tables.go. -- --The third major adjustment handles optionality, controlling `*` and `,omitempty` placement when --the default rules don't match what gopls is expecting. (The map is `goplsStar`, also in tables.go) --(If the intermediate components in expressions of the form `A.B.C.S` were optional, the code would need --a lot of useless checking for nils. Typescript has a language construct to avoid most checks.) -- --Then there are some additional special cases. There are a few places with adjustments to avoid --recursive types. For instance `LSPArray` is `[]LSPAny`, but `LSPAny` is an "or" type including `LSPArray`. --The solution is to make `LSPAny` an `interface{}`. Another instance is `_InitializeParams.trace` --whose type is an "or" of 3 stringLiterals, which just becomes a `string`. -- --### Checking --`TestAll(t *testing.T)` checks that there are no unexpected fields in the json specification. -- --While the code is executing, it checks that all the entries in the maps in tables.go are used. --It also checks that the entries in `renameProp` and `goplsStar` are not redundant. -- --As a one-time check on the first release of this code, diff-ing the existing and generated tsclient.go --and tsserver.go code results in only whitespace and comment diffs. The existing and generated --tsprotocol.go differ in whitespace and comments, and in a substantial number of new type definitions --that the older, more heuristic, code did not generate. (And the unused type `_InitializeParams` differs --slightly between the new and the old, and is not worth fixing.) -- --### Some history --The original stub code was written by hand, but with the protocol under active development, that --couldn't last. The web page existed before the json specification, but it lagged the implementation --and was hard to process by machine. So the earlier version of the generating code was written in Typescript, and --used the Typescript compiler's API to parse the protocol code in the repository. --It then used a set of heuristics --to pick out the elements of the protocol, and another set of overlapping heuristics to create the Go code. --The output was functional, but idiosyncratic, and the code was fragile and barely maintainable. -- --### The future --Most of the adjustments using the maps in tables.go could be removed by making changes, mostly to names, --in the gopls code. Using more "or" types in gopls requires more elaborate, but stereotyped, changes. --But even without all the adjustments, making this its own module would face problems; a number of --dependencies would have to be factored out. And, it is fragile. The custom unmarshaling code knows what --types it expects. A design that return an 'any' on unexpected types would match the json --'ignore unexpected values' philosophy better, but the the Go code would need extra checking. diff -urN a/gopls/internal/lsp/protocol/generate/tables.go b/gopls/internal/lsp/protocol/generate/tables.go --- a/gopls/internal/lsp/protocol/generate/tables.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/generate/tables.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,327 +0,0 @@ ++++ b/gopls/internal/lsp/protocol/generate/tables.go 1970-01-01 08:00:00 +@@ -1,341 +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. @@ -47562,6 +53426,8 @@ diff -urN a/gopls/internal/lsp/protocol/generate/tables.go b/gopls/internal/lsp/ - -package main - +-import "log" +- -// prop combines the name of a property with the name of the structure it is in. -type prop [2]string - @@ -47623,6 +53489,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/tables.go b/gopls/internal/lsp/ - {"Command", "arguments"}: "[]json.RawMessage", - {"CompletionItem", "textEdit"}: "TextEdit", - {"Diagnostic", "code"}: "interface{}", +- {"Diagnostic", "data"}: "json.RawMessage", // delay unmarshalling quickfixes - - {"DocumentDiagnosticReportPartialResult", "relatedDocuments"}: "map[DocumentURI]interface{}", - @@ -47665,6 +53532,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/tables.go b/gopls/internal/lsp/ - "DiagnosticSeverity": {"Severity", ""}, - "DocumentDiagnosticReportKind": {"Diagnostic", ""}, - "FileOperationPatternKind": {"", "Pattern"}, +- "InlineCompletionTriggerKind": {"Inline", ""}, - "InsertTextFormat": {"", "TextFormat"}, - "SemanticTokenModifiers": {"Mod", ""}, - "SemanticTokenTypes": {"", "Type"}, @@ -47831,6 +53699,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/tables.go b/gopls/internal/lsp/ - "textDocument/hover": "Hover", - "textDocument/implementation": "Implementation", - "textDocument/inlayHint": "InlayHint", +- "textDocument/inlineCompletion": "InlineCompletion", - "textDocument/inlineValue": "InlineValue", - "textDocument/linkedEditingRange": "LinkedEditingRange", - "textDocument/moniker": "Moniker", @@ -47840,6 +53709,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/tables.go b/gopls/internal/lsp/ - "textDocument/prepareTypeHierarchy": "PrepareTypeHierarchy", - "textDocument/publishDiagnostics": "PublishDiagnostics", - "textDocument/rangeFormatting": "RangeFormatting", +- "textDocument/rangesFormatting": "RangesFormatting", - "textDocument/references": "References", - "textDocument/rename": "Rename", - "textDocument/selectionRange": "SelectionRange", @@ -47880,9 +53750,17 @@ diff -urN a/gopls/internal/lsp/protocol/generate/tables.go b/gopls/internal/lsp/ - "workspace/workspaceFolders": "WorkspaceFolders", - "workspaceSymbol/resolve": "ResolveWorkspaceSymbol", -} +- +-func methodName(method string) string { +- ans := methodNames[method] +- if ans == "" { +- log.Fatalf("unknown method %q", method) +- } +- return ans +-} diff -urN a/gopls/internal/lsp/protocol/generate/typenames.go b/gopls/internal/lsp/protocol/generate/typenames.go --- a/gopls/internal/lsp/protocol/generate/typenames.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/generate/typenames.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/generate/typenames.go 1970-01-01 08:00:00 @@ -1,184 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -48070,7 +53948,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/typenames.go b/gopls/internal/l -} diff -urN a/gopls/internal/lsp/protocol/generate/types.go b/gopls/internal/lsp/protocol/generate/types.go --- a/gopls/internal/lsp/protocol/generate/types.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/generate/types.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/generate/types.go 1970-01-01 08:00:00 @@ -1,170 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -48244,7 +54122,7 @@ diff -urN a/gopls/internal/lsp/protocol/generate/types.go b/gopls/internal/lsp/p -} diff -urN a/gopls/internal/lsp/protocol/log.go b/gopls/internal/lsp/protocol/log.go --- a/gopls/internal/lsp/protocol/log.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/log.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/log.go 1970-01-01 08:00:00 @@ -1,136 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -48384,7 +54262,7 @@ diff -urN a/gopls/internal/lsp/protocol/log.go b/gopls/internal/lsp/protocol/log -} diff -urN a/gopls/internal/lsp/protocol/mapper.go b/gopls/internal/lsp/protocol/mapper.go --- a/gopls/internal/lsp/protocol/mapper.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/mapper.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/mapper.go 1970-01-01 08:00:00 @@ -1,529 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -48460,9 +54338,9 @@ diff -urN a/gopls/internal/lsp/protocol/mapper.go b/gopls/internal/lsp/protocol/ - "sync" - "unicode/utf8" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" -) - -// A Mapper wraps the content of a file and provides mapping @@ -48917,8 +54795,8 @@ diff -urN a/gopls/internal/lsp/protocol/mapper.go b/gopls/internal/lsp/protocol/ -} diff -urN a/gopls/internal/lsp/protocol/mapper_test.go b/gopls/internal/lsp/protocol/mapper_test.go --- a/gopls/internal/lsp/protocol/mapper_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/mapper_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,441 +0,0 @@ ++++ b/gopls/internal/lsp/protocol/mapper_test.go 1970-01-01 08:00:00 +@@ -1,439 +0,0 @@ -// Copyright 2023 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. @@ -49358,11 +55236,9 @@ diff -urN a/gopls/internal/lsp/protocol/mapper_test.go b/gopls/internal/lsp/prot - } - } -} -- --// -- end -- diff -urN a/gopls/internal/lsp/protocol/protocol.go b/gopls/internal/lsp/protocol/protocol.go --- a/gopls/internal/lsp/protocol/protocol.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/protocol.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/protocol.go 1970-01-01 08:00:00 @@ -1,284 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -49650,7 +55526,7 @@ diff -urN a/gopls/internal/lsp/protocol/protocol.go b/gopls/internal/lsp/protoco -} diff -urN a/gopls/internal/lsp/protocol/span.go b/gopls/internal/lsp/protocol/span.go --- a/gopls/internal/lsp/protocol/span.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/span.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/span.go 1970-01-01 08:00:00 @@ -1,118 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -49772,7 +55648,7 @@ diff -urN a/gopls/internal/lsp/protocol/span.go b/gopls/internal/lsp/protocol/sp -} diff -urN a/gopls/internal/lsp/protocol/tsclient.go b/gopls/internal/lsp/protocol/tsclient.go --- a/gopls/internal/lsp/protocol/tsclient.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/tsclient.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/tsclient.go 1970-01-01 08:00:00 @@ -1,249 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -49782,8 +55658,8 @@ diff -urN a/gopls/internal/lsp/protocol/tsclient.go b/gopls/internal/lsp/protoco - -package protocol - --// Code generated from protocol/metaModel.json at ref release/protocol/3.17.3-next.6 (hash 56c23c557e3568a9f56f42435fd5a80f9458957f). --// https://github.com/microsoft/vscode-languageserver-node/blob/release/protocol/3.17.3-next.6/protocol/metaModel.json +-// Code generated from protocol/metaModel.json at ref release/protocol/3.17.4-next.2 (hash 184c8a7f010d335582f24337fe182baa6f2fccdd). +-// https://github.com/microsoft/vscode-languageserver-node/blob/release/protocol/3.17.4-next.2/protocol/metaModel.json -// LSP metaData.version = 3.17.0. - -import ( @@ -50025,7 +55901,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsclient.go b/gopls/internal/lsp/protoco -} diff -urN a/gopls/internal/lsp/protocol/tsdocument_changes.go b/gopls/internal/lsp/protocol/tsdocument_changes.go --- a/gopls/internal/lsp/protocol/tsdocument_changes.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/tsdocument_changes.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/protocol/tsdocument_changes.go 1970-01-01 08:00:00 @@ -1,42 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -50071,8 +55947,8 @@ diff -urN a/gopls/internal/lsp/protocol/tsdocument_changes.go b/gopls/internal/l -} diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/tsjson.go --- a/gopls/internal/lsp/protocol/tsjson.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/tsjson.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1997 +0,0 @@ ++++ b/gopls/internal/lsp/protocol/tsjson.go 1970-01-01 08:00:00 +@@ -1,2090 +0,0 @@ -// Copyright 2023 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. @@ -50081,8 +55957,8 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - -package protocol - --// Code generated from protocol/metaModel.json at ref release/protocol/3.17.3-next.6 (hash 56c23c557e3568a9f56f42435fd5a80f9458957f). --// https://github.com/microsoft/vscode-languageserver-node/blob/release/protocol/3.17.3-next.6/protocol/metaModel.json +-// Code generated from protocol/metaModel.json at ref release/protocol/3.17.4-next.2 (hash 184c8a7f010d335582f24337fe182baa6f2fccdd). +-// https://github.com/microsoft/vscode-languageserver-node/blob/release/protocol/3.17.4-next.2/protocol/metaModel.json -// LSP metaData.version = 3.17.0. - -import "encoding/json" @@ -50099,7 +55975,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return e.msg -} - --// from line 4769 +-// from line 4964 -func (t OrFEditRangePItemDefaults) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case FEditRangePItemDefaults: @@ -50130,7 +56006,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [FEditRangePItemDefaults Range]"} -} - --// from line 9811 +-// from line 10165 -func (t OrFNotebookPNotebookSelector) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case NotebookDocumentFilter: @@ -50161,7 +56037,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [NotebookDocumentFilter string]"} -} - --// from line 5520 +-// from line 5715 -func (t OrPLocation_workspace_symbol) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case Location: @@ -50192,7 +56068,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [Location PLocationMsg_workspace_symbol]"} -} - --// from line 4163 +-// from line 4358 -func (t OrPSection_workspace_didChangeConfiguration) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case []string: @@ -50223,7 +56099,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [[]string string]"} -} - --// from line 7075 +-// from line 7311 -func (t OrPTooltipPLabel) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case MarkupContent: @@ -50254,7 +56130,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [MarkupContent string]"} -} - --// from line 3699 +-// from line 3772 -func (t OrPTooltip_textDocument_inlayHint) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case MarkupContent: @@ -50285,7 +56161,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [MarkupContent string]"} -} - --// from line 6184 +-// from line 6420 -func (t Or_CancelParams_id) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case int32: @@ -50316,7 +56192,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [int32 string]"} -} - --// from line 4582 +-// from line 4777 -func (t Or_CompletionItem_documentation) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case MarkupContent: @@ -50347,7 +56223,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [MarkupContent string]"} -} - --// from line 4665 +-// from line 4860 -func (t Or_CompletionItem_textEdit) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case InsertReplaceEdit: @@ -50378,7 +56254,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [InsertReplaceEdit TextEdit]"} -} - --// from line 13753 +-// from line 14168 -func (t Or_Definition) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case Location: @@ -50409,7 +56285,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [Location []Location]"} -} - --// from line 8547 +-// from line 8865 -func (t Or_Diagnostic_code) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case int32: @@ -50440,7 +56316,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [int32 string]"} -} - --// from line 13885 +-// from line 14300 -func (t Or_DocumentDiagnosticReport) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case RelatedFullDocumentDiagnosticReport: @@ -50471,7 +56347,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [RelatedFullDocumentDiagnosticReport RelatedUnchangedDocumentDiagnosticReport]"} -} - --// from line 3822 +-// from line 3895 -func (t Or_DocumentDiagnosticReportPartialResult_relatedDocuments_Value) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case FullDocumentDiagnosticReport: @@ -50502,7 +56378,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [FullDocumentDiagnosticReport UnchangedDocumentDiagnosticReport]"} -} - --// from line 14095 +-// from line 14510 -func (t Or_DocumentFilter) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case NotebookCellTextDocumentFilter: @@ -50533,7 +56409,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [NotebookCellTextDocumentFilter TextDocumentFilter]"} -} - --// from line 4891 +-// from line 5086 -func (t Or_Hover_contents) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case MarkedString: @@ -50571,7 +56447,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [MarkedString MarkupContent []MarkedString]"} -} - --// from line 3658 +-// from line 3731 -func (t Or_InlayHint_label) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case []InlayHintLabelPart: @@ -50602,7 +56478,38 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [[]InlayHintLabelPart string]"} -} - --// from line 13863 +-// from line 4163 +-func (t Or_InlineCompletionItem_insertText) MarshalJSON() ([]byte, error) { +- switch x := t.Value.(type) { +- case StringValue: +- return json.Marshal(x) +- case string: +- return json.Marshal(x) +- case nil: +- return []byte("null"), nil +- } +- return nil, fmt.Errorf("type %T not one of [StringValue string]", t) +-} +- +-func (t *Or_InlineCompletionItem_insertText) UnmarshalJSON(x []byte) error { +- if string(x) == "null" { +- t.Value = nil +- return nil +- } +- var h0 StringValue +- if err := json.Unmarshal(x, &h0); err == nil { +- t.Value = h0 +- return nil +- } +- var h1 string +- if err := json.Unmarshal(x, &h1); err == nil { +- t.Value = h1 +- return nil +- } +- return &UnmarshalError{"unmarshal failed to match one of [StringValue string]"} +-} +- +-// from line 14278 -func (t Or_InlineValue) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case InlineValueEvaluatableExpression: @@ -50640,7 +56547,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [InlineValueEvaluatableExpression InlineValueText InlineValueVariableLookup]"} -} - --// from line 14060 +-// from line 14475 -func (t Or_MarkedString) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case Msg_MarkedString: @@ -50671,7 +56578,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [Msg_MarkedString string]"} -} - --// from line 10118 +-// from line 10472 -func (t Or_NotebookCellTextDocumentFilter_notebook) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case NotebookDocumentFilter: @@ -50702,7 +56609,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [NotebookDocumentFilter string]"} -} - --// from line 9857 +-// from line 10211 -func (t Or_NotebookDocumentSyncOptions_notebookSelector_Elem_Item1_notebook) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case NotebookDocumentFilter: @@ -50733,7 +56640,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [NotebookDocumentFilter string]"} -} - --// from line 7168 +-// from line 7404 -func (t Or_RelatedFullDocumentDiagnosticReport_relatedDocuments_Value) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case FullDocumentDiagnosticReport: @@ -50764,7 +56671,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [FullDocumentDiagnosticReport UnchangedDocumentDiagnosticReport]"} -} - --// from line 7207 +-// from line 7443 -func (t Or_RelatedUnchangedDocumentDiagnosticReport_relatedDocuments_Value) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case FullDocumentDiagnosticReport: @@ -50795,7 +56702,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [FullDocumentDiagnosticReport UnchangedDocumentDiagnosticReport]"} -} - --// from line 10741 +-// from line 11106 -func (t Or_RelativePattern_baseUri) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case URI: @@ -50826,7 +56733,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [URI WorkspaceFolder]"} -} - --// from line 1371 +-// from line 1413 -func (t Or_Result_textDocument_codeAction_Item0_Elem) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case CodeAction: @@ -50857,7 +56764,38 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [CodeAction Command]"} -} - --// from line 12197 +-// from line 980 +-func (t Or_Result_textDocument_inlineCompletion) MarshalJSON() ([]byte, error) { +- switch x := t.Value.(type) { +- case InlineCompletionList: +- return json.Marshal(x) +- case []InlineCompletionItem: +- return json.Marshal(x) +- case nil: +- return []byte("null"), nil +- } +- return nil, fmt.Errorf("type %T not one of [InlineCompletionList []InlineCompletionItem]", t) +-} +- +-func (t *Or_Result_textDocument_inlineCompletion) UnmarshalJSON(x []byte) error { +- if string(x) == "null" { +- t.Value = nil +- return nil +- } +- var h0 InlineCompletionList +- if err := json.Unmarshal(x, &h0); err == nil { +- t.Value = h0 +- return nil +- } +- var h1 []InlineCompletionItem +- if err := json.Unmarshal(x, &h1); err == nil { +- t.Value = h1 +- return nil +- } +- return &UnmarshalError{"unmarshal failed to match one of [InlineCompletionList []InlineCompletionItem]"} +-} +- +-// from line 12573 -func (t Or_SemanticTokensClientCapabilities_requests_full) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case FFullPRequests: @@ -50888,7 +56826,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [FFullPRequests bool]"} -} - --// from line 12177 +-// from line 12553 -func (t Or_SemanticTokensClientCapabilities_requests_range) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case FRangePRequests: @@ -50919,7 +56857,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [FRangePRequests bool]"} -} - --// from line 6579 +-// from line 6815 -func (t Or_SemanticTokensOptions_full) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case PFullESemanticTokensOptions: @@ -50950,7 +56888,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [PFullESemanticTokensOptions bool]"} -} - --// from line 6559 +-// from line 6795 -func (t Or_SemanticTokensOptions_range) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case PRangeESemanticTokensOptions: @@ -50981,7 +56919,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [PRangeESemanticTokensOptions bool]"} -} - --// from line 8227 +-// from line 8525 -func (t Or_ServerCapabilities_callHierarchyProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case CallHierarchyOptions: @@ -51019,7 +56957,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [CallHierarchyOptions CallHierarchyRegistrationOptions bool]"} -} - --// from line 8035 +-// from line 8333 -func (t Or_ServerCapabilities_codeActionProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case CodeActionOptions: @@ -51050,7 +56988,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [CodeActionOptions bool]"} -} - --// from line 8071 +-// from line 8369 -func (t Or_ServerCapabilities_colorProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case DocumentColorOptions: @@ -51088,7 +57026,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [DocumentColorOptions DocumentColorRegistrationOptions bool]"} -} - --// from line 7897 +-// from line 8195 -func (t Or_ServerCapabilities_declarationProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case DeclarationOptions: @@ -51126,7 +57064,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [DeclarationOptions DeclarationRegistrationOptions bool]"} -} - --// from line 7919 +-// from line 8217 -func (t Or_ServerCapabilities_definitionProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case DefinitionOptions: @@ -51157,7 +57095,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [DefinitionOptions bool]"} -} - --// from line 8384 +-// from line 8682 -func (t Or_ServerCapabilities_diagnosticProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case DiagnosticOptions: @@ -51188,7 +57126,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [DiagnosticOptions DiagnosticRegistrationOptions]"} -} - --// from line 8111 +-// from line 8409 -func (t Or_ServerCapabilities_documentFormattingProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case DocumentFormattingOptions: @@ -51219,7 +57157,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [DocumentFormattingOptions bool]"} -} - --// from line 7999 +-// from line 8297 -func (t Or_ServerCapabilities_documentHighlightProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case DocumentHighlightOptions: @@ -51250,7 +57188,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [DocumentHighlightOptions bool]"} -} - --// from line 8129 +-// from line 8427 -func (t Or_ServerCapabilities_documentRangeFormattingProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case DocumentRangeFormattingOptions: @@ -51281,7 +57219,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [DocumentRangeFormattingOptions bool]"} -} - --// from line 8017 +-// from line 8315 -func (t Or_ServerCapabilities_documentSymbolProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case DocumentSymbolOptions: @@ -51312,7 +57250,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [DocumentSymbolOptions bool]"} -} - --// from line 8174 +-// from line 8472 -func (t Or_ServerCapabilities_foldingRangeProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case FoldingRangeOptions: @@ -51350,7 +57288,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [FoldingRangeOptions FoldingRangeRegistrationOptions bool]"} -} - --// from line 7870 +-// from line 8168 -func (t Or_ServerCapabilities_hoverProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case HoverOptions: @@ -51381,7 +57319,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [HoverOptions bool]"} -} - --// from line 7959 +-// from line 8257 -func (t Or_ServerCapabilities_implementationProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case ImplementationOptions: @@ -51419,7 +57357,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [ImplementationOptions ImplementationRegistrationOptions bool]"} -} - --// from line 8361 +-// from line 8659 -func (t Or_ServerCapabilities_inlayHintProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case InlayHintOptions: @@ -51457,7 +57395,38 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [InlayHintOptions InlayHintRegistrationOptions bool]"} -} - --// from line 8338 +-// from line 8701 +-func (t Or_ServerCapabilities_inlineCompletionProvider) MarshalJSON() ([]byte, error) { +- switch x := t.Value.(type) { +- case InlineCompletionOptions: +- return json.Marshal(x) +- case bool: +- return json.Marshal(x) +- case nil: +- return []byte("null"), nil +- } +- return nil, fmt.Errorf("type %T not one of [InlineCompletionOptions bool]", t) +-} +- +-func (t *Or_ServerCapabilities_inlineCompletionProvider) UnmarshalJSON(x []byte) error { +- if string(x) == "null" { +- t.Value = nil +- return nil +- } +- var h0 InlineCompletionOptions +- if err := json.Unmarshal(x, &h0); err == nil { +- t.Value = h0 +- return nil +- } +- var h1 bool +- if err := json.Unmarshal(x, &h1); err == nil { +- t.Value = h1 +- return nil +- } +- return &UnmarshalError{"unmarshal failed to match one of [InlineCompletionOptions bool]"} +-} +- +-// from line 8636 -func (t Or_ServerCapabilities_inlineValueProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case InlineValueOptions: @@ -51495,7 +57464,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [InlineValueOptions InlineValueRegistrationOptions bool]"} -} - --// from line 8250 +-// from line 8548 -func (t Or_ServerCapabilities_linkedEditingRangeProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case LinkedEditingRangeOptions: @@ -51533,7 +57502,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [LinkedEditingRangeOptions LinkedEditingRangeRegistrationOptions bool]"} -} - --// from line 8292 +-// from line 8590 -func (t Or_ServerCapabilities_monikerProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case MonikerOptions: @@ -51571,7 +57540,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [MonikerOptions MonikerRegistrationOptions bool]"} -} - --// from line 7842 +-// from line 8140 -func (t Or_ServerCapabilities_notebookDocumentSync) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case NotebookDocumentSyncOptions: @@ -51602,7 +57571,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [NotebookDocumentSyncOptions NotebookDocumentSyncRegistrationOptions]"} -} - --// from line 7981 +-// from line 8279 -func (t Or_ServerCapabilities_referencesProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case ReferenceOptions: @@ -51633,7 +57602,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [ReferenceOptions bool]"} -} - --// from line 8156 +-// from line 8454 -func (t Or_ServerCapabilities_renameProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case RenameOptions: @@ -51664,7 +57633,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [RenameOptions bool]"} -} - --// from line 8196 +-// from line 8494 -func (t Or_ServerCapabilities_selectionRangeProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case SelectionRangeOptions: @@ -51702,7 +57671,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [SelectionRangeOptions SelectionRangeRegistrationOptions bool]"} -} - --// from line 8273 +-// from line 8571 -func (t Or_ServerCapabilities_semanticTokensProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case SemanticTokensOptions: @@ -51733,7 +57702,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [SemanticTokensOptions SemanticTokensRegistrationOptions]"} -} - --// from line 7824 +-// from line 8122 -func (t Or_ServerCapabilities_textDocumentSync) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case TextDocumentSyncKind: @@ -51764,7 +57733,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [TextDocumentSyncKind TextDocumentSyncOptions]"} -} - --// from line 7937 +-// from line 8235 -func (t Or_ServerCapabilities_typeDefinitionProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case TypeDefinitionOptions: @@ -51802,7 +57771,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [TypeDefinitionOptions TypeDefinitionRegistrationOptions bool]"} -} - --// from line 8315 +-// from line 8613 -func (t Or_ServerCapabilities_typeHierarchyProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case TypeHierarchyOptions: @@ -51840,7 +57809,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [TypeHierarchyOptions TypeHierarchyRegistrationOptions bool]"} -} - --// from line 8093 +-// from line 8391 -func (t Or_ServerCapabilities_workspaceSymbolProvider) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case WorkspaceSymbolOptions: @@ -51871,7 +57840,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [WorkspaceSymbolOptions bool]"} -} - --// from line 8841 +-// from line 9159 -func (t Or_SignatureInformation_documentation) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case MarkupContent: @@ -51902,7 +57871,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [MarkupContent string]"} -} - --// from line 6692 +-// from line 6928 -func (t Or_TextDocumentEdit_edits_Elem) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case AnnotatedTextEdit: @@ -51933,7 +57902,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [AnnotatedTextEdit TextEdit]"} -} - --// from line 9777 +-// from line 10131 -func (t Or_TextDocumentSyncOptions_save) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case SaveOptions: @@ -51964,7 +57933,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [SaveOptions bool]"} -} - --// from line 13986 +-// from line 14401 -func (t Or_WorkspaceDocumentDiagnosticReport) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case WorkspaceFullDocumentDiagnosticReport: @@ -51995,7 +57964,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ - return &UnmarshalError{"unmarshal failed to match one of [WorkspaceFullDocumentDiagnosticReport WorkspaceUnchangedDocumentDiagnosticReport]"} -} - --// from line 3219 +-// from line 3292 -func (t Or_WorkspaceEdit_documentChanges_Elem) MarshalJSON() ([]byte, error) { - switch x := t.Value.(type) { - case CreateFile: @@ -52072,8 +58041,8 @@ diff -urN a/gopls/internal/lsp/protocol/tsjson.go b/gopls/internal/lsp/protocol/ -} diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/protocol/tsprotocol.go --- a/gopls/internal/lsp/protocol/tsprotocol.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/tsprotocol.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,5450 +0,0 @@ ++++ b/gopls/internal/lsp/protocol/tsprotocol.go 1970-01-01 08:00:00 +@@ -1,5642 +0,0 @@ -// Copyright 2023 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. @@ -52082,8 +58051,8 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -package protocol - --// Code generated from protocol/metaModel.json at ref release/protocol/3.17.3-next.6 (hash 56c23c557e3568a9f56f42435fd5a80f9458957f). --// https://github.com/microsoft/vscode-languageserver-node/blob/release/protocol/3.17.3-next.6/protocol/metaModel.json +-// Code generated from protocol/metaModel.json at ref release/protocol/3.17.4-next.2 (hash 184c8a7f010d335582f24337fe182baa6f2fccdd). +-// https://github.com/microsoft/vscode-languageserver-node/blob/release/protocol/3.17.4-next.2/protocol/metaModel.json -// LSP metaData.version = 3.17.0. - -import "encoding/json" @@ -52091,14 +58060,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A special text edit with an additional change annotation. -// -// @since 3.16.0. --type AnnotatedTextEdit struct { // line 9372 +-type AnnotatedTextEdit struct { - // The actual identifier of the change annotation - AnnotationID ChangeAnnotationIdentifier `json:"annotationId"` - TextEdit -} - --// The parameters passed via a apply workspace edit request. --type ApplyWorkspaceEditParams struct { // line 5984 +-// The parameters passed via an apply workspace edit request. +-type ApplyWorkspaceEditParams struct { - // An optional label of the workspace edit. This label is - // presented in the user interface for example on an undo - // stack to undo the workspace edit. @@ -52110,7 +58079,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The result returned from the apply workspace edit request. -// -// @since 3.17 renamed from ApplyWorkspaceEditResponse --type ApplyWorkspaceEditResult struct { // line 6007 +-type ApplyWorkspaceEditResult struct { - // Indicates whether the edit was applied or not. - Applied bool `json:"applied"` - // An optional textual description for why the edit was not applied. @@ -52124,7 +58093,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// A base for all symbol information. --type BaseSymbolInformation struct { // line 8966 +-type BaseSymbolInformation struct { - // The name of this symbol. - Name string `json:"name"` - // The kind of this symbol. @@ -52141,7 +58110,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// @since 3.16.0 --type CallHierarchyClientCapabilities struct { // line 12141 +-type CallHierarchyClientCapabilities struct { - // Whether implementation supports dynamic registration. If this is set to `true` - // the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - // return value for the corresponding server capability as well. @@ -52151,7 +58120,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Represents an incoming call, e.g. a caller of a method or constructor. -// -// @since 3.16.0 --type CallHierarchyIncomingCall struct { // line 2779 +-type CallHierarchyIncomingCall struct { - // The item that makes the call. - From CallHierarchyItem `json:"from"` - // The ranges at which the calls appear. This is relative to the caller @@ -52162,7 +58131,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The parameter of a `callHierarchy/incomingCalls` request. -// -// @since 3.16.0 --type CallHierarchyIncomingCallsParams struct { // line 2755 +-type CallHierarchyIncomingCallsParams struct { - Item CallHierarchyItem `json:"item"` - WorkDoneProgressParams - PartialResultParams @@ -52172,7 +58141,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// of call hierarchy. -// -// @since 3.16.0 --type CallHierarchyItem struct { // line 2656 +-type CallHierarchyItem struct { - // The name of this item. - Name string `json:"name"` - // The kind of this item. @@ -52196,14 +58165,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Call hierarchy options used during static registration. -// -// @since 3.16.0 --type CallHierarchyOptions struct { // line 6534 +-type CallHierarchyOptions struct { - WorkDoneProgressOptions -} - -// Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc. -// -// @since 3.16.0 --type CallHierarchyOutgoingCall struct { // line 2829 +-type CallHierarchyOutgoingCall struct { - // The item that is called. - To CallHierarchyItem `json:"to"` - // The range at which this item is called. This is the range relative to the caller, e.g the item @@ -52215,7 +58184,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The parameter of a `callHierarchy/outgoingCalls` request. -// -// @since 3.16.0 --type CallHierarchyOutgoingCallsParams struct { // line 2805 +-type CallHierarchyOutgoingCallsParams struct { - Item CallHierarchyItem `json:"item"` - WorkDoneProgressParams - PartialResultParams @@ -52224,7 +58193,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The parameter of a `textDocument/prepareCallHierarchy` request. -// -// @since 3.16.0 --type CallHierarchyPrepareParams struct { // line 2638 +-type CallHierarchyPrepareParams struct { - TextDocumentPositionParams - WorkDoneProgressParams -} @@ -52232,12 +58201,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Call hierarchy options used during static or dynamic registration. -// -// @since 3.16.0 --type CallHierarchyRegistrationOptions struct { // line 2733 +-type CallHierarchyRegistrationOptions struct { - TextDocumentRegistrationOptions - CallHierarchyOptions - StaticRegistrationOptions -} --type CancelParams struct { // line 6179 +-type CancelParams struct { - // The request id to cancel. - ID interface{} `json:"id"` -} @@ -52245,7 +58214,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Additional information that describes document changes. -// -// @since 3.16.0 --type ChangeAnnotation struct { // line 6831 +-type ChangeAnnotation struct { - // A human-readable string describing the actual change. The string - // is rendered prominent in the user interface. - Label string `json:"label"` @@ -52258,9 +58227,9 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// An identifier to refer to a change annotation stored with a workspace edit. --type ChangeAnnotationIdentifier = string // (alias) line 13976 +-type ChangeAnnotationIdentifier = string // (alias) line 14391 -// Defines the capabilities provided by the client. --type ClientCapabilities struct { // line 9674 +-type ClientCapabilities struct { - // Workspace specific client capabilities. - Workspace WorkspaceClientCapabilities `json:"workspace,omitempty"` - // Text document specific client capabilities. @@ -52283,7 +58252,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// to refactor code. -// -// A CodeAction must set either `edit` and/or a `command`. If both are supplied, the `edit` is applied first, then the `command` is executed. --type CodeAction struct { // line 5382 +-type CodeAction struct { - // A short, human-readable, title for this code action. - Title string `json:"title"` - // The kind of the code action. @@ -52330,7 +58299,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The Client Capabilities of a {@link CodeActionRequest}. --type CodeActionClientCapabilities struct { // line 11721 +-type CodeActionClientCapabilities struct { - // Whether code action supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // The client support code action literals of type `CodeAction` as a valid @@ -52370,7 +58339,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -// Contains additional diagnostic information about the context in which -// a {@link CodeActionProvider.provideCodeActions code action} is run. --type CodeActionContext struct { // line 9032 +-type CodeActionContext struct { - // An array of diagnostics known on the client side overlapping the range provided to the - // `textDocument/codeAction` request. They are provided so that the server knows which - // errors are currently presented to the user for the given range. There is no guarantee @@ -52389,9 +58358,10 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// A set of predefined code action kinds --type CodeActionKind string // line 13326 +-type CodeActionKind string +- -// Provider options for a {@link CodeActionRequest}. --type CodeActionOptions struct { // line 9071 +-type CodeActionOptions struct { - // CodeActionKinds that this server may return. - // - // The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server @@ -52406,7 +58376,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The parameters of a {@link CodeActionRequest}. --type CodeActionParams struct { // line 5308 +-type CodeActionParams struct { - // The document in which the command was invoked. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The range for which the command was invoked. @@ -52418,7 +58388,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link CodeActionRequest}. --type CodeActionRegistrationOptions struct { // line 5476 +-type CodeActionRegistrationOptions struct { - TextDocumentRegistrationOptions - CodeActionOptions -} @@ -52426,11 +58396,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The reason why code actions were requested. -// -// @since 3.17.0 --type CodeActionTriggerKind uint32 // line 13606 +-type CodeActionTriggerKind uint32 +- -// Structure to capture a description for an error code. -// -// @since 3.16.0 --type CodeDescription struct { // line 10026 +-type CodeDescription struct { - // An URI to open with more information about the diagnostic error. - Href URI `json:"href"` -} @@ -52440,7 +58411,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// -// A code lens is _unresolved_ when no command is associated to it. For performance -// reasons the creation of a code lens and resolving should be done in two stages. --type CodeLens struct { // line 5599 +-type CodeLens struct { - // The range in which this code lens is valid. Should only span a single line. - Range Range `json:"range"` - // The command this code lens represents. @@ -52452,20 +58423,20 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The client capabilities of a {@link CodeLensRequest}. --type CodeLensClientCapabilities struct { // line 11835 +-type CodeLensClientCapabilities struct { - // Whether code lens supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} - -// Code Lens provider options of a {@link CodeLensRequest}. --type CodeLensOptions struct { // line 9127 +-type CodeLensOptions struct { - // Code lens has a resolve provider as well. - ResolveProvider bool `json:"resolveProvider,omitempty"` - WorkDoneProgressOptions -} - -// The parameters of a {@link CodeLensRequest}. --type CodeLensParams struct { // line 5575 +-type CodeLensParams struct { - // The document to request code lens for. - TextDocument TextDocumentIdentifier `json:"textDocument"` - WorkDoneProgressParams @@ -52473,13 +58444,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link CodeLensRequest}. --type CodeLensRegistrationOptions struct { // line 5631 +-type CodeLensRegistrationOptions struct { - TextDocumentRegistrationOptions - CodeLensOptions -} - -// @since 3.16.0 --type CodeLensWorkspaceClientCapabilities struct { // line 10993 +-type CodeLensWorkspaceClientCapabilities struct { - // Whether the client implementation supports a refresh request sent from the - // server to the client. - // @@ -52491,7 +58462,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Represents a color in RGBA space. --type Color struct { // line 6433 +-type Color struct { - // The red component of this color in the range [0-1]. - Red float64 `json:"red"` - // The green component of this color in the range [0-1]. @@ -52503,13 +58474,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Represents a color range from a document. --type ColorInformation struct { // line 2239 +-type ColorInformation struct { - // The range in the document where this color appears. - Range Range `json:"range"` - // The actual color value for this color range. - Color Color `json:"color"` -} --type ColorPresentation struct { // line 2321 +-type ColorPresentation struct { - // The label of this color presentation. It will be shown on the color - // picker header. By default this is also the text that is inserted when selecting - // this color presentation. @@ -52524,7 +58495,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Parameters for a {@link ColorPresentationRequest}. --type ColorPresentationParams struct { // line 2281 +-type ColorPresentationParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The color to request presentations for. @@ -52539,7 +58510,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// will be used to represent a command in the UI and, optionally, -// an array of arguments which will be passed to the command handler -// function when invoked. --type Command struct { // line 5348 +-type Command struct { - // Title of the command, like `save`. - Title string `json:"title"` - // The identifier of the actual command handler. @@ -52550,7 +58521,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Completion client capabilities --type CompletionClientCapabilities struct { // line 11168 +-type CompletionClientCapabilities struct { - // Whether completion supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // The client supports the following `CompletionItem` specific @@ -52574,7 +58545,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Contains additional information about the context in which a completion request is triggered. --type CompletionContext struct { // line 8628 +-type CompletionContext struct { - // How the completion was triggered. - TriggerKind CompletionTriggerKind `json:"triggerKind"` - // The trigger character (a single character) that has trigger code complete. @@ -52584,7 +58555,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -// A completion item represents a text snippet that is -// proposed to complete text that is being typed. --type CompletionItem struct { // line 4528 +-type CompletionItem struct { - // The label of this completion item. - // - // The label property is also by default the text that @@ -52705,11 +58676,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The kind of a completion entry. --type CompletionItemKind uint32 // line 13134 +-type CompletionItemKind uint32 +- -// Additional details for a completion item label. -// -// @since 3.17.0 --type CompletionItemLabelDetails struct { // line 8651 +-type CompletionItemLabelDetails struct { - // An optional string which is rendered less prominently directly after {@link CompletionItem.label label}, - // without any spacing. Should be used for function signatures and type annotations. - Detail string `json:"detail,omitempty"` @@ -52722,10 +58694,11 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// item. -// -// @since 3.15.0 --type CompletionItemTag uint32 // line 13244 +-type CompletionItemTag uint32 +- -// Represents a collection of {@link CompletionItem completion items} to be presented -// in the editor. --type CompletionList struct { // line 4737 +-type CompletionList struct { - // This list it not complete. Further typing results in recomputing this list. - // - // Recomputed lists have all their items replaced (not appended) in the @@ -52750,7 +58723,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Completion options. --type CompletionOptions struct { // line 8707 +-type CompletionOptions struct { - // Most tools trigger completion request automatically without explicitly requesting - // it using a keyboard shortcut (e.g. Ctrl+Space). Typically they do so when the user - // starts to type an identifier. For example if the user types `c` in a JavaScript file @@ -52781,7 +58754,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Completion parameters --type CompletionParams struct { // line 4497 +-type CompletionParams struct { - // The completion context. This is only available it the client specifies - // to send this using the client capability `textDocument.completion.contextSupport === true` - Context CompletionContext `json:"context,omitempty"` @@ -52791,14 +58764,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link CompletionRequest}. --type CompletionRegistrationOptions struct { // line 4854 +-type CompletionRegistrationOptions struct { - TextDocumentRegistrationOptions - CompletionOptions -} - -// How a completion was triggered --type CompletionTriggerKind uint32 // line 13555 --type ConfigurationItem struct { // line 6396 +-type CompletionTriggerKind uint32 +-type ConfigurationItem struct { - // The scope to get the configuration section for. - ScopeURI string `json:"scopeUri,omitempty"` - // The configuration section asked for. @@ -52806,12 +58779,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The parameters of a configuration request. --type ConfigurationParams struct { // line 2199 +-type ConfigurationParams struct { - Items []ConfigurationItem `json:"items"` -} - -// Create file operation. --type CreateFile struct { // line 6712 +-type CreateFile struct { - // A create - Kind string `json:"kind"` - // The resource to create. @@ -52822,7 +58795,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Options to create a file. --type CreateFileOptions struct { // line 9417 +-type CreateFileOptions struct { - // Overwrite existing file. Overwrite wins over `ignoreIfExists` - Overwrite bool `json:"overwrite,omitempty"` - // Ignore if exists. @@ -52833,15 +58806,15 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// files. -// -// @since 3.16.0 --type CreateFilesParams struct { // line 3175 +-type CreateFilesParams struct { - // An array of all files/folders created in this operation. - Files []FileCreate `json:"files"` -} - -// The declaration of a symbol representation as one or many {@link Location locations}. --type Declaration = []Location // (alias) line 13833 +-type Declaration = []Location // (alias) line 14248 -// @since 3.14.0 --type DeclarationClientCapabilities struct { // line 11509 +-type DeclarationClientCapabilities struct { - // Whether declaration supports dynamic registration. If this is set to `true` - // the client supports the new `DeclarationRegistrationOptions` return value - // for the corresponding server capability as well. @@ -52857,16 +58830,16 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// -// Servers should prefer returning `DeclarationLink` over `Declaration` if supported -// by the client. --type DeclarationLink = LocationLink // (alias) line 13853 --type DeclarationOptions struct { // line 6491 +-type DeclarationLink = LocationLink // (alias) line 14268 +-type DeclarationOptions struct { - WorkDoneProgressOptions -} --type DeclarationParams struct { // line 2494 +-type DeclarationParams struct { - TextDocumentPositionParams - WorkDoneProgressParams - PartialResultParams -} --type DeclarationRegistrationOptions struct { // line 2514 +-type DeclarationRegistrationOptions struct { - DeclarationOptions - TextDocumentRegistrationOptions - StaticRegistrationOptions @@ -52878,9 +58851,9 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// -// Servers should prefer returning `DefinitionLink` over `Definition` if supported -// by the client. --type Definition = Or_Definition // (alias) line 13751 +-type Definition = Or_Definition // (alias) line 14166 -// Client Capabilities for a {@link DefinitionRequest}. --type DefinitionClientCapabilities struct { // line 11534 +-type DefinitionClientCapabilities struct { - // Whether definition supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // The client supports additional metadata in the form of definition links. @@ -52893,27 +58866,27 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// -// Provides additional metadata over normal {@link Location location} definitions, including the range of -// the defining symbol --type DefinitionLink = LocationLink // (alias) line 13771 +-type DefinitionLink = LocationLink // (alias) line 14186 -// Server Capabilities for a {@link DefinitionRequest}. --type DefinitionOptions struct { // line 8919 +-type DefinitionOptions struct { - WorkDoneProgressOptions -} - -// Parameters for a {@link DefinitionRequest}. --type DefinitionParams struct { // line 5018 +-type DefinitionParams struct { - TextDocumentPositionParams - WorkDoneProgressParams - PartialResultParams -} - -// Registration options for a {@link DefinitionRequest}. --type DefinitionRegistrationOptions struct { // line 5039 +-type DefinitionRegistrationOptions struct { - TextDocumentRegistrationOptions - DefinitionOptions -} - -// Delete file operation --type DeleteFile struct { // line 6794 +-type DeleteFile struct { - // A delete - Kind string `json:"kind"` - // The file to delete. @@ -52924,7 +58897,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Delete file options --type DeleteFileOptions struct { // line 9465 +-type DeleteFileOptions struct { - // Delete the content recursively if a folder is denoted. - Recursive bool `json:"recursive,omitempty"` - // Ignore the operation if the file doesn't exist. @@ -52935,14 +58908,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// files. -// -// @since 3.16.0 --type DeleteFilesParams struct { // line 3300 +-type DeleteFilesParams struct { - // An array of all files/folders deleted in this operation. - Files []FileDelete `json:"files"` -} - -// Represents a diagnostic, such as a compiler error or warning. Diagnostic objects -// are only valid in the scope of a resource. --type Diagnostic struct { // line 8525 +-type Diagnostic struct { - // The range at which the message applies - Range Range `json:"range"` - // The diagnostic's severity. Can be omitted. If omitted it is up to the @@ -52972,13 +58945,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // notification and `textDocument/codeAction` request. - // - // @since 3.16.0 -- Data interface{} `json:"data,omitempty"` +- Data *json.RawMessage `json:"data,omitempty"` -} - -// Client capabilities specific to diagnostic pull requests. -// -// @since 3.17.0 --type DiagnosticClientCapabilities struct { // line 12408 +-type DiagnosticClientCapabilities struct { - // Whether implementation supports dynamic registration. If this is set to `true` - // the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - // return value for the corresponding server capability as well. @@ -52990,7 +58963,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Diagnostic options. -// -// @since 3.17.0 --type DiagnosticOptions struct { // line 7293 +-type DiagnosticOptions struct { - // An optional identifier under which the diagnostics are - // managed by the client. - Identifier string `json:"identifier,omitempty"` @@ -53007,7 +58980,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Diagnostic registration options. -// -// @since 3.17.0 --type DiagnosticRegistrationOptions struct { // line 3855 +-type DiagnosticRegistrationOptions struct { - TextDocumentRegistrationOptions - DiagnosticOptions - StaticRegistrationOptions @@ -53016,7 +58989,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Represents a related message and source code location for a diagnostic. This should be -// used to point to code locations that cause or related to a diagnostics, e.g when duplicating -// a symbol in a scope. --type DiagnosticRelatedInformation struct { // line 10041 +-type DiagnosticRelatedInformation struct { - // The location of this related diagnostic information. - Location Location `json:"location"` - // The message of this related diagnostic information. @@ -53026,20 +58999,22 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Cancellation data returned from a diagnostic request. -// -// @since 3.17.0 --type DiagnosticServerCancellationData struct { // line 3841 +-type DiagnosticServerCancellationData struct { - RetriggerRequest bool `json:"retriggerRequest"` -} - -// The diagnostic's severity. --type DiagnosticSeverity uint32 // line 13504 +-type DiagnosticSeverity uint32 +- -// The diagnostic tags. -// -// @since 3.15.0 --type DiagnosticTag uint32 // line 13534 +-type DiagnosticTag uint32 +- -// Workspace client capabilities specific to diagnostic pull requests. -// -// @since 3.17.0 --type DiagnosticWorkspaceClientCapabilities struct { // line 11111 +-type DiagnosticWorkspaceClientCapabilities struct { - // Whether the client implementation supports a refresh request sent from - // the server to the client. - // @@ -53049,24 +59024,24 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // change that requires such a calculation. - RefreshSupport bool `json:"refreshSupport,omitempty"` -} --type DidChangeConfigurationClientCapabilities struct { // line 10837 +-type DidChangeConfigurationClientCapabilities struct { - // Did change configuration notification supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} - -// The parameters of a change configuration notification. --type DidChangeConfigurationParams struct { // line 4144 +-type DidChangeConfigurationParams struct { - // The actual changed settings - Settings interface{} `json:"settings"` -} --type DidChangeConfigurationRegistrationOptions struct { // line 4158 +-type DidChangeConfigurationRegistrationOptions struct { - Section *OrPSection_workspace_didChangeConfiguration `json:"section,omitempty"` -} - -// The params sent in a change notebook document notification. -// -// @since 3.17.0 --type DidChangeNotebookDocumentParams struct { // line 3974 +-type DidChangeNotebookDocumentParams struct { - // The notebook document that did change. The version number points - // to the version after all provided changes have been applied. If - // only the text document content of a cell changes the notebook version @@ -53090,7 +59065,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The change text document notification's parameters. --type DidChangeTextDocumentParams struct { // line 4287 +-type DidChangeTextDocumentParams struct { - // The document that did change. The version number points - // to the version after all provided content changes have - // been applied. @@ -53109,7 +59084,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // you receive them. - ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"` -} --type DidChangeWatchedFilesClientCapabilities struct { // line 10851 +-type DidChangeWatchedFilesClientCapabilities struct { - // Did change watched files notification supports dynamic registration. Please note - // that the current protocol doesn't support static configuration for file changes - // from the server side. @@ -53122,19 +59097,19 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The watched files change notification's parameters. --type DidChangeWatchedFilesParams struct { // line 4428 +-type DidChangeWatchedFilesParams struct { - // The actual file events. - Changes []FileEvent `json:"changes"` -} - -// Describe options to be used when registered for text document change events. --type DidChangeWatchedFilesRegistrationOptions struct { // line 4445 +-type DidChangeWatchedFilesRegistrationOptions struct { - // The watchers to register. - Watchers []FileSystemWatcher `json:"watchers"` -} - -// The parameters of a `workspace/didChangeWorkspaceFolders` notification. --type DidChangeWorkspaceFoldersParams struct { // line 2185 +-type DidChangeWorkspaceFoldersParams struct { - // The actual workspace folder change event. - Event WorkspaceFoldersChangeEvent `json:"event"` -} @@ -53142,7 +59117,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The params sent in a close notebook document notification. -// -// @since 3.17.0 --type DidCloseNotebookDocumentParams struct { // line 4012 +-type DidCloseNotebookDocumentParams struct { - // The notebook document that got closed. - NotebookDocument NotebookDocumentIdentifier `json:"notebookDocument"` - // The text documents that represent the content @@ -53151,7 +59126,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The parameters sent in a close text document notification --type DidCloseTextDocumentParams struct { // line 4332 +-type DidCloseTextDocumentParams struct { - // The document that was closed. - TextDocument TextDocumentIdentifier `json:"textDocument"` -} @@ -53159,7 +59134,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The params sent in an open notebook document notification. -// -// @since 3.17.0 --type DidOpenNotebookDocumentParams struct { // line 3948 +-type DidOpenNotebookDocumentParams struct { - // The notebook document that got opened. - NotebookDocument NotebookDocument `json:"notebookDocument"` - // The text documents that represent the content @@ -53168,7 +59143,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The parameters sent in an open text document notification --type DidOpenTextDocumentParams struct { // line 4273 +-type DidOpenTextDocumentParams struct { - // The document that was opened. - TextDocument TextDocumentItem `json:"textDocument"` -} @@ -53176,37 +59151,37 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The params sent in a save notebook document notification. -// -// @since 3.17.0 --type DidSaveNotebookDocumentParams struct { // line 3997 +-type DidSaveNotebookDocumentParams struct { - // The notebook document that got saved. - NotebookDocument NotebookDocumentIdentifier `json:"notebookDocument"` -} - -// The parameters sent in a save text document notification --type DidSaveTextDocumentParams struct { // line 4346 +-type DidSaveTextDocumentParams struct { - // The document that was saved. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // Optional the content when saved. Depends on the includeText value - // when the save notification was requested. - Text *string `json:"text,omitempty"` -} --type DocumentColorClientCapabilities struct { // line 11875 +-type DocumentColorClientCapabilities struct { - // Whether implementation supports dynamic registration. If this is set to `true` - // the client supports the new `DocumentColorRegistrationOptions` return value - // for the corresponding server capability as well. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} --type DocumentColorOptions struct { // line 6471 +-type DocumentColorOptions struct { - WorkDoneProgressOptions -} - -// Parameters for a {@link DocumentColorRequest}. --type DocumentColorParams struct { // line 2215 +-type DocumentColorParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - WorkDoneProgressParams - PartialResultParams -} --type DocumentColorRegistrationOptions struct { // line 2261 +-type DocumentColorRegistrationOptions struct { - TextDocumentRegistrationOptions - DocumentColorOptions - StaticRegistrationOptions @@ -53215,7 +59190,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Parameters of the document diagnostic request. -// -// @since 3.17.0 --type DocumentDiagnosticParams struct { // line 3768 +-type DocumentDiagnosticParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The additional identifier provided during registration. @@ -53229,11 +59204,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The document diagnostic report kinds. -// -// @since 3.17.0 --type DocumentDiagnosticReportKind string // line 12722 +-type DocumentDiagnosticReportKind string +- -// A partial result for a document diagnostic report. -// -// @since 3.17.0 --type DocumentDiagnosticReportPartialResult struct { // line 3811 +-type DocumentDiagnosticReportPartialResult struct { - RelatedDocuments map[DocumentURI]interface{} `json:"relatedDocuments"` -} - @@ -53241,20 +59217,20 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// a notebook cell document. -// -// @since 3.17.0 - proposed support for NotebookCellTextDocumentFilter. --type DocumentFilter = Or_DocumentFilter // (alias) line 14093 +-type DocumentFilter = Or_DocumentFilter // (alias) line 14508 -// Client capabilities of a {@link DocumentFormattingRequest}. --type DocumentFormattingClientCapabilities struct { // line 11889 +-type DocumentFormattingClientCapabilities struct { - // Whether formatting supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} - -// Provider options for a {@link DocumentFormattingRequest}. --type DocumentFormattingOptions struct { // line 9221 +-type DocumentFormattingOptions struct { - WorkDoneProgressOptions -} - -// The parameters of a {@link DocumentFormattingRequest}. --type DocumentFormattingParams struct { // line 5727 +-type DocumentFormattingParams struct { - // The document to format. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The format options. @@ -53263,7 +59239,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link DocumentFormattingRequest}. --type DocumentFormattingRegistrationOptions struct { // line 5755 +-type DocumentFormattingRegistrationOptions struct { - TextDocumentRegistrationOptions - DocumentFormattingOptions -} @@ -53271,7 +59247,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A document highlight is a range inside a text document which deserves -// special attention. Usually a document highlight is visualized by changing -// the background color of its range. --type DocumentHighlight struct { // line 5119 +-type DocumentHighlight struct { - // The range this highlight applies to. - Range Range `json:"range"` - // The highlight kind, default is {@link DocumentHighlightKind.Text text}. @@ -53279,38 +59255,39 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Client Capabilities for a {@link DocumentHighlightRequest}. --type DocumentHighlightClientCapabilities struct { // line 11624 +-type DocumentHighlightClientCapabilities struct { - // Whether document highlight supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} - -// A document highlight kind. --type DocumentHighlightKind uint32 // line 13301 +-type DocumentHighlightKind uint32 +- -// Provider options for a {@link DocumentHighlightRequest}. --type DocumentHighlightOptions struct { // line 8955 +-type DocumentHighlightOptions struct { - WorkDoneProgressOptions -} - -// Parameters for a {@link DocumentHighlightRequest}. --type DocumentHighlightParams struct { // line 5098 +-type DocumentHighlightParams struct { - TextDocumentPositionParams - WorkDoneProgressParams - PartialResultParams -} - -// Registration options for a {@link DocumentHighlightRequest}. --type DocumentHighlightRegistrationOptions struct { // line 5142 +-type DocumentHighlightRegistrationOptions struct { - TextDocumentRegistrationOptions - DocumentHighlightOptions -} - -// A document link is a range in a text document that links to an internal or external resource, like another -// text document or a web site. --type DocumentLink struct { // line 5670 +-type DocumentLink struct { - // The range this link applies to. - Range Range `json:"range"` - // The uri this link points to. If missing a resolve request is sent later. -- Target string `json:"target,omitempty"` +- Target *URI `json:"target,omitempty"` - // The tooltip text when you hover over this link. - // - // If a tooltip is provided, is will be displayed in a string that includes instructions on how to @@ -53325,7 +59302,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The client capabilities of a {@link DocumentLinkRequest}. --type DocumentLinkClientCapabilities struct { // line 11850 +-type DocumentLinkClientCapabilities struct { - // Whether document link supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // Whether the client supports the `tooltip` property on `DocumentLink`. @@ -53335,14 +59312,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Provider options for a {@link DocumentLinkRequest}. --type DocumentLinkOptions struct { // line 9148 +-type DocumentLinkOptions struct { - // Document links have a resolve provider as well. - ResolveProvider bool `json:"resolveProvider,omitempty"` - WorkDoneProgressOptions -} - -// The parameters of a {@link DocumentLinkRequest}. --type DocumentLinkParams struct { // line 5646 +-type DocumentLinkParams struct { - // The document to provide document links for. - TextDocument TextDocumentIdentifier `json:"textDocument"` - WorkDoneProgressParams @@ -53350,19 +59327,19 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link DocumentLinkRequest}. --type DocumentLinkRegistrationOptions struct { // line 5712 +-type DocumentLinkRegistrationOptions struct { - TextDocumentRegistrationOptions - DocumentLinkOptions -} - -// Client capabilities of a {@link DocumentOnTypeFormattingRequest}. --type DocumentOnTypeFormattingClientCapabilities struct { // line 11919 +-type DocumentOnTypeFormattingClientCapabilities struct { - // Whether on type formatting supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} - -// Provider options for a {@link DocumentOnTypeFormattingRequest}. --type DocumentOnTypeFormattingOptions struct { // line 9243 +-type DocumentOnTypeFormattingOptions struct { - // A character on which formatting should be triggered, like `{`. - FirstTriggerCharacter string `json:"firstTriggerCharacter"` - // More trigger characters. @@ -53370,7 +59347,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The parameters of a {@link DocumentOnTypeFormattingRequest}. --type DocumentOnTypeFormattingParams struct { // line 5821 +-type DocumentOnTypeFormattingParams struct { - // The document to format. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The position around which the on type formatting should happen. @@ -53387,24 +59364,34 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link DocumentOnTypeFormattingRequest}. --type DocumentOnTypeFormattingRegistrationOptions struct { // line 5859 +-type DocumentOnTypeFormattingRegistrationOptions struct { - TextDocumentRegistrationOptions - DocumentOnTypeFormattingOptions -} - -// Client capabilities of a {@link DocumentRangeFormattingRequest}. --type DocumentRangeFormattingClientCapabilities struct { // line 11904 +-type DocumentRangeFormattingClientCapabilities struct { - // Whether range formatting supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +- // Whether the client supports formatting multiple ranges at once. +- // +- // @since 3.18.0 +- // @proposed +- RangesSupport bool `json:"rangesSupport,omitempty"` -} - -// Provider options for a {@link DocumentRangeFormattingRequest}. --type DocumentRangeFormattingOptions struct { // line 9232 +-type DocumentRangeFormattingOptions struct { +- // Whether the server supports formatting multiple ranges at once. +- // +- // @since 3.18.0 +- // @proposed +- RangesSupport bool `json:"rangesSupport,omitempty"` - WorkDoneProgressOptions -} - -// The parameters of a {@link DocumentRangeFormattingRequest}. --type DocumentRangeFormattingParams struct { // line 5770 +-type DocumentRangeFormattingParams struct { - // The document to format. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The range to format @@ -53415,22 +59402,36 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link DocumentRangeFormattingRequest}. --type DocumentRangeFormattingRegistrationOptions struct { // line 5806 +-type DocumentRangeFormattingRegistrationOptions struct { - TextDocumentRegistrationOptions - DocumentRangeFormattingOptions -} - +-// The parameters of a {@link DocumentRangesFormattingRequest}. +-// +-// @since 3.18.0 +-// @proposed +-type DocumentRangesFormattingParams struct { +- // The document to format. +- TextDocument TextDocumentIdentifier `json:"textDocument"` +- // The ranges to format +- Ranges []Range `json:"ranges"` +- // The format options +- Options FormattingOptions `json:"options"` +- WorkDoneProgressParams +-} +- -// A document selector is the combination of one or many document filters. -// -// @sample `let sel:DocumentSelector = [{ language: 'typescript' }, { language: 'json', pattern: '**∕tsconfig.json' }]`; -// -// The use of a string as a document filter is deprecated @since 3.16.0. --type DocumentSelector = []DocumentFilter // (alias) line 13948 +-type DocumentSelector = []DocumentFilter // (alias) line 14363 -// Represents programming constructs like variables, classes, interfaces etc. -// that appear in a document. Document symbols can be hierarchical and they -// have two ranges: one that encloses its definition and one that points to -// its most interesting range, e.g. the range of an identifier. --type DocumentSymbol struct { // line 5211 +-type DocumentSymbol struct { - // The name of this symbol. Will be displayed in the user interface and therefore must not be - // an empty string or a string only consisting of white spaces. - Name string `json:"name"` @@ -53458,7 +59459,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Client Capabilities for a {@link DocumentSymbolRequest}. --type DocumentSymbolClientCapabilities struct { // line 11639 +-type DocumentSymbolClientCapabilities struct { - // Whether document symbol supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // Specific capabilities for the `SymbolKind` in the @@ -53480,7 +59481,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Provider options for a {@link DocumentSymbolRequest}. --type DocumentSymbolOptions struct { // line 9010 +-type DocumentSymbolOptions struct { - // A human-readable string that is shown when multiple outlines trees - // are shown for the same document. - // @@ -53490,7 +59491,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Parameters for a {@link DocumentSymbolRequest}. --type DocumentSymbolParams struct { // line 5157 +-type DocumentSymbolParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - WorkDoneProgressParams @@ -53498,29 +59499,30 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link DocumentSymbolRequest}. --type DocumentSymbolRegistrationOptions struct { // line 5293 +-type DocumentSymbolRegistrationOptions struct { - TextDocumentRegistrationOptions - DocumentSymbolOptions -} -type DocumentURI string - -// Predefined error codes. --type ErrorCodes int32 // line 12743 +-type ErrorCodes int32 +- -// The client capabilities of a {@link ExecuteCommandRequest}. --type ExecuteCommandClientCapabilities struct { // line 10962 +-type ExecuteCommandClientCapabilities struct { - // Execute command supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} - -// The server capabilities of a {@link ExecuteCommandRequest}. --type ExecuteCommandOptions struct { // line 9291 +-type ExecuteCommandOptions struct { - // The commands to be executed on the server - Commands []string `json:"commands"` - WorkDoneProgressOptions -} - -// The parameters of a {@link ExecuteCommandRequest}. --type ExecuteCommandParams struct { // line 5941 +-type ExecuteCommandParams struct { - // The identifier of the actual command handler. - Command string `json:"command"` - // Arguments that the command should be invoked with. @@ -53529,10 +59531,10 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link ExecuteCommandRequest}. --type ExecuteCommandRegistrationOptions struct { // line 5973 +-type ExecuteCommandRegistrationOptions struct { - ExecuteCommandOptions -} --type ExecutionSummary struct { // line 10162 +-type ExecutionSummary struct { - // A strict monotonically increasing value - // indicating the execution order of a cell - // inside a notebook. @@ -53543,7 +59545,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_CodeActionClientCapabilities_codeActionLiteralSupport_codeActionKind) --type FCodeActionKindPCodeActionLiteralSupport struct { // line 11742 +-type FCodeActionKindPCodeActionLiteralSupport struct { - // The code action kind values the client supports. When this - // property exists the client also guarantees that it will - // handle values outside its set gracefully and falls back @@ -53552,25 +59554,25 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_CompletionList_itemDefaults_editRange_Item1) --type FEditRangePItemDefaults struct { // line 4777 +-type FEditRangePItemDefaults struct { - Insert Range `json:"insert"` - Replace Range `json:"replace"` -} - -// created for Literal (Lit_SemanticTokensClientCapabilities_requests_full_Item1) --type FFullPRequests struct { // line 12205 +-type FFullPRequests struct { - // The client will send the `textDocument/semanticTokens/full/delta` request if - // the server provides a corresponding handler. - Delta bool `json:"delta"` -} - -// created for Literal (Lit_CompletionClientCapabilities_completionItem_insertTextModeSupport) --type FInsertTextModeSupportPCompletionItem struct { // line 11295 +-type FInsertTextModeSupportPCompletionItem struct { - ValueSet []InsertTextMode `json:"valueSet"` -} - -// created for Literal (Lit_SignatureHelpClientCapabilities_signatureInformation_parameterInformation) --type FParameterInformationPSignatureInformation struct { // line 11461 +-type FParameterInformationPSignatureInformation struct { - // The client supports processing label offsets instead of a - // simple label string. - // @@ -53579,17 +59581,17 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_SemanticTokensClientCapabilities_requests_range_Item1) --type FRangePRequests struct { // line 12185 +-type FRangePRequests struct { -} - -// created for Literal (Lit_CompletionClientCapabilities_completionItem_resolveSupport) --type FResolveSupportPCompletionItem struct { // line 11271 +-type FResolveSupportPCompletionItem struct { - // The properties that a client can resolve lazily. - Properties []string `json:"properties"` -} - -// created for Literal (Lit_NotebookDocumentChangeEvent_cells_structure) --type FStructurePCells struct { // line 7487 +-type FStructurePCells struct { - // The change to the cell array. - Array NotebookCellArrayChange `json:"array"` - // Additional opened cell text documents. @@ -53599,17 +59601,19 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_CompletionClientCapabilities_completionItem_tagSupport) --type FTagSupportPCompletionItem struct { // line 11237 +-type FTagSupportPCompletionItem struct { - // The tags supported by the client. - ValueSet []CompletionItemTag `json:"valueSet"` -} --type FailureHandlingKind string // line 13693 +-type FailureHandlingKind string +- -// The file event type --type FileChangeType uint32 // line 13454 +-type FileChangeType uint32 +- -// Represents information on a file/folder create. -// -// @since 3.16.0 --type FileCreate struct { // line 6662 +-type FileCreate struct { - // A file:// URI for the location of the file/folder being created. - URI string `json:"uri"` -} @@ -53617,13 +59621,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Represents information on a file/folder delete. -// -// @since 3.16.0 --type FileDelete struct { // line 6911 +-type FileDelete struct { - // A file:// URI for the location of the file/folder being deleted. - URI string `json:"uri"` -} - -// An event describing a file change. --type FileEvent struct { // line 8480 +-type FileEvent struct { - // The file's uri. - URI DocumentURI `json:"uri"` - // The change type. @@ -53636,7 +59640,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// like renaming a file in the UI. -// -// @since 3.16.0 --type FileOperationClientCapabilities struct { // line 11009 +-type FileOperationClientCapabilities struct { - // Whether the client supports dynamic registration for file requests/notifications. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // The client has support for sending didCreateFiles notifications. @@ -53657,7 +59661,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// the server is interested in receiving. -// -// @since 3.16.0 --type FileOperationFilter struct { // line 6864 +-type FileOperationFilter struct { - // A Uri scheme like `file` or `untitled`. - Scheme string `json:"scheme,omitempty"` - // The actual file operation pattern. @@ -53667,7 +59671,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Options for notifications/requests for user operations on files. -// -// @since 3.16.0 --type FileOperationOptions struct { // line 9965 +-type FileOperationOptions struct { - // The server is interested in receiving didCreateFiles notifications. - DidCreate *FileOperationRegistrationOptions `json:"didCreate,omitempty"` - // The server is interested in receiving willCreateFiles requests. @@ -53686,7 +59690,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// the server is interested in receiving. -// -// @since 3.16.0 --type FileOperationPattern struct { // line 9489 +-type FileOperationPattern struct { - // The glob pattern to match. Glob patterns can have the following syntax: - // - // - `*` to match one or more characters in a path segment @@ -53708,11 +59712,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// both. -// -// @since 3.16.0 --type FileOperationPatternKind string // line 13627 +-type FileOperationPatternKind string +- -// Matching options for the file operation pattern. -// -// @since 3.16.0 --type FileOperationPatternOptions struct { // line 10146 +-type FileOperationPatternOptions struct { - // The pattern should be matched ignoring casing. - IgnoreCase bool `json:"ignoreCase,omitempty"` -} @@ -53720,7 +59725,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The options to register for file operations. -// -// @since 3.16.0 --type FileOperationRegistrationOptions struct { // line 3264 +-type FileOperationRegistrationOptions struct { - // The actual filters. - Filters []FileOperationFilter `json:"filters"` -} @@ -53728,13 +59733,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Represents information on a file/folder rename. -// -// @since 3.16.0 --type FileRename struct { // line 6888 +-type FileRename struct { - // A file:// URI for the original location of the file/folder being renamed. - OldURI string `json:"oldUri"` - // A file:// URI for the new location of the file/folder being renamed. - NewURI string `json:"newUri"` -} --type FileSystemWatcher struct { // line 8502 +-type FileSystemWatcher struct { - // The glob pattern to watch. See {@link GlobPattern glob pattern} for more detail. - // - // @since 3.17.0 support for relative patterns. @@ -53747,7 +59752,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -// Represents a folding range. To be valid, start and end line must be bigger than zero and smaller -// than the number of lines in the document. Clients are free to ignore invalid ranges. --type FoldingRange struct { // line 2415 +-type FoldingRange struct { - // The zero-based start line of the range to fold. The folded area starts after the line's last character. - // To be valid, the end must be zero or larger and smaller than the number of lines in the document. - StartLine uint32 `json:"startLine"` @@ -53769,7 +59774,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // @since 3.17.0 - CollapsedText string `json:"collapsedText,omitempty"` -} --type FoldingRangeClientCapabilities struct { // line 11978 +-type FoldingRangeClientCapabilities struct { - // Whether implementation supports dynamic registration for folding range - // providers. If this is set to `true` the client supports the new - // `FoldingRangeRegistrationOptions` return value for the corresponding @@ -53794,26 +59799,26 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// A set of predefined range kinds. --type FoldingRangeKind string // line 12815 --type FoldingRangeOptions struct { // line 6481 +-type FoldingRangeKind string +-type FoldingRangeOptions struct { - WorkDoneProgressOptions -} - -// Parameters for a {@link FoldingRangeRequest}. --type FoldingRangeParams struct { // line 2391 +-type FoldingRangeParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - WorkDoneProgressParams - PartialResultParams -} --type FoldingRangeRegistrationOptions struct { // line 2474 +-type FoldingRangeRegistrationOptions struct { - TextDocumentRegistrationOptions - FoldingRangeOptions - StaticRegistrationOptions -} - -// Value-object describing what options formatting should use. --type FormattingOptions struct { // line 9169 +-type FormattingOptions struct { - // Size of a tab in spaces. - TabSize uint32 `json:"tabSize"` - // Prefer spaces over tabs. @@ -53835,7 +59840,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A diagnostic report with a full set of problems. -// -// @since 3.17.0 --type FullDocumentDiagnosticReport struct { // line 7235 +-type FullDocumentDiagnosticReport struct { - // A full document diagnostic report. - Kind string `json:"kind"` - // An optional result id. If provided it will @@ -53849,7 +59854,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// General client capabilities. -// -// @since 3.16.0 --type GeneralClientCapabilities struct { // line 10664 +-type GeneralClientCapabilities struct { - // Client capability that signals how the client - // handles stale requests (e.g. a request - // for which the client will not process the response @@ -53889,16 +59894,16 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The glob pattern. Either a string pattern or a relative pattern. -// -// @since 3.17.0 --type GlobPattern = string // (alias) line 14127 +-type GlobPattern = string // (alias) line 14542 -// The result of a hover request. --type Hover struct { // line 4886 +-type Hover struct { - // The hover's content - Contents MarkupContent `json:"contents"` - // An optional range inside the text document that is used to - // visualize the hover, e.g. by changing the background color. - Range Range `json:"range,omitempty"` -} --type HoverClientCapabilities struct { // line 11402 +-type HoverClientCapabilities struct { - // Whether hover supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // Client supports the following content formats for the content @@ -53907,24 +59912,24 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Hover options. --type HoverOptions struct { // line 8776 +-type HoverOptions struct { - WorkDoneProgressOptions -} - -// Parameters for a {@link HoverRequest}. --type HoverParams struct { // line 4869 +-type HoverParams struct { - TextDocumentPositionParams - WorkDoneProgressParams -} - -// Registration options for a {@link HoverRequest}. --type HoverRegistrationOptions struct { // line 4925 +-type HoverRegistrationOptions struct { - TextDocumentRegistrationOptions - HoverOptions -} - -// @since 3.6.0 --type ImplementationClientCapabilities struct { // line 11583 +-type ImplementationClientCapabilities struct { - // Whether implementation supports dynamic registration. If this is set to `true` - // the client supports the new `ImplementationRegistrationOptions` return value - // for the corresponding server capability as well. @@ -53934,15 +59939,15 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // @since 3.14.0 - LinkSupport bool `json:"linkSupport,omitempty"` -} --type ImplementationOptions struct { // line 6333 +-type ImplementationOptions struct { - WorkDoneProgressOptions -} --type ImplementationParams struct { // line 2063 +-type ImplementationParams struct { - TextDocumentPositionParams - WorkDoneProgressParams - PartialResultParams -} --type ImplementationRegistrationOptions struct { // line 2103 +-type ImplementationRegistrationOptions struct { - TextDocumentRegistrationOptions - ImplementationOptions - StaticRegistrationOptions @@ -53950,20 +59955,20 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -// The data type of the ResponseError if the -// initialize request fails. --type InitializeError struct { // line 4126 +-type InitializeError struct { - // Indicates whether the client execute the following retry logic: - // (1) show the message provided by the ResponseError to the user - // (2) user selects retry or cancel - // (3) if user selected retry the initialize method is sent again. - Retry bool `json:"retry"` -} --type InitializeParams struct { // line 4068 +-type InitializeParams struct { - XInitializeParams - WorkspaceFoldersInitializeParams -} - -// The result returned from an initialize request. --type InitializeResult struct { // line 4082 +-type InitializeResult struct { - // The capabilities the language server provides. - Capabilities ServerCapabilities `json:"capabilities"` - // Information about the server. @@ -53971,13 +59976,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // @since 3.15.0 - ServerInfo *PServerInfoMsg_initialize `json:"serverInfo,omitempty"` -} --type InitializedParams struct { // line 4140 +-type InitializedParams struct { -} - -// Inlay hint information. -// -// @since 3.17.0 --type InlayHint struct { // line 3645 +-type InlayHint struct { - // The position of this hint. - Position Position `json:"position"` - // The label of this hint. A human readable string or an array of @@ -54016,7 +60021,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Inlay hint client capabilities. -// -// @since 3.17.0 --type InlayHintClientCapabilities struct { // line 12369 +-type InlayHintClientCapabilities struct { - // Whether inlay hints support dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // Indicates which properties a client can resolve lazily on an inlay @@ -54027,12 +60032,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Inlay hint kinds. -// -// @since 3.17.0 --type InlayHintKind uint32 // line 13033 +-type InlayHintKind uint32 +- -// An inlay hint label part allows for interactive and composite labels -// of inlay hints. -// -// @since 3.17.0 --type InlayHintLabelPart struct { // line 7062 +-type InlayHintLabelPart struct { - // The value of this label part. - Value string `json:"value"` - // The tooltip text when you hover over this label part. Depending on @@ -54061,7 +60067,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Inlay hint options used during static registration. -// -// @since 3.17.0 --type InlayHintOptions struct { // line 7135 +-type InlayHintOptions struct { - // The server provides support to resolve additional - // information for an inlay hint item. - ResolveProvider bool `json:"resolveProvider,omitempty"` @@ -54071,7 +60077,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A parameter literal used in inlay hint requests. -// -// @since 3.17.0 --type InlayHintParams struct { // line 3616 +-type InlayHintParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The document range for which inlay hints should be computed. @@ -54082,7 +60088,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Inlay hint options used during static or dynamic registration. -// -// @since 3.17.0 --type InlayHintRegistrationOptions struct { // line 3746 +-type InlayHintRegistrationOptions struct { - InlayHintOptions - TextDocumentRegistrationOptions - StaticRegistrationOptions @@ -54091,7 +60097,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Client workspace capabilities specific to inlay hints. -// -// @since 3.17.0 --type InlayHintWorkspaceClientCapabilities struct { // line 11095 +-type InlayHintWorkspaceClientCapabilities struct { - // Whether the client implementation supports a refresh request sent from - // the server to the client. - // @@ -54102,6 +60108,86 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - RefreshSupport bool `json:"refreshSupport,omitempty"` -} - +-// Client capabilities specific to inline completions. +-// +-// @since 3.18.0 +-// @proposed +-type InlineCompletionClientCapabilities struct { +- // Whether implementation supports dynamic registration for inline completion providers. +- DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +-} +- +-// Provides information about the context in which an inline completion was requested. +-// +-// @since 3.18.0 +-// @proposed +-type InlineCompletionContext struct { +- // Describes how the inline completion was triggered. +- TriggerKind InlineCompletionTriggerKind `json:"triggerKind"` +- // Provides information about the currently selected item in the autocomplete widget if it is visible. +- SelectedCompletionInfo *SelectedCompletionInfo `json:"selectedCompletionInfo,omitempty"` +-} +- +-// An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. +-// +-// @since 3.18.0 +-// @proposed +-type InlineCompletionItem struct { +- // The text to replace the range with. Must be set. +- InsertText Or_InlineCompletionItem_insertText `json:"insertText"` +- // A text that is used to decide if this inline completion should be shown. When `falsy` the {@link InlineCompletionItem.insertText} is used. +- FilterText string `json:"filterText,omitempty"` +- // The range to replace. Must begin and end on the same line. +- Range *Range `json:"range,omitempty"` +- // An optional {@link Command} that is executed *after* inserting this completion. +- Command *Command `json:"command,omitempty"` +-} +- +-// Represents a collection of {@link InlineCompletionItem inline completion items} to be presented in the editor. +-// +-// @since 3.18.0 +-// @proposed +-type InlineCompletionList struct { +- // The inline completion items +- Items []InlineCompletionItem `json:"items"` +-} +- +-// Inline completion options used during static registration. +-// +-// @since 3.18.0 +-// @proposed +-type InlineCompletionOptions struct { +- WorkDoneProgressOptions +-} +- +-// A parameter literal used in inline completion requests. +-// +-// @since 3.18.0 +-// @proposed +-type InlineCompletionParams struct { +- // Additional information about the context in which inline completions were +- // requested. +- Context InlineCompletionContext `json:"context"` +- TextDocumentPositionParams +- WorkDoneProgressParams +-} +- +-// Inline completion options used during static or dynamic registration. +-// +-// @since 3.18.0 +-// @proposed +-type InlineCompletionRegistrationOptions struct { +- InlineCompletionOptions +- TextDocumentRegistrationOptions +- StaticRegistrationOptions +-} +- +-// Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. +-// +-// @since 3.18.0 +-// @proposed +-type InlineCompletionTriggerKind uint32 +- -// Inline value information can be provided by different means: -// -// - directly as a text value (class InlineValueText). @@ -54111,17 +60197,17 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The InlineValue types combines all inline value types into one type. -// -// @since 3.17.0 --type InlineValue = Or_InlineValue // (alias) line 13861 +-type InlineValue = Or_InlineValue // (alias) line 14276 -// Client capabilities specific to inline values. -// -// @since 3.17.0 --type InlineValueClientCapabilities struct { // line 12353 +-type InlineValueClientCapabilities struct { - // Whether implementation supports dynamic registration for inline value providers. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} - -// @since 3.17.0 --type InlineValueContext struct { // line 6948 +-type InlineValueContext struct { - // The stack frame (as a DAP Id) where the execution has stopped. - FrameID int32 `json:"frameId"` - // The document range where execution has stopped. @@ -54134,7 +60220,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// An optional expression can be used to override the extracted expression. -// -// @since 3.17.0 --type InlineValueEvaluatableExpression struct { // line 7026 +-type InlineValueEvaluatableExpression struct { - // The document range for which the inline value applies. - // The range is used to extract the evaluatable expression from the underlying document. - Range Range `json:"range"` @@ -54145,14 +60231,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Inline value options used during static registration. -// -// @since 3.17.0 --type InlineValueOptions struct { // line 7050 +-type InlineValueOptions struct { - WorkDoneProgressOptions -} - -// A parameter literal used in inline value requests. -// -// @since 3.17.0 --type InlineValueParams struct { // line 3557 +-type InlineValueParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The document range for which inline values should be computed. @@ -54166,7 +60252,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Inline value options used during static or dynamic registration. -// -// @since 3.17.0 --type InlineValueRegistrationOptions struct { // line 3594 +-type InlineValueRegistrationOptions struct { - InlineValueOptions - TextDocumentRegistrationOptions - StaticRegistrationOptions @@ -54175,7 +60261,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Provide inline value as text. -// -// @since 3.17.0 --type InlineValueText struct { // line 6971 +-type InlineValueText struct { - // The document range for which the inline value applies. - Range Range `json:"range"` - // The text of the inline value. @@ -54187,7 +60273,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// An optional variable name can be used to override the extracted name. -// -// @since 3.17.0 --type InlineValueVariableLookup struct { // line 6994 +-type InlineValueVariableLookup struct { - // The document range for which the inline value applies. - // The range is used to extract the variable name from the underlying document. - Range Range `json:"range"` @@ -54200,7 +60286,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Client workspace capabilities specific to inline values. -// -// @since 3.17.0 --type InlineValueWorkspaceClientCapabilities struct { // line 11079 +-type InlineValueWorkspaceClientCapabilities struct { - // Whether the client implementation supports a refresh request sent from the - // server to the client. - // @@ -54214,7 +60300,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A special text edit to provide an insert and a replace operation. -// -// @since 3.16.0 --type InsertReplaceEdit struct { // line 8676 +-type InsertReplaceEdit struct { - // The string to be inserted. - NewText string `json:"newText"` - // The range if the insert is requested @@ -54225,38 +60311,40 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -// Defines whether the insert text in a completion item should be interpreted as -// plain text or a snippet. --type InsertTextFormat uint32 // line 13260 +-type InsertTextFormat uint32 +- -// How whitespace and indentation is handled during completion -// item insertion. -// -// @since 3.16.0 --type InsertTextMode uint32 // line 13280 +-type InsertTextMode uint32 -type LSPAny = interface{} - -// LSP arrays. -// @since 3.17.0 --type LSPArray = []interface{} // (alias) line 13779 --type LSPErrorCodes int32 // line 12783 +-type LSPArray = []interface{} // (alias) line 14194 +-type LSPErrorCodes int32 +- -// LSP object definition. -// @since 3.17.0 --type LSPObject = map[string]LSPAny // (alias) line 14111 +-type LSPObject = map[string]LSPAny // (alias) line 14526 -// Client capabilities for the linked editing range request. -// -// @since 3.16.0 --type LinkedEditingRangeClientCapabilities struct { // line 12305 +-type LinkedEditingRangeClientCapabilities struct { - // Whether implementation supports dynamic registration. If this is set to `true` - // the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - // return value for the corresponding server capability as well. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} --type LinkedEditingRangeOptions struct { // line 6652 +-type LinkedEditingRangeOptions struct { - WorkDoneProgressOptions -} --type LinkedEditingRangeParams struct { // line 3112 +-type LinkedEditingRangeParams struct { - TextDocumentPositionParams - WorkDoneProgressParams -} --type LinkedEditingRangeRegistrationOptions struct { // line 3155 +-type LinkedEditingRangeRegistrationOptions struct { - TextDocumentRegistrationOptions - LinkedEditingRangeOptions - StaticRegistrationOptions @@ -54265,7 +60353,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The result of a linked editing range request. -// -// @since 3.16.0 --type LinkedEditingRanges struct { // line 3128 +-type LinkedEditingRanges struct { - // A list of ranges that can be edited together. The ranges must have - // identical length and contain identical text content. The ranges cannot overlap. - Ranges []Range `json:"ranges"` @@ -54276,13 +60364,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_NotebookDocumentChangeEvent_cells_textContent_Elem) --type Lit_NotebookDocumentChangeEvent_cells_textContent_Elem struct { // line 7545 +-type Lit_NotebookDocumentChangeEvent_cells_textContent_Elem struct { - Document VersionedTextDocumentIdentifier `json:"document"` - Changes []TextDocumentContentChangeEvent `json:"changes"` -} - -// created for Literal (Lit_NotebookDocumentFilter_Item1) --type Lit_NotebookDocumentFilter_Item1 struct { // line 14293 +-type Lit_NotebookDocumentFilter_Item1 struct { - // The type of the enclosing notebook. - NotebookType string `json:"notebookType,omitempty"` - // A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. @@ -54292,7 +60380,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_NotebookDocumentFilter_Item2) --type Lit_NotebookDocumentFilter_Item2 struct { // line 14326 +-type Lit_NotebookDocumentFilter_Item2 struct { - // The type of the enclosing notebook. - NotebookType string `json:"notebookType,omitempty"` - // A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. @@ -54302,12 +60390,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_NotebookDocumentSyncOptions_notebookSelector_Elem_Item0_cells_Elem) --type Lit_NotebookDocumentSyncOptions_notebookSelector_Elem_Item0_cells_Elem struct { // line 9831 +-type Lit_NotebookDocumentSyncOptions_notebookSelector_Elem_Item0_cells_Elem struct { - Language string `json:"language"` -} - -// created for Literal (Lit_NotebookDocumentSyncOptions_notebookSelector_Elem_Item1) --type Lit_NotebookDocumentSyncOptions_notebookSelector_Elem_Item1 struct { // line 9852 +-type Lit_NotebookDocumentSyncOptions_notebookSelector_Elem_Item1 struct { - // The notebook to be synced If a string - // value is provided it matches against the - // notebook type. '*' matches every notebook. @@ -54317,23 +60405,23 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_NotebookDocumentSyncOptions_notebookSelector_Elem_Item1_cells_Elem) --type Lit_NotebookDocumentSyncOptions_notebookSelector_Elem_Item1_cells_Elem struct { // line 9878 +-type Lit_NotebookDocumentSyncOptions_notebookSelector_Elem_Item1_cells_Elem struct { - Language string `json:"language"` -} - -// created for Literal (Lit_PrepareRenameResult_Item2) --type Lit_PrepareRenameResult_Item2 struct { // line 13932 +-type Lit_PrepareRenameResult_Item2 struct { - DefaultBehavior bool `json:"defaultBehavior"` -} - -// created for Literal (Lit_TextDocumentContentChangeEvent_Item1) --type Lit_TextDocumentContentChangeEvent_Item1 struct { // line 14040 +-type Lit_TextDocumentContentChangeEvent_Item1 struct { - // The new text of the whole document. - Text string `json:"text"` -} - -// created for Literal (Lit_TextDocumentFilter_Item2) --type Lit_TextDocumentFilter_Item2 struct { // line 14217 +-type Lit_TextDocumentFilter_Item2 struct { - // A language id, like `typescript`. - Language string `json:"language,omitempty"` - // A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. @@ -54344,14 +60432,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -// Represents a location inside a resource, such as a line -// inside a text file. --type Location struct { // line 2083 +-type Location struct { - URI DocumentURI `json:"uri"` - Range Range `json:"range"` -} - -// Represents the connection of two locations. Provides additional metadata over normal {@link Location locations}, -// including an origin range. --type LocationLink struct { // line 6272 +-type LocationLink struct { - // Span of the origin of this link. - // - // Used as the underlined span for mouse interaction. Defaults to the word range at @@ -54369,13 +60457,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The log message parameters. --type LogMessageParams struct { // line 4251 +-type LogMessageParams struct { - // The message type. See {@link MessageType} - Type MessageType `json:"type"` - // The actual message. - Message string `json:"message"` -} --type LogTraceParams struct { // line 6159 +-type LogTraceParams struct { - Message string `json:"message"` - Verbose string `json:"verbose,omitempty"` -} @@ -54383,7 +60471,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Client capabilities specific to the used markdown parser. -// -// @since 3.16.0 --type MarkdownClientCapabilities struct { // line 12524 +-type MarkdownClientCapabilities struct { - // The name of the parser. - Parser string `json:"parser"` - // The version of the parser. @@ -54407,7 +60495,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// -// Note that markdown strings will be sanitized - that means html will be escaped. -// @deprecated use MarkupContent instead. --type MarkedString = Or_MarkedString // (alias) line 14058 +-type MarkedString = Or_MarkedString // (alias) line 14473 -// A `MarkupContent` literal represents a string value which content is interpreted base on its -// kind flag. Currently the protocol supports `plaintext` and `markdown` as markup kinds. -// @@ -54432,7 +60520,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// -// *Please Note* that clients might sanitize the return markdown. A client could decide to -// remove HTML from the markdown to avoid script execution. --type MarkupContent struct { // line 7113 +-type MarkupContent struct { - // The type of the Markup - Kind MarkupKind `json:"kind"` - // The content itself @@ -54444,18 +60532,19 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// -// Please note that `MarkupKinds` must not start with a `$`. This kinds -// are reserved for internal usage. --type MarkupKind string // line 13407 --type MessageActionItem struct { // line 4238 +-type MarkupKind string +-type MessageActionItem struct { - // A short title like 'Retry', 'Open Log' etc. - Title string `json:"title"` -} - -// The message type --type MessageType uint32 // line 13054 +-type MessageType uint32 +- -// Moniker definition to match LSIF 0.5 moniker definition. -// -// @since 3.16.0 --type Moniker struct { // line 3338 +-type Moniker struct { - // The scheme of the moniker. For example tsc or .Net - Scheme string `json:"scheme"` - // The identifier of the moniker. The value is opaque in LSIF however @@ -54470,7 +60559,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Client capabilities specific to the moniker request. -// -// @since 3.16.0 --type MonikerClientCapabilities struct { // line 12321 +-type MonikerClientCapabilities struct { - // Whether moniker supports dynamic registration. If this is set to `true` - // the client supports the new `MonikerRegistrationOptions` return value - // for the corresponding server capability as well. @@ -54480,28 +60569,28 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The moniker kind. -// -// @since 3.16.0 --type MonikerKind string // line 13007 --type MonikerOptions struct { // line 6926 +-type MonikerKind string +-type MonikerOptions struct { - WorkDoneProgressOptions -} --type MonikerParams struct { // line 3318 +-type MonikerParams struct { - TextDocumentPositionParams - WorkDoneProgressParams - PartialResultParams -} --type MonikerRegistrationOptions struct { // line 3378 +-type MonikerRegistrationOptions struct { - TextDocumentRegistrationOptions - MonikerOptions -} - -// created for Literal (Lit_MarkedString_Item1) --type Msg_MarkedString struct { // line 14068 +-type Msg_MarkedString struct { - Language string `json:"language"` - Value string `json:"value"` -} - -// created for Literal (Lit_NotebookDocumentFilter_Item0) --type Msg_NotebookDocumentFilter struct { // line 14260 +-type Msg_NotebookDocumentFilter struct { - // The type of the enclosing notebook. - NotebookType string `json:"notebookType"` - // A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. @@ -54511,13 +60600,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_PrepareRenameResult_Item1) --type Msg_PrepareRename2Gn struct { // line 13911 +-type Msg_PrepareRename2Gn struct { - Range Range `json:"range"` - Placeholder string `json:"placeholder"` -} - -// created for Literal (Lit_TextDocumentContentChangeEvent_Item0) --type Msg_TextDocumentContentChangeEvent struct { // line 14008 +-type Msg_TextDocumentContentChangeEvent struct { - // The range of the document that changed. - Range *Range `json:"range"` - // The optional length of the range that got replaced. @@ -54529,7 +60618,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_TextDocumentFilter_Item1) --type Msg_TextDocumentFilter struct { // line 14184 +-type Msg_TextDocumentFilter struct { - // A language id, like `typescript`. - Language string `json:"language,omitempty"` - // A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. @@ -54539,7 +60628,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit__InitializeParams_clientInfo) --type Msg_XInitializeParams_clientInfo struct { // line 7673 +-type Msg_XInitializeParams_clientInfo struct { - // The name of the client as defined by the client. - Name string `json:"name"` - // The client's version as defined by the client. @@ -54553,7 +60642,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// notebook cell or the cell's text document. -// -// @since 3.17.0 --type NotebookCell struct { // line 9598 +-type NotebookCell struct { - // The cell's kind - Kind NotebookCellKind `json:"kind"` - // The URI of the cell's text document @@ -54572,7 +60661,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// array from state S to S'. -// -// @since 3.17.0 --type NotebookCellArrayChange struct { // line 9639 +-type NotebookCellArrayChange struct { - // The start oftest of the cell that changed. - Start uint32 `json:"start"` - // The deleted cells @@ -54584,12 +60673,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A notebook cell kind. -// -// @since 3.17.0 --type NotebookCellKind uint32 // line 13648 +-type NotebookCellKind uint32 +- -// A notebook cell text document filter denotes a cell text -// document by different properties. -// -// @since 3.17.0 --type NotebookCellTextDocumentFilter struct { // line 10113 +-type NotebookCellTextDocumentFilter struct { - // A filter that matches against the notebook - // containing the notebook cell. If a string - // value is provided it matches against the @@ -54605,7 +60695,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A notebook document. -// -// @since 3.17.0 --type NotebookDocument struct { // line 7354 +-type NotebookDocument struct { - // The notebook document's uri. - URI URI `json:"uri"` - // The type of the notebook. @@ -54625,7 +60715,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A change event for a notebook document. -// -// @since 3.17.0 --type NotebookDocumentChangeEvent struct { // line 7466 +-type NotebookDocumentChangeEvent struct { - // The changed meta data if any. - // - // Note: should always be an object literal (e.g. LSPObject) @@ -54637,7 +60727,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Capabilities specific to the notebook document support. -// -// @since 3.17.0 --type NotebookDocumentClientCapabilities struct { // line 10613 +-type NotebookDocumentClientCapabilities struct { - // Capabilities specific to notebook document synchronization - // - // @since 3.17.0 @@ -54649,11 +60739,11 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// against the notebook's URI (same as with documents) -// -// @since 3.17.0 --type NotebookDocumentFilter = Msg_NotebookDocumentFilter // (alias) line 14254 +-type NotebookDocumentFilter = Msg_NotebookDocumentFilter // (alias) line 14669 -// A literal to identify a notebook document in the client. -// -// @since 3.17.0 --type NotebookDocumentIdentifier struct { // line 7582 +-type NotebookDocumentIdentifier struct { - // The notebook document's uri. - URI URI `json:"uri"` -} @@ -54661,7 +60751,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Notebook specific client capabilities. -// -// @since 3.17.0 --type NotebookDocumentSyncClientCapabilities struct { // line 12433 +-type NotebookDocumentSyncClientCapabilities struct { - // Whether implementation supports dynamic registration. If this is - // set to `true` the client supports the new - // `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` @@ -54684,7 +60774,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// cell will be synced. -// -// @since 3.17.0 --type NotebookDocumentSyncOptions struct { // line 9795 +-type NotebookDocumentSyncOptions struct { - // The notebooks to be synced - NotebookSelector []PNotebookSelectorPNotebookDocumentSync `json:"notebookSelector"` - // Whether save notification should be forwarded to @@ -54695,13 +60785,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Registration options specific to a notebook. -// -// @since 3.17.0 --type NotebookDocumentSyncRegistrationOptions struct { // line 9915 +-type NotebookDocumentSyncRegistrationOptions struct { - NotebookDocumentSyncOptions - StaticRegistrationOptions -} - -// A text document identifier to optionally denote a specific version of a text document. --type OptionalVersionedTextDocumentIdentifier struct { // line 9343 +-type OptionalVersionedTextDocumentIdentifier struct { - // The version number of this document. If a versioned text document identifier - // is sent from the server to the client and the file is not open in the editor - // (the server has not received an open notification before) the server can send @@ -54712,307 +60802,322 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Or [FEditRangePItemDefaults Range] --type OrFEditRangePItemDefaults struct { // line 4770 +-type OrFEditRangePItemDefaults struct { - Value interface{} `json:"value"` -} - -// created for Or [NotebookDocumentFilter string] --type OrFNotebookPNotebookSelector struct { // line 9812 +-type OrFNotebookPNotebookSelector struct { - Value interface{} `json:"value"` -} - -// created for Or [Location PLocationMsg_workspace_symbol] --type OrPLocation_workspace_symbol struct { // line 5521 +-type OrPLocation_workspace_symbol struct { - Value interface{} `json:"value"` -} - -// created for Or [[]string string] --type OrPSection_workspace_didChangeConfiguration struct { // line 4164 +-type OrPSection_workspace_didChangeConfiguration struct { - Value interface{} `json:"value"` -} - -// created for Or [MarkupContent string] --type OrPTooltipPLabel struct { // line 7076 +-type OrPTooltipPLabel struct { - Value interface{} `json:"value"` -} - -// created for Or [MarkupContent string] --type OrPTooltip_textDocument_inlayHint struct { // line 3700 +-type OrPTooltip_textDocument_inlayHint struct { - Value interface{} `json:"value"` -} - -// created for Or [int32 string] --type Or_CancelParams_id struct { // line 6185 +-type Or_CancelParams_id struct { - Value interface{} `json:"value"` -} - -// created for Or [MarkupContent string] --type Or_CompletionItem_documentation struct { // line 4583 +-type Or_CompletionItem_documentation struct { - Value interface{} `json:"value"` -} - -// created for Or [InsertReplaceEdit TextEdit] --type Or_CompletionItem_textEdit struct { // line 4666 +-type Or_CompletionItem_textEdit struct { - Value interface{} `json:"value"` -} - -// created for Or [Location []Location] --type Or_Definition struct { // line 13754 +-type Or_Definition struct { - Value interface{} `json:"value"` -} - -// created for Or [int32 string] --type Or_Diagnostic_code struct { // line 8548 +-type Or_Diagnostic_code struct { - Value interface{} `json:"value"` -} - -// created for Or [RelatedFullDocumentDiagnosticReport RelatedUnchangedDocumentDiagnosticReport] --type Or_DocumentDiagnosticReport struct { // line 13886 +-type Or_DocumentDiagnosticReport struct { - Value interface{} `json:"value"` -} - -// created for Or [FullDocumentDiagnosticReport UnchangedDocumentDiagnosticReport] --type Or_DocumentDiagnosticReportPartialResult_relatedDocuments_Value struct { // line 3823 +-type Or_DocumentDiagnosticReportPartialResult_relatedDocuments_Value struct { - Value interface{} `json:"value"` -} - -// created for Or [NotebookCellTextDocumentFilter TextDocumentFilter] --type Or_DocumentFilter struct { // line 14096 +-type Or_DocumentFilter struct { - Value interface{} `json:"value"` -} - -// created for Or [MarkedString MarkupContent []MarkedString] --type Or_Hover_contents struct { // line 4892 +-type Or_Hover_contents struct { - Value interface{} `json:"value"` -} - -// created for Or [[]InlayHintLabelPart string] --type Or_InlayHint_label struct { // line 3659 +-type Or_InlayHint_label struct { +- Value interface{} `json:"value"` +-} +- +-// created for Or [StringValue string] +-type Or_InlineCompletionItem_insertText struct { - Value interface{} `json:"value"` -} - -// created for Or [InlineValueEvaluatableExpression InlineValueText InlineValueVariableLookup] --type Or_InlineValue struct { // line 13864 +-type Or_InlineValue struct { - Value interface{} `json:"value"` -} - -// created for Or [Msg_MarkedString string] --type Or_MarkedString struct { // line 14061 +-type Or_MarkedString struct { - Value interface{} `json:"value"` -} - -// created for Or [NotebookDocumentFilter string] --type Or_NotebookCellTextDocumentFilter_notebook struct { // line 10119 +-type Or_NotebookCellTextDocumentFilter_notebook struct { - Value interface{} `json:"value"` -} - -// created for Or [NotebookDocumentFilter string] --type Or_NotebookDocumentSyncOptions_notebookSelector_Elem_Item1_notebook struct { // line 9858 +-type Or_NotebookDocumentSyncOptions_notebookSelector_Elem_Item1_notebook struct { - Value interface{} `json:"value"` -} - -// created for Or [FullDocumentDiagnosticReport UnchangedDocumentDiagnosticReport] --type Or_RelatedFullDocumentDiagnosticReport_relatedDocuments_Value struct { // line 7169 +-type Or_RelatedFullDocumentDiagnosticReport_relatedDocuments_Value struct { - Value interface{} `json:"value"` -} - -// created for Or [FullDocumentDiagnosticReport UnchangedDocumentDiagnosticReport] --type Or_RelatedUnchangedDocumentDiagnosticReport_relatedDocuments_Value struct { // line 7208 +-type Or_RelatedUnchangedDocumentDiagnosticReport_relatedDocuments_Value struct { - Value interface{} `json:"value"` -} - -// created for Or [URI WorkspaceFolder] --type Or_RelativePattern_baseUri struct { // line 10742 +-type Or_RelativePattern_baseUri struct { - Value interface{} `json:"value"` -} - -// created for Or [CodeAction Command] --type Or_Result_textDocument_codeAction_Item0_Elem struct { // line 1372 +-type Or_Result_textDocument_codeAction_Item0_Elem struct { +- Value interface{} `json:"value"` +-} +- +-// created for Or [InlineCompletionList []InlineCompletionItem] +-type Or_Result_textDocument_inlineCompletion struct { - Value interface{} `json:"value"` -} - -// created for Or [FFullPRequests bool] --type Or_SemanticTokensClientCapabilities_requests_full struct { // line 12198 +-type Or_SemanticTokensClientCapabilities_requests_full struct { - Value interface{} `json:"value"` -} - -// created for Or [FRangePRequests bool] --type Or_SemanticTokensClientCapabilities_requests_range struct { // line 12178 +-type Or_SemanticTokensClientCapabilities_requests_range struct { - Value interface{} `json:"value"` -} - -// created for Or [PFullESemanticTokensOptions bool] --type Or_SemanticTokensOptions_full struct { // line 6580 +-type Or_SemanticTokensOptions_full struct { - Value interface{} `json:"value"` -} - -// created for Or [PRangeESemanticTokensOptions bool] --type Or_SemanticTokensOptions_range struct { // line 6560 +-type Or_SemanticTokensOptions_range struct { - Value interface{} `json:"value"` -} - -// created for Or [CallHierarchyOptions CallHierarchyRegistrationOptions bool] --type Or_ServerCapabilities_callHierarchyProvider struct { // line 8228 +-type Or_ServerCapabilities_callHierarchyProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [CodeActionOptions bool] --type Or_ServerCapabilities_codeActionProvider struct { // line 8036 +-type Or_ServerCapabilities_codeActionProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [DocumentColorOptions DocumentColorRegistrationOptions bool] --type Or_ServerCapabilities_colorProvider struct { // line 8072 +-type Or_ServerCapabilities_colorProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [DeclarationOptions DeclarationRegistrationOptions bool] --type Or_ServerCapabilities_declarationProvider struct { // line 7898 +-type Or_ServerCapabilities_declarationProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [DefinitionOptions bool] --type Or_ServerCapabilities_definitionProvider struct { // line 7920 +-type Or_ServerCapabilities_definitionProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [DiagnosticOptions DiagnosticRegistrationOptions] --type Or_ServerCapabilities_diagnosticProvider struct { // line 8385 +-type Or_ServerCapabilities_diagnosticProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [DocumentFormattingOptions bool] --type Or_ServerCapabilities_documentFormattingProvider struct { // line 8112 +-type Or_ServerCapabilities_documentFormattingProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [DocumentHighlightOptions bool] --type Or_ServerCapabilities_documentHighlightProvider struct { // line 8000 +-type Or_ServerCapabilities_documentHighlightProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [DocumentRangeFormattingOptions bool] --type Or_ServerCapabilities_documentRangeFormattingProvider struct { // line 8130 +-type Or_ServerCapabilities_documentRangeFormattingProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [DocumentSymbolOptions bool] --type Or_ServerCapabilities_documentSymbolProvider struct { // line 8018 +-type Or_ServerCapabilities_documentSymbolProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [FoldingRangeOptions FoldingRangeRegistrationOptions bool] --type Or_ServerCapabilities_foldingRangeProvider struct { // line 8175 +-type Or_ServerCapabilities_foldingRangeProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [HoverOptions bool] --type Or_ServerCapabilities_hoverProvider struct { // line 7871 +-type Or_ServerCapabilities_hoverProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [ImplementationOptions ImplementationRegistrationOptions bool] --type Or_ServerCapabilities_implementationProvider struct { // line 7960 +-type Or_ServerCapabilities_implementationProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [InlayHintOptions InlayHintRegistrationOptions bool] --type Or_ServerCapabilities_inlayHintProvider struct { // line 8362 +-type Or_ServerCapabilities_inlayHintProvider struct { +- Value interface{} `json:"value"` +-} +- +-// created for Or [InlineCompletionOptions bool] +-type Or_ServerCapabilities_inlineCompletionProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [InlineValueOptions InlineValueRegistrationOptions bool] --type Or_ServerCapabilities_inlineValueProvider struct { // line 8339 +-type Or_ServerCapabilities_inlineValueProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [LinkedEditingRangeOptions LinkedEditingRangeRegistrationOptions bool] --type Or_ServerCapabilities_linkedEditingRangeProvider struct { // line 8251 +-type Or_ServerCapabilities_linkedEditingRangeProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [MonikerOptions MonikerRegistrationOptions bool] --type Or_ServerCapabilities_monikerProvider struct { // line 8293 +-type Or_ServerCapabilities_monikerProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [NotebookDocumentSyncOptions NotebookDocumentSyncRegistrationOptions] --type Or_ServerCapabilities_notebookDocumentSync struct { // line 7843 +-type Or_ServerCapabilities_notebookDocumentSync struct { - Value interface{} `json:"value"` -} - -// created for Or [ReferenceOptions bool] --type Or_ServerCapabilities_referencesProvider struct { // line 7982 +-type Or_ServerCapabilities_referencesProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [RenameOptions bool] --type Or_ServerCapabilities_renameProvider struct { // line 8157 +-type Or_ServerCapabilities_renameProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [SelectionRangeOptions SelectionRangeRegistrationOptions bool] --type Or_ServerCapabilities_selectionRangeProvider struct { // line 8197 +-type Or_ServerCapabilities_selectionRangeProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [SemanticTokensOptions SemanticTokensRegistrationOptions] --type Or_ServerCapabilities_semanticTokensProvider struct { // line 8274 +-type Or_ServerCapabilities_semanticTokensProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [TextDocumentSyncKind TextDocumentSyncOptions] --type Or_ServerCapabilities_textDocumentSync struct { // line 7825 +-type Or_ServerCapabilities_textDocumentSync struct { - Value interface{} `json:"value"` -} - -// created for Or [TypeDefinitionOptions TypeDefinitionRegistrationOptions bool] --type Or_ServerCapabilities_typeDefinitionProvider struct { // line 7938 +-type Or_ServerCapabilities_typeDefinitionProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [TypeHierarchyOptions TypeHierarchyRegistrationOptions bool] --type Or_ServerCapabilities_typeHierarchyProvider struct { // line 8316 +-type Or_ServerCapabilities_typeHierarchyProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [WorkspaceSymbolOptions bool] --type Or_ServerCapabilities_workspaceSymbolProvider struct { // line 8094 +-type Or_ServerCapabilities_workspaceSymbolProvider struct { - Value interface{} `json:"value"` -} - -// created for Or [MarkupContent string] --type Or_SignatureInformation_documentation struct { // line 8842 +-type Or_SignatureInformation_documentation struct { - Value interface{} `json:"value"` -} - -// created for Or [AnnotatedTextEdit TextEdit] --type Or_TextDocumentEdit_edits_Elem struct { // line 6693 +-type Or_TextDocumentEdit_edits_Elem struct { - Value interface{} `json:"value"` -} - -// created for Or [SaveOptions bool] --type Or_TextDocumentSyncOptions_save struct { // line 9778 +-type Or_TextDocumentSyncOptions_save struct { - Value interface{} `json:"value"` -} - -// created for Or [WorkspaceFullDocumentDiagnosticReport WorkspaceUnchangedDocumentDiagnosticReport] --type Or_WorkspaceDocumentDiagnosticReport struct { // line 13987 +-type Or_WorkspaceDocumentDiagnosticReport struct { - Value interface{} `json:"value"` -} - -// created for Or [CreateFile DeleteFile RenameFile TextDocumentEdit] --type Or_WorkspaceEdit_documentChanges_Elem struct { // line 3220 +-type Or_WorkspaceEdit_documentChanges_Elem struct { - Value interface{} `json:"value"` -} - -// created for Or [Declaration []DeclarationLink] --type Or_textDocument_declaration struct { // line 249 +-type Or_textDocument_declaration struct { - Value interface{} `json:"value"` -} - -// created for Literal (Lit_NotebookDocumentChangeEvent_cells) --type PCellsPChange struct { // line 7481 +-type PCellsPChange struct { - // Changes to the cell structure to add or - // remove cells. - Structure *FStructurePCells `json:"structure,omitempty"` @@ -55024,7 +61129,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_WorkspaceEditClientCapabilities_changeAnnotationSupport) --type PChangeAnnotationSupportPWorkspaceEdit struct { // line 10816 +-type PChangeAnnotationSupportPWorkspaceEdit struct { - // Whether the client groups edits with equal labels into tree nodes, - // for instance all edits labelled with "Changes in Strings" would - // be a tree node. @@ -55032,14 +61137,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_CodeActionClientCapabilities_codeActionLiteralSupport) --type PCodeActionLiteralSupportPCodeAction struct { // line 11736 +-type PCodeActionLiteralSupportPCodeAction struct { - // The code action kind is support with the following value - // set. - CodeActionKind FCodeActionKindPCodeActionLiteralSupport `json:"codeActionKind"` -} - -// created for Literal (Lit_CompletionClientCapabilities_completionItemKind) --type PCompletionItemKindPCompletion struct { // line 11334 +-type PCompletionItemKindPCompletion struct { - // The completion item kind values the client supports. When this - // property exists the client also guarantees that it will - // handle values outside its set gracefully and falls back @@ -55052,7 +61157,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_CompletionClientCapabilities_completionItem) --type PCompletionItemPCompletion struct { // line 11183 +-type PCompletionItemPCompletion struct { - // Client supports snippets as insert text. - // - // A snippet can define tab stops and placeholders with `$1`, `$2` @@ -55101,7 +61206,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_CompletionOptions_completionItem) --type PCompletionItemPCompletionProvider struct { // line 8747 +-type PCompletionItemPCompletionProvider struct { - // The server has support for completion item label - // details (see also `CompletionItemLabelDetails`) when - // receiving a completion item in a resolve call. @@ -55111,7 +61216,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_CompletionClientCapabilities_completionList) --type PCompletionListPCompletion struct { // line 11376 +-type PCompletionListPCompletion struct { - // The client supports the following itemDefaults on - // a completion list. - // @@ -55124,7 +61229,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_CodeAction_disabled) --type PDisabledMsg_textDocument_codeAction struct { // line 5427 +-type PDisabledMsg_textDocument_codeAction struct { - // Human readable description of why the code action is currently disabled. - // - // This is displayed in the code actions UI. @@ -55132,7 +61237,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_FoldingRangeClientCapabilities_foldingRangeKind) --type PFoldingRangeKindPFoldingRange struct { // line 12011 +-type PFoldingRangeKindPFoldingRange struct { - // The folding range kind values the client supports. When this - // property exists the client also guarantees that it will - // handle values outside its set gracefully and falls back @@ -55141,7 +61246,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_FoldingRangeClientCapabilities_foldingRange) --type PFoldingRangePFoldingRange struct { // line 12036 +-type PFoldingRangePFoldingRange struct { - // If set, the client signals that it supports setting collapsedText on - // folding ranges to display custom labels instead of the default text. - // @@ -55150,13 +61255,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_SemanticTokensOptions_full_Item1) --type PFullESemanticTokensOptions struct { // line 6587 +-type PFullESemanticTokensOptions struct { - // The server supports deltas for full documents. - Delta bool `json:"delta"` -} - -// created for Literal (Lit_CompletionList_itemDefaults) --type PItemDefaultsMsg_textDocument_completion struct { // line 4751 +-type PItemDefaultsMsg_textDocument_completion struct { - // A default commit character set. - // - // @since 3.17.0 @@ -55180,12 +61285,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_WorkspaceSymbol_location_Item1) --type PLocationMsg_workspace_symbol struct { // line 5528 +-type PLocationMsg_workspace_symbol struct { - URI DocumentURI `json:"uri"` -} - -// created for Literal (Lit_ShowMessageRequestClientCapabilities_messageActionItem) --type PMessageActionItemPShowMessage struct { // line 12464 +-type PMessageActionItemPShowMessage struct { - // Whether the client supports additional attributes which - // are preserved and send back to the server in the - // request's response. @@ -55193,7 +61298,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_NotebookDocumentSyncOptions_notebookSelector_Elem_Item0) --type PNotebookSelectorPNotebookDocumentSync struct { // line 9806 +-type PNotebookSelectorPNotebookDocumentSync struct { - // The notebook to be synced If a string - // value is provided it matches against the - // notebook type. '*' matches every notebook. @@ -55203,11 +61308,11 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_SemanticTokensOptions_range_Item1) --type PRangeESemanticTokensOptions struct { // line 6567 +-type PRangeESemanticTokensOptions struct { -} - -// created for Literal (Lit_SemanticTokensClientCapabilities_requests) --type PRequestsPSemanticTokens struct { // line 12172 +-type PRequestsPSemanticTokens struct { - // The client will send the `textDocument/semanticTokens/range` request if - // the server provides a corresponding handler. - Range Or_SemanticTokensClientCapabilities_requests_range `json:"range"` @@ -55217,26 +61322,26 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_CodeActionClientCapabilities_resolveSupport) --type PResolveSupportPCodeAction struct { // line 11801 +-type PResolveSupportPCodeAction struct { - // The properties that a client can resolve lazily. - Properties []string `json:"properties"` -} - -// created for Literal (Lit_InlayHintClientCapabilities_resolveSupport) --type PResolveSupportPInlayHint struct { // line 12384 +-type PResolveSupportPInlayHint struct { - // The properties that a client can resolve lazily. - Properties []string `json:"properties"` -} - -// created for Literal (Lit_WorkspaceSymbolClientCapabilities_resolveSupport) --type PResolveSupportPSymbol struct { // line 10938 +-type PResolveSupportPSymbol struct { - // The properties that a client can resolve lazily. Usually - // `location.range` - Properties []string `json:"properties"` -} - -// created for Literal (Lit_InitializeResult_serverInfo) --type PServerInfoMsg_initialize struct { // line 4096 +-type PServerInfoMsg_initialize struct { - // The name of the server as defined by the server. - Name string `json:"name"` - // The server's version as defined by the server. @@ -55244,7 +61349,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_SignatureHelpClientCapabilities_signatureInformation) --type PSignatureInformationPSignatureHelp struct { // line 11443 +-type PSignatureInformationPSignatureHelp struct { - // Client supports the following content formats for the documentation - // property. The order describes the preferred format of the client. - DocumentationFormat []MarkupKind `json:"documentationFormat,omitempty"` @@ -55258,7 +61363,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_GeneralClientCapabilities_staleRequestSupport) --type PStaleRequestSupportPGeneral struct { // line 10670 +-type PStaleRequestSupportPGeneral struct { - // The client will actively cancel the request. - Cancel bool `json:"cancel"` - // The list of requests for which the client @@ -55268,7 +61373,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_DocumentSymbolClientCapabilities_symbolKind) --type PSymbolKindPDocumentSymbol struct { // line 11654 +-type PSymbolKindPDocumentSymbol struct { - // The symbol kind values the client supports. When this - // property exists the client also guarantees that it will - // handle values outside its set gracefully and falls back @@ -55281,7 +61386,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_WorkspaceSymbolClientCapabilities_symbolKind) --type PSymbolKindPSymbol struct { // line 10890 +-type PSymbolKindPSymbol struct { - // The symbol kind values the client supports. When this - // property exists the client also guarantees that it will - // handle values outside its set gracefully and falls back @@ -55294,35 +61399,35 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_DocumentSymbolClientCapabilities_tagSupport) --type PTagSupportPDocumentSymbol struct { // line 11687 +-type PTagSupportPDocumentSymbol struct { - // The tags supported by the client. - ValueSet []SymbolTag `json:"valueSet"` -} - -// created for Literal (Lit_PublishDiagnosticsClientCapabilities_tagSupport) --type PTagSupportPPublishDiagnostics struct { // line 12087 +-type PTagSupportPPublishDiagnostics struct { - // The tags supported by the client. - ValueSet []DiagnosticTag `json:"valueSet"` -} - -// created for Literal (Lit_WorkspaceSymbolClientCapabilities_tagSupport) --type PTagSupportPSymbol struct { // line 10914 +-type PTagSupportPSymbol struct { - // The tags supported by the client. - ValueSet []SymbolTag `json:"valueSet"` -} - -// The parameters of a configuration request. --type ParamConfiguration struct { // line 2199 +-type ParamConfiguration struct { - Items []ConfigurationItem `json:"items"` -} --type ParamInitialize struct { // line 4068 +-type ParamInitialize struct { - XInitializeParams - WorkspaceFoldersInitializeParams -} - -// Represents a parameter of a callable-signature. A parameter can -// have a label and a doc-comment. --type ParameterInformation struct { // line 10063 +-type ParameterInformation struct { - // The label of this parameter information. - // - // Either a string or an inclusive start and exclusive end offsets within its containing @@ -55336,7 +61441,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // in the UI but can be omitted. - Documentation string `json:"documentation,omitempty"` -} --type PartialResultParams struct { // line 6258 +-type PartialResultParams struct { - // An optional token that a server can use to report partial results (e.g. streaming) to - // the client. - PartialResultToken *ProgressToken `json:"partialResultToken,omitempty"` @@ -55352,7 +61457,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) -// -// @since 3.17.0 --type Pattern = string // (alias) line 14363 +-type Pattern = string // (alias) line 14778 -// Position in a text document expressed as zero-based line and character -// offset. Prior to 3.17 the offsets were always based on a UTF-16 string -// representation. So a string of the form `a𐐀b` the character offset of the @@ -55380,7 +61485,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// that denotes `\r|\n` or `\n|` where `|` represents the character offset. -// -// @since 3.17.0 - support for negotiated position encoding. --type Position struct { // line 6501 +-type Position struct { - // Line position in a document (zero-based). - // - // If a line number is greater than the number of lines in a document, it defaults back to the number of lines in the document. @@ -55399,18 +61504,19 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A set of predefined position encoding kinds. -// -// @since 3.17.0 --type PositionEncodingKind string // line 13427 +-type PositionEncodingKind string -type PrepareRename2Gn = Msg_PrepareRename2Gn // (alias) line 13927 --type PrepareRenameParams struct { // line 5925 +-type PrepareRenameParams struct { - TextDocumentPositionParams - WorkDoneProgressParams -} -type PrepareRenameResult = Msg_PrepareRename2Gn // (alias) line 13927 --type PrepareSupportDefaultBehavior uint32 // line 13722 +-type PrepareSupportDefaultBehavior uint32 +- -// A previous result id in a workspace pull request. -// -// @since 3.17.0 --type PreviousResultID struct { // line 7331 +-type PreviousResultID struct { - // The URI for which the client knowns a - // result id. - URI DocumentURI `json:"uri"` @@ -55421,22 +61527,22 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A previous result id in a workspace pull request. -// -// @since 3.17.0 --type PreviousResultId struct { // line 7331 +-type PreviousResultId struct { - // The URI for which the client knowns a - // result id. - URI DocumentURI `json:"uri"` - // The value of the previous result id. - Value string `json:"value"` -} --type ProgressParams struct { // line 6201 +-type ProgressParams struct { - // The progress token provided by the client or server. - Token ProgressToken `json:"token"` - // The progress data. - Value interface{} `json:"value"` -} --type ProgressToken = interface{} // (alias) line 13960 +-type ProgressToken = interface{} // (alias) line 14375 -// The publish diagnostic client capabilities. --type PublishDiagnosticsClientCapabilities struct { // line 12072 +-type PublishDiagnosticsClientCapabilities struct { - // Whether the clients accepts diagnostics with related information. - RelatedInformation bool `json:"relatedInformation,omitempty"` - // Client supports the tag property to provide meta data about a diagnostic. @@ -55462,7 +61568,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The publish diagnostic notification's parameters. --type PublishDiagnosticsParams struct { // line 4462 +-type PublishDiagnosticsParams struct { - // The URI for which diagnostic information is reported. - URI DocumentURI `json:"uri"` - // Optional the version number of the document the diagnostics are published for. @@ -55486,7 +61592,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// } -// -// ``` --type Range struct { // line 6311 +-type Range struct { - // The range's start position. - Start Position `json:"start"` - // The range's end position. @@ -55494,25 +61600,25 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Client Capabilities for a {@link ReferencesRequest}. --type ReferenceClientCapabilities struct { // line 11609 +-type ReferenceClientCapabilities struct { - // Whether references supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} - -// Value-object that contains additional information when -// requesting references. --type ReferenceContext struct { // line 8930 +-type ReferenceContext struct { - // Include the declaration of the current symbol. - IncludeDeclaration bool `json:"includeDeclaration"` -} - -// Reference options. --type ReferenceOptions struct { // line 8944 +-type ReferenceOptions struct { - WorkDoneProgressOptions -} - -// Parameters for a {@link ReferencesRequest}. --type ReferenceParams struct { // line 5054 +-type ReferenceParams struct { - Context ReferenceContext `json:"context"` - TextDocumentPositionParams - WorkDoneProgressParams @@ -55520,13 +61626,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link ReferencesRequest}. --type ReferenceRegistrationOptions struct { // line 5083 +-type ReferenceRegistrationOptions struct { - TextDocumentRegistrationOptions - ReferenceOptions -} - --// General parameters to to register for an notification or to register a provider. --type Registration struct { // line 7597 +-// General parameters to register for a notification or to register a provider. +-type Registration struct { - // The id used to register the request. The id can be used to deregister - // the request again. - ID string `json:"id"` @@ -55535,14 +61641,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // Options necessary for the registration. - RegisterOptions interface{} `json:"registerOptions,omitempty"` -} --type RegistrationParams struct { // line 4038 +-type RegistrationParams struct { - Registrations []Registration `json:"registrations"` -} - -// Client capabilities specific to regular expressions. -// -// @since 3.16.0 --type RegularExpressionsClientCapabilities struct { // line 12500 +-type RegularExpressionsClientCapabilities struct { - // The engine's name. - Engine string `json:"engine"` - // The engine's version. @@ -55552,7 +61658,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A full diagnostic report with a set of related documents. -// -// @since 3.17.0 --type RelatedFullDocumentDiagnosticReport struct { // line 7157 +-type RelatedFullDocumentDiagnosticReport struct { - // Diagnostics of related documents. This information is useful - // in programming languages where code in a file A can generate - // diagnostics in a file B which A depends on. An example of @@ -55567,7 +61673,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// An unchanged diagnostic report with a set of related documents. -// -// @since 3.17.0 --type RelatedUnchangedDocumentDiagnosticReport struct { // line 7196 +-type RelatedUnchangedDocumentDiagnosticReport struct { - // Diagnostics of related documents. This information is useful - // in programming languages where code in a file A can generate - // diagnostics in a file B which A depends on. An example of @@ -55584,14 +61690,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// folder root, but it can be another absolute URI as well. -// -// @since 3.17.0 --type RelativePattern struct { // line 10736 +-type RelativePattern struct { - // A workspace folder or a base URI to which this pattern will be matched - // against relatively. - BaseURI Or_RelativePattern_baseUri `json:"baseUri"` - // The actual glob pattern; - Pattern Pattern `json:"pattern"` -} --type RenameClientCapabilities struct { // line 11934 +-type RenameClientCapabilities struct { - // Whether rename supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // Client supports testing for validity of rename operations @@ -55617,7 +61723,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Rename file operation --type RenameFile struct { // line 6749 +-type RenameFile struct { - // A rename - Kind string `json:"kind"` - // The old (existing) location. @@ -55630,7 +61736,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Rename file options --type RenameFileOptions struct { // line 9441 +-type RenameFileOptions struct { - // Overwrite target if existing. Overwrite wins over `ignoreIfExists` - Overwrite bool `json:"overwrite,omitempty"` - // Ignores if target exists. @@ -55641,14 +61747,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// files. -// -// @since 3.16.0 --type RenameFilesParams struct { // line 3282 +-type RenameFilesParams struct { - // An array of all files/folders renamed in this operation. When a folder is renamed, only - // the folder will be included, and not its children. - Files []FileRename `json:"files"` -} - -// Provider options for a {@link RenameRequest}. --type RenameOptions struct { // line 9269 +-type RenameOptions struct { - // Renames should be checked and tested before being executed. - // - // @since version 3.12.0 @@ -55657,7 +61763,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The parameters of a {@link RenameRequest}. --type RenameParams struct { // line 5874 +-type RenameParams struct { - // The document to rename. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The position at which this request was sent. @@ -55670,13 +61776,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link RenameRequest}. --type RenameRegistrationOptions struct { // line 5910 +-type RenameRegistrationOptions struct { - TextDocumentRegistrationOptions - RenameOptions -} - -// A generic resource operation. --type ResourceOperation struct { // line 9393 +-type ResourceOperation struct { - // The resource operation kind. - Kind string `json:"kind"` - // An optional annotation identifier describing the operation. @@ -55684,33 +61790,45 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // @since 3.16.0 - AnnotationID *ChangeAnnotationIdentifier `json:"annotationId,omitempty"` -} --type ResourceOperationKind string // line 13669 +-type ResourceOperationKind string +- -// Save options. --type SaveOptions struct { // line 8465 +-type SaveOptions struct { - // The client is supposed to include the content on save. - IncludeText bool `json:"includeText,omitempty"` -} - +-// Describes the currently selected completion item. +-// +-// @since 3.18.0 +-// @proposed +-type SelectedCompletionInfo struct { +- // The range that will be replaced if this completion item is accepted. +- Range Range `json:"range"` +- // The text the range will be replaced with if this completion is accepted. +- Text string `json:"text"` +-} +- -// A selection range represents a part of a selection hierarchy. A selection range -// may have a parent selection range that contains it. --type SelectionRange struct { // line 2569 +-type SelectionRange struct { - // The {@link Range range} of this selection range. - Range Range `json:"range"` - // The parent selection range containing this range. Therefore `parent.range` must contain `this.range`. - Parent *SelectionRange `json:"parent,omitempty"` -} --type SelectionRangeClientCapabilities struct { // line 12058 +-type SelectionRangeClientCapabilities struct { - // Whether implementation supports dynamic registration for selection range providers. If this is set to `true` - // the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server - // capability as well. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` -} --type SelectionRangeOptions struct { // line 6524 +-type SelectionRangeOptions struct { - WorkDoneProgressOptions -} - -// A parameter literal used in selection range requests. --type SelectionRangeParams struct { // line 2534 +-type SelectionRangeParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The positions inside the text document. @@ -55718,7 +61836,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - WorkDoneProgressParams - PartialResultParams -} --type SelectionRangeRegistrationOptions struct { // line 2592 +-type SelectionRangeRegistrationOptions struct { - SelectionRangeOptions - TextDocumentRegistrationOptions - StaticRegistrationOptions @@ -55729,15 +61847,17 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// corresponding client capabilities. -// -// @since 3.16.0 --type SemanticTokenModifiers string // line 12670 +-type SemanticTokenModifiers string +- -// A set of predefined token types. This set is not fixed -// an clients can specify additional token types via the -// corresponding client capabilities. -// -// @since 3.16.0 --type SemanticTokenTypes string // line 12563 +-type SemanticTokenTypes string +- -// @since 3.16.0 --type SemanticTokens struct { // line 2880 +-type SemanticTokens struct { - // An optional result id. If provided and clients support delta updating - // the client will include the result id in the next semantic token request. - // A server can then instead of computing all semantic tokens again simply @@ -55748,7 +61868,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// @since 3.16.0 --type SemanticTokensClientCapabilities struct { // line 12157 +-type SemanticTokensClientCapabilities struct { - // Whether implementation supports dynamic registration. If this is set to `true` - // the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - // return value for the corresponding server capability as well. @@ -55793,14 +61913,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// @since 3.16.0 --type SemanticTokensDelta struct { // line 2979 +-type SemanticTokensDelta struct { - ResultID string `json:"resultId,omitempty"` - // The semantic token edits to transform a previous result into a new result. - Edits []SemanticTokensEdit `json:"edits"` -} - -// @since 3.16.0 --type SemanticTokensDeltaParams struct { // line 2946 +-type SemanticTokensDeltaParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The result id of a previous response. The result Id can either point to a full response @@ -55811,12 +61931,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// @since 3.16.0 --type SemanticTokensDeltaPartialResult struct { // line 3005 +-type SemanticTokensDeltaPartialResult struct { - Edits []SemanticTokensEdit `json:"edits"` -} - -// @since 3.16.0 --type SemanticTokensEdit struct { // line 6617 +-type SemanticTokensEdit struct { - // The start offset of the edit. - Start uint32 `json:"start"` - // The count of elements to remove. @@ -55826,7 +61946,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// @since 3.16.0 --type SemanticTokensLegend struct { // line 9314 +-type SemanticTokensLegend struct { - // The token types a server uses. - TokenTypes []string `json:"tokenTypes"` - // The token modifiers a server uses. @@ -55834,7 +61954,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// @since 3.16.0 --type SemanticTokensOptions struct { // line 6546 +-type SemanticTokensOptions struct { - // The legend used by the server - Legend SemanticTokensLegend `json:"legend"` - // Server supports providing semantic tokens for a specific range @@ -55846,7 +61966,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// @since 3.16.0 --type SemanticTokensParams struct { // line 2855 +-type SemanticTokensParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - WorkDoneProgressParams @@ -55854,12 +61974,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// @since 3.16.0 --type SemanticTokensPartialResult struct { // line 2907 +-type SemanticTokensPartialResult struct { - Data []uint32 `json:"data"` -} - -// @since 3.16.0 --type SemanticTokensRangeParams struct { // line 3022 +-type SemanticTokensRangeParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The range the semantic tokens are requested for. @@ -55869,14 +61989,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// @since 3.16.0 --type SemanticTokensRegistrationOptions struct { // line 2924 +-type SemanticTokensRegistrationOptions struct { - TextDocumentRegistrationOptions - SemanticTokensOptions - StaticRegistrationOptions -} - -// @since 3.16.0 --type SemanticTokensWorkspaceClientCapabilities struct { // line 10977 +-type SemanticTokensWorkspaceClientCapabilities struct { - // Whether the client implementation supports a refresh request sent from - // the server to the client. - // @@ -55889,7 +62009,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -// Defines the capabilities provided by a language -// server. --type ServerCapabilities struct { // line 7809 +-type ServerCapabilities struct { - // The position encoding the server picked from the encodings offered - // by the client via the client capability `general.positionEncodings`. - // @@ -55988,32 +62108,37 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // - // @since 3.17.0 - DiagnosticProvider *Or_ServerCapabilities_diagnosticProvider `json:"diagnosticProvider,omitempty"` +- // Inline completion options used during static registration. +- // +- // @since 3.18.0 +- // @proposed +- InlineCompletionProvider *Or_ServerCapabilities_inlineCompletionProvider `json:"inlineCompletionProvider,omitempty"` - // Workspace specific server capabilities. - Workspace *Workspace6Gn `json:"workspace,omitempty"` - // Experimental server capabilities. - Experimental interface{} `json:"experimental,omitempty"` -} --type SetTraceParams struct { // line 6147 +-type SetTraceParams struct { - Value TraceValues `json:"value"` -} - -// Client capabilities for the showDocument request. -// -// @since 3.16.0 --type ShowDocumentClientCapabilities struct { // line 12485 +-type ShowDocumentClientCapabilities struct { - // The client has support for the showDocument - // request. - Support bool `json:"support"` -} - --// Params to show a document. +-// Params to show a resource in the UI. -// -// @since 3.16.0 --type ShowDocumentParams struct { // line 3055 -- // The document uri to show. +-type ShowDocumentParams struct { +- // The uri to show. - URI URI `json:"uri"` - // Indicates to show the resource in an external program. -- // To show for example `https://code.visualstudio.com/` +- // To show, for example, `https://code.visualstudio.com/` - // in the default WEB browser set `external` to `true`. - External bool `json:"external,omitempty"` - // An optional property to indicate whether the editor @@ -56031,13 +62156,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The result of a showDocument request. -// -// @since 3.16.0 --type ShowDocumentResult struct { // line 3097 +-type ShowDocumentResult struct { - // A boolean indicating if the show was successful. - Success bool `json:"success"` -} - -// The parameters of a notification message. --type ShowMessageParams struct { // line 4183 +-type ShowMessageParams struct { - // The message type. See {@link MessageType} - Type MessageType `json:"type"` - // The actual message. @@ -56045,11 +62170,11 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Show message request client capabilities --type ShowMessageRequestClientCapabilities struct { // line 12458 +-type ShowMessageRequestClientCapabilities struct { - // Capabilities specific to the `MessageActionItem` type. - MessageActionItem *PMessageActionItemPShowMessage `json:"messageActionItem,omitempty"` -} --type ShowMessageRequestParams struct { // line 4205 +-type ShowMessageRequestParams struct { - // The message type. See {@link MessageType} - Type MessageType `json:"type"` - // The actual message. @@ -56061,7 +62186,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Signature help represents the signature of something -// callable. There can be multiple signature but only one -// active and only one active parameter. --type SignatureHelp struct { // line 4968 +-type SignatureHelp struct { - // One or more signatures. - Signatures []SignatureInformation `json:"signatures"` - // The active signature. If omitted or the value lies outside the @@ -56085,7 +62210,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Client Capabilities for a {@link SignatureHelpRequest}. --type SignatureHelpClientCapabilities struct { // line 11428 +-type SignatureHelpClientCapabilities struct { - // Whether signature help supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // The client supports the following `SignatureInformation` @@ -56103,7 +62228,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Additional information about the context in which a signature help request was triggered. -// -// @since 3.15.0 --type SignatureHelpContext struct { // line 8787 +-type SignatureHelpContext struct { - // Action that caused signature help to be triggered. - TriggerKind SignatureHelpTriggerKind `json:"triggerKind"` - // Character that caused signature help to be triggered. @@ -56123,7 +62248,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Server Capabilities for a {@link SignatureHelpRequest}. --type SignatureHelpOptions struct { // line 8882 +-type SignatureHelpOptions struct { - // List of characters that trigger signature help automatically. - TriggerCharacters []string `json:"triggerCharacters,omitempty"` - // List of characters that re-trigger signature help. @@ -56137,7 +62262,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Parameters for a {@link SignatureHelpRequest}. --type SignatureHelpParams struct { // line 4940 +-type SignatureHelpParams struct { - // The signature help context. This is only available if the client specifies - // to send this using the client capability `textDocument.signatureHelp.contextSupport === true` - // @@ -56148,7 +62273,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link SignatureHelpRequest}. --type SignatureHelpRegistrationOptions struct { // line 5003 +-type SignatureHelpRegistrationOptions struct { - TextDocumentRegistrationOptions - SignatureHelpOptions -} @@ -56156,11 +62281,12 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// How a signature help was triggered. -// -// @since 3.15.0 --type SignatureHelpTriggerKind uint32 // line 13580 +-type SignatureHelpTriggerKind uint32 +- -// Represents the signature of something callable. A signature -// can have a label, like a function-name, a doc-comment, and -// a set of parameters. --type SignatureInformation struct { // line 8828 +-type SignatureInformation struct { - // The label of this signature. Will be shown in - // the UI. - Label string `json:"label"` @@ -56179,15 +62305,32 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -// Static registration options to be returned in the initialize -// request. --type StaticRegistrationOptions struct { // line 6343 +-type StaticRegistrationOptions struct { - // The id used to register the request. The id can be used to deregister - // the request again. See also Registration#id. - ID string `json:"id,omitempty"` -} - +-// A string value used as a snippet is a template which allows to insert text +-// and to control the editor cursor when insertion happens. +-// +-// A snippet can define tab stops and placeholders with `$1`, `$2` +-// and `${3:foo}`. `$0` defines the final tab stop, it defaults to +-// the end of the snippet. Variables are defined with `$name` and +-// `${name:default value}`. +-// +-// @since 3.18.0 +-// @proposed +-type StringValue struct { +- // The kind of string value. +- Kind string `json:"kind"` +- // The snippet string. +- Value string `json:"value"` +-} +- -// Represents information about programming constructs like variables, classes, -// interfaces etc. --type SymbolInformation struct { // line 5181 +-type SymbolInformation struct { - // extends BaseSymbolInformation - // Indicates if this symbol is deprecated. - // @@ -56219,20 +62362,22 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// A symbol kind. --type SymbolKind uint32 // line 12841 +-type SymbolKind uint32 +- -// Symbol tags are extra annotations that tweak the rendering of a symbol. -// -// @since 3.16 --type SymbolTag uint32 // line 12955 +-type SymbolTag uint32 +- -// Describe options to be used when registered for text document change events. --type TextDocumentChangeRegistrationOptions struct { // line 4312 +-type TextDocumentChangeRegistrationOptions struct { - // How documents are synced to the server. - SyncKind TextDocumentSyncKind `json:"syncKind"` - TextDocumentRegistrationOptions -} - -// Text document specific client capabilities. --type TextDocumentClientCapabilities struct { // line 10323 +-type TextDocumentClientCapabilities struct { - // Defines which synchronization capabilities the client supports. - Synchronization *TextDocumentSyncClientCapabilities `json:"synchronization,omitempty"` - // Capabilities specific to the `textDocument/completion` request. @@ -56322,16 +62467,21 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // - // @since 3.17.0 - Diagnostic *DiagnosticClientCapabilities `json:"diagnostic,omitempty"` +- // Client capabilities specific to inline completions. +- // +- // @since 3.18.0 +- // @proposed +- InlineCompletion *InlineCompletionClientCapabilities `json:"inlineCompletion,omitempty"` -} - -// An event describing a change to a text document. If only a text is provided -// it is considered to be the full content of the document. --type TextDocumentContentChangeEvent = Msg_TextDocumentContentChangeEvent // (alias) line 14002 +-type TextDocumentContentChangeEvent = Msg_TextDocumentContentChangeEvent // (alias) line 14417 -// Describes textual changes on a text document. A TextDocumentEdit describes all changes -// on a document version Si and after they are applied move the document to version Si+1. -// So the creator of a TextDocumentEdit doesn't need to sort the array of edits or do any -// kind of ordering. However the edits must be non overlapping. --type TextDocumentEdit struct { // line 6677 +-type TextDocumentEdit struct { - // The text document to change. - TextDocument OptionalVersionedTextDocumentIdentifier `json:"textDocument"` - // The edits to be applied. @@ -56358,16 +62508,16 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// @sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**package.json' }` -// -// @since 3.17.0 --type TextDocumentFilter = Msg_TextDocumentFilter // (alias) line 14145 +-type TextDocumentFilter = Msg_TextDocumentFilter // (alias) line 14560 -// A literal to identify a text document in the client. --type TextDocumentIdentifier struct { // line 6419 +-type TextDocumentIdentifier struct { - // The text document's uri. - URI DocumentURI `json:"uri"` -} - -// An item to transfer a text document from the client to the -// server. --type TextDocumentItem struct { // line 7405 +-type TextDocumentItem struct { - // The text document's uri. - URI DocumentURI `json:"uri"` - // The text document's language identifier. @@ -56381,7 +62531,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -// A parameter literal used in requests to pass a text document and a position inside that -// document. --type TextDocumentPositionParams struct { // line 6222 +-type TextDocumentPositionParams struct { - // The text document. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The position inside the text document. @@ -56389,20 +62539,21 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// General text document registration options. --type TextDocumentRegistrationOptions struct { // line 2368 +-type TextDocumentRegistrationOptions struct { - // A document selector to identify the scope of the registration. If set to null - // the document selector provided on the client side will be used. - DocumentSelector DocumentSelector `json:"documentSelector"` -} - -// Represents reasons why a text document is saved. --type TextDocumentSaveReason uint32 // line 13109 +-type TextDocumentSaveReason uint32 +- -// Save registration options. --type TextDocumentSaveRegistrationOptions struct { // line 4369 +-type TextDocumentSaveRegistrationOptions struct { - TextDocumentRegistrationOptions - SaveOptions -} --type TextDocumentSyncClientCapabilities struct { // line 11127 +-type TextDocumentSyncClientCapabilities struct { - // Whether text document synchronization supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // The client supports sending will save notifications. @@ -56417,8 +62568,8 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - -// Defines how the host (editor) should sync -// document changes to the language server. --type TextDocumentSyncKind uint32 // line 13084 --type TextDocumentSyncOptions struct { // line 9736 +-type TextDocumentSyncKind uint32 +-type TextDocumentSyncOptions struct { - // Open and close notifications are sent to the server. If omitted open close notification should not - // be sent. - OpenClose bool `json:"openClose,omitempty"` @@ -56437,7 +62588,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// A text edit applicable to a text document. --type TextEdit struct { // line 4406 +-type TextEdit struct { - // The range of the text document to be manipulated. To insert - // text into a document create a range where start === end. - Range Range `json:"range"` @@ -56445,10 +62596,11 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // empty string. - NewText string `json:"newText"` -} --type TokenFormat string // line 13736 --type TraceValues string // line 13383 +-type TokenFormat string +-type TraceValues string +- -// Since 3.6.0 --type TypeDefinitionClientCapabilities struct { // line 11559 +-type TypeDefinitionClientCapabilities struct { - // Whether implementation supports dynamic registration. If this is set to `true` - // the client supports the new `TypeDefinitionRegistrationOptions` return value - // for the corresponding server capability as well. @@ -56458,22 +62610,22 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // Since 3.14.0 - LinkSupport bool `json:"linkSupport,omitempty"` -} --type TypeDefinitionOptions struct { // line 6358 +-type TypeDefinitionOptions struct { - WorkDoneProgressOptions -} --type TypeDefinitionParams struct { // line 2123 +-type TypeDefinitionParams struct { - TextDocumentPositionParams - WorkDoneProgressParams - PartialResultParams -} --type TypeDefinitionRegistrationOptions struct { // line 2143 +-type TypeDefinitionRegistrationOptions struct { - TextDocumentRegistrationOptions - TypeDefinitionOptions - StaticRegistrationOptions -} - -// @since 3.17.0 --type TypeHierarchyClientCapabilities struct { // line 12337 +-type TypeHierarchyClientCapabilities struct { - // Whether implementation supports dynamic registration. If this is set to `true` - // the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` - // return value for the corresponding server capability as well. @@ -56481,7 +62633,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// @since 3.17.0 --type TypeHierarchyItem struct { // line 3410 +-type TypeHierarchyItem struct { - // The name of this item. - Name string `json:"name"` - // The kind of this item. @@ -56509,14 +62661,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Type hierarchy options used during static registration. -// -// @since 3.17.0 --type TypeHierarchyOptions struct { // line 6936 +-type TypeHierarchyOptions struct { - WorkDoneProgressOptions -} - -// The parameter of a `textDocument/prepareTypeHierarchy` request. -// -// @since 3.17.0 --type TypeHierarchyPrepareParams struct { // line 3392 +-type TypeHierarchyPrepareParams struct { - TextDocumentPositionParams - WorkDoneProgressParams -} @@ -56524,7 +62676,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Type hierarchy options used during static or dynamic registration. -// -// @since 3.17.0 --type TypeHierarchyRegistrationOptions struct { // line 3487 +-type TypeHierarchyRegistrationOptions struct { - TextDocumentRegistrationOptions - TypeHierarchyOptions - StaticRegistrationOptions @@ -56533,7 +62685,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The parameter of a `typeHierarchy/subtypes` request. -// -// @since 3.17.0 --type TypeHierarchySubtypesParams struct { // line 3533 +-type TypeHierarchySubtypesParams struct { - Item TypeHierarchyItem `json:"item"` - WorkDoneProgressParams - PartialResultParams @@ -56542,14 +62694,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// The parameter of a `typeHierarchy/supertypes` request. -// -// @since 3.17.0 --type TypeHierarchySupertypesParams struct { // line 3509 +-type TypeHierarchySupertypesParams struct { - Item TypeHierarchyItem `json:"item"` - WorkDoneProgressParams - PartialResultParams -} - -// created for Tuple --type UIntCommaUInt struct { // line 10076 +-type UIntCommaUInt struct { - Fld0 uint32 `json:"fld0"` - Fld1 uint32 `json:"fld1"` -} @@ -56559,7 +62711,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// report is still accurate. -// -// @since 3.17.0 --type UnchangedDocumentDiagnosticReport struct { // line 7270 +-type UnchangedDocumentDiagnosticReport struct { - // A document diagnostic report indicating - // no changes to the last result. A server can - // only return `unchanged` if result ids are @@ -56573,23 +62725,24 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Moniker uniqueness level to define scope of the moniker. -// -// @since 3.16.0 --type UniquenessLevel string // line 12971 +-type UniquenessLevel string +- -// General parameters to unregister a request or notification. --type Unregistration struct { // line 7628 +-type Unregistration struct { - // The id used to unregister the request or notification. Usually an id - // provided during the register request. - ID string `json:"id"` - // The method to unregister for. - Method string `json:"method"` -} --type UnregistrationParams struct { // line 4053 +-type UnregistrationParams struct { - Unregisterations []Unregistration `json:"unregisterations"` -} - -// A versioned notebook document identifier. -// -// @since 3.17.0 --type VersionedNotebookDocumentIdentifier struct { // line 7443 +-type VersionedNotebookDocumentIdentifier struct { - // The version number of this notebook document. - Version int32 `json:"version"` - // The notebook document's uri. @@ -56597,19 +62750,19 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// A text document identifier to denote a specific version of a text document. --type VersionedTextDocumentIdentifier struct { // line 8445 +-type VersionedTextDocumentIdentifier struct { - // The version number of this document. - Version int32 `json:"version"` - TextDocumentIdentifier -} --type WatchKind = uint32 // line 13505// The parameters sent in a will save text document notification. --type WillSaveTextDocumentParams struct { // line 4384 +-type WatchKind = uint32 // line 13505// The parameters sent in a will save text document notification. +-type WillSaveTextDocumentParams struct { - // The document that will be saved. - TextDocument TextDocumentIdentifier `json:"textDocument"` - // The 'TextDocumentSaveReason'. - Reason TextDocumentSaveReason `json:"reason"` -} --type WindowClientCapabilities struct { // line 10629 +-type WindowClientCapabilities struct { - // It indicates whether the client supports server initiated - // progress using the `window/workDoneProgress/create` request. - // @@ -56629,7 +62782,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // @since 3.16.0 - ShowDocument *ShowDocumentClientCapabilities `json:"showDocument,omitempty"` -} --type WorkDoneProgressBegin struct { // line 6040 +-type WorkDoneProgressBegin struct { - Kind string `json:"kind"` - // Mandatory title of the progress operation. Used to briefly inform about - // the kind of operation being performed. @@ -56654,34 +62807,34 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // that are not following this rule. The value range is [0, 100]. - Percentage uint32 `json:"percentage,omitempty"` -} --type WorkDoneProgressCancelParams struct { // line 2625 +-type WorkDoneProgressCancelParams struct { - // The token to be used to report progress. - Token ProgressToken `json:"token"` -} --type WorkDoneProgressCreateParams struct { // line 2612 +-type WorkDoneProgressCreateParams struct { - // The token to be used to report progress. - Token ProgressToken `json:"token"` -} --type WorkDoneProgressEnd struct { // line 6126 +-type WorkDoneProgressEnd struct { - Kind string `json:"kind"` - // Optional, a final message indicating to for example indicate the outcome - // of the operation. - Message string `json:"message,omitempty"` -} --type WorkDoneProgressOptions struct { // line 2355 +-type WorkDoneProgressOptions struct { - WorkDoneProgress bool `json:"workDoneProgress,omitempty"` -} - -// created for And --type WorkDoneProgressOptionsAndTextDocumentRegistrationOptions struct { // line 196 +-type WorkDoneProgressOptionsAndTextDocumentRegistrationOptions struct { - WorkDoneProgressOptions - TextDocumentRegistrationOptions -} --type WorkDoneProgressParams struct { // line 6244 +-type WorkDoneProgressParams struct { - // An optional token that a server can use to report work done progress. - WorkDoneToken ProgressToken `json:"workDoneToken,omitempty"` -} --type WorkDoneProgressReport struct { // line 6087 +-type WorkDoneProgressReport struct { - Kind string `json:"kind"` - // Controls enablement state of a cancel button. - // @@ -56704,7 +62857,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// created for Literal (Lit_ServerCapabilities_workspace) --type Workspace6Gn struct { // line 8404 +-type Workspace6Gn struct { - // The server supports workspace folder. - // - // @since 3.6.0 @@ -56716,7 +62869,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Workspace specific client capabilities. --type WorkspaceClientCapabilities struct { // line 10184 +-type WorkspaceClientCapabilities struct { - // The client supports applying batch edits - // to the workspace by supporting the request - // 'workspace/applyEdit' @@ -56773,7 +62926,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// Parameters of the workspace diagnostic request. -// -// @since 3.17.0 --type WorkspaceDiagnosticParams struct { // line 3877 +-type WorkspaceDiagnosticParams struct { - // The additional identifier provided during registration. - Identifier string `json:"identifier,omitempty"` - // The currently known diagnostic reports with their @@ -56786,21 +62939,21 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A workspace diagnostic report. -// -// @since 3.17.0 --type WorkspaceDiagnosticReport struct { // line 3914 +-type WorkspaceDiagnosticReport struct { - Items []WorkspaceDocumentDiagnosticReport `json:"items"` -} - -// A partial result for a workspace diagnostic report. -// -// @since 3.17.0 --type WorkspaceDiagnosticReportPartialResult struct { // line 3931 +-type WorkspaceDiagnosticReportPartialResult struct { - Items []WorkspaceDocumentDiagnosticReport `json:"items"` -} - -// A workspace diagnostic document report. -// -// @since 3.17.0 --type WorkspaceDocumentDiagnosticReport = Or_WorkspaceDocumentDiagnosticReport // (alias) line 13984 +-type WorkspaceDocumentDiagnosticReport = Or_WorkspaceDocumentDiagnosticReport // (alias) line 14399 -// A workspace edit represents changes to many resources managed in the workspace. The edit -// should either provide `changes` or `documentChanges`. If documentChanges are present -// they are preferred over `changes` if the client can handle versioned document edits. @@ -56813,7 +62966,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// An invalid sequence (e.g. (1) delete file a.txt and (2) insert text into file a.txt) will -// cause failure of the operation. How the client recovers from the failure is described by -// the client capability: `workspace.workspaceEdit.failureHandling` --type WorkspaceEdit struct { // line 3193 +-type WorkspaceEdit struct { - // Holds changes to existing resources. - Changes map[DocumentURI][]TextEdit `json:"changes,omitempty"` - // Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes @@ -56835,7 +62988,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // @since 3.16.0 - ChangeAnnotations map[ChangeAnnotationIdentifier]ChangeAnnotation `json:"changeAnnotations,omitempty"` -} --type WorkspaceEditClientCapabilities struct { // line 10768 +-type WorkspaceEditClientCapabilities struct { - // The client supports versioned document changes in `WorkspaceEdit`s - DocumentChanges bool `json:"documentChanges,omitempty"` - // The resource operations the client supports. Clients should at least @@ -56864,14 +63017,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// A workspace folder inside a client. --type WorkspaceFolder struct { // line 2163 +-type WorkspaceFolder struct { - // The associated URI for this workspace folder. - URI URI `json:"uri"` - // The name of the workspace folder. Used to refer to this - // workspace folder in the user interface. - Name string `json:"name"` -} --type WorkspaceFolders5Gn struct { // line 9933 +-type WorkspaceFolders5Gn struct { - // The server has support for workspace folders - Supported bool `json:"supported,omitempty"` - // Whether the server wants to receive workspace folder @@ -56885,13 +63038,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The workspace folder change event. --type WorkspaceFoldersChangeEvent struct { // line 6368 +-type WorkspaceFoldersChangeEvent struct { - // The array of added workspace folders - Added []WorkspaceFolder `json:"added"` - // The array of the removed workspace folders - Removed []WorkspaceFolder `json:"removed"` -} --type WorkspaceFoldersInitializeParams struct { // line 7782 +-type WorkspaceFoldersInitializeParams struct { - // The workspace folders configured in the client when the server starts. - // - // This property is only available if the client supports workspace folders. @@ -56901,7 +63054,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // @since 3.6.0 - WorkspaceFolders []WorkspaceFolder `json:"workspaceFolders,omitempty"` -} --type WorkspaceFoldersServerCapabilities struct { // line 9933 +-type WorkspaceFoldersServerCapabilities struct { - // The server has support for workspace folders - Supported bool `json:"supported,omitempty"` - // Whether the server wants to receive workspace folder @@ -56917,7 +63070,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// A full document diagnostic report for a workspace diagnostic result. -// -// @since 3.17.0 --type WorkspaceFullDocumentDiagnosticReport struct { // line 9522 +-type WorkspaceFullDocumentDiagnosticReport struct { - // The URI for which diagnostic information is reported. - URI DocumentURI `json:"uri"` - // The version number for which the diagnostics are reported. @@ -56931,7 +63084,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -// See also SymbolInformation. -// -// @since 3.17.0 --type WorkspaceSymbol struct { // line 5515 +-type WorkspaceSymbol struct { - // The location of the symbol. Whether a server is allowed to - // return a location without a range depends on the client - // capability `workspace.symbol.resolveSupport`. @@ -56945,7 +63098,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Client capabilities for a {@link WorkspaceSymbolRequest}. --type WorkspaceSymbolClientCapabilities struct { // line 10875 +-type WorkspaceSymbolClientCapabilities struct { - // Symbol request supports dynamic registration. - DynamicRegistration bool `json:"dynamicRegistration,omitempty"` - // Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. @@ -56964,7 +63117,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Server capabilities for a {@link WorkspaceSymbolRequest}. --type WorkspaceSymbolOptions struct { // line 9105 +-type WorkspaceSymbolOptions struct { - // The server provides support to resolve additional - // information for a workspace symbol. - // @@ -56974,7 +63127,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The parameters of a {@link WorkspaceSymbolRequest}. --type WorkspaceSymbolParams struct { // line 5491 +-type WorkspaceSymbolParams struct { - // A query string to filter symbols by. Clients may send an empty - // string here to request all symbols. - Query string `json:"query"` @@ -56983,14 +63136,14 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// Registration options for a {@link WorkspaceSymbolRequest}. --type WorkspaceSymbolRegistrationOptions struct { // line 5564 +-type WorkspaceSymbolRegistrationOptions struct { - WorkspaceSymbolOptions -} - -// An unchanged document diagnostic report for a workspace diagnostic result. -// -// @since 3.17.0 --type WorkspaceUnchangedDocumentDiagnosticReport struct { // line 9560 +-type WorkspaceUnchangedDocumentDiagnosticReport struct { - // The URI for which diagnostic information is reported. - URI DocumentURI `json:"uri"` - // The version number for which the diagnostics are reported. @@ -57000,7 +63153,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The initialize parameters --type XInitializeParams struct { // line 7650 +-type XInitializeParams struct { - // The process Id of the parent process that started - // the server. - // @@ -57041,7 +63194,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -} - -// The initialize parameters --type _InitializeParams struct { // line 7650 +-type _InitializeParams struct { - // The process Id of the parent process that started - // the server. - // @@ -57084,11 +63237,11 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto -const ( - // A set of predefined code action kinds - // Empty kind. -- Empty CodeActionKind = "" // line 13333 +- Empty CodeActionKind = "" - // Base kind for quickfix actions: 'quickfix' -- QuickFix CodeActionKind = "quickfix" // line 13338 +- QuickFix CodeActionKind = "quickfix" - // Base kind for refactoring actions: 'refactor' -- Refactor CodeActionKind = "refactor" // line 13343 +- Refactor CodeActionKind = "refactor" - // Base kind for refactoring extraction actions: 'refactor.extract' - // - // Example extract actions: @@ -57099,7 +63252,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // - Extract variable - // - Extract interface from class - // - ... -- RefactorExtract CodeActionKind = "refactor.extract" // line 13348 +- RefactorExtract CodeActionKind = "refactor.extract" - // Base kind for refactoring inline actions: 'refactor.inline' - // - // Example inline actions: @@ -57109,7 +63262,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // - Inline variable - // - Inline constant - // - ... -- RefactorInline CodeActionKind = "refactor.inline" // line 13353 +- RefactorInline CodeActionKind = "refactor.inline" - // Base kind for refactoring rewrite actions: 'refactor.rewrite' - // - // Example rewrite actions: @@ -57121,80 +63274,80 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // - Make method static - // - Move method to base class - // - ... -- RefactorRewrite CodeActionKind = "refactor.rewrite" // line 13358 +- RefactorRewrite CodeActionKind = "refactor.rewrite" - // Base kind for source actions: `source` - // - // Source code actions apply to the entire file. -- Source CodeActionKind = "source" // line 13363 +- Source CodeActionKind = "source" - // Base kind for an organize imports source action: `source.organizeImports` -- SourceOrganizeImports CodeActionKind = "source.organizeImports" // line 13368 +- SourceOrganizeImports CodeActionKind = "source.organizeImports" - // Base kind for auto-fix source actions: `source.fixAll`. - // - // Fix all actions automatically fix errors that have a clear fix that do not require user input. - // They should not suppress errors or perform unsafe fixes such as generating new types or classes. - // - // @since 3.15.0 -- SourceFixAll CodeActionKind = "source.fixAll" // line 13373 +- SourceFixAll CodeActionKind = "source.fixAll" - // The reason why code actions were requested. - // - // @since 3.17.0 - // Code actions were explicitly requested by the user or by an extension. -- CodeActionInvoked CodeActionTriggerKind = 1 // line 13613 +- CodeActionInvoked CodeActionTriggerKind = 1 - // Code actions were requested automatically. - // - // This typically happens when current selection in a file changes, but can - // also be triggered when file content changes. -- CodeActionAutomatic CodeActionTriggerKind = 2 // line 13618 +- CodeActionAutomatic CodeActionTriggerKind = 2 - // The kind of a completion entry. -- TextCompletion CompletionItemKind = 1 // line 13141 -- MethodCompletion CompletionItemKind = 2 // line 13145 -- FunctionCompletion CompletionItemKind = 3 // line 13149 -- ConstructorCompletion CompletionItemKind = 4 // line 13153 -- FieldCompletion CompletionItemKind = 5 // line 13157 -- VariableCompletion CompletionItemKind = 6 // line 13161 -- ClassCompletion CompletionItemKind = 7 // line 13165 -- InterfaceCompletion CompletionItemKind = 8 // line 13169 -- ModuleCompletion CompletionItemKind = 9 // line 13173 -- PropertyCompletion CompletionItemKind = 10 // line 13177 -- UnitCompletion CompletionItemKind = 11 // line 13181 -- ValueCompletion CompletionItemKind = 12 // line 13185 -- EnumCompletion CompletionItemKind = 13 // line 13189 -- KeywordCompletion CompletionItemKind = 14 // line 13193 -- SnippetCompletion CompletionItemKind = 15 // line 13197 -- ColorCompletion CompletionItemKind = 16 // line 13201 -- FileCompletion CompletionItemKind = 17 // line 13205 -- ReferenceCompletion CompletionItemKind = 18 // line 13209 -- FolderCompletion CompletionItemKind = 19 // line 13213 -- EnumMemberCompletion CompletionItemKind = 20 // line 13217 -- ConstantCompletion CompletionItemKind = 21 // line 13221 -- StructCompletion CompletionItemKind = 22 // line 13225 -- EventCompletion CompletionItemKind = 23 // line 13229 -- OperatorCompletion CompletionItemKind = 24 // line 13233 -- TypeParameterCompletion CompletionItemKind = 25 // line 13237 +- TextCompletion CompletionItemKind = 1 +- MethodCompletion CompletionItemKind = 2 +- FunctionCompletion CompletionItemKind = 3 +- ConstructorCompletion CompletionItemKind = 4 +- FieldCompletion CompletionItemKind = 5 +- VariableCompletion CompletionItemKind = 6 +- ClassCompletion CompletionItemKind = 7 +- InterfaceCompletion CompletionItemKind = 8 +- ModuleCompletion CompletionItemKind = 9 +- PropertyCompletion CompletionItemKind = 10 +- UnitCompletion CompletionItemKind = 11 +- ValueCompletion CompletionItemKind = 12 +- EnumCompletion CompletionItemKind = 13 +- KeywordCompletion CompletionItemKind = 14 +- SnippetCompletion CompletionItemKind = 15 +- ColorCompletion CompletionItemKind = 16 +- FileCompletion CompletionItemKind = 17 +- ReferenceCompletion CompletionItemKind = 18 +- FolderCompletion CompletionItemKind = 19 +- EnumMemberCompletion CompletionItemKind = 20 +- ConstantCompletion CompletionItemKind = 21 +- StructCompletion CompletionItemKind = 22 +- EventCompletion CompletionItemKind = 23 +- OperatorCompletion CompletionItemKind = 24 +- TypeParameterCompletion CompletionItemKind = 25 - // Completion item tags are extra annotations that tweak the rendering of a completion - // item. - // - // @since 3.15.0 - // Render a completion as obsolete, usually using a strike-out. -- ComplDeprecated CompletionItemTag = 1 // line 13251 +- ComplDeprecated CompletionItemTag = 1 - // How a completion was triggered - // Completion was triggered by typing an identifier (24x7 code - // complete), manual invocation (e.g Ctrl+Space) or via API. -- Invoked CompletionTriggerKind = 1 // line 13562 +- Invoked CompletionTriggerKind = 1 - // Completion was triggered by a trigger character specified by - // the `triggerCharacters` properties of the `CompletionRegistrationOptions`. -- TriggerCharacter CompletionTriggerKind = 2 // line 13567 +- TriggerCharacter CompletionTriggerKind = 2 - // Completion was re-triggered as current completion list is incomplete -- TriggerForIncompleteCompletions CompletionTriggerKind = 3 // line 13572 +- TriggerForIncompleteCompletions CompletionTriggerKind = 3 - // The diagnostic's severity. - // Reports an error. -- SeverityError DiagnosticSeverity = 1 // line 13511 +- SeverityError DiagnosticSeverity = 1 - // Reports a warning. -- SeverityWarning DiagnosticSeverity = 2 // line 13516 +- SeverityWarning DiagnosticSeverity = 2 - // Reports an information. -- SeverityInformation DiagnosticSeverity = 3 // line 13521 +- SeverityInformation DiagnosticSeverity = 3 - // Reports a hint. -- SeverityHint DiagnosticSeverity = 4 // line 13526 +- SeverityHint DiagnosticSeverity = 4 - // The diagnostic tags. - // - // @since 3.15.0 @@ -57202,83 +63355,91 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // - // Clients are allowed to render diagnostics with this tag faded out instead of having - // an error squiggle. -- Unnecessary DiagnosticTag = 1 // line 13541 +- Unnecessary DiagnosticTag = 1 - // Deprecated or obsolete code. - // - // Clients are allowed to rendered diagnostics with this tag strike through. -- Deprecated DiagnosticTag = 2 // line 13546 +- Deprecated DiagnosticTag = 2 - // The document diagnostic report kinds. - // - // @since 3.17.0 - // A diagnostic report with a full - // set of problems. -- DiagnosticFull DocumentDiagnosticReportKind = "full" // line 12729 +- DiagnosticFull DocumentDiagnosticReportKind = "full" - // A report indicating that the last - // returned report is still accurate. -- DiagnosticUnchanged DocumentDiagnosticReportKind = "unchanged" // line 12734 +- DiagnosticUnchanged DocumentDiagnosticReportKind = "unchanged" - // A document highlight kind. - // A textual occurrence. -- Text DocumentHighlightKind = 1 // line 13308 +- Text DocumentHighlightKind = 1 - // Read-access of a symbol, like reading a variable. -- Read DocumentHighlightKind = 2 // line 13313 +- Read DocumentHighlightKind = 2 - // Write-access of a symbol, like writing to a variable. -- Write DocumentHighlightKind = 3 // line 13318 +- Write DocumentHighlightKind = 3 - // Predefined error codes. -- ParseError ErrorCodes = -32700 // line 12750 -- InvalidRequest ErrorCodes = -32600 // line 12754 -- MethodNotFound ErrorCodes = -32601 // line 12758 -- InvalidParams ErrorCodes = -32602 // line 12762 -- InternalError ErrorCodes = -32603 // line 12766 +- ParseError ErrorCodes = -32700 +- InvalidRequest ErrorCodes = -32600 +- MethodNotFound ErrorCodes = -32601 +- InvalidParams ErrorCodes = -32602 +- InternalError ErrorCodes = -32603 - // Error code indicating that a server received a notification or - // request before the server has received the `initialize` request. -- ServerNotInitialized ErrorCodes = -32002 // line 12770 -- UnknownErrorCode ErrorCodes = -32001 // line 12775 +- ServerNotInitialized ErrorCodes = -32002 +- UnknownErrorCode ErrorCodes = -32001 - // Applying the workspace change is simply aborted if one of the changes provided - // fails. All operations executed before the failing operation stay executed. -- Abort FailureHandlingKind = "abort" // line 13700 +- Abort FailureHandlingKind = "abort" - // All operations are executed transactional. That means they either all - // succeed or no changes at all are applied to the workspace. -- Transactional FailureHandlingKind = "transactional" // line 13705 +- Transactional FailureHandlingKind = "transactional" - // If the workspace edit contains only textual file changes they are executed transactional. - // If resource changes (create, rename or delete file) are part of the change the failure - // handling strategy is abort. -- TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional" // line 13710 +- TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional" - // The client tries to undo the operations already executed. But there is no - // guarantee that this is succeeding. -- Undo FailureHandlingKind = "undo" // line 13715 +- Undo FailureHandlingKind = "undo" - // The file event type - // The file got created. -- Created FileChangeType = 1 // line 13461 +- Created FileChangeType = 1 - // The file got changed. -- Changed FileChangeType = 2 // line 13466 +- Changed FileChangeType = 2 - // The file got deleted. -- Deleted FileChangeType = 3 // line 13471 +- Deleted FileChangeType = 3 - // A pattern kind describing if a glob pattern matches a file a folder or - // both. - // - // @since 3.16.0 - // The pattern matches a file only. -- FilePattern FileOperationPatternKind = "file" // line 13634 +- FilePattern FileOperationPatternKind = "file" - // The pattern matches a folder only. -- FolderPattern FileOperationPatternKind = "folder" // line 13639 +- FolderPattern FileOperationPatternKind = "folder" - // A set of predefined range kinds. - // Folding range for a comment -- Comment FoldingRangeKind = "comment" // line 12822 +- Comment FoldingRangeKind = "comment" - // Folding range for an import or include -- Imports FoldingRangeKind = "imports" // line 12827 +- Imports FoldingRangeKind = "imports" - // Folding range for a region (e.g. `#region`) -- Region FoldingRangeKind = "region" // line 12832 +- Region FoldingRangeKind = "region" - // Inlay hint kinds. - // - // @since 3.17.0 - // An inlay hint that for a type annotation. -- Type InlayHintKind = 1 // line 13040 +- Type InlayHintKind = 1 - // An inlay hint that is for a parameter. -- Parameter InlayHintKind = 2 // line 13045 +- Parameter InlayHintKind = 2 +- // Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. +- // +- // @since 3.18.0 +- // @proposed +- // Completion was triggered explicitly by a user gesture. +- InlineInvoked InlineCompletionTriggerKind = 0 +- // Completion was triggered automatically while editing. +- InlineAutomatic InlineCompletionTriggerKind = 1 - // Defines whether the insert text in a completion item should be interpreted as - // plain text or a snippet. - // The primary text to be inserted is treated as a plain string. -- PlainTextTextFormat InsertTextFormat = 1 // line 13267 +- PlainTextTextFormat InsertTextFormat = 1 - // The primary text to be inserted is treated as a snippet. - // - // A snippet can define tab stops and placeholders with `$1`, `$2` @@ -57287,7 +63448,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // that is typing in one will update others too. - // - // See also: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#snippet_syntax -- SnippetTextFormat InsertTextFormat = 2 // line 13272 +- SnippetTextFormat InsertTextFormat = 2 - // How whitespace and indentation is handled during completion - // item insertion. - // @@ -57297,7 +63458,7 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // inserted using the indentation defined in the string value. - // The client will not apply any kind of adjustments to the - // string. -- AsIs InsertTextMode = 1 // line 13287 +- AsIs InsertTextMode = 1 - // The editor adjusts leading whitespace of new lines so that - // they match the indentation up to the cursor of the line for - // which the item is accepted. @@ -57305,20 +63466,20 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // Consider a line like this: <2tabs><3tabs>foo. Accepting a - // multi line completion item is indented using 2 tabs and all - // following lines inserted will be indented using 2 tabs as well. -- AdjustIndentation InsertTextMode = 2 // line 13292 +- AdjustIndentation InsertTextMode = 2 - // A request failed but it was syntactically correct, e.g the - // method name was known and the parameters were valid. The error - // message should contain human readable information about why - // the request failed. - // - // @since 3.17.0 -- RequestFailed LSPErrorCodes = -32803 // line 12790 +- RequestFailed LSPErrorCodes = -32803 - // The server cancelled the request. This error code should - // only be used for requests that explicitly support being - // server cancellable. - // - // @since 3.17.0 -- ServerCancelled LSPErrorCodes = -32802 // line 12796 +- ServerCancelled LSPErrorCodes = -32802 - // The server detected that the content of a document got - // modified outside normal conditions. A server should - // NOT send this error code if it detects a content change @@ -57327,207 +63488,207 @@ diff -urN a/gopls/internal/lsp/protocol/tsprotocol.go b/gopls/internal/lsp/proto - // - // If a client decides that a result is not of any use anymore - // the client should cancel the request. -- ContentModified LSPErrorCodes = -32801 // line 12802 +- ContentModified LSPErrorCodes = -32801 - // The client has canceled a request and a server as detected - // the cancel. -- RequestCancelled LSPErrorCodes = -32800 // line 12807 +- RequestCancelled LSPErrorCodes = -32800 - // Describes the content type that a client supports in various - // result literals like `Hover`, `ParameterInfo` or `CompletionItem`. - // - // Please note that `MarkupKinds` must not start with a `$`. This kinds - // are reserved for internal usage. - // Plain text is supported as a content format -- PlainText MarkupKind = "plaintext" // line 13414 +- PlainText MarkupKind = "plaintext" - // Markdown is supported as a content format -- Markdown MarkupKind = "markdown" // line 13419 +- Markdown MarkupKind = "markdown" - // The message type - // An error message. -- Error MessageType = 1 // line 13061 +- Error MessageType = 1 - // A warning message. -- Warning MessageType = 2 // line 13066 +- Warning MessageType = 2 - // An information message. -- Info MessageType = 3 // line 13071 +- Info MessageType = 3 - // A log message. -- Log MessageType = 4 // line 13076 +- Log MessageType = 4 - // The moniker kind. - // - // @since 3.16.0 - // The moniker represent a symbol that is imported into a project -- Import MonikerKind = "import" // line 13014 +- Import MonikerKind = "import" - // The moniker represents a symbol that is exported from a project -- Export MonikerKind = "export" // line 13019 +- Export MonikerKind = "export" - // The moniker represents a symbol that is local to a project (e.g. a local - // variable of a function, a class not visible outside the project, ...) -- Local MonikerKind = "local" // line 13024 +- Local MonikerKind = "local" - // A notebook cell kind. - // - // @since 3.17.0 - // A markup-cell is formatted source that is used for display. -- Markup NotebookCellKind = 1 // line 13655 +- Markup NotebookCellKind = 1 - // A code-cell is source code. -- Code NotebookCellKind = 2 // line 13660 +- Code NotebookCellKind = 2 - // A set of predefined position encoding kinds. - // - // @since 3.17.0 -- // Character offsets count UTF-8 code units. -- UTF8 PositionEncodingKind = "utf-8" // line 13434 +- // Character offsets count UTF-8 code units (e.g. bytes). +- UTF8 PositionEncodingKind = "utf-8" - // Character offsets count UTF-16 code units. - // - // This is the default and must always be supported - // by servers -- UTF16 PositionEncodingKind = "utf-16" // line 13439 +- UTF16 PositionEncodingKind = "utf-16" - // Character offsets count UTF-32 code units. - // -- // Implementation note: these are the same as Unicode code points, +- // Implementation note: these are the same as Unicode codepoints, - // so this `PositionEncodingKind` may also be used for an - // encoding-agnostic representation of character offsets. -- UTF32 PositionEncodingKind = "utf-32" // line 13444 +- UTF32 PositionEncodingKind = "utf-32" - // The client's default behavior is to select the identifier - // according the to language's syntax rule. -- Identifier PrepareSupportDefaultBehavior = 1 // line 13729 +- Identifier PrepareSupportDefaultBehavior = 1 - // Supports creating new files and folders. -- Create ResourceOperationKind = "create" // line 13676 +- Create ResourceOperationKind = "create" - // Supports renaming existing files and folders. -- Rename ResourceOperationKind = "rename" // line 13681 +- Rename ResourceOperationKind = "rename" - // Supports deleting existing files and folders. -- Delete ResourceOperationKind = "delete" // line 13686 +- Delete ResourceOperationKind = "delete" - // A set of predefined token modifiers. This set is not fixed - // an clients can specify additional token types via the - // corresponding client capabilities. - // - // @since 3.16.0 -- ModDeclaration SemanticTokenModifiers = "declaration" // line 12677 -- ModDefinition SemanticTokenModifiers = "definition" // line 12681 -- ModReadonly SemanticTokenModifiers = "readonly" // line 12685 -- ModStatic SemanticTokenModifiers = "static" // line 12689 -- ModDeprecated SemanticTokenModifiers = "deprecated" // line 12693 -- ModAbstract SemanticTokenModifiers = "abstract" // line 12697 -- ModAsync SemanticTokenModifiers = "async" // line 12701 -- ModModification SemanticTokenModifiers = "modification" // line 12705 -- ModDocumentation SemanticTokenModifiers = "documentation" // line 12709 -- ModDefaultLibrary SemanticTokenModifiers = "defaultLibrary" // line 12713 +- ModDeclaration SemanticTokenModifiers = "declaration" +- ModDefinition SemanticTokenModifiers = "definition" +- ModReadonly SemanticTokenModifiers = "readonly" +- ModStatic SemanticTokenModifiers = "static" +- ModDeprecated SemanticTokenModifiers = "deprecated" +- ModAbstract SemanticTokenModifiers = "abstract" +- ModAsync SemanticTokenModifiers = "async" +- ModModification SemanticTokenModifiers = "modification" +- ModDocumentation SemanticTokenModifiers = "documentation" +- ModDefaultLibrary SemanticTokenModifiers = "defaultLibrary" - // A set of predefined token types. This set is not fixed - // an clients can specify additional token types via the - // corresponding client capabilities. - // - // @since 3.16.0 -- NamespaceType SemanticTokenTypes = "namespace" // line 12570 +- NamespaceType SemanticTokenTypes = "namespace" - // Represents a generic type. Acts as a fallback for types which can't be mapped to - // a specific type like class or enum. -- TypeType SemanticTokenTypes = "type" // line 12574 -- ClassType SemanticTokenTypes = "class" // line 12579 -- EnumType SemanticTokenTypes = "enum" // line 12583 -- InterfaceType SemanticTokenTypes = "interface" // line 12587 -- StructType SemanticTokenTypes = "struct" // line 12591 -- TypeParameterType SemanticTokenTypes = "typeParameter" // line 12595 -- ParameterType SemanticTokenTypes = "parameter" // line 12599 -- VariableType SemanticTokenTypes = "variable" // line 12603 -- PropertyType SemanticTokenTypes = "property" // line 12607 -- EnumMemberType SemanticTokenTypes = "enumMember" // line 12611 -- EventType SemanticTokenTypes = "event" // line 12615 -- FunctionType SemanticTokenTypes = "function" // line 12619 -- MethodType SemanticTokenTypes = "method" // line 12623 -- MacroType SemanticTokenTypes = "macro" // line 12627 -- KeywordType SemanticTokenTypes = "keyword" // line 12631 -- ModifierType SemanticTokenTypes = "modifier" // line 12635 -- CommentType SemanticTokenTypes = "comment" // line 12639 -- StringType SemanticTokenTypes = "string" // line 12643 -- NumberType SemanticTokenTypes = "number" // line 12647 -- RegexpType SemanticTokenTypes = "regexp" // line 12651 -- OperatorType SemanticTokenTypes = "operator" // line 12655 +- TypeType SemanticTokenTypes = "type" +- ClassType SemanticTokenTypes = "class" +- EnumType SemanticTokenTypes = "enum" +- InterfaceType SemanticTokenTypes = "interface" +- StructType SemanticTokenTypes = "struct" +- TypeParameterType SemanticTokenTypes = "typeParameter" +- ParameterType SemanticTokenTypes = "parameter" +- VariableType SemanticTokenTypes = "variable" +- PropertyType SemanticTokenTypes = "property" +- EnumMemberType SemanticTokenTypes = "enumMember" +- EventType SemanticTokenTypes = "event" +- FunctionType SemanticTokenTypes = "function" +- MethodType SemanticTokenTypes = "method" +- MacroType SemanticTokenTypes = "macro" +- KeywordType SemanticTokenTypes = "keyword" +- ModifierType SemanticTokenTypes = "modifier" +- CommentType SemanticTokenTypes = "comment" +- StringType SemanticTokenTypes = "string" +- NumberType SemanticTokenTypes = "number" +- RegexpType SemanticTokenTypes = "regexp" +- OperatorType SemanticTokenTypes = "operator" - // @since 3.17.0 -- DecoratorType SemanticTokenTypes = "decorator" // line 12659 +- DecoratorType SemanticTokenTypes = "decorator" - // How a signature help was triggered. - // - // @since 3.15.0 - // Signature help was invoked manually by the user or by a command. -- SigInvoked SignatureHelpTriggerKind = 1 // line 13587 +- SigInvoked SignatureHelpTriggerKind = 1 - // Signature help was triggered by a trigger character. -- SigTriggerCharacter SignatureHelpTriggerKind = 2 // line 13592 +- SigTriggerCharacter SignatureHelpTriggerKind = 2 - // Signature help was triggered by the cursor moving or by the document content changing. -- SigContentChange SignatureHelpTriggerKind = 3 // line 13597 +- SigContentChange SignatureHelpTriggerKind = 3 - // A symbol kind. -- File SymbolKind = 1 // line 12848 -- Module SymbolKind = 2 // line 12852 -- Namespace SymbolKind = 3 // line 12856 -- Package SymbolKind = 4 // line 12860 -- Class SymbolKind = 5 // line 12864 -- Method SymbolKind = 6 // line 12868 -- Property SymbolKind = 7 // line 12872 -- Field SymbolKind = 8 // line 12876 -- Constructor SymbolKind = 9 // line 12880 -- Enum SymbolKind = 10 // line 12884 -- Interface SymbolKind = 11 // line 12888 -- Function SymbolKind = 12 // line 12892 -- Variable SymbolKind = 13 // line 12896 -- Constant SymbolKind = 14 // line 12900 -- String SymbolKind = 15 // line 12904 -- Number SymbolKind = 16 // line 12908 -- Boolean SymbolKind = 17 // line 12912 -- Array SymbolKind = 18 // line 12916 -- Object SymbolKind = 19 // line 12920 -- Key SymbolKind = 20 // line 12924 -- Null SymbolKind = 21 // line 12928 -- EnumMember SymbolKind = 22 // line 12932 -- Struct SymbolKind = 23 // line 12936 -- Event SymbolKind = 24 // line 12940 -- Operator SymbolKind = 25 // line 12944 -- TypeParameter SymbolKind = 26 // line 12948 +- File SymbolKind = 1 +- Module SymbolKind = 2 +- Namespace SymbolKind = 3 +- Package SymbolKind = 4 +- Class SymbolKind = 5 +- Method SymbolKind = 6 +- Property SymbolKind = 7 +- Field SymbolKind = 8 +- Constructor SymbolKind = 9 +- Enum SymbolKind = 10 +- Interface SymbolKind = 11 +- Function SymbolKind = 12 +- Variable SymbolKind = 13 +- Constant SymbolKind = 14 +- String SymbolKind = 15 +- Number SymbolKind = 16 +- Boolean SymbolKind = 17 +- Array SymbolKind = 18 +- Object SymbolKind = 19 +- Key SymbolKind = 20 +- Null SymbolKind = 21 +- EnumMember SymbolKind = 22 +- Struct SymbolKind = 23 +- Event SymbolKind = 24 +- Operator SymbolKind = 25 +- TypeParameter SymbolKind = 26 - // Symbol tags are extra annotations that tweak the rendering of a symbol. - // - // @since 3.16 - // Render a symbol as obsolete, usually using a strike-out. -- DeprecatedSymbol SymbolTag = 1 // line 12962 +- DeprecatedSymbol SymbolTag = 1 - // Represents reasons why a text document is saved. - // Manually triggered, e.g. by the user pressing save, by starting debugging, - // or by an API call. -- Manual TextDocumentSaveReason = 1 // line 13116 +- Manual TextDocumentSaveReason = 1 - // Automatic after a delay. -- AfterDelay TextDocumentSaveReason = 2 // line 13121 +- AfterDelay TextDocumentSaveReason = 2 - // When the editor lost focus. -- FocusOut TextDocumentSaveReason = 3 // line 13126 +- FocusOut TextDocumentSaveReason = 3 - // Defines how the host (editor) should sync - // document changes to the language server. - // Documents should not be synced at all. -- None TextDocumentSyncKind = 0 // line 13091 +- None TextDocumentSyncKind = 0 - // Documents are synced by always sending the full content - // of the document. -- Full TextDocumentSyncKind = 1 // line 13096 +- Full TextDocumentSyncKind = 1 - // Documents are synced by sending the full content on open. - // After that only incremental updates to the document are - // send. -- Incremental TextDocumentSyncKind = 2 // line 13101 -- Relative TokenFormat = "relative" // line 13743 +- Incremental TextDocumentSyncKind = 2 +- Relative TokenFormat = "relative" - // Turn tracing off. -- Off TraceValues = "off" // line 13390 +- Off TraceValues = "off" - // Trace messages only. -- Messages TraceValues = "messages" // line 13395 +- Messages TraceValues = "messages" - // Verbose message tracing. -- Verbose TraceValues = "verbose" // line 13400 +- Verbose TraceValues = "verbose" - // Moniker uniqueness level to define scope of the moniker. - // - // @since 3.16.0 - // The moniker is only unique inside a document -- Document UniquenessLevel = "document" // line 12978 +- Document UniquenessLevel = "document" - // The moniker is unique inside a project for which a dump got created -- Project UniquenessLevel = "project" // line 12983 +- Project UniquenessLevel = "project" - // The moniker is unique inside the group to which a project belongs -- Group UniquenessLevel = "group" // line 12988 +- Group UniquenessLevel = "group" - // The moniker is unique inside the moniker scheme. -- Scheme UniquenessLevel = "scheme" // line 12993 +- Scheme UniquenessLevel = "scheme" - // The moniker is globally unique -- Global UniquenessLevel = "global" // line 12998 +- Global UniquenessLevel = "global" - // Interested in create events. -- WatchCreate WatchKind = 1 // line 13486 +- WatchCreate WatchKind = 1 - // Interested in change events -- WatchChange WatchKind = 2 // line 13491 +- WatchChange WatchKind = 2 - // Interested in delete events -- WatchDelete WatchKind = 4 // line 13496 +- WatchDelete WatchKind = 4 -) diff -urN a/gopls/internal/lsp/protocol/tsserver.go b/gopls/internal/lsp/protocol/tsserver.go --- a/gopls/internal/lsp/protocol/tsserver.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/protocol/tsserver.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1160 +0,0 @@ ++++ b/gopls/internal/lsp/protocol/tsserver.go 1970-01-01 08:00:00 +@@ -1,1196 +0,0 @@ -// Copyright 2023 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. @@ -57536,8 +63697,8 @@ diff -urN a/gopls/internal/lsp/protocol/tsserver.go b/gopls/internal/lsp/protoco - -package protocol - --// Code generated from protocol/metaModel.json at ref release/protocol/3.17.3-next.6 (hash 56c23c557e3568a9f56f42435fd5a80f9458957f). --// https://github.com/microsoft/vscode-languageserver-node/blob/release/protocol/3.17.3-next.6/protocol/metaModel.json +-// Code generated from protocol/metaModel.json at ref release/protocol/3.17.4-next.2 (hash 184c8a7f010d335582f24337fe182baa6f2fccdd). +-// https://github.com/microsoft/vscode-languageserver-node/blob/release/protocol/3.17.4-next.2/protocol/metaModel.json -// LSP metaData.version = 3.17.0. - -import ( @@ -57548,77 +63709,79 @@ diff -urN a/gopls/internal/lsp/protocol/tsserver.go b/gopls/internal/lsp/protoco -) - -type Server interface { -- Progress(context.Context, *ProgressParams) error // $/progress -- SetTrace(context.Context, *SetTraceParams) error // $/setTrace -- IncomingCalls(context.Context, *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall, error) // callHierarchy/incomingCalls -- OutgoingCalls(context.Context, *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall, error) // callHierarchy/outgoingCalls -- ResolveCodeAction(context.Context, *CodeAction) (*CodeAction, error) // codeAction/resolve -- ResolveCodeLens(context.Context, *CodeLens) (*CodeLens, error) // codeLens/resolve -- ResolveCompletionItem(context.Context, *CompletionItem) (*CompletionItem, error) // completionItem/resolve -- ResolveDocumentLink(context.Context, *DocumentLink) (*DocumentLink, error) // documentLink/resolve -- Exit(context.Context) error // exit -- Initialize(context.Context, *ParamInitialize) (*InitializeResult, error) // initialize -- Initialized(context.Context, *InitializedParams) error // initialized -- Resolve(context.Context, *InlayHint) (*InlayHint, error) // inlayHint/resolve -- DidChangeNotebookDocument(context.Context, *DidChangeNotebookDocumentParams) error // notebookDocument/didChange -- DidCloseNotebookDocument(context.Context, *DidCloseNotebookDocumentParams) error // notebookDocument/didClose -- DidOpenNotebookDocument(context.Context, *DidOpenNotebookDocumentParams) error // notebookDocument/didOpen -- DidSaveNotebookDocument(context.Context, *DidSaveNotebookDocumentParams) error // notebookDocument/didSave -- Shutdown(context.Context) error // shutdown -- CodeAction(context.Context, *CodeActionParams) ([]CodeAction, error) // textDocument/codeAction -- CodeLens(context.Context, *CodeLensParams) ([]CodeLens, error) // textDocument/codeLens -- ColorPresentation(context.Context, *ColorPresentationParams) ([]ColorPresentation, error) // textDocument/colorPresentation -- Completion(context.Context, *CompletionParams) (*CompletionList, error) // textDocument/completion -- Declaration(context.Context, *DeclarationParams) (*Or_textDocument_declaration, error) // textDocument/declaration -- Definition(context.Context, *DefinitionParams) ([]Location, error) // textDocument/definition -- Diagnostic(context.Context, *string) (*string, error) // textDocument/diagnostic -- DidChange(context.Context, *DidChangeTextDocumentParams) error // textDocument/didChange -- DidClose(context.Context, *DidCloseTextDocumentParams) error // textDocument/didClose -- DidOpen(context.Context, *DidOpenTextDocumentParams) error // textDocument/didOpen -- DidSave(context.Context, *DidSaveTextDocumentParams) error // textDocument/didSave -- DocumentColor(context.Context, *DocumentColorParams) ([]ColorInformation, error) // textDocument/documentColor -- DocumentHighlight(context.Context, *DocumentHighlightParams) ([]DocumentHighlight, error) // textDocument/documentHighlight -- DocumentLink(context.Context, *DocumentLinkParams) ([]DocumentLink, error) // textDocument/documentLink -- DocumentSymbol(context.Context, *DocumentSymbolParams) ([]interface{}, error) // textDocument/documentSymbol -- FoldingRange(context.Context, *FoldingRangeParams) ([]FoldingRange, error) // textDocument/foldingRange -- Formatting(context.Context, *DocumentFormattingParams) ([]TextEdit, error) // textDocument/formatting -- Hover(context.Context, *HoverParams) (*Hover, error) // textDocument/hover -- Implementation(context.Context, *ImplementationParams) ([]Location, error) // textDocument/implementation -- InlayHint(context.Context, *InlayHintParams) ([]InlayHint, error) // textDocument/inlayHint -- InlineValue(context.Context, *InlineValueParams) ([]InlineValue, error) // textDocument/inlineValue -- LinkedEditingRange(context.Context, *LinkedEditingRangeParams) (*LinkedEditingRanges, error) // textDocument/linkedEditingRange -- Moniker(context.Context, *MonikerParams) ([]Moniker, error) // textDocument/moniker -- OnTypeFormatting(context.Context, *DocumentOnTypeFormattingParams) ([]TextEdit, error) // textDocument/onTypeFormatting -- PrepareCallHierarchy(context.Context, *CallHierarchyPrepareParams) ([]CallHierarchyItem, error) // textDocument/prepareCallHierarchy -- PrepareRename(context.Context, *PrepareRenameParams) (*PrepareRename2Gn, error) // textDocument/prepareRename -- PrepareTypeHierarchy(context.Context, *TypeHierarchyPrepareParams) ([]TypeHierarchyItem, error) // textDocument/prepareTypeHierarchy -- RangeFormatting(context.Context, *DocumentRangeFormattingParams) ([]TextEdit, error) // textDocument/rangeFormatting -- References(context.Context, *ReferenceParams) ([]Location, error) // textDocument/references -- Rename(context.Context, *RenameParams) (*WorkspaceEdit, error) // textDocument/rename -- SelectionRange(context.Context, *SelectionRangeParams) ([]SelectionRange, error) // textDocument/selectionRange -- SemanticTokensFull(context.Context, *SemanticTokensParams) (*SemanticTokens, error) // textDocument/semanticTokens/full -- SemanticTokensFullDelta(context.Context, *SemanticTokensDeltaParams) (interface{}, error) // textDocument/semanticTokens/full/delta -- SemanticTokensRange(context.Context, *SemanticTokensRangeParams) (*SemanticTokens, error) // textDocument/semanticTokens/range -- SignatureHelp(context.Context, *SignatureHelpParams) (*SignatureHelp, error) // textDocument/signatureHelp -- TypeDefinition(context.Context, *TypeDefinitionParams) ([]Location, error) // textDocument/typeDefinition -- WillSave(context.Context, *WillSaveTextDocumentParams) error // textDocument/willSave -- WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit, error) // textDocument/willSaveWaitUntil -- Subtypes(context.Context, *TypeHierarchySubtypesParams) ([]TypeHierarchyItem, error) // typeHierarchy/subtypes -- Supertypes(context.Context, *TypeHierarchySupertypesParams) ([]TypeHierarchyItem, error) // typeHierarchy/supertypes -- WorkDoneProgressCancel(context.Context, *WorkDoneProgressCancelParams) error // window/workDoneProgress/cancel -- DiagnosticWorkspace(context.Context, *WorkspaceDiagnosticParams) (*WorkspaceDiagnosticReport, error) // workspace/diagnostic -- DidChangeConfiguration(context.Context, *DidChangeConfigurationParams) error // workspace/didChangeConfiguration -- DidChangeWatchedFiles(context.Context, *DidChangeWatchedFilesParams) error // workspace/didChangeWatchedFiles -- DidChangeWorkspaceFolders(context.Context, *DidChangeWorkspaceFoldersParams) error // workspace/didChangeWorkspaceFolders -- DidCreateFiles(context.Context, *CreateFilesParams) error // workspace/didCreateFiles -- DidDeleteFiles(context.Context, *DeleteFilesParams) error // workspace/didDeleteFiles -- DidRenameFiles(context.Context, *RenameFilesParams) error // workspace/didRenameFiles -- ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{}, error) // workspace/executeCommand -- Symbol(context.Context, *WorkspaceSymbolParams) ([]SymbolInformation, error) // workspace/symbol -- WillCreateFiles(context.Context, *CreateFilesParams) (*WorkspaceEdit, error) // workspace/willCreateFiles -- WillDeleteFiles(context.Context, *DeleteFilesParams) (*WorkspaceEdit, error) // workspace/willDeleteFiles -- WillRenameFiles(context.Context, *RenameFilesParams) (*WorkspaceEdit, error) // workspace/willRenameFiles -- ResolveWorkspaceSymbol(context.Context, *WorkspaceSymbol) (*WorkspaceSymbol, error) // workspaceSymbol/resolve +- Progress(context.Context, *ProgressParams) error // $/progress +- SetTrace(context.Context, *SetTraceParams) error // $/setTrace +- IncomingCalls(context.Context, *CallHierarchyIncomingCallsParams) ([]CallHierarchyIncomingCall, error) // callHierarchy/incomingCalls +- OutgoingCalls(context.Context, *CallHierarchyOutgoingCallsParams) ([]CallHierarchyOutgoingCall, error) // callHierarchy/outgoingCalls +- ResolveCodeAction(context.Context, *CodeAction) (*CodeAction, error) // codeAction/resolve +- ResolveCodeLens(context.Context, *CodeLens) (*CodeLens, error) // codeLens/resolve +- ResolveCompletionItem(context.Context, *CompletionItem) (*CompletionItem, error) // completionItem/resolve +- ResolveDocumentLink(context.Context, *DocumentLink) (*DocumentLink, error) // documentLink/resolve +- Exit(context.Context) error // exit +- Initialize(context.Context, *ParamInitialize) (*InitializeResult, error) // initialize +- Initialized(context.Context, *InitializedParams) error // initialized +- Resolve(context.Context, *InlayHint) (*InlayHint, error) // inlayHint/resolve +- DidChangeNotebookDocument(context.Context, *DidChangeNotebookDocumentParams) error // notebookDocument/didChange +- DidCloseNotebookDocument(context.Context, *DidCloseNotebookDocumentParams) error // notebookDocument/didClose +- DidOpenNotebookDocument(context.Context, *DidOpenNotebookDocumentParams) error // notebookDocument/didOpen +- DidSaveNotebookDocument(context.Context, *DidSaveNotebookDocumentParams) error // notebookDocument/didSave +- Shutdown(context.Context) error // shutdown +- CodeAction(context.Context, *CodeActionParams) ([]CodeAction, error) // textDocument/codeAction +- CodeLens(context.Context, *CodeLensParams) ([]CodeLens, error) // textDocument/codeLens +- ColorPresentation(context.Context, *ColorPresentationParams) ([]ColorPresentation, error) // textDocument/colorPresentation +- Completion(context.Context, *CompletionParams) (*CompletionList, error) // textDocument/completion +- Declaration(context.Context, *DeclarationParams) (*Or_textDocument_declaration, error) // textDocument/declaration +- Definition(context.Context, *DefinitionParams) ([]Location, error) // textDocument/definition +- Diagnostic(context.Context, *string) (*string, error) // textDocument/diagnostic +- DidChange(context.Context, *DidChangeTextDocumentParams) error // textDocument/didChange +- DidClose(context.Context, *DidCloseTextDocumentParams) error // textDocument/didClose +- DidOpen(context.Context, *DidOpenTextDocumentParams) error // textDocument/didOpen +- DidSave(context.Context, *DidSaveTextDocumentParams) error // textDocument/didSave +- DocumentColor(context.Context, *DocumentColorParams) ([]ColorInformation, error) // textDocument/documentColor +- DocumentHighlight(context.Context, *DocumentHighlightParams) ([]DocumentHighlight, error) // textDocument/documentHighlight +- DocumentLink(context.Context, *DocumentLinkParams) ([]DocumentLink, error) // textDocument/documentLink +- DocumentSymbol(context.Context, *DocumentSymbolParams) ([]interface{}, error) // textDocument/documentSymbol +- FoldingRange(context.Context, *FoldingRangeParams) ([]FoldingRange, error) // textDocument/foldingRange +- Formatting(context.Context, *DocumentFormattingParams) ([]TextEdit, error) // textDocument/formatting +- Hover(context.Context, *HoverParams) (*Hover, error) // textDocument/hover +- Implementation(context.Context, *ImplementationParams) ([]Location, error) // textDocument/implementation +- InlayHint(context.Context, *InlayHintParams) ([]InlayHint, error) // textDocument/inlayHint +- InlineCompletion(context.Context, *InlineCompletionParams) (*Or_Result_textDocument_inlineCompletion, error) // textDocument/inlineCompletion +- InlineValue(context.Context, *InlineValueParams) ([]InlineValue, error) // textDocument/inlineValue +- LinkedEditingRange(context.Context, *LinkedEditingRangeParams) (*LinkedEditingRanges, error) // textDocument/linkedEditingRange +- Moniker(context.Context, *MonikerParams) ([]Moniker, error) // textDocument/moniker +- OnTypeFormatting(context.Context, *DocumentOnTypeFormattingParams) ([]TextEdit, error) // textDocument/onTypeFormatting +- PrepareCallHierarchy(context.Context, *CallHierarchyPrepareParams) ([]CallHierarchyItem, error) // textDocument/prepareCallHierarchy +- PrepareRename(context.Context, *PrepareRenameParams) (*PrepareRename2Gn, error) // textDocument/prepareRename +- PrepareTypeHierarchy(context.Context, *TypeHierarchyPrepareParams) ([]TypeHierarchyItem, error) // textDocument/prepareTypeHierarchy +- RangeFormatting(context.Context, *DocumentRangeFormattingParams) ([]TextEdit, error) // textDocument/rangeFormatting +- RangesFormatting(context.Context, *DocumentRangesFormattingParams) ([]TextEdit, error) // textDocument/rangesFormatting +- References(context.Context, *ReferenceParams) ([]Location, error) // textDocument/references +- Rename(context.Context, *RenameParams) (*WorkspaceEdit, error) // textDocument/rename +- SelectionRange(context.Context, *SelectionRangeParams) ([]SelectionRange, error) // textDocument/selectionRange +- SemanticTokensFull(context.Context, *SemanticTokensParams) (*SemanticTokens, error) // textDocument/semanticTokens/full +- SemanticTokensFullDelta(context.Context, *SemanticTokensDeltaParams) (interface{}, error) // textDocument/semanticTokens/full/delta +- SemanticTokensRange(context.Context, *SemanticTokensRangeParams) (*SemanticTokens, error) // textDocument/semanticTokens/range +- SignatureHelp(context.Context, *SignatureHelpParams) (*SignatureHelp, error) // textDocument/signatureHelp +- TypeDefinition(context.Context, *TypeDefinitionParams) ([]Location, error) // textDocument/typeDefinition +- WillSave(context.Context, *WillSaveTextDocumentParams) error // textDocument/willSave +- WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit, error) // textDocument/willSaveWaitUntil +- Subtypes(context.Context, *TypeHierarchySubtypesParams) ([]TypeHierarchyItem, error) // typeHierarchy/subtypes +- Supertypes(context.Context, *TypeHierarchySupertypesParams) ([]TypeHierarchyItem, error) // typeHierarchy/supertypes +- WorkDoneProgressCancel(context.Context, *WorkDoneProgressCancelParams) error // window/workDoneProgress/cancel +- DiagnosticWorkspace(context.Context, *WorkspaceDiagnosticParams) (*WorkspaceDiagnosticReport, error) // workspace/diagnostic +- DidChangeConfiguration(context.Context, *DidChangeConfigurationParams) error // workspace/didChangeConfiguration +- DidChangeWatchedFiles(context.Context, *DidChangeWatchedFilesParams) error // workspace/didChangeWatchedFiles +- DidChangeWorkspaceFolders(context.Context, *DidChangeWorkspaceFoldersParams) error // workspace/didChangeWorkspaceFolders +- DidCreateFiles(context.Context, *CreateFilesParams) error // workspace/didCreateFiles +- DidDeleteFiles(context.Context, *DeleteFilesParams) error // workspace/didDeleteFiles +- DidRenameFiles(context.Context, *RenameFilesParams) error // workspace/didRenameFiles +- ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{}, error) // workspace/executeCommand +- Symbol(context.Context, *WorkspaceSymbolParams) ([]SymbolInformation, error) // workspace/symbol +- WillCreateFiles(context.Context, *CreateFilesParams) (*WorkspaceEdit, error) // workspace/willCreateFiles +- WillDeleteFiles(context.Context, *DeleteFilesParams) (*WorkspaceEdit, error) // workspace/willDeleteFiles +- WillRenameFiles(context.Context, *RenameFilesParams) (*WorkspaceEdit, error) // workspace/willRenameFiles +- ResolveWorkspaceSymbol(context.Context, *WorkspaceSymbol) (*WorkspaceSymbol, error) // workspaceSymbol/resolve - NonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) -} - @@ -57947,6 +64110,16 @@ diff -urN a/gopls/internal/lsp/protocol/tsserver.go b/gopls/internal/lsp/protoco - return true, reply(ctx, nil, err) - } - return true, reply(ctx, resp, nil) +- case "textDocument/inlineCompletion": +- var params InlineCompletionParams +- if err := json.Unmarshal(r.Params(), ¶ms); err != nil { +- return true, sendParseError(ctx, reply, err) +- } +- resp, err := server.InlineCompletion(ctx, ¶ms) +- if err != nil { +- return true, reply(ctx, nil, err) +- } +- return true, reply(ctx, resp, nil) - case "textDocument/inlineValue": - var params InlineValueParams - if err := json.Unmarshal(r.Params(), ¶ms); err != nil { @@ -58027,6 +64200,16 @@ diff -urN a/gopls/internal/lsp/protocol/tsserver.go b/gopls/internal/lsp/protoco - return true, reply(ctx, nil, err) - } - return true, reply(ctx, resp, nil) +- case "textDocument/rangesFormatting": +- var params DocumentRangesFormattingParams +- if err := json.Unmarshal(r.Params(), ¶ms); err != nil { +- return true, sendParseError(ctx, reply, err) +- } +- resp, err := server.RangesFormatting(ctx, ¶ms) +- if err != nil { +- return true, reply(ctx, nil, err) +- } +- return true, reply(ctx, resp, nil) - case "textDocument/references": - var params ReferenceParams - if err := json.Unmarshal(r.Params(), ¶ms); err != nil { @@ -58475,6 +64658,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsserver.go b/gopls/internal/lsp/protoco - } - return result, nil -} +-func (s *serverDispatcher) InlineCompletion(ctx context.Context, params *InlineCompletionParams) (*Or_Result_textDocument_inlineCompletion, error) { +- var result *Or_Result_textDocument_inlineCompletion +- if err := s.sender.Call(ctx, "textDocument/inlineCompletion", params, &result); err != nil { +- return nil, err +- } +- return result, nil +-} -func (s *serverDispatcher) InlineValue(ctx context.Context, params *InlineValueParams) ([]InlineValue, error) { - var result []InlineValue - if err := s.sender.Call(ctx, "textDocument/inlineValue", params, &result); err != nil { @@ -58531,6 +64721,13 @@ diff -urN a/gopls/internal/lsp/protocol/tsserver.go b/gopls/internal/lsp/protoco - } - return result, nil -} +-func (s *serverDispatcher) RangesFormatting(ctx context.Context, params *DocumentRangesFormattingParams) ([]TextEdit, error) { +- var result []TextEdit +- if err := s.sender.Call(ctx, "textDocument/rangesFormatting", params, &result); err != nil { +- return nil, err +- } +- return result, nil +-} -func (s *serverDispatcher) References(ctx context.Context, params *ReferenceParams) ([]Location, error) { - var result []Location - if err := s.sender.Call(ctx, "textDocument/references", params, &result); err != nil { @@ -58688,21 +64885,10 @@ diff -urN a/gopls/internal/lsp/protocol/tsserver.go b/gopls/internal/lsp/protoco - } - return result, nil -} -diff -urN a/gopls/internal/lsp/README.md b/gopls/internal/lsp/README.md ---- a/gopls/internal/lsp/README.md 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/README.md 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --# lsp -- --internal/lsp provides much of the Language Server Protocol (lsp) implementation --for gopls. -- --Documentation for users and contributors can be found in the --[`gopls/doc`](../../gopls/doc) directory. diff -urN a/gopls/internal/lsp/references.go b/gopls/internal/lsp/references.go --- a/gopls/internal/lsp/references.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/references.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,25 +0,0 @@ ++++ b/gopls/internal/lsp/references.go 1970-01-01 08:00:00 +@@ -1,36 +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. @@ -58715,22 +64901,33 @@ diff -urN a/gopls/internal/lsp/references.go b/gopls/internal/lsp/references.go - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/lsp/template" +- "golang.org/x/tools/gopls/internal/telemetry" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" -) - --func (s *Server) references(ctx context.Context, params *protocol.ReferenceParams) ([]protocol.Location, error) { +-func (s *Server) references(ctx context.Context, params *protocol.ReferenceParams) (_ []protocol.Location, rerr error) { +- recordLatency := telemetry.StartLatencyTimer("references") +- defer func() { +- recordLatency(ctx, rerr) +- }() +- +- ctx, done := event.Start(ctx, "lsp.Server.references", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) - defer release() - if !ok { - return nil, err - } -- if snapshot.View().FileKind(fh) == source.Tmpl { +- if snapshot.FileKind(fh) == source.Tmpl { - return template.References(ctx, snapshot, fh, params) - } - return source.References(ctx, snapshot, fh, params.Position, params.Context.IncludeDeclaration) -} diff -urN a/gopls/internal/lsp/regtest/doc.go b/gopls/internal/lsp/regtest/doc.go --- a/gopls/internal/lsp/regtest/doc.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/regtest/doc.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/regtest/doc.go 1970-01-01 08:00:00 @@ -1,157 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -58891,8 +65088,8 @@ diff -urN a/gopls/internal/lsp/regtest/doc.go b/gopls/internal/lsp/regtest/doc.g -package regtest diff -urN a/gopls/internal/lsp/regtest/env.go b/gopls/internal/lsp/regtest/env.go --- a/gopls/internal/lsp/regtest/env.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/regtest/env.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,391 +0,0 @@ ++++ b/gopls/internal/lsp/regtest/env.go 1970-01-01 08:00:00 +@@ -1,403 +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. @@ -58964,6 +65161,7 @@ diff -urN a/gopls/internal/lsp/regtest/env.go b/gopls/internal/lsp/regtest/env.g - OnLogMessage: a.onLogMessage, - OnWorkDoneProgressCreate: a.onWorkDoneProgressCreate, - OnProgress: a.onProgress, +- OnShowDocument: a.onShowDocument, - OnShowMessage: a.onShowMessage, - OnShowMessageRequest: a.onShowMessageRequest, - OnRegisterCapability: a.onRegisterCapability, @@ -58977,6 +65175,7 @@ diff -urN a/gopls/internal/lsp/regtest/env.go b/gopls/internal/lsp/regtest/env.g - // diagnostics are a map of relative path->diagnostics params - diagnostics map[string]*protocol.PublishDiagnosticsParams - logs []*protocol.LogMessageParams +- showDocument []*protocol.ShowDocumentParams - showMessage []*protocol.ShowMessageParams - showMessageRequest []*protocol.ShowMessageRequestParams - @@ -59096,6 +65295,15 @@ diff -urN a/gopls/internal/lsp/regtest/env.go b/gopls/internal/lsp/regtest/env.g - return nil -} - +-func (a *Awaiter) onShowDocument(_ context.Context, params *protocol.ShowDocumentParams) error { +- a.mu.Lock() +- defer a.mu.Unlock() +- +- a.state.showDocument = append(a.state.showDocument, params) +- a.checkConditionsLocked() +- return nil +-} +- -func (a *Awaiter) onShowMessage(_ context.Context, m *protocol.ShowMessageParams) error { - a.mu.Lock() - defer a.mu.Unlock() @@ -59237,6 +65445,7 @@ diff -urN a/gopls/internal/lsp/regtest/env.go b/gopls/internal/lsp/regtest/env.g -// unmeetable. If it was met, OnceMet checks that the state meets all -// expectations in mustMeets. -func (e *Env) OnceMet(precondition Expectation, mustMeets ...Expectation) { +- e.T.Helper() - e.Await(OnceMet(precondition, mustMeets...)) -} - @@ -59286,7 +65495,7 @@ diff -urN a/gopls/internal/lsp/regtest/env.go b/gopls/internal/lsp/regtest/env.g -} diff -urN a/gopls/internal/lsp/regtest/env_test.go b/gopls/internal/lsp/regtest/env_test.go --- a/gopls/internal/lsp/regtest/env_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/regtest/env_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/regtest/env_test.go 1970-01-01 08:00:00 @@ -1,66 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -59356,8 +65565,8 @@ diff -urN a/gopls/internal/lsp/regtest/env_test.go b/gopls/internal/lsp/regtest/ -} diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regtest/expectation.go --- a/gopls/internal/lsp/regtest/expectation.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/regtest/expectation.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,769 +0,0 @@ ++++ b/gopls/internal/lsp/regtest/expectation.go 1970-01-01 08:00:00 +@@ -1,807 +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. @@ -59370,6 +65579,7 @@ diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regte - "sort" - "strings" - +- "github.com/google/go-cmp/cmp" - "golang.org/x/tools/gopls/internal/lsp" - "golang.org/x/tools/gopls/internal/lsp/protocol" -) @@ -59465,6 +65675,26 @@ diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regte - return strings.Join(descriptions, "\n") -} - +-// Not inverts the sense of an expectation: a met expectation is unmet, and an +-// unmet expectation is met. +-func Not(e Expectation) Expectation { +- check := func(s State) Verdict { +- switch v := e.Check(s); v { +- case Met: +- return Unmet +- case Unmet, Unmeetable: +- return Met +- default: +- panic(fmt.Sprintf("unexpected verdict %v", v)) +- } +- } +- description := describeExpectations(e) +- return Expectation{ +- Check: check, +- Description: fmt.Sprintf("not: %s", description), +- } +-} +- -// AnyOf returns an expectation that is satisfied when any of the given -// expectations is met. -func AnyOf(anyOf ...Expectation) Expectation { @@ -59551,18 +65781,20 @@ diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regte - } -} - --// NoOutstandingWork asserts that there is no work initiated using the LSP --// $/progress API that has not completed. --func NoOutstandingWork() Expectation { +-// ShownDocument asserts that the client has received a +-// ShowDocumentRequest for the given URI. +-func ShownDocument(uri protocol.URI) Expectation { - check := func(s State) Verdict { -- if len(s.outstandingWork()) == 0 { -- return Met +- for _, params := range s.showDocument { +- if params.URI == uri { +- return Met +- } - } - return Unmet - } - return Expectation{ - Check: check, -- Description: "no outstanding work", +- Description: fmt.Sprintf("received window/showDocument for URI %s", uri), - } -} - @@ -59595,30 +65827,28 @@ diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regte - } - return Expectation{ - Check: check, -- Description: "received ShowMessage", +- Description: fmt.Sprintf("received window/showMessage containing %q", containing), - } -} - --// ShowMessageRequest asserts that the editor has received a ShowMessageRequest --// with an action item that has the given title. --func ShowMessageRequest(title string) Expectation { +-// ShownMessageRequest asserts that the editor has received a +-// ShowMessageRequest with message matching the given regular expression. +-func ShownMessageRequest(messageRegexp string) Expectation { +- msgRE := regexp.MustCompile(messageRegexp) - check := func(s State) Verdict { - if len(s.showMessageRequest) == 0 { - return Unmet - } -- // Only check the most recent one. -- m := s.showMessageRequest[len(s.showMessageRequest)-1] -- if len(m.Actions) == 0 || len(m.Actions) > 1 { -- return Unmet -- } -- if m.Actions[0].Title == title { -- return Met +- for _, m := range s.showMessageRequest { +- if msgRE.MatchString(m.Message) { +- return Met +- } - } - return Unmet - } - return Expectation{ - Check: check, -- Description: "received ShowMessageRequest", +- Description: fmt.Sprintf("ShowMessageRequest matching %q", messageRegexp), - } -} - @@ -59634,11 +65864,12 @@ diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regte -func (e *Env) DoneDiagnosingChanges() Expectation { - stats := e.Editor.Stats() - statsBySource := map[lsp.ModificationSource]uint64{ -- lsp.FromDidOpen: stats.DidOpen, -- lsp.FromDidChange: stats.DidChange, -- lsp.FromDidSave: stats.DidSave, -- lsp.FromDidChangeWatchedFiles: stats.DidChangeWatchedFiles, -- lsp.FromDidClose: stats.DidClose, +- lsp.FromDidOpen: stats.DidOpen, +- lsp.FromDidChange: stats.DidChange, +- lsp.FromDidSave: stats.DidSave, +- lsp.FromDidChangeWatchedFiles: stats.DidChangeWatchedFiles, +- lsp.FromDidClose: stats.DidClose, +- lsp.FromDidChangeConfiguration: stats.DidChangeConfiguration, - } - - var expected []lsp.ModificationSource @@ -59817,6 +66048,46 @@ diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regte - } -} - +-// NoOutstandingWork asserts that there is no work initiated using the LSP +-// $/progress API that has not completed. +-// +-// If non-nil, the ignore func is used to ignore certain work items for the +-// purpose of this check. +-// +-// TODO(rfindley): consider refactoring to treat outstanding work the same way +-// we treat diagnostics: with an algebra of filters. +-func NoOutstandingWork(ignore func(title, msg string) bool) Expectation { +- check := func(s State) Verdict { +- for _, w := range s.work { +- if w.complete { +- continue +- } +- if w.title == "" { +- // A token that has been created but not yet used. +- // +- // TODO(rfindley): this should be separated in the data model: until +- // the "begin" notification, work should not be in progress. +- continue +- } +- if ignore(w.title, w.msg) { +- continue +- } +- return Unmet +- } +- return Met +- } +- return Expectation{ +- Check: check, +- Description: "no outstanding work", +- } +-} +- +-// IgnoreTelemetryPromptWork may be used in conjunction with NoOutStandingWork +-// to ignore the telemetry prompt. +-func IgnoreTelemetryPromptWork(title, msg string) bool { +- return title == lsp.TelemetryPromptWorkTitle +-} +- -// NoErrorLogs asserts that the client has not received any log messages of -// error severity. -func NoErrorLogs() Expectation { @@ -59829,6 +66100,10 @@ diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regte -// The count argument specifies the expected number of matching logs. If -// atLeast is set, this is a lower bound, otherwise there must be exactly count -// matching logs. +-// +-// Logs are asynchronous to other LSP messages, so this expectation should not +-// be used with combinators such as OnceMet or AfterChange that assert on +-// ordering with respect to other operations. -func LogMatching(typ protocol.MessageType, re string, count int, atLeast bool) Expectation { - rec, err := regexp.Compile(re) - if err != nil { @@ -59845,6 +66120,11 @@ diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regte - if found == count || (found >= count && atLeast) { - return Met - } +- // If we require an exact count, and have received more than expected, the +- // expectation can never be met. +- if found > count && !atLeast { +- return Unmeetable +- } - return Unmet - } - desc := fmt.Sprintf("log message matching %q expected %v times", re, count) @@ -59936,50 +66216,6 @@ diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regte - return jsonProperty(m[path[0]], path[1:]...) -} - --// RegistrationMatching asserts that the client has received a capability --// registration matching the given regexp. --// --// TODO(rfindley): remove this once TestWatchReplaceTargets has been revisited. --// --// Deprecated: use (No)FileWatchMatching --func RegistrationMatching(re string) Expectation { -- rec := regexp.MustCompile(re) -- check := func(s State) Verdict { -- for _, p := range s.registrations { -- for _, r := range p.Registrations { -- if rec.Match([]byte(r.Method)) { -- return Met -- } -- } -- } -- return Unmet -- } -- return Expectation{ -- Check: check, -- Description: fmt.Sprintf("registration matching %q", re), -- } --} -- --// UnregistrationMatching asserts that the client has received an --// unregistration whose ID matches the given regexp. --func UnregistrationMatching(re string) Expectation { -- rec := regexp.MustCompile(re) -- check := func(s State) Verdict { -- for _, p := range s.unregistrations { -- for _, r := range p.Unregisterations { -- if rec.Match([]byte(r.Method)) { -- return Met -- } -- } -- } -- return Unmet -- } -- return Expectation{ -- Check: check, -- Description: fmt.Sprintf("unregistration matching %q", re), -- } --} -- -// Diagnostics asserts that there is at least one diagnostic matching the given -// filters. -func Diagnostics(filters ...DiagnosticFilter) Expectation { @@ -60127,10 +66363,21 @@ diff -urN a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regte - }, - } -} +- +-// WithSeverityTags filters to diagnostics whose severity and tags match +-// the given expectation. +-func WithSeverityTags(diagName string, severity protocol.DiagnosticSeverity, tags []protocol.DiagnosticTag) DiagnosticFilter { +- return DiagnosticFilter{ +- desc: fmt.Sprintf("with diagnostic %q with severity %q and tag %#q", diagName, severity, tags), +- check: func(_ string, d protocol.Diagnostic) bool { +- return d.Source == diagName && d.Severity == severity && cmp.Equal(d.Tags, tags) +- }, +- } +-} diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/marker.go --- a/gopls/internal/lsp/regtest/marker.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/regtest/marker.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1273 +0,0 @@ ++++ b/gopls/internal/lsp/regtest/marker.go 1970-01-01 08:00:00 +@@ -1,2227 +0,0 @@ -// Copyright 2023 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. @@ -60144,16 +66391,20 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - "flag" - "fmt" - "go/token" +- "go/types" - "io/fs" +- "log" - "os" - "path" - "path/filepath" - "reflect" - "regexp" +- "runtime" - "sort" - "strings" - "testing" - +- "github.com/google/go-cmp/cmp" - "golang.org/x/tools/go/expect" - "golang.org/x/tools/gopls/internal/hooks" - "golang.org/x/tools/gopls/internal/lsp/cache" @@ -60174,6 +66425,11 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -var update = flag.Bool("update", false, "if set, update test data during marker tests") - -// RunMarkerTests runs "marker" tests in the given test data directory. +-// (In practice: ../../regtest/marker/testdata) +-// +-// Use this command to run the tests: +-// +-// $ go test ./gopls/internal/regtest/marker [-update] -// -// A marker test uses the '//@' marker syntax of the x/tools/go/expect package -// to annotate source code with various information such as locations and @@ -60223,14 +66479,28 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -// -// # Special files -// --// There are three types of file within the test archive that are given special +-// There are several types of file within the test archive that are given special -// treatment by the test runner: +-// - "skip": the presence of this file causes the test to be skipped, with +-// the file content used as the skip message. -// - "flags": this file is treated as a whitespace-separated list of flags --// that configure the MarkerTest instance. For example, -min_go=go1.18 sets --// the minimum required Go version for the test. +-// that configure the MarkerTest instance. Supported flags: +-// -min_go=go1.18 sets the minimum Go version for the test; +-// -cgo requires that CGO_ENABLED is set and the cgo tool is available +-// -write_sumfile=a,b,c instructs the test runner to generate go.sum files +-// in these directories before running the test. +-// -skip_goos=a,b,c instructs the test runner to skip the test for the +-// listed GOOS values. +-// -ignore_extra_diags suppresses errors for unmatched diagnostics +-// TODO(rfindley): using build constraint expressions for -skip_goos would +-// be clearer. -// TODO(rfindley): support flag values containing whitespace. -// - "settings.json": this file is parsed as JSON, and used as the -// session configuration (see gopls/doc/settings.md) +-// - "capabilities.json": this file is parsed as JSON client capabilities, +-// and applied as an overlay over the default editor client capabilities. +-// see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#clientCapabilities +-// for more details. -// - "env": this file is parsed as a list of VAR=VALUE fields specifying the -// editor environment. -// - Golden files: Within the archive, file names starting with '@' are @@ -60240,22 +66510,88 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -// Foo were of type *Golden, the test runner would convert the identifier a -// in the call @foo(a, "b", 3) into a *Golden by collecting golden file -// data starting with "@a/". +-// - proxy files: any file starting with proxy/ is treated as a Go proxy +-// file. If present, these files are written to a separate temporary +-// directory and GOPROXY is set to file://. -// -// # Marker types -// +-// Markers are of two kinds. A few are "value markers" (e.g. @item), which are +-// processed in a first pass and each computes a value that may be referred to +-// by name later. Most are "action markers", which are processed in a second +-// pass and take some action such as testing an LSP operation; they may refer +-// to values computed by value markers. +-// -// The following markers are supported within marker tests: -// +-// - acceptcompletion(location, label, golden): specifies that accepting the +-// completion candidate produced at the given location with provided label +-// results in the given golden state. +-// +-// - codeaction(kind, start, end, golden): specifies a codeaction to request +-// for the given range. To support multi-line ranges, the range is defined +-// to be between start.Start and end.End. The golden directory contains +-// changed file content after the code action is applied. +-// +-// - codeactionerr(kind, start, end, wantError): specifies a codeaction that +-// fails with an error that matches the expectation. +-// +-// - codelens(location, title): specifies that a codelens is expected at the +-// given location, with given title. Must be used in conjunction with +-// @codelenses. +-// +-// - codelenses(): specifies that textDocument/codeLens should be run for the +-// current document, with results compared to the @codelens annotations in +-// the current document. +-// +-// - complete(location, ...items): specifies expected completion results at +-// the given location. Must be used in conjunction with @item. +-// -// - diag(location, regexp): specifies an expected diagnostic matching the -// given regexp at the given location. The test runner requires --// a 1:1 correspondence between observed diagnostics and diag annotations +-// a 1:1 correspondence between observed diagnostics and diag annotations. +-// The diagnostics source and kind fields are ignored, to reduce fuss. +-// +-// The specified location must match the start position of the diagnostic, +-// but end positions are ignored. +-// +-// TODO(adonovan): in the older marker framework, the annotation asserted +-// two additional fields (source="compiler", kind="error"). Restore them? -// -// - def(src, dst location): perform a textDocument/definition request at --// the src location, and check the the result points to the dst location. +-// the src location, and check the result points to the dst location. +-// +-// - foldingrange(golden): perform a textDocument/foldingRange for the +-// current document, and compare with the golden content, which is the +-// original source annotated with numbered tags delimiting the resulting +-// ranges (e.g. <1 kind="..."> ... ). +-// +-// - format(golden): perform a textDocument/format request for the enclosing +-// file, and compare against the named golden file. If the formatting +-// request succeeds, the golden file must contain the resulting formatted +-// source. If the formatting request fails, the golden file must contain +-// the error message. +-// +-// - highlight(src location, dsts ...location): makes a +-// textDocument/highlight request at the given src location, which should +-// highlight the provided dst locations. -// -// - hover(src, dst location, g Golden): perform a textDocument/hover at the -// src location, and checks that the result is the dst location, with hover -// content matching "hover.md" in the golden data g. -// +-// - implementations(src location, want ...location): makes a +-// textDocument/implementation query at the src location and +-// checks that the resulting set of locations matches want. +-// +-// - item(label, details, kind): defines a completion item with the provided +-// fields. This information is not positional, and therefore @item markers +-// may occur anywhere in the source. Used in conjunction with @complete, +-// snippet, or rank. +-// +-// TODO(rfindley): rethink whether floating @item annotations are the best +-// way to specify completion results. +-// -// - loc(name, location): specifies the name for a location in the source. These -// locations may be referenced by other markers. -// @@ -60272,6 +66608,47 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -// This action is executed for its editing effects on the source files. -// Like rename, the golden directory contains the expected transformed files. -// +-// - rank(location, ...completionItem): executes a textDocument/completion +-// request at the given location, and verifies that each expected +-// completion item occurs in the results, in the expected order. Other +-// unexpected completion items may occur in the results. +-// TODO(rfindley): this exists for compatibility with the old marker tests. +-// Replace this with rankl, and rename. +-// +-// - rankl(location, ...label): like rank, but only cares about completion +-// item labels. +-// +-// - refs(location, want ...location): executes a textDocument/references +-// request at the first location and asserts that the result is the set of +-// 'want' locations. The first want location must be the declaration +-// (assumedly unique). +-// +-// - snippet(location, completionItem, snippet): executes a +-// textDocument/completion request at the location, and searches for a +-// result with label matching that of the provided completion item +-// (TODO(rfindley): accept a label rather than a completion item). Check +-// the the result snippet matches the provided snippet. +-// +-// - symbol(golden): makes a textDocument/documentSymbol request +-// for the enclosing file, formats the response with one symbol +-// per line, sorts it, and compares against the named golden file. +-// Each line is of the form: +-// +-// dotted.symbol.name kind "detail" +n lines +-// +-// where the "+n lines" part indicates that the declaration spans +-// several lines. The test otherwise makes no attempt to check +-// location information. There is no point to using more than one +-// @symbol marker in a given file. +-// +-// - workspacesymbol(query, golden): makes a workspace/symbol request for the +-// given query, formats the response with one symbol per line, and compares +-// against the named golden file. As workspace symbols are by definition a +-// workspace-wide request, the location of the workspace symbol marker does +-// not matter. Each line is of the form: +-// +-// location name kind +-// -// # Argument conversion -// -// Marker arguments are first parsed by the go/expect package, which accepts @@ -60357,36 +66734,30 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -// internal/lsp/tests. -// -// Remaining TODO: --// - parallelize/optimize test execution +-// - optimize test execution -// - reorganize regtest packages (and rename to just 'test'?) -// - Rename the files .txtar. +-// - Provide some means by which locations in the standard library +-// (or builtin.go) can be named, so that, for example, we can we +-// can assert that MyError implements the built-in error type. -// --// Existing marker tests to port: +-// Existing marker tests (in ../testdata) to port: -// - CallHierarchy --// - CodeLens -// - Diagnostics -// - CompletionItems -// - Completions -// - CompletionSnippets --// - UnimportedCompletions -// - DeepCompletions -// - FuzzyCompletions -// - CaseSensitiveCompletions -// - RankCompletions --// - FoldingRanges -// - Formats -// - Imports -// - SemanticTokens --// - SuggestedFixes -// - FunctionExtractions -// - MethodExtractions --// - Definitions --// - Implementations --// - Highlights --// - References -// - Renames -// - PrepareRenames --// - Symbols -// - InlayHints -// - WorkspaceSymbols -// - Signatures @@ -60407,7 +66778,18 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - cache := cache.New(nil) - - for _, test := range tests { +- test := test - t.Run(test.name, func(t *testing.T) { +- t.Parallel() +- if test.skipReason != "" { +- t.Skip(test.skipReason) +- } +- for _, goos := range test.skipGOOS { +- if runtime.GOOS == goos { +- t.Skipf("skipping on %s due to -skip_goos", runtime.GOOS) +- } +- } +- - // TODO(rfindley): it may be more useful to have full support for build - // constraints. - if test.minGoVersion != "" { @@ -60415,18 +66797,31 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - if _, err := fmt.Sscanf(test.minGoVersion, "go1.%d", &go1point); err != nil { - t.Fatalf("parsing -min_go version: %v", err) - } -- testenv.NeedsGo1Point(t, 18) +- testenv.NeedsGo1Point(t, go1point) +- } +- if test.cgo { +- testenv.NeedsTool(t, "cgo") - } - config := fake.EditorConfig{ -- Settings: test.settings, -- Env: test.env, +- Settings: test.settings, +- CapabilitiesJSON: test.capabilities, +- Env: test.env, - } -- run := &markerTestRun{ -- test: test, -- env: newEnv(t, cache, test.files, config), +- if _, ok := config.Settings["diagnosticsDelay"]; !ok { +- if config.Settings == nil { +- config.Settings = make(map[string]any) +- } +- config.Settings["diagnosticsDelay"] = "10ms" +- } +- // inv: config.Settings != nil - -- locations: make(map[expect.Identifier]protocol.Location), -- diags: make(map[protocol.Location][]protocol.Diagnostic), +- run := &markerTestRun{ +- test: test, +- env: newEnv(t, cache, test.files, test.proxyFiles, test.writeGoSum, config), +- settings: config.Settings, +- values: make(map[expect.Identifier]any), +- diags: make(map[protocol.Location][]protocol.Diagnostic), +- extraNotes: make(map[protocol.DocumentURI]map[string][]*expect.Note), - } - // TODO(rfindley): make it easier to clean up the regtest environment. - defer run.env.Editor.Shutdown(context.Background()) // ignore error @@ -60440,19 +66835,6 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - for file := range test.files { - run.env.OpenFile(file) - } -- -- // Pre-process locations. -- var markers []marker -- for _, note := range test.notes { -- mark := marker{run: run, note: note} -- switch note.Name { -- case "loc": -- mark.execute() -- default: -- markers = append(markers, mark) -- } -- } -- - // Wait for the didOpen notifications to be processed, then collect - // diagnostics. - var diags map[string]*protocol.PublishDiagnosticsParams @@ -60461,22 +66843,52 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - uri := run.env.Sandbox.Workdir.URI(path) - for _, diag := range params.Diagnostics { - loc := protocol.Location{ -- URI: uri, -- Range: diag.Range, +- URI: uri, +- Range: protocol.Range{ +- Start: diag.Range.Start, +- End: diag.Range.Start, // ignore end positions +- }, - } - run.diags[loc] = append(run.diags[loc], diag) - } - } - +- var markers []marker +- for _, note := range test.notes { +- mark := marker{run: run, note: note} +- if fn, ok := valueMarkerFuncs[note.Name]; ok { +- fn(mark) +- } else if _, ok := actionMarkerFuncs[note.Name]; ok { +- markers = append(markers, mark) // save for later +- } else { +- uri := mark.uri() +- if run.extraNotes[uri] == nil { +- run.extraNotes[uri] = make(map[string][]*expect.Note) +- } +- run.extraNotes[uri][note.Name] = append(run.extraNotes[uri][note.Name], note) +- } +- } +- - // Invoke each remaining marker in the test. - for _, mark := range markers { -- mark.execute() +- actionMarkerFuncs[mark.note.Name](mark) - } - - // Any remaining (un-eliminated) diagnostics are an error. -- for loc, diags := range run.diags { -- for _, diag := range diags { -- t.Errorf("%s: unexpected diagnostic: %q", run.fmtLoc(loc), diag.Message) +- if !test.ignoreExtraDiags { +- for loc, diags := range run.diags { +- for _, diag := range diags { +- t.Errorf("%s: unexpected diagnostic: %q", run.fmtLoc(loc), diag.Message) +- } +- } +- } +- +- // TODO(rfindley): use these for whole-file marker tests. +- for uri, extras := range run.extraNotes { +- for name, extra := range extras { +- if len(extra) > 0 { +- t.Errorf("%s: %d unused %q markers", run.env.Sandbox.Workdir.URIToPath(uri), len(extra), name) +- } - } - } - @@ -60505,6 +66917,10 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - } - }) - } +- +- if abs, err := filepath.Abs(dir); err == nil && t.Failed() { +- t.Logf("(Filenames are relative to %s.)", abs) +- } -} - -// A marker holds state for the execution of a single @marker @@ -60514,9 +66930,16 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - note *expect.Note -} - +-// server returns the LSP server for the marker test run. +-func (m marker) server() protocol.Server { +- return m.run.env.Editor.Server +-} +- -// errorf reports an error with a prefix indicating the position of the marker note. --func (mark marker) errorf(format string, args ...interface{}) { -- msg := fmt.Sprintf(format, args...) +-// +-// It formats the error message using mark.sprintf. +-func (mark marker) errorf(format string, args ...any) { +- msg := mark.sprintf(format, args...) - // TODO(adonovan): consider using fmt.Fprintf(os.Stderr)+t.Fail instead of - // t.Errorf to avoid reporting uninteresting positions in the Go source of - // the driver. However, this loses the order of stderr wrt "FAIL: TestFoo" @@ -60524,56 +66947,164 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - mark.run.env.T.Errorf("%s: %s", mark.run.fmtPos(mark.note.Pos), msg) -} - --// execute invokes the marker's function with the arguments from note. --func (mark marker) execute() { -- fn, ok := markerFuncs[mark.note.Name] -- if !ok { -- mark.errorf("no marker function named %s", mark.note.Name) -- return +-// valueMarkerFunc returns a wrapper around a function that allows it to be +-// called during the processing of value markers (e.g. @value(v, 123)) with marker +-// arguments converted to function parameters. The provided function's first +-// parameter must be of type 'marker', and it must return a value. +-// +-// Unlike action markers, which are executed for actions such as test +-// assertions, value markers are all evaluated first, and each computes +-// a value that is recorded by its identifier, which is the marker's first +-// argument. These values may be referred to from an action marker by +-// this identifier, e.g. @action(... , v, ...). +-// +-// For example, given a fn with signature +-// +-// func(mark marker, label, details, kind string) CompletionItem +-// +-// The result of valueMarkerFunc can associated with @item notes, and invoked +-// as follows: +-// +-// //@item(FooCompletion, "Foo", "func() int", "func") +-// +-// The provided fn should not mutate the test environment. +-func valueMarkerFunc(fn any) func(marker) { +- ftype := reflect.TypeOf(fn) +- if ftype.NumIn() == 0 || ftype.In(0) != markerType { +- panic(fmt.Sprintf("value marker function %#v must accept marker as its first argument", ftype)) - } -- -- // The first converter corresponds to the *Env argument. -- // All others must be converted from the marker syntax. -- if got, want := len(mark.note.Args), len(fn.converters); got != want { -- mark.errorf("got %d arguments to %s, expect %d", got, mark.note.Name, want) -- return +- if ftype.NumOut() != 1 { +- panic(fmt.Sprintf("value marker function %#v must have exactly 1 result", ftype)) - } - -- args := []reflect.Value{reflect.ValueOf(mark)} -- for i, in := range mark.note.Args { -- // Special handling for the blank identifier: treat it as the zero -- // value. -- if ident, ok := in.(expect.Identifier); ok && ident == "_" { -- zero := reflect.Zero(fn.paramTypes[i]) -- args = append(args, zero) -- continue +- return func(mark marker) { +- if len(mark.note.Args) == 0 || !is[expect.Identifier](mark.note.Args[0]) { +- mark.errorf("first argument to a value marker function must be an identifier") +- return +- } +- id := mark.note.Args[0].(expect.Identifier) +- if alt, ok := mark.run.values[id]; ok { +- mark.errorf("%s already declared as %T", id, alt) +- return - } -- out, err := fn.converters[i](mark, in) +- args := append([]any{mark}, mark.note.Args[1:]...) +- argValues, err := convertArgs(mark, ftype, args) - if err != nil { -- mark.errorf("converting argument #%d of %s (%v): %v", i, mark.note.Name, in, err) +- mark.errorf("converting args: %v", err) - return - } -- args = append(args, reflect.ValueOf(out)) +- results := reflect.ValueOf(fn).Call(argValues) +- mark.run.values[id] = results[0].Interface() - } -- -- fn.fn.Call(args) -} - --// Supported marker functions. --// --// Each marker function must accept a marker as its first argument, with --// subsequent arguments converted from the marker arguments. +-// actionMarkerFunc returns a wrapper around a function that allows it to be +-// called during the processing of action markers (e.g. @action("abc", 123)) +-// with marker arguments converted to function parameters. The provided +-// function's first parameter must be of type 'marker', and it must not return +-// any values. -// --// Marker funcs should not mutate the test environment (e.g. via opening files --// or applying edits in the editor). --var markerFuncs = map[string]markerFunc{ -- "def": makeMarkerFunc(defMarker), -- "diag": makeMarkerFunc(diagMarker), -- "hover": makeMarkerFunc(hoverMarker), -- "loc": makeMarkerFunc(locMarker), -- "rename": makeMarkerFunc(renameMarker), -- "renameerr": makeMarkerFunc(renameErrMarker), -- "suggestedfix": makeMarkerFunc(suggestedfixMarker), +-// The provided fn should not mutate the test environment. +-func actionMarkerFunc(fn any) func(marker) { +- ftype := reflect.TypeOf(fn) +- if ftype.NumIn() == 0 || ftype.In(0) != markerType { +- panic(fmt.Sprintf("action marker function %#v must accept marker as its first argument", ftype)) +- } +- if ftype.NumOut() != 0 { +- panic(fmt.Sprintf("action marker function %#v cannot have results", ftype)) +- } +- +- return func(mark marker) { +- args := append([]any{mark}, mark.note.Args...) +- argValues, err := convertArgs(mark, ftype, args) +- if err != nil { +- mark.errorf("converting args: %v", err) +- return +- } +- reflect.ValueOf(fn).Call(argValues) +- } +-} +- +-func convertArgs(mark marker, ftype reflect.Type, args []any) ([]reflect.Value, error) { +- var ( +- argValues []reflect.Value +- pnext int // next param index +- p reflect.Type // current param +- ) +- for i, arg := range args { +- if i < ftype.NumIn() { +- p = ftype.In(pnext) +- pnext++ +- } else if p == nil || !ftype.IsVariadic() { +- // The actual number of arguments expected by the mark varies, depending +- // on whether this is a value marker or an action marker. +- // +- // Since this error indicates a bug, probably OK to have an imprecise +- // error message here. +- return nil, fmt.Errorf("too many arguments to %s", mark.note.Name) +- } +- elemType := p +- if ftype.IsVariadic() && pnext == ftype.NumIn() { +- elemType = p.Elem() +- } +- var v reflect.Value +- if id, ok := arg.(expect.Identifier); ok && id == "_" { +- v = reflect.Zero(elemType) +- } else { +- a, err := convert(mark, arg, elemType) +- if err != nil { +- return nil, err +- } +- v = reflect.ValueOf(a) +- } +- argValues = append(argValues, v) +- } +- // Check that we have sufficient arguments. If the function is variadic, we +- // do not need arguments for the final parameter. +- if pnext < ftype.NumIn()-1 || pnext == ftype.NumIn()-1 && !ftype.IsVariadic() { +- // Same comment as above: OK to be vague here. +- return nil, fmt.Errorf("not enough arguments to %s", mark.note.Name) +- } +- return argValues, nil +-} +- +-// is reports whether arg is a T. +-func is[T any](arg any) bool { +- _, ok := arg.(T) +- return ok +-} +- +-// Supported value marker functions. See [valueMarkerFunc] for more details. +-var valueMarkerFuncs = map[string]func(marker){ +- "loc": valueMarkerFunc(locMarker), +- "item": valueMarkerFunc(completionItemMarker), +-} +- +-// Supported action marker functions. See [actionMarkerFunc] for more details. +-var actionMarkerFuncs = map[string]func(marker){ +- "acceptcompletion": actionMarkerFunc(acceptCompletionMarker), +- "codeaction": actionMarkerFunc(codeActionMarker), +- "codeactionerr": actionMarkerFunc(codeActionErrMarker), +- "codelenses": actionMarkerFunc(codeLensesMarker), +- "complete": actionMarkerFunc(completeMarker), +- "def": actionMarkerFunc(defMarker), +- "diag": actionMarkerFunc(diagMarker), +- "foldingrange": actionMarkerFunc(foldingRangeMarker), +- "format": actionMarkerFunc(formatMarker), +- "highlight": actionMarkerFunc(highlightMarker), +- "hover": actionMarkerFunc(hoverMarker), +- "implementation": actionMarkerFunc(implementationMarker), +- "rank": actionMarkerFunc(rankMarker), +- "rankl": actionMarkerFunc(ranklMarker), +- "refs": actionMarkerFunc(refsMarker), +- "rename": actionMarkerFunc(renameMarker), +- "renameerr": actionMarkerFunc(renameErrMarker), +- "signature": actionMarkerFunc(signatureMarker), +- "snippet": actionMarkerFunc(snippetMarker), +- "suggestedfix": actionMarkerFunc(suggestedfixMarker), +- "symbol": actionMarkerFunc(symbolMarker), +- "typedef": actionMarkerFunc(typedefMarker), +- "workspacesymbol": actionMarkerFunc(workspaceSymbolMarker), -} - -// markerTest holds all the test data extracted from a test txtar archive. @@ -60581,20 +67112,27 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -// See the documentation for RunMarkerTests for more information on the archive -// format. -type markerTest struct { -- name string // relative path to the txtar file in the testdata dir -- fset *token.FileSet // fileset used for parsing notes -- content []byte // raw test content -- archive *txtar.Archive // original test archive -- settings map[string]interface{} // gopls settings -- env map[string]string // editor environment -- files map[string][]byte // data files from the archive (excluding special files) -- notes []*expect.Note // extracted notes from data files -- golden map[string]*Golden // extracted golden content, by identifier name -- -- // flags holds flags extracted from the special "flags" archive file. -- flags []string +- name string // relative path to the txtar file in the testdata dir +- fset *token.FileSet // fileset used for parsing notes +- content []byte // raw test content +- archive *txtar.Archive // original test archive +- settings map[string]any // gopls settings +- capabilities []byte // content of capabilities.json file +- env map[string]string // editor environment +- proxyFiles map[string][]byte // proxy content +- files map[string][]byte // data files from the archive (excluding special files) +- notes []*expect.Note // extracted notes from data files +- golden map[expect.Identifier]*Golden // extracted golden content, by identifier name +- +- skipReason string // the skip reason extracted from the "skip" archive file +- flags []string // flags extracted from the special "flags" archive file. +- - // Parsed flags values. -- minGoVersion string +- minGoVersion string +- cgo bool +- writeGoSum []string // comma separated dirs to write go sum for +- skipGOOS []string // comma separated GOOS values to skip +- ignoreExtraDiags bool -} - -// flagSet returns the flagset used for parsing the special "flags" file in the @@ -60602,10 +67140,30 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -func (t *markerTest) flagSet() *flag.FlagSet { - flags := flag.NewFlagSet(t.name, flag.ContinueOnError) - flags.StringVar(&t.minGoVersion, "min_go", "", "if set, the minimum go1.X version required for this test") +- flags.BoolVar(&t.cgo, "cgo", false, "if set, requires cgo (both the cgo tool and CGO_ENABLED=1)") +- flags.Var((*stringListValue)(&t.writeGoSum), "write_sumfile", "if set, write the sumfile for these directories") +- flags.Var((*stringListValue)(&t.skipGOOS), "skip_goos", "if set, skip this test on these GOOS values") +- flags.BoolVar(&t.ignoreExtraDiags, "ignore_extra_diags", false, "if set, suppress errors for unmatched diagnostics") - return flags -} - --func (t *markerTest) getGolden(id string) *Golden { +-// stringListValue implements flag.Value. +-type stringListValue []string +- +-func (l *stringListValue) Set(s string) error { +- if s != "" { +- for _, d := range strings.Split(s, ",") { +- *l = append(*l, strings.TrimSpace(d)) +- } +- } +- return nil +-} +- +-func (l stringListValue) String() string { +- return strings.Join([]string(l), ",") +-} +- +-func (t *markerTest) getGolden(id expect.Identifier) *Golden { - golden, ok := t.golden[id] - // If there was no golden content for this identifier, we must create one - // to handle the case where -update is set: we need a place to store @@ -60627,7 +67185,7 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -// When -update is set, golden captures the updated golden contents for later -// writing. -type Golden struct { -- id string +- id expect.Identifier - data map[string][]byte // key "" => @id itself - updated map[string][]byte -} @@ -60670,9 +67228,6 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -// -// See the documentation for RunMarkerTests for more details on the test data -// archive. --// --// TODO(rfindley): this test could sanity check the results. For example, it is --// too easy to write "// @" instead of "//@", which we will happy skip silently. -func loadMarkerTests(dir string) ([]*markerTest, error) { - var tests []*markerTest - err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { @@ -60681,6 +67236,7 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - if err != nil { - return err - } +- - name := strings.TrimPrefix(path, dir+string(filepath.Separator)) - test, err := loadMarkerTest(name, content) - if err != nil { @@ -60695,16 +67251,28 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - -func loadMarkerTest(name string, content []byte) (*markerTest, error) { - archive := txtar.Parse(content) +- if len(archive.Files) == 0 { +- return nil, fmt.Errorf("txtar file has no '-- filename --' sections") +- } +- if bytes.Contains(archive.Comment, []byte("\n-- ")) { +- // This check is conservative, but the comment is only a comment. +- return nil, fmt.Errorf("ill-formed '-- filename --' header in comment") +- } - test := &markerTest{ - name: name, - fset: token.NewFileSet(), - content: content, - archive: archive, - files: make(map[string][]byte), -- golden: make(map[string]*Golden), +- golden: make(map[expect.Identifier]*Golden), - } - for _, file := range archive.Files { - switch { +- case file.Name == "skip": +- reason := strings.ReplaceAll(string(file.Data), "\n", " ") +- reason = strings.TrimSpace(reason) +- test.skipReason = reason +- - case file.Name == "flags": - test.flags = strings.Fields(string(file.Data)) - if err := test.flagSet().Parse(test.flags); err != nil { @@ -60716,12 +67284,14 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - return nil, err - } - +- case file.Name == "capabilities.json": +- test.capabilities = file.Data // lazily unmarshalled by the editor +- - case file.Name == "env": - test.env = make(map[string]string) - fields := strings.Fields(string(file.Data)) - for _, field := range fields { -- // TODO: use strings.Cut once we are on 1.18+. -- key, value, ok := cut(field, "=") +- key, value, ok := strings.Cut(field, "=") - if !ok { - return nil, fmt.Errorf("env vars must be formatted as var=value, got %q", field) - } @@ -60729,7 +67299,8 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - } - - case strings.HasPrefix(file.Name, "@"): // golden content -- id, name, _ := cut(file.Name[len("@"):], "/") +- idstring, name, _ := strings.Cut(file.Name[len("@"):], "/") +- id := expect.Identifier(idstring) - // Note that a file.Name of just "@id" gives (id, name) = ("id", ""). - if _, ok := test.golden[id]; !ok { - test.golden[id] = &Golden{ @@ -60739,29 +67310,41 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - } - test.golden[id].data[name] = file.Data - +- case strings.HasPrefix(file.Name, "proxy/"): +- name := file.Name[len("proxy/"):] +- if test.proxyFiles == nil { +- test.proxyFiles = make(map[string][]byte) +- } +- test.proxyFiles[name] = file.Data +- - default: // ordinary file content - notes, err := expect.Parse(test.fset, file.Name, file.Data) - if err != nil { - return nil, fmt.Errorf("parsing notes in %q: %v", file.Name, err) - } +- +- // Reject common misspelling: "// @mark". +- // TODO(adonovan): permit "// @" within a string. Detect multiple spaces. +- if i := bytes.Index(file.Data, []byte("// @")); i >= 0 { +- line := 1 + bytes.Count(file.Data[:i], []byte("\n")) +- return nil, fmt.Errorf("%s:%d: unwanted space before marker (// @)", file.Name, line) +- } +- - test.notes = append(test.notes, notes...) - test.files[file.Name] = file.Data - } +- +- // Print a warning if we see what looks like "-- filename --" +- // without the second "--". It's not necessarily wrong, +- // but it should almost never appear in our test inputs. +- if bytes.Contains(file.Data, []byte("\n-- ")) { +- log.Printf("ill-formed '-- filename --' header in %s?", file.Name) +- } - } - - return test, nil -} - --// cut is a copy of strings.Cut. --// --// TODO: once we only support Go 1.18+, just use strings.Cut. --func cut(s, sep string) (before, after string, found bool) { -- if i := strings.Index(s, sep); i >= 0 { -- return s[:i], s[i+len(sep):], true -- } -- return s, "", false --} -- -// formatTest formats the test as a txtar archive. -func formatTest(test *markerTest) ([]byte, error) { - arch := &txtar.Archive{ @@ -60771,7 +67354,7 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - updatedGolden := make(map[string][]byte) - for id, g := range test.golden { - for name, data := range g.updated { -- filename := "@" + path.Join(id, name) // name may be "" +- filename := "@" + path.Join(string(id), name) // name may be "" - updatedGolden[filename] = data - } - } @@ -60781,11 +67364,13 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - switch file.Name { - // Preserve configuration files exactly as they were. They must have parsed - // if we got this far. -- case "flags", "settings.json", "env": +- case "skip", "flags", "settings.json", "capabilities.json", "env": - arch.Files = append(arch.Files, file) - default: - if _, ok := test.files[file.Name]; ok { // ordinary file - arch.Files = append(arch.Files, file) +- } else if strings.HasPrefix(file.Name, "proxy/") { // proxy file +- arch.Files = append(arch.Files, file) - } else if data, ok := updatedGolden[file.Name]; ok { // golden file - arch.Files = append(arch.Files, txtar.File{Name: file.Name, Data: data}) - delete(updatedGolden, file.Name) @@ -60811,16 +67396,22 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -// -// TODO(rfindley): simplify and refactor the construction of testing -// environments across regtests, marker tests, and benchmarks. --func newEnv(t *testing.T, cache *cache.Cache, files map[string][]byte, config fake.EditorConfig) *Env { +-func newEnv(t *testing.T, cache *cache.Cache, files, proxyFiles map[string][]byte, writeGoSum []string, config fake.EditorConfig) *Env { - sandbox, err := fake.NewSandbox(&fake.SandboxConfig{ -- RootDir: t.TempDir(), -- GOPROXY: "https://proxy.golang.org", -- Files: files, +- RootDir: t.TempDir(), +- Files: files, +- ProxyFiles: proxyFiles, - }) - if err != nil { - t.Fatal(err) - } - +- for _, dir := range writeGoSum { +- if err := sandbox.RunGoCommand(context.Background(), dir, "list", []string{"-mod=mod", "..."}, []string{"GOWORK=off"}, true); err != nil { +- t.Fatal(err) +- } +- } +- - // Put a debug instance in the context to prevent logging to stderr. - // See associated TODO in runner.go: we should revisit this pattern. - ctx := context.Background() @@ -60848,22 +67439,53 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - } -} - --// A markerFunc is a reflectively callable @mark implementation function. --type markerFunc struct { -- fn reflect.Value // the func to invoke -- paramTypes []reflect.Type // parameter types, for zero values -- converters []converter // to convert non-blank arguments --} -- -// A markerTestRun holds the state of one run of a marker test archive. -type markerTestRun struct { -- test *markerTest -- env *Env +- test *markerTest +- env *Env +- settings map[string]any - - // Collected information. - // Each @diag/@suggestedfix marker eliminates an entry from diags. -- locations map[expect.Identifier]protocol.Location -- diags map[protocol.Location][]protocol.Diagnostic +- values map[expect.Identifier]any +- diags map[protocol.Location][]protocol.Diagnostic // diagnostics by position; location end == start +- +- // Notes that weren't associated with a top-level marker func. They may be +- // consumed by another marker (e.g. @codelenses collects @codelens markers). +- // Any notes that aren't consumed are flagged as an error. +- extraNotes map[protocol.DocumentURI]map[string][]*expect.Note +-} +- +-// sprintf returns a formatted string after applying pre-processing to +-// arguments of the following types: +-// - token.Pos: formatted using (*markerTestRun).fmtPos +-// - protocol.Location: formatted using (*markerTestRun).fmtLoc +-func (c *marker) sprintf(format string, args ...any) string { +- if false { +- _ = fmt.Sprintf(format, args...) // enable vet printf checker +- } +- var args2 []any +- for _, arg := range args { +- switch arg := arg.(type) { +- case token.Pos: +- args2 = append(args2, c.run.fmtPos(arg)) +- case protocol.Location: +- args2 = append(args2, c.run.fmtLoc(arg)) +- default: +- args2 = append(args2, arg) +- } +- } +- return fmt.Sprintf(format, args2...) +-} +- +-// uri returns the URI of the file containing the marker. +-func (mark marker) uri() protocol.DocumentURI { +- return mark.run.env.Sandbox.Workdir.URI(mark.run.test.fset.File(mark.note.Pos).Name()) +-} +- +-// path returns the relative path to the file containing the marker. +-func (mark marker) path() string { +- return mark.run.env.Sandbox.Workdir.RelPath(mark.run.test.fset.File(mark.note.Pos).Name()) -} - -// fmtLoc formats the given pos in the context of the test, using @@ -60891,8 +67513,21 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -// archive-relative paths for files and including the line number in the full -// archive file. -func (run *markerTestRun) fmtLoc(loc protocol.Location) string { +- formatted := run.fmtLocDetails(loc, true) +- if formatted == "" { +- run.env.T.Errorf("unable to find %s in test archive", loc) +- return "" +- } +- return formatted +-} +- +-// See fmtLoc. If includeTxtPos is not set, the position in the full archive +-// file is omitted. +-// +-// If the location cannot be found within the archive, fmtLocDetails returns "". +-func (run *markerTestRun) fmtLocDetails(loc protocol.Location, includeTxtPos bool) string { - if loc == (protocol.Location{}) { -- return "" +- return "" - } - lines := bytes.Count(run.test.archive.Comment, []byte("\n")) - var name string @@ -60906,8 +67541,7 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - lines += bytes.Count(f.Data, []byte("\n")) - } - if name == "" { -- run.env.T.Errorf("unable to find %s in test archive", loc) -- return "" +- return "" - } - m, err := run.env.Editor.Mapper(name) - if err != nil { @@ -60932,35 +67566,18 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - } - } - -- return fmt.Sprintf("%s:%s (%s:%s)", name, innerSpan, run.test.name, outerSpan) --} -- --// makeMarkerFunc uses reflection to create a markerFunc for the given func value. --func makeMarkerFunc(fn interface{}) markerFunc { -- mi := markerFunc{ -- fn: reflect.ValueOf(fn), -- } -- mtyp := mi.fn.Type() -- if mtyp.NumIn() == 0 || mtyp.In(0) != markerType { -- panic(fmt.Sprintf("marker function %#v must accept marker as its first argument", mi.fn)) -- } -- if mtyp.NumOut() != 0 { -- panic(fmt.Sprintf("marker function %#v must not have results", mi.fn)) -- } -- for a := 1; a < mtyp.NumIn(); a++ { -- in := mtyp.In(a) -- mi.paramTypes = append(mi.paramTypes, in) -- c := makeConverter(in) -- mi.converters = append(mi.converters, c) +- if includeTxtPos { +- return fmt.Sprintf("%s:%s (%s:%s)", name, innerSpan, run.test.name, outerSpan) +- } else { +- return fmt.Sprintf("%s:%s", name, innerSpan) - } -- return mi -} - -// ---- converters ---- - -// converter is the signature of argument converters. -// A converter should return an error rather than calling marker.errorf(). --type converter func(marker, interface{}) (interface{}, error) +-type converter func(marker, any) (any, error) - -// Types with special conversions. -var ( @@ -60971,28 +67588,39 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - wantErrorType = reflect.TypeOf(wantError{}) -) - --func makeConverter(paramType reflect.Type) converter { +-func convert(mark marker, arg any, paramType reflect.Type) (any, error) { +- if paramType == goldenType { +- id, ok := arg.(expect.Identifier) +- if !ok { +- return nil, fmt.Errorf("invalid input type %T: golden key must be an identifier", arg) +- } +- return mark.run.test.getGolden(id), nil +- } +- if id, ok := arg.(expect.Identifier); ok { +- if arg, ok := mark.run.values[id]; ok { +- if !reflect.TypeOf(arg).AssignableTo(paramType) { +- return nil, fmt.Errorf("cannot convert %v to %s", arg, paramType) +- } +- return arg, nil +- } +- } +- if reflect.TypeOf(arg).AssignableTo(paramType) { +- return arg, nil // no conversion required +- } - switch paramType { -- case goldenType: -- return goldenConverter - case locationType: -- return locationConverter +- return convertLocation(mark, arg) - case wantErrorType: -- return wantErrorConverter +- return convertWantError(mark, arg) - default: -- return func(_ marker, arg interface{}) (interface{}, error) { -- if argType := reflect.TypeOf(arg); argType != paramType { -- return nil, fmt.Errorf("cannot convert type %s to %s", argType, paramType) -- } -- return arg, nil -- } +- return nil, fmt.Errorf("cannot convert %v to %s", arg, paramType) - } -} - --// locationConverter converts a string argument into the protocol location --// corresponding to the first position of the string in the line preceding the --// note. --func locationConverter(mark marker, arg interface{}) (interface{}, error) { +-// convertLocation converts a string or regexp argument into the protocol +-// location corresponding to the first position of the string (or first match +-// of the regexp) in the line preceding the note. +-func convertLocation(mark marker, arg any) (protocol.Location, error) { - switch arg := arg.(type) { - case string: - startOff, preceding, m, err := linePreceding(mark.run, mark.note.Pos) @@ -61001,20 +67629,14 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - } - idx := bytes.Index(preceding, []byte(arg)) - if idx < 0 { -- return nil, fmt.Errorf("substring %q not found in %q", arg, preceding) +- return protocol.Location{}, fmt.Errorf("substring %q not found in %q", arg, preceding) - } - off := startOff + idx - return m.OffsetLocation(off, off+len(arg)) - case *regexp.Regexp: - return findRegexpInLine(mark.run, mark.note.Pos, arg) -- case expect.Identifier: -- loc, ok := mark.run.locations[arg] -- if !ok { -- return nil, fmt.Errorf("no location named %q", arg) -- } -- return loc, nil - default: -- return nil, fmt.Errorf("cannot convert argument type %T to location (must be a string to match the preceding line)", arg) +- return protocol.Location{}, fmt.Errorf("cannot convert argument type %T to location (must be a string to match the preceding line)", arg) - } -} - @@ -61062,22 +67684,22 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - return startOff, m.Content[startOff:endOff], m, nil -} - --// wantErrorConverter converts a string, regexp, or identifier +-// convertWantError converts a string, regexp, or identifier -// argument into a wantError. The string is a substring of the -// expected error, the regexp is a pattern than matches the expected -// error, and the identifier is a golden file containing the expected -// error. --func wantErrorConverter(mark marker, arg interface{}) (interface{}, error) { +-func convertWantError(mark marker, arg any) (wantError, error) { - switch arg := arg.(type) { - case string: - return wantError{substr: arg}, nil - case *regexp.Regexp: - return wantError{pattern: arg}, nil - case expect.Identifier: -- golden := mark.run.test.getGolden(string(arg)) +- golden := mark.run.test.getGolden(arg) - return wantError{golden: golden}, nil - default: -- return nil, fmt.Errorf("cannot convert %T to wantError (want: string, regexp, or identifier)", arg) +- return wantError{}, fmt.Errorf("cannot convert %T to wantError (want: string, regexp, or identifier)", arg) - } -} - @@ -61137,17 +67759,6 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - } -} - --// goldenConverter converts an identifier into the Golden directory of content --// prefixed by @ in the test archive file. --func goldenConverter(mark marker, arg interface{}) (interface{}, error) { -- switch arg := arg.(type) { -- case expect.Identifier: -- return mark.run.test.getGolden(string(arg)), nil -- default: -- return nil, fmt.Errorf("invalid input type %T: golden key must be an identifier", arg) -- } --} -- -// checkChangedFiles compares the files changed by an operation with their expected (golden) state. -func checkChangedFiles(mark marker, changed map[string][]byte, golden *Golden) { - // Check changed files match expectations. @@ -61175,7 +67786,195 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - -// ---- marker functions ---- - --// defMarker implements the @godef marker, running textDocument/definition at +-// TODO(rfindley): consolidate documentation of these markers. They are already +-// documented above, so much of the documentation here is redundant. +- +-// completionItem is a simplified summary of a completion item. +-type completionItem struct { +- Label, Detail, Kind, Documentation string +-} +- +-func completionItemMarker(mark marker, label string, other ...string) completionItem { +- if len(other) > 3 { +- mark.errorf("too many arguments to @item: expect at most 4") +- } +- item := completionItem{ +- Label: label, +- } +- if len(other) > 0 { +- item.Detail = other[0] +- } +- if len(other) > 1 { +- item.Kind = other[1] +- } +- if len(other) > 2 { +- item.Documentation = other[2] +- } +- return item +-} +- +-func rankMarker(mark marker, src protocol.Location, items ...completionItem) { +- list := mark.run.env.Completion(src) +- var got []string +- // Collect results that are present in items, preserving their order. +- for _, g := range list.Items { +- for _, w := range items { +- if g.Label == w.Label { +- got = append(got, g.Label) +- break +- } +- } +- } +- var want []string +- for _, w := range items { +- want = append(want, w.Label) +- } +- if diff := cmp.Diff(want, got); diff != "" { +- mark.errorf("completion rankings do not match (-want +got):\n%s", diff) +- } +-} +- +-func ranklMarker(mark marker, src protocol.Location, labels ...string) { +- list := mark.run.env.Completion(src) +- var got []string +- // Collect results that are present in items, preserving their order. +- for _, g := range list.Items { +- for _, label := range labels { +- if g.Label == label { +- got = append(got, g.Label) +- break +- } +- } +- } +- if diff := cmp.Diff(labels, got); diff != "" { +- mark.errorf("completion rankings do not match (-want +got):\n%s", diff) +- } +-} +- +-func snippetMarker(mark marker, src protocol.Location, item completionItem, want string) { +- list := mark.run.env.Completion(src) +- var ( +- found bool +- got string +- all []string // for errors +- ) +- items := filterBuiltinsAndKeywords(list.Items) +- for _, i := range items { +- all = append(all, i.Label) +- if i.Label == item.Label { +- found = true +- if i.TextEdit != nil { +- got = i.TextEdit.NewText +- } +- break +- } +- } +- if !found { +- mark.errorf("no completion item found matching %s (got: %v)", item.Label, all) +- return +- } +- if got != want { +- mark.errorf("snippets do not match: got %q, want %q", got, want) +- } +-} +- +-// completeMarker implements the @complete marker, running +-// textDocument/completion at the given src location and asserting that the +-// results match the expected results. +-func completeMarker(mark marker, src protocol.Location, want ...completionItem) { +- list := mark.run.env.Completion(src) +- items := filterBuiltinsAndKeywords(list.Items) +- var got []completionItem +- for i, item := range items { +- simplified := completionItem{ +- Label: item.Label, +- Detail: item.Detail, +- Kind: fmt.Sprint(item.Kind), +- } +- if item.Documentation != nil { +- switch v := item.Documentation.Value.(type) { +- case string: +- simplified.Documentation = v +- case protocol.MarkupContent: +- simplified.Documentation = strings.TrimSpace(v.Value) // trim newlines +- } +- } +- // Support short-hand notation: if Detail, Kind, or Documentation are omitted from the +- // item, don't match them. +- if i < len(want) { +- if want[i].Detail == "" { +- simplified.Detail = "" +- } +- if want[i].Kind == "" { +- simplified.Kind = "" +- } +- if want[i].Documentation == "" { +- simplified.Documentation = "" +- } +- } +- got = append(got, simplified) +- } +- if len(want) == 0 { +- want = nil // got is nil if empty +- } +- if diff := cmp.Diff(want, got); diff != "" { +- mark.errorf("Completion(...) returned unexpect results (-want +got):\n%s", diff) +- } +-} +- +-// filterBuiltinsAndKeywords filters out builtins and keywords from completion +-// results. +-// +-// It over-approximates, and does not detect if builtins are shadowed. +-func filterBuiltinsAndKeywords(items []protocol.CompletionItem) []protocol.CompletionItem { +- keep := 0 +- for _, item := range items { +- if types.Universe.Lookup(item.Label) == nil && token.Lookup(item.Label) == token.IDENT { +- items[keep] = item +- keep++ +- } +- } +- return items[:keep] +-} +- +-// acceptCompletionMarker implements the @acceptCompletion marker, running +-// textDocument/completion at the given src location and accepting the +-// candidate with the given label. The resulting source must match the provided +-// golden content. +-func acceptCompletionMarker(mark marker, src protocol.Location, label string, golden *Golden) { +- list := mark.run.env.Completion(src) +- var selected *protocol.CompletionItem +- for _, item := range list.Items { +- if item.Label == label { +- selected = &item +- break +- } +- } +- if selected == nil { +- mark.errorf("Completion(...) did not return an item labeled %q", label) +- return +- } +- filename := mark.path() +- mapper, err := mark.run.env.Editor.Mapper(filename) +- if err != nil { +- mark.errorf("Editor.Mapper(%s) failed: %v", filename, err) +- return +- } +- +- patched, _, err := source.ApplyProtocolEdits(mapper, append([]protocol.TextEdit{ +- *selected.TextEdit, +- }, selected.AdditionalTextEdits...)) +- +- if err != nil { +- mark.errorf("ApplyProtocolEdits failed: %v", err) +- return +- } +- changes := map[string][]byte{filename: patched} +- // Check the file state. +- checkChangedFiles(mark, changes, golden) +-} +- +-// defMarker implements the @def marker, running textDocument/definition at -// the given src location and asserting that there is exactly one resulting -// location, matching dst. -// @@ -61188,6 +67987,115 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - } -} - +-func typedefMarker(mark marker, src, dst protocol.Location) { +- got := mark.run.env.TypeDefinition(src) +- if got != dst { +- mark.errorf("type definition location does not match:\n\tgot: %s\n\twant %s", +- mark.run.fmtLoc(got), mark.run.fmtLoc(dst)) +- } +-} +- +-func foldingRangeMarker(mark marker, g *Golden) { +- env := mark.run.env +- ranges, err := mark.server().FoldingRange(env.Ctx, &protocol.FoldingRangeParams{ +- TextDocument: protocol.TextDocumentIdentifier{URI: mark.uri()}, +- }) +- if err != nil { +- mark.errorf("foldingRange failed: %v", err) +- return +- } +- var edits []protocol.TextEdit +- insert := func(line, char uint32, text string) { +- pos := protocol.Position{Line: line, Character: char} +- edits = append(edits, protocol.TextEdit{ +- Range: protocol.Range{ +- Start: pos, +- End: pos, +- }, +- NewText: text, +- }) +- } +- for i, rng := range ranges { +- insert(rng.StartLine, rng.StartCharacter, fmt.Sprintf("<%d kind=%q>", i, rng.Kind)) +- insert(rng.EndLine, rng.EndCharacter, fmt.Sprintf("", i)) +- } +- filename := mark.path() +- mapper, err := env.Editor.Mapper(filename) +- if err != nil { +- mark.errorf("Editor.Mapper(%s) failed: %v", filename, err) +- return +- } +- got, _, err := source.ApplyProtocolEdits(mapper, edits) +- if err != nil { +- mark.errorf("ApplyProtocolEdits failed: %v", err) +- return +- } +- want, _ := g.Get(mark.run.env.T, "", got) +- if diff := compare.Bytes(want, got); diff != "" { +- mark.errorf("foldingRange mismatch:\n%s", diff) +- } +-} +- +-// formatMarker implements the @format marker. +-func formatMarker(mark marker, golden *Golden) { +- edits, err := mark.server().Formatting(mark.run.env.Ctx, &protocol.DocumentFormattingParams{ +- TextDocument: protocol.TextDocumentIdentifier{URI: mark.uri()}, +- }) +- var got []byte +- if err != nil { +- got = []byte(err.Error() + "\n") // all golden content is newline terminated +- } else { +- env := mark.run.env +- filename := mark.path() +- mapper, err := env.Editor.Mapper(filename) +- if err != nil { +- mark.errorf("Editor.Mapper(%s) failed: %v", filename, err) +- } +- +- got, _, err = source.ApplyProtocolEdits(mapper, edits) +- if err != nil { +- mark.errorf("ApplyProtocolEdits failed: %v", err) +- return +- } +- } +- +- want, ok := golden.Get(mark.run.env.T, "", got) +- if !ok { +- mark.errorf("missing golden file @%s", golden.id) +- return +- } +- +- if diff := compare.Bytes(want, got); diff != "" { +- mark.errorf("golden file @%s does not match format results:\n%s", golden.id, diff) +- } +-} +- +-func highlightMarker(mark marker, src protocol.Location, dsts ...protocol.Location) { +- highlights := mark.run.env.DocumentHighlight(src) +- var got []protocol.Range +- for _, h := range highlights { +- got = append(got, h.Range) +- } +- +- var want []protocol.Range +- for _, d := range dsts { +- want = append(want, d.Range) +- } +- +- sortRanges := func(s []protocol.Range) { +- sort.Slice(s, func(i, j int) bool { +- return protocol.CompareRange(s[i], s[j]) < 0 +- }) +- } +- +- sortRanges(got) +- sortRanges(want) +- +- if diff := cmp.Diff(want, got); diff != "" { +- mark.errorf("DocumentHighlight(%v) mismatch (-want +got):\n%s", src, diff) +- } +-} +- -// hoverMarker implements the @hover marker, running textDocument/hover at the -// given src location and asserting that the resulting hover is over the dst -// location (typically a span surrounding src), and that the markdown content @@ -61218,32 +68126,37 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - -// locMarker implements the @loc marker. It is executed before other -// markers, so that locations are available. --func locMarker(mark marker, name expect.Identifier, loc protocol.Location) { -- mark.run.locations[name] = loc --} +-func locMarker(mark marker, loc protocol.Location) protocol.Location { return loc } - -// diagMarker implements the @diag marker. It eliminates diagnostics from -// the observed set in mark.test. -func diagMarker(mark marker, loc protocol.Location, re *regexp.Regexp) { -- if _, err := removeDiagnostic(mark, loc, re); err != nil { -- mark.errorf("%v", err) +- if _, ok := removeDiagnostic(mark, loc, re); !ok { +- mark.errorf("no diagnostic at %v matches %q", loc, re) - } -} - --func removeDiagnostic(mark marker, loc protocol.Location, re *regexp.Regexp) (protocol.Diagnostic, error) { +-// removeDiagnostic looks for a diagnostic matching loc at the given position. +-// +-// If found, it returns (diag, true), and eliminates the matched diagnostic +-// from the unmatched set. +-// +-// If not found, it returns (protocol.Diagnostic{}, false). +-func removeDiagnostic(mark marker, loc protocol.Location, re *regexp.Regexp) (protocol.Diagnostic, bool) { +- loc.Range.End = loc.Range.Start // diagnostics ignore end position. - diags := mark.run.diags[loc] - for i, diag := range diags { - if re.MatchString(diag.Message) { - mark.run.diags[loc] = append(diags[:i], diags[i+1:]...) -- return diag, nil +- return diag, true - } - } -- return protocol.Diagnostic{}, fmt.Errorf("no diagnostic matches %q", re) +- return protocol.Diagnostic{}, false -} - -// renameMarker implements the @rename(location, new, golden) marker. --func renameMarker(mark marker, loc protocol.Location, newName expect.Identifier, golden *Golden) { -- changed, err := rename(mark.run.env, loc, string(newName)) +-func renameMarker(mark marker, loc protocol.Location, newName string, golden *Golden) { +- changed, err := rename(mark.run.env, loc, newName) - if err != nil { - mark.errorf("rename failed: %v. (Use @renameerr for expected errors.)", err) - return @@ -61252,11 +68165,22 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -} - -// renameErrMarker implements the @renamererr(location, new, error) marker. --func renameErrMarker(mark marker, loc protocol.Location, newName expect.Identifier, wantErr wantError) { -- _, err := rename(mark.run.env, loc, string(newName)) +-func renameErrMarker(mark marker, loc protocol.Location, newName string, wantErr wantError) { +- _, err := rename(mark.run.env, loc, newName) - wantErr.check(mark, err) -} - +-func signatureMarker(mark marker, src protocol.Location, want string) { +- got := mark.run.env.SignatureHelp(src) +- if got == nil || len(got.Signatures) != 1 { +- mark.errorf("signatureHelp = %v, want exactly 1 signature", got) +- return +- } +- if got := got.Signatures[0].Label; got != want { +- mark.errorf("signatureHelp: got %q, want %q", got, want) +- } +-} +- -// rename returns the new contents of the files that would be modified -// by renaming the identifier at loc to newName. -func rename(env *Env, loc protocol.Location, newName string) (map[string][]byte, error) { @@ -61275,41 +68199,125 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - return nil, err - } - -- return applyDocumentChanges(env, editMap.DocumentChanges) +- fileChanges := make(map[string][]byte) +- if err := applyDocumentChanges(env, editMap.DocumentChanges, fileChanges); err != nil { +- return nil, fmt.Errorf("applying document changes: %v", err) +- } +- return fileChanges, nil -} - --// applyDocumentChanges returns the effect of applying the document --// changes to the contents of the Editor buffers. The actual editor --// buffers are unchanged. --func applyDocumentChanges(env *Env, changes []protocol.DocumentChanges) (map[string][]byte, error) { -- result := make(map[string][]byte) +-// applyDocumentChanges applies the given document changes to the editor buffer +-// content, recording the resulting contents in the fileChanges map. It is an +-// error for a change to an edit a file that is already present in the +-// fileChanges map. +-func applyDocumentChanges(env *Env, changes []protocol.DocumentChanges, fileChanges map[string][]byte) error { +- getMapper := func(path string) (*protocol.Mapper, error) { +- if _, ok := fileChanges[path]; ok { +- return nil, fmt.Errorf("internal error: %s is already edited", path) +- } +- return env.Editor.Mapper(path) +- } +- - for _, change := range changes { - if change.RenameFile != nil { - // rename - oldFile := env.Sandbox.Workdir.URIToPath(change.RenameFile.OldURI) -- newFile := env.Sandbox.Workdir.URIToPath(change.RenameFile.NewURI) -- mapper, err := env.Editor.Mapper(oldFile) +- mapper, err := getMapper(oldFile) - if err != nil { -- return nil, err +- return err - } -- result[newFile] = mapper.Content -- +- newFile := env.Sandbox.Workdir.URIToPath(change.RenameFile.NewURI) +- fileChanges[newFile] = mapper.Content - } else { - // edit - filename := env.Sandbox.Workdir.URIToPath(change.TextDocumentEdit.TextDocument.URI) -- mapper, err := env.Editor.Mapper(filename) +- mapper, err := getMapper(filename) - if err != nil { -- return nil, err +- return err - } - patched, _, err := source.ApplyProtocolEdits(mapper, change.TextDocumentEdit.Edits) - if err != nil { -- return nil, err +- return err - } -- result[filename] = patched +- fileChanges[filename] = patched - } - } - -- return result, nil +- return nil +-} +- +-func codeActionMarker(mark marker, actionKind string, start, end protocol.Location, golden *Golden) { +- // Request the range from start.Start to end.End. +- loc := start +- loc.Range.End = end.Range.End +- +- // Apply the fix it suggests. +- changed, err := codeAction(mark.run.env, loc.URI, loc.Range, actionKind, nil) +- if err != nil { +- mark.errorf("codeAction failed: %v", err) +- return +- } +- +- // Check the file state. +- checkChangedFiles(mark, changed, golden) +-} +- +-func codeActionErrMarker(mark marker, actionKind string, start, end protocol.Location, wantErr wantError) { +- loc := start +- loc.Range.End = end.Range.End +- _, err := codeAction(mark.run.env, loc.URI, loc.Range, actionKind, nil) +- wantErr.check(mark, err) +-} +- +-// codeLensesMarker runs the @codelenses() marker, collecting @codelens marks +-// in the current file and comparing with the result of the +-// textDocument/codeLens RPC. +-func codeLensesMarker(mark marker) { +- type codeLens struct { +- Range protocol.Range +- Title string +- } +- +- lenses := mark.run.env.CodeLens(mark.path()) +- var got []codeLens +- for _, lens := range lenses { +- title := "" +- if lens.Command != nil { +- title = lens.Command.Title +- } +- got = append(got, codeLens{lens.Range, title}) +- } +- +- var want []codeLens +- mark.consumeExtraNotes("codelens", actionMarkerFunc(func(mark marker, loc protocol.Location, title string) { +- want = append(want, codeLens{loc.Range, title}) +- })) +- +- for _, s := range [][]codeLens{got, want} { +- sort.Slice(s, func(i, j int) bool { +- li, lj := s[i], s[j] +- if c := protocol.CompareRange(li.Range, lj.Range); c != 0 { +- return c < 0 +- } +- return li.Title < lj.Title +- }) +- } +- +- if diff := cmp.Diff(want, got); diff != "" { +- mark.errorf("codelenses: unexpected diff (-want +got):\n%s", diff) +- } +-} +- +-// consumeExtraNotes runs the provided func for each extra note with the given +-// name, and deletes all matching notes. +-func (mark marker) consumeExtraNotes(name string, f func(marker)) { +- uri := mark.uri() +- notes := mark.run.extraNotes[uri][name] +- delete(mark.run.extraNotes[uri], name) +- +- for _, note := range notes { +- f(marker{run: mark.run, note: note}) +- } -} - -// suggestedfixMarker implements the @suggestedfix(location, regexp, @@ -61317,15 +68325,16 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma -// the expectation of a diagnostic, but then it applies the first code -// action of the specified kind suggested by the matched diagnostic. -func suggestedfixMarker(mark marker, loc protocol.Location, re *regexp.Regexp, actionKind string, golden *Golden) { +- loc.Range.End = loc.Range.Start // diagnostics ignore end position. - // Find and remove the matching diagnostic. -- diag, err := removeDiagnostic(mark, loc, re) -- if err != nil { -- mark.errorf("%v", err) +- diag, ok := removeDiagnostic(mark, loc, re) +- if !ok { +- mark.errorf("no diagnostic at %v matches %q", loc, re) - return - } - - // Apply the fix it suggests. -- changed, err := suggestedfix(mark.run.env, loc, diag, actionKind) +- changed, err := codeAction(mark.run.env, loc.URI, diag.Range, actionKind, &diag) - if err != nil { - mark.errorf("suggestedfix failed: %v. (Use @suggestedfixerr for expected errors.)", err) - return @@ -61335,19 +68344,29 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - checkChangedFiles(mark, changed, golden) -} - --func suggestedfix(env *Env, loc protocol.Location, diag protocol.Diagnostic, actionKind string) (map[string][]byte, error) { -- +-// codeAction executes a textDocument/codeAction request for the specified +-// location and kind. If diag is non-nil, it is used as the code action +-// context. +-// +-// The resulting map contains resulting file contents after the code action is +-// applied. Currently, this function does not support code actions that return +-// edits directly; it only supports code action commands. +-func codeAction(env *Env, uri protocol.DocumentURI, rng protocol.Range, actionKind string, diag *protocol.Diagnostic) (map[string][]byte, error) { - // Request all code actions that apply to the diagnostic. - // (The protocol supports filtering using Context.Only={actionKind} - // but we can give a better error if we don't filter.) -- actions, err := env.Editor.Server.CodeAction(env.Ctx, &protocol.CodeActionParams{ -- TextDocument: protocol.TextDocumentIdentifier{URI: loc.URI}, -- Range: diag.Range, +- params := &protocol.CodeActionParams{ +- TextDocument: protocol.TextDocumentIdentifier{URI: uri}, +- Range: rng, - Context: protocol.CodeActionContext{ -- Only: nil, // => all kinds -- Diagnostics: []protocol.Diagnostic{diag}, +- Only: nil, // => all kinds - }, -- }) +- } +- if diag != nil { +- params.Context.Diagnostics = []protocol.Diagnostic{*diag} +- } +- +- actions, err := env.Editor.Server.CodeAction(env.Ctx, params) - if err != nil { - return nil, err - } @@ -61367,54 +68386,239 @@ diff -urN a/gopls/internal/lsp/regtest/marker.go b/gopls/internal/lsp/regtest/ma - } - action := candidates[0] - +- // Apply the codeAction. +- // +- // Spec: +- // "If a code action provides an edit and a command, first the edit is +- // executed and then the command." +- fileChanges := make(map[string][]byte) - // An action may specify an edit and/or a command, to be - // applied in that order. But since applyDocumentChanges(env, - // action.Edit.DocumentChanges) doesn't compose, for now we - // assert that all commands used in the @suggestedfix tests - // return only a command. -- if action.Edit.DocumentChanges != nil { -- env.T.Errorf("internal error: discarding unexpected CodeAction{Kind=%s, Title=%q}.Edit.DocumentChanges", action.Kind, action.Title) -- } -- if action.Command == nil { -- return nil, fmt.Errorf("missing CodeAction{Kind=%s, Title=%q}.Command", action.Kind, action.Title) +- if action.Edit != nil { +- if action.Edit.Changes != nil { +- env.T.Errorf("internal error: discarding unexpected CodeAction{Kind=%s, Title=%q}.Edit.Changes", action.Kind, action.Title) +- } +- if action.Edit.DocumentChanges != nil { +- if err := applyDocumentChanges(env, action.Edit.DocumentChanges, fileChanges); err != nil { +- return nil, fmt.Errorf("applying document changes: %v", err) +- } +- } - } - -- // This is a typical CodeAction command: -- // -- // Title: "Implement error" -- // Command: gopls.apply_fix -- // Arguments: [{"Fix":"stub_methods","URI":".../a.go","Range":...}}] -- // -- // The client makes an ExecuteCommand RPC to the server, -- // which dispatches it to the ApplyFix handler. -- // ApplyFix dispatches to the "stub_methods" suggestedfix hook (the meat). -- // The server then makes an ApplyEdit RPC to the client, -- // whose Awaiter hook gathers the edits instead of applying them. +- if action.Command != nil { +- // This is a typical CodeAction command: +- // +- // Title: "Implement error" +- // Command: gopls.apply_fix +- // Arguments: [{"Fix":"stub_methods","URI":".../a.go","Range":...}}] +- // +- // The client makes an ExecuteCommand RPC to the server, +- // which dispatches it to the ApplyFix handler. +- // ApplyFix dispatches to the "stub_methods" suggestedfix hook (the meat). +- // The server then makes an ApplyEdit RPC to the client, +- // whose Awaiter hook gathers the edits instead of applying them. - -- _ = env.Awaiter.takeDocumentChanges() // reset (assuming Env is confined to this thread) +- _ = env.Awaiter.takeDocumentChanges() // reset (assuming Env is confined to this thread) - -- if _, err := env.Editor.Server.ExecuteCommand(env.Ctx, &protocol.ExecuteCommandParams{ -- Command: action.Command.Command, -- Arguments: action.Command.Arguments, -- }); err != nil { -- env.T.Fatalf("error converting command %q to edits: %v", action.Command.Command, err) +- if _, err := env.Editor.Server.ExecuteCommand(env.Ctx, &protocol.ExecuteCommandParams{ +- Command: action.Command.Command, +- Arguments: action.Command.Arguments, +- }); err != nil { +- env.T.Fatalf("error converting command %q to edits: %v", action.Command.Command, err) +- } +- +- if err := applyDocumentChanges(env, env.Awaiter.takeDocumentChanges(), fileChanges); err != nil { +- return nil, fmt.Errorf("applying document changes from command: %v", err) +- } - } - -- return applyDocumentChanges(env, env.Awaiter.takeDocumentChanges()) +- return fileChanges, nil -} - -// TODO(adonovan): suggestedfixerr +- +-// refsMarker implements the @refs marker. +-func refsMarker(mark marker, src protocol.Location, want ...protocol.Location) { +- refs := func(includeDeclaration bool, want []protocol.Location) error { +- got, err := mark.server().References(mark.run.env.Ctx, &protocol.ReferenceParams{ +- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(src), +- Context: protocol.ReferenceContext{ +- IncludeDeclaration: includeDeclaration, +- }, +- }) +- if err != nil { +- return err +- } +- +- return compareLocations(mark, got, want) +- } +- +- for _, includeDeclaration := range []bool{false, true} { +- // Ignore first 'want' location if we didn't request the declaration. +- // TODO(adonovan): don't assume a single declaration: +- // there may be >1 if corresponding methods are considered. +- want := want +- if !includeDeclaration && len(want) > 0 { +- want = want[1:] +- } +- if err := refs(includeDeclaration, want); err != nil { +- mark.errorf("refs(includeDeclaration=%t) failed: %v", +- includeDeclaration, err) +- } +- } +-} +- +-// implementationMarker implements the @implementation marker. +-func implementationMarker(mark marker, src protocol.Location, want ...protocol.Location) { +- got, err := mark.server().Implementation(mark.run.env.Ctx, &protocol.ImplementationParams{ +- TextDocumentPositionParams: protocol.LocationTextDocumentPositionParams(src), +- }) +- if err != nil { +- mark.errorf("implementation at %s failed: %v", src, err) +- return +- } +- if err := compareLocations(mark, got, want); err != nil { +- mark.errorf("implementation: %v", err) +- } +-} +- +-// symbolMarker implements the @symbol marker. +-func symbolMarker(mark marker, golden *Golden) { +- // Retrieve information about all symbols in this file. +- symbols, err := mark.server().DocumentSymbol(mark.run.env.Ctx, &protocol.DocumentSymbolParams{ +- TextDocument: protocol.TextDocumentIdentifier{URI: mark.uri()}, +- }) +- if err != nil { +- mark.errorf("DocumentSymbol request failed: %v", err) +- return +- } +- +- // Format symbols one per line, sorted (in effect) by first column, a dotted name. +- var lines []string +- for _, symbol := range symbols { +- // Each result element is a union of (legacy) +- // SymbolInformation and (new) DocumentSymbol, +- // so we ascertain which one and then transcode. +- data, err := json.Marshal(symbol) +- if err != nil { +- mark.run.env.T.Fatal(err) +- } +- if _, ok := symbol.(map[string]any)["location"]; ok { +- // This case is not reached because Editor initialization +- // enables HierarchicalDocumentSymbolSupport. +- // TODO(adonovan): test this too. +- var sym protocol.SymbolInformation +- if err := json.Unmarshal(data, &sym); err != nil { +- mark.run.env.T.Fatal(err) +- } +- mark.errorf("fake Editor doesn't support SymbolInformation") +- +- } else { +- var sym protocol.DocumentSymbol // new hierarchical hotness +- if err := json.Unmarshal(data, &sym); err != nil { +- mark.run.env.T.Fatal(err) +- } +- +- // Print each symbol in the response tree. +- var visit func(sym protocol.DocumentSymbol, prefix []string) +- visit = func(sym protocol.DocumentSymbol, prefix []string) { +- var out strings.Builder +- out.WriteString(strings.Join(prefix, ".")) +- fmt.Fprintf(&out, " %q", sym.Detail) +- if delta := sym.Range.End.Line - sym.Range.Start.Line; delta > 0 { +- fmt.Fprintf(&out, " +%d lines", delta) +- } +- lines = append(lines, out.String()) +- +- for _, child := range sym.Children { +- visit(child, append(prefix, child.Name)) +- } +- } +- visit(sym, []string{sym.Name}) +- } +- } +- sort.Strings(lines) +- lines = append(lines, "") // match trailing newline in .txtar file +- got := []byte(strings.Join(lines, "\n")) +- +- // Compare with golden. +- want, ok := golden.Get(mark.run.env.T, "", got) +- if !ok { +- mark.errorf("%s: missing golden file @%s", mark.note.Name, golden.id) +- } else if diff := cmp.Diff(string(got), string(want)); diff != "" { +- mark.errorf("%s: unexpected output: got:\n%s\nwant:\n%s\ndiff:\n%s", +- mark.note.Name, got, want, diff) +- } +-} +- +-// compareLocations returns an error message if got and want are not +-// the same set of locations. The marker is used only for fmtLoc. +-func compareLocations(mark marker, got, want []protocol.Location) error { +- toStrings := func(locs []protocol.Location) []string { +- strs := make([]string, len(locs)) +- for i, loc := range locs { +- strs[i] = mark.run.fmtLoc(loc) +- } +- sort.Strings(strs) +- return strs +- } +- if diff := cmp.Diff(toStrings(want), toStrings(got)); diff != "" { +- return fmt.Errorf("incorrect result locations: (got %d, want %d):\n%s", +- len(got), len(want), diff) +- } +- return nil +-} +- +-func workspaceSymbolMarker(mark marker, query string, golden *Golden) { +- params := &protocol.WorkspaceSymbolParams{ +- Query: query, +- } +- +- gotSymbols, err := mark.server().Symbol(mark.run.env.Ctx, params) +- if err != nil { +- mark.errorf("Symbol(%q) failed: %v", query, err) +- return +- } +- var got bytes.Buffer +- for _, s := range gotSymbols { +- // Omit the txtar position of the symbol location; otherwise edits to the +- // txtar archive lead to unexpected failures. +- loc := mark.run.fmtLocDetails(s.Location, false) +- // TODO(rfindley): can we do better here, by detecting if the location is +- // relative to GOROOT? +- if loc == "" { +- loc = "" +- } +- fmt.Fprintf(&got, "%s %s %s\n", loc, s.Name, s.Kind) +- } +- +- want, ok := golden.Get(mark.run.env.T, "", got.Bytes()) +- if !ok { +- mark.errorf("missing golden file @%s", golden.id) +- return +- } +- +- if diff := compare.Bytes(want, got.Bytes()); diff != "" { +- mark.errorf("Symbol(%q) mismatch:\n%s", query, diff) +- } +-} diff -urN a/gopls/internal/lsp/regtest/options.go b/gopls/internal/lsp/regtest/options.go --- a/gopls/internal/lsp/regtest/options.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/regtest/options.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,105 +0,0 @@ ++++ b/gopls/internal/lsp/regtest/options.go 1970-01-01 08:00:00 +@@ -1,134 +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 regtest - --import "golang.org/x/tools/gopls/internal/lsp/fake" +-import ( +- "golang.org/x/tools/gopls/internal/lsp/fake" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +-) - -type runConfig struct { - editor fake.EditorConfig @@ -61423,6 +68627,18 @@ diff -urN a/gopls/internal/lsp/regtest/options.go b/gopls/internal/lsp/regtest/o - skipHooks bool -} - +-func defaultConfig() runConfig { +- return runConfig{ +- editor: fake.EditorConfig{ +- Settings: map[string]interface{}{ +- // Shorten the diagnostic delay to speed up test execution (else we'd add +- // the default delay to each assertion about diagnostics) +- "diagnosticsDelay": "10ms", +- }, +- }, +- } +-} +- -// A RunOption augments the behavior of the test runner. -type RunOption interface { - set(*runConfig) @@ -61462,8 +68678,14 @@ diff -urN a/gopls/internal/lsp/regtest/options.go b/gopls/internal/lsp/regtest/o - }) -} - --// Settings is a RunOption that sets user-provided configuration for the LSP --// server. +-// ClientName sets the LSP client name. +-func ClientName(name string) RunOption { +- return optionSetter(func(opts *runConfig) { +- opts.editor.ClientName = name +- }) +-} +- +-// Settings sets user-provided configuration for the LSP server. -// -// As a special case, the env setting must not be provided via Settings: use -// EnvVars instead. @@ -61513,10 +68735,18 @@ diff -urN a/gopls/internal/lsp/regtest/options.go b/gopls/internal/lsp/regtest/o - opts.sandbox.InGoPath = true - }) -} +- +-// MessageResponder configures the editor to respond to +-// window/showMessageRequest messages using the provided function. +-func MessageResponder(f func(*protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error)) RunOption { +- return optionSetter(func(opts *runConfig) { +- opts.editor.MessageResponder = f +- }) +-} diff -urN a/gopls/internal/lsp/regtest/regtest.go b/gopls/internal/lsp/regtest/regtest.go --- a/gopls/internal/lsp/regtest/regtest.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/regtest/regtest.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,153 +0,0 @@ ++++ b/gopls/internal/lsp/regtest/regtest.go 1970-01-01 08:00:00 +@@ -1,156 +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. @@ -61527,7 +68757,6 @@ diff -urN a/gopls/internal/lsp/regtest/regtest.go b/gopls/internal/lsp/regtest/r - "context" - "flag" - "fmt" -- "io/ioutil" - "os" - "runtime" - "testing" @@ -61623,6 +68852,10 @@ diff -urN a/gopls/internal/lsp/regtest/regtest.go b/gopls/internal/lsp/regtest/r - os.Exit(0) - } - +- if !testenv.HasExec() { +- fmt.Printf("skipping all tests: exec not supported on %s\n", runtime.GOOS) +- os.Exit(0) +- } - testenv.ExitIfSmallMachine() - - // Disable GOPACKAGESDRIVER, as it can cause spurious test failures. @@ -61648,7 +68881,7 @@ diff -urN a/gopls/internal/lsp/regtest/regtest.go b/gopls/internal/lsp/regtest/r - } - } - -- dir, err := ioutil.TempDir("", "gopls-regtest-") +- dir, err := os.MkdirTemp("", "gopls-regtest-") - if err != nil { - panic(fmt.Errorf("creating regtest temp directory: %v", err)) - } @@ -61672,8 +68905,8 @@ diff -urN a/gopls/internal/lsp/regtest/regtest.go b/gopls/internal/lsp/regtest/r -} diff -urN a/gopls/internal/lsp/regtest/runner.go b/gopls/internal/lsp/regtest/runner.go --- a/gopls/internal/lsp/regtest/runner.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/regtest/runner.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,437 +0,0 @@ ++++ b/gopls/internal/lsp/regtest/runner.go 1970-01-01 08:00:00 +@@ -1,436 +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. @@ -61685,7 +68918,6 @@ diff -urN a/gopls/internal/lsp/regtest/runner.go b/gopls/internal/lsp/regtest/ru - "context" - "fmt" - "io" -- "io/ioutil" - "net" - "os" - "path/filepath" @@ -61830,7 +69062,7 @@ diff -urN a/gopls/internal/lsp/regtest/runner.go b/gopls/internal/lsp/regtest/ru - - for _, tc := range tests { - tc := tc -- var config runConfig +- config := defaultConfig() - for _, opt := range opts { - opt.set(&config) - } @@ -62046,7 +69278,7 @@ diff -urN a/gopls/internal/lsp/regtest/runner.go b/gopls/internal/lsp/regtest/ru - } - - r.startRemoteOnce.Do(func() { -- socketDir, err := ioutil.TempDir(r.tempDir, "gopls-regtest-socket") +- socketDir, err := os.MkdirTemp(r.tempDir, "gopls-regtest-socket") - if err != nil { - r.remoteErr = err - return @@ -62113,8 +69345,8 @@ diff -urN a/gopls/internal/lsp/regtest/runner.go b/gopls/internal/lsp/regtest/ru -} diff -urN a/gopls/internal/lsp/regtest/wrappers.go b/gopls/internal/lsp/regtest/wrappers.go --- a/gopls/internal/lsp/regtest/wrappers.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/regtest/wrappers.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,489 +0,0 @@ ++++ b/gopls/internal/lsp/regtest/wrappers.go 1970-01-01 08:00:00 +@@ -1,544 +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. @@ -62272,9 +69504,20 @@ diff -urN a/gopls/internal/lsp/regtest/wrappers.go b/gopls/internal/lsp/regtest/ - -// GoToDefinition goes to definition in the editor, calling t.Fatal on any -// error. It returns the path and position of the resulting jump. +-// +-// TODO(rfindley): rename this to just 'Definition'. -func (e *Env) GoToDefinition(loc protocol.Location) protocol.Location { - e.T.Helper() -- loc, err := e.Editor.GoToDefinition(e.Ctx, loc) +- loc, err := e.Editor.Definition(e.Ctx, loc) +- if err != nil { +- e.T.Fatal(err) +- } +- return loc +-} +- +-func (e *Env) TypeDefinition(loc protocol.Location) protocol.Location { +- e.T.Helper() +- loc, err := e.Editor.TypeDefinition(e.Ctx, loc) - if err != nil { - e.T.Fatal(err) - } @@ -62362,7 +69605,7 @@ diff -urN a/gopls/internal/lsp/regtest/wrappers.go b/gopls/internal/lsp/regtest/ - if err := e.Editor.RunGenerate(e.Ctx, dir); err != nil { - e.T.Fatal(err) - } -- e.Await(NoOutstandingWork()) +- e.Await(NoOutstandingWork(IgnoreTelemetryPromptWork)) - // Ideally the fake.Workspace would handle all synthetic file watching, but - // we help it out here as we need to wait for the generate command to - // complete before checking the filesystem. @@ -62373,7 +69616,7 @@ diff -urN a/gopls/internal/lsp/regtest/wrappers.go b/gopls/internal/lsp/regtest/ -// directory. -func (e *Env) RunGoCommand(verb string, args ...string) { - e.T.Helper() -- if err := e.Sandbox.RunGoCommand(e.Ctx, "", verb, args, true); err != nil { +- if err := e.Sandbox.RunGoCommand(e.Ctx, "", verb, args, nil, true); err != nil { - e.T.Fatal(err) - } -} @@ -62382,7 +69625,16 @@ diff -urN a/gopls/internal/lsp/regtest/wrappers.go b/gopls/internal/lsp/regtest/ -// relative directory of the sandbox. -func (e *Env) RunGoCommandInDir(dir, verb string, args ...string) { - e.T.Helper() -- if err := e.Sandbox.RunGoCommand(e.Ctx, dir, verb, args, true); err != nil { +- if err := e.Sandbox.RunGoCommand(e.Ctx, dir, verb, args, nil, true); err != nil { +- e.T.Fatal(err) +- } +-} +- +-// RunGoCommandInDirWithEnv is like RunGoCommand, but executes in the given +-// relative directory of the sandbox with the given additional environment variables. +-func (e *Env) RunGoCommandInDirWithEnv(dir string, env []string, verb string, args ...string) { +- e.T.Helper() +- if err := e.Sandbox.RunGoCommand(e.Ctx, dir, verb, args, env, true); err != nil { - e.T.Fatal(err) - } -} @@ -62403,7 +69655,7 @@ diff -urN a/gopls/internal/lsp/regtest/wrappers.go b/gopls/internal/lsp/regtest/ -func (e *Env) DumpGoSum(dir string) { - e.T.Helper() - -- if err := e.Sandbox.RunGoCommand(e.Ctx, dir, "list", []string{"-mod=mod", "..."}, true); err != nil { +- if err := e.Sandbox.RunGoCommand(e.Ctx, dir, "list", []string{"-mod=mod", "..."}, nil, true); err != nil { - e.T.Fatal(err) - } - sumFile := path.Join(dir, "/go.sum") @@ -62478,6 +69730,41 @@ diff -urN a/gopls/internal/lsp/regtest/wrappers.go b/gopls/internal/lsp/regtest/ - } -} - +-// StartProfile starts a CPU profile with the given name, using the +-// gopls.start_profile custom command. It calls t.Fatal on any error. +-// +-// The resulting stop function must be called to stop profiling (using the +-// gopls.stop_profile custom command). +-func (e *Env) StartProfile() (stop func() string) { +- // TODO(golang/go#61217): revisit the ergonomics of these command APIs. +- // +- // This would be a lot simpler if we generated params constructors. +- args, err := command.MarshalArgs(command.StartProfileArgs{}) +- if err != nil { +- e.T.Fatal(err) +- } +- params := &protocol.ExecuteCommandParams{ +- Command: command.StartProfile.ID(), +- Arguments: args, +- } +- var result command.StartProfileResult +- e.ExecuteCommand(params, &result) +- +- return func() string { +- stopArgs, err := command.MarshalArgs(command.StopProfileArgs{}) +- if err != nil { +- e.T.Fatal(err) +- } +- stopParams := &protocol.ExecuteCommandParams{ +- Command: command.StopProfile.ID(), +- Arguments: stopArgs, +- } +- var result command.StopProfileResult +- e.ExecuteCommand(stopParams, &result) +- return result.File +- } +-} +- -// InlayHints calls textDocument/inlayHints for the given path, calling t.Fatal on -// any error. -func (e *Env) InlayHints(path string) []protocol.InlayHint { @@ -62606,8 +69893,8 @@ diff -urN a/gopls/internal/lsp/regtest/wrappers.go b/gopls/internal/lsp/regtest/ -} diff -urN a/gopls/internal/lsp/rename.go b/gopls/internal/lsp/rename.go --- a/gopls/internal/lsp/rename.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/rename.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,78 +0,0 @@ ++++ b/gopls/internal/lsp/rename.go 1970-01-01 08:00:00 +@@ -1,86 +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. @@ -62620,9 +69907,14 @@ diff -urN a/gopls/internal/lsp/rename.go b/gopls/internal/lsp/rename.go - - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" -) - -func (s *Server) rename(ctx context.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) { +- ctx, done := event.Start(ctx, "lsp.Server.rename", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go) - defer release() - if !ok { @@ -62636,9 +69928,9 @@ diff -urN a/gopls/internal/lsp/rename.go b/gopls/internal/lsp/rename.go - return nil, err - } - -- var docChanges []protocol.DocumentChanges +- docChanges := []protocol.DocumentChanges{} // must be a slice - for uri, e := range edits { -- fh, err := snapshot.GetFile(ctx, uri) +- fh, err := snapshot.ReadFile(ctx, uri) - if err != nil { - return nil, err - } @@ -62668,6 +69960,9 @@ diff -urN a/gopls/internal/lsp/rename.go b/gopls/internal/lsp/rename.go -// TODO(rfindley): why wouldn't we want to show an error to the user, if the -// user initiated a rename request at the cursor? -func (s *Server) prepareRename(ctx context.Context, params *protocol.PrepareRenameParams) (*protocol.PrepareRename2Gn, error) { +- ctx, done := event.Start(ctx, "lsp.Server.prepareRename", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go) - defer release() - if !ok { @@ -62688,7 +69983,7 @@ diff -urN a/gopls/internal/lsp/rename.go b/gopls/internal/lsp/rename.go -} diff -urN a/gopls/internal/lsp/reset_golden.sh b/gopls/internal/lsp/reset_golden.sh --- a/gopls/internal/lsp/reset_golden.sh 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/reset_golden.sh 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/reset_golden.sh 1970-01-01 08:00:00 @@ -1,30 +0,0 @@ -#!/bin/bash -# @@ -62722,8 +70017,8 @@ diff -urN a/gopls/internal/lsp/reset_golden.sh b/gopls/internal/lsp/reset_golden -go test ./test -golden diff -urN a/gopls/internal/lsp/safetoken/safetoken.go b/gopls/internal/lsp/safetoken/safetoken.go --- a/gopls/internal/lsp/safetoken/safetoken.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/safetoken/safetoken.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,122 +0,0 @@ ++++ b/gopls/internal/lsp/safetoken/safetoken.go 1970-01-01 08:00:00 +@@ -1,127 +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. @@ -62817,6 +70112,11 @@ diff -urN a/gopls/internal/lsp/safetoken/safetoken.go b/gopls/internal/lsp/safet - return f.PositionFor(pos, false) -} - +-// Line returns the line number for the given offset in the given file. +-func Line(f *token.File, pos token.Pos) int { +- return Position(f, pos).Line +-} +- -// StartPosition converts a start Pos in the FileSet into a Position. -// -// Call this function only if start represents the start of a token or @@ -62848,8 +70148,8 @@ diff -urN a/gopls/internal/lsp/safetoken/safetoken.go b/gopls/internal/lsp/safet -} diff -urN a/gopls/internal/lsp/safetoken/safetoken_test.go b/gopls/internal/lsp/safetoken/safetoken_test.go --- a/gopls/internal/lsp/safetoken/safetoken_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/safetoken/safetoken_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,121 +0,0 @@ ++++ b/gopls/internal/lsp/safetoken/safetoken_test.go 1970-01-01 08:00:00 +@@ -1,132 +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. @@ -62924,10 +70224,20 @@ diff -urN a/gopls/internal/lsp/safetoken/safetoken_test.go b/gopls/internal/lsp/ -// suggests alternatives. -func TestGoplsSourceDoesNotCallTokenFileMethods(t *testing.T) { - testenv.NeedsGoPackages(t) +- testenv.NeedsGo1Point(t, 18) +- testenv.NeedsLocalXTools(t) - -- pkgs, err := packages.Load(&packages.Config{ +- cfg := &packages.Config{ - Mode: packages.NeedName | packages.NeedModule | packages.NeedCompiledGoFiles | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax | packages.NeedImports | packages.NeedDeps, -- }, "go/token", "golang.org/x/tools/gopls/...") +- } +- cfg.Env = os.Environ() +- cfg.Env = append(cfg.Env, +- "GOPACKAGESDRIVER=off", +- "GOWORK=off", // necessary for -mod=mod below +- "GOFLAGS=-mod=mod", +- ) +- +- pkgs, err := packages.Load(cfg, "go/token", "golang.org/x/tools/gopls/...") - if err != nil { - t.Fatal(err) - } @@ -62950,6 +70260,7 @@ diff -urN a/gopls/internal/lsp/safetoken/safetoken_test.go b/gopls/internal/lsp/ - oldMethod, _, _ := types.LookupFieldOrMethod(recv.Type(), true, recv.Pkg(), old) - alternative[oldMethod] = new - } +- setAlternative(File, "Line", "safetoken.Line") - setAlternative(File, "Offset", "safetoken.Offset") - setAlternative(File, "Position", "safetoken.Position") - setAlternative(File, "PositionFor", "safetoken.Position") @@ -62973,7 +70284,7 @@ diff -urN a/gopls/internal/lsp/safetoken/safetoken_test.go b/gopls/internal/lsp/ -} diff -urN a/gopls/internal/lsp/selection_range.go b/gopls/internal/lsp/selection_range.go --- a/gopls/internal/lsp/selection_range.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/selection_range.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/selection_range.go 1970-01-01 08:00:00 @@ -1,69 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -63002,7 +70313,7 @@ diff -urN a/gopls/internal/lsp/selection_range.go b/gopls/internal/lsp/selection -// returned for each cursor to avoid multiple round-trips when the user is -// likely to issue this command multiple times in quick succession. -func (s *Server) selectionRange(ctx context.Context, params *protocol.SelectionRangeParams) ([]protocol.SelectionRange, error) { -- ctx, done := event.Start(ctx, "lsp.Server.documentSymbol") +- ctx, done := event.Start(ctx, "lsp.Server.selectionRange") - defer done() - - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) @@ -63046,8 +70357,8 @@ diff -urN a/gopls/internal/lsp/selection_range.go b/gopls/internal/lsp/selection -} diff -urN a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go --- a/gopls/internal/lsp/semantic.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/semantic.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1003 +0,0 @@ ++++ b/gopls/internal/lsp/semantic.go 1970-01-01 08:00:00 +@@ -1,999 +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. @@ -63073,6 +70384,7 @@ diff -urN a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/lsp/template" - "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" - "golang.org/x/tools/internal/typeparams" -) - @@ -63087,25 +70399,22 @@ diff -urN a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go -// semDebug should NEVER be true in checked-in code -const semDebug = false - --func (s *Server) semanticTokensFull(ctx context.Context, p *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) { -- ret, err := s.computeSemanticTokens(ctx, p.TextDocument, nil) +-func (s *Server) semanticTokensFull(ctx context.Context, params *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) { +- ctx, done := event.Start(ctx, "lsp.Server.semanticTokensFull", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- +- ret, err := s.computeSemanticTokens(ctx, params.TextDocument, nil) - return ret, err -} - --func (s *Server) semanticTokensFullDelta(ctx context.Context, p *protocol.SemanticTokensDeltaParams) (interface{}, error) { -- return nil, fmt.Errorf("implement SemanticTokensFullDelta") --} +-func (s *Server) semanticTokensRange(ctx context.Context, params *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) { +- ctx, done := event.Start(ctx, "lsp.Server.semanticTokensRange", tag.URI.Of(params.TextDocument.URI)) +- defer done() - --func (s *Server) semanticTokensRange(ctx context.Context, p *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) { -- ret, err := s.computeSemanticTokens(ctx, p.TextDocument, &p.Range) +- ret, err := s.computeSemanticTokens(ctx, params.TextDocument, ¶ms.Range) - return ret, err -} - --func (s *Server) semanticTokensRefresh(ctx context.Context) error { -- // in the code, but not in the protocol spec -- return fmt.Errorf("implement SemanticTokensRefresh") --} -- -func (s *Server) computeSemanticTokens(ctx context.Context, td protocol.TextDocumentIdentifier, rng *protocol.Range) (*protocol.SemanticTokens, error) { - ans := protocol.SemanticTokens{ - Data: []uint32{}, @@ -63115,13 +70424,12 @@ diff -urN a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go - if !ok { - return nil, err - } -- vv := snapshot.View() -- if !vv.Options().SemanticTokens { +- if !snapshot.Options().SemanticTokens { - // return an error, so if the option changes - // the client won't remember the wrong answer - return nil, fmt.Errorf("semantictokens are disabled") - } -- kind := snapshot.View().FileKind(fh) +- kind := snapshot.FileKind(fh) - if kind == source.Tmpl { - // this is a little cumbersome to avoid both exporting 'encoded' and its methods - // and to avoid import cycles @@ -63129,8 +70437,8 @@ diff -urN a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go - ctx: ctx, - metadataSource: snapshot, - rng: rng, -- tokTypes: s.session.Options().SemanticTypes, -- tokMods: s.session.Options().SemanticMods, +- tokTypes: snapshot.Options().SemanticTypes, +- tokMods: snapshot.Options().SemanticMods, - } - add := func(line, start uint32, len uint32) { - e.add(line, start, len, tokMacro, nil) @@ -63143,7 +70451,7 @@ diff -urN a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go - if kind != source.Go { - return nil, nil - } -- pkg, pgf, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.NarrowestPackage) +- pkg, pgf, err := source.NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, err - } @@ -63161,10 +70469,10 @@ diff -urN a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go - ti: pkg.GetTypesInfo(), - pkg: pkg, - fset: pkg.FileSet(), -- tokTypes: s.session.Options().SemanticTypes, -- tokMods: s.session.Options().SemanticMods, -- noStrings: vv.Options().NoSemanticString, -- noNumbers: vv.Options().NoSemanticNumber, +- tokTypes: snapshot.Options().SemanticTypes, +- tokMods: snapshot.Options().SemanticMods, +- noStrings: snapshot.Options().NoSemanticString, +- noNumbers: snapshot.Options().NoSemanticNumber, - } - if err := e.init(); err != nil { - // e.init should never return an error, unless there's some @@ -63308,7 +70616,7 @@ diff -urN a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go -// find the line in the source -func (e *encoded) srcLine(x ast.Node) string { - file := e.pgf.Tok -- line := file.Line(x.Pos()) +- line := safetoken.Line(file, x.Pos()) - start, err := safetoken.Offset(file, file.LineStart(line)) - if err != nil { - return "" @@ -63889,17 +71197,16 @@ diff -urN a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go -} - -func (e *encoded) init() error { -- e.start = token.Pos(e.pgf.Tok.Base()) -- e.end = e.start + token.Pos(e.pgf.Tok.Size()) -- if e.rng == nil { -- return nil -- } -- span, err := e.pgf.Mapper.RangeSpan(*e.rng) -- if err != nil { -- return fmt.Errorf("range span (%w) error for %s", err, e.pgf.File.Name) +- if e.rng != nil { +- var err error +- e.start, e.end, err = e.pgf.RangePos(*e.rng) +- if err != nil { +- return fmt.Errorf("range span (%w) error for %s", err, e.pgf.File.Name) +- } +- } else { +- tok := e.pgf.Tok +- e.start, e.end = tok.Pos(0), tok.Pos(tok.Size()) // entire file - } -- e.end = e.start + token.Pos(span.End().Offset()) -- e.start += token.Pos(span.Start().Offset()) - return nil -} - @@ -64051,10 +71358,219 @@ diff -urN a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go - "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary", - } -) +diff -urN a/gopls/internal/lsp/server.go b/gopls/internal/lsp/server.go +--- a/gopls/internal/lsp/server.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/server.go 1970-01-01 08:00:00 +@@ -1,205 +0,0 @@ +-// Copyright 2018 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 go run ./helper -d protocol/tsserver.go -o server_gen.go -u . +- +-// Package lsp implements LSP for gopls. +-package lsp +- +-import ( +- "context" +- "fmt" +- "os" +- "sync" +- +- "golang.org/x/tools/gopls/internal/lsp/cache" +- "golang.org/x/tools/gopls/internal/lsp/progress" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/jsonrpc2" +-) +- +-const concurrentAnalyses = 1 +- +-// NewServer creates an LSP server and binds it to handle incoming client +-// messages on the supplied stream. +-func NewServer(session *cache.Session, client protocol.ClientCloser, options *source.Options) *Server { +- return &Server{ +- diagnostics: map[span.URI]*fileReports{}, +- gcOptimizationDetails: make(map[source.PackageID]struct{}), +- watchedGlobPatterns: nil, // empty +- changedFiles: make(map[span.URI]struct{}), +- session: session, +- client: client, +- diagnosticsSema: make(chan struct{}, concurrentAnalyses), +- progress: progress.NewTracker(client), +- options: options, +- } +-} +- +-type serverState int +- +-const ( +- serverCreated = serverState(iota) +- serverInitializing // set once the server has received "initialize" request +- serverInitialized // set once the server has received "initialized" request +- serverShutDown +-) +- +-func (s serverState) String() string { +- switch s { +- case serverCreated: +- return "created" +- case serverInitializing: +- return "initializing" +- case serverInitialized: +- return "initialized" +- case serverShutDown: +- return "shutDown" +- } +- return fmt.Sprintf("(unknown state: %d)", int(s)) +-} +- +-// Server implements the protocol.Server interface. +-type Server struct { +- client protocol.ClientCloser +- +- stateMu sync.Mutex +- state serverState +- // notifications generated before serverInitialized +- notifications []*protocol.ShowMessageParams +- +- session *cache.Session +- +- tempDir string +- +- // changedFiles tracks files for which there has been a textDocument/didChange. +- changedFilesMu sync.Mutex +- changedFiles map[span.URI]struct{} +- +- // folders is only valid between initialize and initialized, and holds the +- // set of folders to build views for when we are ready +- pendingFolders []protocol.WorkspaceFolder +- +- // watchedGlobPatterns is the set of glob patterns that we have requested +- // the client watch on disk. It will be updated as the set of directories +- // that the server should watch changes. +- // The map field may be reassigned but the map is immutable. +- watchedGlobPatternsMu sync.Mutex +- watchedGlobPatterns map[string]struct{} +- watchRegistrationCount int +- +- diagnosticsMu sync.Mutex +- diagnostics map[span.URI]*fileReports +- +- // gcOptimizationDetails describes the packages for which we want +- // optimization details to be included in the diagnostics. The key is the +- // ID of the package. +- gcOptimizationDetailsMu sync.Mutex +- gcOptimizationDetails map[source.PackageID]struct{} +- +- // diagnosticsSema limits the concurrency of diagnostics runs, which can be +- // expensive. +- diagnosticsSema chan struct{} +- +- progress *progress.Tracker +- +- // When the workspace fails to load, we show its status through a progress +- // report with an error message. +- criticalErrorStatusMu sync.Mutex +- criticalErrorStatus *progress.WorkDone +- +- // Track an ongoing CPU profile created with the StartProfile command and +- // terminated with the StopProfile command. +- ongoingProfileMu sync.Mutex +- ongoingProfile *os.File // if non-nil, an ongoing profile is writing to this file +- +- // Track most recently requested options. +- optionsMu sync.Mutex +- options *source.Options +-} +- +-func (s *Server) workDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error { +- ctx, done := event.Start(ctx, "lsp.Server.workDoneProgressCancel") +- defer done() +- +- return s.progress.Cancel(params.Token) +-} +- +-func (s *Server) nonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) { +- ctx, done := event.Start(ctx, "lsp.Server.nonstandardRequest") +- defer done() +- +- switch method { +- case "gopls/diagnoseFiles": +- paramMap := params.(map[string]interface{}) +- // TODO(adonovan): opt: parallelize FileDiagnostics(URI...), either +- // by calling it in multiple goroutines or, better, by making +- // the relevant APIs accept a set of URIs/packages. +- for _, file := range paramMap["files"].([]interface{}) { +- snapshot, fh, ok, release, err := s.beginFileRequest(ctx, protocol.DocumentURI(file.(string)), source.UnknownKind) +- defer release() +- if !ok { +- return nil, err +- } +- +- fileID, diagnostics, err := s.diagnoseFile(ctx, snapshot, fh.URI()) +- if err != nil { +- return nil, err +- } +- if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{ +- URI: protocol.URIFromSpanURI(fh.URI()), +- Diagnostics: toProtocolDiagnostics(diagnostics), +- Version: fileID.Version(), +- }); err != nil { +- return nil, err +- } +- } +- if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{ +- URI: "gopls://diagnostics-done", +- }); err != nil { +- return nil, err +- } +- return struct{}{}, nil +- } +- return nil, notImplemented(method) +-} +- +-// fileDiagnostics reports diagnostics in the specified file, +-// as used by the "gopls check" or "gopls fix" commands. +-// +-// TODO(adonovan): opt: this function is called in a loop from the +-// "gopls/diagnoseFiles" nonstandard request handler. It would be more +-// efficient to compute the set of packages and TypeCheck and +-// Analyze them all at once. Or instead support textDocument/diagnostic +-// (golang/go#60122). +-func (s *Server) diagnoseFile(ctx context.Context, snapshot source.Snapshot, uri span.URI) (source.FileHandle, []*source.Diagnostic, error) { +- fh, err := snapshot.ReadFile(ctx, uri) +- if err != nil { +- return nil, nil, err +- } +- pkg, _, err := source.NarrowestPackageForFile(ctx, snapshot, uri) +- if err != nil { +- return nil, nil, err +- } +- pkgDiags, err := pkg.DiagnosticsForFile(ctx, snapshot, uri) +- if err != nil { +- return nil, nil, err +- } +- adiags, err := source.Analyze(ctx, snapshot, map[source.PackageID]unit{pkg.Metadata().ID: {}}, nil /* progress tracker */) +- if err != nil { +- return nil, nil, err +- } +- var td, ad []*source.Diagnostic // combine load/parse/type + analysis diagnostics +- source.CombineDiagnostics(pkgDiags, adiags[uri], &td, &ad) +- s.storeDiagnostics(snapshot, uri, typeCheckSource, td, true) +- s.storeDiagnostics(snapshot, uri, analysisSource, ad, true) +- return fh, append(td, ad...), nil +-} +- +-func notImplemented(method string) error { +- return fmt.Errorf("%w: %q not yet implemented", jsonrpc2.ErrMethodNotFound, method) +-} diff -urN a/gopls/internal/lsp/server_gen.go b/gopls/internal/lsp/server_gen.go --- a/gopls/internal/lsp/server_gen.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/server_gen.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,301 +0,0 @@ ++++ b/gopls/internal/lsp/server_gen.go 1970-01-01 08:00:00 +@@ -1,309 +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. @@ -64213,6 +71729,10 @@ diff -urN a/gopls/internal/lsp/server_gen.go b/gopls/internal/lsp/server_gen.go - return s.inlayHint(ctx, params) -} - +-func (s *Server) InlineCompletion(context.Context, *protocol.InlineCompletionParams) (*protocol.Or_Result_textDocument_inlineCompletion, error) { +- return nil, notImplemented("InlineCompletion") +-} +- -func (s *Server) InlineValue(context.Context, *protocol.InlineValueParams) ([]protocol.InlineValue, error) { - return nil, notImplemented("InlineValue") -} @@ -64257,6 +71777,10 @@ diff -urN a/gopls/internal/lsp/server_gen.go b/gopls/internal/lsp/server_gen.go - return nil, notImplemented("RangeFormatting") -} - +-func (s *Server) RangesFormatting(context.Context, *protocol.DocumentRangesFormattingParams) ([]protocol.TextEdit, error) { +- return nil, notImplemented("RangesFormatting") +-} +- -func (s *Server) References(ctx context.Context, params *protocol.ReferenceParams) ([]protocol.Location, error) { - return s.references(ctx, params) -} @@ -64293,16 +71817,16 @@ diff -urN a/gopls/internal/lsp/server_gen.go b/gopls/internal/lsp/server_gen.go - return s.selectionRange(ctx, params) -} - --func (s *Server) SemanticTokensFull(ctx context.Context, p *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) { -- return s.semanticTokensFull(ctx, p) +-func (s *Server) SemanticTokensFull(ctx context.Context, params *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) { +- return s.semanticTokensFull(ctx, params) -} - --func (s *Server) SemanticTokensFullDelta(ctx context.Context, p *protocol.SemanticTokensDeltaParams) (interface{}, error) { -- return s.semanticTokensFullDelta(ctx, p) +-func (s *Server) SemanticTokensFullDelta(context.Context, *protocol.SemanticTokensDeltaParams) (interface{}, error) { +- return nil, notImplemented("SemanticTokensFullDelta") -} - --func (s *Server) SemanticTokensRange(ctx context.Context, p *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) { -- return s.semanticTokensRange(ctx, p) +-func (s *Server) SemanticTokensRange(ctx context.Context, params *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) { +- return s.semanticTokensRange(ctx, params) -} - -func (s *Server) SetTrace(context.Context, *protocol.SetTraceParams) error { @@ -64356,172 +71880,10 @@ diff -urN a/gopls/internal/lsp/server_gen.go b/gopls/internal/lsp/server_gen.go -func (s *Server) WorkDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error { - return s.workDoneProgressCancel(ctx, params) -} -diff -urN a/gopls/internal/lsp/server.go b/gopls/internal/lsp/server.go ---- a/gopls/internal/lsp/server.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/server.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,158 +0,0 @@ --// Copyright 2018 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 go run ./helper -d protocol/tsserver.go -o server_gen.go -u . -- --// Package lsp implements LSP for gopls. --package lsp -- --import ( -- "context" -- "fmt" -- "sync" -- -- "golang.org/x/tools/gopls/internal/lsp/cache" -- "golang.org/x/tools/gopls/internal/lsp/progress" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/jsonrpc2" --) -- --const concurrentAnalyses = 1 -- --// NewServer creates an LSP server and binds it to handle incoming client --// messages on on the supplied stream. --func NewServer(session *cache.Session, client protocol.ClientCloser) *Server { -- return &Server{ -- diagnostics: map[span.URI]*fileReports{}, -- gcOptimizationDetails: make(map[source.PackageID]struct{}), -- watchedGlobPatterns: make(map[string]struct{}), -- changedFiles: make(map[span.URI]struct{}), -- session: session, -- client: client, -- diagnosticsSema: make(chan struct{}, concurrentAnalyses), -- progress: progress.NewTracker(client), -- diagDebouncer: newDebouncer(), -- } --} -- --type serverState int -- --const ( -- serverCreated = serverState(iota) -- serverInitializing // set once the server has received "initialize" request -- serverInitialized // set once the server has received "initialized" request -- serverShutDown --) -- --func (s serverState) String() string { -- switch s { -- case serverCreated: -- return "created" -- case serverInitializing: -- return "initializing" -- case serverInitialized: -- return "initialized" -- case serverShutDown: -- return "shutDown" -- } -- return fmt.Sprintf("(unknown state: %d)", int(s)) --} -- --// Server implements the protocol.Server interface. --type Server struct { -- client protocol.ClientCloser -- -- stateMu sync.Mutex -- state serverState -- // notifications generated before serverInitialized -- notifications []*protocol.ShowMessageParams -- -- session *cache.Session -- -- tempDir string -- -- // changedFiles tracks files for which there has been a textDocument/didChange. -- changedFilesMu sync.Mutex -- changedFiles map[span.URI]struct{} -- -- // folders is only valid between initialize and initialized, and holds the -- // set of folders to build views for when we are ready -- pendingFolders []protocol.WorkspaceFolder -- -- // watchedGlobPatterns is the set of glob patterns that we have requested -- // the client watch on disk. It will be updated as the set of directories -- // that the server should watch changes. -- watchedGlobPatternsMu sync.Mutex -- watchedGlobPatterns map[string]struct{} -- watchRegistrationCount int -- -- diagnosticsMu sync.Mutex -- diagnostics map[span.URI]*fileReports -- -- // gcOptimizationDetails describes the packages for which we want -- // optimization details to be included in the diagnostics. The key is the -- // ID of the package. -- gcOptimizationDetailsMu sync.Mutex -- gcOptimizationDetails map[source.PackageID]struct{} -- -- // diagnosticsSema limits the concurrency of diagnostics runs, which can be -- // expensive. -- diagnosticsSema chan struct{} -- -- progress *progress.Tracker -- -- // diagDebouncer is used for debouncing diagnostics. -- diagDebouncer *debouncer -- -- // When the workspace fails to load, we show its status through a progress -- // report with an error message. -- criticalErrorStatusMu sync.Mutex -- criticalErrorStatus *progress.WorkDone --} -- --func (s *Server) workDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error { -- return s.progress.Cancel(params.Token) --} -- --func (s *Server) nonstandardRequest(ctx context.Context, method string, params interface{}) (interface{}, error) { -- switch method { -- case "gopls/diagnoseFiles": -- paramMap := params.(map[string]interface{}) -- // TODO(adonovan): opt: parallelize FileDiagnostics(URI...), either -- // by calling it in multiple goroutines or, better, by making -- // the relevant APIs accept a set of URIs/packages. -- for _, file := range paramMap["files"].([]interface{}) { -- snapshot, fh, ok, release, err := s.beginFileRequest(ctx, protocol.DocumentURI(file.(string)), source.UnknownKind) -- defer release() -- if !ok { -- return nil, err -- } -- -- fileID, diagnostics, err := source.FileDiagnostics(ctx, snapshot, fh.URI()) -- if err != nil { -- return nil, err -- } -- if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{ -- URI: protocol.URIFromSpanURI(fh.URI()), -- Diagnostics: toProtocolDiagnostics(diagnostics), -- Version: fileID.Version(), -- }); err != nil { -- return nil, err -- } -- } -- if err := s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{ -- URI: "gopls://diagnostics-done", -- }); err != nil { -- return nil, err -- } -- return struct{}{}, nil -- } -- return nil, notImplemented(method) --} -- --func notImplemented(method string) error { -- return fmt.Errorf("%w: %q not yet implemented", jsonrpc2.ErrMethodNotFound, method) --} diff -urN a/gopls/internal/lsp/signature_help.go b/gopls/internal/lsp/signature_help.go --- a/gopls/internal/lsp/signature_help.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/signature_help.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,31 +0,0 @@ ++++ b/gopls/internal/lsp/signature_help.go 1970-01-01 08:00:00 +@@ -1,34 +0,0 @@ -// Copyright 2018 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. @@ -64538,6 +71900,9 @@ diff -urN a/gopls/internal/lsp/signature_help.go b/gopls/internal/lsp/signature_ -) - -func (s *Server) signatureHelp(ctx context.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) { +- ctx, done := event.Start(ctx, "lsp.Server.signatureHelp", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- - snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.Go) - defer release() - if !ok { @@ -64555,7 +71920,7 @@ diff -urN a/gopls/internal/lsp/signature_help.go b/gopls/internal/lsp/signature_ -} diff -urN a/gopls/internal/lsp/snippet/snippet_builder.go b/gopls/internal/lsp/snippet/snippet_builder.go --- a/gopls/internal/lsp/snippet/snippet_builder.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/snippet/snippet_builder.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/snippet/snippet_builder.go 1970-01-01 08:00:00 @@ -1,111 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -64670,7 +72035,7 @@ diff -urN a/gopls/internal/lsp/snippet/snippet_builder.go b/gopls/internal/lsp/s -} diff -urN a/gopls/internal/lsp/snippet/snippet_builder_test.go b/gopls/internal/lsp/snippet/snippet_builder_test.go --- a/gopls/internal/lsp/snippet/snippet_builder_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/snippet/snippet_builder_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/snippet/snippet_builder_test.go 1970-01-01 08:00:00 @@ -1,62 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -64736,7 +72101,7 @@ diff -urN a/gopls/internal/lsp/snippet/snippet_builder_test.go b/gopls/internal/ -} diff -urN a/gopls/internal/lsp/source/add_import.go b/gopls/internal/lsp/source/add_import.go --- a/gopls/internal/lsp/source/add_import.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/add_import.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/add_import.go 1970-01-01 08:00:00 @@ -1,26 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -64766,8 +72131,8 @@ diff -urN a/gopls/internal/lsp/source/add_import.go b/gopls/internal/lsp/source/ -} diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/api_json.go --- a/gopls/internal/lsp/source/api_json.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/api_json.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1118 +0,0 @@ ++++ b/gopls/internal/lsp/source/api_json.go 1970-01-01 08:00:00 +@@ -1,1263 +0,0 @@ -// Code generated by "golang.org/x/tools/gopls/doc/generate"; DO NOT EDIT. - -package source @@ -64917,6 +72282,13 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Hierarchy: "ui.completion", - }, - { +- Name: "completeFunctionCalls", +- Type: "bool", +- Doc: "completeFunctionCalls enables function call completion.\n\nWhen completing a statement, or when a function return type matches the\nexpected of the expression being completed, completion may suggest call\nexpressions (i.e. may include parentheses).\n", +- Default: "true", +- Hierarchy: "ui.completion", +- }, +- { - Name: "importShortcut", - Type: "enum", - Doc: "importShortcut specifies whether import statements should link to\ndocumentation or go to definitions.\n", @@ -64965,6 +72337,23 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Hierarchy: "ui.navigation", - }, - { +- Name: "symbolScope", +- Type: "enum", +- Doc: "symbolScope controls which packages are searched for workspace/symbol\nrequests. The default value, \"workspace\", searches only workspace\npackages. The legacy behavior, \"all\", causes all loaded packages to be\nsearched, including dependencies; this is more expensive and may return\nunwanted results.\n", +- EnumValues: []EnumValue{ +- { +- Value: "\"all\"", +- Doc: "`\"all\"` matches symbols in any loaded package, including\ndependencies.\n", +- }, +- { +- Value: "\"workspace\"", +- Doc: "`\"workspace\"` matches symbols in workspace packages only.\n", +- }, +- }, +- Default: "\"all\"", +- Hierarchy: "ui.navigation", +- }, +- { - Name: "analyses", - Type: "map[string]bool", - Doc: "analyses specify analyses that the user would like to enable or disable.\nA map of the names of analysis passes that should be enabled/disabled.\nA full list of analyzers that gopls uses can be found in\n[analyzers.md](https://github.com/golang/tools/blob/master/gopls/doc/analyzers.md).\n\nExample Usage:\n\n```json5\n...\n\"analyses\": {\n \"unreachable\": false, // Disable the unreachable analyzer.\n \"unusedparams\": true // Enable the unusedparams analyzer.\n}\n...\n```\n", @@ -64972,6 +72361,11 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - ValueType: "bool", - Keys: []EnumKey{ - { +- Name: "\"appends\"", +- Doc: "check for missing values after append\n\nThis checker reports calls to append that pass\nno values to be appended to the slice.\n\n\ts := []string{\"a\", \"b\", \"c\"}\n\t_ = append(s)\n\nSuch calls are always no-ops and often indicate an\nunderlying mistake.", +- Default: "true", +- }, +- { - Name: "\"asmdecl\"", - Doc: "report mismatches between assembly files and Go declarations", - Default: "true", @@ -65022,13 +72416,23 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Default: "true", - }, - { +- Name: "\"defers\"", +- Doc: "report common mistakes in defer statements\n\nThe defers analyzer reports a diagnostic when a defer statement would\nresult in a non-deferred call to time.Since, as experience has shown\nthat this is nearly always a mistake.\n\nFor example:\n\n\tstart := time.Now()\n\t...\n\tdefer recordLatency(time.Since(start)) // error: call to time.Since is not deferred\n\nThe correct code is:\n\n\tdefer func() { recordLatency(time.Since(start)) }()", +- Default: "true", +- }, +- { +- Name: "\"deprecated\"", +- Doc: "check for use of deprecated identifiers\n\nThe deprecated analyzer looks for deprecated symbols and package imports.\n\nSee https://go.dev/wiki/Deprecated to learn about Go's convention\nfor documenting and signaling deprecated identifiers.", +- Default: "true", +- }, +- { - Name: "\"directive\"", - Doc: "check Go toolchain directives such as //go:debug\n\nThis analyzer checks for problems with known Go toolchain directives\nin all Go source files in a package directory, even those excluded by\n//go:build constraints, and all non-Go source files too.\n\nFor //go:debug (see https://go.dev/doc/godebug), the analyzer checks\nthat the directives are placed only in Go source files, only above the\npackage comment, and only in package main or *_test.go files.\n\nSupport for other known directives may be added in the future.\n\nThis analyzer does not check //go:build, which is handled by the\nbuildtag analyzer.\n", - Default: "true", - }, - { - Name: "\"embed\"", -- Doc: "check for //go:embed directive import\n\nThis analyzer checks that the embed package is imported when source code contains //go:embed comment directives.\nThe embed package must be imported for //go:embed directives to function.import _ \"embed\".", +- Doc: "check //go:embed directive usage\n\nThis analyzer checks that the embed package is imported if //go:embed\ndirectives are present, providing a suggested fix to add the import if\nit is missing.\n\nThis analyzer also checks that //go:embed directives precede the\ndeclaration of a single variable.", - Default: "true", - }, - { @@ -65048,17 +72452,12 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - }, - { - Name: "\"ifaceassert\"", -- Doc: "detect impossible interface-to-interface type assertions\n\nThis checker flags type assertions v.(T) and corresponding type-switch cases\nin which the static type V of v is an interface that cannot possibly implement\nthe target interface T. This occurs when V and T contain methods with the same\nname but different signatures. Example:\n\n\tvar v interface {\n\t\tRead()\n\t}\n\t_ = v.(io.Reader)\n\nThe Read method in v has a different signature than the Read method in\nio.Reader, so this assertion cannot succeed.\n", -- Default: "true", -- }, -- { -- Name: "\"infertypeargs\"", -- Doc: "check for unnecessary type arguments in call expressions\n\nExplicit type arguments may be omitted from call expressions if they can be\ninferred from function arguments, or from other type arguments:\n\n\tfunc f[T any](T) {}\n\t\n\tfunc _() {\n\t\tf[string](\"foo\") // string could be inferred\n\t}\n", +- Doc: "detect impossible interface-to-interface type assertions\n\nThis checker flags type assertions v.(T) and corresponding type-switch cases\nin which the static type V of v is an interface that cannot possibly implement\nthe target interface T. This occurs when V and T contain methods with the same\nname but different signatures. Example:\n\n\tvar v interface {\n\t\tRead()\n\t}\n\t_ = v.(io.Reader)\n\nThe Read method in v has a different signature than the Read method in\nio.Reader, so this assertion cannot succeed.", - Default: "true", - }, - { - Name: "\"loopclosure\"", -- Doc: "check references to loop variables from within nested functions\n\nThis analyzer reports places where a function literal references the\niteration variable of an enclosing loop, and the loop calls the function\nin such a way (e.g. with go or defer) that it may outlive the loop\niteration and possibly observe the wrong value of the variable.\n\nIn this example, all the deferred functions run after the loop has\ncompleted, so all observe the final value of v.\n\n for _, v := range list {\n defer func() {\n use(v) // incorrect\n }()\n }\n\nOne fix is to create a new variable for each iteration of the loop:\n\n for _, v := range list {\n v := v // new var per iteration\n defer func() {\n use(v) // ok\n }()\n }\n\nThe next example uses a go statement and has a similar problem.\nIn addition, it has a data race because the loop updates v\nconcurrent with the goroutines accessing it.\n\n for _, v := range elem {\n go func() {\n use(v) // incorrect, and a data race\n }()\n }\n\nA fix is the same as before. The checker also reports problems\nin goroutines started by golang.org/x/sync/errgroup.Group.\nA hard-to-spot variant of this form is common in parallel tests:\n\n func Test(t *testing.T) {\n for _, test := range tests {\n t.Run(test.name, func(t *testing.T) {\n t.Parallel()\n use(test) // incorrect, and a data race\n })\n }\n }\n\nThe t.Parallel() call causes the rest of the function to execute\nconcurrent with the loop.\n\nThe analyzer reports references only in the last statement,\nas it is not deep enough to understand the effects of subsequent\nstatements that might render the reference benign.\n(\"Last statement\" is defined recursively in compound\nstatements such as if, switch, and select.)\n\nSee: https://golang.org/doc/go_faq.html#closures_and_goroutines", +- Doc: "check references to loop variables from within nested functions\n\nThis analyzer reports places where a function literal references the\niteration variable of an enclosing loop, and the loop calls the function\nin such a way (e.g. with go or defer) that it may outlive the loop\niteration and possibly observe the wrong value of the variable.\n\nIn this example, all the deferred functions run after the loop has\ncompleted, so all observe the final value of v.\n\n\tfor _, v := range list {\n\t defer func() {\n\t use(v) // incorrect\n\t }()\n\t}\n\nOne fix is to create a new variable for each iteration of the loop:\n\n\tfor _, v := range list {\n\t v := v // new var per iteration\n\t defer func() {\n\t use(v) // ok\n\t }()\n\t}\n\nThe next example uses a go statement and has a similar problem.\nIn addition, it has a data race because the loop updates v\nconcurrent with the goroutines accessing it.\n\n\tfor _, v := range elem {\n\t go func() {\n\t use(v) // incorrect, and a data race\n\t }()\n\t}\n\nA fix is the same as before. The checker also reports problems\nin goroutines started by golang.org/x/sync/errgroup.Group.\nA hard-to-spot variant of this form is common in parallel tests:\n\n\tfunc Test(t *testing.T) {\n\t for _, test := range tests {\n\t t.Run(test.name, func(t *testing.T) {\n\t t.Parallel()\n\t use(test) // incorrect, and a data race\n\t })\n\t }\n\t}\n\nThe t.Parallel() call causes the rest of the function to execute\nconcurrent with the loop.\n\nThe analyzer reports references only in the last statement,\nas it is not deep enough to understand the effects of subsequent\nstatements that might render the reference benign.\n(\"Last statement\" is defined recursively in compound\nstatements such as if, switch, and select.)\n\nSee: https://golang.org/doc/go_faq.html#closures_and_goroutines", - Default: "true", - }, - { @@ -65073,17 +72472,17 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - }, - { - Name: "\"nilness\"", -- Doc: "check for redundant or impossible nil comparisons\n\nThe nilness checker inspects the control-flow graph of each function in\na package and reports nil pointer dereferences, degenerate nil\npointers, and panics with nil values. A degenerate comparison is of the form\nx==nil or x!=nil where x is statically known to be nil or non-nil. These are\noften a mistake, especially in control flow related to errors. Panics with nil\nvalues are checked because they are not detectable by\n\n\tif r := recover(); r != nil {\n\nThis check reports conditions such as:\n\n\tif f == nil { // impossible condition (f is a function)\n\t}\n\nand:\n\n\tp := &v\n\t...\n\tif p != nil { // tautological condition\n\t}\n\nand:\n\n\tif p == nil {\n\t\tprint(*p) // nil dereference\n\t}\n\nand:\n\n\tif p == nil {\n\t\tpanic(p)\n\t}\n", +- Doc: "check for redundant or impossible nil comparisons\n\nThe nilness checker inspects the control-flow graph of each function in\na package and reports nil pointer dereferences, degenerate nil\npointers, and panics with nil values. A degenerate comparison is of the form\nx==nil or x!=nil where x is statically known to be nil or non-nil. These are\noften a mistake, especially in control flow related to errors. Panics with nil\nvalues are checked because they are not detectable by\n\n\tif r := recover(); r != nil {\n\nThis check reports conditions such as:\n\n\tif f == nil { // impossible condition (f is a function)\n\t}\n\nand:\n\n\tp := &v\n\t...\n\tif p != nil { // tautological condition\n\t}\n\nand:\n\n\tif p == nil {\n\t\tprint(*p) // nil dereference\n\t}\n\nand:\n\n\tif p == nil {\n\t\tpanic(p)\n\t}", - Default: "false", - }, - { - Name: "\"printf\"", -- Doc: "check consistency of Printf format strings and arguments\n\nThe check applies to known functions (for example, those in package fmt)\nas well as any detected wrappers of known functions.\n\nA function that wants to avail itself of printf checking but is not\nfound by this analyzer's heuristics (for example, due to use of\ndynamic calls) can insert a bogus call:\n\n\tif false {\n\t\t_ = fmt.Sprintf(format, args...) // enable printf checking\n\t}\n\nThe -funcs flag specifies a comma-separated list of names of additional\nknown formatting functions or methods. If the name contains a period,\nit must denote a specific function using one of the following forms:\n\n\tdir/pkg.Function\n\tdir/pkg.Type.Method\n\t(*dir/pkg.Type).Method\n\nOtherwise the name is interpreted as a case-insensitive unqualified\nidentifier such as \"errorf\". Either way, if a listed name ends in f, the\nfunction is assumed to be Printf-like, taking a format string before the\nargument list. Otherwise it is assumed to be Print-like, taking a list\nof arguments with no format string.\n", +- Doc: "check consistency of Printf format strings and arguments\n\nThe check applies to calls of the formatting functions such as\n[fmt.Printf] and [fmt.Sprintf], as well as any detected wrappers of\nthose functions.\n\nIn this example, the %d format operator requires an integer operand:\n\n\tfmt.Printf(\"%d\", \"hello\") // fmt.Printf format %d has arg \"hello\" of wrong type string\n\nSee the documentation of the fmt package for the complete set of\nformat operators and their operand types.\n\nTo enable printf checking on a function that is not found by this\nanalyzer's heuristics (for example, because control is obscured by\ndynamic method calls), insert a bogus call:\n\n\tfunc MyPrintf(format string, args ...any) {\n\t\tif false {\n\t\t\t_ = fmt.Sprintf(format, args...) // enable printf checker\n\t\t}\n\t\t...\n\t}\n\nThe -funcs flag specifies a comma-separated list of names of additional\nknown formatting functions or methods. If the name contains a period,\nit must denote a specific function using one of the following forms:\n\n\tdir/pkg.Function\n\tdir/pkg.Type.Method\n\t(*dir/pkg.Type).Method\n\nOtherwise the name is interpreted as a case-insensitive unqualified\nidentifier such as \"errorf\". Either way, if a listed name ends in f, the\nfunction is assumed to be Printf-like, taking a format string before the\nargument list. Otherwise it is assumed to be Print-like, taking a list\nof arguments with no format string.", - Default: "true", - }, - { - Name: "\"shadow\"", -- Doc: "check for possible unintended shadowing of variables\n\nThis analyzer check for shadowed variables.\nA shadowed variable is a variable declared in an inner scope\nwith the same name and type as a variable in an outer scope,\nand where the outer variable is mentioned after the inner one\nis declared.\n\n(This definition can be refined; the module generates too many\nfalse positives and is not yet enabled by default.)\n\nFor example:\n\n\tfunc BadRead(f *os.File, buf []byte) error {\n\t\tvar err error\n\t\tfor {\n\t\t\tn, err := f.Read(buf) // shadows the function variable 'err'\n\t\t\tif err != nil {\n\t\t\t\tbreak // causes return of wrong value\n\t\t\t}\n\t\t\tfoo(buf)\n\t\t}\n\t\treturn err\n\t}\n", +- Doc: "check for possible unintended shadowing of variables\n\nThis analyzer check for shadowed variables.\nA shadowed variable is a variable declared in an inner scope\nwith the same name and type as a variable in an outer scope,\nand where the outer variable is mentioned after the inner one\nis declared.\n\n(This definition can be refined; the module generates too many\nfalse positives and is not yet enabled by default.)\n\nFor example:\n\n\tfunc BadRead(f *os.File, buf []byte) error {\n\t\tvar err error\n\t\tfor {\n\t\t\tn, err := f.Read(buf) // shadows the function variable 'err'\n\t\t\tif err != nil {\n\t\t\t\tbreak // causes return of wrong value\n\t\t\t}\n\t\t\tfoo(buf)\n\t\t}\n\t\treturn err\n\t}", - Default: "false", - }, - { @@ -65107,18 +72506,23 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Default: "true", - }, - { +- Name: "\"slog\"", +- Doc: "check for invalid structured logging calls\n\nThe slog checker looks for calls to functions from the log/slog\npackage that take alternating key-value pairs. It reports calls\nwhere an argument in a key position is neither a string nor a\nslog.Attr, and where a final key is missing its value.\nFor example,it would report\n\n\tslog.Warn(\"message\", 11, \"k\") // slog.Warn arg \"11\" should be a string or a slog.Attr\n\nand\n\n\tslog.Info(\"message\", \"k1\", v1, \"k2\") // call to slog.Info missing a final value", +- Default: "true", +- }, +- { - Name: "\"sortslice\"", - Doc: "check the argument type of sort.Slice\n\nsort.Slice requires an argument of a slice type. Check that\nthe interface{} value passed to sort.Slice is actually a slice.", - Default: "true", - }, - { - Name: "\"stdmethods\"", -- Doc: "check signature of methods of well-known interfaces\n\nSometimes a type may be intended to satisfy an interface but may fail to\ndo so because of a mistake in its method signature.\nFor example, the result of this WriteTo method should be (int64, error),\nnot error, to satisfy io.WriterTo:\n\n\ttype myWriterTo struct{...}\n func (myWriterTo) WriteTo(w io.Writer) error { ... }\n\nThis check ensures that each method whose name matches one of several\nwell-known interface methods from the standard library has the correct\nsignature for that interface.\n\nChecked method names include:\n\tFormat GobEncode GobDecode MarshalJSON MarshalXML\n\tPeek ReadByte ReadFrom ReadRune Scan Seek\n\tUnmarshalJSON UnreadByte UnreadRune WriteByte\n\tWriteTo\n", +- Doc: "check signature of methods of well-known interfaces\n\nSometimes a type may be intended to satisfy an interface but may fail to\ndo so because of a mistake in its method signature.\nFor example, the result of this WriteTo method should be (int64, error),\nnot error, to satisfy io.WriterTo:\n\n\ttype myWriterTo struct{...}\n\tfunc (myWriterTo) WriteTo(w io.Writer) error { ... }\n\nThis check ensures that each method whose name matches one of several\nwell-known interface methods from the standard library has the correct\nsignature for that interface.\n\nChecked method names include:\n\n\tFormat GobEncode GobDecode MarshalJSON MarshalXML\n\tPeek ReadByte ReadFrom ReadRune Scan Seek\n\tUnmarshalJSON UnreadByte UnreadRune WriteByte\n\tWriteTo", - Default: "true", - }, - { - Name: "\"stringintconv\"", -- Doc: "check for string(int) conversions\n\nThis checker flags conversions of the form string(x) where x is an integer\n(but not byte or rune) type. Such conversions are discouraged because they\nreturn the UTF-8 representation of the Unicode code point x, and not a decimal\nstring representation of x as one might expect. Furthermore, if x denotes an\ninvalid code point, the conversion cannot be statically rejected.\n\nFor conversions that intend on using the code point, consider replacing them\nwith string(rune(x)). Otherwise, strconv.Itoa and its equivalents return the\nstring representation of the value in the desired base.\n", +- Doc: "check for string(int) conversions\n\nThis checker flags conversions of the form string(x) where x is an integer\n(but not byte or rune) type. Such conversions are discouraged because they\nreturn the UTF-8 representation of the Unicode code point x, and not a decimal\nstring representation of x as one might expect. Furthermore, if x denotes an\ninvalid code point, the conversion cannot be statically rejected.\n\nFor conversions that intend on using the code point, consider replacing them\nwith string(rune(x)). Otherwise, strconv.Itoa and its equivalents return the\nstring representation of the value in the desired base.", - Default: "true", - }, - { @@ -65128,17 +72532,17 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - }, - { - Name: "\"testinggoroutine\"", -- Doc: "report calls to (*testing.T).Fatal from goroutines started by a test.\n\nFunctions that abruptly terminate a test, such as the Fatal, Fatalf, FailNow, and\nSkip{,f,Now} methods of *testing.T, must be called from the test goroutine itself.\nThis checker detects calls to these functions that occur within a goroutine\nstarted by the test. For example:\n\nfunc TestFoo(t *testing.T) {\n go func() {\n t.Fatal(\"oops\") // error: (*T).Fatal called from non-test goroutine\n }()\n}\n", +- Doc: "report calls to (*testing.T).Fatal from goroutines started by a test.\n\nFunctions that abruptly terminate a test, such as the Fatal, Fatalf, FailNow, and\nSkip{,f,Now} methods of *testing.T, must be called from the test goroutine itself.\nThis checker detects calls to these functions that occur within a goroutine\nstarted by the test. For example:\n\n\tfunc TestFoo(t *testing.T) {\n\t go func() {\n\t t.Fatal(\"oops\") // error: (*T).Fatal called from non-test goroutine\n\t }()\n\t}", - Default: "true", - }, - { - Name: "\"tests\"", -- Doc: "check for common mistaken usages of tests and examples\n\nThe tests checker walks Test, Benchmark and Example functions checking\nmalformed names, wrong signatures and examples documenting non-existent\nidentifiers.\n\nPlease see the documentation for package testing in golang.org/pkg/testing\nfor the conventions that are enforced for Tests, Benchmarks, and Examples.", +- Doc: "check for common mistaken usages of tests and examples\n\nThe tests checker walks Test, Benchmark, Fuzzing and Example functions checking\nmalformed names, wrong signatures and examples documenting non-existent\nidentifiers.\n\nPlease see the documentation for package testing in golang.org/pkg/testing\nfor the conventions that are enforced for Tests, Benchmarks, and Examples.", - Default: "true", - }, - { - Name: "\"timeformat\"", -- Doc: "check for calls of (time.Time).Format or time.Parse with 2006-02-01\n\nThe timeformat checker looks for time formats with the 2006-02-01 (yyyy-dd-mm)\nformat. Internationally, \"yyyy-dd-mm\" does not occur in common calendar date\nstandards, and so it is more likely that 2006-01-02 (yyyy-mm-dd) was intended.\n", +- Doc: "check for calls of (time.Time).Format or time.Parse with 2006-02-01\n\nThe timeformat checker looks for time formats with the 2006-02-01 (yyyy-dd-mm)\nformat. Internationally, \"yyyy-dd-mm\" does not occur in common calendar date\nstandards, and so it is more likely that 2006-01-02 (yyyy-mm-dd) was intended.", - Default: "true", - }, - { @@ -65158,17 +72562,17 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - }, - { - Name: "\"unusedparams\"", -- Doc: "check for unused parameters of functions\n\nThe unusedparams analyzer checks functions to see if there are\nany parameters that are not being used.\n\nTo reduce false positives it ignores:\n- methods\n- parameters that do not have a name or are underscored\n- functions in test files\n- functions with empty bodies or those with just a return stmt", +- Doc: "check for unused parameters of functions\n\nThe unusedparams analyzer checks functions to see if there are\nany parameters that are not being used.\n\nTo reduce false positives it ignores:\n- methods\n- parameters that do not have a name or have the name '_' (the blank identifier)\n- functions in test files\n- functions with empty bodies or those with just a return stmt", - Default: "false", - }, - { - Name: "\"unusedresult\"", -- Doc: "check for unused results of calls to some functions\n\nSome functions like fmt.Errorf return a result and have no side effects,\nso it is always a mistake to discard the result. This analyzer reports\ncalls to certain functions in which the result of the call is ignored.\n\nThe set of functions may be controlled using flags.", +- Doc: "check for unused results of calls to some functions\n\nSome functions like fmt.Errorf return a result and have no side\neffects, so it is always a mistake to discard the result. Other\nfunctions may return an error that must not be ignored, or a cleanup\noperation that must be called. This analyzer reports calls to\nfunctions like these when the result of the call is ignored.\n\nThe set of functions may be controlled using flags.", - Default: "true", - }, - { - Name: "\"unusedwrite\"", -- Doc: "checks for unused writes\n\nThe analyzer reports instances of writes to struct fields and\narrays that are never read. Specifically, when a struct object\nor an array is copied, its elements are copied implicitly by\nthe compiler, and any element write to this copy does nothing\nwith the original object.\n\nFor example:\n\n\ttype T struct { x int }\n\tfunc f(input []T) {\n\t\tfor i, v := range input { // v is a copy\n\t\t\tv.x = i // unused write to field x\n\t\t}\n\t}\n\nAnother example is about non-pointer receiver:\n\n\ttype T struct { x int }\n\tfunc (t T) f() { // t is a copy\n\t\tt.x = i // unused write to field x\n\t}\n", +- Doc: "checks for unused writes\n\nThe analyzer reports instances of writes to struct fields and\narrays that are never read. Specifically, when a struct object\nor an array is copied, its elements are copied implicitly by\nthe compiler, and any element write to this copy does nothing\nwith the original object.\n\nFor example:\n\n\ttype T struct { x int }\n\n\tfunc f(input []T) {\n\t\tfor i, v := range input { // v is a copy\n\t\t\tv.x = i // unused write to field x\n\t\t}\n\t}\n\nAnother example is about non-pointer receiver:\n\n\ttype T struct { x int }\n\n\tfunc (t T) f() { // t is a copy\n\t\tt.x = i // unused write to field x\n\t}", - Default: "false", - }, - { @@ -65207,6 +72611,11 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Default: "true", - }, - { +- Name: "\"infertypeargs\"", +- Doc: "check for unnecessary type arguments in call expressions\n\nExplicit type arguments may be omitted from call expressions if they can be\ninferred from function arguments, or from other type arguments:\n\n\tfunc f[T any](T) {}\n\t\n\tfunc _() {\n\t\tf[string](\"foo\") // string could be inferred\n\t}\n", +- Default: "true", +- }, +- { - Name: "\"stubmethods\"", - Doc: "stub methods analyzer\n\nThis analyzer generates method stubs for concrete types\nin order to implement a target interface", - Default: "true", @@ -65279,11 +72688,18 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Name: "diagnosticsDelay", - Type: "time.Duration", - Doc: "diagnosticsDelay controls the amount of time that gopls waits\nafter the most recent file modification before computing deep diagnostics.\nSimple diagnostics (parsing and type-checking) are always run immediately\non recently modified packages.\n\nThis option must be set to a valid duration string, for example `\"250ms\"`.\n", -- Default: "\"250ms\"", +- Default: "\"1s\"", - Status: "advanced", - Hierarchy: "ui.diagnostic", - }, - { +- Name: "analysisProgressReporting", +- Type: "bool", +- Doc: "analysisProgressReporting controls whether gopls sends progress\nnotifications when construction of its index of analysis facts is taking a\nlong time. Cancelling these notifications will cancel the indexing task,\nthough it will restart after the next change in the workspace.\n\nWhen a package is opened for the first time and heavyweight analyses such as\nstaticcheck are enabled, it can take a while to construct the index of\nanalysis facts for all its dependencies. The index is cached in the\nfilesystem, so subsequent analysis should be faster.\n", +- Default: "true", +- Hierarchy: "ui.diagnostic", +- }, +- { - Name: "hints", - Type: "map[string]bool", - Doc: "hints specify inlay hints that users want to see. A full list of hints\nthat gopls uses can be found in\n[inlayHints.md](https://github.com/golang/tools/blob/master/gopls/doc/inlayHints.md).\n", @@ -65441,6 +72857,12 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - ArgDoc: "{\n\t// ImportPath is the target import path that should\n\t// be added to the URI file\n\t\"ImportPath\": string,\n\t// URI is the file that the ImportPath should be\n\t// added to\n\t\"URI\": string,\n}", - }, - { +- Command: "gopls.add_telemetry_counters", +- Title: "update the given telemetry counters.", +- Doc: "Gopls will prepend \"fwd/\" to all the counters updated using this command\nto avoid conflicts with other counters gopls collects.", +- ArgDoc: "{\n\t// Names and Values must have the same length.\n\t\"Names\": []string,\n\t\"Values\": []int64,\n}", +- }, +- { - Command: "gopls.apply_fix", - Title: "Apply a fix", - Doc: "Applies a fix to a region of source code.", @@ -65463,7 +72885,7 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Title: "Get known vulncheck result", - Doc: "Fetch the result of latest vulnerability check (`govulncheck`).", - ArgDoc: "{\n\t// The file URI.\n\t\"URI\": string,\n}", -- ResultDoc: "map[golang.org/x/tools/gopls/internal/lsp/protocol.DocumentURI]*golang.org/x/tools/gopls/internal/govulncheck.Result", +- ResultDoc: "map[golang.org/x/tools/gopls/internal/lsp/protocol.DocumentURI]*golang.org/x/tools/gopls/internal/vulncheck.Result", - }, - { - Command: "gopls.gc_details", @@ -65498,10 +72920,15 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - ResultDoc: "{\n\t// Packages is a list of packages relative\n\t// to the URIArg passed by the command request.\n\t// In other words, it omits paths that are already\n\t// imported or cannot be imported due to compiler\n\t// restrictions.\n\t\"Packages\": []string,\n}", - }, - { +- Command: "gopls.maybe_prompt_for_telemetry", +- Title: "checks for the right conditions, and then prompts", +- Doc: "the user to ask if they want to enable Go telemetry uploading. If the user\nresponds 'Yes', the telemetry mode is set to \"on\".", +- }, +- { - Command: "gopls.mem_stats", - Title: "fetch memory statistics", - Doc: "Call runtime.GC multiple times and return memory statistics as reported by\nruntime.MemStats.\n\nThis command is used for benchmarking, and may change in the future.", -- ResultDoc: "{\n\t\"HeapAlloc\": uint64,\n\t\"HeapInUse\": uint64,\n}", +- ResultDoc: "{\n\t\"HeapAlloc\": uint64,\n\t\"HeapInUse\": uint64,\n\t\"TotalAlloc\": uint64,\n}", - }, - { - Command: "gopls.regenerate_cgo", @@ -65513,7 +72940,7 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Command: "gopls.remove_dependency", - Title: "Remove a dependency", - Doc: "Removes a dependency from the go.mod file of a module.", -- ArgDoc: "{\n\t// The go.mod file URI.\n\t\"URI\": string,\n\t// The module path to remove.\n\t\"ModulePath\": string,\n\t\"OnlyDiagnostic\": bool,\n}", +- ArgDoc: "{\n\t// The go.mod file URI.\n\t\"URI\": string,\n\t// The module path to remove.\n\t\"ModulePath\": string,\n\t// If the module is tidied apart from the one unused diagnostic, we can\n\t// run `go get module@none`, and then run `go mod tidy`. Otherwise, we\n\t// must make textual edits.\n\t\"OnlyDiagnostic\": bool,\n}", - }, - { - Command: "gopls.reset_go_mod_diagnostics", @@ -65522,8 +72949,14 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - ArgDoc: "{\n\t\"URIArg\": {\n\t\t\"URI\": string,\n\t},\n\t// Optional: source of the diagnostics to reset.\n\t// If not set, all resettable go.mod diagnostics will be cleared.\n\t\"DiagnosticSource\": string,\n}", - }, - { +- Command: "gopls.run_go_work_command", +- Title: "run `go work [args...]`, and apply the resulting go.work", +- Doc: "edits to the current go.work file.", +- ArgDoc: "{\n\t\"ViewID\": string,\n\t\"InitFirst\": bool,\n\t\"Args\": []string,\n}", +- }, +- { - Command: "gopls.run_govulncheck", -- Title: "Run govulncheck.", +- Title: "Run vulncheck.", - Doc: "Run vulnerability check (`govulncheck`).", - ArgDoc: "{\n\t// Any document in the directory from which govulncheck will run.\n\t\"URI\": string,\n\t// Package pattern. E.g. \"\", \".\", \"./...\".\n\t\"Pattern\": string,\n}", - ResultDoc: "{\n\t// Token holds the progress token for LSP workDone reporting of the vulncheck\n\t// invocation.\n\t\"Token\": interface{},\n}", @@ -65538,8 +72971,22 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Command: "gopls.start_debugging", - Title: "Start the gopls debug server", - Doc: "Start the gopls debug server if it isn't running, and return the debug\naddress.", -- ArgDoc: "{\n\t// Optional: the address (including port) for the debug server to listen on.\n\t// If not provided, the debug server will bind to \"localhost:0\", and the\n\t// full debug URL will be contained in the result.\n\t// \n\t// If there is more than one gopls instance along the serving path (i.e. you\n\t// are using a daemon), each gopls instance will attempt to start debugging.\n\t// If Addr specifies a port, only the daemon will be able to bind to that\n\t// port, and each intermediate gopls instance will fail to start debugging.\n\t// For this reason it is recommended not to specify a port (or equivalently,\n\t// to specify \":0\").\n\t// \n\t// If the server was already debugging this field has no effect, and the\n\t// result will contain the previously configured debug URL(s).\n\t\"Addr\": string,\n}", -- ResultDoc: "{\n\t// The URLs to use to access the debug servers, for all gopls instances in\n\t// the serving path. For the common case of a single gopls instance (i.e. no\n\t// daemon), this will be exactly one address.\n\t// \n\t// In the case of one or more gopls instances forwarding the LSP to a daemon,\n\t// URLs will contain debug addresses for each server in the serving path, in\n\t// serving order. The daemon debug address will be the last entry in the\n\t// slice. If any intermediate gopls instance fails to start debugging, no\n\t// error will be returned but the debug URL for that server in the URLs slice\n\t// will be empty.\n\t\"URLs\": []string,\n}", +- ArgDoc: "{\n\t// Optional: the address (including port) for the debug server to listen on.\n\t// If not provided, the debug server will bind to \"localhost:0\", and the\n\t// full debug URL will be contained in the result.\n\t//\n\t// If there is more than one gopls instance along the serving path (i.e. you\n\t// are using a daemon), each gopls instance will attempt to start debugging.\n\t// If Addr specifies a port, only the daemon will be able to bind to that\n\t// port, and each intermediate gopls instance will fail to start debugging.\n\t// For this reason it is recommended not to specify a port (or equivalently,\n\t// to specify \":0\").\n\t//\n\t// If the server was already debugging this field has no effect, and the\n\t// result will contain the previously configured debug URL(s).\n\t\"Addr\": string,\n}", +- ResultDoc: "{\n\t// The URLs to use to access the debug servers, for all gopls instances in\n\t// the serving path. For the common case of a single gopls instance (i.e. no\n\t// daemon), this will be exactly one address.\n\t//\n\t// In the case of one or more gopls instances forwarding the LSP to a daemon,\n\t// URLs will contain debug addresses for each server in the serving path, in\n\t// serving order. The daemon debug address will be the last entry in the\n\t// slice. If any intermediate gopls instance fails to start debugging, no\n\t// error will be returned but the debug URL for that server in the URLs slice\n\t// will be empty.\n\t\"URLs\": []string,\n}", +- }, +- { +- Command: "gopls.start_profile", +- Title: "start capturing a profile of gopls' execution.", +- Doc: "Start a new pprof profile. Before using the resulting file, profiling must\nbe stopped with a corresponding call to StopProfile.\n\nThis command is intended for internal use only, by the gopls benchmark\nrunner.", +- ArgDoc: "struct{}", +- ResultDoc: "struct{}", +- }, +- { +- Command: "gopls.stop_profile", +- Title: "stop an ongoing profile.", +- Doc: "This command is intended for internal use only, by the gopls benchmark\nrunner.", +- ArgDoc: "struct{}", +- ResultDoc: "{\n\t// File is the profile file name.\n\t\"File\": string,\n}", - }, - { - Command: "gopls.test", @@ -65577,6 +73024,12 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Doc: "Runs `go mod vendor` for a module.", - ArgDoc: "{\n\t// The file URI.\n\t\"URI\": string,\n}", - }, +- { +- Command: "gopls.workspace_stats", +- Title: "fetch workspace statistics", +- Doc: "Query statistics about workspace builds, modules, packages, and files.\n\nThis command is intended for internal use only, by the gopls stats\ncommand.", +- ResultDoc: "{\n\t\"Files\": {\n\t\t\"Total\": int,\n\t\t\"Largest\": int,\n\t\t\"Errs\": int,\n\t},\n\t\"Views\": []{\n\t\t\"GoCommandVersion\": string,\n\t\t\"AllPackages\": {\n\t\t\t\"Packages\": int,\n\t\t\t\"LargestPackage\": int,\n\t\t\t\"CompiledGoFiles\": int,\n\t\t\t\"Modules\": int,\n\t\t},\n\t\t\"WorkspacePackages\": {\n\t\t\t\"Packages\": int,\n\t\t\t\"LargestPackage\": int,\n\t\t\t\"CompiledGoFiles\": int,\n\t\t\t\"Modules\": int,\n\t\t},\n\t\t\"Diagnostics\": int,\n\t},\n}", +- }, - }, - Lenses: []*LensJSON{ - { @@ -65596,7 +73049,7 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - }, - { - Lens: "run_govulncheck", -- Title: "Run govulncheck.", +- Title: "Run vulncheck.", - Doc: "Run vulnerability check (`govulncheck`).", - }, - { @@ -65622,120 +73075,154 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - }, - Analyzers: []*AnalyzerJSON{ - { +- Name: "appends", +- Doc: "check for missing values after append\n\nThis checker reports calls to append that pass\nno values to be appended to the slice.\n\n\ts := []string{\"a\", \"b\", \"c\"}\n\t_ = append(s)\n\nSuch calls are always no-ops and often indicate an\nunderlying mistake.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/appends", +- Default: true, +- }, +- { - Name: "asmdecl", - Doc: "report mismatches between assembly files and Go declarations", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/asmdecl", - Default: true, - }, - { - Name: "assign", - Doc: "check for useless assignments\n\nThis checker reports assignments of the form x = x or a[i] = a[i].\nThese are almost always useless, and even when they aren't they are\nusually a mistake.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/assign", - Default: true, - }, - { - Name: "atomic", - Doc: "check for common mistakes using the sync/atomic package\n\nThe atomic checker looks for assignment statements of the form:\n\n\tx = atomic.AddUint64(&x, 1)\n\nwhich are not atomic.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/atomic", - Default: true, - }, - { - Name: "atomicalign", - Doc: "check for non-64-bits-aligned arguments to sync/atomic functions", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/atomicalign", - Default: true, - }, - { - Name: "bools", - Doc: "check for common mistakes involving boolean operators", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/bools", - Default: true, - }, - { - Name: "buildtag", - Doc: "check //go:build and // +build directives", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/buildtag", - Default: true, - }, - { - Name: "cgocall", - Doc: "detect some violations of the cgo pointer passing rules\n\nCheck for invalid cgo pointer passing.\nThis looks for code that uses cgo to call C code passing values\nwhose types are almost always invalid according to the cgo pointer\nsharing rules.\nSpecifically, it warns about attempts to pass a Go chan, map, func,\nor slice to C, either directly, or via a pointer, array, or struct.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/cgocall", - Default: true, - }, - { - Name: "composites", - Doc: "check for unkeyed composite literals\n\nThis analyzer reports a diagnostic for composite literals of struct\ntypes imported from another package that do not use the field-keyed\nsyntax. Such literals are fragile because the addition of a new field\n(even if unexported) to the struct will cause compilation to fail.\n\nAs an example,\n\n\terr = &net.DNSConfigError{err}\n\nshould be replaced by:\n\n\terr = &net.DNSConfigError{Err: err}\n", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/composite", - Default: true, - }, - { - Name: "copylocks", - Doc: "check for locks erroneously passed by value\n\nInadvertently copying a value containing a lock, such as sync.Mutex or\nsync.WaitGroup, may cause both copies to malfunction. Generally such\nvalues should be referred to through a pointer.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/copylocks", - Default: true, - }, - { - Name: "deepequalerrors", - Doc: "check for calls of reflect.DeepEqual on error values\n\nThe deepequalerrors checker looks for calls of the form:\n\n reflect.DeepEqual(err1, err2)\n\nwhere err1 and err2 are errors. Using reflect.DeepEqual to compare\nerrors is discouraged.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/deepequalerrors", +- Default: true, +- }, +- { +- Name: "defers", +- Doc: "report common mistakes in defer statements\n\nThe defers analyzer reports a diagnostic when a defer statement would\nresult in a non-deferred call to time.Since, as experience has shown\nthat this is nearly always a mistake.\n\nFor example:\n\n\tstart := time.Now()\n\t...\n\tdefer recordLatency(time.Since(start)) // error: call to time.Since is not deferred\n\nThe correct code is:\n\n\tdefer func() { recordLatency(time.Since(start)) }()", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/defers", +- Default: true, +- }, +- { +- Name: "deprecated", +- Doc: "check for use of deprecated identifiers\n\nThe deprecated analyzer looks for deprecated symbols and package imports.\n\nSee https://go.dev/wiki/Deprecated to learn about Go's convention\nfor documenting and signaling deprecated identifiers.", - Default: true, - }, - { - Name: "directive", - Doc: "check Go toolchain directives such as //go:debug\n\nThis analyzer checks for problems with known Go toolchain directives\nin all Go source files in a package directory, even those excluded by\n//go:build constraints, and all non-Go source files too.\n\nFor //go:debug (see https://go.dev/doc/godebug), the analyzer checks\nthat the directives are placed only in Go source files, only above the\npackage comment, and only in package main or *_test.go files.\n\nSupport for other known directives may be added in the future.\n\nThis analyzer does not check //go:build, which is handled by the\nbuildtag analyzer.\n", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/directive", - Default: true, - }, - { - Name: "embed", -- Doc: "check for //go:embed directive import\n\nThis analyzer checks that the embed package is imported when source code contains //go:embed comment directives.\nThe embed package must be imported for //go:embed directives to function.import _ \"embed\".", +- Doc: "check //go:embed directive usage\n\nThis analyzer checks that the embed package is imported if //go:embed\ndirectives are present, providing a suggested fix to add the import if\nit is missing.\n\nThis analyzer also checks that //go:embed directives precede the\ndeclaration of a single variable.", - Default: true, - }, - { - Name: "errorsas", - Doc: "report passing non-pointer or non-error values to errors.As\n\nThe errorsas analysis reports calls to errors.As where the type\nof the second argument is not a pointer to a type implementing error.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/errorsas", - Default: true, - }, - { - Name: "fieldalignment", - Doc: "find structs that would use less memory if their fields were sorted\n\nThis analyzer find structs that can be rearranged to use less memory, and provides\na suggested edit with the most compact order.\n\nNote that there are two different diagnostics reported. One checks struct size,\nand the other reports \"pointer bytes\" used. Pointer bytes is how many bytes of the\nobject that the garbage collector has to potentially scan for pointers, for example:\n\n\tstruct { uint32; string }\n\nhave 16 pointer bytes because the garbage collector has to scan up through the string's\ninner pointer.\n\n\tstruct { string; *uint32 }\n\nhas 24 pointer bytes because it has to scan further through the *uint32.\n\n\tstruct { string; uint32 }\n\nhas 8 because it can stop immediately after the string pointer.\n\nBe aware that the most compact order is not always the most efficient.\nIn rare cases it may cause two variables each updated by its own goroutine\nto occupy the same CPU cache line, inducing a form of memory contention\nknown as \"false sharing\" that slows down both goroutines.\n", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/fieldalignment", - }, - { - Name: "httpresponse", - Doc: "check for mistakes using HTTP responses\n\nA common mistake when using the net/http package is to defer a function\ncall to close the http.Response Body before checking the error that\ndetermines whether the response is valid:\n\n\tresp, err := http.Head(url)\n\tdefer resp.Body.Close()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\t// (defer statement belongs here)\n\nThis checker helps uncover latent nil dereference bugs by reporting a\ndiagnostic for such mistakes.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/httpresponse", - Default: true, - }, - { - Name: "ifaceassert", -- Doc: "detect impossible interface-to-interface type assertions\n\nThis checker flags type assertions v.(T) and corresponding type-switch cases\nin which the static type V of v is an interface that cannot possibly implement\nthe target interface T. This occurs when V and T contain methods with the same\nname but different signatures. Example:\n\n\tvar v interface {\n\t\tRead()\n\t}\n\t_ = v.(io.Reader)\n\nThe Read method in v has a different signature than the Read method in\nio.Reader, so this assertion cannot succeed.\n", -- Default: true, -- }, -- { -- Name: "infertypeargs", -- Doc: "check for unnecessary type arguments in call expressions\n\nExplicit type arguments may be omitted from call expressions if they can be\ninferred from function arguments, or from other type arguments:\n\n\tfunc f[T any](T) {}\n\t\n\tfunc _() {\n\t\tf[string](\"foo\") // string could be inferred\n\t}\n", +- Doc: "detect impossible interface-to-interface type assertions\n\nThis checker flags type assertions v.(T) and corresponding type-switch cases\nin which the static type V of v is an interface that cannot possibly implement\nthe target interface T. This occurs when V and T contain methods with the same\nname but different signatures. Example:\n\n\tvar v interface {\n\t\tRead()\n\t}\n\t_ = v.(io.Reader)\n\nThe Read method in v has a different signature than the Read method in\nio.Reader, so this assertion cannot succeed.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/ifaceassert", - Default: true, - }, - { - Name: "loopclosure", -- Doc: "check references to loop variables from within nested functions\n\nThis analyzer reports places where a function literal references the\niteration variable of an enclosing loop, and the loop calls the function\nin such a way (e.g. with go or defer) that it may outlive the loop\niteration and possibly observe the wrong value of the variable.\n\nIn this example, all the deferred functions run after the loop has\ncompleted, so all observe the final value of v.\n\n for _, v := range list {\n defer func() {\n use(v) // incorrect\n }()\n }\n\nOne fix is to create a new variable for each iteration of the loop:\n\n for _, v := range list {\n v := v // new var per iteration\n defer func() {\n use(v) // ok\n }()\n }\n\nThe next example uses a go statement and has a similar problem.\nIn addition, it has a data race because the loop updates v\nconcurrent with the goroutines accessing it.\n\n for _, v := range elem {\n go func() {\n use(v) // incorrect, and a data race\n }()\n }\n\nA fix is the same as before. The checker also reports problems\nin goroutines started by golang.org/x/sync/errgroup.Group.\nA hard-to-spot variant of this form is common in parallel tests:\n\n func Test(t *testing.T) {\n for _, test := range tests {\n t.Run(test.name, func(t *testing.T) {\n t.Parallel()\n use(test) // incorrect, and a data race\n })\n }\n }\n\nThe t.Parallel() call causes the rest of the function to execute\nconcurrent with the loop.\n\nThe analyzer reports references only in the last statement,\nas it is not deep enough to understand the effects of subsequent\nstatements that might render the reference benign.\n(\"Last statement\" is defined recursively in compound\nstatements such as if, switch, and select.)\n\nSee: https://golang.org/doc/go_faq.html#closures_and_goroutines", +- Doc: "check references to loop variables from within nested functions\n\nThis analyzer reports places where a function literal references the\niteration variable of an enclosing loop, and the loop calls the function\nin such a way (e.g. with go or defer) that it may outlive the loop\niteration and possibly observe the wrong value of the variable.\n\nIn this example, all the deferred functions run after the loop has\ncompleted, so all observe the final value of v.\n\n\tfor _, v := range list {\n\t defer func() {\n\t use(v) // incorrect\n\t }()\n\t}\n\nOne fix is to create a new variable for each iteration of the loop:\n\n\tfor _, v := range list {\n\t v := v // new var per iteration\n\t defer func() {\n\t use(v) // ok\n\t }()\n\t}\n\nThe next example uses a go statement and has a similar problem.\nIn addition, it has a data race because the loop updates v\nconcurrent with the goroutines accessing it.\n\n\tfor _, v := range elem {\n\t go func() {\n\t use(v) // incorrect, and a data race\n\t }()\n\t}\n\nA fix is the same as before. The checker also reports problems\nin goroutines started by golang.org/x/sync/errgroup.Group.\nA hard-to-spot variant of this form is common in parallel tests:\n\n\tfunc Test(t *testing.T) {\n\t for _, test := range tests {\n\t t.Run(test.name, func(t *testing.T) {\n\t t.Parallel()\n\t use(test) // incorrect, and a data race\n\t })\n\t }\n\t}\n\nThe t.Parallel() call causes the rest of the function to execute\nconcurrent with the loop.\n\nThe analyzer reports references only in the last statement,\nas it is not deep enough to understand the effects of subsequent\nstatements that might render the reference benign.\n(\"Last statement\" is defined recursively in compound\nstatements such as if, switch, and select.)\n\nSee: https://golang.org/doc/go_faq.html#closures_and_goroutines", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/loopclosure", - Default: true, - }, - { - Name: "lostcancel", - Doc: "check cancel func returned by context.WithCancel is called\n\nThe cancellation function returned by context.WithCancel, WithTimeout,\nand WithDeadline must be called or the new context will remain live\nuntil its parent context is cancelled.\n(The background context is never cancelled.)", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/lostcancel", - Default: true, - }, - { - Name: "nilfunc", - Doc: "check for useless comparisons between functions and nil\n\nA useless comparison is one like f == nil as opposed to f() == nil.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/nilfunc", - Default: true, - }, - { - Name: "nilness", -- Doc: "check for redundant or impossible nil comparisons\n\nThe nilness checker inspects the control-flow graph of each function in\na package and reports nil pointer dereferences, degenerate nil\npointers, and panics with nil values. A degenerate comparison is of the form\nx==nil or x!=nil where x is statically known to be nil or non-nil. These are\noften a mistake, especially in control flow related to errors. Panics with nil\nvalues are checked because they are not detectable by\n\n\tif r := recover(); r != nil {\n\nThis check reports conditions such as:\n\n\tif f == nil { // impossible condition (f is a function)\n\t}\n\nand:\n\n\tp := &v\n\t...\n\tif p != nil { // tautological condition\n\t}\n\nand:\n\n\tif p == nil {\n\t\tprint(*p) // nil dereference\n\t}\n\nand:\n\n\tif p == nil {\n\t\tpanic(p)\n\t}\n", +- Doc: "check for redundant or impossible nil comparisons\n\nThe nilness checker inspects the control-flow graph of each function in\na package and reports nil pointer dereferences, degenerate nil\npointers, and panics with nil values. A degenerate comparison is of the form\nx==nil or x!=nil where x is statically known to be nil or non-nil. These are\noften a mistake, especially in control flow related to errors. Panics with nil\nvalues are checked because they are not detectable by\n\n\tif r := recover(); r != nil {\n\nThis check reports conditions such as:\n\n\tif f == nil { // impossible condition (f is a function)\n\t}\n\nand:\n\n\tp := &v\n\t...\n\tif p != nil { // tautological condition\n\t}\n\nand:\n\n\tif p == nil {\n\t\tprint(*p) // nil dereference\n\t}\n\nand:\n\n\tif p == nil {\n\t\tpanic(p)\n\t}", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/nilness", - }, - { - Name: "printf", -- Doc: "check consistency of Printf format strings and arguments\n\nThe check applies to known functions (for example, those in package fmt)\nas well as any detected wrappers of known functions.\n\nA function that wants to avail itself of printf checking but is not\nfound by this analyzer's heuristics (for example, due to use of\ndynamic calls) can insert a bogus call:\n\n\tif false {\n\t\t_ = fmt.Sprintf(format, args...) // enable printf checking\n\t}\n\nThe -funcs flag specifies a comma-separated list of names of additional\nknown formatting functions or methods. If the name contains a period,\nit must denote a specific function using one of the following forms:\n\n\tdir/pkg.Function\n\tdir/pkg.Type.Method\n\t(*dir/pkg.Type).Method\n\nOtherwise the name is interpreted as a case-insensitive unqualified\nidentifier such as \"errorf\". Either way, if a listed name ends in f, the\nfunction is assumed to be Printf-like, taking a format string before the\nargument list. Otherwise it is assumed to be Print-like, taking a list\nof arguments with no format string.\n", +- Doc: "check consistency of Printf format strings and arguments\n\nThe check applies to calls of the formatting functions such as\n[fmt.Printf] and [fmt.Sprintf], as well as any detected wrappers of\nthose functions.\n\nIn this example, the %d format operator requires an integer operand:\n\n\tfmt.Printf(\"%d\", \"hello\") // fmt.Printf format %d has arg \"hello\" of wrong type string\n\nSee the documentation of the fmt package for the complete set of\nformat operators and their operand types.\n\nTo enable printf checking on a function that is not found by this\nanalyzer's heuristics (for example, because control is obscured by\ndynamic method calls), insert a bogus call:\n\n\tfunc MyPrintf(format string, args ...any) {\n\t\tif false {\n\t\t\t_ = fmt.Sprintf(format, args...) // enable printf checker\n\t\t}\n\t\t...\n\t}\n\nThe -funcs flag specifies a comma-separated list of names of additional\nknown formatting functions or methods. If the name contains a period,\nit must denote a specific function using one of the following forms:\n\n\tdir/pkg.Function\n\tdir/pkg.Type.Method\n\t(*dir/pkg.Type).Method\n\nOtherwise the name is interpreted as a case-insensitive unqualified\nidentifier such as \"errorf\". Either way, if a listed name ends in f, the\nfunction is assumed to be Printf-like, taking a format string before the\nargument list. Otherwise it is assumed to be Print-like, taking a list\nof arguments with no format string.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/printf", - Default: true, - }, - { - Name: "shadow", -- Doc: "check for possible unintended shadowing of variables\n\nThis analyzer check for shadowed variables.\nA shadowed variable is a variable declared in an inner scope\nwith the same name and type as a variable in an outer scope,\nand where the outer variable is mentioned after the inner one\nis declared.\n\n(This definition can be refined; the module generates too many\nfalse positives and is not yet enabled by default.)\n\nFor example:\n\n\tfunc BadRead(f *os.File, buf []byte) error {\n\t\tvar err error\n\t\tfor {\n\t\t\tn, err := f.Read(buf) // shadows the function variable 'err'\n\t\t\tif err != nil {\n\t\t\t\tbreak // causes return of wrong value\n\t\t\t}\n\t\t\tfoo(buf)\n\t\t}\n\t\treturn err\n\t}\n", +- Doc: "check for possible unintended shadowing of variables\n\nThis analyzer check for shadowed variables.\nA shadowed variable is a variable declared in an inner scope\nwith the same name and type as a variable in an outer scope,\nand where the outer variable is mentioned after the inner one\nis declared.\n\n(This definition can be refined; the module generates too many\nfalse positives and is not yet enabled by default.)\n\nFor example:\n\n\tfunc BadRead(f *os.File, buf []byte) error {\n\t\tvar err error\n\t\tfor {\n\t\t\tn, err := f.Read(buf) // shadows the function variable 'err'\n\t\t\tif err != nil {\n\t\t\t\tbreak // causes return of wrong value\n\t\t\t}\n\t\t\tfoo(buf)\n\t\t}\n\t\treturn err\n\t}", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/shadow", - }, - { - Name: "shift", - Doc: "check for shifts that equal or exceed the width of the integer", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/shift", - Default: true, - }, - { @@ -65754,67 +73241,85 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Default: true, - }, - { +- Name: "slog", +- Doc: "check for invalid structured logging calls\n\nThe slog checker looks for calls to functions from the log/slog\npackage that take alternating key-value pairs. It reports calls\nwhere an argument in a key position is neither a string nor a\nslog.Attr, and where a final key is missing its value.\nFor example,it would report\n\n\tslog.Warn(\"message\", 11, \"k\") // slog.Warn arg \"11\" should be a string or a slog.Attr\n\nand\n\n\tslog.Info(\"message\", \"k1\", v1, \"k2\") // call to slog.Info missing a final value", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/slog", +- Default: true, +- }, +- { - Name: "sortslice", - Doc: "check the argument type of sort.Slice\n\nsort.Slice requires an argument of a slice type. Check that\nthe interface{} value passed to sort.Slice is actually a slice.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/sortslice", - Default: true, - }, - { - Name: "stdmethods", -- Doc: "check signature of methods of well-known interfaces\n\nSometimes a type may be intended to satisfy an interface but may fail to\ndo so because of a mistake in its method signature.\nFor example, the result of this WriteTo method should be (int64, error),\nnot error, to satisfy io.WriterTo:\n\n\ttype myWriterTo struct{...}\n func (myWriterTo) WriteTo(w io.Writer) error { ... }\n\nThis check ensures that each method whose name matches one of several\nwell-known interface methods from the standard library has the correct\nsignature for that interface.\n\nChecked method names include:\n\tFormat GobEncode GobDecode MarshalJSON MarshalXML\n\tPeek ReadByte ReadFrom ReadRune Scan Seek\n\tUnmarshalJSON UnreadByte UnreadRune WriteByte\n\tWriteTo\n", +- Doc: "check signature of methods of well-known interfaces\n\nSometimes a type may be intended to satisfy an interface but may fail to\ndo so because of a mistake in its method signature.\nFor example, the result of this WriteTo method should be (int64, error),\nnot error, to satisfy io.WriterTo:\n\n\ttype myWriterTo struct{...}\n\tfunc (myWriterTo) WriteTo(w io.Writer) error { ... }\n\nThis check ensures that each method whose name matches one of several\nwell-known interface methods from the standard library has the correct\nsignature for that interface.\n\nChecked method names include:\n\n\tFormat GobEncode GobDecode MarshalJSON MarshalXML\n\tPeek ReadByte ReadFrom ReadRune Scan Seek\n\tUnmarshalJSON UnreadByte UnreadRune WriteByte\n\tWriteTo", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/stdmethods", - Default: true, - }, - { - Name: "stringintconv", -- Doc: "check for string(int) conversions\n\nThis checker flags conversions of the form string(x) where x is an integer\n(but not byte or rune) type. Such conversions are discouraged because they\nreturn the UTF-8 representation of the Unicode code point x, and not a decimal\nstring representation of x as one might expect. Furthermore, if x denotes an\ninvalid code point, the conversion cannot be statically rejected.\n\nFor conversions that intend on using the code point, consider replacing them\nwith string(rune(x)). Otherwise, strconv.Itoa and its equivalents return the\nstring representation of the value in the desired base.\n", +- Doc: "check for string(int) conversions\n\nThis checker flags conversions of the form string(x) where x is an integer\n(but not byte or rune) type. Such conversions are discouraged because they\nreturn the UTF-8 representation of the Unicode code point x, and not a decimal\nstring representation of x as one might expect. Furthermore, if x denotes an\ninvalid code point, the conversion cannot be statically rejected.\n\nFor conversions that intend on using the code point, consider replacing them\nwith string(rune(x)). Otherwise, strconv.Itoa and its equivalents return the\nstring representation of the value in the desired base.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/stringintconv", - Default: true, - }, - { - Name: "structtag", - Doc: "check that struct field tags conform to reflect.StructTag.Get\n\nAlso report certain struct tags (json, xml) used with unexported fields.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/structtag", - Default: true, - }, - { - Name: "testinggoroutine", -- Doc: "report calls to (*testing.T).Fatal from goroutines started by a test.\n\nFunctions that abruptly terminate a test, such as the Fatal, Fatalf, FailNow, and\nSkip{,f,Now} methods of *testing.T, must be called from the test goroutine itself.\nThis checker detects calls to these functions that occur within a goroutine\nstarted by the test. For example:\n\nfunc TestFoo(t *testing.T) {\n go func() {\n t.Fatal(\"oops\") // error: (*T).Fatal called from non-test goroutine\n }()\n}\n", +- Doc: "report calls to (*testing.T).Fatal from goroutines started by a test.\n\nFunctions that abruptly terminate a test, such as the Fatal, Fatalf, FailNow, and\nSkip{,f,Now} methods of *testing.T, must be called from the test goroutine itself.\nThis checker detects calls to these functions that occur within a goroutine\nstarted by the test. For example:\n\n\tfunc TestFoo(t *testing.T) {\n\t go func() {\n\t t.Fatal(\"oops\") // error: (*T).Fatal called from non-test goroutine\n\t }()\n\t}", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/testinggoroutine", - Default: true, - }, - { - Name: "tests", -- Doc: "check for common mistaken usages of tests and examples\n\nThe tests checker walks Test, Benchmark and Example functions checking\nmalformed names, wrong signatures and examples documenting non-existent\nidentifiers.\n\nPlease see the documentation for package testing in golang.org/pkg/testing\nfor the conventions that are enforced for Tests, Benchmarks, and Examples.", +- Doc: "check for common mistaken usages of tests and examples\n\nThe tests checker walks Test, Benchmark, Fuzzing and Example functions checking\nmalformed names, wrong signatures and examples documenting non-existent\nidentifiers.\n\nPlease see the documentation for package testing in golang.org/pkg/testing\nfor the conventions that are enforced for Tests, Benchmarks, and Examples.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/tests", - Default: true, - }, - { - Name: "timeformat", -- Doc: "check for calls of (time.Time).Format or time.Parse with 2006-02-01\n\nThe timeformat checker looks for time formats with the 2006-02-01 (yyyy-dd-mm)\nformat. Internationally, \"yyyy-dd-mm\" does not occur in common calendar date\nstandards, and so it is more likely that 2006-01-02 (yyyy-mm-dd) was intended.\n", +- Doc: "check for calls of (time.Time).Format or time.Parse with 2006-02-01\n\nThe timeformat checker looks for time formats with the 2006-02-01 (yyyy-dd-mm)\nformat. Internationally, \"yyyy-dd-mm\" does not occur in common calendar date\nstandards, and so it is more likely that 2006-01-02 (yyyy-mm-dd) was intended.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/timeformat", - Default: true, - }, - { - Name: "unmarshal", - Doc: "report passing non-pointer or non-interface values to unmarshal\n\nThe unmarshal analysis reports calls to functions such as json.Unmarshal\nin which the argument type is not a pointer or an interface.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unmarshal", - Default: true, - }, - { - Name: "unreachable", - Doc: "check for unreachable code\n\nThe unreachable analyzer finds statements that execution can never reach\nbecause they are preceded by an return statement, a call to panic, an\ninfinite loop, or similar constructs.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unreachable", - Default: true, - }, - { - Name: "unsafeptr", - Doc: "check for invalid conversions of uintptr to unsafe.Pointer\n\nThe unsafeptr analyzer reports likely incorrect uses of unsafe.Pointer\nto convert integers to pointers. A conversion from uintptr to\nunsafe.Pointer is invalid if it implies that there is a uintptr-typed\nword in memory that holds a pointer value, because that word will be\ninvisible to stack copying and to the garbage collector.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unsafeptr", - Default: true, - }, - { - Name: "unusedparams", -- Doc: "check for unused parameters of functions\n\nThe unusedparams analyzer checks functions to see if there are\nany parameters that are not being used.\n\nTo reduce false positives it ignores:\n- methods\n- parameters that do not have a name or are underscored\n- functions in test files\n- functions with empty bodies or those with just a return stmt", +- Doc: "check for unused parameters of functions\n\nThe unusedparams analyzer checks functions to see if there are\nany parameters that are not being used.\n\nTo reduce false positives it ignores:\n- methods\n- parameters that do not have a name or have the name '_' (the blank identifier)\n- functions in test files\n- functions with empty bodies or those with just a return stmt", - }, - { - Name: "unusedresult", -- Doc: "check for unused results of calls to some functions\n\nSome functions like fmt.Errorf return a result and have no side effects,\nso it is always a mistake to discard the result. This analyzer reports\ncalls to certain functions in which the result of the call is ignored.\n\nThe set of functions may be controlled using flags.", +- Doc: "check for unused results of calls to some functions\n\nSome functions like fmt.Errorf return a result and have no side\neffects, so it is always a mistake to discard the result. Other\nfunctions may return an error that must not be ignored, or a cleanup\noperation that must be called. This analyzer reports calls to\nfunctions like these when the result of the call is ignored.\n\nThe set of functions may be controlled using flags.", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedresult", - Default: true, - }, - { - Name: "unusedwrite", -- Doc: "checks for unused writes\n\nThe analyzer reports instances of writes to struct fields and\narrays that are never read. Specifically, when a struct object\nor an array is copied, its elements are copied implicitly by\nthe compiler, and any element write to this copy does nothing\nwith the original object.\n\nFor example:\n\n\ttype T struct { x int }\n\tfunc f(input []T) {\n\t\tfor i, v := range input { // v is a copy\n\t\t\tv.x = i // unused write to field x\n\t\t}\n\t}\n\nAnother example is about non-pointer receiver:\n\n\ttype T struct { x int }\n\tfunc (t T) f() { // t is a copy\n\t\tt.x = i // unused write to field x\n\t}\n", +- Doc: "checks for unused writes\n\nThe analyzer reports instances of writes to struct fields and\narrays that are never read. Specifically, when a struct object\nor an array is copied, its elements are copied implicitly by\nthe compiler, and any element write to this copy does nothing\nwith the original object.\n\nFor example:\n\n\ttype T struct { x int }\n\n\tfunc f(input []T) {\n\t\tfor i, v := range input { // v is a copy\n\t\t\tv.x = i // unused write to field x\n\t\t}\n\t}\n\nAnother example is about non-pointer receiver:\n\n\ttype T struct { x int }\n\n\tfunc (t T) f() { // t is a copy\n\t\tt.x = i // unused write to field x\n\t}", +- URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedwrite", - }, - { - Name: "useany", @@ -65850,6 +73355,11 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap - Default: true, - }, - { +- Name: "infertypeargs", +- Doc: "check for unnecessary type arguments in call expressions\n\nExplicit type arguments may be omitted from call expressions if they can be\ninferred from function arguments, or from other type arguments:\n\n\tfunc f[T any](T) {}\n\t\n\tfunc _() {\n\t\tf[string](\"foo\") // string could be inferred\n\t}\n", +- Default: true, +- }, +- { - Name: "stubmethods", - Doc: "stub methods analyzer\n\nThis analyzer generates method stubs for concrete types\nin order to implement a target interface", - Default: true, @@ -65888,7 +73398,7 @@ diff -urN a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/ap -} diff -urN a/gopls/internal/lsp/source/call_hierarchy.go b/gopls/internal/lsp/source/call_hierarchy.go --- a/gopls/internal/lsp/source/call_hierarchy.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/call_hierarchy.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/call_hierarchy.go 1970-01-01 08:00:00 @@ -1,311 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -65906,10 +73416,10 @@ diff -urN a/gopls/internal/lsp/source/call_hierarchy.go b/gopls/internal/lsp/sou - "path/filepath" - - "golang.org/x/tools/go/ast/astutil" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/event/tag" -) @@ -65919,7 +73429,7 @@ diff -urN a/gopls/internal/lsp/source/call_hierarchy.go b/gopls/internal/lsp/sou - ctx, done := event.Start(ctx, "source.PrepareCallHierarchy") - defer done() - -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, err - } @@ -65999,7 +73509,7 @@ diff -urN a/gopls/internal/lsp/source/call_hierarchy.go b/gopls/internal/lsp/sou -// enclosingNodeCallItem creates a CallHierarchyItem representing the function call at loc. -func enclosingNodeCallItem(ctx context.Context, snapshot Snapshot, pkgPath PackagePath, loc protocol.Location) (protocol.CallHierarchyItem, error) { - // Parse the file containing the reference. -- fh, err := snapshot.GetFile(ctx, loc.URI.SpanURI()) +- fh, err := snapshot.ReadFile(ctx, loc.URI.SpanURI()) - if err != nil { - return protocol.CallHierarchyItem{}, err - } @@ -66074,7 +73584,7 @@ diff -urN a/gopls/internal/lsp/source/call_hierarchy.go b/gopls/internal/lsp/sou - ctx, done := event.Start(ctx, "source.OutgoingCalls") - defer done() - -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, err - } @@ -66113,7 +73623,7 @@ diff -urN a/gopls/internal/lsp/source/call_hierarchy.go b/gopls/internal/lsp/sou - } - - // Use TypecheckFull as we want to inspect the body of the function declaration. -- declPkg, declPGF, err := PackageForFile(ctx, snapshot, uri, NarrowestPackage) +- declPkg, declPGF, err := NarrowestPackageForFile(ctx, snapshot, uri) - if err != nil { - return nil, err - } @@ -66203,7 +73713,7 @@ diff -urN a/gopls/internal/lsp/source/call_hierarchy.go b/gopls/internal/lsp/sou -} diff -urN a/gopls/internal/lsp/source/code_lens.go b/gopls/internal/lsp/source/code_lens.go --- a/gopls/internal/lsp/source/code_lens.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/code_lens.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/code_lens.go 1970-01-01 08:00:00 @@ -1,248 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -66243,9 +73753,13 @@ diff -urN a/gopls/internal/lsp/source/code_lens.go b/gopls/internal/lsp/source/c -) - -func runTestCodeLens(ctx context.Context, snapshot Snapshot, fh FileHandle) ([]protocol.CodeLens, error) { -- codeLens := make([]protocol.CodeLens, 0) +- var codeLens []protocol.CodeLens - -- fns, err := TestsAndBenchmarks(ctx, snapshot, fh) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) +- if err != nil { +- return nil, err +- } +- fns, err := TestsAndBenchmarks(ctx, snapshot, pkg, pgf) - if err != nil { - return nil, err - } @@ -66301,16 +73815,12 @@ diff -urN a/gopls/internal/lsp/source/code_lens.go b/gopls/internal/lsp/source/c - Benchmarks []testFn -} - --func TestsAndBenchmarks(ctx context.Context, snapshot Snapshot, fh FileHandle) (testFns, error) { +-func TestsAndBenchmarks(ctx context.Context, snapshot Snapshot, pkg Package, pgf *ParsedGoFile) (testFns, error) { - var out testFns - -- if !strings.HasSuffix(fh.URI().Filename(), "_test.go") { +- if !strings.HasSuffix(pgf.URI.Filename(), "_test.go") { - return out, nil - } -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) -- if err != nil { -- return out, err -- } - - for _, d := range pgf.File.Decls { - fn, ok := d.(*ast.FuncDecl) @@ -66455,7 +73965,7 @@ diff -urN a/gopls/internal/lsp/source/code_lens.go b/gopls/internal/lsp/source/c -} diff -urN a/gopls/internal/lsp/source/comment.go b/gopls/internal/lsp/source/comment.go --- a/gopls/internal/lsp/source/comment.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/comment.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/comment.go 1970-01-01 08:00:00 @@ -1,384 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -66843,7 +74353,7 @@ diff -urN a/gopls/internal/lsp/source/comment.go b/gopls/internal/lsp/source/com -} diff -urN a/gopls/internal/lsp/source/comment_go118_test.go b/gopls/internal/lsp/source/comment_go118_test.go --- a/gopls/internal/lsp/source/comment_go118_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/comment_go118_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/comment_go118_test.go 1970-01-01 08:00:00 @@ -1,371 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -67218,7 +74728,7 @@ diff -urN a/gopls/internal/lsp/source/comment_go118_test.go b/gopls/internal/lsp -} diff -urN a/gopls/internal/lsp/source/comment_go119.go b/gopls/internal/lsp/source/comment_go119.go --- a/gopls/internal/lsp/source/comment_go119.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/comment_go119.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/comment_go119.go 1970-01-01 08:00:00 @@ -1,56 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -67278,7 +74788,7 @@ diff -urN a/gopls/internal/lsp/source/comment_go119.go b/gopls/internal/lsp/sour -} diff -urN a/gopls/internal/lsp/source/completion/builtin.go b/gopls/internal/lsp/source/completion/builtin.go --- a/gopls/internal/lsp/source/completion/builtin.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/builtin.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/builtin.go 1970-01-01 08:00:00 @@ -1,147 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -67429,8 +74939,8 @@ diff -urN a/gopls/internal/lsp/source/completion/builtin.go b/gopls/internal/lsp -} diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/lsp/source/completion/completion.go --- a/gopls/internal/lsp/source/completion/completion.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/completion.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3252 +0,0 @@ ++++ b/gopls/internal/lsp/source/completion/completion.go 1970-01-01 08:00:00 +@@ -1,3279 +0,0 @@ -// Copyright 2018 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. @@ -67440,12 +74950,12 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ -package completion - -import ( -- "bytes" - "context" - "fmt" - "go/ast" - "go/constant" - "go/parser" +- "go/printer" - "go/scanner" - "go/token" - "go/types" @@ -67460,6 +74970,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - - "golang.org/x/sync/errgroup" - "golang.org/x/tools/go/ast/astutil" +- goplsastutil "golang.org/x/tools/gopls/internal/astutil" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/lsp/snippet" @@ -67536,15 +75047,16 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - -// completionOptions holds completion specific configuration. -type completionOptions struct { -- unimported bool -- documentation bool -- fullDocumentation bool -- placeholders bool -- literal bool -- snippets bool -- postfix bool -- matcher source.Matcher -- budget time.Duration +- unimported bool +- documentation bool +- fullDocumentation bool +- placeholders bool +- literal bool +- snippets bool +- postfix bool +- matcher source.Matcher +- budget time.Duration +- completeFunctionCalls bool -} - -// Snippet is a convenience returns the snippet if available, otherwise @@ -67633,7 +75145,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - // completionCallbacks is a list of callbacks to collect completions that - // require expensive operations. This includes operations where we search - // through the entire module cache. -- completionCallbacks []func(opts *imports.Options) error +- completionCallbacks []func(context.Context, *imports.Options) error - - // surrounding describes the identifier surrounding the position. - surrounding *Selection @@ -67666,6 +75178,11 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - - // startTime is when we started processing this completion request. It does - // not include any time the request spent in the queue. +- // +- // Note: in CL 503016, startTime move to *after* type checking, but it was +- // subsequently determined that it was better to keep setting it *before* +- // type checking, so that the completion budget best approximates the user +- // experience. See golang/go#62665 for more details. - startTime time.Time - - // scopes contains all scopes defined by nodes in our path, @@ -67738,10 +75255,6 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - mapper *protocol.Mapper -} - --func (p Selection) Content() string { -- return p.content --} -- -func (p Selection) Range() (protocol.Range, error) { - return p.mapper.PosRange(p.tokFile, p.start, p.end) -} @@ -67879,7 +75392,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - - startTime := time.Now() - -- pkg, pgf, err := source.PackageForFile(ctx, snapshot, fh.URI(), source.NarrowestPackage) +- pkg, pgf, err := source.NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil || pgf.File.Package == token.NoPos { - // If we can't parse this file or find position for the package - // keyword, it may be missing a package declaration. Try offering @@ -67893,6 +75406,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - } - return items, surrounding, nil - } +- - pos, err := pgf.PositionPos(protoPos) - if err != nil { - return nil, nil, err @@ -67950,7 +75464,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - scopes := source.CollectScopes(pkg.GetTypesInfo(), path, pos) - scopes = append(scopes, pkg.GetTypes().Scope(), types.Universe) - -- opts := snapshot.View().Options() +- opts := snapshot.Options() - c := &completer{ - pkg: pkg, - snapshot: snapshot, @@ -67973,15 +75487,16 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - enabled: opts.DeepCompletion, - }, - opts: &completionOptions{ -- matcher: opts.Matcher, -- unimported: opts.CompleteUnimported, -- documentation: opts.CompletionDocumentation && opts.HoverKind != source.NoDocumentation, -- fullDocumentation: opts.HoverKind == source.FullDocumentation, -- placeholders: opts.UsePlaceholders, -- literal: opts.LiteralCompletions && opts.InsertTextFormat == protocol.SnippetTextFormat, -- budget: opts.CompletionBudget, -- snippets: opts.InsertTextFormat == protocol.SnippetTextFormat, -- postfix: opts.ExperimentalPostfixCompletions, +- matcher: opts.Matcher, +- unimported: opts.CompleteUnimported, +- documentation: opts.CompletionDocumentation && opts.HoverKind != source.NoDocumentation, +- fullDocumentation: opts.HoverKind == source.FullDocumentation, +- placeholders: opts.UsePlaceholders, +- literal: opts.LiteralCompletions && opts.InsertTextFormat == protocol.SnippetTextFormat, +- budget: opts.CompletionBudget, +- snippets: opts.InsertTextFormat == protocol.SnippetTextFormat, +- postfix: opts.ExperimentalPostfixCompletions, +- completeFunctionCalls: opts.CompleteFunctionCalls, - }, - // default to a matcher that always matches - matcher: prefixMatcher(""), @@ -67991,20 +75506,27 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - scopes: scopes, - } - -- var cancel context.CancelFunc -- if c.opts.budget == 0 { -- ctx, cancel = context.WithCancel(ctx) -- } else { -- // timeoutDuration is the completion budget remaining. If less than -- // 10ms, set to 10ms -- timeoutDuration := time.Until(c.startTime.Add(c.opts.budget)) -- if timeoutDuration < 10*time.Millisecond { -- timeoutDuration = 10 * time.Millisecond -- } -- ctx, cancel = context.WithTimeout(ctx, timeoutDuration) -- } +- ctx, cancel := context.WithCancel(ctx) - defer cancel() - +- // Compute the deadline for this operation. Deadline is relative to the +- // search operation, not the entire completion RPC, as the work up until this +- // point depends significantly on how long it took to type-check, which in +- // turn depends on the timing of the request relative to other operations on +- // the snapshot. Including that work in the budget leads to inconsistent +- // results (and realistically, if type-checking took 200ms already, the user +- // is unlikely to be significantly more bothered by e.g. another 100ms of +- // search). +- // +- // Don't overload the context with this deadline, as we don't want to +- // conflate user cancellation (=fail the operation) with our time limit +- // (=stop searching and succeed with partial results). +- var deadline *time.Time +- if c.opts.budget > 0 { +- d := startTime.Add(c.opts.budget) +- deadline = &d +- } +- - if surrounding := c.containingIdent(pgf.Src); surrounding != nil { - c.setSurrounding(surrounding) - } @@ -68017,17 +75539,30 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - } - - // Deep search collected candidates and their members for more candidates. -- c.deepSearch(ctx) +- c.deepSearch(ctx, 1, deadline) +- +- // At this point we have a sufficiently complete set of results, and want to +- // return as close to the completion budget as possible. Previously, we +- // avoided cancelling the context because it could result in partial results +- // for e.g. struct fields. At this point, we have a minimal valid set of +- // candidates, and so truncating due to context cancellation is acceptable. +- if c.opts.budget > 0 { +- timeoutDuration := time.Until(c.startTime.Add(c.opts.budget)) +- ctx, cancel = context.WithTimeout(ctx, timeoutDuration) +- defer cancel() +- } - - for _, callback := range c.completionCallbacks { -- if err := c.snapshot.RunProcessEnvFunc(ctx, callback); err != nil { -- return nil, nil, err +- if deadline == nil || time.Now().Before(*deadline) { +- if err := c.snapshot.RunProcessEnvFunc(ctx, callback); err != nil { +- return nil, nil, err +- } - } - } - - // Search candidates populated by expensive operations like - // unimportedMembers etc. for more completion items. -- c.deepSearch(ctx) +- c.deepSearch(ctx, 0, deadline) - - // Statement candidates offer an entire statement in certain contexts, as - // opposed to a single object. Add statement candidates last because they @@ -68046,7 +75581,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - if !(importSpec.Path.Pos() <= c.pos && c.pos <= importSpec.Path.End()) { - continue - } -- return c.populateImportCompletions(ctx, importSpec) +- return c.populateImportCompletions(importSpec) - } - - // Inside comments, offer completions for the name of the relevant symbol. @@ -68091,7 +75626,11 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - // recv.‸(arg) - case *ast.TypeAssertExpr: - // Create a fake selector expression. -- return c.selector(ctx, &ast.SelectorExpr{X: n.X}) +- // +- // The name "_" is the convention used by go/parser to represent phantom +- // selectors. +- sel := &ast.Ident{NamePos: n.X.End() + token.Pos(len(".")), Name: "_"} +- return c.selector(ctx, &ast.SelectorExpr{X: n.X, Sel: sel}) - case *ast.SelectorExpr: - return c.selector(ctx, n) - // At the file scope, only keywords are allowed. @@ -68195,7 +75734,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ -// Completions for "golang.org/" yield its subdirectories -// (i.e. "golang.org/x/"). The user is meant to accept completion suggestions -// until they reach a complete import path. --func (c *completer) populateImportCompletions(ctx context.Context, searchImport *ast.ImportSpec) error { +-func (c *completer) populateImportCompletions(searchImport *ast.ImportSpec) error { - if !strings.HasPrefix(searchImport.Path.Value, `"`) { - return nil - } @@ -68316,7 +75855,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - }) - } - -- c.completionCallbacks = append(c.completionCallbacks, func(opts *imports.Options) error { +- c.completionCallbacks = append(c.completionCallbacks, func(ctx context.Context, opts *imports.Options) error { - return imports.GetImportPaths(ctx, searchImports, prefix, c.filename, c.pkg.GetTypes().Name(), opts.Env) - }) - return nil @@ -68345,14 +75884,14 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - // comment itself. - c.opts.documentation = false - -- commentLine := file.Line(comment.End()) +- commentLine := safetoken.Line(file, comment.End()) - - // comment is valid, set surrounding as word boundaries around cursor - c.setSurroundingForComment(comment) - - // Using the next line pos, grab and parse the exported symbol on that line - for _, n := range c.file.Decls { -- declLine := file.Line(n.Pos()) +- declLine := safetoken.Line(file, n.Pos()) - // if the comment is not in, directly above or on the same line as a declaration - if declLine != commentLine && declLine != commentLine+1 && - !(n.Pos() <= comment.Pos() && comment.End() <= n.End()) { @@ -68567,7 +76106,6 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - // Imported declaration with missing type information. - // Fall through to shallow completion of unimported package members. - // Match candidate packages by path. -- // TODO(adonovan): simplify by merging with else case and matching on name only? - filter = func(m *source.Metadata) bool { - return strings.TrimPrefix(string(m.PkgPath), "vendor/") == imp.Path() - } @@ -68604,27 +76142,47 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - // not assume global Pos/Object realms and then use export - // data instead of the quick parse approach taken here. - -- // First, we search among packages in the workspace. +- // First, we search among packages in the forward transitive +- // closure of the workspace. - // We'll use a fast parse to extract package members - // from those that match the name/path criterion. - all, err := c.snapshot.AllMetadata(ctx) - if err != nil { - return err - } -- var paths []string -- known := make(map[source.PackagePath][]*source.Metadata) // may include test variant +- known := make(map[source.PackagePath]*source.Metadata) - for _, m := range all { -- if m.IsIntermediateTestVariant() || m.Name == "main" || !filter(m) { +- if m.Name == "main" { +- continue // not importable +- } +- if m.IsIntermediateTestVariant() { - continue - } -- known[m.PkgPath] = append(known[m.PkgPath], m) -- paths = append(paths, string(m.PkgPath)) +- // The only test variant we admit is "p [p.test]" +- // when we are completing within "p_test [p.test]", +- // as in that case we would like to offer completions +- // of the test variants' additional symbols. +- if m.ForTest != "" && c.pkg.Metadata().PkgPath != m.ForTest+"_test" { +- continue +- } +- if !filter(m) { +- continue +- } +- // Prefer previous entry unless this one is its test variant. +- if m.ForTest != "" || known[m.PkgPath] == nil { +- known[m.PkgPath] = m +- } +- } +- +- paths := make([]string, 0, len(known)) +- for path := range known { +- paths = append(paths, string(path)) - } - - // Rank import paths as goimports would. - var relevances map[string]float64 - if len(paths) > 0 { -- if err := c.snapshot.RunProcessEnvFunc(ctx, func(opts *imports.Options) error { +- if err := c.snapshot.RunProcessEnvFunc(ctx, func(ctx context.Context, opts *imports.Options) error { - var err error - relevances, err = imports.ScoreImportPaths(ctx, opts.Env, paths) - return err @@ -68638,18 +76196,20 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - - // quickParse does a quick parse of a single file of package m, - // extracts exported package members and adds candidates to c.items. -- var itemsMu sync.Mutex // guards c.items -- var enough int32 // atomic bool +- // TODO(rfindley): synchronizing access to c here does not feel right. +- // Consider adding a concurrency-safe API for completer. +- var cMu sync.Mutex // guards c.items and c.matcher +- var enough int32 // atomic bool - quickParse := func(uri span.URI, m *source.Metadata) error { - if atomic.LoadInt32(&enough) != 0 { - return nil - } - -- fh, err := c.snapshot.GetFile(ctx, uri) +- fh, err := c.snapshot.ReadFile(ctx, uri) - if err != nil { - return err - } -- content, err := fh.Read() +- content, err := fh.Content() - if err != nil { - return err - } @@ -68659,18 +76219,27 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - return - } - -- if !id.IsExported() || -- sel.Sel.Name != "_" && !strings.HasPrefix(id.Name, sel.Sel.Name) { -- return // not a match +- if !id.IsExported() { +- return +- } +- +- cMu.Lock() +- score := c.matcher.Score(id.Name) +- cMu.Unlock() +- +- if sel.Sel.Name != "_" && score == 0 { +- return // not a match; avoid constructing the completion item below - } - - // The only detail is the kind and package: `var (from "example.com/foo")` - // TODO(adonovan): pretty-print FuncDecl.FuncType or TypeSpec.Type? +- // TODO(adonovan): should this score consider the actual c.matcher.Score +- // of the item? How does this compare with the deepState.enqueue path? - item := CompletionItem{ - Label: id.Name, - Detail: fmt.Sprintf("%s (from %q)", strings.ToLower(tok.String()), m.PkgPath), - InsertText: id.Name, -- Score: unimportedScore(relevances[path]), +- Score: float64(score) * unimportedScore(relevances[path]), - } - switch tok { - case token.FUNC: @@ -68694,33 +76263,44 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - - // For functions, add a parameter snippet. - if fn != nil { -- var sn snippet.Builder -- sn.WriteText(id.Name) -- sn.WriteText("(") -- var nparams int -- for _, field := range fn.Type.Params.List { -- if field.Names != nil { -- nparams += len(field.Names) -- } else { -- nparams++ -- } -- } -- for i := 0; i < nparams; i++ { -- if i > 0 { -- sn.WriteText(", ") +- paramList := func(list *ast.FieldList) []string { +- var params []string +- if list != nil { +- var cfg printer.Config // slight overkill +- param := func(name string, typ ast.Expr) { +- var buf strings.Builder +- buf.WriteString(name) +- buf.WriteByte(' ') +- cfg.Fprint(&buf, token.NewFileSet(), typ) +- params = append(params, buf.String()) +- } +- +- for _, field := range list.List { +- if field.Names != nil { +- for _, name := range field.Names { +- param(name.Name, field.Type) +- } +- } else { +- param("_", field.Type) +- } +- } - } -- sn.WritePlaceholder(nil) +- return params - } -- sn.WriteText(")") +- +- tparams := paramList(fn.Type.TypeParams) +- params := paramList(fn.Type.Params) +- var sn snippet.Builder +- c.functionCallSnippet(id.Name, tparams, params, &sn) - item.snippet = &sn - } - -- itemsMu.Lock() +- cMu.Lock() - c.items = append(c.items, item) - if len(c.items) >= unimportedMemberTarget { - atomic.StoreInt32(&enough, 1) - } -- itemsMu.Unlock() +- cMu.Unlock() - }) - return nil - } @@ -68728,14 +76308,12 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - // Extract the package-level candidates using a quick parse. - var g errgroup.Group - for _, path := range paths { -- for _, m := range known[source.PackagePath(path)] { -- m := m -- for _, uri := range m.CompiledGoFiles { -- uri := uri -- g.Go(func() error { -- return quickParse(uri, m) -- }) -- } +- m := known[source.PackagePath(path)] +- for _, uri := range m.CompiledGoFiles { +- uri := uri +- g.Go(func() error { +- return quickParse(uri, m) +- }) - } - } - if err := g.Wait(); err != nil { @@ -68746,6 +76324,10 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - ctx, cancel := context.WithCancel(ctx) - var mu sync.Mutex - add := func(pkgExport imports.PackageExport) { +- if ignoreUnimportedCompletion(pkgExport.Fix) { +- return +- } +- - mu.Lock() - defer mu.Unlock() - // TODO(adonovan): what if the actual package has a vendor/ prefix? @@ -68771,7 +76353,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - } - } - -- c.completionCallbacks = append(c.completionCallbacks, func(opts *imports.Options) error { +- c.completionCallbacks = append(c.completionCallbacks, func(ctx context.Context, opts *imports.Options) error { - defer cancel() - return imports.GetPackageExports(ctx, add, id.Name, c.filename, c.pkg.GetTypes().Name(), opts.Env) - }) @@ -68797,6 +76379,13 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - } -} - +-// ignoreUnimportedCompletion reports whether an unimported completion +-// resulting in the given import should be ignored. +-func ignoreUnimportedCompletion(fix *imports.ImportFix) bool { +- // golang/go#60062: don't add unimported completion to golang.org/toolchain. +- return fix != nil && strings.HasPrefix(fix.StmtInfo.ImportPath, "golang.org/toolchain") +-} +- -func (c *completer) methodsAndFields(typ types.Type, addressable bool, imp *importInfo, cb func(candidate)) { - mset := c.methodSetCache[methodSetKey{typ, addressable}] - if mset == nil { @@ -69040,7 +76629,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - - count := 0 - -- // Search packages across the entire workspace. +- // Search the forward transitive closure of the workspace. - all, err := c.snapshot.AllMetadata(ctx) - if err != nil { - return err @@ -69064,7 +76653,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - // Rank candidates using goimports' algorithm. - var relevances map[string]float64 - if len(paths) != 0 { -- if err := c.snapshot.RunProcessEnvFunc(ctx, func(opts *imports.Options) error { +- if err := c.snapshot.RunProcessEnvFunc(ctx, func(ctx context.Context, opts *imports.Options) error { - var err error - relevances, err = imports.ScoreImportPaths(ctx, opts.Env, paths) - return err @@ -69109,6 +76698,9 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - - var mu sync.Mutex - add := func(pkg imports.ImportFix) { +- if ignoreUnimportedCompletion(&pkg) { +- return +- } - mu.Lock() - defer mu.Unlock() - if _, ok := seen[pkg.IdentName]; ok { @@ -69137,7 +76729,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - }) - count++ - } -- c.completionCallbacks = append(c.completionCallbacks, func(opts *imports.Options) error { +- c.completionCallbacks = append(c.completionCallbacks, func(ctx context.Context, opts *imports.Options) error { - defer cancel() - return imports.GetAllCandidates(ctx, add, prefix, c.filename, c.pkg.GetTypes().Name(), opts.Env) - }) @@ -70606,7 +78198,7 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ -// quick partial parse. fn is non-nil only for function declarations. -// The AST position information is garbage. -func forEachPackageMember(content []byte, f func(tok token.Token, id *ast.Ident, fn *ast.FuncDecl)) { -- purged := purgeFuncBodies(content) +- purged := goplsastutil.PurgeFuncBodies(content) - file, _ := parser.ParseFile(token.NewFileSet(), "", purged, 0) - for _, decl := range file.Decls { - switch decl := decl.(type) { @@ -70628,65 +78220,10 @@ diff -urN a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/ - } - } -} -- --// purgeFuncBodies returns a copy of src in which the contents of each --// outermost {...} region except struct and interface types have been --// deleted. It does not preserve newlines. This reduces the amount of --// work required to parse the top-level declarations. --func purgeFuncBodies(src []byte) []byte { -- // Destroy the content of any {...}-bracketed regions that are -- // not immediately preceded by a "struct" or "interface" -- // token. That includes function bodies, composite literals, -- // switch/select bodies, and all blocks of statements. -- // This will lead to non-void functions that don't have return -- // statements, which of course is a type error, but that's ok. -- -- var out bytes.Buffer -- file := token.NewFileSet().AddFile("", -1, len(src)) -- var sc scanner.Scanner -- sc.Init(file, src, nil, 0) -- var prev token.Token -- var cursor int // last consumed src offset -- var braces []token.Pos // stack of unclosed braces or -1 for struct/interface type -- for { -- pos, tok, _ := sc.Scan() -- if tok == token.EOF { -- break -- } -- switch tok { -- case token.COMMENT: -- // TODO(adonovan): opt: skip, to save an estimated 20% of time. -- -- case token.LBRACE: -- if prev == token.STRUCT || prev == token.INTERFACE { -- pos = -1 -- } -- braces = append(braces, pos) -- -- case token.RBRACE: -- if last := len(braces) - 1; last >= 0 { -- top := braces[last] -- braces = braces[:last] -- if top < 0 { -- // struct/interface type: leave alone -- } else if len(braces) == 0 { // toplevel only -- // Delete {...} body. -- start, _ := safetoken.Offset(file, top) -- end, _ := safetoken.Offset(file, pos) -- out.Write(src[cursor : start+len("{")]) -- cursor = end -- } -- } -- } -- prev = tok -- } -- out.Write(src[cursor:]) -- return out.Bytes() --} diff -urN a/gopls/internal/lsp/source/completion/deep_completion.go b/gopls/internal/lsp/source/completion/deep_completion.go --- a/gopls/internal/lsp/source/completion/deep_completion.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/deep_completion.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,362 +0,0 @@ ++++ b/gopls/internal/lsp/source/completion/deep_completion.go 1970-01-01 08:00:00 +@@ -1,378 +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. @@ -70802,7 +78339,7 @@ diff -urN a/gopls/internal/lsp/source/completion/deep_completion.go b/gopls/inte -// deepSearch searches a candidate and its subordinate objects for completion -// items if deep completion is enabled and adds the valid candidates to -// completion items. --func (c *completer) deepSearch(ctx context.Context) { +-func (c *completer) deepSearch(ctx context.Context, minDepth int, deadline *time.Time) { - defer func() { - // We can return early before completing the search, so be sure to - // clear out our queues to not impact any further invocations. @@ -70810,7 +78347,25 @@ diff -urN a/gopls/internal/lsp/source/completion/deep_completion.go b/gopls/inte - c.deepState.nextQueue = c.deepState.nextQueue[:0] - }() - +- depth := 0 // current depth being processed +- // Stop reports whether we should stop the search immediately. +- stop := func() bool { +- // Context cancellation indicates that the actual completion operation was +- // cancelled, so ignore minDepth and deadline. +- select { +- case <-ctx.Done(): +- return true +- default: +- } +- // Otherwise, only stop if we've searched at least minDepth and reached the deadline. +- return depth > minDepth && deadline != nil && time.Now().After(*deadline) +- } +- - for len(c.deepState.nextQueue) > 0 { +- depth++ +- if stop() { +- return +- } - c.deepState.thisQueue, c.deepState.nextQueue = c.deepState.nextQueue, c.deepState.thisQueue[:0] - - outer: @@ -70859,17 +78414,15 @@ diff -urN a/gopls/internal/lsp/source/completion/deep_completion.go b/gopls/inte - - c.deepState.candidateCount++ - if c.opts.budget > 0 && c.deepState.candidateCount%100 == 0 { -- spent := float64(time.Since(c.startTime)) / float64(c.opts.budget) -- select { -- case <-ctx.Done(): +- if stop() { - return -- default: -- // If we are almost out of budgeted time, no further elements -- // should be added to the queue. This ensures remaining time is -- // used for processing current queue. -- if !c.deepState.queueClosed && spent >= 0.85 { -- c.deepState.queueClosed = true -- } +- } +- spent := float64(time.Since(c.startTime)) / float64(c.opts.budget) +- // If we are almost out of budgeted time, no further elements +- // should be added to the queue. This ensures remaining time is +- // used for processing current queue. +- if !c.deepState.queueClosed && spent >= 0.85 { +- c.deepState.queueClosed = true - } - } - @@ -71051,7 +78604,7 @@ diff -urN a/gopls/internal/lsp/source/completion/deep_completion.go b/gopls/inte -} diff -urN a/gopls/internal/lsp/source/completion/deep_completion_test.go b/gopls/internal/lsp/source/completion/deep_completion_test.go --- a/gopls/internal/lsp/source/completion/deep_completion_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/deep_completion_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/deep_completion_test.go 1970-01-01 08:00:00 @@ -1,33 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -71088,7 +78641,7 @@ diff -urN a/gopls/internal/lsp/source/completion/deep_completion_test.go b/gopls -} diff -urN a/gopls/internal/lsp/source/completion/definition.go b/gopls/internal/lsp/source/completion/definition.go --- a/gopls/internal/lsp/source/completion/definition.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/definition.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/definition.go 1970-01-01 08:00:00 @@ -1,160 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -71252,8 +78805,8 @@ diff -urN a/gopls/internal/lsp/source/completion/definition.go b/gopls/internal/ -} diff -urN a/gopls/internal/lsp/source/completion/format.go b/gopls/internal/lsp/source/completion/format.go --- a/gopls/internal/lsp/source/completion/format.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/format.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,338 +0,0 @@ ++++ b/gopls/internal/lsp/source/completion/format.go 1970-01-01 08:00:00 +@@ -1,345 +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. @@ -71439,11 +78992,18 @@ diff -urN a/gopls/internal/lsp/source/completion/format.go b/gopls/internal/lsp/ - if cand.convertTo != nil { - typeName := types.TypeString(cand.convertTo, c.qf) - -- switch cand.convertTo.(type) { +- switch t := cand.convertTo.(type) { - // We need extra parens when casting to these types. For example, - // we need "(*int)(foo)", not "*int(foo)". - case *types.Pointer, *types.Signature: - typeName = "(" + typeName + ")" +- case *types.Basic: +- // If the types are incompatible (as determined by typeMatches), then we +- // must need a conversion here. However, if the target type is untyped, +- // don't suggest converting to e.g. "untyped float" (golang/go#62141). +- if t.Info()&types.IsUntyped != 0 { +- typeName = types.TypeString(types.Default(cand.convertTo), c.qf) +- } - } - - prefix = typeName + "(" + prefix @@ -71512,9 +79072,9 @@ diff -urN a/gopls/internal/lsp/source/completion/format.go b/gopls/internal/lsp/ - // TODO(rfindley): It doesn't look like this does the right thing for - // multi-line comments. - if strings.HasPrefix(comment.Text(), "Deprecated") { -- if c.snapshot.View().Options().CompletionTags { +- if c.snapshot.Options().CompletionTags { - item.Tags = []protocol.CompletionItemTag{protocol.ComplDeprecated} -- } else if c.snapshot.View().Options().CompletionDeprecated { +- } else if c.snapshot.Options().CompletionDeprecated { - item.Deprecated = true - } - } @@ -71594,7 +79154,7 @@ diff -urN a/gopls/internal/lsp/source/completion/format.go b/gopls/internal/lsp/ -} diff -urN a/gopls/internal/lsp/source/completion/fuzz.go b/gopls/internal/lsp/source/completion/fuzz.go --- a/gopls/internal/lsp/source/completion/fuzz.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/fuzz.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/fuzz.go 1970-01-01 08:00:00 @@ -1,142 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -71740,7 +79300,7 @@ diff -urN a/gopls/internal/lsp/source/completion/fuzz.go b/gopls/internal/lsp/so -} diff -urN a/gopls/internal/lsp/source/completion/keywords.go b/gopls/internal/lsp/source/completion/keywords.go --- a/gopls/internal/lsp/source/completion/keywords.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/keywords.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/keywords.go 1970-01-01 08:00:00 @@ -1,154 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -71898,7 +79458,7 @@ diff -urN a/gopls/internal/lsp/source/completion/keywords.go b/gopls/internal/ls -} diff -urN a/gopls/internal/lsp/source/completion/labels.go b/gopls/internal/lsp/source/completion/labels.go --- a/gopls/internal/lsp/source/completion/labels.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/labels.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/labels.go 1970-01-01 08:00:00 @@ -1,112 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -72014,7 +79574,7 @@ diff -urN a/gopls/internal/lsp/source/completion/labels.go b/gopls/internal/lsp/ -} diff -urN a/gopls/internal/lsp/source/completion/literal.go b/gopls/internal/lsp/source/completion/literal.go --- a/gopls/internal/lsp/source/completion/literal.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/literal.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/literal.go 1970-01-01 08:00:00 @@ -1,592 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -72610,7 +80170,7 @@ diff -urN a/gopls/internal/lsp/source/completion/literal.go b/gopls/internal/lsp -} diff -urN a/gopls/internal/lsp/source/completion/package.go b/gopls/internal/lsp/source/completion/package.go --- a/gopls/internal/lsp/source/completion/package.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/package.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/package.go 1970-01-01 08:00:00 @@ -1,351 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -72722,7 +80282,7 @@ diff -urN a/gopls/internal/lsp/source/completion/package.go b/gopls/internal/lsp - // appear on any line of the file as long as it's the first code expression - // in the file. - lines := strings.Split(string(pgf.Src), "\n") -- cursorLine := tok.Line(cursor) +- cursorLine := safetoken.Line(tok, cursor) - if cursorLine <= 0 || cursorLine > len(lines) { - return nil, fmt.Errorf("invalid line number") - } @@ -72817,7 +80377,7 @@ diff -urN a/gopls/internal/lsp/source/completion/package.go b/gopls/internal/lsp -// file. This also includes test packages for these packages (_test) and -// the directory name itself. -func packageSuggestions(ctx context.Context, snapshot source.Snapshot, fileURI span.URI, prefix string) (packages []candidate, err error) { -- active, err := snapshot.ActiveMetadata(ctx) +- active, err := snapshot.WorkspaceMetadata(ctx) - if err != nil { - return nil, err - } @@ -72965,7 +80525,7 @@ diff -urN a/gopls/internal/lsp/source/completion/package.go b/gopls/internal/lsp -} diff -urN a/gopls/internal/lsp/source/completion/package_test.go b/gopls/internal/lsp/source/completion/package_test.go --- a/gopls/internal/lsp/source/completion/package_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/package_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/package_test.go 1970-01-01 08:00:00 @@ -1,81 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -73050,8 +80610,8 @@ diff -urN a/gopls/internal/lsp/source/completion/package_test.go b/gopls/interna -} diff -urN a/gopls/internal/lsp/source/completion/postfix_snippets.go b/gopls/internal/lsp/source/completion/postfix_snippets.go --- a/gopls/internal/lsp/source/completion/postfix_snippets.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/postfix_snippets.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,471 +0,0 @@ ++++ b/gopls/internal/lsp/source/completion/postfix_snippets.go 1970-01-01 08:00:00 +@@ -1,481 +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. @@ -73071,6 +80631,7 @@ diff -urN a/gopls/internal/lsp/source/completion/postfix_snippets.go b/gopls/int - "text/template" - - "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/lsp/snippet" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/internal/event" @@ -73247,6 +80808,14 @@ diff -urN a/gopls/internal/lsp/source/completion/postfix_snippets.go b/gopls/int - body: `{{if and (eq .Kind "slice") (eq (.TypeName .ElemType) "string") -}} -{{.Import "strings"}}.Join({{.X}}, "{{.Cursor}}") -{{- end}}`, +-}, { +- label: "ifnotnil", +- details: "if expr != nil", +- body: `{{if and (or (eq .Kind "pointer") (eq .Kind "chan") (eq .Kind "signature") (eq .Kind "interface") (eq .Kind "map") (eq .Kind "slice")) .StmtOK -}} +-if {{.X}} != nil {{"{"}} +- {{.Cursor}} +-{{"}"}} +-{{- end}}`, -}} - -// Cursor indicates where the client's cursor should end up after the @@ -73264,6 +80833,7 @@ diff -urN a/gopls/internal/lsp/source/completion/postfix_snippets.go b/gopls/int - return "", fmt.Errorf("couldn't import %q: %w", path, err) - } - a.edits = append(a.edits, edits...) +- - return name, nil -} - @@ -73394,7 +80964,7 @@ diff -urN a/gopls/internal/lsp/source/completion/postfix_snippets.go b/gopls/int - // - // detect that "foo." makes up the entire statement since the - // apparent selector spans lines. -- stmtOK = tokFile.Line(c.pos) < tokFile.Line(p.TokPos) +- stmtOK = safetoken.Line(tokFile, c.pos) < safetoken.Line(tokFile, p.TokPos) - } - break - } @@ -73416,8 +80986,8 @@ diff -urN a/gopls/internal/lsp/source/completion/postfix_snippets.go b/gopls/int - // - // and adjust afterDot so that we don't mistakenly delete the - // newline thinking "bar" is part of our selector. -- if startLine := tokFile.Line(sel.Pos()); startLine != tokFile.Line(afterDot) { -- if tokFile.Line(c.pos) != startLine { +- if startLine := safetoken.Line(tokFile, sel.Pos()); startLine != safetoken.Line(tokFile, afterDot) { +- if safetoken.Line(tokFile, c.pos) != startLine { - return - } - afterDot = c.pos @@ -73525,7 +81095,7 @@ diff -urN a/gopls/internal/lsp/source/completion/postfix_snippets.go b/gopls/int -} diff -urN a/gopls/internal/lsp/source/completion/printf.go b/gopls/internal/lsp/source/completion/printf.go --- a/gopls/internal/lsp/source/completion/printf.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/printf.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/printf.go 1970-01-01 08:00:00 @@ -1,172 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -73701,7 +81271,7 @@ diff -urN a/gopls/internal/lsp/source/completion/printf.go b/gopls/internal/lsp/ -} diff -urN a/gopls/internal/lsp/source/completion/printf_test.go b/gopls/internal/lsp/source/completion/printf_test.go --- a/gopls/internal/lsp/source/completion/printf_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/printf_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/printf_test.go 1970-01-01 08:00:00 @@ -1,72 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -73777,8 +81347,8 @@ diff -urN a/gopls/internal/lsp/source/completion/printf_test.go b/gopls/internal -} diff -urN a/gopls/internal/lsp/source/completion/snippet.go b/gopls/internal/lsp/source/completion/snippet.go --- a/gopls/internal/lsp/source/completion/snippet.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/snippet.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,116 +0,0 @@ ++++ b/gopls/internal/lsp/source/completion/snippet.go 1970-01-01 08:00:00 +@@ -1,121 +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. @@ -73832,6 +81402,11 @@ diff -urN a/gopls/internal/lsp/source/completion/snippet.go b/gopls/internal/lsp - -// functionCallSnippet calculates the snippet for function calls. -func (c *completer) functionCallSnippet(name string, tparams, params []string, snip *snippet.Builder) { +- if !c.opts.completeFunctionCalls { +- snip.WriteText(name) +- return +- } +- - // If there is no suffix then we need to reuse existing call parens - // "()" if present. If there is an identifier suffix then we always - // need to include "()" since we don't overwrite the suffix. @@ -73897,7 +81472,7 @@ diff -urN a/gopls/internal/lsp/source/completion/snippet.go b/gopls/internal/lsp -} diff -urN a/gopls/internal/lsp/source/completion/statements.go b/gopls/internal/lsp/source/completion/statements.go --- a/gopls/internal/lsp/source/completion/statements.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/statements.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/statements.go 1970-01-01 08:00:00 @@ -1,361 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -74262,7 +81837,7 @@ diff -urN a/gopls/internal/lsp/source/completion/statements.go b/gopls/internal/ -} diff -urN a/gopls/internal/lsp/source/completion/util.go b/gopls/internal/lsp/source/completion/util.go --- a/gopls/internal/lsp/source/completion/util.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/util.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/util.go 1970-01-01 08:00:00 @@ -1,344 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -74610,7 +82185,7 @@ diff -urN a/gopls/internal/lsp/source/completion/util.go b/gopls/internal/lsp/so -} diff -urN a/gopls/internal/lsp/source/completion/util_test.go b/gopls/internal/lsp/source/completion/util_test.go --- a/gopls/internal/lsp/source/completion/util_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/completion/util_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/completion/util_test.go 1970-01-01 08:00:00 @@ -1,28 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -74642,8 +82217,8 @@ diff -urN a/gopls/internal/lsp/source/completion/util_test.go b/gopls/internal/l -} diff -urN a/gopls/internal/lsp/source/definition.go b/gopls/internal/lsp/source/definition.go --- a/gopls/internal/lsp/source/definition.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/definition.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,229 +0,0 @@ ++++ b/gopls/internal/lsp/source/definition.go 1970-01-01 08:00:00 +@@ -1,261 +0,0 @@ -// Copyright 2023 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. @@ -74652,14 +82227,15 @@ diff -urN a/gopls/internal/lsp/source/definition.go b/gopls/internal/lsp/source/ - -import ( - "context" +- "errors" - "fmt" - "go/ast" - "go/token" - "go/types" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/event" -) - @@ -74668,7 +82244,7 @@ diff -urN a/gopls/internal/lsp/source/definition.go b/gopls/internal/lsp/source/ - ctx, done := event.Start(ctx, "source.Definition") - defer done() - -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, err - } @@ -74704,31 +82280,67 @@ diff -urN a/gopls/internal/lsp/source/definition.go b/gopls/internal/lsp/source/ - return []protocol.Location{loc}, nil - } - +- // Handle the case where the cursor is in a linkname directive. +- locations, err := LinknameDefinition(ctx, snapshot, fh, position) +- if !errors.Is(err, ErrNoLinkname) { +- return locations, err +- } +- +- // Handle the case where the cursor is in an embed directive. +- locations, err = EmbedDefinition(pgf.Mapper, position) +- if !errors.Is(err, ErrNoEmbed) { +- return locations, err +- } +- - // The general case: the cursor is on an identifier. - _, obj, _ := referencedObject(pkg, pgf, pos) - if obj == nil { - return nil, nil - } - -- // Handle built-in identifiers. -- if obj.Parent() == types.Universe { -- builtin, err := snapshot.BuiltinFile(ctx) -- if err != nil { -- return nil, err +- // Handle objects with no position: builtin, unsafe. +- if !obj.Pos().IsValid() { +- var pgf *ParsedGoFile +- if obj.Parent() == types.Universe { +- // pseudo-package "builtin" +- builtinPGF, err := snapshot.BuiltinFile(ctx) +- if err != nil { +- return nil, err +- } +- pgf = builtinPGF +- +- } else if obj.Pkg() == types.Unsafe { +- // package "unsafe" +- unsafe := snapshot.Metadata("unsafe") +- if unsafe == nil { +- return nil, fmt.Errorf("no metadata for package 'unsafe'") +- } +- uri := unsafe.GoFiles[0] +- fh, err := snapshot.ReadFile(ctx, uri) +- if err != nil { +- return nil, err +- } +- pgf, err = snapshot.ParseGo(ctx, fh, ParseFull&^SkipObjectResolution) +- if err != nil { +- return nil, err +- } +- +- } else { +- return nil, bug.Errorf("internal error: no position for %v", obj.Name()) - } -- // Note that builtinObj is an ast.Object, not types.Object :) -- builtinObj := builtin.File.Scope.Lookup(obj.Name()) -- if builtinObj == nil { -- // Every builtin should have documentation. -- return nil, bug.Errorf("internal error: no builtin object for %s", obj.Name()) +- // Inv: pgf ∈ {builtin,unsafe}.go +- +- // Use legacy (go/ast) object resolution. +- astObj := pgf.File.Scope.Lookup(obj.Name()) +- if astObj == nil { +- // Every built-in should have documentation syntax. +- return nil, bug.Errorf("internal error: no object for %s", obj.Name()) - } -- decl, ok := builtinObj.Decl.(ast.Node) +- decl, ok := astObj.Decl.(ast.Node) - if !ok { - return nil, bug.Errorf("internal error: no declaration for %s", obj.Name()) - } -- // The builtin package isn't in the dependency graph, so the usual -- // utilities won't work here. -- loc, err := builtin.PosLocation(decl.Pos(), decl.Pos()+token.Pos(len(obj.Name()))) +- loc, err := pgf.PosLocation(decl.Pos(), decl.Pos()+token.Pos(len(obj.Name()))) - if err != nil { - return nil, err - } @@ -74736,16 +82348,11 @@ diff -urN a/gopls/internal/lsp/source/definition.go b/gopls/internal/lsp/source/ - } - - // Finally, map the object position. -- var locs []protocol.Location -- if !obj.Pos().IsValid() { -- return nil, bug.Errorf("internal error: no position for %v", obj.Name()) -- } - loc, err := mapPosition(ctx, pkg.FileSet(), snapshot, obj.Pos(), adjustedObjEnd(obj)) - if err != nil { - return nil, err - } -- locs = append(locs, loc) -- return locs, nil +- return []protocol.Location{loc}, nil -} - -// referencedObject returns the identifier and object referenced at the @@ -74829,7 +82436,7 @@ diff -urN a/gopls/internal/lsp/source/definition.go b/gopls/internal/lsp/source/ - - var locs []protocol.Location - for _, f := range impMetadata.CompiledGoFiles { -- fh, err := s.GetFile(ctx, f) +- fh, err := s.ReadFile(ctx, f) - if err != nil { - if ctx.Err() != nil { - return nil, ctx.Err() @@ -74862,11 +82469,11 @@ diff -urN a/gopls/internal/lsp/source/definition.go b/gopls/internal/lsp/source/ -func mapPosition(ctx context.Context, fset *token.FileSet, s FileSource, start, end token.Pos) (protocol.Location, error) { - file := fset.File(start) - uri := span.URIFromPath(file.Name()) -- fh, err := s.GetFile(ctx, uri) +- fh, err := s.ReadFile(ctx, uri) - if err != nil { - return protocol.Location{}, err - } -- content, err := fh.Read() +- content, err := fh.Content() - if err != nil { - return protocol.Location{}, err - } @@ -74875,8 +82482,8 @@ diff -urN a/gopls/internal/lsp/source/definition.go b/gopls/internal/lsp/source/ -} diff -urN a/gopls/internal/lsp/source/diagnostics.go b/gopls/internal/lsp/source/diagnostics.go --- a/gopls/internal/lsp/source/diagnostics.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/diagnostics.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,138 +0,0 @@ ++++ b/gopls/internal/lsp/source/diagnostics.go 1970-01-01 08:00:00 +@@ -1,187 +0,0 @@ -// Copyright 2018 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. @@ -74885,7 +82492,10 @@ diff -urN a/gopls/internal/lsp/source/diagnostics.go b/gopls/internal/lsp/source - -import ( - "context" +- "encoding/json" - +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/progress" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/span" -) @@ -74898,22 +82508,22 @@ diff -urN a/gopls/internal/lsp/source/diagnostics.go b/gopls/internal/lsp/source -} - -// Analyze reports go/analysis-framework diagnostics in the specified package. --func Analyze(ctx context.Context, snapshot Snapshot, pkgid PackageID, includeConvenience bool) (map[span.URI][]*Diagnostic, error) { +-// +-// If the provided tracker is non-nil, it may be used to provide notifications +-// of the ongoing analysis pass. +-func Analyze(ctx context.Context, snapshot Snapshot, pkgIDs map[PackageID]unit, tracker *progress.Tracker) (map[span.URI][]*Diagnostic, error) { - // Exit early if the context has been canceled. This also protects us - // from a race on Options, see golang/go#36699. - if ctx.Err() != nil { - return nil, ctx.Err() - } - -- options := snapshot.View().Options() +- options := snapshot.Options() - categories := []map[string]*Analyzer{ - options.DefaultAnalyzers, - options.StaticcheckAnalyzers, - options.TypeErrorAnalyzers, - } -- if includeConvenience { // e.g. for codeAction -- categories = append(categories, options.ConvenienceAnalyzers) // e.g. fillstruct -- } - - var analyzers []*Analyzer - for _, cat := range categories { @@ -74922,7 +82532,7 @@ diff -urN a/gopls/internal/lsp/source/diagnostics.go b/gopls/internal/lsp/source - } - } - -- analysisDiagnostics, err := snapshot.Analyze(ctx, pkgid, analyzers) +- analysisDiagnostics, err := snapshot.Analyze(ctx, pkgIDs, analyzers, tracker) - if err != nil { - return nil, err - } @@ -74935,38 +82545,6 @@ diff -urN a/gopls/internal/lsp/source/diagnostics.go b/gopls/internal/lsp/source - return reports, nil -} - --// FileDiagnostics reports diagnostics in the specified file, --// as used by the "gopls check" command. --// --// TODO(adonovan): factor in common with (*Server).codeAction, which --// executes { PackageForFile; Analyze } too? --// --// TODO(adonovan): opt: this function is called in a loop from the --// "gopls/diagnoseFiles" nonstandard request handler. It would be more --// efficient to compute the set of packages and TypeCheck and --// Analyze them all at once. --func FileDiagnostics(ctx context.Context, snapshot Snapshot, uri span.URI) (FileHandle, []*Diagnostic, error) { -- fh, err := snapshot.GetFile(ctx, uri) -- if err != nil { -- return nil, nil, err -- } -- pkg, _, err := PackageForFile(ctx, snapshot, uri, NarrowestPackage) -- if err != nil { -- return nil, nil, err -- } -- pkgDiags, err := pkg.DiagnosticsForFile(ctx, snapshot, uri) -- if err != nil { -- return nil, nil, err -- } -- adiags, err := Analyze(ctx, snapshot, pkg.Metadata().ID, false) -- if err != nil { -- return nil, nil, err -- } -- var fileDiags []*Diagnostic // combine load/parse/type + analysis diagnostics -- CombineDiagnostics(pkgDiags, adiags[uri], &fileDiags, &fileDiags) -- return fh, fileDiags, nil --} -- -// CombineDiagnostics combines and filters list/parse/type diagnostics from -// tdiags with adiags, and appends the two lists to *outT and *outA, -// respectively. @@ -75015,10 +82593,287 @@ diff -urN a/gopls/internal/lsp/source/diagnostics.go b/gopls/internal/lsp/source - - *outT = append(*outT, tdiags...) -} +- +-// quickFixesJSON is a JSON-serializable list of quick fixes +-// to be saved in the protocol.Diagnostic.Data field. +-type quickFixesJSON struct { +- // TODO(rfindley): pack some sort of identifier here for later +- // lookup/validation? +- Fixes []protocol.CodeAction +-} +- +-// BundleQuickFixes attempts to bundle sd.SuggestedFixes into the +-// sd.BundledFixes field, so that it can be round-tripped through the client. +-// It returns false if the quick-fixes cannot be bundled. +-func BundleQuickFixes(sd *Diagnostic) bool { +- if len(sd.SuggestedFixes) == 0 { +- return true +- } +- var actions []protocol.CodeAction +- for _, fix := range sd.SuggestedFixes { +- if fix.Edits != nil { +- // For now, we only support bundled code actions that execute commands. +- // +- // In order to cleanly support bundled edits, we'd have to guarantee that +- // the edits were generated on the current snapshot. But this naively +- // implies that every fix would have to include a snapshot ID, which +- // would require us to republish all diagnostics on each new snapshot. +- // +- // TODO(rfindley): in order to avoid this additional chatter, we'd need +- // to build some sort of registry or other mechanism on the snapshot to +- // check whether a diagnostic is still valid. +- return false +- } +- action := protocol.CodeAction{ +- Title: fix.Title, +- Kind: fix.ActionKind, +- Command: fix.Command, +- } +- actions = append(actions, action) +- } +- fixes := quickFixesJSON{ +- Fixes: actions, +- } +- data, err := json.Marshal(fixes) +- if err != nil { +- bug.Reportf("marshalling quick fixes: %v", err) +- return false +- } +- msg := json.RawMessage(data) +- sd.BundledFixes = &msg +- return true +-} +- +-// BundledQuickFixes extracts any bundled codeActions from the +-// diag.Data field. +-func BundledQuickFixes(diag protocol.Diagnostic) []protocol.CodeAction { +- if diag.Data == nil { +- return nil +- } +- var fix quickFixesJSON +- if err := json.Unmarshal(*diag.Data, &fix); err != nil { +- bug.Reportf("unmarshalling quick fix: %v", err) +- return nil +- } +- +- var actions []protocol.CodeAction +- for _, action := range fix.Fixes { +- // See BundleQuickFixes: for now we only support bundling commands. +- if action.Edit != nil { +- bug.Reportf("bundled fix %q includes workspace edits", action.Title) +- continue +- } +- // associate the action with the incoming diagnostic +- // (Note that this does not mutate the fix.Fixes slice). +- action.Diagnostics = []protocol.Diagnostic{diag} +- actions = append(actions, action) +- } +- +- return actions +-} +diff -urN a/gopls/internal/lsp/source/embeddirective.go b/gopls/internal/lsp/source/embeddirective.go +--- a/gopls/internal/lsp/source/embeddirective.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/embeddirective.go 1970-01-01 08:00:00 +@@ -1,195 +0,0 @@ +-// Copyright 2023 The Go Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style +-// license that can be found in the LICENSE file. +- +-package source +- +-import ( +- "errors" +- "fmt" +- "io/fs" +- "path/filepath" +- "strconv" +- "strings" +- "unicode" +- "unicode/utf8" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +-) +- +-// ErrNoEmbed is returned by EmbedDefinition when no embed +-// directive is found at a particular position. +-// As such it indicates that other definitions could be worth checking. +-var ErrNoEmbed = errors.New("no embed directive found") +- +-var errStopWalk = errors.New("stop walk") +- +-// EmbedDefinition finds a file matching the embed directive at pos in the mapped file. +-// If there is no embed directive at pos, returns ErrNoEmbed. +-// If multiple files match the embed pattern, one is picked at random. +-func EmbedDefinition(m *protocol.Mapper, pos protocol.Position) ([]protocol.Location, error) { +- pattern, _ := parseEmbedDirective(m, pos) +- if pattern == "" { +- return nil, ErrNoEmbed +- } +- +- // Find the first matching file. +- var match string +- dir := filepath.Dir(m.URI.Filename()) +- err := filepath.WalkDir(dir, func(abs string, d fs.DirEntry, e error) error { +- if e != nil { +- return e +- } +- rel, err := filepath.Rel(dir, abs) +- if err != nil { +- return err +- } +- ok, err := filepath.Match(pattern, rel) +- if err != nil { +- return err +- } +- if ok && !d.IsDir() { +- match = abs +- return errStopWalk +- } +- return nil +- }) +- if err != nil && !errors.Is(err, errStopWalk) { +- return nil, err +- } +- if match == "" { +- return nil, fmt.Errorf("%q does not match any files in %q", pattern, dir) +- } +- +- loc := protocol.Location{ +- URI: protocol.URIFromPath(match), +- Range: protocol.Range{ +- Start: protocol.Position{Line: 0, Character: 0}, +- }, +- } +- return []protocol.Location{loc}, nil +-} +- +-// parseEmbedDirective attempts to parse a go:embed directive argument at pos. +-// If successful it return the directive argument and its range, else zero values are returned. +-func parseEmbedDirective(m *protocol.Mapper, pos protocol.Position) (string, protocol.Range) { +- lineStart, err := m.PositionOffset(protocol.Position{Line: pos.Line, Character: 0}) +- if err != nil { +- return "", protocol.Range{} +- } +- lineEnd, err := m.PositionOffset(protocol.Position{Line: pos.Line + 1, Character: 0}) +- if err != nil { +- return "", protocol.Range{} +- } +- +- text := string(m.Content[lineStart:lineEnd]) +- if !strings.HasPrefix(text, "//go:embed") { +- return "", protocol.Range{} +- } +- text = text[len("//go:embed"):] +- offset := lineStart + len("//go:embed") +- +- // Find the first pattern in text that covers the offset of the pos we are looking for. +- findOffset, err := m.PositionOffset(pos) +- if err != nil { +- return "", protocol.Range{} +- } +- patterns, err := parseGoEmbed(text, offset) +- if err != nil { +- return "", protocol.Range{} +- } +- for _, p := range patterns { +- if p.startOffset <= findOffset && findOffset <= p.endOffset { +- // Found our match. +- rng, err := m.OffsetRange(p.startOffset, p.endOffset) +- if err != nil { +- return "", protocol.Range{} +- } +- return p.pattern, rng +- } +- } +- +- return "", protocol.Range{} +-} +- +-type fileEmbed struct { +- pattern string +- startOffset int +- endOffset int +-} +- +-// parseGoEmbed patterns that come after the directive. +-// +-// Copied and adapted from go/build/read.go. +-// Replaced token.Position with start/end offset (including quotes if present). +-func parseGoEmbed(args string, offset int) ([]fileEmbed, error) { +- trimBytes := func(n int) { +- offset += n +- args = args[n:] +- } +- trimSpace := func() { +- trim := strings.TrimLeftFunc(args, unicode.IsSpace) +- trimBytes(len(args) - len(trim)) +- } +- +- var list []fileEmbed +- for trimSpace(); args != ""; trimSpace() { +- var path string +- pathOffset := offset +- Switch: +- switch args[0] { +- default: +- i := len(args) +- for j, c := range args { +- if unicode.IsSpace(c) { +- i = j +- break +- } +- } +- path = args[:i] +- trimBytes(i) +- +- case '`': +- var ok bool +- path, _, ok = strings.Cut(args[1:], "`") +- if !ok { +- return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) +- } +- trimBytes(1 + len(path) + 1) +- +- case '"': +- i := 1 +- for ; i < len(args); i++ { +- if args[i] == '\\' { +- i++ +- continue +- } +- if args[i] == '"' { +- q, err := strconv.Unquote(args[:i+1]) +- if err != nil { +- return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1]) +- } +- path = q +- trimBytes(i + 1) +- break Switch +- } +- } +- if i >= len(args) { +- return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) +- } +- } +- +- if args != "" { +- r, _ := utf8.DecodeRuneInString(args) +- if !unicode.IsSpace(r) { +- return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) +- } +- } +- list = append(list, fileEmbed{ +- pattern: path, +- startOffset: pathOffset, +- endOffset: offset, +- }) +- } +- return list, nil +-} diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/extract.go --- a/gopls/internal/lsp/source/extract.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/extract.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1331 +0,0 @@ ++++ b/gopls/internal/lsp/source/extract.go 1970-01-01 08:00:00 +@@ -1,1352 +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. @@ -75039,12 +82894,12 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/ast/astutil" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/internal/analysisinternal" -- "golang.org/x/tools/internal/bug" -) - --func extractVariable(fset *token.FileSet, start, end token.Pos, src []byte, file *ast.File, _ *types.Package, info *types.Info) (*analysis.SuggestedFix, error) { +-func extractVariable(fset *token.FileSet, start, end token.Pos, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error) { - tokFile := fset.File(file.Pos()) - expr, path, ok, err := CanExtractVariable(start, end, file) - if !ok { @@ -75057,14 +82912,14 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext - // TODO: stricter rules for selectorExpr. - case *ast.BasicLit, *ast.CompositeLit, *ast.IndexExpr, *ast.SliceExpr, - *ast.UnaryExpr, *ast.BinaryExpr, *ast.SelectorExpr: -- lhsName, _ := generateAvailableIdentifier(expr.Pos(), file, path, info, "x", 0) +- lhsName, _ := generateAvailableIdentifier(expr.Pos(), file, path, pkg, info, "x", 0) - lhsNames = append(lhsNames, lhsName) - case *ast.CallExpr: - tup, ok := info.TypeOf(expr).(*types.Tuple) - if !ok { - // If the call expression only has one return value, we can treat it the - // same as our standard extract variable case. -- lhsName, _ := generateAvailableIdentifier(expr.Pos(), file, path, info, "x", 0) +- lhsName, _ := generateAvailableIdentifier(expr.Pos(), file, path, pkg, info, "x", 0) - lhsNames = append(lhsNames, lhsName) - break - } @@ -75072,7 +82927,7 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext - for i := 0; i < tup.Len(); i++ { - // Generate a unique variable for each return value. - var lhsName string -- lhsName, idx = generateAvailableIdentifier(expr.Pos(), file, path, info, "x", idx) +- lhsName, idx = generateAvailableIdentifier(expr.Pos(), file, path, pkg, info, "x", idx) - lhsNames = append(lhsNames, lhsName) - } - default: @@ -75153,7 +83008,7 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext -// formatting (i.e. the proper indentation). To do so, we observe the indentation on the -// line of code on which the insertion occurs. -func calculateIndentation(content []byte, tok *token.File, insertBeforeStmt ast.Node) (string, error) { -- line := tok.Line(insertBeforeStmt.Pos()) +- line := safetoken.Line(tok, insertBeforeStmt.Pos()) - lineOffset, stmtOffset, err := safetoken.Offsets(tok, tok.LineStart(line), insertBeforeStmt.Pos()) - if err != nil { - return "", err @@ -75163,10 +83018,16 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext - -// generateAvailableIdentifier adjusts the new function name until there are no collisions in scope. -// Possible collisions include other function and variable names. Returns the next index to check for prefix. --func generateAvailableIdentifier(pos token.Pos, file *ast.File, path []ast.Node, info *types.Info, prefix string, idx int) (string, int) { +-func generateAvailableIdentifier(pos token.Pos, file *ast.File, path []ast.Node, pkg *types.Package, info *types.Info, prefix string, idx int) (string, int) { - scopes := CollectScopes(info, path, pos) +- scopes = append(scopes, pkg.Scope()) - return generateIdentifier(idx, prefix, func(name string) bool { -- return file.Scope.Lookup(name) != nil || !isValidName(name, scopes) +- for _, scope := range scopes { +- if scope != nil && scope.Lookup(name) != nil { +- return true +- } +- } +- return false - }) -} - @@ -75182,19 +83043,6 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext - return name, idx + 1 -} - --// isValidName checks for variable collision in scope. --func isValidName(name string, scopes []*types.Scope) bool { -- for _, scope := range scopes { -- if scope == nil { -- continue -- } -- if scope.Lookup(name) != nil { -- return false -- } -- } -- return true --} -- -// returnVariable keeps track of the information we need to properly introduce a new variable -// that we will return in the extracted function. -type returnVariable struct { @@ -75391,6 +83239,8 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext - } - } - +- reorderParams(params, paramTypes) +- - // Find the function literal that encloses the selection. The enclosing function literal - // may not be the enclosing function declaration (i.e. 'outer'). For example, in the - // following block: @@ -75555,7 +83405,7 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext - funName = name - } else { - name = "newFunction" -- funName, _ = generateAvailableIdentifier(start, file, path, info, name, 0) +- funName, _ = generateAvailableIdentifier(start, file, path, pkg, info, name, 0) - } - extractedFunCall := generateFuncCall(hasNonNestedReturn, hasReturnValues, params, - append(returns, getNames(retVars)...), funName, sym, receiverName) @@ -75659,6 +83509,33 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext - }, nil -} - +-// isSelector reports if e is the selector expr , . +-func isSelector(e ast.Expr, x, sel string) bool { +- selectorExpr, ok := e.(*ast.SelectorExpr) +- if !ok { +- return false +- } +- ident, ok := selectorExpr.X.(*ast.Ident) +- if !ok { +- return false +- } +- return ident.Name == x && selectorExpr.Sel.Name == sel +-} +- +-// reorderParams reorders the given parameters in-place to follow common Go conventions. +-func reorderParams(params []ast.Expr, paramTypes []*ast.Field) { +- // Move Context parameter (if any) to front. +- for i, t := range paramTypes { +- if isSelector(t.Type, "context", "Context") { +- p, t := params[i], paramTypes[i] +- copy(params[1:], params[:i]) +- copy(paramTypes[1:], paramTypes[:i]) +- params[0], paramTypes[0] = p, t +- break +- } +- } +-} +- -// adjustRangeForCommentsAndWhiteSpace adjusts the given range to exclude unnecessary leading or -// trailing whitespace characters from selection as well as leading or trailing comments. -// In the following example, each line of the if statement is indented once. There are also two @@ -76158,7 +84035,7 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext - var cond *ast.Ident - if !hasNonNestedReturns { - // Generate information for the added bool value. -- name, _ := generateAvailableIdentifier(pos, file, path, info, "shouldReturn", 0) +- name, _ := generateAvailableIdentifier(pos, file, path, pkg, info, "shouldReturn", 0) - cond = &ast.Ident{Name: name} - retVars = append(retVars, &returnVariable{ - name: cond, @@ -76180,8 +84057,7 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext - return nil, nil, fmt.Errorf("nil AST expression") - } - var name string -- name, idx = generateAvailableIdentifier(pos, file, -- path, info, "returnValue", idx) +- name, idx = generateAvailableIdentifier(pos, file, path, pkg, info, "returnValue", idx) - retVars = append(retVars, &returnVariable{ - name: ast.NewIdent(name), - decl: &ast.Field{Type: expr}, @@ -76352,8 +84228,8 @@ diff -urN a/gopls/internal/lsp/source/extract.go b/gopls/internal/lsp/source/ext -} diff -urN a/gopls/internal/lsp/source/fix.go b/gopls/internal/lsp/source/fix.go --- a/gopls/internal/lsp/source/fix.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/fix.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,138 +0,0 @@ ++++ b/gopls/internal/lsp/source/fix.go 1970-01-01 08:00:00 +@@ -1,195 +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. @@ -76368,11 +84244,13 @@ diff -urN a/gopls/internal/lsp/source/fix.go b/gopls/internal/lsp/source/fix.go - "go/types" - - "golang.org/x/tools/go/analysis" +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/analysis/embeddirective" - "golang.org/x/tools/gopls/internal/lsp/analysis/fillstruct" - "golang.org/x/tools/gopls/internal/lsp/analysis/undeclaredname" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" +- "golang.org/x/tools/internal/imports" -) - -type ( @@ -76389,29 +84267,37 @@ diff -urN a/gopls/internal/lsp/source/fix.go b/gopls/internal/lsp/source/fix.go - singleFileFixFunc func(fset *token.FileSet, start, end token.Pos, src []byte, file *ast.File, pkg *types.Package, info *types.Info) (*analysis.SuggestedFix, error) -) - +-// These strings identify kinds of suggested fix, both in Analyzer.Fix +-// and in the ApplyFix subcommand (see ExecuteCommand and ApplyFixArgs.Fix). -const ( -- FillStruct = "fill_struct" -- StubMethods = "stub_methods" -- UndeclaredName = "undeclared_name" -- ExtractVariable = "extract_variable" -- ExtractFunction = "extract_function" -- ExtractMethod = "extract_method" +- FillStruct = "fill_struct" +- StubMethods = "stub_methods" +- UndeclaredName = "undeclared_name" +- ExtractVariable = "extract_variable" +- ExtractFunction = "extract_function" +- ExtractMethod = "extract_method" +- InlineCall = "inline_call" +- InvertIfCondition = "invert_if_condition" +- AddEmbedImport = "add_embed_import" -) - -// suggestedFixes maps a suggested fix command id to its handler. -var suggestedFixes = map[string]SuggestedFixFunc{ -- FillStruct: singleFile(fillstruct.SuggestedFix), -- UndeclaredName: singleFile(undeclaredname.SuggestedFix), -- ExtractVariable: singleFile(extractVariable), -- ExtractFunction: singleFile(extractFunction), -- ExtractMethod: singleFile(extractMethod), -- StubMethods: stubSuggestedFixFunc, +- FillStruct: singleFile(fillstruct.SuggestedFix), +- UndeclaredName: singleFile(undeclaredname.SuggestedFix), +- ExtractVariable: singleFile(extractVariable), +- InlineCall: inlineCall, +- ExtractFunction: singleFile(extractFunction), +- ExtractMethod: singleFile(extractMethod), +- InvertIfCondition: singleFile(invertIfCondition), +- StubMethods: stubSuggestedFixFunc, +- AddEmbedImport: addEmbedImport, -} - -// singleFile calls analyzers that expect inputs for a single file -func singleFile(sf singleFileFixFunc) SuggestedFixFunc { - return func(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) (*token.FileSet, *analysis.SuggestedFix, error) { -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, nil, err - } @@ -76456,7 +84342,7 @@ diff -urN a/gopls/internal/lsp/source/fix.go b/gopls/internal/lsp/source/fix.go - if !end.IsValid() { - end = edit.Pos - } -- fh, err := snapshot.GetFile(ctx, span.URIFromPath(tokFile.Name())) +- fh, err := snapshot.ReadFile(ctx, span.URIFromPath(tokFile.Name())) - if err != nil { - return nil, err - } @@ -76472,7 +84358,7 @@ diff -urN a/gopls/internal/lsp/source/fix.go b/gopls/internal/lsp/source/fix.go - } - editsPerFile[fh.URI()] = te - } -- content, err := fh.Read() +- content, err := fh.Content() - if err != nil { - return nil, err - } @@ -76492,10 +84378,57 @@ diff -urN a/gopls/internal/lsp/source/fix.go b/gopls/internal/lsp/source/fix.go - } - return edits, nil -} +- +-// fixedByImportingEmbed returns true if diag can be fixed by addEmbedImport. +-func fixedByImportingEmbed(diag *Diagnostic) bool { +- if diag == nil { +- return false +- } +- return diag.Message == embeddirective.MissingImportMessage +-} +- +-// addEmbedImport adds a missing embed "embed" import with blank name. +-func addEmbedImport(ctx context.Context, snapshot Snapshot, fh FileHandle, rng protocol.Range) (*token.FileSet, *analysis.SuggestedFix, error) { +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) +- if err != nil { +- return nil, nil, fmt.Errorf("narrow pkg: %w", err) +- } +- +- // Like source.AddImport, but with _ as Name and using our pgf. +- protoEdits, err := ComputeOneImportFixEdits(snapshot, pgf, &imports.ImportFix{ +- StmtInfo: imports.ImportInfo{ +- ImportPath: "embed", +- Name: "_", +- }, +- FixType: imports.AddImport, +- }) +- if err != nil { +- return nil, nil, fmt.Errorf("compute edits: %w", err) +- } +- +- var edits []analysis.TextEdit +- for _, e := range protoEdits { +- start, end, err := pgf.RangePos(e.Range) +- if err != nil { +- return nil, nil, fmt.Errorf("map range: %w", err) +- } +- edits = append(edits, analysis.TextEdit{ +- Pos: start, +- End: end, +- NewText: []byte(e.NewText), +- }) +- } +- +- fix := &analysis.SuggestedFix{ +- Message: "Add embed import", +- TextEdits: edits, +- } +- return pkg.FileSet(), fix, nil +-} diff -urN a/gopls/internal/lsp/source/folding_range.go b/gopls/internal/lsp/source/folding_range.go --- a/gopls/internal/lsp/source/folding_range.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/folding_range.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,193 +0,0 @@ ++++ b/gopls/internal/lsp/source/folding_range.go 1970-01-01 08:00:00 +@@ -1,194 +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. @@ -76509,8 +84442,9 @@ diff -urN a/gopls/internal/lsp/source/folding_range.go b/gopls/internal/lsp/sour - "sort" - "strings" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/safetoken" -) - -// FoldingRangeInfo holds range and kind info of folding for an ast.Node @@ -76622,7 +84556,7 @@ diff -urN a/gopls/internal/lsp/source/folding_range.go b/gopls/internal/lsp/sour - return nil - } - // in line folding mode, do not fold if the start and end lines are the same. -- if lineFoldingOnly && pgf.Tok.Line(start) == pgf.Tok.Line(end) { +- if lineFoldingOnly && safetoken.Line(pgf.Tok, start) == safetoken.Line(pgf.Tok, end) { - return nil - } - mrng, err := pgf.PosMappedRange(start, end) @@ -76647,8 +84581,8 @@ diff -urN a/gopls/internal/lsp/source/folding_range.go b/gopls/internal/lsp/sour - // as an example, the example below should *not* fold: - // var x = [2]string{"d", - // "e" } -- if tokFile.Line(open) == tokFile.Line(start) || -- tokFile.Line(close) == tokFile.Line(end) { +- if safetoken.Line(tokFile, open) == safetoken.Line(tokFile, start) || +- safetoken.Line(tokFile, close) == safetoken.Line(tokFile, end) { - return token.NoPos, token.NoPos - } - @@ -76663,7 +84597,7 @@ diff -urN a/gopls/internal/lsp/source/folding_range.go b/gopls/internal/lsp/sour -func commentsFoldingRange(pgf *ParsedGoFile) (comments []*FoldingRangeInfo) { - tokFile := pgf.Tok - for _, commentGrp := range pgf.File.Comments { -- startGrpLine, endGrpLine := tokFile.Line(commentGrp.Pos()), tokFile.Line(commentGrp.End()) +- startGrpLine, endGrpLine := safetoken.Line(tokFile, commentGrp.Pos()), safetoken.Line(tokFile, commentGrp.End()) - if startGrpLine == endGrpLine { - // Don't fold single line comments. - continue @@ -76671,7 +84605,7 @@ diff -urN a/gopls/internal/lsp/source/folding_range.go b/gopls/internal/lsp/sour - - firstComment := commentGrp.List[0] - startPos, endLinePos := firstComment.Pos(), firstComment.End() -- startCmmntLine, endCmmntLine := tokFile.Line(startPos), tokFile.Line(endLinePos) +- startCmmntLine, endCmmntLine := safetoken.Line(tokFile, startPos), safetoken.Line(tokFile, endLinePos) - if startCmmntLine != endCmmntLine { - // If the first comment spans multiple lines, then we want to have the - // folding range start at the end of the first line. @@ -76691,8 +84625,8 @@ diff -urN a/gopls/internal/lsp/source/folding_range.go b/gopls/internal/lsp/sour -} diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/format.go --- a/gopls/internal/lsp/source/format.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/format.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,391 +0,0 @@ ++++ b/gopls/internal/lsp/source/format.go 1970-01-01 08:00:00 +@@ -1,388 +0,0 @@ -// Copyright 2018 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. @@ -76716,6 +84650,7 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form - "golang.org/x/tools/internal/diff" - "golang.org/x/tools/internal/event" - "golang.org/x/tools/internal/imports" +- "golang.org/x/tools/internal/tokeninternal" -) - -// Format formats a file with a given range. @@ -76748,7 +84683,7 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form - // This should be acceptable for all users, who likely be prompted to rebuild - // the LSP server on each Go release. - buf := &bytes.Buffer{} -- fset := FileSetFor(pgf.Tok) +- fset := tokeninternal.FileSetFor(pgf.Tok) - if err := format.Node(buf, fset, pgf.File); err != nil { - return nil, err - } @@ -76756,7 +84691,7 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form - - // Apply additional formatting, if any is supported. Currently, the only - // supported additional formatter is gofumpt. -- if format := snapshot.View().Options().GofumptFormat; snapshot.View().Options().Gofumpt && format != nil { +- if format := snapshot.Options().GofumptFormat; snapshot.Options().Gofumpt && format != nil { - // gofumpt can customize formatting based on language version and module - // path, if available. - // @@ -76766,9 +84701,9 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form - // Can this, for example, result in inconsistent formatting across saves, - // due to pending calls to packages.Load? - var langVersion, modulePath string -- mds, err := snapshot.MetadataForFile(ctx, fh.URI()) -- if err == nil && len(mds) > 0 { -- if mi := mds[0].Module; mi != nil { +- meta, err := NarrowestMetadataForFile(ctx, snapshot, fh.URI()) +- if err == nil { +- if mi := meta.Module; mi != nil { - langVersion = mi.GoVersion - modulePath = mi.Path - } @@ -76786,7 +84721,7 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form - _, done := event.Start(ctx, "source.formatSource") - defer done() - -- data, err := fh.Read() +- data, err := fh.Content() - if err != nil { - return nil, err - } @@ -76802,16 +84737,12 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form -// In addition to returning the result of applying all edits, -// it returns a list of fixes that could be applied to the file, with the -// corresponding TextEdits that would be needed to apply that fix. --func AllImportsFixes(ctx context.Context, snapshot Snapshot, fh FileHandle) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) { +-func AllImportsFixes(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) { - ctx, done := event.Start(ctx, "source.AllImportsFixes") - defer done() - -- pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) -- if err != nil { -- return nil, nil, err -- } -- if err := snapshot.RunProcessEnvFunc(ctx, func(opts *imports.Options) error { -- allFixEdits, editsPerFix, err = computeImportEdits(snapshot, pgf, opts) +- if err := snapshot.RunProcessEnvFunc(ctx, func(ctx context.Context, opts *imports.Options) error { +- allFixEdits, editsPerFix, err = computeImportEdits(ctx, snapshot, pgf, opts) - return err - }); err != nil { - return nil, nil, fmt.Errorf("AllImportsFixes: %v", err) @@ -76821,11 +84752,11 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form - -// computeImportEdits computes a set of edits that perform one or all of the -// necessary import fixes. --func computeImportEdits(snapshot Snapshot, pgf *ParsedGoFile, options *imports.Options) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) { +-func computeImportEdits(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile, options *imports.Options) (allFixEdits []protocol.TextEdit, editsPerFix []*ImportFix, err error) { - filename := pgf.URI.Filename() - - // Build up basic information about the original file. -- allFixes, err := imports.FixImports(filename, pgf.Src, options) +- allFixes, err := imports.FixImports(ctx, filename, pgf.Src, options) - if err != nil { - return nil, nil, err - } @@ -76853,7 +84784,7 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form -// ComputeOneImportFixEdits returns text edits for a single import fix. -func ComputeOneImportFixEdits(snapshot Snapshot, pgf *ParsedGoFile, fix *imports.ImportFix) ([]protocol.TextEdit, error) { - options := &imports.Options{ -- LocalPrefix: snapshot.View().Options().Local, +- LocalPrefix: snapshot.Options().Local, - // Defaults. - AllErrors: true, - Comments: true, @@ -76892,7 +84823,7 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form - if fixedData == nil || fixedData[len(fixedData)-1] != '\n' { - fixedData = append(fixedData, '\n') // ApplyFixes may miss the newline, go figure. - } -- edits := snapshot.View().Options().ComputeEdits(left, string(fixedData)) +- edits := snapshot.Options().ComputeEdits(left, string(fixedData)) - return protocolEditsFromSource([]byte(left), edits) -} - @@ -76932,7 +84863,7 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form - // specifically, in the text of a comment, it will strip out \r\n line - // endings in favor of \n. To account for these differences, we try to - // return a position on the next line whenever possible. -- switch line := tok.Line(tok.Pos(offset)); { +- switch line := safetoken.Line(tok, tok.Pos(offset)); { - case line < tok.LineCount(): - nextLineOffset, err := safetoken.Offset(tok, tok.LineStart(line+1)) - if err != nil { @@ -77002,7 +84933,7 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form - _, done := event.Start(ctx, "source.computeTextEdits") - defer done() - -- edits := snapshot.View().Options().ComputeEdits(string(pgf.Src), formatted) +- edits := snapshot.Options().ComputeEdits(string(pgf.Src), formatted) - return ToProtocolEdits(pgf.Mapper, edits) -} - @@ -77030,7 +84961,7 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form - return result, nil -} - --// ToProtocolEdits converts diff.Edits to LSP TextEdits. +-// ToProtocolEdits converts diff.Edits to a non-nil slice of LSP TextEdits. -// See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEditArray -func ToProtocolEdits(m *protocol.Mapper, edits []diff.Edit) ([]protocol.TextEdit, error) { - // LSP doesn't require TextEditArray to be sorted: @@ -77086,7 +85017,7 @@ diff -urN a/gopls/internal/lsp/source/format.go b/gopls/internal/lsp/source/form -} diff -urN a/gopls/internal/lsp/source/format_test.go b/gopls/internal/lsp/source/format_test.go --- a/gopls/internal/lsp/source/format_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/format_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/format_test.go 1970-01-01 08:00:00 @@ -1,75 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -77165,7 +85096,7 @@ diff -urN a/gopls/internal/lsp/source/format_test.go b/gopls/internal/lsp/source -} diff -urN a/gopls/internal/lsp/source/gc_annotations.go b/gopls/internal/lsp/source/gc_annotations.go --- a/gopls/internal/lsp/source/gc_annotations.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/gc_annotations.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/gc_annotations.go 1970-01-01 08:00:00 @@ -1,221 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -77178,7 +85109,6 @@ diff -urN a/gopls/internal/lsp/source/gc_annotations.go b/gopls/internal/lsp/sou - "context" - "encoding/json" - "fmt" -- "io/ioutil" - "os" - "path/filepath" - "strings" @@ -77214,7 +85144,7 @@ diff -urN a/gopls/internal/lsp/source/gc_annotations.go b/gopls/internal/lsp/sou - if err := os.MkdirAll(outDir, 0700); err != nil { - return nil, err - } -- tmpFile, err := ioutil.TempFile(os.TempDir(), "gopls-x") +- tmpFile, err := os.CreateTemp(os.TempDir(), "gopls-x") - if err != nil { - return nil, err - } @@ -77244,7 +85174,7 @@ diff -urN a/gopls/internal/lsp/source/gc_annotations.go b/gopls/internal/lsp/sou - return nil, err - } - reports := make(map[span.URI][]*Diagnostic) -- opts := snapshot.View().Options() +- opts := snapshot.Options() - var parseError error - for _, fn := range files { - uri, diagnostics, err := parseDetailsFile(fn, opts) @@ -77268,7 +85198,7 @@ diff -urN a/gopls/internal/lsp/source/gc_annotations.go b/gopls/internal/lsp/sou -} - -func parseDetailsFile(filename string, options *Options) (span.URI, []*Diagnostic, error) { -- buf, err := ioutil.ReadFile(filename) +- buf, err := os.ReadFile(filename) - if err != nil { - return "", nil, err - } @@ -77298,6 +85228,7 @@ diff -urN a/gopls/internal/lsp/source/gc_annotations.go b/gopls/internal/lsp/sou - if err := dec.Decode(d); err != nil { - return "", nil, err - } +- d.Tags = []protocol.DiagnosticTag{} // must be an actual slice - msg := d.Code.(string) - if msg != "" { - msg = fmt.Sprintf("%s(%s)", msg, d.Message) @@ -77390,8 +85321,8 @@ diff -urN a/gopls/internal/lsp/source/gc_annotations.go b/gopls/internal/lsp/sou -} diff -urN a/gopls/internal/lsp/source/highlight.go b/gopls/internal/lsp/source/highlight.go --- a/gopls/internal/lsp/source/highlight.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/highlight.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,484 +0,0 @@ ++++ b/gopls/internal/lsp/source/highlight.go 1970-01-01 08:00:00 +@@ -1,480 +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. @@ -77404,7 +85335,6 @@ diff -urN a/gopls/internal/lsp/source/highlight.go b/gopls/internal/lsp/source/h - "go/ast" - "go/token" - "go/types" -- "strings" - - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/gopls/internal/lsp/protocol" @@ -77417,7 +85347,7 @@ diff -urN a/gopls/internal/lsp/source/highlight.go b/gopls/internal/lsp/source/h - - // We always want fully parsed files for highlight, regardless - // of whether the file belongs to a workspace package. -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, fmt.Errorf("getting package for Highlight: %w", err) - } @@ -77461,10 +85391,27 @@ diff -urN a/gopls/internal/lsp/source/highlight.go b/gopls/internal/lsp/source/h - result := make(map[posRange]struct{}) - switch node := path[0].(type) { - case *ast.BasicLit: +- // Import path string literal? - if len(path) > 1 { -- if _, ok := path[1].(*ast.ImportSpec); ok { -- err := highlightImportUses(path, info, result) -- return result, err +- if imp, ok := path[1].(*ast.ImportSpec); ok { +- highlight := func(n ast.Node) { +- result[posRange{start: n.Pos(), end: n.End()}] = struct{}{} +- } +- +- // Highlight the import itself... +- highlight(imp) +- +- // ...and all references to it in the file. +- if pkgname, ok := ImportedPkgName(info, imp); ok { +- ast.Inspect(file, func(n ast.Node) bool { +- if id, ok := n.(*ast.Ident); ok && +- info.Uses[id] == pkgname { +- highlight(id) +- } +- return true +- }) +- } +- return result, nil - } - } - highlightFuncControlFlow(path, result) @@ -77813,73 +85760,53 @@ diff -urN a/gopls/internal/lsp/source/highlight.go b/gopls/internal/lsp/source/h - }) -} - --func highlightImportUses(path []ast.Node, info *types.Info, result map[posRange]struct{}) error { -- basicLit, ok := path[0].(*ast.BasicLit) -- if !ok { -- return fmt.Errorf("highlightImportUses called with an ast.Node of type %T", basicLit) -- } -- ast.Inspect(path[len(path)-1], func(node ast.Node) bool { -- if imp, ok := node.(*ast.ImportSpec); ok && imp.Path == basicLit { -- result[posRange{start: node.Pos(), end: node.End()}] = struct{}{} -- return false -- } -- n, ok := node.(*ast.Ident) -- if !ok { -- return true -- } -- obj, ok := info.ObjectOf(n).(*types.PkgName) -- if !ok { -- return true -- } -- if !strings.Contains(basicLit.Value, obj.Name()) { -- return true -- } +-func highlightIdentifier(id *ast.Ident, file *ast.File, info *types.Info, result map[posRange]struct{}) { +- highlight := func(n ast.Node) { - result[posRange{start: n.Pos(), end: n.End()}] = struct{}{} -- return false -- }) -- return nil --} +- } - --func highlightIdentifier(id *ast.Ident, file *ast.File, info *types.Info, result map[posRange]struct{}) { -- // TODO(rfindley): idObj may be nil. Note that returning early in this case -- // causes tests to fail (because the nObj == idObj check below was succeeded -- // for nil == nil!) -- // -- // Revisit this. If ObjectOf is nil, there are type errors, and it seems -- // reasonable for identifier highlighting not to work. -- idObj := info.ObjectOf(id) -- pkgObj, isImported := idObj.(*types.PkgName) -- ast.Inspect(file, func(node ast.Node) bool { -- if imp, ok := node.(*ast.ImportSpec); ok && isImported { -- highlightImport(pkgObj, imp, result) -- } -- n, ok := node.(*ast.Ident) -- if !ok { -- return true -- } -- if n.Name != id.Name { -- return false -- } -- if nObj := info.ObjectOf(n); nObj == idObj { -- result[posRange{start: n.Pos(), end: n.End()}] = struct{}{} +- // obj may be nil if the Ident is undefined. +- // In this case, the behavior expected by tests is +- // to match other undefined Idents of the same name. +- obj := info.ObjectOf(id) +- +- ast.Inspect(file, func(n ast.Node) bool { +- switch n := n.(type) { +- case *ast.Ident: +- if n.Name == id.Name && info.ObjectOf(n) == obj { +- highlight(n) +- } +- +- case *ast.ImportSpec: +- pkgname, ok := ImportedPkgName(info, n) +- if ok && pkgname == obj { +- if n.Name != nil { +- highlight(n.Name) +- } else { +- highlight(n) +- } +- } - } -- return false +- return true - }) -} - --func highlightImport(obj *types.PkgName, imp *ast.ImportSpec, result map[posRange]struct{}) { -- if imp.Name != nil || imp.Path == nil { -- return -- } -- if !strings.Contains(imp.Path.Value, obj.Name()) { -- return +-// ImportedPkgName returns the PkgName object declared by an ImportSpec. +-// TODO(adonovan): make this a method of types.Info. +-func ImportedPkgName(info *types.Info, imp *ast.ImportSpec) (*types.PkgName, bool) { +- var obj types.Object +- if imp.Name != nil { +- obj = info.Defs[imp.Name] +- } else { +- obj = info.Implicits[imp] - } -- result[posRange{start: imp.Path.Pos(), end: imp.Path.End()}] = struct{}{} +- pkgname, ok := obj.(*types.PkgName) +- return pkgname, ok -} diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover.go --- a/gopls/internal/lsp/source/hover.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/hover.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,951 +0,0 @@ ++++ b/gopls/internal/lsp/source/hover.go 1970-01-01 08:00:00 +@@ -1,1069 +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. @@ -77896,6 +85823,8 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - "go/format" - "go/token" - "go/types" +- "io/fs" +- "path/filepath" - "strconv" - "strings" - "time" @@ -77904,11 +85833,12 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - "golang.org/x/text/unicode/runenames" - "golang.org/x/tools/go/ast/astutil" - "golang.org/x/tools/go/types/typeutil" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/tokeninternal" - "golang.org/x/tools/internal/typeparams" -) - @@ -77952,13 +85882,13 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - if h == nil { - return nil, nil - } -- hover, err := formatHover(h, snapshot.View().Options()) +- hover, err := formatHover(h, snapshot.Options()) - if err != nil { - return nil, err - } - return &protocol.Hover{ - Contents: protocol.MarkupContent{ -- Kind: snapshot.View().Options().PreferredContentFormat, +- Kind: snapshot.Options().PreferredContentFormat, - Value: hover, - }, - Range: rng, @@ -77969,7 +85899,7 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover -// hovering at the position, it returns _, nil, nil: an error is only returned -// if the position is valid but we fail to compute hover information. -func hover(ctx context.Context, snapshot Snapshot, fh FileHandle, pp protocol.Position) (protocol.Range, *HoverJSON, error) { -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return protocol.Range{}, nil, err - } @@ -78002,6 +85932,28 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - } - } - +- // Handle hovering over embed directive argument. +- pattern, embedRng := parseEmbedDirective(pgf.Mapper, pp) +- if pattern != "" { +- return hoverEmbed(fh, embedRng, pattern) +- } +- +- // Handle linkname directive by overriding what to look for. +- var linkedRange *protocol.Range // range referenced by linkname directive, or nil +- if pkgPath, name, offset := parseLinkname(ctx, snapshot, fh, pp); pkgPath != "" && name != "" { +- // rng covering 2nd linkname argument: pkgPath.name. +- rng, err := pgf.PosRange(pgf.Tok.Pos(offset), pgf.Tok.Pos(offset+len(pkgPath)+len(".")+len(name))) +- if err != nil { +- return protocol.Range{}, nil, fmt.Errorf("range over linkname arg: %w", err) +- } +- linkedRange = &rng +- +- pkg, pgf, pos, err = findLinkname(ctx, snapshot, PackagePath(pkgPath), name) +- if err != nil { +- return protocol.Range{}, nil, fmt.Errorf("find linkname: %w", err) +- } +- } +- - // The general case: compute hover information for the object referenced by - // the identifier at pos. - ident, obj, selectedType := referencedObject(pkg, pgf, pos) @@ -78009,9 +85961,15 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - return protocol.Range{}, nil, nil // no object to hover - } - -- rng, err := pgf.NodeRange(ident) -- if err != nil { -- return protocol.Range{}, nil, err +- // Unless otherwise specified, rng covers the ident being hovered. +- var rng protocol.Range +- if linkedRange != nil { +- rng = *linkedRange +- } else { +- rng, err = pgf.NodeRange(ident) +- if err != nil { +- return protocol.Range{}, nil, err +- } - } - - // By convention, we qualify hover information relative to the package @@ -78024,7 +85982,7 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - // There's not much useful information to provide. - if selectedType != nil { - fakeObj := types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), selectedType) -- signature := objectString(fakeObj, qf, nil) +- signature := types.ObjectString(fakeObj, qf) - return rng, &HoverJSON{ - Signature: signature, - SingleLine: signature, @@ -78049,10 +86007,14 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - docText := comment.Text() - - // By default, types.ObjectString provides a reasonable signature. -- signature := objectString(obj, qf, nil) +- signature := objectString(obj, qf, declPos, declPGF.Tok, spec) +- singleLineSignature := signature +- - // TODO(rfindley): we could do much better for inferred signatures. - if inferred := inferredSignature(pkg.GetTypesInfo(), ident); inferred != nil { -- signature = objectString(obj, qf, inferred) +- if s := inferredSignatureString(obj, qf, inferred); s != "" { +- signature = s +- } - } - - // For "objects defined by a type spec", the signature produced by @@ -78077,7 +86039,7 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - spec2.Comment = nil - var b strings.Builder - b.WriteString("type ") -- fset := FileSetFor(declPGF.Tok) +- fset := tokeninternal.FileSetFor(declPGF.Tok) - if err := format.Node(&b, fset, &spec2); err != nil { - return protocol.Range{}, nil, err - } @@ -78095,7 +86057,7 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - if (m.Obj().Exported() || m.Obj().Pkg() == pkg.GetTypes()) && len(m.Index()) == 1 { - b.WriteString(sep) - sep = "\n" -- b.WriteString(objectString(m.Obj(), qf, nil)) +- b.WriteString(types.ObjectString(m.Obj(), qf)) - } - } - } @@ -78202,7 +86164,7 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - return rng, &HoverJSON{ - Synopsis: doc.Synopsis(docText), - FullDocumentation: docText, -- SingleLine: objectString(obj, qf, nil), +- SingleLine: singleLineSignature, - SymbolName: linkName, - Signature: signature, - LinkPath: linkPath, @@ -78219,8 +86181,6 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - return nil, err - } - -- // TODO(rfindley): add a test for jump to definition of error.Error (which is -- // probably failing, considering it lacks special handling). - if obj.Name() == "Error" { - signature := obj.String() - return &HoverJSON{ @@ -78306,7 +86266,7 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - // Find the first file with a package doc comment. - var comment *ast.CommentGroup - for _, f := range impMetadata.CompiledGoFiles { -- fh, err := snapshot.GetFile(ctx, f) +- fh, err := snapshot.ReadFile(ctx, f) - if err != nil { - if ctx.Err() != nil { - return protocol.Range{}, nil, ctx.Err() @@ -78364,8 +86324,11 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover -// -// '∑', U+2211, N-ARY SUMMATION -func hoverLit(pgf *ParsedGoFile, lit *ast.BasicLit, pos token.Pos) (protocol.Range, *HoverJSON, error) { -- var r rune -- var start, end token.Pos +- var ( +- value string // if non-empty, a constant value to format in hover +- r rune // if non-zero, format a description of this rune in hover +- start, end token.Pos // hover span +- ) - // Extract a rune from the current position. - // 'Ω', "...Ω...", or 0x03A9 => 'Ω', U+03A9, GREEK CAPITAL LETTER OMEGA - switch lit.Kind { @@ -78381,23 +86344,34 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - return protocol.Range{}, nil, fmt.Errorf("rune error") - } - start, end = lit.Pos(), lit.End() -- case token.INT: -- // TODO(rfindley): add support for hex/octal/binary->int conversion here. - -- // It's an integer, scan only if it is a hex literal whose bitsize in -- // ranging from 8 to 32. -- if !(strings.HasPrefix(lit.Value, "0x") && len(lit.Value[2:]) >= 2 && len(lit.Value[2:]) <= 8) { +- case token.INT: +- // Short literals (e.g. 99 decimal, 07 octal) are uninteresting. +- if len(lit.Value) < 3 { - return protocol.Range{}, nil, nil - } -- v, err := strconv.ParseUint(lit.Value[2:], 16, 32) -- if err != nil { -- return protocol.Range{}, nil, fmt.Errorf("parsing int: %v", err) +- +- v := constant.MakeFromLiteral(lit.Value, lit.Kind, 0) +- if v.Kind() != constant.Int { +- return protocol.Range{}, nil, nil - } -- r = rune(v) -- if r == utf8.RuneError { -- return protocol.Range{}, nil, fmt.Errorf("rune error") +- +- switch lit.Value[:2] { +- case "0x", "0X": +- // As a special case, try to recognize hexadecimal literals as runes if +- // they are within the range of valid unicode values. +- if v, ok := constant.Int64Val(v); ok && v > 0 && v <= utf8.MaxRune && utf8.ValidRune(rune(v)) { +- r = rune(v) +- } +- fallthrough +- case "0o", "0O", "0b", "0B": +- // Format the decimal value of non-decimal literals. +- value = v.ExactString() +- start, end = lit.Pos(), lit.End() +- default: +- return protocol.Range{}, nil, nil - } -- start, end = lit.Pos(), lit.End() +- - case token.STRING: - // It's a string, scan only if it contains a unicode escape sequence under or before the - // current cursor position. @@ -78432,38 +86406,88 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - } - } - } -- if r == 0 { +- +- if value == "" && r == 0 { // nothing to format - return protocol.Range{}, nil, nil - } +- - rng, err := pgf.PosRange(start, end) - if err != nil { - return protocol.Range{}, nil, err - } - -- var desc string -- runeName := runenames.Name(r) -- if len(runeName) > 0 && runeName[0] == '<' { -- // Check if the rune looks like an HTML tag. If so, trim the surrounding <> -- // characters to work around https://github.com/microsoft/vscode/issues/124042. -- runeName = strings.TrimRight(runeName[1:], ">") +- var b strings.Builder +- if value != "" { +- b.WriteString(value) - } -- if strconv.IsPrint(r) { -- desc = fmt.Sprintf("'%s', U+%04X, %s", string(r), uint32(r), runeName) -- } else { -- desc = fmt.Sprintf("U+%04X, %s", uint32(r), runeName) +- if r != 0 { +- runeName := runenames.Name(r) +- if len(runeName) > 0 && runeName[0] == '<' { +- // Check if the rune looks like an HTML tag. If so, trim the surrounding <> +- // characters to work around https://github.com/microsoft/vscode/issues/124042. +- runeName = strings.TrimRight(runeName[1:], ">") +- } +- if b.Len() > 0 { +- b.WriteString(", ") +- } +- if strconv.IsPrint(r) { +- fmt.Fprintf(&b, "'%c', ", r) +- } +- fmt.Fprintf(&b, "U+%04X, %s", r, runeName) - } +- hover := b.String() - return rng, &HoverJSON{ -- Synopsis: desc, -- FullDocumentation: desc, +- Synopsis: hover, +- FullDocumentation: hover, - }, nil -} - --// objectString is a wrapper around the types.ObjectString function. --// It handles adding more information to the object string. --// --// TODO(rfindley): this function does too much. We should lift the special --// handling to callsites. --func objectString(obj types.Object, qf types.Qualifier, inferred *types.Signature) string { +-// hoverEmbed computes hover information for a filepath.Match pattern. +-// Assumes that the pattern is relative to the location of fh. +-func hoverEmbed(fh FileHandle, rng protocol.Range, pattern string) (protocol.Range, *HoverJSON, error) { +- s := &strings.Builder{} +- +- dir := filepath.Dir(fh.URI().Filename()) +- var matches []string +- err := filepath.WalkDir(dir, func(abs string, d fs.DirEntry, e error) error { +- if e != nil { +- return e +- } +- rel, err := filepath.Rel(dir, abs) +- if err != nil { +- return err +- } +- ok, err := filepath.Match(pattern, rel) +- if err != nil { +- return err +- } +- if ok && !d.IsDir() { +- matches = append(matches, rel) +- } +- return nil +- }) +- if err != nil { +- return protocol.Range{}, nil, err +- } +- +- for _, m := range matches { +- // TODO: Renders each file as separate markdown paragraphs. +- // If forcing (a single) newline is possible it might be more clear. +- fmt.Fprintf(s, "%s\n\n", m) +- } +- +- json := &HoverJSON{ +- Signature: fmt.Sprintf("Embedding %q", pattern), +- Synopsis: s.String(), +- FullDocumentation: s.String(), +- } +- return rng, json, nil +-} +- +-// inferredSignatureString is a wrapper around the types.ObjectString function +-// that adds more information to inferred signatures. It will return an empty string +-// if the passed types.Object is not a signature. +-func inferredSignatureString(obj types.Object, qf types.Qualifier, inferred *types.Signature) string { - // If the signature type was inferred, prefer the inferred signature with a - // comment showing the generic signature. - if sig, _ := obj.Type().(*types.Signature); sig != nil && typeparams.ForSignature(sig).Len() > 0 && inferred != nil { @@ -78478,22 +86502,65 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - str += "// " + types.TypeString(sig, qf) - return str - } +- return "" +-} +- +-// objectString is a wrapper around the types.ObjectString function. +-// It handles adding more information to the object string. +-// If spec is non-nil, it may be used to format additional declaration +-// syntax, and file must be the token.File describing its positions. +-func objectString(obj types.Object, qf types.Qualifier, declPos token.Pos, file *token.File, spec ast.Spec) string { - str := types.ObjectString(obj, qf) +- - switch obj := obj.(type) { - case *types.Const: -- str = fmt.Sprintf("%s = %s", str, obj.Val()) +- var ( +- declaration = obj.Val().String() // default formatted declaration +- comment = "" // if non-empty, a clarifying comment +- ) - -- // Try to add a formatted duration as an inline comment -- typ, ok := obj.Type().(*types.Named) -- if !ok { -- break +- // Try to use the original declaration. +- switch obj.Val().Kind() { +- case constant.String: +- // Usually the original declaration of a string doesn't carry much information. +- // Also strings can be very long. So, just use the constant's value. +- +- default: +- if spec, _ := spec.(*ast.ValueSpec); spec != nil { +- for i, name := range spec.Names { +- if declPos == name.Pos() { +- if i < len(spec.Values) { +- originalDeclaration := FormatNodeFile(file, spec.Values[i]) +- if originalDeclaration != declaration { +- comment = declaration +- declaration = originalDeclaration +- } +- } +- break +- } +- } +- } - } -- pkg := typ.Obj().Pkg() -- if pkg.Path() == "time" && typ.Obj().Name() == "Duration" { -- if d, ok := constant.Int64Val(obj.Val()); ok { -- str += " // " + time.Duration(d).String() +- +- // Special formatting cases. +- switch typ := obj.Type().(type) { +- case *types.Named: +- // Try to add a formatted duration as an inline comment. +- pkg := typ.Obj().Pkg() +- if pkg.Path() == "time" && typ.Obj().Name() == "Duration" { +- if d, ok := constant.Int64Val(obj.Val()); ok { +- comment = time.Duration(d).String() +- } - } - } +- if comment == declaration { +- comment = "" +- } +- +- str += " = " + declaration +- if comment != "" { +- str += " // " + comment +- } - } - return str -} @@ -78566,7 +86633,7 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - } - - uri := span.URIFromPath(f.Name()) -- fh, err := snapshot.GetFile(ctx, uri) +- fh, err := snapshot.ReadFile(ctx, uri) - if err != nil { - return nil, 0, err - } @@ -78589,28 +86656,6 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover - return pgf, fullPos, nil -} - --// extractFieldList recursively tries to extract a field list. --// If it is not found, nil is returned. --func extractFieldList(specType ast.Expr) *ast.FieldList { -- switch t := specType.(type) { -- case *ast.StructType: -- return t.Fields -- case *ast.InterfaceType: -- return t.Methods -- case *ast.ArrayType: -- return extractFieldList(t.Elt) -- case *ast.MapType: -- // Map value has a greater chance to be a struct -- if fields := extractFieldList(t.Value); fields != nil { -- return fields -- } -- return extractFieldList(t.Key) -- case *ast.ChanType: -- return extractFieldList(t.Value) -- } -- return nil --} -- -func formatHover(h *HoverJSON, options *Options) (string, error) { - signature := formatSignature(h, options) - @@ -78833,7 +86878,7 @@ diff -urN a/gopls/internal/lsp/source/hover.go b/gopls/internal/lsp/source/hover -} diff -urN a/gopls/internal/lsp/source/identifier.go b/gopls/internal/lsp/source/identifier.go --- a/gopls/internal/lsp/source/identifier.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/identifier.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/identifier.go 1970-01-01 08:00:00 @@ -1,174 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -78893,7 +86938,7 @@ diff -urN a/gopls/internal/lsp/source/identifier.go b/gopls/internal/lsp/source/ - -// typeToObject returns the relevant type name for the given type, after -// unwrapping pointers, arrays, slices, channels, and function signatures with --// a single non-error result. +-// a single non-error result, and ignoring built-in named types. -func typeToObject(typ types.Type) *types.TypeName { - switch typ := typ.(type) { - case *types.Named: @@ -78916,7 +86961,7 @@ diff -urN a/gopls/internal/lsp/source/identifier.go b/gopls/internal/lsp/source/ - for i := 0; i < results.Len(); i++ { - obj := typeToObject(results.At(i).Type()) - if obj == nil || hasErrorType(obj) { -- // Skip builtins. +- // Skip builtins. TODO(rfindley): should comparable be handled here as well? - continue - } - if res != nil { @@ -79011,7 +87056,7 @@ diff -urN a/gopls/internal/lsp/source/identifier.go b/gopls/internal/lsp/source/ -} diff -urN a/gopls/internal/lsp/source/identifier_test.go b/gopls/internal/lsp/source/identifier_test.go --- a/gopls/internal/lsp/source/identifier_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/identifier_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/identifier_test.go 1970-01-01 08:00:00 @@ -1,103 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -79118,8 +87163,8 @@ diff -urN a/gopls/internal/lsp/source/identifier_test.go b/gopls/internal/lsp/so -} diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/source/implementation.go --- a/gopls/internal/lsp/source/implementation.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/implementation.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,482 +0,0 @@ ++++ b/gopls/internal/lsp/source/implementation.go 1970-01-01 08:00:00 +@@ -1,495 +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. @@ -79139,6 +87184,7 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - "sync" - - "golang.org/x/sync/errgroup" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/lsp/source/methodsets" @@ -79152,7 +87198,6 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou -// -// TODO(adonovan): -// - Audit to ensure robustness in face of type errors. --// - Support 'error' and 'error.Error', which were also lacking from the old implementation. -// - Eliminate false positives due to 'tricky' cases of the global algorithm. -// - Ensure we have test coverage of: -// type aliases @@ -79172,7 +87217,7 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - ctx, done := event.Start(ctx, "source.Implementation") - defer done() - -- locs, err := implementations2(ctx, snapshot, f, pp) +- locs, err := implementations(ctx, snapshot, f, pp) - if err != nil { - return nil, err - } @@ -79192,60 +87237,37 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - return locs, nil -} - --func implementations2(ctx context.Context, snapshot Snapshot, fh FileHandle, pp protocol.Position) ([]protocol.Location, error) { -- -- // Type-check the query package, find the query identifier, -- // and locate the type or method declaration it refers to. -- declPosn, err := typeDeclPosition(ctx, snapshot, fh.URI(), pp) -- if err != nil { -- return nil, err -- } -- -- // Type-check the declaring package (incl. variants) for use -- // by the "local" search, which uses type information to -- // enumerate all types within the package that satisfy the -- // query type, even those defined local to a function. -- declURI := span.URIFromPath(declPosn.Filename) -- declMetas, err := snapshot.MetadataForFile(ctx, declURI) +-func implementations(ctx context.Context, snapshot Snapshot, fh FileHandle, pp protocol.Position) ([]protocol.Location, error) { +- obj, pkg, err := implementsObj(ctx, snapshot, fh.URI(), pp) - if err != nil { - return nil, err - } -- if len(declMetas) == 0 { -- return nil, fmt.Errorf("no packages for file %s", declURI) -- } -- ids := make([]PackageID, len(declMetas)) -- for i, m := range declMetas { -- ids[i] = m.ID -- } -- localPkgs, err := snapshot.TypeCheck(ctx, ids...) -- if err != nil { -- return nil, err -- } -- // The narrowest package will do, since the local search is based -- // on position and the global search is based on fingerprint. -- // (Neither is based on object identity.) -- declPkg := localPkgs[0] -- declFile, err := declPkg.File(declURI) -- if err != nil { -- return nil, err // "can't happen" -- } - -- // Find declaration of corresponding object -- // in this package based on (URI, offset). -- pos, err := safetoken.Pos(declFile.Tok, declPosn.Offset) -- if err != nil { -- return nil, err -- } -- // TODO(adonovan): simplify: use objectsAt? -- path := pathEnclosingObjNode(declFile.File, pos) -- if path == nil { -- return nil, ErrNoIdentFound // checked earlier -- } -- id, ok := path[0].(*ast.Ident) -- if !ok { -- return nil, ErrNoIdentFound // checked earlier +- var localPkgs []Package +- if obj.Pos().IsValid() { // no local package for error or error.Error +- declPosn := safetoken.StartPosition(pkg.FileSet(), obj.Pos()) +- // Type-check the declaring package (incl. variants) for use +- // by the "local" search, which uses type information to +- // enumerate all types within the package that satisfy the +- // query type, even those defined local to a function. +- declURI := span.URIFromPath(declPosn.Filename) +- declMetas, err := snapshot.MetadataForFile(ctx, declURI) +- if err != nil { +- return nil, err +- } +- RemoveIntermediateTestVariants(&declMetas) +- if len(declMetas) == 0 { +- return nil, fmt.Errorf("no packages for file %s", declURI) +- } +- ids := make([]PackageID, len(declMetas)) +- for i, m := range declMetas { +- ids[i] = m.ID +- } +- localPkgs, err = snapshot.TypeCheck(ctx, ids...) +- if err != nil { +- return nil, err +- } - } -- obj := declPkg.GetTypesInfo().ObjectOf(id) // may be nil - - // Is the selected identifier a type name or method? - // (For methods, report the corresponding method names.) @@ -79262,7 +87284,7 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - } - } - if queryType == nil { -- return nil, fmt.Errorf("%s is not a type or method", id.Name) +- return nil, bug.Errorf("%s is not a type or method", obj.Name()) // should have been handled by implementsObj - } - - // Compute the method-set fingerprint used as a key to the global search. @@ -79273,8 +87295,9 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - return nil, nil - } - -- // The global search needs to look at every package in the workspace; -- // see package ./methodsets. +- // The global search needs to look at every package in the +- // forward transitive closure of the workspace; see package +- // ./methodsets. - // - // For now we do all the type checking before beginning the search. - // TODO(adonovan): opt: search in parallel topological order @@ -79285,16 +87308,22 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - if err != nil { - return nil, err - } +- RemoveIntermediateTestVariants(&globalMetas) - globalIDs := make([]PackageID, 0, len(globalMetas)) +- +- var pkgPath PackagePath +- if obj.Pkg() != nil { // nil for error +- pkgPath = PackagePath(obj.Pkg().Path()) +- } - for _, m := range globalMetas { -- if m.PkgPath == declPkg.Metadata().PkgPath { +- if m.PkgPath == pkgPath { - continue // declaring package is handled by local implementation - } - globalIDs = append(globalIDs, m.ID) - } - indexes, err := snapshot.MethodSets(ctx, globalIDs...) - if err != nil { -- return nil, err +- return nil, fmt.Errorf("querying method sets: %v", err) - } - - // Search local and global packages in parallel. @@ -79349,11 +87378,11 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou -// which requires reading the file. -func offsetToLocation(ctx context.Context, snapshot Snapshot, filename string, start, end int) (protocol.Location, error) { - uri := span.URIFromPath(filename) -- fh, err := snapshot.GetFile(ctx, uri) +- fh, err := snapshot.ReadFile(ctx, uri) - if err != nil { - return protocol.Location{}, err // cancelled, perhaps - } -- content, err := fh.Read() +- content, err := fh.Content() - if err != nil { - return protocol.Location{}, err // nonexistent or deleted ("can't happen") - } @@ -79361,18 +87390,19 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - return m.OffsetLocation(start, end) -} - --// typeDeclPosition returns the position of the declaration of the --// type (or one of its methods) referred to at (uri, ppos). --func typeDeclPosition(ctx context.Context, snapshot Snapshot, uri span.URI, ppos protocol.Position) (token.Position, error) { -- var noPosn token.Position -- -- pkg, pgf, err := PackageForFile(ctx, snapshot, uri, WidestPackage) +-// implementsObj returns the object to query for implementations, which is a +-// type name or method. +-// +-// The returned Package is the narrowest package containing ppos, which is the +-// package using the resulting obj but not necessarily the declaring package. +-func implementsObj(ctx context.Context, snapshot Snapshot, uri span.URI, ppos protocol.Position) (types.Object, Package, error) { +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, uri) - if err != nil { -- return noPosn, err +- return nil, nil, err - } - pos, err := pgf.PositionPos(ppos) - if err != nil { -- return noPosn, err +- return nil, nil, err - } - - // This function inherits the limitation of its predecessor in @@ -79387,11 +87417,11 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - // TODO(adonovan): simplify: use objectsAt? - path := pathEnclosingObjNode(pgf.File, pos) - if path == nil { -- return noPosn, ErrNoIdentFound +- return nil, nil, ErrNoIdentFound - } - id, ok := path[0].(*ast.Ident) - if !ok { -- return noPosn, ErrNoIdentFound +- return nil, nil, ErrNoIdentFound - } - - // Is the object a type or method? Reject other kinds. @@ -79407,18 +87437,17 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - // ok - case *types.Func: - if obj.Type().(*types.Signature).Recv() == nil { -- return noPosn, fmt.Errorf("%s is a function, not a method", id.Name) +- return nil, nil, fmt.Errorf("%s is a function, not a method", id.Name) - } - case nil: -- return noPosn, fmt.Errorf("%s denotes unknown object", id.Name) +- return nil, nil, fmt.Errorf("%s denotes unknown object", id.Name) - default: - // e.g. *types.Var -> "var". - kind := strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types.")) -- return noPosn, fmt.Errorf("%s is a %s, not a type", id.Name, kind) +- return nil, nil, fmt.Errorf("%s is a %s, not a type", id.Name, kind) - } - -- declPosn := safetoken.StartPosition(pkg.FileSet(), obj.Pos()) -- return declPosn, nil +- return obj, pkg, nil -} - -// localImplementations searches within pkg for declarations of all @@ -79507,9 +87536,38 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - locs = append(locs, loc) - } - +- // Special case: for types that satisfy error, report builtin.go (see #59527). +- if types.Implements(queryType, errorInterfaceType) { +- loc, err := errorLocation(ctx, snapshot) +- if err != nil { +- return nil, err +- } +- locs = append(locs, loc) +- } +- - return locs, nil -} - +-var errorInterfaceType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) +- +-// errorLocation returns the location of the 'error' type in builtin.go. +-func errorLocation(ctx context.Context, snapshot Snapshot) (protocol.Location, error) { +- pgf, err := snapshot.BuiltinFile(ctx) +- if err != nil { +- return protocol.Location{}, err +- } +- for _, decl := range pgf.File.Decls { +- if decl, ok := decl.(*ast.GenDecl); ok { +- for _, spec := range decl.Specs { +- if spec, ok := spec.(*ast.TypeSpec); ok && spec.Name.Name == "error" { +- return pgf.NodeLocation(spec.Name) +- } +- } +- } +- } +- return protocol.Location{}, fmt.Errorf("built-in error type not found") +-} +- -// concreteImplementsIntf returns true if a is an interface type implemented by -// concrete type b, or vice versa. -func concreteImplementsIntf(a, b types.Type) bool { @@ -79529,7 +87587,7 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou - // to report (e.g.) "ArrayList[T] implements List[T]", but - // GenericAssignableTo doesn't work correctly on pointers to - // generic named types. Thus the legacy implementation and the -- // "local" part of implementation2 fail to report generics. +- // "local" part of implementations fail to report generics. - // The global algorithm based on subsets does the right thing. - return types.AssignableTo(a, b) -} @@ -79604,7 +87662,7 @@ diff -urN a/gopls/internal/lsp/source/implementation.go b/gopls/internal/lsp/sou -} diff -urN a/gopls/internal/lsp/source/inlay_hint.go b/gopls/internal/lsp/source/inlay_hint.go --- a/gopls/internal/lsp/source/inlay_hint.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/inlay_hint.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/inlay_hint.go 1970-01-01 08:00:00 @@ -1,394 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -79690,13 +87748,13 @@ diff -urN a/gopls/internal/lsp/source/inlay_hint.go b/gopls/internal/lsp/source/ - ctx, done := event.Start(ctx, "source.InlayHint") - defer done() - -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, fmt.Errorf("getting file for InlayHint: %w", err) - } - - // Collect a list of the inlay hints that are enabled. -- inlayHintOptions := snapshot.View().Options().InlayHintOptions +- inlayHintOptions := snapshot.Options().InlayHintOptions - var enabledHints []InlayHintFunc - for hint, enabled := range inlayHintOptions.Hints { - if !enabled { @@ -79765,7 +87823,7 @@ diff -urN a/gopls/internal/lsp/source/inlay_hint.go b/gopls/internal/lsp/source/ - if param.Name() == "" { - continue - } -- // Skip the parameter name hint if the arg matches the +- // Skip the parameter name hint if the arg matches - // the parameter name. - if i, ok := v.(*ast.Ident); ok && i.Name == param.Name() { - continue @@ -80000,10 +88058,424 @@ diff -urN a/gopls/internal/lsp/source/inlay_hint.go b/gopls/internal/lsp/source/ - } - return []protocol.InlayHintLabelPart{label} -} +diff -urN a/gopls/internal/lsp/source/inline.go b/gopls/internal/lsp/source/inline.go +--- a/gopls/internal/lsp/source/inline.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/inline.go 1970-01-01 08:00:00 +@@ -1,138 +0,0 @@ +-// Copyright 2023 The Go Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style +-// license that can be found in the LICENSE file. +- +-package source +- +-// This file defines the refactor.inline code action. +- +-import ( +- "context" +- "fmt" +- "go/ast" +- "go/token" +- "go/types" +- "runtime/debug" +- +- "golang.org/x/tools/go/analysis" +- "golang.org/x/tools/go/ast/astutil" +- "golang.org/x/tools/go/types/typeutil" +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/safetoken" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/diff" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/refactor/inline" +-) +- +-// EnclosingStaticCall returns the innermost function call enclosing +-// the selected range, along with the callee. +-func EnclosingStaticCall(pkg Package, pgf *ParsedGoFile, rng protocol.Range) (*ast.CallExpr, *types.Func, error) { +- start, end, err := pgf.RangePos(rng) +- if err != nil { +- return nil, nil, err +- } +- path, _ := astutil.PathEnclosingInterval(pgf.File, start, end) +- +- var call *ast.CallExpr +-loop: +- for _, n := range path { +- switch n := n.(type) { +- case *ast.FuncLit: +- break loop +- case *ast.CallExpr: +- call = n +- break loop +- } +- } +- if call == nil { +- return nil, nil, fmt.Errorf("no enclosing call") +- } +- if safetoken.Line(pgf.Tok, call.Lparen) != safetoken.Line(pgf.Tok, start) { +- return nil, nil, fmt.Errorf("enclosing call is not on this line") +- } +- fn := typeutil.StaticCallee(pkg.GetTypesInfo(), call) +- if fn == nil { +- return nil, nil, fmt.Errorf("not a static call to a Go function") +- } +- return call, fn, nil +-} +- +-func inlineCall(ctx context.Context, snapshot Snapshot, fh FileHandle, rng protocol.Range) (_ *token.FileSet, _ *analysis.SuggestedFix, err error) { +- // Find enclosing static call. +- callerPkg, callerPGF, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) +- if err != nil { +- return nil, nil, err +- } +- call, fn, err := EnclosingStaticCall(callerPkg, callerPGF, rng) +- if err != nil { +- return nil, nil, err +- } +- +- // Locate callee by file/line and analyze it. +- calleePosn := safetoken.StartPosition(callerPkg.FileSet(), fn.Pos()) +- calleePkg, calleePGF, err := NarrowestPackageForFile(ctx, snapshot, span.URIFromPath(calleePosn.Filename)) +- if err != nil { +- return nil, nil, err +- } +- var calleeDecl *ast.FuncDecl +- for _, decl := range calleePGF.File.Decls { +- if decl, ok := decl.(*ast.FuncDecl); ok { +- posn := safetoken.StartPosition(calleePkg.FileSet(), decl.Name.Pos()) +- if posn.Line == calleePosn.Line && posn.Column == calleePosn.Column { +- calleeDecl = decl +- break +- } +- } +- } +- if calleeDecl == nil { +- return nil, nil, fmt.Errorf("can't find callee") +- } +- +- // The inliner assumes that input is well-typed, +- // but that is frequently not the case within gopls. +- // Until we are able to harden the inliner, +- // report panics as errors to avoid crashing the server. +- bad := func(p Package) bool { return len(p.GetParseErrors())+len(p.GetTypeErrors()) > 0 } +- if bad(calleePkg) || bad(callerPkg) { +- defer func() { +- if x := recover(); x != nil { +- err = bug.Errorf("inlining failed unexpectedly: %v\nstack: %v", +- x, debug.Stack()) +- } +- }() +- } +- +- // Users can consult the gopls event log to see +- // why a particular inlining strategy was chosen. +- logf := func(format string, args ...any) { +- event.Log(ctx, "inliner: "+fmt.Sprintf(format, args...)) +- } +- +- callee, err := inline.AnalyzeCallee(logf, calleePkg.FileSet(), calleePkg.GetTypes(), calleePkg.GetTypesInfo(), calleeDecl, calleePGF.Src) +- if err != nil { +- return nil, nil, err +- } +- +- // Inline the call. +- caller := &inline.Caller{ +- Fset: callerPkg.FileSet(), +- Types: callerPkg.GetTypes(), +- Info: callerPkg.GetTypesInfo(), +- File: callerPGF.File, +- Call: call, +- Content: callerPGF.Src, +- } +- +- got, err := inline.Inline(logf, caller, callee) +- if err != nil { +- return nil, nil, err +- } +- +- // Suggest the fix. +- return callerPkg.FileSet(), &analysis.SuggestedFix{ +- Message: fmt.Sprintf("inline call of %v", callee), +- TextEdits: diffToTextEdits(callerPGF.Tok, diff.Bytes(callerPGF.Src, got)), +- }, nil +-} +diff -urN a/gopls/internal/lsp/source/invertifcondition.go b/gopls/internal/lsp/source/invertifcondition.go +--- a/gopls/internal/lsp/source/invertifcondition.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/invertifcondition.go 1970-01-01 08:00:00 +@@ -1,268 +0,0 @@ +-// Copyright 2023 The Go Authors. All rights reserved. +-// Use of this source code is governed by a BSD-style +-// license that can be found in the LICENSE file. +- +-package source +- +-import ( +- "fmt" +- "go/ast" +- "go/token" +- "go/types" +- "strings" +- +- "golang.org/x/tools/go/analysis" +- "golang.org/x/tools/go/ast/astutil" +- "golang.org/x/tools/gopls/internal/lsp/safetoken" +- "golang.org/x/tools/internal/typeparams" +-) +- +-// invertIfCondition is a singleFileFixFunc that inverts an if/else statement +-func invertIfCondition(fset *token.FileSet, start, end token.Pos, src []byte, file *ast.File, _ *types.Package, _ *types.Info) (*analysis.SuggestedFix, error) { +- ifStatement, _, err := CanInvertIfCondition(file, start, end) +- if err != nil { +- return nil, err +- } +- +- var replaceElse analysis.TextEdit +- +- endsWithReturn, err := endsWithReturn(ifStatement.Else) +- if err != nil { +- return nil, err +- } +- +- if endsWithReturn { +- // Replace the whole else part with an empty line and an unindented +- // version of the original if body +- sourcePos := safetoken.StartPosition(fset, ifStatement.Pos()) +- +- indent := sourcePos.Column - 1 +- if indent < 0 { +- indent = 0 +- } +- +- standaloneBodyText := ifBodyToStandaloneCode(fset, ifStatement.Body, src) +- replaceElse = analysis.TextEdit{ +- Pos: ifStatement.Body.Rbrace + 1, // 1 == len("}") +- End: ifStatement.End(), +- NewText: []byte("\n\n" + strings.Repeat("\t", indent) + standaloneBodyText), +- } +- } else { +- // Replace the else body text with the if body text +- bodyStart := safetoken.StartPosition(fset, ifStatement.Body.Lbrace) +- bodyEnd := safetoken.EndPosition(fset, ifStatement.Body.Rbrace+1) // 1 == len("}") +- bodyText := src[bodyStart.Offset:bodyEnd.Offset] +- replaceElse = analysis.TextEdit{ +- Pos: ifStatement.Else.Pos(), +- End: ifStatement.Else.End(), +- NewText: bodyText, +- } +- } +- +- // Replace the if text with the else text +- elsePosInSource := safetoken.StartPosition(fset, ifStatement.Else.Pos()) +- elseEndInSource := safetoken.EndPosition(fset, ifStatement.Else.End()) +- elseText := src[elsePosInSource.Offset:elseEndInSource.Offset] +- replaceBodyWithElse := analysis.TextEdit{ +- Pos: ifStatement.Body.Pos(), +- End: ifStatement.Body.End(), +- NewText: elseText, +- } +- +- // Replace the if condition with its inverse +- inverseCondition, err := invertCondition(fset, ifStatement.Cond, src) +- if err != nil { +- return nil, err +- } +- replaceConditionWithInverse := analysis.TextEdit{ +- Pos: ifStatement.Cond.Pos(), +- End: ifStatement.Cond.End(), +- NewText: inverseCondition, +- } +- +- // Return a SuggestedFix with just that TextEdit in there +- return &analysis.SuggestedFix{ +- TextEdits: []analysis.TextEdit{ +- replaceConditionWithInverse, +- replaceBodyWithElse, +- replaceElse, +- }, +- }, nil +-} +- +-func endsWithReturn(elseBranch ast.Stmt) (bool, error) { +- elseBlock, isBlockStatement := elseBranch.(*ast.BlockStmt) +- if !isBlockStatement { +- return false, fmt.Errorf("Unable to figure out whether this ends with return: %T", elseBranch) +- } +- +- if len(elseBlock.List) == 0 { +- // Empty blocks don't end in returns +- return false, nil +- } +- +- lastStatement := elseBlock.List[len(elseBlock.List)-1] +- +- _, lastStatementIsReturn := lastStatement.(*ast.ReturnStmt) +- return lastStatementIsReturn, nil +-} +- +-// Turn { fmt.Println("Hello") } into just fmt.Println("Hello"), with one less +-// level of indentation. +-// +-// The first line of the result will not be indented, but all of the following +-// lines will. +-func ifBodyToStandaloneCode(fset *token.FileSet, ifBody *ast.BlockStmt, src []byte) string { +- // Get the whole body (without the surrounding braces) as a string +- bodyStart := safetoken.StartPosition(fset, ifBody.Lbrace+1) // 1 == len("}") +- bodyEnd := safetoken.EndPosition(fset, ifBody.Rbrace) +- bodyWithoutBraces := string(src[bodyStart.Offset:bodyEnd.Offset]) +- bodyWithoutBraces = strings.TrimSpace(bodyWithoutBraces) +- +- // Unindent +- bodyWithoutBraces = strings.ReplaceAll(bodyWithoutBraces, "\n\t", "\n") +- +- return bodyWithoutBraces +-} +- +-func invertCondition(fset *token.FileSet, cond ast.Expr, src []byte) ([]byte, error) { +- condStart := safetoken.StartPosition(fset, cond.Pos()) +- condEnd := safetoken.EndPosition(fset, cond.End()) +- oldText := string(src[condStart.Offset:condEnd.Offset]) +- +- switch expr := cond.(type) { +- case *ast.Ident, *ast.ParenExpr, *ast.CallExpr, *ast.StarExpr, *ast.IndexExpr, *typeparams.IndexListExpr, *ast.SelectorExpr: +- newText := "!" + oldText +- if oldText == "true" { +- newText = "false" +- } else if oldText == "false" { +- newText = "true" +- } +- +- return []byte(newText), nil +- +- case *ast.UnaryExpr: +- if expr.Op != token.NOT { +- // This should never happen +- return dumbInvert(fset, cond, src), nil +- } +- +- inverse := expr.X +- if p, isParen := inverse.(*ast.ParenExpr); isParen { +- // We got !(x), remove the parentheses with the ! so we get just "x" +- inverse = p.X +- +- start := safetoken.StartPosition(fset, inverse.Pos()) +- end := safetoken.EndPosition(fset, inverse.End()) +- if start.Line != end.Line { +- // The expression is multi-line, so we can't remove the parentheses +- inverse = expr.X +- } +- } +- +- start := safetoken.StartPosition(fset, inverse.Pos()) +- end := safetoken.EndPosition(fset, inverse.End()) +- textWithoutNot := src[start.Offset:end.Offset] +- +- return textWithoutNot, nil +- +- case *ast.BinaryExpr: +- // These inversions are unsound for floating point NaN, but that's ok. +- negations := map[token.Token]string{ +- token.EQL: "!=", +- token.LSS: ">=", +- token.GTR: "<=", +- token.NEQ: "==", +- token.LEQ: ">", +- token.GEQ: "<", +- } +- +- negation, negationFound := negations[expr.Op] +- if !negationFound { +- return invertAndOr(fset, expr, src) +- } +- +- xPosInSource := safetoken.StartPosition(fset, expr.X.Pos()) +- opPosInSource := safetoken.StartPosition(fset, expr.OpPos) +- yPosInSource := safetoken.StartPosition(fset, expr.Y.Pos()) +- +- textBeforeOp := string(src[xPosInSource.Offset:opPosInSource.Offset]) +- +- oldOpWithTrailingWhitespace := string(src[opPosInSource.Offset:yPosInSource.Offset]) +- newOpWithTrailingWhitespace := negation + oldOpWithTrailingWhitespace[len(expr.Op.String()):] +- +- textAfterOp := string(src[yPosInSource.Offset:condEnd.Offset]) +- +- return []byte(textBeforeOp + newOpWithTrailingWhitespace + textAfterOp), nil +- } +- +- return dumbInvert(fset, cond, src), nil +-} +- +-// dumbInvert is a fallback, inverting cond into !(cond). +-func dumbInvert(fset *token.FileSet, expr ast.Expr, src []byte) []byte { +- start := safetoken.StartPosition(fset, expr.Pos()) +- end := safetoken.EndPosition(fset, expr.End()) +- text := string(src[start.Offset:end.Offset]) +- return []byte("!(" + text + ")") +-} +- +-func invertAndOr(fset *token.FileSet, expr *ast.BinaryExpr, src []byte) ([]byte, error) { +- if expr.Op != token.LAND && expr.Op != token.LOR { +- // Neither AND nor OR, don't know how to invert this +- return dumbInvert(fset, expr, src), nil +- } +- +- oppositeOp := "&&" +- if expr.Op == token.LAND { +- oppositeOp = "||" +- } +- +- xEndInSource := safetoken.EndPosition(fset, expr.X.End()) +- opPosInSource := safetoken.StartPosition(fset, expr.OpPos) +- whitespaceAfterBefore := src[xEndInSource.Offset:opPosInSource.Offset] +- +- invertedBefore, err := invertCondition(fset, expr.X, src) +- if err != nil { +- return nil, err +- } +- +- invertedAfter, err := invertCondition(fset, expr.Y, src) +- if err != nil { +- return nil, err +- } +- +- yPosInSource := safetoken.StartPosition(fset, expr.Y.Pos()) +- +- oldOpWithTrailingWhitespace := string(src[opPosInSource.Offset:yPosInSource.Offset]) +- newOpWithTrailingWhitespace := oppositeOp + oldOpWithTrailingWhitespace[len(expr.Op.String()):] +- +- return []byte(string(invertedBefore) + string(whitespaceAfterBefore) + newOpWithTrailingWhitespace + string(invertedAfter)), nil +-} +- +-// CanInvertIfCondition reports whether we can do invert-if-condition on the +-// code in the given range +-func CanInvertIfCondition(file *ast.File, start, end token.Pos) (*ast.IfStmt, bool, error) { +- path, _ := astutil.PathEnclosingInterval(file, start, end) +- for _, node := range path { +- stmt, isIfStatement := node.(*ast.IfStmt) +- if !isIfStatement { +- continue +- } +- +- if stmt.Else == nil { +- // Can't invert conditions without else clauses +- return nil, false, fmt.Errorf("else clause required") +- } +- +- if _, hasElseIf := stmt.Else.(*ast.IfStmt); hasElseIf { +- // Can't invert conditions with else-if clauses, unclear what that +- // would look like +- return nil, false, fmt.Errorf("else-if not supported") +- } +- +- return stmt, true, nil +- } +- +- return nil, false, fmt.Errorf("not an if statement") +-} diff -urN a/gopls/internal/lsp/source/known_packages.go b/gopls/internal/lsp/source/known_packages.go --- a/gopls/internal/lsp/source/known_packages.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/known_packages.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,140 +0,0 @@ ++++ b/gopls/internal/lsp/source/known_packages.go 1970-01-01 08:00:00 +@@ -1,134 +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. @@ -80012,7 +88484,6 @@ diff -urN a/gopls/internal/lsp/source/known_packages.go b/gopls/internal/lsp/sou - -import ( - "context" -- "fmt" - "go/parser" - "go/token" - "sort" @@ -80034,19 +88505,14 @@ diff -urN a/gopls/internal/lsp/source/known_packages.go b/gopls/internal/lsp/sou - // This algorithm is expressed in terms of Metadata, not Packages, - // so it doesn't cause or wait for type checking. - -- // Find a Metadata containing the file. -- metas, err := snapshot.MetadataForFile(ctx, fh.URI()) +- current, err := NarrowestMetadataForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, err // e.g. context cancelled - } -- if len(metas) == 0 { -- return nil, fmt.Errorf("no loaded package contain file %s", fh.URI()) -- } -- current := metas[0] // pick one arbitrarily (they should all have the same package path) - - // Parse the file's imports so we can compute which - // PackagePaths are imported by this specific file. -- src, err := fh.Read() +- src, err := fh.Content() - if err != nil { - return nil, err - } @@ -80063,7 +88529,7 @@ diff -urN a/gopls/internal/lsp/source/known_packages.go b/gopls/internal/lsp/sou - } - } - -- // Now find candidates among known packages. +- // Now find candidates among all known packages. - knownPkgs, err := snapshot.AllMetadata(ctx) - if err != nil { - return nil, err @@ -80097,7 +88563,7 @@ diff -urN a/gopls/internal/lsp/source/known_packages.go b/gopls/internal/lsp/sou - } - - // Augment the set by invoking the goimports algorithm. -- if err := snapshot.RunProcessEnvFunc(ctx, func(o *imports.Options) error { +- if err := snapshot.RunProcessEnvFunc(ctx, func(ctx context.Context, o *imports.Options) error { - ctx, cancel := context.WithTimeout(ctx, time.Millisecond*80) - defer cancel() - var seenMu sync.Mutex @@ -80146,8 +88612,8 @@ diff -urN a/gopls/internal/lsp/source/known_packages.go b/gopls/internal/lsp/sou -} diff -urN a/gopls/internal/lsp/source/linkname.go b/gopls/internal/lsp/source/linkname.go --- a/gopls/internal/lsp/source/linkname.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/linkname.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,136 +0,0 @@ ++++ b/gopls/internal/lsp/source/linkname.go 1970-01-01 08:00:00 +@@ -1,156 +0,0 @@ -// Copyright 2023 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. @@ -80173,65 +88639,88 @@ diff -urN a/gopls/internal/lsp/source/linkname.go b/gopls/internal/lsp/source/li - -// LinknameDefinition finds the definition of the linkname directive in fh at pos. -// If there is no linkname directive at pos, returns ErrNoLinkname. --func LinknameDefinition(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]protocol.Location, error) { -- pkgPath, name := parseLinkname(ctx, snapshot, fh, pos) +-func LinknameDefinition(ctx context.Context, snapshot Snapshot, fh FileHandle, from protocol.Position) ([]protocol.Location, error) { +- pkgPath, name, _ := parseLinkname(ctx, snapshot, fh, from) - if pkgPath == "" { - return nil, ErrNoLinkname - } -- return findLinkname(ctx, snapshot, fh, pos, PackagePath(pkgPath), name) +- +- _, pgf, pos, err := findLinkname(ctx, snapshot, PackagePath(pkgPath), name) +- if err != nil { +- return nil, fmt.Errorf("find linkname: %w", err) +- } +- loc, err := pgf.PosLocation(pos, pos+token.Pos(len(name))) +- if err != nil { +- return nil, fmt.Errorf("location of linkname: %w", err) +- } +- return []protocol.Location{loc}, nil -} - -// parseLinkname attempts to parse a go:linkname declaration at the given pos. --// If successful, it returns the package path and object name referenced by the second --// argument of the linkname directive. --// --// If the position is not in the second argument of a go:linkname directive, or parsing fails, it returns "", "". --func parseLinkname(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) (pkgPath, name string) { +-// If successful, it returns +-// - package path referenced +-// - object name referenced +-// - byte offset in fh of the start of the link target +-// of the linkname directives 2nd argument. +-// +-// If the position is not in the second argument of a go:linkname directive, +-// or parsing fails, it returns "", "", 0. +-func parseLinkname(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) (pkgPath, name string, targetOffset int) { +- // TODO(adonovan): opt: parsing isn't necessary here. +- // We're only looking for a line comment. - pgf, err := snapshot.ParseGo(ctx, fh, ParseFull) - if err != nil { -- return "", "" +- return "", "", 0 - } - -- span, err := pgf.Mapper.PositionPoint(pos) +- offset, err := pgf.Mapper.PositionOffset(pos) - if err != nil { -- return "", "" +- return "", "", 0 - } -- atLine := span.Line() -- atColumn := span.Column() - - // Looking for pkgpath in '//go:linkname f pkgpath.g'. - // (We ignore 1-arg linkname directives.) -- directive, column := findLinknameOnLine(pgf, atLine) +- directive, end := findLinknameAtOffset(pgf, offset) - parts := strings.Fields(directive) - if len(parts) != 3 { -- return "", "" +- return "", "", 0 - } - - // Inside 2nd arg [start, end]? -- end := column + len(directive) +- // (Assumes no trailing spaces.) - start := end - len(parts[2]) -- if !(start <= atColumn && atColumn <= end) { -- return "", "" +- if !(start <= offset && offset <= end) { +- return "", "", 0 - } - linkname := parts[2] - - // Split the pkg path from the name. - dot := strings.LastIndexByte(linkname, '.') - if dot < 0 { -- return "", "" +- return "", "", 0 - } -- return linkname[:dot], linkname[dot+1:] +- +- return linkname[:dot], linkname[dot+1:], start -} - --// findLinknameOnLine returns the first linkname directive on line and the column it starts at. --// Returns "", 0 if no linkname directive is found on the line. --func findLinknameOnLine(pgf *ParsedGoFile, line int) (string, int) { +-// findLinknameAtOffset returns the first linkname directive on line and its end offset. +-// Returns "", 0 if the offset is not in a linkname directive. +-func findLinknameAtOffset(pgf *ParsedGoFile, offset int) (string, int) { - for _, grp := range pgf.File.Comments { - for _, com := range grp.List { - if strings.HasPrefix(com.Text, "//go:linkname") { - p := safetoken.Position(pgf.Tok, com.Pos()) -- if p.Line == line { -- return com.Text, p.Column +- +- // Sometimes source code (typically tests) has another +- // comment after the directive, trim that away. +- text := com.Text +- if i := strings.LastIndex(text, "//"); i != 0 { +- text = strings.TrimSpace(text[:i]) +- } +- +- end := p.Offset + len(text) +- if p.Offset <= offset && offset < end { +- return text, end - } - } - } @@ -80241,16 +88730,16 @@ diff -urN a/gopls/internal/lsp/source/linkname.go b/gopls/internal/lsp/source/li - -// findLinkname searches dependencies of packages containing fh for an object -// with linker name matching the given package path and name. --func findLinkname(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position, pkgPath PackagePath, name string) ([]protocol.Location, error) { +-func findLinkname(ctx context.Context, snapshot Snapshot, pkgPath PackagePath, name string) (Package, *ParsedGoFile, token.Pos, error) { - // Typically the linkname refers to a forward dependency - // or a reverse dependency, but in general it may refer -- // to any package in the workspace. +- // to any package that is linked with this one. - var pkgMeta *Metadata - metas, err := snapshot.AllMetadata(ctx) - if err != nil { -- return nil, err +- return nil, nil, token.NoPos, err - } -- metas = RemoveIntermediateTestVariants(metas) +- RemoveIntermediateTestVariants(&metas) - for _, meta := range metas { - if meta.PkgPath == pkgPath { - pkgMeta = meta @@ -80258,36 +88747,33 @@ diff -urN a/gopls/internal/lsp/source/linkname.go b/gopls/internal/lsp/source/li - } - } - if pkgMeta == nil { -- return nil, fmt.Errorf("cannot find package %q", pkgPath) +- return nil, nil, token.NoPos, fmt.Errorf("cannot find package %q", pkgPath) - } - - // When found, type check the desired package (snapshot.TypeCheck in TypecheckFull mode), - pkgs, err := snapshot.TypeCheck(ctx, pkgMeta.ID) - if err != nil { -- return nil, err +- return nil, nil, token.NoPos, err - } - pkg := pkgs[0] - - obj := pkg.GetTypes().Scope().Lookup(name) - if obj == nil { -- return nil, fmt.Errorf("package %q does not define %s", pkgPath, name) +- return nil, nil, token.NoPos, fmt.Errorf("package %q does not define %s", pkgPath, name) - } - - objURI := safetoken.StartPosition(pkg.FileSet(), obj.Pos()) - pgf, err := pkg.File(span.URIFromPath(objURI.Filename)) - if err != nil { -- return nil, err +- return nil, nil, token.NoPos, err - } -- loc, err := pgf.PosLocation(obj.Pos(), obj.Pos()+token.Pos(len(name))) -- if err != nil { -- return nil, err -- } -- return []protocol.Location{loc}, nil +- +- return pkg, pgf, obj.Pos(), nil -} diff -urN a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/lsp/source/methodsets/methodsets.go --- a/gopls/internal/lsp/source/methodsets/methodsets.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/methodsets/methodsets.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,508 +0,0 @@ ++++ b/gopls/internal/lsp/source/methodsets/methodsets.go 1970-01-01 08:00:00 +@@ -1,490 +0,0 @@ -// Copyright 2023 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. @@ -80299,7 +88785,7 @@ diff -urN a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/ -// This package provides only the "global" (all workspace) search; the -// "local" search within a given package uses a different -// implementation based on type-checker data structures for a single --// package plus variants; see ../implementation2.go. +-// package plus variants; see ../implementation.go. -// The local algorithm is more precise as it tests function-local types too. -// -// A global index of function-local types is challenging since they @@ -80334,20 +88820,17 @@ diff -urN a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/ -// single 64-bit mask is quite effective. See CL 452060 for details. - -import ( -- "bytes" -- "encoding/gob" - "fmt" - "go/token" - "go/types" - "hash/crc32" -- "log" - "strconv" - "strings" - - "golang.org/x/tools/go/types/objectpath" +- "golang.org/x/tools/gopls/internal/lsp/frob" - "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/internal/typeparams" -- "golang.org/x/tools/internal/typesinternal" -) - -// An Index records the non-empty method sets of all package-level @@ -80360,27 +88843,13 @@ diff -urN a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/ -// Decode decodes the given gob-encoded data as an Index. -func Decode(data []byte) *Index { - var pkg gobPackage -- mustDecode(data, &pkg) +- packageCodec.Decode(data, &pkg) - return &Index{pkg} -} - -// Encode encodes the receiver as gob-encoded data. -func (index *Index) Encode() []byte { -- return mustEncode(index.pkg) --} -- --func mustEncode(x interface{}) []byte { -- var buf bytes.Buffer -- if err := gob.NewEncoder(&buf).Encode(x); err != nil { -- log.Fatalf("internal error encoding %T: %v", x, err) -- } -- return buf.Bytes() --} -- --func mustDecode(data []byte, ptr interface{}) { -- if err := gob.NewDecoder(bytes.NewReader(data)).Decode(ptr); err != nil { -- log.Fatalf("internal error decoding %T: %v", ptr, err) -- } +- return packageCodec.Encode(index.pkg) -} - -// NewIndex returns a new index of method-set information for all @@ -80522,7 +88991,7 @@ diff -urN a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/ - return gobPosition{b.string(posn.Filename), posn.Offset, len(obj.Name())} - } - -- objectpathFor := typesinternal.NewObjectpathFunc() +- objectpathFor := new(objectpath.Encoder).For - - // setindexInfo sets the (Posn, PkgPath, ObjectPath) fields for each method declaration. - setIndexInfo := func(m *gobMethod, method *types.Func) { @@ -80642,8 +89111,8 @@ diff -urN a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/ - if tname.Pkg() != nil { - buf.WriteString(strconv.Quote(tname.Pkg().Path())) - buf.WriteByte('.') -- } else if tname.Name() != "error" { -- panic(tname) // error is the only named type with no package +- } else if tname.Name() != "error" && tname.Name() != "comparable" { +- panic(tname) // error and comparable the only named types with no package - } - buf.WriteString(tname.Name()) - @@ -80761,9 +89230,8 @@ diff -urN a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/ - -// -- serial format of index -- - --// The cost of gob encoding and decoding for most packages in x/tools --// is under 50us, with occasional peaks of around 1-3ms. --// The encoded indexes are around 1KB-50KB. +-// (The name says gob but in fact we use frob.) +-var packageCodec = frob.CodecFor[gobPackage]() - -// A gobPackage records the method set of each package-level type for a single package. -type gobPackage struct { @@ -80798,8 +89266,8 @@ diff -urN a/gopls/internal/lsp/source/methodsets/methodsets.go b/gopls/internal/ -} diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/options.go --- a/gopls/internal/lsp/source/options.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/options.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1631 +0,0 @@ ++++ b/gopls/internal/lsp/source/options.go 1970-01-01 08:00:00 +@@ -1,1797 +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. @@ -80818,6 +89286,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - "time" - - "golang.org/x/tools/go/analysis" +- "golang.org/x/tools/go/analysis/passes/appends" - "golang.org/x/tools/go/analysis/passes/asmdecl" - "golang.org/x/tools/go/analysis/passes/assign" - "golang.org/x/tools/go/analysis/passes/atomic" @@ -80828,6 +89297,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - "golang.org/x/tools/go/analysis/passes/composite" - "golang.org/x/tools/go/analysis/passes/copylock" - "golang.org/x/tools/go/analysis/passes/deepequalerrors" +- "golang.org/x/tools/go/analysis/passes/defers" - "golang.org/x/tools/go/analysis/passes/directive" - "golang.org/x/tools/go/analysis/passes/errorsas" - "golang.org/x/tools/go/analysis/passes/fieldalignment" @@ -80840,6 +89310,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - "golang.org/x/tools/go/analysis/passes/printf" - "golang.org/x/tools/go/analysis/passes/shadow" - "golang.org/x/tools/go/analysis/passes/shift" +- "golang.org/x/tools/go/analysis/passes/slog" - "golang.org/x/tools/go/analysis/passes/sortslice" - "golang.org/x/tools/go/analysis/passes/stdmethods" - "golang.org/x/tools/go/analysis/passes/stringintconv" @@ -80852,6 +89323,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - "golang.org/x/tools/go/analysis/passes/unsafeptr" - "golang.org/x/tools/go/analysis/passes/unusedresult" - "golang.org/x/tools/go/analysis/passes/unusedwrite" +- "golang.org/x/tools/gopls/internal/lsp/analysis/deprecated" - "golang.org/x/tools/gopls/internal/lsp/analysis/embeddirective" - "golang.org/x/tools/gopls/internal/lsp/analysis/fillreturns" - "golang.org/x/tools/gopls/internal/lsp/analysis/fillstruct" @@ -80880,7 +89352,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt -// DefaultOptions is the options that are used for Gopls execution independent -// of any externally provided configuration (LSP initialization, command -// invocation, etc.). --func DefaultOptions() *Options { +-func DefaultOptions(overrides ...func(*Options)) *Options { - optionsOnce.Do(func() { - var commands []string - for _, c := range command.Commands { @@ -80904,6 +89376,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - protocol.SourceOrganizeImports: true, - protocol.QuickFix: true, - protocol.RefactorRewrite: true, +- protocol.RefactorInline: true, - protocol.RefactorExtract: true, - }, - Mod: { @@ -80926,14 +89399,15 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - }, - UIOptions: UIOptions{ - DiagnosticOptions: DiagnosticOptions{ -- DiagnosticsDelay: 250 * time.Millisecond, - Annotations: map[Annotation]bool{ - Bounds: true, - Escape: true, - Inline: true, - Nil: true, - }, -- Vulncheck: ModeVulncheckOff, +- Vulncheck: ModeVulncheckOff, +- DiagnosticsDelay: 1 * time.Second, +- AnalysisProgressReporting: true, - }, - InlayHintOptions: InlayHintOptions{}, - DocumentationOptions: DocumentationOptions{ @@ -80945,11 +89419,13 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - ImportShortcut: BothShortcuts, - SymbolMatcher: SymbolFastFuzzy, - SymbolStyle: DynamicSymbols, +- SymbolScope: AllSymbolScope, - }, - CompletionOptions: CompletionOptions{ - Matcher: Fuzzy, - CompletionBudget: 100 * time.Millisecond, - ExperimentalPostfixCompletions: true, +- CompleteFunctionCalls: true, - }, - Codelenses: map[string]bool{ - string(command.Generate): true, @@ -80963,13 +89439,17 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - }, - }, - InternalOptions: InternalOptions{ -- LiteralCompletions: true, -- TempModfile: true, -- CompleteUnimported: true, -- CompletionDocumentation: true, -- DeepCompletion: true, -- ChattyDiagnostics: true, -- NewDiff: "both", +- LiteralCompletions: true, +- TempModfile: true, +- CompleteUnimported: true, +- CompletionDocumentation: true, +- DeepCompletion: true, +- ChattyDiagnostics: true, +- NewDiff: "new", +- SubdirWatchPatterns: SubdirWatchPatternsAuto, +- ReportAnalysisProgressAfter: 5 * time.Second, +- TelemetryPrompt: false, +- LinkifyShowMessage: false, - }, - Hooks: Hooks{ - // TODO(adonovan): switch to new diff.Strings implementation. @@ -80983,7 +89463,13 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - }, - } - }) -- return defaultOptions +- options := defaultOptions.Clone() +- for _, override := range overrides { +- if override != nil { +- override(options) +- } +- } +- return options -} - -// Options holds various configuration that affects Gopls execution, organized @@ -80996,9 +89482,26 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - Hooks -} - +-// IsAnalyzerEnabled reports whether an analyzer with the given name is +-// enabled. +-// +-// TODO(rfindley): refactor to simplify this function. We no longer need the +-// different categories of analyzer. +-func (opts *Options) IsAnalyzerEnabled(name string) bool { +- for _, amap := range []map[string]*Analyzer{opts.DefaultAnalyzers, opts.TypeErrorAnalyzers, opts.ConvenienceAnalyzers, opts.StaticcheckAnalyzers} { +- for _, analyzer := range amap { +- if analyzer.Analyzer.Name == name && analyzer.IsEnabled(opts) { +- return true +- } +- } +- } +- return false +-} +- -// ClientOptions holds LSP-specific configuration that is provided by the -// client. -type ClientOptions struct { +- ClientInfo *protocol.Msg_XInitializeParams_clientInfo - InsertTextFormat protocol.InsertTextFormat - ConfigurationSupported bool - DynamicConfigurationSupported bool @@ -81156,6 +89659,13 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - // ExperimentalPostfixCompletions enables artificial method snippets - // such as "someSlice.sort!". - ExperimentalPostfixCompletions bool `status:"experimental"` +- +- // CompleteFunctionCalls enables function call completion. +- // +- // When completing a statement, or when a function return type matches the +- // expected of the expression being completed, completion may suggest call +- // expressions (i.e. may include parentheses). +- CompleteFunctionCalls bool -} - -type DocumentationOptions struct { @@ -81227,6 +89737,17 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - // - // This option must be set to a valid duration string, for example `"250ms"`. - DiagnosticsDelay time.Duration `status:"advanced"` +- +- // AnalysisProgressReporting controls whether gopls sends progress +- // notifications when construction of its index of analysis facts is taking a +- // long time. Cancelling these notifications will cancel the indexing task, +- // though it will restart after the next change in the workspace. +- // +- // When a package is opened for the first time and heavyweight analyses such as +- // staticcheck are enabled, it can take a while to construct the index of +- // analysis facts for all its dependencies. The index is cached in the +- // filesystem, so subsequent analysis should be faster. +- AnalysisProgressReporting bool -} - -type InlayHintOptions struct { @@ -81256,6 +89777,13 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - // } - // ``` - SymbolStyle SymbolStyle `status:"advanced"` +- +- // SymbolScope controls which packages are searched for workspace/symbol +- // requests. The default value, "workspace", searches only workspace +- // packages. The legacy behavior, "all", causes all loaded packages to be +- // searched, including dependencies; this is more expensive and may return +- // unwanted results. +- SymbolScope SymbolScope -} - -// UserOptions holds custom Gopls configuration (not part of the LSP) that is @@ -81330,6 +89858,9 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt -// average user. These may be settings used by tests or outdated settings that -// will soon be deprecated. Some of these settings may not even be configurable -// by the user. +-// +-// TODO(rfindley): even though these settings are not intended for +-// modification, some of them should be surfaced in our documentation. -type InternalOptions struct { - // LiteralCompletions controls whether literal candidates such as - // "&someStruct{}" are offered. Tests disable this flag to simplify @@ -81393,8 +89924,58 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - // file change. If unset, gopls only reports diagnostics when they change, or - // when a file is opened or closed. - ChattyDiagnostics bool +- +- // SubdirWatchPatterns configures the file watching glob patterns registered +- // by gopls. +- // +- // Some clients (namely VS Code) do not send workspace/didChangeWatchedFile +- // notifications for files contained in a directory when that directory is +- // deleted: +- // https://github.com/microsoft/vscode/issues/109754 +- // +- // In this case, gopls would miss important notifications about deleted +- // packages. To work around this, gopls registers a watch pattern for each +- // directory containing Go files. +- // +- // Unfortunately, other clients experience performance problems with this +- // many watch patterns, so there is no single behavior that works well for +- // all clients. +- // +- // The "subdirWatchPatterns" setting allows configuring this behavior. Its +- // default value of "auto" attempts to guess the correct behavior based on +- // the client name. We'd love to avoid this specialization, but as described +- // above there is no single value that works for all clients. +- // +- // If any LSP client does not behave well with the default value (for +- // example, if like VS Code it drops file notifications), please file an +- // issue. +- SubdirWatchPatterns SubdirWatchPatterns +- +- // ReportAnalysisProgressAfter sets the duration for gopls to wait before starting +- // progress reporting for ongoing go/analysis passes. +- // +- // It is intended to be used for testing only. +- ReportAnalysisProgressAfter time.Duration +- +- // TelemetryPrompt controls whether gopls prompts about enabling Go telemetry. +- // +- // Once the prompt is answered, gopls doesn't ask again, but TelemetryPrompt +- // can prevent the question from ever being asked in the first place. +- TelemetryPrompt bool +- +- // LinkifyShowMessage controls whether the client wants gopls +- // to linkify links in showMessage. e.g. [go.dev](https://go.dev). +- LinkifyShowMessage bool -} - +-type SubdirWatchPatterns string +- +-const ( +- SubdirWatchPatternsOn SubdirWatchPatterns = "on" +- SubdirWatchPatternsOff SubdirWatchPatterns = "off" +- SubdirWatchPatternsAuto SubdirWatchPatterns = "auto" +-) +- -type ImportShortcut string - -const ( @@ -81419,6 +90000,8 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - CaseSensitive Matcher = "CaseSensitive" -) - +-// A SymbolMatcher controls the matching of symbols for workspace/symbol +-// requests. -type SymbolMatcher string - -const ( @@ -81428,6 +90011,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - SymbolCaseSensitive SymbolMatcher = "CaseSensitive" -) - +-// A SymbolStyle controls the formatting of symbols in workspace/symbol results. -type SymbolStyle string - -const ( @@ -81444,6 +90028,17 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - DynamicSymbols SymbolStyle = "Dynamic" -) - +-// A SymbolScope controls the search scope for workspace/symbol requests. +-type SymbolScope string +- +-const ( +- // WorkspaceSymbolScope matches symbols in workspace packages only. +- WorkspaceSymbolScope SymbolScope = "workspace" +- // AllSymbolScope matches symbols in any loaded package, including +- // dependencies. +- AllSymbolScope SymbolScope = "all" +-) +- -type HoverKind string - -const ( @@ -81522,7 +90117,8 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - return results -} - --func (o *Options) ForClientCapabilities(caps protocol.ClientCapabilities) { +-func (o *Options) ForClientCapabilities(clientName *protocol.Msg_XInitializeParams_clientInfo, caps protocol.ClientCapabilities) { +- o.ClientInfo = clientName - // Check if the client supports snippets in completion items. - if caps.Workspace.WorkspaceEdit != nil { - o.SupportedResourceOperations = caps.Workspace.WorkspaceEdit.ResourceOperations @@ -81771,6 +90367,14 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - o.SymbolStyle = SymbolStyle(s) - } - +- case "symbolScope": +- if s, ok := result.asOneOf( +- string(WorkspaceSymbolScope), +- string(AllSymbolScope), +- ); ok { +- o.SymbolScope = SymbolScope(s) +- } +- - case "hoverKind": - if s, ok := result.asOneOf( - string(NoDocumentation), @@ -81860,6 +90464,8 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - " rebuild gopls with a more recent version of Go", result.Name, runtime.Version()) - } - } +- case "completeFunctionCalls": +- result.setBool(&o.CompleteFunctionCalls) - - case "semanticTokens": - result.setBool(&o.SemanticTokens) @@ -81903,6 +90509,9 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - case "diagnosticsDelay": - result.setDuration(&o.DiagnosticsDelay) - +- case "analysisProgressReporting": +- result.setBool(&o.AnalysisProgressReporting) +- - case "experimentalWatchedFileDelay": - result.deprecated("") - @@ -81931,6 +90540,23 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - case "chattyDiagnostics": - result.setBool(&o.ChattyDiagnostics) - +- case "subdirWatchPatterns": +- if s, ok := result.asOneOf( +- string(SubdirWatchPatternsOn), +- string(SubdirWatchPatternsOff), +- string(SubdirWatchPatternsAuto), +- ); ok { +- o.SubdirWatchPatterns = SubdirWatchPatterns(s) +- } +- +- case "reportAnalysisProgressAfter": +- result.setDuration(&o.ReportAnalysisProgressAfter) +- +- case "telemetryPrompt": +- result.setBool(&o.TelemetryPrompt) +- case "linkifyShowMessage": +- result.setBool(&o.LinkifyShowMessage) +- - // Replaced settings. - case "experimentalDisabledAnalyses": - result.deprecated("analyses") @@ -81990,14 +90616,6 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - return e.msg -} - --// softErrorf reports an error that does not affect the functionality of gopls --// (a warning in the UI). --// The formatted message will be shown to the user unmodified. --func (r *OptionResult) softErrorf(format string, values ...interface{}) { -- msg := fmt.Sprintf(format, values...) -- r.Error = &SoftError{msg} --} -- -// deprecated reports the current setting as deprecated. If 'replacement' is -// non-nil, it is suggested to the user. -func (r *OptionResult) deprecated(replacement string) { @@ -82166,7 +90784,8 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt -func typeErrorAnalyzers() map[string]*Analyzer { - return map[string]*Analyzer{ - fillreturns.Analyzer.Name: { -- Analyzer: fillreturns.Analyzer, +- Analyzer: fillreturns.Analyzer, +- // TODO(rfindley): is SourceFixAll even necessary here? Is that not implied? - ActionKind: []protocol.CodeActionKind{protocol.SourceFixAll, protocol.QuickFix}, - Enabled: true, - }, @@ -82190,6 +90809,8 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - } -} - +-// TODO(golang/go#61559): remove convenience analyzers now that they are not +-// used from the analysis framework. -func convenienceAnalyzers() map[string]*Analyzer { - return map[string]*Analyzer{ - fillstruct.Analyzer.Name: { @@ -82199,10 +90820,14 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - ActionKind: []protocol.CodeActionKind{protocol.RefactorRewrite}, - }, - stubmethods.Analyzer.Name: { -- Analyzer: stubmethods.Analyzer, -- ActionKind: []protocol.CodeActionKind{protocol.RefactorRewrite}, -- Fix: StubMethods, +- Analyzer: stubmethods.Analyzer, +- Fix: StubMethods, +- Enabled: true, +- }, +- infertypeargs.Analyzer.Name: { +- Analyzer: infertypeargs.Analyzer, - Enabled: true, +- ActionKind: []protocol.CodeActionKind{protocol.RefactorRewrite}, - }, - } -} @@ -82210,6 +90835,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt -func defaultAnalyzers() map[string]*Analyzer { - return map[string]*Analyzer{ - // The traditional vet suite: +- appends.Analyzer.Name: {Analyzer: appends.Analyzer, Enabled: true}, - asmdecl.Analyzer.Name: {Analyzer: asmdecl.Analyzer, Enabled: true}, - assign.Analyzer.Name: {Analyzer: assign.Analyzer, Enabled: true}, - atomic.Analyzer.Name: {Analyzer: atomic.Analyzer, Enabled: true}, @@ -82218,6 +90844,8 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - cgocall.Analyzer.Name: {Analyzer: cgocall.Analyzer, Enabled: true}, - composite.Analyzer.Name: {Analyzer: composite.Analyzer, Enabled: true}, - copylock.Analyzer.Name: {Analyzer: copylock.Analyzer, Enabled: true}, +- defers.Analyzer.Name: {Analyzer: defers.Analyzer, Enabled: true}, +- deprecated.Analyzer.Name: {Analyzer: deprecated.Analyzer, Enabled: true, Severity: protocol.SeverityHint, Tag: []protocol.DiagnosticTag{protocol.Deprecated}}, - directive.Analyzer.Name: {Analyzer: directive.Analyzer, Enabled: true}, - errorsas.Analyzer.Name: {Analyzer: errorsas.Analyzer, Enabled: true}, - httpresponse.Analyzer.Name: {Analyzer: httpresponse.Analyzer, Enabled: true}, @@ -82227,6 +90855,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - nilfunc.Analyzer.Name: {Analyzer: nilfunc.Analyzer, Enabled: true}, - printf.Analyzer.Name: {Analyzer: printf.Analyzer, Enabled: true}, - shift.Analyzer.Name: {Analyzer: shift.Analyzer, Enabled: true}, +- slog.Analyzer.Name: {Analyzer: slog.Analyzer, Enabled: true}, - stdmethods.Analyzer.Name: {Analyzer: stdmethods.Analyzer, Enabled: true}, - stringintconv.Analyzer.Name: {Analyzer: stringintconv.Analyzer, Enabled: true}, - structtag.Analyzer.Name: {Analyzer: structtag.Analyzer, Enabled: true}, @@ -82247,9 +90876,13 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt - unusedparams.Analyzer.Name: {Analyzer: unusedparams.Analyzer, Enabled: false}, - unusedwrite.Analyzer.Name: {Analyzer: unusedwrite.Analyzer, Enabled: false}, - useany.Analyzer.Name: {Analyzer: useany.Analyzer, Enabled: false}, -- infertypeargs.Analyzer.Name: {Analyzer: infertypeargs.Analyzer, Enabled: true}, -- embeddirective.Analyzer.Name: {Analyzer: embeddirective.Analyzer, Enabled: true}, - timeformat.Analyzer.Name: {Analyzer: timeformat.Analyzer, Enabled: true}, +- embeddirective.Analyzer.Name: { +- Analyzer: embeddirective.Analyzer, +- Enabled: true, +- Fix: AddEmbedImport, +- fixesDiagnostic: fixedByImportingEmbed, +- }, - - // gofmt -s suite: - simplifycompositelit.Analyzer.Name: { @@ -82407,6 +91040,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt -type AnalyzerJSON struct { - Name string - Doc string +- URL string - Default bool -} - @@ -82433,7 +91067,7 @@ diff -urN a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/opt -} diff -urN a/gopls/internal/lsp/source/options_test.go b/gopls/internal/lsp/source/options_test.go --- a/gopls/internal/lsp/source/options_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/options_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/options_test.go 1970-01-01 08:00:00 @@ -1,206 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -82641,10 +91275,110 @@ diff -urN a/gopls/internal/lsp/source/options_test.go b/gopls/internal/lsp/sourc - } - } -} +diff -urN a/gopls/internal/lsp/source/origin.go b/gopls/internal/lsp/source/origin.go +--- a/gopls/internal/lsp/source/origin.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/origin.go 1970-01-01 08:00:00 +@@ -1,26 +0,0 @@ +-// Copyright 2023 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.19 +-// +build !go1.19 +- +-package source +- +-import "go/types" +- +-// containsOrigin reports whether the provided object set contains an object +-// with the same origin as the provided obj (which may be a synthetic object +-// created during instantiation). +-func containsOrigin(objSet map[types.Object]bool, obj types.Object) bool { +- if obj == nil { +- return objSet[obj] +- } +- // In Go 1.18, we can't use the types.Var.Origin and types.Func.Origin methods. +- for target := range objSet { +- if target.Pkg() == obj.Pkg() && target.Pos() == obj.Pos() && target.Name() == obj.Name() { +- return true +- } +- } +- return false +-} +diff -urN a/gopls/internal/lsp/source/origin_119.go b/gopls/internal/lsp/source/origin_119.go +--- a/gopls/internal/lsp/source/origin_119.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/origin_119.go 1970-01-01 08:00:00 +@@ -1,33 +0,0 @@ +-// Copyright 2023 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.19 +-// +build go1.19 +- +-package source +- +-import "go/types" +- +-// containsOrigin reports whether the provided object set contains an object +-// with the same origin as the provided obj (which may be a synthetic object +-// created during instantiation). +-func containsOrigin(objSet map[types.Object]bool, obj types.Object) bool { +- objOrigin := origin(obj) +- for target := range objSet { +- if origin(target) == objOrigin { +- return true +- } +- } +- return false +-} +- +-func origin(obj types.Object) types.Object { +- switch obj := obj.(type) { +- case *types.Var: +- return obj.Origin() +- case *types.Func: +- return obj.Origin() +- } +- return obj +-} +diff -urN a/gopls/internal/lsp/source/parsemode_go116.go b/gopls/internal/lsp/source/parsemode_go116.go +--- a/gopls/internal/lsp/source/parsemode_go116.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/parsemode_go116.go 1970-01-01 08:00:00 +@@ -1,13 +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.17 +-// +build !go1.17 +- +-package source +- +-import "go/parser" +- +-// The parser.SkipObjectResolution mode flag is not supported before Go 1.17. +-const SkipObjectResolution parser.Mode = 0 +diff -urN a/gopls/internal/lsp/source/parsemode_go117.go b/gopls/internal/lsp/source/parsemode_go117.go +--- a/gopls/internal/lsp/source/parsemode_go117.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/parsemode_go117.go 1970-01-01 08:00:00 +@@ -1,12 +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.17 +-// +build go1.17 +- +-package source +- +-import "go/parser" +- +-const SkipObjectResolution = parser.SkipObjectResolution diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/references.go --- a/gopls/internal/lsp/source/references.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/references.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,582 +0,0 @@ ++++ b/gopls/internal/lsp/source/references.go 1970-01-01 08:00:00 +@@ -1,692 +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. @@ -82672,11 +91406,11 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - - "golang.org/x/sync/errgroup" - "golang.org/x/tools/go/types/objectpath" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/lsp/source/methodsets" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/event" -) - @@ -82707,7 +91441,7 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ -// definitions before uses) to the object denoted by the identifier at -// the given file/position, searching the entire workspace. -func references(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position, includeDeclaration bool) ([]reference, error) { -- ctx, done := event.Start(ctx, "source.References2") +- ctx, done := event.Start(ctx, "source.references") - defer done() - - // Is the cursor within the package name declaration? @@ -82789,9 +91523,23 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - if err != nil { - return nil, err - } +- +- // Restrict search to workspace packages. +- workspace, err := snapshot.WorkspaceMetadata(ctx) +- if err != nil { +- return nil, err +- } +- workspaceMap := make(map[PackageID]*Metadata, len(workspace)) +- for _, m := range workspace { +- workspaceMap[m.ID] = m +- } +- - for _, rdep := range rdeps { +- if _, ok := workspaceMap[rdep.ID]; !ok { +- continue +- } - for _, uri := range rdep.CompiledGoFiles { -- fh, err := snapshot.GetFile(ctx, uri) +- fh, err := snapshot.ReadFile(ctx, uri) - if err != nil { - return nil, err - } @@ -82818,9 +91566,9 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - // The widest package (possibly a test variant) has the - // greatest number of files and thus we choose it for the - // "internal" references. -- widest := metas[len(metas)-1] +- widest := metas[len(metas)-1] // may include _test.go files - for _, uri := range widest.CompiledGoFiles { -- fh, err := snapshot.GetFile(ctx, uri) +- fh, err := snapshot.ReadFile(ctx, uri) - if err != nil { - return nil, err - } @@ -82851,12 +91599,13 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - // declaration (e.g. because the _test.go files can change the - // meaning of a field or method selection), but the narrower - // package reports the more broadly referenced object. -- pkg, pgf, err := PackageForFile(ctx, snapshot, uri, NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, uri) - if err != nil { - return nil, err - } - - // Find the selected object (declaration or reference). +- // For struct{T}, we choose the field (Def) over the type (Use). - pos, err := pgf.PositionPos(pp) - if err != nil { - return nil, err @@ -82879,14 +91628,14 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - - // nil, error, error.Error, iota, or other built-in? - if obj.Pkg() == nil { -- // For some reason, existing tests require that iota has no references, -- // nor an error. TODO(adonovan): do something more principled. -- if obj.Name() == "iota" { -- return nil, nil -- } -- - return nil, fmt.Errorf("references to builtin %q are not supported", obj.Name()) - } +- if !obj.Pos().IsValid() { +- if obj.Pkg().Path() != "unsafe" { +- bug.Reportf("references: object %v has no position", obj) +- } +- return nil, fmt.Errorf("references to unsafe.%s are not supported", obj.Name()) +- } - - // Find metadata of all packages containing the object's defining file. - // This may include the query pkg, and possibly other variants. @@ -82899,14 +91648,16 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - if len(variants) == 0 { - return nil, fmt.Errorf("no packages for file %q", declURI) // can't happen - } +- // (variants must include ITVs for reverse dependency computation below.) - - // Is object exported? - // If so, compute scope and targets of the global search. - var ( -- globalScope = make(map[PackageID]*Metadata) +- globalScope = make(map[PackageID]*Metadata) // (excludes ITVs) - globalTargets map[PackagePath]map[objectpath.Path]unit +- expansions = make(map[PackageID]unit) // packages that caused search expansion - ) -- // TODO(adonovan): what about generic functions. Need to consider both +- // TODO(adonovan): what about generic functions? Need to consider both - // uninstantiated and instantiated. The latter have no objectpath. Use Origin? - if path, err := objectpath.For(obj); err == nil && obj.Exported() { - pkgPath := variants[0].PkgPath // (all variants have same package path) @@ -82914,6 +91665,47 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - pkgPath: {path: {}}, // primary target - } - +- // Compute set of (non-ITV) workspace packages. +- // We restrict references to this subset. +- workspace, err := snapshot.WorkspaceMetadata(ctx) +- if err != nil { +- return nil, err +- } +- workspaceMap := make(map[PackageID]*Metadata, len(workspace)) +- workspaceIDs := make([]PackageID, 0, len(workspace)) +- for _, m := range workspace { +- workspaceMap[m.ID] = m +- workspaceIDs = append(workspaceIDs, m.ID) +- } +- +- // addRdeps expands the global scope to include the +- // reverse dependencies of the specified package. +- addRdeps := func(id PackageID, transitive bool) error { +- rdeps, err := snapshot.ReverseDependencies(ctx, id, transitive) +- if err != nil { +- return err +- } +- for rdepID, rdep := range rdeps { +- // Skip non-workspace packages. +- // +- // This means we also skip any expansion of the +- // search that might be caused by a non-workspace +- // package, possibly causing us to miss references +- // to the expanded target set from workspace packages. +- // +- // TODO(adonovan): don't skip those expansions. +- // The challenge is how to so without type-checking +- // a lot of non-workspace packages not covered by +- // the initial workspace load. +- if _, ok := workspaceMap[rdepID]; !ok { +- continue +- } +- +- globalScope[rdepID] = rdep +- } +- return nil +- } +- - // How far need we search? - // For package-level objects, we need only search the direct importers. - // For fields and methods, we must search transitively. @@ -82921,15 +91713,11 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - - // The scope is the union of rdeps of each variant. - // (Each set is disjoint so there's no benefit to -- // to combining the metadata graph traversals.) +- // combining the metadata graph traversals.) - for _, m := range variants { -- rdeps, err := snapshot.ReverseDependencies(ctx, m.ID, transitive) -- if err != nil { +- if err := addRdeps(m.ID, transitive); err != nil { - return nil, err - } -- for id, rdep := range rdeps { -- globalScope[id] = rdep -- } - } - - // Is object a method? @@ -82938,8 +91726,11 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - // all methods that correspond to it through interface - // satisfaction, and the scope includes the rdeps of - // the package that declares each corresponding type. +- // +- // 'expansions' records the packages that declared +- // such types. - if recv := effectiveReceiver(obj); recv != nil { -- if err := expandMethodSearch(ctx, snapshot, obj.(*types.Func), recv, globalScope, globalTargets); err != nil { +- if err := expandMethodSearch(ctx, snapshot, workspaceIDs, obj.(*types.Func), recv, addRdeps, globalTargets, expansions); err != nil { - return nil, err - } - } @@ -82973,6 +91764,7 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - var group errgroup.Group - - // Compute local references for each variant. +- // The target objects are identified by (URI, offset). - for _, m := range variants { - // We want the ordinary importable package, - // plus any test-augmented variants, since @@ -82988,7 +91780,73 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - } - m := m - group.Go(func() error { -- return localReferences(ctx, snapshot, declURI, declPosn.Offset, m, report) +- // TODO(adonovan): opt: batch these TypeChecks. +- pkgs, err := snapshot.TypeCheck(ctx, m.ID) +- if err != nil { +- return err +- } +- pkg := pkgs[0] +- +- // Find the declaration of the corresponding +- // object in this package based on (URI, offset). +- pgf, err := pkg.File(declURI) +- if err != nil { +- return err +- } +- pos, err := safetoken.Pos(pgf.Tok, declPosn.Offset) +- if err != nil { +- return err +- } +- objects, _, err := objectsAt(pkg.GetTypesInfo(), pgf.File, pos) +- if err != nil { +- return err // unreachable? (probably caught earlier) +- } +- +- // Report the locations of the declaration(s). +- // TODO(adonovan): what about for corresponding methods? Add tests. +- for _, node := range objects { +- report(mustLocation(pgf, node), true) +- } +- +- // Convert targets map to set. +- targets := make(map[types.Object]bool) +- for obj := range objects { +- targets[obj] = true +- } +- +- return localReferences(pkg, targets, true, report) +- }) +- } +- +- // Also compute local references within packages that declare +- // corresponding methods (see above), which expand the global search. +- // The target objects are identified by (PkgPath, objectpath). +- for id := range expansions { +- id := id +- group.Go(func() error { +- // TODO(adonovan): opt: batch these TypeChecks. +- pkgs, err := snapshot.TypeCheck(ctx, id) +- if err != nil { +- return err +- } +- pkg := pkgs[0] +- +- targets := make(map[types.Object]bool) +- for objpath := range globalTargets[pkg.Metadata().PkgPath] { +- obj, err := objectpath.Object(pkg.GetTypes(), objpath) +- if err != nil { +- // No such object, because it was +- // declared only in the test variant. +- continue +- } +- targets[obj] = true +- } +- +- // Don't include corresponding types or methods +- // since expansions did that already, and we don't +- // want (e.g.) concrete -> interface -> concrete. +- const correspond = false +- return localReferences(pkg, targets, correspond, report) - }) - } - @@ -83017,30 +91875,28 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ -} - -// expandMethodSearch expands the scope and targets of a global search --// for an exported method to include all methods that correspond to --// it through interface satisfaction. +-// for an exported method to include all methods in the workspace +-// that correspond to it through interface satisfaction. +-// +-// Each package that declares a corresponding type is added to +-// expansions so that we can also find local references to the type +-// within the package, which of course requires type checking. +-// +-// The scope is expanded by a sequence of calls (not concurrent) to addRdeps. -// -// recv is the method's effective receiver type, for method-set computations. --func expandMethodSearch(ctx context.Context, snapshot Snapshot, method *types.Func, recv types.Type, scope map[PackageID]*Metadata, targets map[PackagePath]map[objectpath.Path]unit) error { +-func expandMethodSearch(ctx context.Context, snapshot Snapshot, workspaceIDs []PackageID, method *types.Func, recv types.Type, addRdeps func(id PackageID, transitive bool) error, targets map[PackagePath]map[objectpath.Path]unit, expansions map[PackageID]unit) error { - // Compute the method-set fingerprint used as a key to the global search. - key, hasMethods := methodsets.KeyOf(recv) - if !hasMethods { - return bug.Errorf("KeyOf(%s)={} yet %s is a method", recv, method) - } -- metas, err := snapshot.AllMetadata(ctx) -- if err != nil { -- return err -- } -- allIDs := make([]PackageID, 0, len(metas)) -- for _, m := range metas { -- allIDs = append(allIDs, m.ID) -- } - // Search the methodset index of each package in the workspace. -- indexes, err := snapshot.MethodSets(ctx, allIDs...) +- indexes, err := snapshot.MethodSets(ctx, workspaceIDs...) - if err != nil { - return err - } -- var mu sync.Mutex // guards scope and targets +- var mu sync.Mutex // guards addRdeps, targets, expansions - var group errgroup.Group - for i, index := range indexes { - i := i @@ -83052,17 +91908,21 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - return nil - } - -- // Expand global search scope to include rdeps of this pkg. -- rdeps, err := snapshot.ReverseDependencies(ctx, allIDs[i], true) -- if err != nil { -- return err -- } +- // We have discovered one or more corresponding types. +- id := workspaceIDs[i] +- - mu.Lock() - defer mu.Unlock() -- for _, rdep := range rdeps { -- scope[rdep.ID] = rdep +- +- // Expand global search scope to include rdeps of this pkg. +- if err := addRdeps(id, true); err != nil { +- return err - } - +- // Mark this package so that we search within it for +- // local references to the additional types/methods. +- expansions[id] = unit{} +- - // Add each corresponding method the to set of global search targets. - for _, res := range results { - methodPkg := PackagePath(res.PkgPath) @@ -83079,55 +91939,32 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - return group.Wait() -} - --// localReferences reports each reference to the object --// declared at the specified URI/offset within its enclosing package m. --func localReferences(ctx context.Context, snapshot Snapshot, declURI span.URI, declOffset int, m *Metadata, report func(loc protocol.Location, isDecl bool)) error { -- pkgs, err := snapshot.TypeCheck(ctx, m.ID) -- if err != nil { -- return err -- } -- pkg := pkgs[0] // narrowest -- -- // Find declaration of corresponding object -- // in this package based on (URI, offset). -- pgf, err := pkg.File(declURI) -- if err != nil { -- return err -- } -- pos, err := safetoken.Pos(pgf.Tok, declOffset) -- if err != nil { -- return err -- } -- targets, _, err := objectsAt(pkg.GetTypesInfo(), pgf.File, pos) -- if err != nil { -- return err // unreachable? (probably caught earlier) -- } -- -- // Report the locations of the declaration(s). -- // TODO(adonovan): what about for corresponding methods? Add tests. -- for _, node := range targets { -- report(mustLocation(pgf, node), true) -- } -- -- // If we're searching for references to a method, broaden the -- // search to include references to corresponding methods of -- // mutually assignable receiver types. +-// localReferences traverses syntax and reports each reference to one +-// of the target objects, or (if correspond is set) an object that +-// corresponds to one of them via interface satisfaction. +-func localReferences(pkg Package, targets map[types.Object]bool, correspond bool, report func(loc protocol.Location, isDecl bool)) error { +- // If we're searching for references to a method optionally +- // broaden the search to include references to corresponding +- // methods of mutually assignable receiver types. - // (We use a slice, but objectsAt never returns >1 methods.) - var methodRecvs []types.Type - var methodName string // name of an arbitrary target, iff a method -- for obj := range targets { -- if t := effectiveReceiver(obj); t != nil { -- methodRecvs = append(methodRecvs, t) -- methodName = obj.Name() +- if correspond { +- for obj := range targets { +- if t := effectiveReceiver(obj); t != nil { +- methodRecvs = append(methodRecvs, t) +- methodName = obj.Name() +- } - } - } - - // matches reports whether obj either is or corresponds to a target. - // (Correspondence is defined as usual for interface methods.) - matches := func(obj types.Object) bool { -- if targets[obj] != nil { +- if containsOrigin(targets, obj) { - return true -- } else if methodRecvs != nil && obj.Name() == methodName { +- } +- if methodRecvs != nil && obj.Name() == methodName { - if orecv := effectiveReceiver(obj); orecv != nil { - for _, mrecv := range methodRecvs { - if concreteImplementsIntf(orecv, mrecv) { @@ -83195,6 +92032,13 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ - targets[obj] = leaf - } - } else { +- // Note: prior to go1.21, go/types issue #60372 causes the position +- // a field Var T created for struct{*p.T} to be recorded at the +- // start of the field type ("*") not the location of the T. +- // This affects references and other gopls operations (issue #60369). +- // TODO(adonovan): delete this comment when we drop support for go1.20. +- +- // For struct{T}, we prefer the defined field Var over the used TypeName. - obj := info.ObjectOf(leaf) - if obj == nil { - return nil, nil, fmt.Errorf("%w for %q", errNoObjectFound, leaf.Name) @@ -83219,2190 +92063,2223 @@ diff -urN a/gopls/internal/lsp/source/references.go b/gopls/internal/lsp/source/ -// mustLocation reports the location interval a syntax node, -// which must belong to m.File. -// --// Safe for use only by references2 and implementations2. +-// Safe for use only by references and implementations. -func mustLocation(pgf *ParsedGoFile, n ast.Node) protocol.Location { - loc, err := pgf.NodeLocation(n) - if err != nil { -- panic(err) // can't happen in references2 or implementations2 +- panic(err) // can't happen in references or implementations - } - return loc -} -diff -urN a/gopls/internal/lsp/source/rename_check.go b/gopls/internal/lsp/source/rename_check.go ---- a/gopls/internal/lsp/source/rename_check.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/rename_check.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,921 +0,0 @@ +diff -urN a/gopls/internal/lsp/source/rename.go b/gopls/internal/lsp/source/rename.go +--- a/gopls/internal/lsp/source/rename.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/rename.go 1970-01-01 08:00:00 +@@ -1,1277 +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. --// --// Taken from golang.org/x/tools/refactor/rename. - -package source - --// This file defines the conflict-checking portion of the rename operation. +-// TODO(adonovan): -// --// The renamer works on a single package of type-checked syntax, and --// is called in parallel for all necessary packages in the workspace, --// possibly up to the transitive reverse dependencies of the --// declaration. Finally the union of all edits and errors is computed. +-// - method of generic concrete type -> arbitrary instances of same -// --// Renaming one object may entail renaming of others. For example: +-// - make satisfy work across packages. -// --// - An embedded field couples a Var (field) and a TypeName. --// So, renaming either one requires renaming the other. --// If the initial object is an embedded field, we must add its --// TypeName (and its enclosing package) to the renaming set; --// this is easily discovered at the outset. +-// - tests, tests, tests: +-// - play with renamings in the k8s tree. +-// - generics +-// - error cases (e.g. conflicts) +-// - renaming a symbol declared in the module cache +-// (currently proceeds with half of the renaming!) +-// - make sure all tests have both a local and a cross-package analogue. +-// - look at coverage +-// - special cases: embedded fields, interfaces, test variants, +-// function-local things with uppercase names; +-// packages with type errors (currently 'satisfy' rejects them), +-// package with missing imports; -// --// Conversely, if the initial object is a TypeName, we must observe --// whether any of its references (from directly importing packages) --// is coincident with an embedded field Var and, if so, initiate a --// renaming of it. +-// - measure performance in k8s. -// --// - A method of an interface type is coupled to all corresponding --// methods of types that are assigned to the interface (as --// discovered by the 'satisfy' pass). As a matter of usability, we --// require that such renamings be initiated from the interface --// method, not the concrete method. +-// - The original gorename tool assumed well-typedness, but the gopls feature +-// does no such check (which actually makes it much more useful). +-// Audit to ensure it is safe on ill-typed code. +-// +-// - Generics support was no doubt buggy before but incrementalization +-// may have exacerbated it. If the problem were just about objects, +-// defs and uses it would be fairly simple, but type assignability +-// comes into play in the 'satisfy' check for method renamings. +-// De-instantiating Vector[int] to Vector[T] changes its type. +-// We need to come up with a theory for the satisfy check that +-// works with generics, and across packages. We currently have no +-// simple way to pass types between packages (think: objectpath for +-// types), though presumably exportdata could be pressed into service. +-// +-// - FileID-based de-duplication of edits to different URIs for the same file. - -import ( +- "context" +- "errors" - "fmt" - "go/ast" - "go/token" - "go/types" +- "path" - "path/filepath" -- "reflect" +- "regexp" +- "sort" +- "strconv" - "strings" -- "unicode" - +- "golang.org/x/mod/modfile" - "golang.org/x/tools/go/ast/astutil" +- "golang.org/x/tools/go/types/objectpath" +- "golang.org/x/tools/go/types/typeutil" +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/safetoken" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/diff" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/typeparams" - "golang.org/x/tools/refactor/satisfy" -) - --// errorf reports an error (e.g. conflict) and prevents file modification. --func (r *renamer) errorf(pos token.Pos, format string, args ...interface{}) { -- // Conflict error messages in the old gorename tool (whence this -- // logic originated) contain rich information associated with -- // multiple source lines, such as: -- // -- // p/a.go:1:2: renaming "x" to "y" here -- // p/b.go:3:4: \t would cause this reference to "y" -- // p/c.go:5:5: \t to become shadowed by this intervening declaration. -- // -- // Unfortunately LSP provides no means to transmit the -- // structure of this error, so we format the positions briefly -- // using dir/file.go where dir is the base name of the parent -- // directory. +-// A renamer holds state of a single call to renameObj, which renames +-// an object (or several coupled objects) within a single type-checked +-// syntax package. +-type renamer struct { +- pkg Package // the syntax package in which the renaming is applied +- objsToUpdate map[types.Object]bool // records progress of calls to check +- hadConflicts bool +- conflicts []string +- from, to string +- satisfyConstraints map[satisfy.Constraint]bool +- msets typeutil.MethodSetCache +- changeMethods bool +-} - -- var conflict strings.Builder +-// A PrepareItem holds the result of a "prepare rename" operation: +-// the source range and value of a selected identifier. +-type PrepareItem struct { +- Range protocol.Range +- Text string +-} - -- // Add prefix of (truncated) position. -- if pos != token.NoPos { -- // TODO(adonovan): skip position of first error if it is -- // on the same line as the renaming itself. -- posn := safetoken.StartPosition(r.pkg.FileSet(), pos).String() -- segments := strings.Split(filepath.ToSlash(posn), "/") -- if n := len(segments); n > 2 { -- segments = segments[n-2:] -- } -- posn = strings.Join(segments, "/") -- fmt.Fprintf(&conflict, "%s:", posn) +-// PrepareRename searches for a valid renaming at position pp. +-// +-// The returned usererr is intended to be displayed to the user to explain why +-// the prepare fails. Probably we could eliminate the redundancy in returning +-// two errors, but for now this is done defensively. +-func PrepareRename(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position) (_ *PrepareItem, usererr, err error) { +- ctx, done := event.Start(ctx, "source.PrepareRename") +- defer done() - -- if !strings.HasPrefix(format, "\t") { -- conflict.WriteByte(' ') -- } +- // Is the cursor within the package name declaration? +- if pgf, inPackageName, err := parsePackageNameDecl(ctx, snapshot, f, pp); err != nil { +- return nil, err, err +- } else if inPackageName { +- item, err := prepareRenamePackageName(ctx, snapshot, pgf) +- return item, err, err - } - -- fmt.Fprintf(&conflict, format, args...) -- r.conflicts = append(r.conflicts, conflict.String()) --} -- --// check performs safety checks of the renaming of the 'from' object to r.to. --func (r *renamer) check(from types.Object) { -- if r.objsToUpdate[from] { -- return +- // Ordinary (non-package) renaming. +- // +- // Type-check the current package, locate the reference at the position, +- // validate the object, and report its name and range. +- // +- // TODO(adonovan): in all cases below, we return usererr=nil, +- // which means we return (nil, nil) at the protocol +- // layer. This seems like a bug, or at best an exploitation of +- // knowledge of VSCode-specific behavior. Can we avoid that? +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, f.URI()) +- if err != nil { +- return nil, nil, err - } -- r.objsToUpdate[from] = true -- -- // NB: order of conditions is important. -- if from_, ok := from.(*types.PkgName); ok { -- r.checkInFileBlock(from_) -- } else if from_, ok := from.(*types.Label); ok { -- r.checkLabel(from_) -- } else if isPackageLevel(from) { -- r.checkInPackageBlock(from) -- } else if v, ok := from.(*types.Var); ok && v.IsField() { -- r.checkStructField(v) -- } else if f, ok := from.(*types.Func); ok && recv(f) != nil { -- r.checkMethod(f) -- } else if isLocal(from) { -- r.checkInLexicalScope(from) -- } else { -- r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n", -- objectKind(from), from) +- pos, err := pgf.PositionPos(pp) +- if err != nil { +- return nil, nil, err - } +- targets, node, err := objectsAt(pkg.GetTypesInfo(), pgf.File, pos) +- if err != nil { +- return nil, nil, err +- } +- var obj types.Object +- for obj = range targets { +- break // pick one arbitrarily +- } +- if err := checkRenamable(obj); err != nil { +- return nil, nil, err +- } +- rng, err := pgf.NodeRange(node) +- if err != nil { +- return nil, nil, err +- } +- if _, isImport := node.(*ast.ImportSpec); isImport { +- // We're not really renaming the import path. +- rng.End = rng.Start +- } +- return &PrepareItem{ +- Range: rng, +- Text: obj.Name(), +- }, nil, nil -} - --// checkInFileBlock performs safety checks for renames of objects in the file block, --// i.e. imported package names. --func (r *renamer) checkInFileBlock(from *types.PkgName) { -- // Check import name is not "init". -- if r.to == "init" { -- r.errorf(from.Pos(), "%q is not a valid imported package name", r.to) +-func prepareRenamePackageName(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile) (*PrepareItem, error) { +- // Does the client support file renaming? +- fileRenameSupported := false +- for _, op := range snapshot.Options().SupportedResourceOperations { +- if op == protocol.Rename { +- fileRenameSupported = true +- break +- } +- } +- if !fileRenameSupported { +- return nil, errors.New("can't rename package: LSP client does not support file renaming") - } - -- // Check for conflicts between file and package block. -- if prev := from.Pkg().Scope().Lookup(r.to); prev != nil { -- r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", -- objectKind(from), from.Name(), r.to) -- r.errorf(prev.Pos(), "\twith this package member %s", -- objectKind(prev)) -- return // since checkInPackageBlock would report redundant errors +- // Check validity of the metadata for the file's containing package. +- meta, err := NarrowestMetadataForFile(ctx, snapshot, pgf.URI) +- if err != nil { +- return nil, err +- } +- if meta.Name == "main" { +- return nil, fmt.Errorf("can't rename package \"main\"") +- } +- if strings.HasSuffix(string(meta.Name), "_test") { +- return nil, fmt.Errorf("can't rename x_test packages") +- } +- if meta.Module == nil { +- return nil, fmt.Errorf("can't rename package: missing module information for package %q", meta.PkgPath) +- } +- if meta.Module.Path == string(meta.PkgPath) { +- return nil, fmt.Errorf("can't rename package: package path %q is the same as module path %q", meta.PkgPath, meta.Module.Path) - } - -- // Check for conflicts in lexical scope. -- r.checkInLexicalScope(from) +- // Return the location of the package declaration. +- rng, err := pgf.NodeRange(pgf.File.Name) +- if err != nil { +- return nil, err +- } +- return &PrepareItem{ +- Range: rng, +- Text: string(meta.Name), +- }, nil -} - --// checkInPackageBlock performs safety checks for renames of --// func/var/const/type objects in the package block. --func (r *renamer) checkInPackageBlock(from types.Object) { -- // Check that there are no references to the name from another -- // package if the renaming would make it unexported. -- if typ := r.pkg.GetTypes(); typ != from.Pkg() && ast.IsExported(r.from) && !ast.IsExported(r.to) { -- if id := someUse(r.pkg.GetTypesInfo(), from); id != nil { -- r.checkExport(id, typ, from) +-func checkRenamable(obj types.Object) error { +- switch obj := obj.(type) { +- case *types.Var: +- if obj.Embedded() { +- return fmt.Errorf("can't rename embedded fields: rename the type directly or name the field") - } +- case *types.Builtin, *types.Nil: +- return fmt.Errorf("%s is built in and cannot be renamed", obj.Name()) - } -- -- // Check that in the package block, "init" is a function, and never referenced. -- if r.to == "init" { -- kind := objectKind(from) -- if kind == "func" { -- // Reject if intra-package references to it exist. -- for id, obj := range r.pkg.GetTypesInfo().Uses { -- if obj == from { -- r.errorf(from.Pos(), -- "renaming this func %q to %q would make it a package initializer", -- from.Name(), r.to) -- r.errorf(id.Pos(), "\tbut references to it exist") -- break -- } -- } -- } else { -- r.errorf(from.Pos(), "you cannot have a %s at package level named %q", -- kind, r.to) -- } +- if obj.Pkg() == nil || obj.Pkg().Path() == "unsafe" { +- // e.g. error.Error, unsafe.Pointer +- return fmt.Errorf("%s is built in and cannot be renamed", obj.Name()) - } -- -- // Check for conflicts between package block and all file blocks. -- for _, f := range r.pkg.GetSyntax() { -- fileScope := r.pkg.GetTypesInfo().Scopes[f] -- b, prev := fileScope.LookupParent(r.to, token.NoPos) -- if b == fileScope { -- r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", objectKind(from), from.Name(), r.to) -- var prevPos token.Pos -- if prev != nil { -- prevPos = prev.Pos() -- } -- r.errorf(prevPos, "\twith this %s", objectKind(prev)) -- return // since checkInPackageBlock would report redundant errors -- } +- if obj.Name() == "_" { +- return errors.New("can't rename \"_\"") - } +- return nil +-} - -- // Check for conflicts in lexical scope. -- r.checkInLexicalScope(from) --} +-// Rename returns a map of TextEdits for each file modified when renaming a +-// given identifier within a package and a boolean value of true for renaming +-// package and false otherwise. +-func Rename(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position, newName string) (map[span.URI][]protocol.TextEdit, bool, error) { +- ctx, done := event.Start(ctx, "source.Rename") +- defer done() - --// checkInLexicalScope performs safety checks that a renaming does not --// change the lexical reference structure of the specified package. --// --// For objects in lexical scope, there are three kinds of conflicts: --// same-, sub-, and super-block conflicts. We will illustrate all three --// using this example: --// --// var x int --// var z int --// --// func f(y int) { --// print(x) --// print(y) --// } --// --// Renaming x to z encounters a "same-block conflict", because an object --// with the new name already exists, defined in the same lexical block --// as the old object. --// --// Renaming x to y encounters a "sub-block conflict", because there exists --// a reference to x from within (what would become) a hole in its scope. --// The definition of y in an (inner) sub-block would cast a shadow in --// the scope of the renamed variable. --// --// Renaming y to x encounters a "super-block conflict". This is the --// converse situation: there is an existing definition of the new name --// (x) in an (enclosing) super-block, and the renaming would create a --// hole in its scope, within which there exist references to it. The --// new name shadows the existing definition of x in the super-block. --// --// Removing the old name (and all references to it) is always safe, and --// requires no checks. --func (r *renamer) checkInLexicalScope(from types.Object) { -- b := from.Parent() // the block defining the 'from' object -- if b != nil { -- toBlock, to := b.LookupParent(r.to, from.Parent().End()) -- if toBlock == b { -- // same-block conflict -- r.errorf(from.Pos(), "renaming this %s %q to %q", -- objectKind(from), from.Name(), r.to) -- r.errorf(to.Pos(), "\tconflicts with %s in same block", -- objectKind(to)) -- return -- } else if toBlock != nil { -- // Check for super-block conflict. -- // The name r.to is defined in a superblock. -- // Is that name referenced from within this block? -- forEachLexicalRef(r.pkg, to, func(id *ast.Ident, block *types.Scope) bool { -- _, obj := block.LookupParent(from.Name(), id.Pos()) -- if obj == from { -- // super-block conflict -- r.errorf(from.Pos(), "renaming this %s %q to %q", -- objectKind(from), from.Name(), r.to) -- r.errorf(id.Pos(), "\twould shadow this reference") -- r.errorf(to.Pos(), "\tto the %s declared here", -- objectKind(to)) -- return false // stop -- } -- return true -- }) -- } +- if !isValidIdentifier(newName) { +- return nil, false, fmt.Errorf("invalid identifier to rename: %q", newName) - } -- // Check for sub-block conflict. -- // Is there an intervening definition of r.to between -- // the block defining 'from' and some reference to it? -- forEachLexicalRef(r.pkg, from, func(id *ast.Ident, block *types.Scope) bool { -- // Find the block that defines the found reference. -- // It may be an ancestor. -- fromBlock, _ := block.LookupParent(from.Name(), id.Pos()) -- // See what r.to would resolve to in the same scope. -- toBlock, to := block.LookupParent(r.to, id.Pos()) -- if to != nil { -- // sub-block conflict -- if deeper(toBlock, fromBlock) { -- r.errorf(from.Pos(), "renaming this %s %q to %q", -- objectKind(from), from.Name(), r.to) -- r.errorf(id.Pos(), "\twould cause this reference to become shadowed") -- r.errorf(to.Pos(), "\tby this intervening %s definition", -- objectKind(to)) -- return false // stop -- } -- } -- return true -- }) - -- // Renaming a type that is used as an embedded field -- // requires renaming the field too. e.g. -- // type T int // if we rename this to U.. -- // var s struct {T} -- // print(s.T) // ...this must change too -- if _, ok := from.(*types.TypeName); ok { -- for id, obj := range r.pkg.GetTypesInfo().Uses { -- if obj == from { -- if field := r.pkg.GetTypesInfo().Defs[id]; field != nil { -- r.check(field) -- } -- } -- } +- // Cursor within package name declaration? +- _, inPackageName, err := parsePackageNameDecl(ctx, snapshot, f, pp) +- if err != nil { +- return nil, false, err - } --} - --// deeper reports whether block x is lexically deeper than y. --func deeper(x, y *types.Scope) bool { -- if x == y || x == nil { -- return false -- } else if y == nil { -- return true +- var editMap map[span.URI][]diff.Edit +- if inPackageName { +- editMap, err = renamePackageName(ctx, snapshot, f, PackageName(newName)) - } else { -- return deeper(x.Parent(), y.Parent()) +- editMap, err = renameOrdinary(ctx, snapshot, f, pp, newName) +- } +- if err != nil { +- return nil, false, err - } --} - --// forEachLexicalRef calls fn(id, block) for each identifier id in package --// pkg that is a reference to obj in lexical scope. block is the --// lexical block enclosing the reference. If fn returns false the --// iteration is terminated and findLexicalRefs returns false. --func forEachLexicalRef(pkg Package, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool { -- ok := true -- var stack []ast.Node +- // Convert edits to protocol form. +- result := make(map[span.URI][]protocol.TextEdit) +- for uri, edits := range editMap { +- // Sort and de-duplicate edits. +- // +- // Overlapping edits may arise in local renamings (due +- // to type switch implicits) and globals ones (due to +- // processing multiple package variants). +- // +- // We assume renaming produces diffs that are all +- // replacements (no adjacent insertions that might +- // become reordered) and that are either identical or +- // non-overlapping. +- diff.SortEdits(edits) +- filtered := edits[:0] +- for i, edit := range edits { +- if i == 0 || edit != filtered[len(filtered)-1] { +- filtered = append(filtered, edit) +- } +- } +- edits = filtered - -- var visit func(n ast.Node) bool -- visit = func(n ast.Node) bool { -- if n == nil { -- stack = stack[:len(stack)-1] // pop -- return false +- // TODO(adonovan): the logic above handles repeat edits to the +- // same file URI (e.g. as a member of package p and p_test) but +- // is not sufficient to handle file-system level aliasing arising +- // from symbolic or hard links. For that, we should use a +- // robustio-FileID-keyed map. +- // See https://go.dev/cl/457615 for example. +- // This really occurs in practice, e.g. kubernetes has +- // vendor/k8s.io/kubectl -> ../../staging/src/k8s.io/kubectl. +- fh, err := snapshot.ReadFile(ctx, uri) +- if err != nil { +- return nil, false, err - } -- if !ok { -- return false // bail out +- data, err := fh.Content() +- if err != nil { +- return nil, false, err +- } +- m := protocol.NewMapper(uri, data) +- protocolEdits, err := ToProtocolEdits(m, edits) +- if err != nil { +- return nil, false, err - } +- result[uri] = protocolEdits +- } - -- stack = append(stack, n) // push -- switch n := n.(type) { -- case *ast.Ident: -- if pkg.GetTypesInfo().Uses[n] == obj { -- block := enclosingBlock(pkg.GetTypesInfo(), stack) -- if !fn(n, block) { -- ok = false -- } -- } -- return visit(nil) // pop stack +- return result, inPackageName, nil +-} - -- case *ast.SelectorExpr: -- // don't visit n.Sel -- ast.Inspect(n.X, visit) -- return visit(nil) // pop stack, don't descend +-// renameOrdinary renames an ordinary (non-package) name throughout the workspace. +-func renameOrdinary(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position, newName string) (map[span.URI][]diff.Edit, error) { +- // Type-check the referring package and locate the object(s). +- // +- // Unlike NarrowestPackageForFile, this operation prefers the +- // widest variant as, for non-exported identifiers, it is the +- // only package we need. (In case you're wondering why +- // 'references' doesn't also want the widest variant: it +- // computes the union across all variants.) +- var targets map[types.Object]ast.Node +- var pkg Package +- { +- metas, err := snapshot.MetadataForFile(ctx, f.URI()) +- if err != nil { +- return nil, err +- } +- RemoveIntermediateTestVariants(&metas) +- if len(metas) == 0 { +- return nil, fmt.Errorf("no package metadata for file %s", f.URI()) +- } +- widest := metas[len(metas)-1] // widest variant may include _test.go files +- pkgs, err := snapshot.TypeCheck(ctx, widest.ID) +- if err != nil { +- return nil, err +- } +- pkg = pkgs[0] +- pgf, err := pkg.File(f.URI()) +- if err != nil { +- return nil, err // "can't happen" +- } +- pos, err := pgf.PositionPos(pp) +- if err != nil { +- return nil, err +- } +- objects, _, err := objectsAt(pkg.GetTypesInfo(), pgf.File, pos) +- if err != nil { +- return nil, err +- } +- targets = objects +- } - -- case *ast.CompositeLit: -- // Handle recursion ourselves for struct literals -- // so we don't visit field identifiers. -- tv, ok := pkg.GetTypesInfo().Types[n] -- if !ok { -- return visit(nil) // pop stack, don't descend +- // Pick a representative object arbitrarily. +- // (All share the same name, pos, and kind.) +- var obj types.Object +- for obj = range targets { +- break +- } +- if obj.Name() == newName { +- return nil, fmt.Errorf("old and new names are the same: %s", newName) +- } +- if err := checkRenamable(obj); err != nil { +- return nil, err +- } +- +- // Find objectpath, if object is exported ("" otherwise). +- var declObjPath objectpath.Path +- if obj.Exported() { +- // objectpath.For requires the origin of a generic function or type, not an +- // instantiation (a bug?). Unfortunately we can't call Func.Origin as this +- // is not available in go/types@go1.18. So we take a scenic route. +- // +- // Note that unlike Funcs, TypeNames are always canonical (they are "left" +- // of the type parameters, unlike methods). +- switch obj.(type) { // avoid "obj :=" since cases reassign the var +- case *types.TypeName: +- if _, ok := obj.Type().(*typeparams.TypeParam); ok { +- // As with capitalized function parameters below, type parameters are +- // local. +- goto skipObjectPath - } -- if _, ok := Deref(tv.Type).Underlying().(*types.Struct); ok { -- if n.Type != nil { -- ast.Inspect(n.Type, visit) -- } -- for _, elt := range n.Elts { -- if kv, ok := elt.(*ast.KeyValueExpr); ok { -- ast.Inspect(kv.Value, visit) -- } else { -- ast.Inspect(elt, visit) -- } -- } -- return visit(nil) // pop stack, don't descend +- case *types.Func: +- obj = funcOrigin(obj.(*types.Func)) +- case *types.Var: +- // TODO(adonovan): do vars need the origin treatment too? (issue #58462) +- +- // Function parameter and result vars that are (unusually) +- // capitalized are technically exported, even though they +- // cannot be referenced, because they may affect downstream +- // error messages. But we can safely treat them as local. +- // +- // This is not merely an optimization: the renameExported +- // operation gets confused by such vars. It finds them from +- // objectpath, the classifies them as local vars, but as +- // they came from export data they lack syntax and the +- // correct scope tree (issue #61294). +- if !obj.(*types.Var).IsField() && !isPackageLevel(obj) { +- goto skipObjectPath - } - } -- return true +- if path, err := objectpath.For(obj); err == nil { +- declObjPath = path +- } +- skipObjectPath: - } - -- for _, f := range pkg.GetSyntax() { -- ast.Inspect(f, visit) -- if len(stack) != 0 { -- panic(stack) -- } -- if !ok { -- break +- // Nonexported? Search locally. +- if declObjPath == "" { +- var objects []types.Object +- for obj := range targets { +- objects = append(objects, obj) - } +- editMap, _, err := renameObjects(ctx, snapshot, newName, pkg, objects...) +- return editMap, err - } -- return ok --} - --// enclosingBlock returns the innermost block enclosing the specified --// AST node, specified in the form of a path from the root of the file, --// [file...n]. --func enclosingBlock(info *types.Info, stack []ast.Node) *types.Scope { -- for i := range stack { -- n := stack[len(stack)-1-i] -- // For some reason, go/types always associates a -- // function's scope with its FuncType. -- // TODO(adonovan): feature or a bug? -- switch f := n.(type) { -- case *ast.FuncDecl: -- n = f.Type -- case *ast.FuncLit: -- n = f.Type +- // Exported: search globally. +- // +- // For exported package-level var/const/func/type objects, the +- // search scope is just the direct importers. +- // +- // For exported fields and methods, the scope is the +- // transitive rdeps. (The exportedness of the field's struct +- // or method's receiver is irrelevant.) +- transitive := false +- switch obj.(type) { +- case *types.TypeName: +- // Renaming an exported package-level type +- // requires us to inspect all transitive rdeps +- // in the event that the type is embedded. +- // +- // TODO(adonovan): opt: this is conservative +- // but inefficient. Instead, expand the scope +- // of the search only if we actually encounter +- // an embedding of the type, and only then to +- // the rdeps of the embedding package. +- if obj.Parent() == obj.Pkg().Scope() { +- transitive = true - } -- if b := info.Scopes[n]; b != nil { -- return b +- +- case *types.Var: +- if obj.(*types.Var).IsField() { +- transitive = true // field +- } +- +- // TODO(adonovan): opt: process only packages that +- // contain a reference (xrefs) to the target field. +- +- case *types.Func: +- if obj.Type().(*types.Signature).Recv() != nil { +- transitive = true // method - } +- +- // It's tempting to optimize by skipping +- // packages that don't contain a reference to +- // the method in the xrefs index, but we still +- // need to apply the satisfy check to those +- // packages to find assignment statements that +- // might expands the scope of the renaming. - } -- panic("no Scope for *ast.File") --} - --func (r *renamer) checkLabel(label *types.Label) { -- // Check there are no identical labels in the function's label block. -- // (Label blocks don't nest, so this is easy.) -- if prev := label.Parent().Lookup(r.to); prev != nil { -- r.errorf(label.Pos(), "renaming this label %q to %q", label.Name(), prev.Name()) -- r.errorf(prev.Pos(), "\twould conflict with this one") +- // Type-check all the packages to inspect. +- declURI := span.URIFromPath(pkg.FileSet().File(obj.Pos()).Name()) +- pkgs, err := typeCheckReverseDependencies(ctx, snapshot, declURI, transitive) +- if err != nil { +- return nil, err - } --} - --// checkStructField checks that the field renaming will not cause --// conflicts at its declaration, or ambiguity or changes to any selection. --func (r *renamer) checkStructField(from *types.Var) { -- // Check that the struct declaration is free of field conflicts, -- // and field/method conflicts. +- // Apply the renaming to the (initial) object. +- declPkgPath := PackagePath(obj.Pkg().Path()) +- return renameExported(ctx, snapshot, pkgs, declPkgPath, declObjPath, newName) +-} - -- // go/types offers no easy way to get from a field (or interface -- // method) to its declaring struct (or interface), so we must -- // ascend the AST. -- pgf, ok := enclosingFile(r.pkg, from.Pos()) -- if !ok { -- return // not declared by syntax of this package +-// funcOrigin is a go1.18-portable implementation of (*types.Func).Origin. +-func funcOrigin(fn *types.Func) *types.Func { +- // Method? +- if fn.Type().(*types.Signature).Recv() != nil { +- return typeparams.OriginMethod(fn) - } -- path, _ := astutil.PathEnclosingInterval(pgf.File, from.Pos(), from.Pos()) -- // path matches this pattern: -- // [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File] - -- // Ascend to FieldList. -- var i int -- for { -- if _, ok := path[i].(*ast.FieldList); ok { -- break -- } -- i++ +- // Package-level function? +- // (Assume the origin has the same position.) +- gen := fn.Pkg().Scope().Lookup(fn.Name()) +- if gen != nil && gen.Pos() == fn.Pos() { +- return gen.(*types.Func) - } -- i++ -- tStruct := path[i].(*ast.StructType) -- i++ -- // Ascend past parens (unlikely). -- for { -- _, ok := path[i].(*ast.ParenExpr) -- if !ok { -- break -- } -- i++ +- +- return fn +-} +- +-// typeCheckReverseDependencies returns the type-checked packages for +-// the reverse dependencies of all packages variants containing +-// file declURI. The packages are in some topological order. +-// +-// It includes all variants (even intermediate test variants) for the +-// purposes of computing reverse dependencies, but discards ITVs for +-// the actual renaming work. +-// +-// (This neglects obscure edge cases where a _test.go file changes the +-// selectors used only in an ITV, but life is short. Also sin must be +-// punished.) +-func typeCheckReverseDependencies(ctx context.Context, snapshot Snapshot, declURI span.URI, transitive bool) ([]Package, error) { +- variants, err := snapshot.MetadataForFile(ctx, declURI) +- if err != nil { +- return nil, err - } -- if spec, ok := path[i].(*ast.TypeSpec); ok { -- // This struct is also a named type. -- // We must check for direct (non-promoted) field/field -- // and method/field conflicts. -- named := r.pkg.GetTypesInfo().Defs[spec.Name].Type() -- prev, indices, _ := types.LookupFieldOrMethod(named, true, r.pkg.GetTypes(), r.to) -- if len(indices) == 1 { -- r.errorf(from.Pos(), "renaming this field %q to %q", -- from.Name(), r.to) -- r.errorf(prev.Pos(), "\twould conflict with this %s", -- objectKind(prev)) -- return // skip checkSelections to avoid redundant errors +- // variants must include ITVs for the reverse dependency +- // computation, but they are filtered out before we typecheck. +- allRdeps := make(map[PackageID]*Metadata) +- for _, variant := range variants { +- rdeps, err := snapshot.ReverseDependencies(ctx, variant.ID, transitive) +- if err != nil { +- return nil, err - } -- } else { -- // This struct is not a named type. -- // We need only check for direct (non-promoted) field/field conflicts. -- T := r.pkg.GetTypesInfo().Types[tStruct].Type.Underlying().(*types.Struct) -- for i := 0; i < T.NumFields(); i++ { -- if prev := T.Field(i); prev.Name() == r.to { -- r.errorf(from.Pos(), "renaming this field %q to %q", -- from.Name(), r.to) -- r.errorf(prev.Pos(), "\twould conflict with this field") -- return // skip checkSelections to avoid redundant errors -- } +- allRdeps[variant.ID] = variant // include self +- for id, meta := range rdeps { +- allRdeps[id] = meta - } - } -- -- // Renaming an anonymous field requires renaming the type too. e.g. -- // print(s.T) // if we rename T to U, -- // type T int // this and -- // var s struct {T} // this must change too. -- if from.Anonymous() { -- if named, ok := from.Type().(*types.Named); ok { -- r.check(named.Obj()) -- } else if named, ok := Deref(from.Type()).(*types.Named); ok { -- r.check(named.Obj()) +- var ids []PackageID +- for id, meta := range allRdeps { +- if meta.IsIntermediateTestVariant() { +- continue - } +- ids = append(ids, id) - } - -- // Check integrity of existing (field and method) selections. -- r.checkSelections(from) +- // Sort the packages into some topological order of the +- // (unfiltered) metadata graph. +- SortPostOrder(snapshot, ids) +- +- // Dependencies must be visited first since they can expand +- // the search set. Ideally we would process the (filtered) set +- // of packages in the parallel postorder of the snapshot's +- // (unfiltered) metadata graph, but this is quite tricky +- // without a good graph abstraction. +- // +- // For now, we visit packages sequentially in order of +- // ascending height, like an inverted breadth-first search. +- // +- // Type checking is by far the dominant cost, so +- // overlapping it with renaming may not be worthwhile. +- return snapshot.TypeCheck(ctx, ids...) -} - --// checkSelections checks that all uses and selections that resolve to --// the specified object would continue to do so after the renaming. --func (r *renamer) checkSelections(from types.Object) { -- pkg := r.pkg -- typ := pkg.GetTypes() -- { -- if id := someUse(pkg.GetTypesInfo(), from); id != nil { -- if !r.checkExport(id, typ, from) { -- return +-// SortPostOrder sorts the IDs so that if x depends on y, then y appears before x. +-func SortPostOrder(meta MetadataSource, ids []PackageID) { +- postorder := make(map[PackageID]int) +- order := 0 +- var visit func(PackageID) +- visit = func(id PackageID) { +- if _, ok := postorder[id]; !ok { +- postorder[id] = -1 // break recursion +- if m := meta.Metadata(id); m != nil { +- for _, depID := range m.DepsByPkgPath { +- visit(depID) +- } - } +- order++ +- postorder[id] = order - } +- } +- for _, id := range ids { +- visit(id) +- } +- sort.Slice(ids, func(i, j int) bool { +- return postorder[ids[i]] < postorder[ids[j]] +- }) +-} - -- for syntax, sel := range pkg.GetTypesInfo().Selections { -- // There may be extant selections of only the old -- // name or only the new name, so we must check both. -- // (If neither, the renaming is sound.) -- // -- // In both cases, we wish to compare the lengths -- // of the implicit field path (Selection.Index) -- // to see if the renaming would change it. -- // -- // If a selection that resolves to 'from', when renamed, -- // would yield a path of the same or shorter length, -- // this indicates ambiguity or a changed referent, -- // analogous to same- or sub-block lexical conflict. -- // -- // If a selection using the name 'to' would -- // yield a path of the same or shorter length, -- // this indicates ambiguity or shadowing, -- // analogous to same- or super-block lexical conflict. -- -- // TODO(adonovan): fix: derive from Types[syntax.X].Mode -- // TODO(adonovan): test with pointer, value, addressable value. -- isAddressable := true +-// renameExported renames the object denoted by (pkgPath, objPath) +-// within the specified packages, along with any other objects that +-// must be renamed as a consequence. The slice of packages must be +-// topologically ordered. +-func renameExported(ctx context.Context, snapshot Snapshot, pkgs []Package, declPkgPath PackagePath, declObjPath objectpath.Path, newName string) (map[span.URI][]diff.Edit, error) { - -- if sel.Obj() == from { -- if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil { -- // Renaming this existing selection of -- // 'from' may block access to an existing -- // type member named 'to'. -- delta := len(indices) - len(sel.Index()) -- if delta > 0 { -- continue // no ambiguity -- } -- r.selectionConflict(from, delta, syntax, obj) -- return -- } -- } else if sel.Obj().Name() == r.to { -- if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from { -- // Renaming 'from' may cause this existing -- // selection of the name 'to' to change -- // its meaning. -- delta := len(indices) - len(sel.Index()) -- if delta > 0 { -- continue // no ambiguity -- } -- r.selectionConflict(from, -delta, syntax, sel.Obj()) -- return -- } -- } -- } +- // A target is a name for an object that is stable across types.Packages. +- type target struct { +- pkg PackagePath +- obj objectpath.Path - } --} -- --func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) { -- r.errorf(from.Pos(), "renaming this %s %q to %q", -- objectKind(from), from.Name(), r.to) - -- switch { -- case delta < 0: -- // analogous to sub-block conflict -- r.errorf(syntax.Sel.Pos(), -- "\twould change the referent of this selection") -- r.errorf(obj.Pos(), "\tof this %s", objectKind(obj)) -- case delta == 0: -- // analogous to same-block conflict -- r.errorf(syntax.Sel.Pos(), -- "\twould make this reference ambiguous") -- r.errorf(obj.Pos(), "\twith this %s", objectKind(obj)) -- case delta > 0: -- // analogous to super-block conflict -- r.errorf(syntax.Sel.Pos(), -- "\twould shadow this selection") -- r.errorf(obj.Pos(), "\tof the %s declared here", -- objectKind(obj)) -- } --} +- // Populate the initial set of target objects. +- // This set may grow as we discover the consequences of each renaming. +- // +- // TODO(adonovan): strictly, each cone of reverse dependencies +- // of a single variant should have its own target map that +- // monotonically expands as we go up the import graph, because +- // declarations in test files can alter the set of +- // package-level names and change the meaning of field and +- // method selectors. So if we parallelize the graph +- // visitation (see above), we should also compute the targets +- // as a union of dependencies. +- // +- // Or we could decide that the logic below is fast enough not +- // to need parallelism. In small measurements so far the +- // type-checking step is about 95% and the renaming only 5%. +- targets := map[target]bool{{declPkgPath, declObjPath}: true} - --// checkMethod performs safety checks for renaming a method. --// There are three hazards: --// - declaration conflicts --// - selection ambiguity/changes --// - entailed renamings of assignable concrete/interface types. --// --// We reject renamings initiated at concrete methods if it would --// change the assignability relation. For renamings of abstract --// methods, we rename all methods transitively coupled to it via --// assignability. --func (r *renamer) checkMethod(from *types.Func) { -- // e.g. error.Error -- if from.Pkg() == nil { -- r.errorf(from.Pos(), "you cannot rename built-in method %s", from) -- return -- } +- // Apply the renaming operation to each package. +- allEdits := make(map[span.URI][]diff.Edit) +- for _, pkg := range pkgs { - -- // ASSIGNABILITY: We reject renamings of concrete methods that -- // would break a 'satisfy' constraint; but renamings of abstract -- // methods are allowed to proceed, and we rename affected -- // concrete and abstract methods as necessary. It is the -- // initial method that determines the policy. +- // Resolved target objects within package pkg. +- var objects []types.Object +- for t := range targets { +- p := pkg.DependencyTypes(t.pkg) +- if p == nil { +- continue // indirect dependency of no consequence +- } +- obj, err := objectpath.Object(p, t.obj) +- if err != nil { +- // Possibly a method or an unexported type +- // that is not reachable through export data? +- // See https://github.com/golang/go/issues/60789. +- // +- // TODO(adonovan): it seems unsatisfactory that Object +- // should return an error for a "valid" path. Perhaps +- // we should define such paths as invalid and make +- // objectpath.For compute reachability? +- // Would that be a compatible change? +- continue +- } +- objects = append(objects, obj) +- } +- if len(objects) == 0 { +- continue // no targets of consequence to this package +- } - -- // Check for conflict at point of declaration. -- // Check to ensure preservation of assignability requirements. -- R := recv(from).Type() -- if types.IsInterface(R) { -- // Abstract method +- // Apply the renaming. +- editMap, moreObjects, err := renameObjects(ctx, snapshot, newName, pkg, objects...) +- if err != nil { +- return nil, err +- } - -- // declaration -- prev, _, _ := types.LookupFieldOrMethod(R, false, from.Pkg(), r.to) -- if prev != nil { -- r.errorf(from.Pos(), "renaming this interface method %q to %q", -- from.Name(), r.to) -- r.errorf(prev.Pos(), "\twould conflict with this method") -- return +- // It is safe to concatenate the edits as they are non-overlapping +- // (or identical, in which case they will be de-duped by Rename). +- for uri, edits := range editMap { +- allEdits[uri] = append(allEdits[uri], edits...) - } - -- // Check all interfaces that embed this one for -- // declaration conflicts too. -- { -- // Start with named interface types (better errors) -- for _, obj := range r.pkg.GetTypesInfo().Defs { -- if obj, ok := obj.(*types.TypeName); ok && types.IsInterface(obj.Type()) { -- f, _, _ := types.LookupFieldOrMethod( -- obj.Type(), false, from.Pkg(), from.Name()) -- if f == nil { -- continue -- } -- t, _, _ := types.LookupFieldOrMethod( -- obj.Type(), false, from.Pkg(), r.to) -- if t == nil { -- continue -- } -- r.errorf(from.Pos(), "renaming this interface method %q to %q", -- from.Name(), r.to) -- r.errorf(t.Pos(), "\twould conflict with this method") -- r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name()) -- } +- // Expand the search set? +- for obj := range moreObjects { +- objpath, err := objectpath.For(obj) +- if err != nil { +- continue // not exported - } +- target := target{PackagePath(obj.Pkg().Path()), objpath} +- targets[target] = true - -- // Now look at all literal interface types (includes named ones again). -- for e, tv := range r.pkg.GetTypesInfo().Types { -- if e, ok := e.(*ast.InterfaceType); ok { -- _ = e -- _ = tv.Type.(*types.Interface) -- // TODO(adonovan): implement same check as above. -- } -- } +- // TODO(adonovan): methods requires dynamic +- // programming of the product targets x +- // packages as any package might add a new +- // target (from a foward dep) as a +- // consequence, and any target might imply a +- // new set of rdeps. See golang/go#58461. - } +- } - -- // assignability -- // -- // Find the set of concrete or abstract methods directly -- // coupled to abstract method 'from' by some -- // satisfy.Constraint, and rename them too. -- for key := range r.satisfy() { -- // key = (lhs, rhs) where lhs is always an interface. +- return allEdits, nil +-} - -- lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) -- if lsel == nil { -- continue -- } -- rmethods := r.msets.MethodSet(key.RHS) -- rsel := rmethods.Lookup(from.Pkg(), from.Name()) -- if rsel == nil { -- continue -- } +-// renamePackageName renames package declarations, imports, and go.mod files. +-func renamePackageName(ctx context.Context, s Snapshot, f FileHandle, newName PackageName) (map[span.URI][]diff.Edit, error) { +- // Rename the package decl and all imports. +- renamingEdits, err := renamePackage(ctx, s, f, newName) +- if err != nil { +- return nil, err +- } - -- // If both sides have a method of this name, -- // and one of them is m, the other must be coupled. -- var coupled *types.Func -- switch from { -- case lsel.Obj(): -- coupled = rsel.Obj().(*types.Func) -- case rsel.Obj(): -- coupled = lsel.Obj().(*types.Func) -- default: -- continue -- } +- // Update the last component of the file's enclosing directory. +- oldBase := filepath.Dir(f.URI().Filename()) +- newPkgDir := filepath.Join(filepath.Dir(oldBase), string(newName)) - -- // We must treat concrete-to-interface -- // constraints like an implicit selection C.f of -- // each interface method I.f, and check that the -- // renaming leaves the selection unchanged and -- // unambiguous. -- // -- // Fun fact: the implicit selection of C.f -- // type I interface{f()} -- // type C struct{I} -- // func (C) g() -- // var _ I = C{} // here -- // yields abstract method I.f. This can make error -- // messages less than obvious. -- // -- if !types.IsInterface(key.RHS) { -- // The logic below was derived from checkSelections. +- // Update any affected replace directives in go.mod files. +- // TODO(adonovan): extract into its own function. +- // +- // Get all workspace modules. +- // TODO(adonovan): should this operate on all go.mod files, +- // irrespective of whether they are included in the workspace? +- modFiles := s.ModFiles() +- for _, m := range modFiles { +- fh, err := s.ReadFile(ctx, m) +- if err != nil { +- return nil, err +- } +- pm, err := s.ParseMod(ctx, fh) +- if err != nil { +- return nil, err +- } - -- rtosel := rmethods.Lookup(from.Pkg(), r.to) -- if rtosel != nil { -- rto := rtosel.Obj().(*types.Func) -- delta := len(rsel.Index()) - len(rtosel.Index()) -- if delta < 0 { -- continue // no ambiguity -- } +- modFileDir := filepath.Dir(pm.URI.Filename()) +- affectedReplaces := []*modfile.Replace{} - -- // TODO(adonovan): record the constraint's position. -- keyPos := token.NoPos +- // Check if any replace directives need to be fixed +- for _, r := range pm.File.Replace { +- if !strings.HasPrefix(r.New.Path, "/") && !strings.HasPrefix(r.New.Path, "./") && !strings.HasPrefix(r.New.Path, "../") { +- continue +- } - -- r.errorf(from.Pos(), "renaming this method %q to %q", -- from.Name(), r.to) -- if delta == 0 { -- // analogous to same-block conflict -- r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous", -- r.to, key.RHS, key.LHS) -- r.errorf(rto.Pos(), "\twith (%s).%s", -- recv(rto).Type(), r.to) -- } else { -- // analogous to super-block conflict -- r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s", -- r.to, key.RHS, key.LHS) -- r.errorf(coupled.Pos(), "\tfrom (%s).%s", -- recv(coupled).Type(), r.to) -- r.errorf(rto.Pos(), "\tto (%s).%s", -- recv(rto).Type(), r.to) -- } -- return // one error is enough -- } +- replacedPath := r.New.Path +- if strings.HasPrefix(r.New.Path, "./") || strings.HasPrefix(r.New.Path, "../") { +- replacedPath = filepath.Join(modFileDir, r.New.Path) - } - -- if !r.changeMethods { -- // This should be unreachable. -- r.errorf(from.Pos(), "internal error: during renaming of abstract method %s", from) -- r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s", coupled) -- r.errorf(from.Pos(), "\tPlease file a bug report") -- return +- // TODO: Is there a risk of converting a '\' delimited replacement to a '/' delimited replacement? +- if !strings.HasPrefix(filepath.ToSlash(replacedPath)+"/", filepath.ToSlash(oldBase)+"/") { +- continue // not affected by the package renaming - } - -- // Rename the coupled method to preserve assignability. -- r.check(coupled) +- affectedReplaces = append(affectedReplaces, r) - } -- } else { -- // Concrete method - -- // declaration -- prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), r.to) -- if prev != nil && len(indices) == 1 { -- r.errorf(from.Pos(), "renaming this method %q to %q", -- from.Name(), r.to) -- r.errorf(prev.Pos(), "\twould conflict with this %s", -- objectKind(prev)) -- return +- if len(affectedReplaces) == 0 { +- continue +- } +- copied, err := modfile.Parse("", pm.Mapper.Content, nil) +- if err != nil { +- return nil, err - } - -- // assignability -- // -- // Find the set of abstract methods coupled to concrete -- // method 'from' by some satisfy.Constraint, and rename -- // them too. -- // -- // Coupling may be indirect, e.g. I.f <-> C.f via type D. -- // -- // type I interface {f()} -- // type C int -- // type (C) f() -- // type D struct{C} -- // var _ I = D{} -- // -- for key := range r.satisfy() { -- // key = (lhs, rhs) where lhs is always an interface. -- if types.IsInterface(key.RHS) { -- continue -- } -- rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name()) -- if rsel == nil || rsel.Obj() != from { -- continue // rhs does not have the method +- for _, r := range affectedReplaces { +- replacedPath := r.New.Path +- if strings.HasPrefix(r.New.Path, "./") || strings.HasPrefix(r.New.Path, "../") { +- replacedPath = filepath.Join(modFileDir, r.New.Path) - } -- lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) -- if lsel == nil { -- continue +- +- suffix := strings.TrimPrefix(replacedPath, string(oldBase)) +- +- newReplacedPath, err := filepath.Rel(modFileDir, newPkgDir+suffix) +- if err != nil { +- return nil, err - } -- imeth := lsel.Obj().(*types.Func) - -- // imeth is the abstract method (e.g. I.f) -- // and key.RHS is the concrete coupling type (e.g. D). -- if !r.changeMethods { -- r.errorf(from.Pos(), "renaming this method %q to %q", -- from.Name(), r.to) -- var pos token.Pos -- var iface string +- newReplacedPath = filepath.ToSlash(newReplacedPath) - -- I := recv(imeth).Type() -- if named, ok := I.(*types.Named); ok { -- pos = named.Obj().Pos() -- iface = "interface " + named.Obj().Name() -- } else { -- pos = from.Pos() -- iface = I.String() -- } -- r.errorf(pos, "\twould make %s no longer assignable to %s", -- key.RHS, iface) -- r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)", -- I, from.Name()) -- return // one error is enough +- if !strings.HasPrefix(newReplacedPath, "/") && !strings.HasPrefix(newReplacedPath, "../") { +- newReplacedPath = "./" + newReplacedPath - } - -- // Rename the coupled interface method to preserve assignability. -- r.check(imeth) +- if err := copied.AddReplace(r.Old.Path, "", newReplacedPath, ""); err != nil { +- return nil, err +- } +- } +- +- copied.Cleanup() +- newContent, err := copied.Format() +- if err != nil { +- return nil, err - } +- +- // Calculate the edits to be made due to the change. +- edits := s.Options().ComputeEdits(string(pm.Mapper.Content), string(newContent)) +- renamingEdits[pm.URI] = append(renamingEdits[pm.URI], edits...) - } - -- // Check integrity of existing (field and method) selections. -- // We skip this if there were errors above, to avoid redundant errors. -- r.checkSelections(from) +- return renamingEdits, nil -} - --func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool { -- // Reject cross-package references if r.to is unexported. -- // (Such references may be qualified identifiers or field/method -- // selections.) -- if !ast.IsExported(r.to) && pkg != from.Pkg() { -- r.errorf(from.Pos(), -- "renaming %q to %q would make it unexported", -- from.Name(), r.to) -- r.errorf(id.Pos(), "\tbreaking references from packages such as %q", -- pkg.Path()) -- return false +-// renamePackage computes all workspace edits required to rename the package +-// described by the given metadata, to newName, by renaming its package +-// directory. +-// +-// It updates package clauses and import paths for the renamed package as well +-// as any other packages affected by the directory renaming among all packages +-// known to the snapshot. +-func renamePackage(ctx context.Context, s Snapshot, f FileHandle, newName PackageName) (map[span.URI][]diff.Edit, error) { +- if strings.HasSuffix(string(newName), "_test") { +- return nil, fmt.Errorf("cannot rename to _test package") - } -- return true --} - --// satisfy returns the set of interface satisfaction constraints. --func (r *renamer) satisfy() map[satisfy.Constraint]bool { -- if r.satisfyConstraints == nil { -- // Compute on demand: it's expensive. -- var f satisfy.Finder -- pkg := r.pkg -- { -- // From satisfy.Finder documentation: -- // -- // The package must be free of type errors, and -- // info.{Defs,Uses,Selections,Types} must have been populated by the -- // type-checker. -- // -- // Only proceed if all packages have no errors. -- if pkg.HasParseErrors() || pkg.HasTypeErrors() { -- r.errorf(token.NoPos, // we don't have a position for this error. -- "renaming %q to %q not possible because %q has errors", -- r.from, r.to, pkg.Metadata().PkgPath) -- return nil -- } -- f.Find(pkg.GetTypesInfo(), pkg.GetSyntax()) -- } -- r.satisfyConstraints = f.Result +- // We need metadata for the relevant package and module paths. +- // These should be the same for all packages containing the file. +- meta, err := NarrowestMetadataForFile(ctx, s, f.URI()) +- if err != nil { +- return nil, err - } -- return r.satisfyConstraints --} - --// -- helpers ---------------------------------------------------------- +- oldPkgPath := meta.PkgPath +- if meta.Module == nil { +- return nil, fmt.Errorf("cannot rename package: missing module information for package %q", meta.PkgPath) +- } +- modulePath := PackagePath(meta.Module.Path) +- if modulePath == oldPkgPath { +- return nil, fmt.Errorf("cannot rename package: module path %q is the same as the package path, so renaming the package directory would have no effect", modulePath) +- } - --// recv returns the method's receiver. --func recv(meth *types.Func) *types.Var { -- return meth.Type().(*types.Signature).Recv() --} +- newPathPrefix := path.Join(path.Dir(string(oldPkgPath)), string(newName)) - --// someUse returns an arbitrary use of obj within info. --func someUse(info *types.Info, obj types.Object) *ast.Ident { -- for id, o := range info.Uses { -- if o == obj { -- return id -- } +- // We must inspect all packages, not just direct importers, +- // because we also rename subpackages, which may be unrelated. +- // (If the renamed package imports a subpackage it may require +- // edits to both its package and import decls.) +- allMetadata, err := s.AllMetadata(ctx) +- if err != nil { +- return nil, err - } -- return nil --} - --func objectKind(obj types.Object) string { -- if obj == nil { -- return "nil object" -- } -- switch obj := obj.(type) { -- case *types.PkgName: -- return "imported package name" -- case *types.TypeName: -- return "type" -- case *types.Var: -- if obj.IsField() { -- return "field" +- // Rename package and import declarations in all relevant packages. +- edits := make(map[span.URI][]diff.Edit) +- for _, m := range allMetadata { +- // Special case: x_test packages for the renamed package will not have the +- // package path as a dir prefix, but still need their package clauses +- // renamed. +- if m.PkgPath == oldPkgPath+"_test" { +- if err := renamePackageClause(ctx, m, s, newName+"_test", edits); err != nil { +- return nil, err +- } +- continue - } -- case *types.Func: -- if obj.Type().(*types.Signature).Recv() != nil { -- return "method" +- +- // Subtle: check this condition before checking for valid module info +- // below, because we should not fail this operation if unrelated packages +- // lack module info. +- if !strings.HasPrefix(string(m.PkgPath)+"/", string(oldPkgPath)+"/") { +- continue // not affected by the package renaming - } -- } -- // label, func, var, const -- return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types.")) --} - --// NB: for renamings, blank is not considered valid. --func isValidIdentifier(id string) bool { -- if id == "" || id == "_" { -- return false -- } -- for i, r := range id { -- if !isLetter(r) && (i == 0 || !isDigit(r)) { -- return false +- if m.Module == nil { +- // This check will always fail under Bazel. +- return nil, fmt.Errorf("cannot rename package: missing module information for package %q", m.PkgPath) - } -- } -- return token.Lookup(id) == token.IDENT --} - --// isLocal reports whether obj is local to some function. --// Precondition: not a struct field or interface method. --func isLocal(obj types.Object) bool { -- // [... 5=stmt 4=func 3=file 2=pkg 1=universe] -- var depth int -- for scope := obj.Parent(); scope != nil; scope = scope.Parent() { -- depth++ -- } -- return depth >= 4 --} +- if modulePath != PackagePath(m.Module.Path) { +- continue // don't edit imports if nested package and renaming package have different module paths +- } - --func isPackageLevel(obj types.Object) bool { -- if obj == nil { -- return false -- } -- return obj.Pkg().Scope().Lookup(obj.Name()) == obj --} +- // Renaming a package consists of changing its import path and package name. +- suffix := strings.TrimPrefix(string(m.PkgPath), string(oldPkgPath)) +- newPath := newPathPrefix + suffix - --// -- Plundered from go/scanner: --------------------------------------- +- pkgName := m.Name +- if m.PkgPath == oldPkgPath { +- pkgName = PackageName(newName) - --func isLetter(ch rune) bool { -- return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) --} +- if err := renamePackageClause(ctx, m, s, newName, edits); err != nil { +- return nil, err +- } +- } - --func isDigit(ch rune) bool { -- return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) --} -diff -urN a/gopls/internal/lsp/source/rename.go b/gopls/internal/lsp/source/rename.go ---- a/gopls/internal/lsp/source/rename.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/rename.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1244 +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. +- imp := ImportPath(newPath) // TODO(adonovan): what if newPath has vendor/ prefix? +- if err := renameImports(ctx, s, m, imp, pkgName, edits); err != nil { +- return nil, err +- } +- } - --package source +- return edits, nil +-} - --// TODO(adonovan): --// --// - method of generic concrete type -> arbitrary instances of same --// --// - make satisfy work across packages. --// --// - tests, tests, tests: --// - play with renamings in the k8s tree. --// - generics --// - error cases (e.g. conflicts) --// - renaming a symbol declared in the module cache --// (currently proceeds with half of the renaming!) --// - make sure all tests have both a local and a cross-package analogue. --// - look at coverage --// - special cases: embedded fields, interfaces, test variants, --// function-local things with uppercase names; --// packages with type errors (currently 'satisfy' rejects them), --// pakage with missing imports; --// --// - measure performance in k8s. --// --// - The original gorename tool assumed well-typedness, but the gopls feature --// does no such check (which actually makes it much more useful). --// Audit to ensure it is safe on ill-typed code. --// --// - Generics support was no doubt buggy before but incrementalization --// may have exacerbated it. If the problem were just about objects, --// defs and uses it would be fairly simple, but type assignability --// comes into play in the 'satisfy' check for method renamings. --// De-instantiating Vector[int] to Vector[T] changes its type. --// We need to come up with a theory for the satisfy check that --// works with generics, and across packages. We currently have no --// simple way to pass types between packages (think: objectpath for --// types), though presumably exportdata could be pressed into service. +-// renamePackageClause computes edits renaming the package clause of files in +-// the package described by the given metadata, to newName. -// --// - FileID-based de-duplication of edits to different URIs for the same file. -- --import ( -- "context" -- "errors" -- "fmt" -- "go/ast" -- "go/token" -- "go/types" -- "path" -- "path/filepath" -- "regexp" -- "sort" -- "strconv" -- "strings" -- -- "golang.org/x/mod/modfile" -- "golang.org/x/tools/go/ast/astutil" -- "golang.org/x/tools/go/types/objectpath" -- "golang.org/x/tools/go/types/typeutil" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/safetoken" -- "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" -- "golang.org/x/tools/internal/diff" -- "golang.org/x/tools/internal/event" -- "golang.org/x/tools/internal/typeparams" -- "golang.org/x/tools/refactor/satisfy" --) +-// Edits are written into the edits map. +-func renamePackageClause(ctx context.Context, m *Metadata, snapshot Snapshot, newName PackageName, edits map[span.URI][]diff.Edit) error { +- // Rename internal references to the package in the renaming package. +- for _, uri := range m.CompiledGoFiles { +- fh, err := snapshot.ReadFile(ctx, uri) +- if err != nil { +- return err +- } +- f, err := snapshot.ParseGo(ctx, fh, ParseHeader) +- if err != nil { +- return err +- } +- if f.File.Name == nil { +- continue // no package declaration +- } - --// A renamer holds state of a single call to renameObj, which renames --// an object (or several coupled objects) within a single type-checked --// syntax package. --type renamer struct { -- pkg Package // the syntax package in which the renaming is applied -- objsToUpdate map[types.Object]bool // records progress of calls to check -- hadConflicts bool -- conflicts []string -- from, to string -- satisfyConstraints map[satisfy.Constraint]bool -- msets typeutil.MethodSetCache -- changeMethods bool --} +- edit, err := posEdit(f.Tok, f.File.Name.Pos(), f.File.Name.End(), string(newName)) +- if err != nil { +- return err +- } +- edits[f.URI] = append(edits[f.URI], edit) +- } - --// A PrepareItem holds the result of a "prepare rename" operation: --// the source range and value of a selected identifier. --type PrepareItem struct { -- Range protocol.Range -- Text string +- return nil -} - --// PrepareRename searches for a valid renaming at position pp. +-// renameImports computes the set of edits to imports resulting from renaming +-// the package described by the given metadata, to a package with import path +-// newPath and name newName. -// --// The returned usererr is intended to be displayed to the user to explain why --// the prepare fails. Probably we could eliminate the redundancy in returning --// two errors, but for now this is done defensively. --func PrepareRename(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position) (_ *PrepareItem, usererr, err error) { -- ctx, done := event.Start(ctx, "source.PrepareRename") -- defer done() -- -- // Is the cursor within the package name declaration? -- if pgf, inPackageName, err := parsePackageNameDecl(ctx, snapshot, f, pp); err != nil { -- return nil, err, err -- } else if inPackageName { -- item, err := prepareRenamePackageName(ctx, snapshot, pgf) -- return item, err, err -- } -- -- // Ordinary (non-package) renaming. -- // -- // Type-check the current package, locate the reference at the position, -- // validate the object, and report its name and range. -- // -- // TODO(adonovan): in all cases below, we return usererr=nil, -- // which means we return (nil, nil) at the protocol -- // layer. This seems like a bug, or at best an exploitation of -- // knowledge of VSCode-specific behavior. Can we avoid that? -- pkg, pgf, err := PackageForFile(ctx, snapshot, f.URI(), NarrowestPackage) -- if err != nil { -- return nil, nil, err -- } -- pos, err := pgf.PositionPos(pp) +-// Edits are written into the edits map. +-func renameImports(ctx context.Context, snapshot Snapshot, m *Metadata, newPath ImportPath, newName PackageName, allEdits map[span.URI][]diff.Edit) error { +- rdeps, err := snapshot.ReverseDependencies(ctx, m.ID, false) // find direct importers - if err != nil { -- return nil, nil, err +- return err - } -- targets, node, err := objectsAt(pkg.GetTypesInfo(), pgf.File, pos) -- if err != nil { -- return nil, nil, err +- +- // Pass 1: rename import paths in import declarations. +- needsTypeCheck := make(map[PackageID][]span.URI) +- for _, rdep := range rdeps { +- if rdep.IsIntermediateTestVariant() { +- continue // for renaming, these variants are redundant +- } +- +- for _, uri := range rdep.CompiledGoFiles { +- fh, err := snapshot.ReadFile(ctx, uri) +- if err != nil { +- return err +- } +- f, err := snapshot.ParseGo(ctx, fh, ParseHeader) +- if err != nil { +- return err +- } +- if f.File.Name == nil { +- continue // no package declaration +- } +- for _, imp := range f.File.Imports { +- if rdep.DepsByImpPath[UnquoteImportPath(imp)] != m.ID { +- continue // not the import we're looking for +- } +- +- // If the import does not explicitly specify +- // a local name, then we need to invoke the +- // type checker to locate references to update. +- // +- // TODO(adonovan): is this actually true? +- // Renaming an import with a local name can still +- // cause conflicts: shadowing of built-ins, or of +- // package-level decls in the same or another file. +- if imp.Name == nil { +- needsTypeCheck[rdep.ID] = append(needsTypeCheck[rdep.ID], uri) +- } +- +- // Create text edit for the import path (string literal). +- edit, err := posEdit(f.Tok, imp.Path.Pos(), imp.Path.End(), strconv.Quote(string(newPath))) +- if err != nil { +- return err +- } +- allEdits[uri] = append(allEdits[uri], edit) +- } +- } - } -- var obj types.Object -- for obj = range targets { -- break // pick one arbitrarily +- +- // If the imported package's name hasn't changed, +- // we don't need to rename references within each file. +- if newName == m.Name { +- return nil - } -- if err := checkRenamable(obj); err != nil { -- return nil, nil, err +- +- // Pass 2: rename local name (types.PkgName) of imported +- // package throughout one or more files of the package. +- ids := make([]PackageID, 0, len(needsTypeCheck)) +- for id := range needsTypeCheck { +- ids = append(ids, id) - } -- rng, err := pgf.NodeRange(node) +- pkgs, err := snapshot.TypeCheck(ctx, ids...) - if err != nil { -- return nil, nil, err -- } -- if _, isImport := node.(*ast.ImportSpec); isImport { -- // We're not really renaming the import path. -- rng.End = rng.Start +- return err - } -- return &PrepareItem{ -- Range: rng, -- Text: obj.Name(), -- }, nil, nil --} +- for i, id := range ids { +- pkg := pkgs[i] +- for _, uri := range needsTypeCheck[id] { +- f, err := pkg.File(uri) +- if err != nil { +- return err +- } +- for _, imp := range f.File.Imports { +- if imp.Name != nil { +- continue // has explicit local name +- } +- if rdeps[id].DepsByImpPath[UnquoteImportPath(imp)] != m.ID { +- continue // not the import we're looking for +- } - --func prepareRenamePackageName(ctx context.Context, snapshot Snapshot, pgf *ParsedGoFile) (*PrepareItem, error) { -- // Does the client support file renaming? -- fileRenameSupported := false -- for _, op := range snapshot.View().Options().SupportedResourceOperations { -- if op == protocol.Rename { -- fileRenameSupported = true -- break +- pkgname := pkg.GetTypesInfo().Implicits[imp].(*types.PkgName) +- +- pkgScope := pkg.GetTypes().Scope() +- fileScope := pkg.GetTypesInfo().Scopes[f.File] +- +- localName := string(newName) +- try := 0 +- +- // Keep trying with fresh names until one succeeds. +- // +- // TODO(adonovan): fix: this loop is not sufficient to choose a name +- // that is guaranteed to be conflict-free; renameObj may still fail. +- // So the retry loop should be around renameObj, and we shouldn't +- // bother with scopes here. +- for fileScope.Lookup(localName) != nil || pkgScope.Lookup(localName) != nil { +- try++ +- localName = fmt.Sprintf("%s%d", newName, try) +- } +- +- // renameObj detects various conflicts, including: +- // - new name conflicts with a package-level decl in this file; +- // - new name hides a package-level decl in another file that +- // is actually referenced in this file; +- // - new name hides a built-in that is actually referenced +- // in this file; +- // - a reference in this file to the old package name would +- // become shadowed by an intervening declaration that +- // uses the new name. +- // It returns the edits if no conflict was detected. +- editMap, _, err := renameObjects(ctx, snapshot, localName, pkg, pkgname) +- if err != nil { +- return err +- } +- +- // If the chosen local package name matches the package's +- // new name, delete the change that would have inserted +- // an explicit local name, which is always the lexically +- // first change. +- if localName == string(newName) { +- edits, ok := editMap[uri] +- if !ok { +- return fmt.Errorf("internal error: no changes for %s", uri) +- } +- diff.SortEdits(edits) +- editMap[uri] = edits[1:] +- } +- for uri, edits := range editMap { +- allEdits[uri] = append(allEdits[uri], edits...) +- } +- } - } - } -- if !fileRenameSupported { -- return nil, errors.New("can't rename package: LSP client does not support file renaming") -- } +- return nil +-} - -- // Check validity of the metadata for the file's containing package. -- fileMeta, err := snapshot.MetadataForFile(ctx, pgf.URI) -- if err != nil { -- return nil, err -- } -- if len(fileMeta) == 0 { -- return nil, fmt.Errorf("no packages found for file %q", pgf.URI) -- } -- meta := fileMeta[0] -- if meta.Name == "main" { -- return nil, fmt.Errorf("can't rename package \"main\"") -- } -- if strings.HasSuffix(string(meta.Name), "_test") { -- return nil, fmt.Errorf("can't rename x_test packages") -- } -- if meta.Module == nil { -- return nil, fmt.Errorf("can't rename package: missing module information for package %q", meta.PkgPath) -- } -- if meta.Module.Path == string(meta.PkgPath) { -- return nil, fmt.Errorf("can't rename package: package path %q is the same as module path %q", meta.PkgPath, meta.Module.Path) +-// renameObjects computes the edits to the type-checked syntax package pkg +-// required to rename a set of target objects to newName. +-// +-// It also returns the set of objects that were found (due to +-// corresponding methods and embedded fields) to require renaming as a +-// consequence of the requested renamings. +-// +-// It returns an error if the renaming would cause a conflict. +-func renameObjects(ctx context.Context, snapshot Snapshot, newName string, pkg Package, targets ...types.Object) (map[span.URI][]diff.Edit, map[types.Object]bool, error) { +- r := renamer{ +- pkg: pkg, +- objsToUpdate: make(map[types.Object]bool), +- from: targets[0].Name(), +- to: newName, - } - -- // Return the location of the package declaration. -- rng, err := pgf.NodeRange(pgf.File.Name) -- if err != nil { -- return nil, err +- // A renaming initiated at an interface method indicates the +- // intention to rename abstract and concrete methods as needed +- // to preserve assignability. +- // TODO(adonovan): pull this into the caller. +- for _, obj := range targets { +- if obj, ok := obj.(*types.Func); ok { +- recv := obj.Type().(*types.Signature).Recv() +- if recv != nil && types.IsInterface(recv.Type().Underlying()) { +- r.changeMethods = true +- break +- } +- } - } -- return &PrepareItem{ -- Range: rng, -- Text: string(meta.Name), -- }, nil --} - --func checkRenamable(obj types.Object) error { -- switch obj := obj.(type) { -- case *types.Var: -- if obj.Embedded() { -- return fmt.Errorf("can't rename embedded fields: rename the type directly or name the field") +- // Check that the renaming of the identifier is ok. +- for _, obj := range targets { +- r.check(obj) +- if len(r.conflicts) > 0 { +- // Stop at first error. +- return nil, nil, fmt.Errorf("%s", strings.Join(r.conflicts, "\n")) - } -- case *types.Builtin, *types.Nil: -- return fmt.Errorf("%s is built in and cannot be renamed", obj.Name()) - } -- if obj.Pkg() == nil || obj.Pkg().Path() == "unsafe" { -- // e.g. error.Error, unsafe.Pointer -- return fmt.Errorf("%s is built in and cannot be renamed", obj.Name()) +- +- editMap, err := r.update() +- if err != nil { +- return nil, nil, err - } -- if obj.Name() == "_" { -- return errors.New("can't rename \"_\"") +- +- // Remove initial targets so that only 'consequences' remain. +- for _, obj := range targets { +- delete(r.objsToUpdate, obj) - } -- return nil +- return editMap, r.objsToUpdate, nil -} - --// Rename returns a map of TextEdits for each file modified when renaming a --// given identifier within a package and a boolean value of true for renaming --// package and false otherwise. --func Rename(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position, newName string) (map[span.URI][]protocol.TextEdit, bool, error) { -- ctx, done := event.Start(ctx, "source.Rename") -- defer done() +-// Rename all references to the target objects. +-func (r *renamer) update() (map[span.URI][]diff.Edit, error) { +- result := make(map[span.URI][]diff.Edit) - -- if !isValidIdentifier(newName) { -- return nil, false, fmt.Errorf("invalid identifier to rename: %q", newName) +- // shouldUpdate reports whether obj is one of (or an +- // instantiation of one of) the target objects. +- shouldUpdate := func(obj types.Object) bool { +- return containsOrigin(r.objsToUpdate, obj) - } - -- // Cursor within package name declaration? -- _, inPackageName, err := parsePackageNameDecl(ctx, snapshot, f, pp) -- if err != nil { -- return nil, false, err +- // Find all identifiers in the package that define or use a +- // renamed object. We iterate over info as it is more efficient +- // than calling ast.Inspect for each of r.pkg.CompiledGoFiles(). +- type item struct { +- node ast.Node // Ident, ImportSpec (obj=PkgName), or CaseClause (obj=Var) +- obj types.Object +- isDef bool - } -- -- var editMap map[span.URI][]diff.Edit -- if inPackageName { -- editMap, err = renamePackageName(ctx, snapshot, f, PackageName(newName)) -- } else { -- editMap, err = renameOrdinary(ctx, snapshot, f, pp, newName) +- var items []item +- info := r.pkg.GetTypesInfo() +- for id, obj := range info.Uses { +- if shouldUpdate(obj) { +- items = append(items, item{id, obj, false}) +- } - } -- if err != nil { -- return nil, false, err +- for id, obj := range info.Defs { +- if shouldUpdate(obj) { +- items = append(items, item{id, obj, true}) +- } - } -- -- // Convert edits to protocol form. -- result := make(map[span.URI][]protocol.TextEdit) -- for uri, edits := range editMap { -- // Sort and de-duplicate edits. -- // -- // Overlapping edits may arise in local renamings (due -- // to type switch implicits) and globals ones (due to -- // processing multiple package variants). -- // -- // We assume renaming produces diffs that are all -- // replacements (no adjacent insertions that might -- // become reordered) and that are either identical or -- // non-overlapping. -- diff.SortEdits(edits) -- filtered := edits[:0] -- for i, edit := range edits { -- if i == 0 || edit != filtered[len(filtered)-1] { -- filtered = append(filtered, edit) +- for node, obj := range info.Implicits { +- if shouldUpdate(obj) { +- switch node.(type) { +- case *ast.ImportSpec, *ast.CaseClause: +- items = append(items, item{node, obj, true}) - } - } -- edits = filtered +- } +- sort.Slice(items, func(i, j int) bool { +- return items[i].node.Pos() < items[j].node.Pos() +- }) - -- // TODO(adonovan): the logic above handles repeat edits to the -- // same file URI (e.g. as a member of package p and p_test) but -- // is not sufficient to handle file-system level aliasing arising -- // from symbolic or hard links. For that, we should use a -- // robustio-FileID-keyed map. -- // See https://go.dev/cl/457615 for example. -- // This really occurs in practice, e.g. kubernetes has -- // vendor/k8s.io/kubectl -> ../../staging/src/k8s.io/kubectl. -- fh, err := snapshot.GetFile(ctx, uri) -- if err != nil { -- return nil, false, err -- } -- data, err := fh.Read() -- if err != nil { -- return nil, false, err -- } -- m := protocol.NewMapper(uri, data) -- protocolEdits, err := ToProtocolEdits(m, edits) -- if err != nil { -- return nil, false, err +- // Update each identifier. +- for _, item := range items { +- pgf, ok := enclosingFile(r.pkg, item.node.Pos()) +- if !ok { +- bug.Reportf("edit does not belong to syntax of package %q", r.pkg) +- continue - } -- result[uri] = protocolEdits -- } - -- return result, inPackageName, nil --} -- --// renameOrdinary renames an ordinary (non-package) name throughout the workspace. --func renameOrdinary(ctx context.Context, snapshot Snapshot, f FileHandle, pp protocol.Position, newName string) (map[span.URI][]diff.Edit, error) { -- // Type-check the referring package and locate the object(s). -- // We choose the widest variant as, for non-exported -- // identifiers, it is the only package we need. -- pkg, pgf, err := PackageForFile(ctx, snapshot, f.URI(), WidestPackage) -- if err != nil { -- return nil, err -- } -- pos, err := pgf.PositionPos(pp) -- if err != nil { -- return nil, err -- } -- targets, _, err := objectsAt(pkg.GetTypesInfo(), pgf.File, pos) -- if err != nil { -- return nil, err -- } -- -- // Pick a representative object arbitrarily. -- // (All share the same name, pos, and kind.) -- var obj types.Object -- for obj = range targets { -- break -- } -- if obj.Name() == newName { -- return nil, fmt.Errorf("old and new names are the same: %s", newName) -- } -- if err := checkRenamable(obj); err != nil { -- return nil, err -- } -- -- // Find objectpath, if object is exported ("" otherwise). -- var declObjPath objectpath.Path -- if obj.Exported() { -- // objectpath.For requires the origin of a generic -- // function or type, not an instantiation (a bug?). -- // Unfortunately we can't call {Func,TypeName}.Origin -- // as these are not available in go/types@go1.18. -- // So we take a scenic route. -- switch obj.(type) { // avoid "obj :=" since cases reassign the var -- case *types.TypeName: -- if named, ok := obj.Type().(*types.Named); ok { -- obj = named.Obj() +- // Renaming a types.PkgName may result in the addition or removal of an identifier, +- // so we deal with this separately. +- if pkgName, ok := item.obj.(*types.PkgName); ok && item.isDef { +- edit, err := r.updatePkgName(pgf, pkgName) +- if err != nil { +- return nil, err - } -- case *types.Func: -- obj = funcOrigin(obj.(*types.Func)) -- case *types.Var: -- // TODO(adonovan): do vars need the origin treatment too? (issue #58462) -- } -- if path, err := objectpath.For(obj); err == nil { -- declObjPath = path +- result[pgf.URI] = append(result[pgf.URI], edit) +- continue - } -- } - -- // Nonexported? Search locally. -- if declObjPath == "" { -- var objects []types.Object -- for obj := range targets { -- objects = append(objects, obj) +- // Workaround the unfortunate lack of a Var object +- // for x in "switch x := expr.(type) {}" by adjusting +- // the case clause to the switch ident. +- // This may result in duplicate edits, but we de-dup later. +- if _, ok := item.node.(*ast.CaseClause); ok { +- path, _ := astutil.PathEnclosingInterval(pgf.File, item.obj.Pos(), item.obj.Pos()) +- item.node = path[0].(*ast.Ident) - } -- editMap, _, err := renameObjects(ctx, snapshot, newName, pkg, objects...) -- return editMap, err -- } - -- // Exported: search globally. -- // -- // For exported package-level var/const/func/type objects, the -- // search scope is just the direct importers. -- // -- // For exported fields and methods, the scope is the -- // transitive rdeps. (The exportedness of the field's struct -- // or method's receiver is irrelevant.) -- transitive := false -- switch obj.(type) { -- case *types.TypeName: -- // Renaming an exported package-level type -- // requires us to inspect all transitive rdeps -- // in the event that the type is embedded. -- // -- // TODO(adonovan): opt: this is conservative -- // but inefficient. Instead, expand the scope -- // of the search only if we actually encounter -- // an embedding of the type, and only then to -- // the rdeps of the embedding package. -- if obj.Parent() == obj.Pkg().Scope() { -- transitive = true +- // Replace the identifier with r.to. +- edit, err := posEdit(pgf.Tok, item.node.Pos(), item.node.End(), r.to) +- if err != nil { +- return nil, err - } - -- case *types.Var: -- if obj.(*types.Var).IsField() { -- transitive = true // field -- } +- result[pgf.URI] = append(result[pgf.URI], edit) - -- // TODO(adonovan): opt: process only packages that -- // contain a reference (xrefs) to the target field. +- if !item.isDef { // uses do not have doc comments to update. +- continue +- } - -- case *types.Func: -- if obj.Type().(*types.Signature).Recv() != nil { -- transitive = true // method +- doc := docComment(pgf, item.node.(*ast.Ident)) +- if doc == nil { +- continue - } - -- // It's tempting to optimize by skipping -- // packages that don't contain a reference to -- // the method in the xrefs index, but we still -- // need to apply the satisfy check to those -- // packages to find assignment statements that -- // might expands the scope of the renaming. +- // Perform the rename in doc comments declared in the original package. +- // go/parser strips out \r\n returns from the comment text, so go +- // line-by-line through the comment text to get the correct positions. +- docRegexp := regexp.MustCompile(`\b` + r.from + `\b`) // valid identifier => valid regexp +- for _, comment := range doc.List { +- if isDirective(comment.Text) { +- continue +- } +- // TODO(adonovan): why are we looping over lines? +- // Just run the loop body once over the entire multiline comment. +- lines := strings.Split(comment.Text, "\n") +- tokFile := pgf.Tok +- commentLine := safetoken.Line(tokFile, comment.Pos()) +- uri := span.URIFromPath(tokFile.Name()) +- for i, line := range lines { +- lineStart := comment.Pos() +- if i > 0 { +- lineStart = tokFile.LineStart(commentLine + i) +- } +- for _, locs := range docRegexp.FindAllIndex([]byte(line), -1) { +- edit, err := posEdit(tokFile, lineStart+token.Pos(locs[0]), lineStart+token.Pos(locs[1]), r.to) +- if err != nil { +- return nil, err // can't happen +- } +- result[uri] = append(result[uri], edit) +- } +- } +- } - } - -- // Type-check all the packages to inspect. -- declURI := span.URIFromPath(pkg.FileSet().File(obj.Pos()).Name()) -- pkgs, err := typeCheckReverseDependencies(ctx, snapshot, declURI, transitive) -- if err != nil { -- return nil, err -- } +- return result, nil +-} - -- // Apply the renaming to the (initial) object. -- declPkgPath := PackagePath(obj.Pkg().Path()) -- return renameExported(ctx, snapshot, pkgs, declPkgPath, declObjPath, newName) +-// docComment returns the doc for an identifier within the specified file. +-func docComment(pgf *ParsedGoFile, id *ast.Ident) *ast.CommentGroup { +- nodes, _ := astutil.PathEnclosingInterval(pgf.File, id.Pos(), id.End()) +- for _, node := range nodes { +- switch decl := node.(type) { +- case *ast.FuncDecl: +- return decl.Doc +- case *ast.Field: +- return decl.Doc +- case *ast.GenDecl: +- return decl.Doc +- // For {Type,Value}Spec, if the doc on the spec is absent, +- // search for the enclosing GenDecl +- case *ast.TypeSpec: +- if decl.Doc != nil { +- return decl.Doc +- } +- case *ast.ValueSpec: +- if decl.Doc != nil { +- return decl.Doc +- } +- case *ast.Ident: +- case *ast.AssignStmt: +- // *ast.AssignStmt doesn't have an associated comment group. +- // So, we try to find a comment just before the identifier. +- +- // Try to find a comment group only for short variable declarations (:=). +- if decl.Tok != token.DEFINE { +- return nil +- } +- +- identLine := safetoken.Line(pgf.Tok, id.Pos()) +- for _, comment := range nodes[len(nodes)-1].(*ast.File).Comments { +- if comment.Pos() > id.Pos() { +- // Comment is after the identifier. +- continue +- } +- +- lastCommentLine := safetoken.Line(pgf.Tok, comment.End()) +- if lastCommentLine+1 == identLine { +- return comment +- } +- } +- default: +- return nil +- } +- } +- return nil -} - --// funcOrigin is a go1.18-portable implementation of (*types.Func).Origin. --func funcOrigin(fn *types.Func) *types.Func { -- // Method? -- if fn.Type().(*types.Signature).Recv() != nil { -- return typeparams.OriginMethod(fn) +-// updatePkgName returns the updates to rename a pkgName in the import spec by +-// only modifying the package name portion of the import declaration. +-func (r *renamer) updatePkgName(pgf *ParsedGoFile, pkgName *types.PkgName) (diff.Edit, error) { +- // Modify ImportSpec syntax to add or remove the Name as needed. +- path, _ := astutil.PathEnclosingInterval(pgf.File, pkgName.Pos(), pkgName.Pos()) +- if len(path) < 2 { +- return diff.Edit{}, fmt.Errorf("no path enclosing interval for %s", pkgName.Name()) +- } +- spec, ok := path[1].(*ast.ImportSpec) +- if !ok { +- return diff.Edit{}, fmt.Errorf("failed to update PkgName for %s", pkgName.Name()) - } - -- // Package-level function? -- // (Assume the origin has the same position.) -- gen := fn.Pkg().Scope().Lookup(fn.Name()) -- if gen != nil && gen.Pos() == fn.Pos() { -- return gen.(*types.Func) +- newText := "" +- if pkgName.Imported().Name() != r.to { +- newText = r.to + " " - } - -- return fn +- // Replace the portion (possibly empty) of the spec before the path: +- // local "path" or "path" +- // -> <- -><- +- return posEdit(pgf.Tok, spec.Pos(), spec.Path.Pos(), newText) -} - --// typeCheckReverseDependencies returns the type-checked packages for --// the reverse dependencies of all packages variants containing --// file declURI. The packages are in some topological order. --// --// It includes all variants (even intermediate test variants) for the --// purposes of computing reverse dependencies, but discards ITVs for --// the actual renaming work. +-// parsePackageNameDecl is a convenience function that parses and +-// returns the package name declaration of file fh, and reports +-// whether the position ppos lies within it. -// --// (This neglects obscure edge cases where a _test.go file changes the --// selectors used only in an ITV, but life is short. Also sin must be --// punished.) --func typeCheckReverseDependencies(ctx context.Context, snapshot Snapshot, declURI span.URI, transitive bool) ([]Package, error) { -- variants, err := snapshot.MetadataForFile(ctx, declURI) +-// Note: also used by references. +-func parsePackageNameDecl(ctx context.Context, snapshot Snapshot, fh FileHandle, ppos protocol.Position) (*ParsedGoFile, bool, error) { +- pgf, err := snapshot.ParseGo(ctx, fh, ParseHeader) - if err != nil { -- return nil, err -- } -- allRdeps := make(map[PackageID]*Metadata) -- for _, variant := range variants { -- rdeps, err := snapshot.ReverseDependencies(ctx, variant.ID, transitive) -- if err != nil { -- return nil, err -- } -- allRdeps[variant.ID] = variant // include self -- for id, meta := range rdeps { -- allRdeps[id] = meta -- } -- } -- var ids []PackageID -- for id, meta := range allRdeps { -- if meta.IsIntermediateTestVariant() { -- continue -- } -- ids = append(ids, id) +- return nil, false, err - } -- -- // Sort the packages into some topological order of the -- // (unfiltered) metadata graph. -- SortPostOrder(snapshot, ids) -- -- // Dependencies must be visited first since they can expand -- // the search set. Ideally we would process the (filtered) set -- // of packages in the parallel postorder of the snapshot's -- // (unfiltered) metadata graph, but this is quite tricky -- // without a good graph abstraction. -- // -- // For now, we visit packages sequentially in order of -- // ascending height, like an inverted breadth-first search. -- // -- // Type checking is by far the dominant cost, so -- // overlapping it with renaming may not be worthwhile. -- return snapshot.TypeCheck(ctx, ids...) +- // Careful: because we used ParseHeader, +- // pgf.Pos(ppos) may be beyond EOF => (0, err). +- pos, _ := pgf.PositionPos(ppos) +- return pgf, pgf.File.Name.Pos() <= pos && pos <= pgf.File.Name.End(), nil -} - --// SortPostOrder sorts the IDs so that if x depends on y, then y appears before x. --func SortPostOrder(meta MetadataSource, ids []PackageID) { -- postorder := make(map[PackageID]int) -- order := 0 -- var visit func(PackageID) -- visit = func(id PackageID) { -- if _, ok := postorder[id]; !ok { -- postorder[id] = -1 // break recursion -- if m := meta.Metadata(id); m != nil { -- for _, depID := range m.DepsByPkgPath { -- visit(depID) -- } -- } -- order++ -- postorder[id] = order +-// enclosingFile returns the CompiledGoFile of pkg that contains the specified position. +-func enclosingFile(pkg Package, pos token.Pos) (*ParsedGoFile, bool) { +- for _, pgf := range pkg.CompiledGoFiles() { +- if pgf.File.Pos() <= pos && pos <= pgf.File.End() { +- return pgf, true - } - } -- for _, id := range ids { -- visit(id) +- return nil, false +-} +- +-// posEdit returns an edit to replace the (start, end) range of tf with 'new'. +-func posEdit(tf *token.File, start, end token.Pos, new string) (diff.Edit, error) { +- startOffset, endOffset, err := safetoken.Offsets(tf, start, end) +- if err != nil { +- return diff.Edit{}, err - } -- sort.Slice(ids, func(i, j int) bool { -- return postorder[ids[i]] < postorder[ids[j]] -- }) +- return diff.Edit{Start: startOffset, End: endOffset, New: new}, nil -} +diff -urN a/gopls/internal/lsp/source/rename_check.go b/gopls/internal/lsp/source/rename_check.go +--- a/gopls/internal/lsp/source/rename_check.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/rename_check.go 1970-01-01 08:00:00 +@@ -1,921 +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. +-// +-// Taken from golang.org/x/tools/refactor/rename. - --// renameExported renames the object denoted by (pkgPath, objPath) --// within the specified packages, along with any other objects that --// must be renamed as a consequence. The slice of packages must be --// topologically ordered. --func renameExported(ctx context.Context, snapshot Snapshot, pkgs []Package, declPkgPath PackagePath, declObjPath objectpath.Path, newName string) (map[span.URI][]diff.Edit, error) { +-package source - -- // A target is a name for an object that is stable across types.Packages. -- type target struct { -- pkg PackagePath -- obj objectpath.Path -- } +-// This file defines the conflict-checking portion of the rename operation. +-// +-// The renamer works on a single package of type-checked syntax, and +-// is called in parallel for all necessary packages in the workspace, +-// possibly up to the transitive reverse dependencies of the +-// declaration. Finally the union of all edits and errors is computed. +-// +-// Renaming one object may entail renaming of others. For example: +-// +-// - An embedded field couples a Var (field) and a TypeName. +-// So, renaming either one requires renaming the other. +-// If the initial object is an embedded field, we must add its +-// TypeName (and its enclosing package) to the renaming set; +-// this is easily discovered at the outset. +-// +-// Conversely, if the initial object is a TypeName, we must observe +-// whether any of its references (from directly importing packages) +-// is coincident with an embedded field Var and, if so, initiate a +-// renaming of it. +-// +-// - A method of an interface type is coupled to all corresponding +-// methods of types that are assigned to the interface (as +-// discovered by the 'satisfy' pass). As a matter of usability, we +-// require that such renamings be initiated from the interface +-// method, not the concrete method. - -- // Populate the initial set of target objects. -- // This set may grow as we discover the consequences of each renaming. +-import ( +- "fmt" +- "go/ast" +- "go/token" +- "go/types" +- "path/filepath" +- "reflect" +- "strings" +- "unicode" +- +- "golang.org/x/tools/go/ast/astutil" +- "golang.org/x/tools/gopls/internal/lsp/safetoken" +- "golang.org/x/tools/refactor/satisfy" +-) +- +-// errorf reports an error (e.g. conflict) and prevents file modification. +-func (r *renamer) errorf(pos token.Pos, format string, args ...interface{}) { +- // Conflict error messages in the old gorename tool (whence this +- // logic originated) contain rich information associated with +- // multiple source lines, such as: - // -- // TODO(adonovan): strictly, each cone of reverse dependencies -- // of a single variant should have its own target map that -- // monotonically expands as we go up the import graph, because -- // declarations in test files can alter the set of -- // package-level names and change the meaning of field and -- // method selectors. So if we parallelize the graph -- // visitation (see above), we should also compute the targets -- // as a union of dependencies. +- // p/a.go:1:2: renaming "x" to "y" here +- // p/b.go:3:4: \t would cause this reference to "y" +- // p/c.go:5:5: \t to become shadowed by this intervening declaration. - // -- // Or we could decide that the logic below is fast enough not -- // to need parallelism. In small measurements so far the -- // type-checking step is about 95% and the renaming only 5%. -- targets := map[target]bool{{declPkgPath, declObjPath}: true} -- -- // Apply the renaming operation to each package. -- allEdits := make(map[span.URI][]diff.Edit) -- for _, pkg := range pkgs { +- // Unfortunately LSP provides no means to transmit the +- // structure of this error, so we format the positions briefly +- // using dir/file.go where dir is the base name of the parent +- // directory. - -- // Resolved target objects within package pkg. -- var objects []types.Object -- for t := range targets { -- p := pkg.DependencyTypes(t.pkg) -- if p == nil { -- continue // indirect dependency of no consequence -- } -- obj, err := objectpath.Object(p, t.obj) -- if err != nil { -- // Though this can happen with regular export data -- // due to trimming of inconsequential objects, -- // it can't happen if we load dependencies from full -- // syntax (as today) or shallow export data (soon), -- // as both are complete. -- bug.Reportf("objectpath.Object(%v, %v) failed: %v", p, t.obj, err) -- continue -- } -- objects = append(objects, obj) -- } -- if len(objects) == 0 { -- continue // no targets of consequence to this package -- } +- var conflict strings.Builder - -- // Apply the renaming. -- editMap, moreObjects, err := renameObjects(ctx, snapshot, newName, pkg, objects...) -- if err != nil { -- return nil, err +- // Add prefix of (truncated) position. +- if pos != token.NoPos { +- // TODO(adonovan): skip position of first error if it is +- // on the same line as the renaming itself. +- posn := safetoken.StartPosition(r.pkg.FileSet(), pos).String() +- segments := strings.Split(filepath.ToSlash(posn), "/") +- if n := len(segments); n > 2 { +- segments = segments[n-2:] - } +- posn = strings.Join(segments, "/") +- fmt.Fprintf(&conflict, "%s:", posn) - -- // It is safe to concatenate the edits as they are non-overlapping -- // (or identical, in which case they will be de-duped by Rename). -- for uri, edits := range editMap { -- allEdits[uri] = append(allEdits[uri], edits...) +- if !strings.HasPrefix(format, "\t") { +- conflict.WriteByte(' ') - } +- } - -- // Expand the search set? -- for obj := range moreObjects { -- objpath, err := objectpath.For(obj) -- if err != nil { -- continue // not exported -- } -- target := target{PackagePath(obj.Pkg().Path()), objpath} -- targets[target] = true +- fmt.Fprintf(&conflict, format, args...) +- r.conflicts = append(r.conflicts, conflict.String()) +-} - -- // TODO(adonovan): methods requires dynamic -- // programming of the product targets x -- // packages as any package might add a new -- // target (from a foward dep) as a -- // consequence, and any target might imply a -- // new set of rdeps. See golang/go#58461. -- } +-// check performs safety checks of the renaming of the 'from' object to r.to. +-func (r *renamer) check(from types.Object) { +- if r.objsToUpdate[from] { +- return - } +- r.objsToUpdate[from] = true - -- return allEdits, nil +- // NB: order of conditions is important. +- if from_, ok := from.(*types.PkgName); ok { +- r.checkInFileBlock(from_) +- } else if from_, ok := from.(*types.Label); ok { +- r.checkLabel(from_) +- } else if isPackageLevel(from) { +- r.checkInPackageBlock(from) +- } else if v, ok := from.(*types.Var); ok && v.IsField() { +- r.checkStructField(v) +- } else if f, ok := from.(*types.Func); ok && recv(f) != nil { +- r.checkMethod(f) +- } else if isLocal(from) { +- r.checkInLexicalScope(from) +- } else { +- r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n", +- objectKind(from), from) +- } -} - --// renamePackageName renames package declarations, imports, and go.mod files. --func renamePackageName(ctx context.Context, s Snapshot, f FileHandle, newName PackageName) (map[span.URI][]diff.Edit, error) { -- // Rename the package decl and all imports. -- renamingEdits, err := renamePackage(ctx, s, f, newName) -- if err != nil { -- return nil, err +-// checkInFileBlock performs safety checks for renames of objects in the file block, +-// i.e. imported package names. +-func (r *renamer) checkInFileBlock(from *types.PkgName) { +- // Check import name is not "init". +- if r.to == "init" { +- r.errorf(from.Pos(), "%q is not a valid imported package name", r.to) - } - -- // Update the last component of the file's enclosing directory. -- oldBase := filepath.Dir(f.URI().Filename()) -- newPkgDir := filepath.Join(filepath.Dir(oldBase), string(newName)) +- // Check for conflicts between file and package block. +- if prev := from.Pkg().Scope().Lookup(r.to); prev != nil { +- r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", +- objectKind(from), from.Name(), r.to) +- r.errorf(prev.Pos(), "\twith this package member %s", +- objectKind(prev)) +- return // since checkInPackageBlock would report redundant errors +- } - -- // Update any affected replace directives in go.mod files. -- // TODO(adonovan): extract into its own function. -- // -- // TODO: should this operate on all go.mod files, irrespective of whether they are included in the workspace? -- // Get all active mod files in the workspace -- modFiles := s.ModFiles() -- for _, m := range modFiles { -- fh, err := s.GetFile(ctx, m) -- if err != nil { -- return nil, err -- } -- pm, err := s.ParseMod(ctx, fh) -- if err != nil { -- return nil, err -- } -- -- modFileDir := filepath.Dir(pm.URI.Filename()) -- affectedReplaces := []*modfile.Replace{} -- -- // Check if any replace directives need to be fixed -- for _, r := range pm.File.Replace { -- if !strings.HasPrefix(r.New.Path, "/") && !strings.HasPrefix(r.New.Path, "./") && !strings.HasPrefix(r.New.Path, "../") { -- continue -- } -- -- replacedPath := r.New.Path -- if strings.HasPrefix(r.New.Path, "./") || strings.HasPrefix(r.New.Path, "../") { -- replacedPath = filepath.Join(modFileDir, r.New.Path) -- } -- -- // TODO: Is there a risk of converting a '\' delimited replacement to a '/' delimited replacement? -- if !strings.HasPrefix(filepath.ToSlash(replacedPath)+"/", filepath.ToSlash(oldBase)+"/") { -- continue // not affected by the package renaming -- } -- -- affectedReplaces = append(affectedReplaces, r) -- } +- // Check for conflicts in lexical scope. +- r.checkInLexicalScope(from) +-} - -- if len(affectedReplaces) == 0 { -- continue -- } -- copied, err := modfile.Parse("", pm.Mapper.Content, nil) -- if err != nil { -- return nil, err +-// checkInPackageBlock performs safety checks for renames of +-// func/var/const/type objects in the package block. +-func (r *renamer) checkInPackageBlock(from types.Object) { +- // Check that there are no references to the name from another +- // package if the renaming would make it unexported. +- if typ := r.pkg.GetTypes(); typ != from.Pkg() && ast.IsExported(r.from) && !ast.IsExported(r.to) { +- if id := someUse(r.pkg.GetTypesInfo(), from); id != nil { +- r.checkExport(id, typ, from) - } +- } - -- for _, r := range affectedReplaces { -- replacedPath := r.New.Path -- if strings.HasPrefix(r.New.Path, "./") || strings.HasPrefix(r.New.Path, "../") { -- replacedPath = filepath.Join(modFileDir, r.New.Path) -- } -- -- suffix := strings.TrimPrefix(replacedPath, string(oldBase)) -- -- newReplacedPath, err := filepath.Rel(modFileDir, newPkgDir+suffix) -- if err != nil { -- return nil, err -- } -- -- newReplacedPath = filepath.ToSlash(newReplacedPath) -- -- if !strings.HasPrefix(newReplacedPath, "/") && !strings.HasPrefix(newReplacedPath, "../") { -- newReplacedPath = "./" + newReplacedPath -- } -- -- if err := copied.AddReplace(r.Old.Path, "", newReplacedPath, ""); err != nil { -- return nil, err +- // Check that in the package block, "init" is a function, and never referenced. +- if r.to == "init" { +- kind := objectKind(from) +- if kind == "func" { +- // Reject if intra-package references to it exist. +- for id, obj := range r.pkg.GetTypesInfo().Uses { +- if obj == from { +- r.errorf(from.Pos(), +- "renaming this func %q to %q would make it a package initializer", +- from.Name(), r.to) +- r.errorf(id.Pos(), "\tbut references to it exist") +- break +- } - } +- } else { +- r.errorf(from.Pos(), "you cannot have a %s at package level named %q", +- kind, r.to) - } +- } - -- copied.Cleanup() -- newContent, err := copied.Format() -- if err != nil { -- return nil, err +- // Check for conflicts between package block and all file blocks. +- for _, f := range r.pkg.GetSyntax() { +- fileScope := r.pkg.GetTypesInfo().Scopes[f] +- b, prev := fileScope.LookupParent(r.to, token.NoPos) +- if b == fileScope { +- r.errorf(from.Pos(), "renaming this %s %q to %q would conflict", objectKind(from), from.Name(), r.to) +- var prevPos token.Pos +- if prev != nil { +- prevPos = prev.Pos() +- } +- r.errorf(prevPos, "\twith this %s", objectKind(prev)) +- return // since checkInPackageBlock would report redundant errors - } -- -- // Calculate the edits to be made due to the change. -- edits := s.View().Options().ComputeEdits(string(pm.Mapper.Content), string(newContent)) -- renamingEdits[pm.URI] = append(renamingEdits[pm.URI], edits...) - } - -- return renamingEdits, nil +- // Check for conflicts in lexical scope. +- r.checkInLexicalScope(from) -} - --// renamePackage computes all workspace edits required to rename the package --// described by the given metadata, to newName, by renaming its package --// directory. +-// checkInLexicalScope performs safety checks that a renaming does not +-// change the lexical reference structure of the specified package. -// --// It updates package clauses and import paths for the renamed package as well --// as any other packages affected by the directory renaming among packages --// described by allMetadata. --func renamePackage(ctx context.Context, s Snapshot, f FileHandle, newName PackageName) (map[span.URI][]diff.Edit, error) { -- if strings.HasSuffix(string(newName), "_test") { -- return nil, fmt.Errorf("cannot rename to _test package") -- } -- -- // We need metadata for the relevant package and module paths. -- // These should be the same for all packages containing the file. -- metas, err := s.MetadataForFile(ctx, f.URI()) -- if err != nil { -- return nil, err -- } -- if len(metas) == 0 { -- return nil, fmt.Errorf("no packages found for file %q", f.URI()) +-// For objects in lexical scope, there are three kinds of conflicts: +-// same-, sub-, and super-block conflicts. We will illustrate all three +-// using this example: +-// +-// var x int +-// var z int +-// +-// func f(y int) { +-// print(x) +-// print(y) +-// } +-// +-// Renaming x to z encounters a "same-block conflict", because an object +-// with the new name already exists, defined in the same lexical block +-// as the old object. +-// +-// Renaming x to y encounters a "sub-block conflict", because there exists +-// a reference to x from within (what would become) a hole in its scope. +-// The definition of y in an (inner) sub-block would cast a shadow in +-// the scope of the renamed variable. +-// +-// Renaming y to x encounters a "super-block conflict". This is the +-// converse situation: there is an existing definition of the new name +-// (x) in an (enclosing) super-block, and the renaming would create a +-// hole in its scope, within which there exist references to it. The +-// new name shadows the existing definition of x in the super-block. +-// +-// Removing the old name (and all references to it) is always safe, and +-// requires no checks. +-func (r *renamer) checkInLexicalScope(from types.Object) { +- b := from.Parent() // the block defining the 'from' object +- if b != nil { +- toBlock, to := b.LookupParent(r.to, from.Parent().End()) +- if toBlock == b { +- // same-block conflict +- r.errorf(from.Pos(), "renaming this %s %q to %q", +- objectKind(from), from.Name(), r.to) +- r.errorf(to.Pos(), "\tconflicts with %s in same block", +- objectKind(to)) +- return +- } else if toBlock != nil { +- // Check for super-block conflict. +- // The name r.to is defined in a superblock. +- // Is that name referenced from within this block? +- forEachLexicalRef(r.pkg, to, func(id *ast.Ident, block *types.Scope) bool { +- _, obj := block.LookupParent(from.Name(), id.Pos()) +- if obj == from { +- // super-block conflict +- r.errorf(from.Pos(), "renaming this %s %q to %q", +- objectKind(from), from.Name(), r.to) +- r.errorf(id.Pos(), "\twould shadow this reference") +- r.errorf(to.Pos(), "\tto the %s declared here", +- objectKind(to)) +- return false // stop +- } +- return true +- }) +- } - } -- meta := metas[0] // narrowest +- // Check for sub-block conflict. +- // Is there an intervening definition of r.to between +- // the block defining 'from' and some reference to it? +- forEachLexicalRef(r.pkg, from, func(id *ast.Ident, block *types.Scope) bool { +- // Find the block that defines the found reference. +- // It may be an ancestor. +- fromBlock, _ := block.LookupParent(from.Name(), id.Pos()) +- // See what r.to would resolve to in the same scope. +- toBlock, to := block.LookupParent(r.to, id.Pos()) +- if to != nil { +- // sub-block conflict +- if deeper(toBlock, fromBlock) { +- r.errorf(from.Pos(), "renaming this %s %q to %q", +- objectKind(from), from.Name(), r.to) +- r.errorf(id.Pos(), "\twould cause this reference to become shadowed") +- r.errorf(to.Pos(), "\tby this intervening %s definition", +- objectKind(to)) +- return false // stop +- } +- } +- return true +- }) - -- oldPkgPath := meta.PkgPath -- if meta.Module == nil { -- return nil, fmt.Errorf("cannot rename package: missing module information for package %q", meta.PkgPath) -- } -- modulePath := PackagePath(meta.Module.Path) -- if modulePath == oldPkgPath { -- return nil, fmt.Errorf("cannot rename package: module path %q is the same as the package path, so renaming the package directory would have no effect", modulePath) +- // Renaming a type that is used as an embedded field +- // requires renaming the field too. e.g. +- // type T int // if we rename this to U.. +- // var s struct {T} +- // print(s.T) // ...this must change too +- if _, ok := from.(*types.TypeName); ok { +- for id, obj := range r.pkg.GetTypesInfo().Uses { +- if obj == from { +- if field := r.pkg.GetTypesInfo().Defs[id]; field != nil { +- r.check(field) +- } +- } +- } - } +-} - -- newPathPrefix := path.Join(path.Dir(string(oldPkgPath)), string(newName)) -- -- // We must inspect all packages, not just direct importers, -- // because we also rename subpackages, which may be unrelated. -- // (If the renamed package imports a subpackage it may require -- // edits to both its package and import decls.) -- allMetadata, err := s.AllMetadata(ctx) -- if err != nil { -- return nil, err +-// deeper reports whether block x is lexically deeper than y. +-func deeper(x, y *types.Scope) bool { +- if x == y || x == nil { +- return false +- } else if y == nil { +- return true +- } else { +- return deeper(x.Parent(), y.Parent()) - } +-} - -- // Rename package and import declarations in all relevant packages. -- edits := make(map[span.URI][]diff.Edit) -- for _, m := range allMetadata { -- // Special case: x_test packages for the renamed package will not have the -- // package path as as a dir prefix, but still need their package clauses -- // renamed. -- if m.PkgPath == oldPkgPath+"_test" { -- if err := renamePackageClause(ctx, m, s, newName+"_test", edits); err != nil { -- return nil, err -- } -- continue -- } -- -- // Subtle: check this condition before checking for valid module info -- // below, because we should not fail this operation if unrelated packages -- // lack module info. -- if !strings.HasPrefix(string(m.PkgPath)+"/", string(oldPkgPath)+"/") { -- continue // not affected by the package renaming -- } +-// forEachLexicalRef calls fn(id, block) for each identifier id in package +-// pkg that is a reference to obj in lexical scope. block is the +-// lexical block enclosing the reference. If fn returns false the +-// iteration is terminated and findLexicalRefs returns false. +-func forEachLexicalRef(pkg Package, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool { +- ok := true +- var stack []ast.Node - -- if m.Module == nil { -- // This check will always fail under Bazel. -- return nil, fmt.Errorf("cannot rename package: missing module information for package %q", m.PkgPath) +- var visit func(n ast.Node) bool +- visit = func(n ast.Node) bool { +- if n == nil { +- stack = stack[:len(stack)-1] // pop +- return false - } -- -- if modulePath != PackagePath(m.Module.Path) { -- continue // don't edit imports if nested package and renaming package have different module paths +- if !ok { +- return false // bail out - } - -- // Renaming a package consists of changing its import path and package name. -- suffix := strings.TrimPrefix(string(m.PkgPath), string(oldPkgPath)) -- newPath := newPathPrefix + suffix +- stack = append(stack, n) // push +- switch n := n.(type) { +- case *ast.Ident: +- if pkg.GetTypesInfo().Uses[n] == obj { +- block := enclosingBlock(pkg.GetTypesInfo(), stack) +- if !fn(n, block) { +- ok = false +- } +- } +- return visit(nil) // pop stack - -- pkgName := m.Name -- if m.PkgPath == oldPkgPath { -- pkgName = PackageName(newName) +- case *ast.SelectorExpr: +- // don't visit n.Sel +- ast.Inspect(n.X, visit) +- return visit(nil) // pop stack, don't descend - -- if err := renamePackageClause(ctx, m, s, newName, edits); err != nil { -- return nil, err +- case *ast.CompositeLit: +- // Handle recursion ourselves for struct literals +- // so we don't visit field identifiers. +- tv, ok := pkg.GetTypesInfo().Types[n] +- if !ok { +- return visit(nil) // pop stack, don't descend +- } +- if _, ok := Deref(tv.Type).Underlying().(*types.Struct); ok { +- if n.Type != nil { +- ast.Inspect(n.Type, visit) +- } +- for _, elt := range n.Elts { +- if kv, ok := elt.(*ast.KeyValueExpr); ok { +- ast.Inspect(kv.Value, visit) +- } else { +- ast.Inspect(elt, visit) +- } +- } +- return visit(nil) // pop stack, don't descend - } - } +- return true +- } - -- imp := ImportPath(newPath) // TODO(adonovan): what if newPath has vendor/ prefix? -- if err := renameImports(ctx, s, m, imp, pkgName, edits); err != nil { -- return nil, err +- for _, f := range pkg.GetSyntax() { +- ast.Inspect(f, visit) +- if len(stack) != 0 { +- panic(stack) +- } +- if !ok { +- break - } - } -- -- return edits, nil +- return ok -} - --// renamePackageClause computes edits renaming the package clause of files in --// the package described by the given metadata, to newName. --// --// Edits are written into the edits map. --func renamePackageClause(ctx context.Context, m *Metadata, snapshot Snapshot, newName PackageName, edits map[span.URI][]diff.Edit) error { -- // Rename internal references to the package in the renaming package. -- for _, uri := range m.CompiledGoFiles { -- fh, err := snapshot.GetFile(ctx, uri) -- if err != nil { -- return err -- } -- f, err := snapshot.ParseGo(ctx, fh, ParseHeader) -- if err != nil { -- return err -- } -- if f.File.Name == nil { -- continue // no package declaration +-// enclosingBlock returns the innermost block enclosing the specified +-// AST node, specified in the form of a path from the root of the file, +-// [file...n]. +-func enclosingBlock(info *types.Info, stack []ast.Node) *types.Scope { +- for i := range stack { +- n := stack[len(stack)-1-i] +- // For some reason, go/types always associates a +- // function's scope with its FuncType. +- // TODO(adonovan): feature or a bug? +- switch f := n.(type) { +- case *ast.FuncDecl: +- n = f.Type +- case *ast.FuncLit: +- n = f.Type - } -- -- edit, err := posEdit(f.Tok, f.File.Name.Pos(), f.File.Name.End(), string(newName)) -- if err != nil { -- return err +- if b := info.Scopes[n]; b != nil { +- return b - } -- edits[f.URI] = append(edits[f.URI], edit) - } -- -- return nil +- panic("no Scope for *ast.File") -} - --// renameImports computes the set of edits to imports resulting from renaming --// the package described by the given metadata, to a package with import path --// newPath and name newName. --// --// Edits are written into the edits map. --func renameImports(ctx context.Context, snapshot Snapshot, m *Metadata, newPath ImportPath, newName PackageName, allEdits map[span.URI][]diff.Edit) error { -- rdeps, err := snapshot.ReverseDependencies(ctx, m.ID, false) // find direct importers -- if err != nil { -- return err +-func (r *renamer) checkLabel(label *types.Label) { +- // Check there are no identical labels in the function's label block. +- // (Label blocks don't nest, so this is easy.) +- if prev := label.Parent().Lookup(r.to); prev != nil { +- r.errorf(label.Pos(), "renaming this label %q to %q", label.Name(), prev.Name()) +- r.errorf(prev.Pos(), "\twould conflict with this one") - } +-} - -- // Pass 1: rename import paths in import declarations. -- needsTypeCheck := make(map[PackageID][]span.URI) -- for _, rdep := range rdeps { -- if rdep.IsIntermediateTestVariant() { -- continue // for renaming, these variants are redundant -- } +-// checkStructField checks that the field renaming will not cause +-// conflicts at its declaration, or ambiguity or changes to any selection. +-func (r *renamer) checkStructField(from *types.Var) { - -- for _, uri := range rdep.CompiledGoFiles { -- fh, err := snapshot.GetFile(ctx, uri) -- if err != nil { -- return err +- // If this is the declaring package, check that the struct +- // declaration is free of field conflicts, and field/method +- // conflicts. +- // +- // go/types offers no easy way to get from a field (or interface +- // method) to its declaring struct (or interface), so we must +- // ascend the AST. +- if pgf, ok := enclosingFile(r.pkg, from.Pos()); ok { +- path, _ := astutil.PathEnclosingInterval(pgf.File, from.Pos(), from.Pos()) +- // path matches this pattern: +- // [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File] +- +- // Ascend to FieldList. +- var i int +- for { +- if _, ok := path[i].(*ast.FieldList); ok { +- break - } -- f, err := snapshot.ParseGo(ctx, fh, ParseHeader) -- if err != nil { -- return err +- i++ +- } +- i++ +- tStruct := path[i].(*ast.StructType) +- i++ +- // Ascend past parens (unlikely). +- for { +- _, ok := path[i].(*ast.ParenExpr) +- if !ok { +- break - } -- if f.File.Name == nil { -- continue // no package declaration +- i++ +- } +- if spec, ok := path[i].(*ast.TypeSpec); ok { +- // This struct is also a named type. +- // We must check for direct (non-promoted) field/field +- // and method/field conflicts. +- named := r.pkg.GetTypesInfo().Defs[spec.Name].Type() +- prev, indices, _ := types.LookupFieldOrMethod(named, true, r.pkg.GetTypes(), r.to) +- if len(indices) == 1 { +- r.errorf(from.Pos(), "renaming this field %q to %q", +- from.Name(), r.to) +- r.errorf(prev.Pos(), "\twould conflict with this %s", +- objectKind(prev)) +- return // skip checkSelections to avoid redundant errors - } -- for _, imp := range f.File.Imports { -- if rdep.DepsByImpPath[UnquoteImportPath(imp)] != m.ID { -- continue // not the import we're looking for -- } -- -- // If the import does not explicitly specify -- // a local name, then we need to invoke the -- // type checker to locate references to update. -- // -- // TODO(adonovan): is this actually true? -- // Renaming an import with a local name can still -- // cause conflicts: shadowing of built-ins, or of -- // package-level decls in the same or another file. -- if imp.Name == nil { -- needsTypeCheck[rdep.ID] = append(needsTypeCheck[rdep.ID], uri) -- } -- -- // Create text edit for the import path (string literal). -- edit, err := posEdit(f.Tok, imp.Path.Pos(), imp.Path.End(), strconv.Quote(string(newPath))) -- if err != nil { -- return err +- } else { +- // This struct is not a named type. +- // We need only check for direct (non-promoted) field/field conflicts. +- T := r.pkg.GetTypesInfo().Types[tStruct].Type.Underlying().(*types.Struct) +- for i := 0; i < T.NumFields(); i++ { +- if prev := T.Field(i); prev.Name() == r.to { +- r.errorf(from.Pos(), "renaming this field %q to %q", +- from.Name(), r.to) +- r.errorf(prev.Pos(), "\twould conflict with this field") +- return // skip checkSelections to avoid redundant errors - } -- allEdits[uri] = append(allEdits[uri], edit) - } - } - } - -- // If the imported package's name hasn't changed, -- // we don't need to rename references within each file. -- if newName == m.Name { -- return nil +- // Renaming an anonymous field requires renaming the type too. e.g. +- // print(s.T) // if we rename T to U, +- // type T int // this and +- // var s struct {T} // this must change too. +- if from.Anonymous() { +- if named, ok := from.Type().(*types.Named); ok { +- r.check(named.Obj()) +- } else if named, ok := Deref(from.Type()).(*types.Named); ok { +- r.check(named.Obj()) +- } - } - -- // Pass 2: rename local name (types.PkgName) of imported -- // package throughout one or more files of the package. -- ids := make([]PackageID, 0, len(needsTypeCheck)) -- for id := range needsTypeCheck { -- ids = append(ids, id) -- } -- pkgs, err := snapshot.TypeCheck(ctx, ids...) -- if err != nil { -- return err -- } -- for i, id := range ids { -- pkg := pkgs[i] -- for _, uri := range needsTypeCheck[id] { -- f, err := pkg.File(uri) -- if err != nil { -- return err -- } -- for _, imp := range f.File.Imports { -- if imp.Name != nil { -- continue // has explicit local name -- } -- if rdeps[id].DepsByImpPath[UnquoteImportPath(imp)] != m.ID { -- continue // not the import we're looking for -- } +- // Check integrity of existing (field and method) selections. +- r.checkSelections(from) +-} - -- pkgname := pkg.GetTypesInfo().Implicits[imp].(*types.PkgName) +-// checkSelections checks that all uses and selections that resolve to +-// the specified object would continue to do so after the renaming. +-func (r *renamer) checkSelections(from types.Object) { +- pkg := r.pkg +- typ := pkg.GetTypes() +- { +- if id := someUse(pkg.GetTypesInfo(), from); id != nil { +- if !r.checkExport(id, typ, from) { +- return +- } +- } - -- pkgScope := pkg.GetTypes().Scope() -- fileScope := pkg.GetTypesInfo().Scopes[f.File] -- -- localName := string(newName) -- try := 0 -- -- // Keep trying with fresh names until one succeeds. -- // -- // TODO(adonovan): fix: this loop is not sufficient to choose a name -- // that is guaranteed to be conflict-free; renameObj may still fail. -- // So the retry loop should be around renameObj, and we shouldn't -- // bother with scopes here. -- for fileScope.Lookup(localName) != nil || pkgScope.Lookup(localName) != nil { -- try++ -- localName = fmt.Sprintf("%s%d", newName, try) -- } +- for syntax, sel := range pkg.GetTypesInfo().Selections { +- // There may be extant selections of only the old +- // name or only the new name, so we must check both. +- // (If neither, the renaming is sound.) +- // +- // In both cases, we wish to compare the lengths +- // of the implicit field path (Selection.Index) +- // to see if the renaming would change it. +- // +- // If a selection that resolves to 'from', when renamed, +- // would yield a path of the same or shorter length, +- // this indicates ambiguity or a changed referent, +- // analogous to same- or sub-block lexical conflict. +- // +- // If a selection using the name 'to' would +- // yield a path of the same or shorter length, +- // this indicates ambiguity or shadowing, +- // analogous to same- or super-block lexical conflict. - -- // renameObj detects various conflicts, including: -- // - new name conflicts with a package-level decl in this file; -- // - new name hides a package-level decl in another file that -- // is actually referenced in this file; -- // - new name hides a built-in that is actually referenced -- // in this file; -- // - a reference in this file to the old package name would -- // become shadowed by an intervening declaration that -- // uses the new name. -- // It returns the edits if no conflict was detected. -- editMap, _, err := renameObjects(ctx, snapshot, localName, pkg, pkgname) -- if err != nil { -- return err -- } +- // TODO(adonovan): fix: derive from Types[syntax.X].Mode +- // TODO(adonovan): test with pointer, value, addressable value. +- isAddressable := true - -- // If the chosen local package name matches the package's -- // new name, delete the change that would have inserted -- // an explicit local name, which is always the lexically -- // first change. -- if localName == string(newName) { -- edits, ok := editMap[uri] -- if !ok { -- return fmt.Errorf("internal error: no changes for %s", uri) +- if sel.Obj() == from { +- if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), r.to); obj != nil { +- // Renaming this existing selection of +- // 'from' may block access to an existing +- // type member named 'to'. +- delta := len(indices) - len(sel.Index()) +- if delta > 0 { +- continue // no ambiguity - } -- diff.SortEdits(edits) -- editMap[uri] = edits[1:] +- r.selectionConflict(from, delta, syntax, obj) +- return - } -- for uri, edits := range editMap { -- allEdits[uri] = append(allEdits[uri], edits...) +- } else if sel.Obj().Name() == r.to { +- if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.Pkg(), from.Name()); obj == from { +- // Renaming 'from' may cause this existing +- // selection of the name 'to' to change +- // its meaning. +- delta := len(indices) - len(sel.Index()) +- if delta > 0 { +- continue // no ambiguity +- } +- r.selectionConflict(from, -delta, syntax, sel.Obj()) +- return - } - } - } - } -- return nil -} - --// renameObjects computes the edits to the type-checked syntax package pkg --// required to rename a set of target objects to newName. --// --// It also returns the set of objects that were found (due to --// corresponding methods and embedded fields) to require renaming as a --// consequence of the requested renamings. --// --// It returns an error if the renaming would cause a conflict. --func renameObjects(ctx context.Context, snapshot Snapshot, newName string, pkg Package, targets ...types.Object) (map[span.URI][]diff.Edit, map[types.Object]bool, error) { -- r := renamer{ -- pkg: pkg, -- objsToUpdate: make(map[types.Object]bool), -- from: targets[0].Name(), -- to: newName, -- } -- -- // A renaming initiated at an interface method indicates the -- // intention to rename abstract and concrete methods as needed -- // to preserve assignability. -- // TODO(adonovan): pull this into the caller. -- for _, obj := range targets { -- if obj, ok := obj.(*types.Func); ok { -- recv := obj.Type().(*types.Signature).Recv() -- if recv != nil && types.IsInterface(recv.Type().Underlying()) { -- r.changeMethods = true -- break -- } -- } -- } +-func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj types.Object) { +- r.errorf(from.Pos(), "renaming this %s %q to %q", +- objectKind(from), from.Name(), r.to) - -- // Check that the renaming of the identifier is ok. -- for _, obj := range targets { -- r.check(obj) -- if len(r.conflicts) > 0 { -- // Stop at first error. -- return nil, nil, fmt.Errorf("%s", strings.Join(r.conflicts, "\n")) -- } +- switch { +- case delta < 0: +- // analogous to sub-block conflict +- r.errorf(syntax.Sel.Pos(), +- "\twould change the referent of this selection") +- r.errorf(obj.Pos(), "\tof this %s", objectKind(obj)) +- case delta == 0: +- // analogous to same-block conflict +- r.errorf(syntax.Sel.Pos(), +- "\twould make this reference ambiguous") +- r.errorf(obj.Pos(), "\twith this %s", objectKind(obj)) +- case delta > 0: +- // analogous to super-block conflict +- r.errorf(syntax.Sel.Pos(), +- "\twould shadow this selection") +- r.errorf(obj.Pos(), "\tof the %s declared here", +- objectKind(obj)) - } +-} - -- editMap, err := r.update() -- if err != nil { -- return nil, nil, err +-// checkMethod performs safety checks for renaming a method. +-// There are three hazards: +-// - declaration conflicts +-// - selection ambiguity/changes +-// - entailed renamings of assignable concrete/interface types. +-// +-// We reject renamings initiated at concrete methods if it would +-// change the assignability relation. For renamings of abstract +-// methods, we rename all methods transitively coupled to it via +-// assignability. +-func (r *renamer) checkMethod(from *types.Func) { +- // e.g. error.Error +- if from.Pkg() == nil { +- r.errorf(from.Pos(), "you cannot rename built-in method %s", from) +- return - } - -- // Remove initial targets so that only 'consequences' remain. -- for _, obj := range targets { -- delete(r.objsToUpdate, obj) -- } -- return editMap, r.objsToUpdate, nil --} +- // ASSIGNABILITY: We reject renamings of concrete methods that +- // would break a 'satisfy' constraint; but renamings of abstract +- // methods are allowed to proceed, and we rename affected +- // concrete and abstract methods as necessary. It is the +- // initial method that determines the policy. - --// Rename all references to the target objects. --func (r *renamer) update() (map[span.URI][]diff.Edit, error) { -- result := make(map[span.URI][]diff.Edit) +- // Check for conflict at point of declaration. +- // Check to ensure preservation of assignability requirements. +- R := recv(from).Type() +- if types.IsInterface(R) { +- // Abstract method - -- // shouldUpdate reports whether obj is one of (or an -- // instantiation of one of) the target objects. -- shouldUpdate := func(obj types.Object) bool { -- if r.objsToUpdate[obj] { -- return true -- } -- if fn, ok := obj.(*types.Func); ok && r.objsToUpdate[funcOrigin(fn)] { -- return true +- // declaration +- prev, _, _ := types.LookupFieldOrMethod(R, false, from.Pkg(), r.to) +- if prev != nil { +- r.errorf(from.Pos(), "renaming this interface method %q to %q", +- from.Name(), r.to) +- r.errorf(prev.Pos(), "\twould conflict with this method") +- return - } -- return false -- } - -- // Find all identifiers in the package that define or use a -- // renamed object. We iterate over info as it is more efficent -- // than calling ast.Inspect for each of r.pkg.CompiledGoFiles(). -- type item struct { -- node ast.Node // Ident, ImportSpec (obj=PkgName), or CaseClause (obj=Var) -- obj types.Object -- isDef bool -- } -- var items []item -- info := r.pkg.GetTypesInfo() -- for id, obj := range info.Uses { -- if shouldUpdate(obj) { -- items = append(items, item{id, obj, false}) -- } -- } -- for id, obj := range info.Defs { -- if shouldUpdate(obj) { -- items = append(items, item{id, obj, true}) -- } -- } -- for node, obj := range info.Implicits { -- if shouldUpdate(obj) { -- switch node.(type) { -- case *ast.ImportSpec, *ast.CaseClause: -- items = append(items, item{node, obj, true}) +- // Check all interfaces that embed this one for +- // declaration conflicts too. +- { +- // Start with named interface types (better errors) +- for _, obj := range r.pkg.GetTypesInfo().Defs { +- if obj, ok := obj.(*types.TypeName); ok && types.IsInterface(obj.Type()) { +- f, _, _ := types.LookupFieldOrMethod( +- obj.Type(), false, from.Pkg(), from.Name()) +- if f == nil { +- continue +- } +- t, _, _ := types.LookupFieldOrMethod( +- obj.Type(), false, from.Pkg(), r.to) +- if t == nil { +- continue +- } +- r.errorf(from.Pos(), "renaming this interface method %q to %q", +- from.Name(), r.to) +- r.errorf(t.Pos(), "\twould conflict with this method") +- r.errorf(obj.Pos(), "\tin named interface type %q", obj.Name()) +- } - } -- } -- } -- sort.Slice(items, func(i, j int) bool { -- return items[i].node.Pos() < items[j].node.Pos() -- }) -- -- // Update each identifier. -- for _, item := range items { -- pgf, ok := enclosingFile(r.pkg, item.node.Pos()) -- if !ok { -- bug.Reportf("edit does not belong to syntax of package %q", r.pkg) -- continue -- } - -- // Renaming a types.PkgName may result in the addition or removal of an identifier, -- // so we deal with this separately. -- if pkgName, ok := item.obj.(*types.PkgName); ok && item.isDef { -- edit, err := r.updatePkgName(pgf, pkgName) -- if err != nil { -- return nil, err +- // Now look at all literal interface types (includes named ones again). +- for e, tv := range r.pkg.GetTypesInfo().Types { +- if e, ok := e.(*ast.InterfaceType); ok { +- _ = e +- _ = tv.Type.(*types.Interface) +- // TODO(adonovan): implement same check as above. +- } - } -- result[pgf.URI] = append(result[pgf.URI], edit) -- continue - } - -- // Workaround the unfortunate lack of a Var object -- // for x in "switch x := expr.(type) {}" by adjusting -- // the case clause to the switch ident. -- // This may result in duplicate edits, but we de-dup later. -- if _, ok := item.node.(*ast.CaseClause); ok { -- path, _ := astutil.PathEnclosingInterval(pgf.File, item.obj.Pos(), item.obj.Pos()) -- item.node = path[0].(*ast.Ident) -- } +- // assignability +- // +- // Find the set of concrete or abstract methods directly +- // coupled to abstract method 'from' by some +- // satisfy.Constraint, and rename them too. +- for key := range r.satisfy() { +- // key = (lhs, rhs) where lhs is always an interface. - -- // Replace the identifier with r.to. -- edit, err := posEdit(pgf.Tok, item.node.Pos(), item.node.End(), r.to) -- if err != nil { -- return nil, err -- } +- lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) +- if lsel == nil { +- continue +- } +- rmethods := r.msets.MethodSet(key.RHS) +- rsel := rmethods.Lookup(from.Pkg(), from.Name()) +- if rsel == nil { +- continue +- } - -- result[pgf.URI] = append(result[pgf.URI], edit) +- // If both sides have a method of this name, +- // and one of them is m, the other must be coupled. +- var coupled *types.Func +- switch from { +- case lsel.Obj(): +- coupled = rsel.Obj().(*types.Func) +- case rsel.Obj(): +- coupled = lsel.Obj().(*types.Func) +- default: +- continue +- } - -- if !item.isDef { // uses do not have doc comments to update. -- continue -- } +- // We must treat concrete-to-interface +- // constraints like an implicit selection C.f of +- // each interface method I.f, and check that the +- // renaming leaves the selection unchanged and +- // unambiguous. +- // +- // Fun fact: the implicit selection of C.f +- // type I interface{f()} +- // type C struct{I} +- // func (C) g() +- // var _ I = C{} // here +- // yields abstract method I.f. This can make error +- // messages less than obvious. +- // +- if !types.IsInterface(key.RHS) { +- // The logic below was derived from checkSelections. - -- doc := docComment(pgf, item.node.(*ast.Ident)) -- if doc == nil { -- continue -- } +- rtosel := rmethods.Lookup(from.Pkg(), r.to) +- if rtosel != nil { +- rto := rtosel.Obj().(*types.Func) +- delta := len(rsel.Index()) - len(rtosel.Index()) +- if delta < 0 { +- continue // no ambiguity +- } - -- // Perform the rename in doc comments declared in the original package. -- // go/parser strips out \r\n returns from the comment text, so go -- // line-by-line through the comment text to get the correct positions. -- docRegexp := regexp.MustCompile(`\b` + r.from + `\b`) // valid identifier => valid regexp -- for _, comment := range doc.List { -- if isDirective(comment.Text) { -- continue -- } -- // TODO(adonovan): why are we looping over lines? -- // Just run the loop body once over the entire multiline comment. -- lines := strings.Split(comment.Text, "\n") -- tokFile := pgf.Tok -- commentLine := tokFile.Line(comment.Pos()) -- uri := span.URIFromPath(tokFile.Name()) -- for i, line := range lines { -- lineStart := comment.Pos() -- if i > 0 { -- lineStart = tokFile.LineStart(commentLine + i) -- } -- for _, locs := range docRegexp.FindAllIndex([]byte(line), -1) { -- edit, err := posEdit(tokFile, lineStart+token.Pos(locs[0]), lineStart+token.Pos(locs[1]), r.to) -- if err != nil { -- return nil, err // can't happen +- // TODO(adonovan): record the constraint's position. +- keyPos := token.NoPos +- +- r.errorf(from.Pos(), "renaming this method %q to %q", +- from.Name(), r.to) +- if delta == 0 { +- // analogous to same-block conflict +- r.errorf(keyPos, "\twould make the %s method of %s invoked via interface %s ambiguous", +- r.to, key.RHS, key.LHS) +- r.errorf(rto.Pos(), "\twith (%s).%s", +- recv(rto).Type(), r.to) +- } else { +- // analogous to super-block conflict +- r.errorf(keyPos, "\twould change the %s method of %s invoked via interface %s", +- r.to, key.RHS, key.LHS) +- r.errorf(coupled.Pos(), "\tfrom (%s).%s", +- recv(coupled).Type(), r.to) +- r.errorf(rto.Pos(), "\tto (%s).%s", +- recv(rto).Type(), r.to) - } -- result[uri] = append(result[uri], edit) +- return // one error is enough - } - } +- +- if !r.changeMethods { +- // This should be unreachable. +- r.errorf(from.Pos(), "internal error: during renaming of abstract method %s", from) +- r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s", coupled) +- r.errorf(from.Pos(), "\tPlease file a bug report") +- return +- } +- +- // Rename the coupled method to preserve assignability. +- r.check(coupled) - } -- } +- } else { +- // Concrete method - -- return result, nil --} +- // declaration +- prev, indices, _ := types.LookupFieldOrMethod(R, true, from.Pkg(), r.to) +- if prev != nil && len(indices) == 1 { +- r.errorf(from.Pos(), "renaming this method %q to %q", +- from.Name(), r.to) +- r.errorf(prev.Pos(), "\twould conflict with this %s", +- objectKind(prev)) +- return +- } - --// docComment returns the doc for an identifier within the specified file. --func docComment(pgf *ParsedGoFile, id *ast.Ident) *ast.CommentGroup { -- nodes, _ := astutil.PathEnclosingInterval(pgf.File, id.Pos(), id.End()) -- for _, node := range nodes { -- switch decl := node.(type) { -- case *ast.FuncDecl: -- return decl.Doc -- case *ast.Field: -- return decl.Doc -- case *ast.GenDecl: -- return decl.Doc -- // For {Type,Value}Spec, if the doc on the spec is absent, -- // search for the enclosing GenDecl -- case *ast.TypeSpec: -- if decl.Doc != nil { -- return decl.Doc +- // assignability +- // +- // Find the set of abstract methods coupled to concrete +- // method 'from' by some satisfy.Constraint, and rename +- // them too. +- // +- // Coupling may be indirect, e.g. I.f <-> C.f via type D. +- // +- // type I interface {f()} +- // type C int +- // type (C) f() +- // type D struct{C} +- // var _ I = D{} +- // +- for key := range r.satisfy() { +- // key = (lhs, rhs) where lhs is always an interface. +- if types.IsInterface(key.RHS) { +- continue - } -- case *ast.ValueSpec: -- if decl.Doc != nil { -- return decl.Doc +- rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name()) +- if rsel == nil || rsel.Obj() != from { +- continue // rhs does not have the method - } -- case *ast.Ident: -- case *ast.AssignStmt: -- // *ast.AssignStmt doesn't have an associated comment group. -- // So, we try to find a comment just before the identifier. -- -- // Try to find a comment group only for short variable declarations (:=). -- if decl.Tok != token.DEFINE { -- return nil +- lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name()) +- if lsel == nil { +- continue - } +- imeth := lsel.Obj().(*types.Func) - -- identLine := pgf.Tok.Line(id.Pos()) -- for _, comment := range nodes[len(nodes)-1].(*ast.File).Comments { -- if comment.Pos() > id.Pos() { -- // Comment is after the identifier. -- continue -- } +- // imeth is the abstract method (e.g. I.f) +- // and key.RHS is the concrete coupling type (e.g. D). +- if !r.changeMethods { +- r.errorf(from.Pos(), "renaming this method %q to %q", +- from.Name(), r.to) +- var pos token.Pos +- var iface string - -- lastCommentLine := pgf.Tok.Line(comment.End()) -- if lastCommentLine+1 == identLine { -- return comment +- I := recv(imeth).Type() +- if named, ok := I.(*types.Named); ok { +- pos = named.Obj().Pos() +- iface = "interface " + named.Obj().Name() +- } else { +- pos = from.Pos() +- iface = I.String() - } +- r.errorf(pos, "\twould make %s no longer assignable to %s", +- key.RHS, iface) +- r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)", +- I, from.Name()) +- return // one error is enough - } -- default: -- return nil +- +- // Rename the coupled interface method to preserve assignability. +- r.check(imeth) - } - } -- return nil +- +- // Check integrity of existing (field and method) selections. +- // We skip this if there were errors above, to avoid redundant errors. +- r.checkSelections(from) -} - --// updatePkgName returns the updates to rename a pkgName in the import spec by --// only modifying the package name portion of the import declaration. --func (r *renamer) updatePkgName(pgf *ParsedGoFile, pkgName *types.PkgName) (diff.Edit, error) { -- // Modify ImportSpec syntax to add or remove the Name as needed. -- path, _ := astutil.PathEnclosingInterval(pgf.File, pkgName.Pos(), pkgName.Pos()) -- if len(path) < 2 { -- return diff.Edit{}, fmt.Errorf("no path enclosing interval for %s", pkgName.Name()) -- } -- spec, ok := path[1].(*ast.ImportSpec) -- if !ok { -- return diff.Edit{}, fmt.Errorf("failed to update PkgName for %s", pkgName.Name()) +-func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool { +- // Reject cross-package references if r.to is unexported. +- // (Such references may be qualified identifiers or field/method +- // selections.) +- if !ast.IsExported(r.to) && pkg != from.Pkg() { +- r.errorf(from.Pos(), +- "renaming %q to %q would make it unexported", +- from.Name(), r.to) +- r.errorf(id.Pos(), "\tbreaking references from packages such as %q", +- pkg.Path()) +- return false - } +- return true +-} - -- newText := "" -- if pkgName.Imported().Name() != r.to { -- newText = r.to + " " +-// satisfy returns the set of interface satisfaction constraints. +-func (r *renamer) satisfy() map[satisfy.Constraint]bool { +- if r.satisfyConstraints == nil { +- // Compute on demand: it's expensive. +- var f satisfy.Finder +- pkg := r.pkg +- { +- // From satisfy.Finder documentation: +- // +- // The package must be free of type errors, and +- // info.{Defs,Uses,Selections,Types} must have been populated by the +- // type-checker. +- // +- // Only proceed if all packages have no errors. +- if len(pkg.GetParseErrors()) > 0 || len(pkg.GetTypeErrors()) > 0 { +- r.errorf(token.NoPos, // we don't have a position for this error. +- "renaming %q to %q not possible because %q has errors", +- r.from, r.to, pkg.Metadata().PkgPath) +- return nil +- } +- f.Find(pkg.GetTypesInfo(), pkg.GetSyntax()) +- } +- r.satisfyConstraints = f.Result - } +- return r.satisfyConstraints +-} - -- // Replace the portion (possibly empty) of the spec before the path: -- // local "path" or "path" -- // -> <- -><- -- return posEdit(pgf.Tok, spec.Pos(), spec.Path.Pos(), newText) +-// -- helpers ---------------------------------------------------------- +- +-// recv returns the method's receiver. +-func recv(meth *types.Func) *types.Var { +- return meth.Type().(*types.Signature).Recv() -} - --// parsePackageNameDecl is a convenience function that parses and --// returns the package name declaration of file fh, and reports --// whether the position ppos lies within it. --// --// Note: also used by references2. --func parsePackageNameDecl(ctx context.Context, snapshot Snapshot, fh FileHandle, ppos protocol.Position) (*ParsedGoFile, bool, error) { -- pgf, err := snapshot.ParseGo(ctx, fh, ParseHeader) -- if err != nil { -- return nil, false, err +-// someUse returns an arbitrary use of obj within info. +-func someUse(info *types.Info, obj types.Object) *ast.Ident { +- for id, o := range info.Uses { +- if o == obj { +- return id +- } - } -- // Careful: because we used ParseHeader, -- // pgf.Pos(ppos) may be beyond EOF => (0, err). -- pos, _ := pgf.PositionPos(ppos) -- return pgf, pgf.File.Name.Pos() <= pos && pos <= pgf.File.Name.End(), nil +- return nil -} - --// enclosingFile returns the CompiledGoFile of pkg that contains the specified position. --func enclosingFile(pkg Package, pos token.Pos) (*ParsedGoFile, bool) { -- for _, pgf := range pkg.CompiledGoFiles() { -- if pgf.File.Pos() <= pos && pos <= pgf.File.End() { -- return pgf, true +-func objectKind(obj types.Object) string { +- if obj == nil { +- return "nil object" +- } +- switch obj := obj.(type) { +- case *types.PkgName: +- return "imported package name" +- case *types.TypeName: +- return "type" +- case *types.Var: +- if obj.IsField() { +- return "field" +- } +- case *types.Func: +- if obj.Type().(*types.Signature).Recv() != nil { +- return "method" - } - } -- return nil, false +- // label, func, var, const +- return strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types.")) -} - --// posEdit returns an edit to replace the (start, end) range of tf with 'new'. --func posEdit(tf *token.File, start, end token.Pos, new string) (diff.Edit, error) { -- startOffset, endOffset, err := safetoken.Offsets(tf, start, end) -- if err != nil { -- return diff.Edit{}, err +-// NB: for renamings, blank is not considered valid. +-func isValidIdentifier(id string) bool { +- if id == "" || id == "_" { +- return false - } -- return diff.Edit{Start: startOffset, End: endOffset, New: new}, nil +- for i, r := range id { +- if !isLetter(r) && (i == 0 || !isDigit(r)) { +- return false +- } +- } +- return token.Lookup(id) == token.IDENT +-} +- +-// isLocal reports whether obj is local to some function. +-// Precondition: not a struct field or interface method. +-func isLocal(obj types.Object) bool { +- // [... 5=stmt 4=func 3=file 2=pkg 1=universe] +- var depth int +- for scope := obj.Parent(); scope != nil; scope = scope.Parent() { +- depth++ +- } +- return depth >= 4 +-} +- +-func isPackageLevel(obj types.Object) bool { +- if obj == nil { +- return false +- } +- return obj.Pkg().Scope().Lookup(obj.Name()) == obj +-} +- +-// -- Plundered from go/scanner: --------------------------------------- +- +-func isLetter(ch rune) bool { +- return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) +-} +- +-func isDigit(ch rune) bool { +- return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) -} diff -urN a/gopls/internal/lsp/source/signature_help.go b/gopls/internal/lsp/source/signature_help.go --- a/gopls/internal/lsp/source/signature_help.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/signature_help.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/signature_help.go 1970-01-01 08:00:00 @@ -1,185 +0,0 @@ -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -85429,7 +94306,7 @@ diff -urN a/gopls/internal/lsp/source/signature_help.go b/gopls/internal/lsp/sou - - // We need full type-checking here, as we must type-check function bodies in - // order to provide signature help at the requested position. -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, 0, fmt.Errorf("getting file for SignatureHelp: %w", err) - } @@ -85523,7 +94400,7 @@ diff -urN a/gopls/internal/lsp/source/signature_help.go b/gopls/internal/lsp/sou - } - return &protocol.SignatureInformation{ - Label: name + s.Format(), -- Documentation: stringToSigInfoDocumentation(s.doc, snapshot.View().Options()), +- Documentation: stringToSigInfoDocumentation(s.doc, snapshot.Options()), - Parameters: paramInfo, - }, activeParam, nil -} @@ -85540,7 +94417,7 @@ diff -urN a/gopls/internal/lsp/source/signature_help.go b/gopls/internal/lsp/sou - activeParam := activeParameter(callExpr, len(sig.params), sig.variadic, pos) - return &protocol.SignatureInformation{ - Label: sig.name + sig.Format(), -- Documentation: stringToSigInfoDocumentation(sig.doc, snapshot.View().Options()), +- Documentation: stringToSigInfoDocumentation(sig.doc, snapshot.Options()), - Parameters: paramInfo, - }, activeParam, nil - @@ -85591,8 +94468,8 @@ diff -urN a/gopls/internal/lsp/source/signature_help.go b/gopls/internal/lsp/sou -} diff -urN a/gopls/internal/lsp/source/stub.go b/gopls/internal/lsp/source/stub.go --- a/gopls/internal/lsp/source/stub.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/stub.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,238 +0,0 @@ ++++ b/gopls/internal/lsp/source/stub.go 1970-01-01 08:00:00 +@@ -1,250 +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. @@ -85613,10 +94490,12 @@ diff -urN a/gopls/internal/lsp/source/stub.go b/gopls/internal/lsp/source/stub.g - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/ast/astutil" +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/analysis/stubmethods" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/safetoken" -- "golang.org/x/tools/internal/bug" +- "golang.org/x/tools/internal/diff" +- "golang.org/x/tools/internal/tokeninternal" - "golang.org/x/tools/internal/typeparams" -) - @@ -85624,7 +94503,7 @@ diff -urN a/gopls/internal/lsp/source/stub.go b/gopls/internal/lsp/source/stub.g -// methods of the concrete type that is assigned to an interface type -// at the cursor position. -func stubSuggestedFixFunc(ctx context.Context, snapshot Snapshot, fh FileHandle, rng protocol.Range) (*token.FileSet, *analysis.SuggestedFix, error) { -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, nil, fmt.Errorf("GetTypedFile: %w", err) - } @@ -85654,7 +94533,7 @@ diff -urN a/gopls/internal/lsp/source/stub.go b/gopls/internal/lsp/source/stub.g - if err != nil { - return nil, nil, fmt.Errorf("failed to parse file %q declaring implementation type: %w", declPGF.URI, err) - } -- if declPGF.Fixed { +- if declPGF.Fixed() { - return nil, nil, fmt.Errorf("file contains parse errors: %s", declPGF.URI) - } - @@ -85719,6 +94598,12 @@ diff -urN a/gopls/internal/lsp/source/stub.go b/gopls/internal/lsp/source/stub.g - var newImports []newImport // for AddNamedImport - qual := func(pkg *types.Package) string { - // TODO(adonovan): don't ignore vendor prefix. +- // +- // Ignore the current package import. +- if pkg.Path() == conc.Pkg().Path() { +- return "" +- } +- - importPath := ImportPath(pkg.Path()) - name, ok := importEnv[importPath] - if !ok { @@ -85756,7 +94641,7 @@ diff -urN a/gopls/internal/lsp/source/stub.go b/gopls/internal/lsp/source/stub.g - // Format the new methods. - var newMethods bytes.Buffer - for _, method := range missing { -- fmt.Fprintf(&newMethods, `// %s implements %s +- fmt.Fprintf(&newMethods, `// %s implements %s. -func (%s%s%s) %s%s { - panic("unimplemented") -} @@ -85818,22 +94703,26 @@ diff -urN a/gopls/internal/lsp/source/stub.go b/gopls/internal/lsp/source/stub.g - } - - // Report the diff. -- diffs := snapshot.View().Options().ComputeEdits(string(input), output.String()) -- var edits []analysis.TextEdit +- diffs := snapshot.Options().ComputeEdits(string(input), output.String()) +- return tokeninternal.FileSetFor(declPGF.Tok), // edits use declPGF.Tok +- &analysis.SuggestedFix{TextEdits: diffToTextEdits(declPGF.Tok, diffs)}, +- nil +-} +- +-func diffToTextEdits(tok *token.File, diffs []diff.Edit) []analysis.TextEdit { +- edits := make([]analysis.TextEdit, 0, len(diffs)) - for _, edit := range diffs { - edits = append(edits, analysis.TextEdit{ -- Pos: declPGF.Tok.Pos(edit.Start), -- End: declPGF.Tok.Pos(edit.End), +- Pos: tok.Pos(edit.Start), +- End: tok.Pos(edit.End), - NewText: []byte(edit.New), - }) - } -- return FileSetFor(declPGF.Tok), // edits use declPGF.Tok -- &analysis.SuggestedFix{TextEdits: edits}, -- nil +- return edits -} diff -urN a/gopls/internal/lsp/source/symbols.go b/gopls/internal/lsp/source/symbols.go --- a/gopls/internal/lsp/source/symbols.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/symbols.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/lsp/source/symbols.go 1970-01-01 08:00:00 @@ -1,227 +0,0 @@ -// Copyright 2019 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -86064,8 +94953,8 @@ diff -urN a/gopls/internal/lsp/source/symbols.go b/gopls/internal/lsp/source/sym -} diff -urN a/gopls/internal/lsp/source/type_definition.go b/gopls/internal/lsp/source/type_definition.go --- a/gopls/internal/lsp/source/type_definition.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/type_definition.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,55 +0,0 @@ ++++ b/gopls/internal/lsp/source/type_definition.go 1970-01-01 08:00:00 +@@ -1,57 +0,0 @@ -// Copyright 2023 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. @@ -86077,6 +94966,7 @@ diff -urN a/gopls/internal/lsp/source/type_definition.go b/gopls/internal/lsp/so - "fmt" - "go/token" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/internal/event" -) @@ -86086,7 +94976,7 @@ diff -urN a/gopls/internal/lsp/source/type_definition.go b/gopls/internal/lsp/so - ctx, done := event.Start(ctx, "source.TypeDefinition") - defer done() - -- pkg, pgf, err := PackageForFile(ctx, snapshot, fh.URI(), NarrowestPackage) +- pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI()) - if err != nil { - return nil, err - } @@ -86103,3320 +94993,6711 @@ diff -urN a/gopls/internal/lsp/source/type_definition.go b/gopls/internal/lsp/so - return nil, nil - } - -- typObj := typeToObject(obj.Type()) -- if typObj == nil { +- tname := typeToObject(obj.Type()) +- if tname == nil { - return nil, fmt.Errorf("no type definition for %s", obj.Name()) - } - -- // Identifiers with the type "error" are a special case with no position. -- if hasErrorType(typObj) { -- // TODO(rfindley): we can do better here, returning a link to the builtin -- // file. +- if !tname.Pos().IsValid() { +- // The only defined types with no position are error and comparable. +- if tname.Name() != "error" && tname.Name() != "comparable" { +- bug.Reportf("unexpected type name with no position: %s", tname) +- } - return nil, nil - } - -- loc, err := mapPosition(ctx, pkg.FileSet(), snapshot, typObj.Pos(), typObj.Pos()+token.Pos(len(typObj.Name()))) +- loc, err := mapPosition(ctx, pkg.FileSet(), snapshot, tname.Pos(), tname.Pos()+token.Pos(len(tname.Name()))) - if err != nil { - return nil, err - } - return []protocol.Location{loc}, nil -} -diff -urN a/gopls/internal/lsp/source/types_format.go b/gopls/internal/lsp/source/types_format.go ---- a/gopls/internal/lsp/source/types_format.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/types_format.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,517 +0,0 @@ --// Copyright 2020 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/source/typerefs/doc.go b/gopls/internal/lsp/source/typerefs/doc.go +--- a/gopls/internal/lsp/source/typerefs/doc.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/typerefs/doc.go 1970-01-01 08:00:00 +@@ -1,151 +0,0 @@ +-// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - --package source +-// Package typerefs extracts symbol-level reachability information +-// from the syntax of a Go package. +-// +-// # Background +-// +-// The goal of this analysis is to determine, for each package P, a nearly +-// minimal set of packages that could affect the type checking of P. This set +-// may contain false positives, but the smaller this set the better we can +-// invalidate and prune packages in gopls. +-// +-// More precisely, for each package P we define the set of "reachable" packages +-// from P as the set of packages that may affect the (deep) export data of the +-// direct dependencies of P. By this definition, the complement of this set +-// cannot affect any information derived from type checking P, such as +-// diagnostics, cross references, or method sets. Therefore we need not +-// invalidate any results for P when a package in the complement of this set +-// changes. +-// +-// # Computing references +-// +-// For a given declaration D, references are computed based on identifiers or +-// dotted identifiers referenced in the declaration of D, that may affect +-// the type of D. However, these references reflect only local knowledge of the +-// package and its dependency metadata, and do not depend on any analysis of +-// the dependencies themselves. This allows the reference information for +-// a package to be cached independent of all others. +-// +-// Specifically, if a referring identifier I appears in the declaration, we +-// record an edge from D to each object possibly referenced by I. We search for +-// references within type syntax, but do not actually type-check, so we can't +-// reliably determine whether an expression is a type or a term, or whether a +-// function is a builtin or generic. For example, the type of x in var x = +-// p.F(W) only depends on W if p.F is a builtin or generic function, which we +-// cannot know without type-checking package p. So we may over-approximate in +-// this way. +-// +-// - If I is declared in the current package, record a reference to its +-// declaration. +-// - Otherwise, if there are any dot imports in the current +-// file and I is exported, record a (possibly dangling) edge to +-// the corresponding declaration in each dot-imported package. +-// +-// If a dotted identifier q.I appears in the declaration, we +-// perform a similar operation: +-// +-// - If q is declared in the current package, we record a reference to that +-// object. It may be a var or const that has a field or method I. +-// - Otherwise, if q is a valid import name based on imports in the current file +-// and the provided metadata for dependency package names, record a +-// reference to the object I in that package. +-// - Additionally, handle the case where Q is exported, and Q.I may refer to +-// a field or method in a dot-imported package. +-// +-// That is essentially the entire algorithm, though there is some subtlety to +-// visiting the set of identifiers or dotted identifiers that may affect the +-// declaration type. See the visitDeclOrSpec function for the details of this +-// analysis. Notably, we also skip identifiers that refer to type parameters in +-// generic declarations. +-// +-// # Graph optimizations +-// +-// The references extracted from the syntax are used to construct +-// edges between nodes representing declarations. Edges are of two +-// kinds: internal references, from one package-level declaration to +-// another; and external references, from a symbol in this package to +-// a symbol imported from a direct dependency. +-// +-// Once the symbol reference graph is constructed, we find its +-// strongly connected components (SCCs) using Tarjan's algorithm. +-// As we coalesce the nodes of each SCC we compute the union of +-// external references reached by each package-level declaration. +-// The final result is the mapping from each exported package-level +-// declaration to the set of external (imported) declarations that it +-// reaches. +-// +-// Because it is common for many package members to have the same +-// reachability, the result takes the form of a set of equivalence +-// classes, each mapping a set of package-level declarations to a set +-// of external symbols. We use a hash table to canonicalize sets so that +-// repeated occurrences of the same set (which are common) are only +-// represented once in memory or in the file system. +-// For example, all declarations that ultimately reference only +-// {fmt.Println,strings.Join} would be classed as equivalent. +-// +-// This approach was inspired by the Hash-Value Numbering (HVN) +-// optimization described by Hardekopf and Lin. See +-// golang.org/x/tools/go/pointer/hvn.go for an implementation. (Like +-// pointer analysis, this problem is fundamentally one of graph +-// reachability.) The HVN algorithm takes the compression a step +-// further by preserving the topology of the SCC DAG, in which edges +-// represent "is a superset of" constraints. Redundant edges that +-// don't increase the solution can be deleted. We could apply the same +-// technique here to further reduce the worst-case size of the result, +-// but the current implementation seems adequate. +-// +-// # API +-// +-// The main entry point for this analysis is the [Encode] function, +-// which implements the analysis described above for one package, and +-// encodes the result as a binary message. +-// +-// The [Decode] function decodes the message into a usable form: a set +-// of equivalence classes. The decoder uses a shared [PackageIndex] to +-// enable more compact representations of sets of packages +-// ([PackageSet]) during the global reacahability computation. +-// +-// The [BuildPackageGraph] constructor implements a whole-graph analysis similar +-// to that which will be implemented by gopls, but for various reasons the +-// logic for this analysis will eventually live in the +-// [golang.org/x/tools/gopls/internal/lsp/cache] package. Nevertheless, +-// BuildPackageGraph and its test serve to verify the syntactic analysis, and +-// may serve as a proving ground for new optimizations of the whole-graph analysis. +-// +-// # Export data is insufficient +-// +-// At first it may seem that the simplest way to implement this analysis would +-// be to consider the types.Packages of the dependencies of P, for example +-// during export. After all, it makes sense that the type checked packages +-// themselves could describe their dependencies. However, this does not work as +-// type information does not describe certain syntactic relationships. +-// +-// For example, the following scenarios cause type information to miss +-// syntactic relationships: +-// +-// Named type forwarding: +-// +-// package a; type A b.B +-// package b; type B int +-// +-// Aliases: +-// +-// package a; func A(f b.B) +-// package b; type B = func() +-// +-// Initializers: +-// +-// package a; var A = b.B() +-// package b; func B() string { return "hi" } +-// +-// Use of the unsafe package: +-// +-// package a; type A [unsafe.Sizeof(B{})]int +-// package b; type B struct { f1, f2, f3 int } +-// +-// In all of these examples, types do not contain information about the edge +-// between the a.A and b.B declarations. +-package typerefs +diff -urN a/gopls/internal/lsp/source/typerefs/packageset.go b/gopls/internal/lsp/source/typerefs/packageset.go +--- a/gopls/internal/lsp/source/typerefs/packageset.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/typerefs/packageset.go 1970-01-01 08:00:00 +@@ -1,148 +0,0 @@ +-// Copyright 2023 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 typerefs - -import ( -- "bytes" -- "context" - "fmt" -- "go/ast" -- "go/doc" -- "go/printer" -- "go/token" -- "go/types" +- "math/bits" +- "sort" - "strings" +- "sync" - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/internal/bug" -- "golang.org/x/tools/internal/event" -- "golang.org/x/tools/internal/event/tag" -- "golang.org/x/tools/internal/typeparams" +- "golang.org/x/tools/gopls/internal/lsp/source" -) - --// FormatType returns the detail and kind for a types.Type. --func FormatType(typ types.Type, qf types.Qualifier) (detail string, kind protocol.CompletionItemKind) { -- if types.IsInterface(typ) { -- detail = "interface{...}" -- kind = protocol.InterfaceCompletion -- } else if _, ok := typ.(*types.Struct); ok { -- detail = "struct{...}" -- kind = protocol.StructCompletion -- } else if typ != typ.Underlying() { -- detail, kind = FormatType(typ.Underlying(), qf) -- } else { -- detail = types.TypeString(typ, qf) -- kind = protocol.ClassCompletion -- } -- return detail, kind +-// PackageIndex stores common data to enable efficient representation of +-// references and package sets. +-type PackageIndex struct { +- // For now, PackageIndex just indexes package ids, to save space and allow for +- // faster unions via sparse int vectors. +- mu sync.Mutex +- ids []source.PackageID +- m map[source.PackageID]IndexID -} - --type signature struct { -- name, doc string -- typeParams, params, results []string -- variadic bool -- needResultParens bool +-// NewPackageIndex creates a new PackageIndex instance for use in building +-// reference and package sets. +-func NewPackageIndex() *PackageIndex { +- return &PackageIndex{ +- m: make(map[source.PackageID]IndexID), +- } -} - --func (s *signature) Format() string { -- var b strings.Builder -- b.WriteByte('(') -- for i, p := range s.params { -- if i > 0 { -- b.WriteString(", ") -- } -- b.WriteString(p) +-// IndexID returns the packageIdx referencing id, creating one if id is not yet +-// tracked by the receiver. +-func (index *PackageIndex) IndexID(id source.PackageID) IndexID { +- index.mu.Lock() +- defer index.mu.Unlock() +- if i, ok := index.m[id]; ok { +- return i - } -- b.WriteByte(')') +- i := IndexID(len(index.ids)) +- index.m[id] = i +- index.ids = append(index.ids, id) +- return i +-} - -- // Add space between parameters and results. -- if len(s.results) > 0 { -- b.WriteByte(' ') -- } -- if s.needResultParens { -- b.WriteByte('(') -- } -- for i, r := range s.results { -- if i > 0 { -- b.WriteString(", ") -- } -- b.WriteString(r) -- } -- if s.needResultParens { -- b.WriteByte(')') +-// PackageID returns the PackageID for idx. +-// +-// idx must have been created by this PackageIndex instance. +-func (index *PackageIndex) PackageID(idx IndexID) source.PackageID { +- index.mu.Lock() +- defer index.mu.Unlock() +- return index.ids[idx] +-} +- +-// A PackageSet is a set of source.PackageIDs, optimized for inuse memory +-// footprint and efficient union operations. +-type PackageSet struct { +- // PackageSet is a sparse int vector of package indexes from parent. +- parent *PackageIndex +- sparse map[int]blockType // high bits in key, set of low bits in value +-} +- +-type blockType = uint // type of each sparse vector element +-const blockSize = bits.UintSize +- +-// NewSet creates a new PackageSet bound to this PackageIndex instance. +-// +-// PackageSets may only be combined with other PackageSets from the same +-// instance. +-func (index *PackageIndex) NewSet() *PackageSet { +- return &PackageSet{ +- parent: index, +- sparse: make(map[int]blockType), - } -- return b.String() -} - --func (s *signature) TypeParams() []string { -- return s.typeParams +-// DeclaringPackage returns the ID of the symbol's declaring package. +-// The package index must be the one used during decoding. +-func (index *PackageIndex) DeclaringPackage(sym Symbol) source.PackageID { +- return index.PackageID(sym.Package) -} - --func (s *signature) Params() []string { -- return s.params +-// Add records a new element in the package set, for the provided package ID. +-func (s *PackageSet) AddPackage(id source.PackageID) { +- s.Add(s.parent.IndexID(id)) -} - --// NewBuiltinSignature returns signature for the builtin object with a given --// name, if a builtin object with the name exists. --func NewBuiltinSignature(ctx context.Context, s Snapshot, name string) (*signature, error) { -- builtin, err := s.BuiltinFile(ctx) -- if err != nil { -- return nil, err +-// Add records a new element in the package set. +-// It is the caller's responsibility to ensure that idx was created with the +-// same PackageIndex as the PackageSet. +-func (s *PackageSet) Add(idx IndexID) { +- i := int(idx) +- s.sparse[i/blockSize] |= 1 << (i % blockSize) +-} +- +-// Union records all elements from other into the receiver, mutating the +-// receiver set but not the argument set. The receiver must not be nil, but the +-// argument set may be nil. +-// +-// Precondition: both package sets were created with the same PackageIndex. +-func (s *PackageSet) Union(other *PackageSet) { +- if other == nil { +- return // e.g. unsafe - } -- obj := builtin.File.Scope.Lookup(name) -- if obj == nil { -- return nil, fmt.Errorf("no builtin object for %s", name) +- if other.parent != s.parent { +- panic("other set is from a different PackageIndex instance") - } -- decl, ok := obj.Decl.(*ast.FuncDecl) -- if !ok { -- return nil, fmt.Errorf("no function declaration for builtin: %s", name) +- for k, v := range other.sparse { +- if v0 := s.sparse[k]; v0 != v { +- s.sparse[k] = v0 | v +- } - } -- if decl.Type == nil { -- return nil, fmt.Errorf("no type for builtin decl %s", decl.Name) +-} +- +-// Contains reports whether id is contained in the receiver set. +-func (s *PackageSet) Contains(id source.PackageID) bool { +- i := int(s.parent.IndexID(id)) +- return s.sparse[i/blockSize]&(1<<(i%blockSize)) != 0 +-} +- +-// Elems calls f for each element of the set in ascending order. +-func (s *PackageSet) Elems(f func(IndexID)) { +- blockIndexes := make([]int, 0, len(s.sparse)) +- for k := range s.sparse { +- blockIndexes = append(blockIndexes, k) - } -- var variadic bool -- if decl.Type.Params.List != nil { -- numParams := len(decl.Type.Params.List) -- lastParam := decl.Type.Params.List[numParams-1] -- if _, ok := lastParam.Type.(*ast.Ellipsis); ok { -- variadic = true +- sort.Ints(blockIndexes) +- for _, i := range blockIndexes { +- v := s.sparse[i] +- for b := 0; b < blockSize; b++ { +- if (v & (1 << b)) != 0 { +- f(IndexID(i*blockSize + b)) +- } - } - } -- fset := FileSetFor(builtin.Tok) -- params, _ := formatFieldList(ctx, fset, decl.Type.Params, variadic) -- results, needResultParens := formatFieldList(ctx, fset, decl.Type.Results, false) -- d := decl.Doc.Text() -- switch s.View().Options().HoverKind { -- case SynopsisDocumentation: -- d = doc.Synopsis(d) -- case NoDocumentation: -- d = "" -- } -- return &signature{ -- doc: d, -- name: name, -- needResultParens: needResultParens, -- params: params, -- results: results, -- variadic: variadic, -- }, nil -} - --// replacer replaces some synthetic "type classes" used in the builtin file --// with their most common constituent type. --var replacer = strings.NewReplacer( -- `ComplexType`, `complex128`, -- `FloatType`, `float64`, -- `IntegerType`, `int`, +-// String returns a human-readable representation of the set: {A, B, ...}. +-func (s *PackageSet) String() string { +- var ids []string +- s.Elems(func(id IndexID) { +- ids = append(ids, string(s.parent.PackageID(id))) +- }) +- return fmt.Sprintf("{%s}", strings.Join(ids, ", ")) +-} +diff -urN a/gopls/internal/lsp/source/typerefs/pkggraph_test.go b/gopls/internal/lsp/source/typerefs/pkggraph_test.go +--- a/gopls/internal/lsp/source/typerefs/pkggraph_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/typerefs/pkggraph_test.go 1970-01-01 08:00:00 +@@ -1,243 +0,0 @@ +-// Copyright 2023 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 typerefs_test +- +-// This file is logically part of the test in pkgrefs_test.go: that +-// file defines the test assertion logic; this file provides a +-// reference implementation of a client of the typerefs package. +- +-import ( +- "bytes" +- "context" +- "fmt" +- "os" +- "runtime" +- "sync" +- +- "golang.org/x/sync/errgroup" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/lsp/source/typerefs" +- "golang.org/x/tools/gopls/internal/span" -) - --func formatFieldList(ctx context.Context, fset *token.FileSet, list *ast.FieldList, variadic bool) ([]string, bool) { -- if list == nil { -- return nil, false +-const ( +- // trace enables additional trace output to stdout, for debugging. +- // +- // Warning: produces a lot of output! Best to run with small package queries. +- trace = false +-) +- +-// A Package holds reference information for a single package. +-type Package struct { +- // metadata holds metadata about this package and its dependencies. +- metadata *source.Metadata +- +- // transitiveRefs records, for each exported declaration in the package, the +- // transitive set of packages within the containing graph that are +- // transitively reachable through references, starting with the given decl. +- transitiveRefs map[string]*typerefs.PackageSet +- +- // ReachesViaDeps records the set of packages in the containing graph whose +- // syntax may affect the current package's types. See the package +- // documentation for more details of what this means. +- ReachesByDeps *typerefs.PackageSet +-} +- +-// A PackageGraph represents a fully analyzed graph of packages and their +-// dependencies. +-type PackageGraph struct { +- pkgIndex *typerefs.PackageIndex +- meta source.MetadataSource +- parse func(context.Context, span.URI) (*source.ParsedGoFile, error) +- +- mu sync.Mutex +- packages map[source.PackageID]*futurePackage +-} +- +-// BuildPackageGraph analyzes the package graph for the requested ids, whose +-// metadata is described by meta. +-// +-// The provided parse function is used to parse the CompiledGoFiles of each package. +-// +-// The resulting PackageGraph is fully evaluated, and may be investigated using +-// the Package method. +-// +-// See the package documentation for more information on the package reference +-// algorithm. +-func BuildPackageGraph(ctx context.Context, meta source.MetadataSource, ids []source.PackageID, parse func(context.Context, span.URI) (*source.ParsedGoFile, error)) (*PackageGraph, error) { +- g := &PackageGraph{ +- pkgIndex: typerefs.NewPackageIndex(), +- meta: meta, +- parse: parse, +- packages: make(map[source.PackageID]*futurePackage), - } -- var writeResultParens bool -- var result []string -- for i := 0; i < len(list.List); i++ { -- if i >= 1 { -- writeResultParens = true -- } -- p := list.List[i] -- cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 4} -- b := &bytes.Buffer{} -- if err := cfg.Fprint(b, fset, p.Type); err != nil { -- event.Error(ctx, "unable to print type", nil, tag.Type.Of(p.Type)) -- continue -- } -- typ := replacer.Replace(b.String()) -- if len(p.Names) == 0 { -- result = append(result, typ) -- } -- for _, name := range p.Names { -- if name.Name != "" { -- if i == 0 { -- writeResultParens = true -- } -- result = append(result, fmt.Sprintf("%s %s", name.Name, typ)) -- } else { -- result = append(result, typ) -- } -- } +- source.SortPostOrder(meta, ids) +- +- workers := runtime.GOMAXPROCS(0) +- if trace { +- workers = 1 - } -- if variadic { -- result[len(result)-1] = strings.Replace(result[len(result)-1], "[]", "...", 1) +- +- var eg errgroup.Group +- eg.SetLimit(workers) +- for _, id := range ids { +- id := id +- eg.Go(func() error { +- _, err := g.Package(ctx, id) +- return err +- }) - } -- return result, writeResultParens +- return g, eg.Wait() -} - --// FormatTypeParams turns TypeParamList into its Go representation, such as: --// [T, Y]. Note that it does not print constraints as this is mainly used for --// formatting type params in method receivers. --func FormatTypeParams(tparams *typeparams.TypeParamList) string { -- if tparams == nil || tparams.Len() == 0 { -- return "" -- } -- var buf bytes.Buffer -- buf.WriteByte('[') -- for i := 0; i < tparams.Len(); i++ { -- if i > 0 { -- buf.WriteString(", ") +-// futurePackage is a future result of analyzing a package, for use from Package only. +-type futurePackage struct { +- done chan struct{} +- pkg *Package +- err error +-} +- +-// Package gets the result of analyzing references for a single package. +-func (g *PackageGraph) Package(ctx context.Context, id source.PackageID) (*Package, error) { +- g.mu.Lock() +- fut, ok := g.packages[id] +- if ok { +- g.mu.Unlock() +- select { +- case <-fut.done: +- case <-ctx.Done(): +- return nil, ctx.Err() - } -- buf.WriteString(tparams.At(i).Obj().Name()) +- } else { +- fut = &futurePackage{done: make(chan struct{})} +- g.packages[id] = fut +- g.mu.Unlock() +- fut.pkg, fut.err = g.buildPackage(ctx, id) +- close(fut.done) - } -- buf.WriteByte(']') -- return buf.String() +- return fut.pkg, fut.err -} - --// NewSignature returns formatted signature for a types.Signature struct. --func NewSignature(ctx context.Context, s Snapshot, pkg Package, sig *types.Signature, comment *ast.CommentGroup, qf types.Qualifier, mq MetadataQualifier) (*signature, error) { -- var tparams []string -- tpList := typeparams.ForSignature(sig) -- for i := 0; i < tpList.Len(); i++ { -- tparam := tpList.At(i) -- // TODO: is it possible to reuse the logic from FormatVarType here? -- s := tparam.Obj().Name() + " " + tparam.Constraint().String() -- tparams = append(tparams, s) +-// buildPackage parses a package and extracts its reference graph. It should +-// only be called from Package. +-func (g *PackageGraph) buildPackage(ctx context.Context, id source.PackageID) (*Package, error) { +- p := &Package{ +- metadata: g.meta.Metadata(id), +- transitiveRefs: make(map[string]*typerefs.PackageSet), - } -- -- params := make([]string, 0, sig.Params().Len()) -- for i := 0; i < sig.Params().Len(); i++ { -- el := sig.Params().At(i) -- typ, err := FormatVarType(ctx, s, pkg, el, qf, mq) +- var files []*source.ParsedGoFile +- for _, filename := range p.metadata.CompiledGoFiles { +- f, err := g.parse(ctx, filename) - if err != nil { - return nil, err - } -- p := typ -- if el.Name() != "" { -- p = el.Name() + " " + typ +- files = append(files, f) +- } +- imports := make(map[source.ImportPath]*source.Metadata) +- for impPath, depID := range p.metadata.DepsByImpPath { +- if depID != "" { +- imports[impPath] = g.meta.Metadata(depID) - } -- params = append(params, p) - } - -- var needResultParens bool -- results := make([]string, 0, sig.Results().Len()) -- for i := 0; i < sig.Results().Len(); i++ { -- if i >= 1 { -- needResultParens = true -- } -- el := sig.Results().At(i) -- typ, err := FormatVarType(ctx, s, pkg, el, qf, mq) -- if err != nil { -- return nil, err -- } -- if el.Name() == "" { -- results = append(results, typ) -- } else { -- if i == 0 { -- needResultParens = true +- // Compute the symbol-level dependencies through this package. +- data := typerefs.Encode(files, id, imports) +- +- // data can be persisted in a filecache, keyed +- // by hash(id, CompiledGoFiles, imports). +- +- // This point separates the local preprocessing +- // -- of a single package (above) from the global -- +- // transitive reachability query (below). +- +- // classes records syntactic edges between declarations in this +- // package and declarations in this package or another +- // package. See the package documentation for a detailed +- // description of what these edges do (and do not) represent. +- classes := typerefs.Decode(g.pkgIndex, id, data) +- +- // Debug +- if trace && len(classes) > 0 { +- var buf bytes.Buffer +- fmt.Fprintf(&buf, "%s\n", id) +- for _, class := range classes { +- for i, name := range class.Decls { +- if i == 0 { +- fmt.Fprintf(&buf, "\t") +- } +- fmt.Fprintf(&buf, " .%s", name) +- } +- // Group symbols by package. +- var prevID PackageID +- for _, sym := range class.Refs { +- id := g.pkgIndex.DeclaringPackage(sym) +- if id != prevID { +- prevID = id +- fmt.Fprintf(&buf, "\n\t\t-> %s:", id) +- } +- fmt.Fprintf(&buf, " .%s", sym.Name) - } -- results = append(results, el.Name()+" "+typ) +- fmt.Fprintln(&buf) - } +- os.Stderr.Write(buf.Bytes()) - } -- var d string -- if comment != nil { -- d = comment.Text() -- } -- switch s.View().Options().HoverKind { -- case SynopsisDocumentation: -- d = doc.Synopsis(d) -- case NoDocumentation: -- d = "" -- } -- return &signature{ -- doc: d, -- typeParams: tparams, -- params: params, -- results: results, -- variadic: sig.Variadic(), -- needResultParens: needResultParens, -- }, nil --} - --// FormatVarType formats a *types.Var, accounting for type aliases. --// To do this, it looks in the AST of the file in which the object is declared. --// On any errors, it always falls back to types.TypeString. --// --// TODO(rfindley): this function could return the actual name used in syntax, --// for better parameter names. --func FormatVarType(ctx context.Context, snapshot Snapshot, srcpkg Package, obj *types.Var, qf types.Qualifier, mq MetadataQualifier) (string, error) { -- // TODO(rfindley): This looks wrong. The previous comment said: -- // "If the given expr refers to a type parameter, then use the -- // object's Type instead of the type parameter declaration. This helps -- // format the instantiated type as opposed to the original undeclared -- // generic type". -- // -- // But of course, if obj is a type param, we are formatting a generic type -- // and not an instantiated type. Handling for instantiated types must be done -- // at a higher level. -- // -- // Left this during refactoring in order to preserve pre-existing logic. -- if typeparams.IsTypeParam(obj.Type()) { -- return types.TypeString(obj.Type(), qf), nil -- } +- // Now compute the transitive closure of packages reachable +- // from any exported symbol of this package. +- for _, class := range classes { +- set := g.pkgIndex.NewSet() - -- if obj.Pkg() == nil || !obj.Pos().IsValid() { -- // This is defensive, though it is extremely unlikely we'll ever have a -- // builtin var. -- return types.TypeString(obj.Type(), qf), nil +- // The Refs slice is sorted by (PackageID, name), +- // so we can economize by calling g.Package only +- // when the package id changes. +- depP := p +- for _, sym := range class.Refs { +- symPkgID := g.pkgIndex.DeclaringPackage(sym) +- if symPkgID == id { +- panic("intra-package edge") +- } +- if depP.metadata.ID != symPkgID { +- // package changed +- var err error +- depP, err = g.Package(ctx, symPkgID) +- if err != nil { +- return nil, err +- } +- } +- set.Add(sym.Package) +- set.Union(depP.transitiveRefs[sym.Name]) +- } +- for _, name := range class.Decls { +- p.transitiveRefs[name] = set +- } - } - -- targetpgf, pos, err := parseFull(ctx, snapshot, srcpkg.FileSet(), obj.Pos()) +- // Finally compute the union of transitiveRefs +- // across the direct deps of this package. +- byDeps, err := g.reachesByDeps(ctx, p.metadata) - if err != nil { -- return "", err // e.g. ctx cancelled -- } -- -- targetMeta := findFileInDeps(snapshot, srcpkg.Metadata(), targetpgf.URI) -- if targetMeta == nil { -- // If we have an object from type-checking, it should exist in a file in -- // the forward transitive closure. -- return "", bug.Errorf("failed to find file %q in deps of %q", targetpgf.URI, srcpkg.Metadata().ID) +- return nil, err - } +- p.ReachesByDeps = byDeps - -- decl, spec, field := findDeclInfo([]*ast.File{targetpgf.File}, pos) +- return p, nil +-} - -- // We can't handle type parameters correctly, so we fall back on TypeString -- // for parameterized decls. -- if decl, _ := decl.(*ast.FuncDecl); decl != nil { -- if typeparams.ForFuncType(decl.Type).NumFields() > 0 { -- return types.TypeString(obj.Type(), qf), nil // in generic function +-// reachesByDeps computes the set of packages that are reachable through +-// dependencies of the package m. +-func (g *PackageGraph) reachesByDeps(ctx context.Context, m *source.Metadata) (*typerefs.PackageSet, error) { +- transitive := g.pkgIndex.NewSet() +- for _, depID := range m.DepsByPkgPath { +- dep, err := g.Package(ctx, depID) +- if err != nil { +- return nil, err - } -- if decl.Recv != nil && len(decl.Recv.List) > 0 { -- if x, _, _, _ := typeparams.UnpackIndexExpr(decl.Recv.List[0].Type); x != nil { -- return types.TypeString(obj.Type(), qf), nil // in method of generic type -- } +- transitive.AddPackage(dep.metadata.ID) +- for _, set := range dep.transitiveRefs { +- transitive.Union(set) - } - } -- if spec, _ := spec.(*ast.TypeSpec); spec != nil && typeparams.ForTypeSpec(spec).NumFields() > 0 { -- return types.TypeString(obj.Type(), qf), nil // in generic type decl -- } -- -- if field == nil { -- // TODO(rfindley): we should never reach here from an ordinary var, so -- // should probably return an error here. -- return types.TypeString(obj.Type(), qf), nil -- } -- expr := field.Type +- return transitive, nil +-} +diff -urN a/gopls/internal/lsp/source/typerefs/pkgrefs_test.go b/gopls/internal/lsp/source/typerefs/pkgrefs_test.go +--- a/gopls/internal/lsp/source/typerefs/pkgrefs_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/typerefs/pkgrefs_test.go 1970-01-01 08:00:00 +@@ -1,407 +0,0 @@ +-// Copyright 2023 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. - -- rq := requalifier(snapshot, targetpgf.File, targetMeta, mq) +-package typerefs_test - -- // The type names in the AST may not be correctly qualified. -- // Determine the package name to use based on the package that originated -- // the query and the package in which the type is declared. -- // We then qualify the value by cloning the AST node and editing it. -- expr = qualifyTypeExpr(expr, rq) +-import ( +- "bytes" +- "context" +- "flag" +- "fmt" +- "go/token" +- "go/types" +- "os" +- "sort" +- "strings" +- "sync" +- "testing" +- "time" - -- // If the request came from a different package than the one in which the -- // types are defined, we may need to modify the qualifiers. -- return FormatNodeFile(targetpgf.Tok, expr), nil --} +- "golang.org/x/tools/go/gcexportdata" +- "golang.org/x/tools/go/packages" +- "golang.org/x/tools/gopls/internal/astutil" +- "golang.org/x/tools/gopls/internal/lsp/cache" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/lsp/source/typerefs" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/packagesinternal" +- "golang.org/x/tools/internal/testenv" +-) - --// qualifyTypeExpr clones the type expression expr after re-qualifying type --// names using the given function, which accepts the current syntactic --// qualifier (possibly "" for unqualified idents), and returns a new qualifier --// (again, possibly "" if the identifier should be unqualified). --// --// The resulting expression may be inaccurate: without type-checking we don't --// properly account for "." imported identifiers or builtins. --// --// TODO(rfindley): add many more tests for this function. --func qualifyTypeExpr(expr ast.Expr, qf func(string) string) ast.Expr { -- switch expr := expr.(type) { -- case *ast.ArrayType: -- return &ast.ArrayType{ -- Lbrack: expr.Lbrack, -- Elt: qualifyTypeExpr(expr.Elt, qf), -- Len: expr.Len, -- } +-var ( +- dir = flag.String("dir", "", "dir to run go/packages from") +- query = flag.String("query", "std", "go/packages load query to use for walkdecl tests") +- verify = flag.Bool("verify", true, "whether to verify reachable packages using export data (may be slow on large graphs)") +-) - -- case *ast.BinaryExpr: -- if expr.Op != token.OR { -- return expr +-type ( +- packageName = source.PackageName +- PackageID = source.PackageID +- ImportPath = source.ImportPath +- PackagePath = source.PackagePath +- Metadata = source.Metadata +- MetadataSource = source.MetadataSource +- ParsedGoFile = source.ParsedGoFile +-) +- +-// TestBuildPackageGraph tests the BuildPackageGraph constructor, which uses +-// the reference analysis of the Refs function to build a graph of +-// relationships between packages. +-// +-// It simulates the operation of gopls at startup: packages are loaded via +-// go/packages, and their syntax+metadata analyzed to determine which packages +-// are reachable from others. +-// +-// The test then verifies that the 'load' graph (the graph of relationships in +-// export data) is a subgraph of the 'reach' graph constructed by +-// BuildPackageGraph. While doing so, it constructs some statistics about the +-// relative sizes of these graphs, along with the 'transitive imports' graph, +-// to report the effectiveness of the reachability analysis. +-// +-// The following flags affect this test: +-// - dir sets the dir from which to run go/packages +-// - query sets the go/packages query to load +-// - verify toggles the verification w.r.t. the load graph (which may be +-// prohibitively expensive with large queries). +-func TestBuildPackageGraph(t *testing.T) { +- if testing.Short() { +- t.Skip("skipping with -short: loading the packages can take a long time with a cold cache") +- } +- testenv.NeedsGoBuild(t) // for go/packages +- +- t0 := time.Now() +- exports, meta, err := load(*query, *verify) +- if err != nil { +- t.Fatalf("loading failed: %v", err) +- } +- t.Logf("loaded %d packages in %v", len(exports), time.Since(t0)) +- +- ctx := context.Background() +- var ids []PackageID +- for id := range exports { +- ids = append(ids, id) +- } +- sort.Slice(ids, func(i, j int) bool { +- return ids[i] < ids[j] +- }) +- +- t0 = time.Now() +- g, err := BuildPackageGraph(ctx, meta, ids, newParser().parse) +- if err != nil { +- t.Fatal(err) +- } +- t.Logf("building package graph took %v", time.Since(t0)) +- +- // Collect information about the edges between packages for later analysis. +- // +- // We compare the following package graphs: +- // - the imports graph: edges are transitive imports +- // - the reaches graph: edges are reachability relationships through syntax +- // of imports (as defined in the package doc) +- // - the loads graph: edges are packages loaded through the export data of +- // imports +- // +- // By definition, loads < reaches < imports. +- type edgeSet map[PackageID]map[PackageID]bool +- var ( +- imports = make(edgeSet) // A imports B transitively +- importedBy = make(edgeSet) // A is imported by B transitively +- reaches = make(edgeSet) // A reaches B through top-level declaration syntax +- reachedBy = make(edgeSet) // A is reached by B through top-level declaration syntax +- loads = make(edgeSet) // A loads B through export data of its direct dependencies +- loadedBy = make(edgeSet) // A is loaded by B through export data of B's direct dependencies +- ) +- recordEdge := func(from, to PackageID, fwd, rev edgeSet) { +- if fwd[from] == nil { +- fwd[from] = make(map[PackageID]bool) - } -- return &ast.BinaryExpr{ -- X: qualifyTypeExpr(expr.X, qf), -- OpPos: expr.OpPos, -- Op: expr.Op, -- Y: qualifyTypeExpr(expr.Y, qf), +- fwd[from][to] = true +- if rev[to] == nil { +- rev[to] = make(map[PackageID]bool) - } +- rev[to][from] = true +- } - -- case *ast.ChanType: -- return &ast.ChanType{ -- Arrow: expr.Arrow, -- Begin: expr.Begin, -- Dir: expr.Dir, -- Value: qualifyTypeExpr(expr.Value, qf), +- exportedPackages := make(map[PackageID]*types.Package) +- importPackage := func(id PackageID) *types.Package { +- exportFile := exports[id] +- if exportFile == "" { +- return nil // no exported symbols +- } +- m := meta.Metadata(id) +- tpkg, ok := exportedPackages[id] +- if !ok { +- pkgPath := string(m.PkgPath) +- tpkg, err = importFromExportData(pkgPath, exportFile) +- if err != nil { +- t.Fatalf("importFromExportData(%s, %s) failed: %v", pkgPath, exportFile, err) +- } +- exportedPackages[id] = tpkg - } +- return tpkg +- } - -- case *ast.Ellipsis: -- return &ast.Ellipsis{ -- Ellipsis: expr.Ellipsis, -- Elt: qualifyTypeExpr(expr.Elt, qf), +- for _, id := range ids { +- pkg, err := g.Package(ctx, id) +- if err != nil { +- t.Fatal(err) - } +- pkg.ReachesByDeps.Elems(func(id2 typerefs.IndexID) { +- recordEdge(id, g.pkgIndex.PackageID(id2), reaches, reachedBy) +- }) - -- case *ast.FuncType: -- return &ast.FuncType{ -- Func: expr.Func, -- Params: qualifyFieldList(expr.Params, qf), -- Results: qualifyFieldList(expr.Results, qf), +- importMap := importMap(id, meta) +- for _, id2 := range importMap { +- recordEdge(id, id2, imports, importedBy) - } - -- case *ast.Ident: -- // Unqualified type (builtin, package local, or dot-imported). +- if *verify { +- for _, depID := range meta.Metadata(id).DepsByPkgPath { +- tpkg := importPackage(depID) +- if tpkg == nil { +- continue +- } +- for _, imp := range tpkg.Imports() { +- depID, ok := importMap[PackagePath(imp.Path())] +- if !ok { +- t.Errorf("import map (len: %d) for %s missing imported types.Package %s", len(importMap), id, imp.Path()) +- continue +- } +- recordEdge(id, depID, loads, loadedBy) +- } +- } - -- // Don't qualify names that look like builtins. -- // -- // Without type-checking this may be inaccurate. It could be made accurate -- // by doing syntactic object resolution for the entire package, but that -- // does not seem worthwhile and we generally want to avoid using -- // ast.Object, which may be inaccurate. -- if obj := types.Universe.Lookup(expr.Name); obj != nil { -- return expr +- for depID := range loads[id] { +- if !pkg.ReachesByDeps.Contains(depID) { +- t.Errorf("package %s was imported by %s, but not detected as reachable", depID, id) +- } +- } - } +- } - -- newName := qf("") -- if newName != "" { -- return &ast.SelectorExpr{ -- X: &ast.Ident{ -- NamePos: expr.Pos(), -- Name: newName, -- }, -- Sel: expr, -- } +- if testing.Verbose() { +- fmt.Printf("%-52s%8s%8s%8s%8s%8s%8s\n", "package ID", "imp", "impBy", "reach", "reachBy", "load", "loadBy") +- for _, id := range ids { +- fmt.Printf("%-52s%8d%8d%8d%8d%8d%8d\n", id, len(imports[id]), len(importedBy[id]), len(reaches[id]), len(reachedBy[id]), len(loads[id]), len(loadedBy[id])) - } -- return expr +- fmt.Println(strings.Repeat("-", 100)) +- fmt.Printf("%-52s%8s%8s%8s%8s%8s%8s\n", "package ID", "imp", "impBy", "reach", "reachBy", "load", "loadBy") - -- case *ast.IndexExpr: -- return &ast.IndexExpr{ -- X: qualifyTypeExpr(expr.X, qf), -- Lbrack: expr.Lbrack, -- Index: qualifyTypeExpr(expr.Index, qf), -- Rbrack: expr.Rbrack, +- avg := func(m edgeSet) float64 { +- var avg float64 +- for _, id := range ids { +- s := m[id] +- avg += float64(len(s)) / float64(len(ids)) +- } +- return avg - } +- fmt.Printf("%52s%8.1f%8.1f%8.1f%8.1f%8.1f%8.1f\n", "averages:", avg(imports), avg(importedBy), avg(reaches), avg(reachedBy), avg(loads), avg(loadedBy)) +- } +-} - -- case *typeparams.IndexListExpr: -- indices := make([]ast.Expr, len(expr.Indices)) -- for i, idx := range expr.Indices { -- indices[i] = qualifyTypeExpr(idx, qf) +-func importMap(id PackageID, meta MetadataSource) map[PackagePath]PackageID { +- imports := make(map[PackagePath]PackageID) +- var recordIDs func(PackageID) +- recordIDs = func(id PackageID) { +- m := meta.Metadata(id) +- if _, ok := imports[m.PkgPath]; ok { +- return - } -- return &typeparams.IndexListExpr{ -- X: qualifyTypeExpr(expr.X, qf), -- Lbrack: expr.Lbrack, -- Indices: indices, -- Rbrack: expr.Rbrack, +- imports[m.PkgPath] = id +- for _, id := range m.DepsByPkgPath { +- recordIDs(id) - } +- } +- for _, id := range meta.Metadata(id).DepsByPkgPath { +- recordIDs(id) +- } +- return imports +-} - -- case *ast.InterfaceType: -- return &ast.InterfaceType{ -- Interface: expr.Interface, -- Methods: qualifyFieldList(expr.Methods, qf), -- Incomplete: expr.Incomplete, -- } +-func importFromExportData(pkgPath, exportFile string) (*types.Package, error) { +- file, err := os.Open(exportFile) +- if err != nil { +- return nil, err +- } +- r, err := gcexportdata.NewReader(file) +- if err != nil { +- file.Close() +- return nil, err +- } +- fset := token.NewFileSet() +- tpkg, err := gcexportdata.Read(r, fset, make(map[string]*types.Package), pkgPath) +- file.Close() +- if err != nil { +- return nil, err +- } +- // The export file reported by go/packages is produced by the compiler, which +- // has additional package dependencies due to inlining. +- // +- // Export and re-import so that we only observe dependencies from the +- // exported API. +- var out bytes.Buffer +- err = gcexportdata.Write(&out, fset, tpkg) +- if err != nil { +- return nil, err +- } +- return gcexportdata.Read(&out, token.NewFileSet(), make(map[string]*types.Package), pkgPath) +-} - -- case *ast.MapType: -- return &ast.MapType{ -- Map: expr.Map, -- Key: qualifyTypeExpr(expr.Key, qf), -- Value: qualifyTypeExpr(expr.Value, qf), -- } +-func BenchmarkBuildPackageGraph(b *testing.B) { +- t0 := time.Now() +- exports, meta, err := load(*query, *verify) +- if err != nil { +- b.Fatalf("loading failed: %v", err) +- } +- b.Logf("loaded %d packages in %v", len(exports), time.Since(t0)) +- ctx := context.Background() +- var ids []PackageID +- for id := range exports { +- ids = append(ids, id) +- } +- b.ResetTimer() - -- case *ast.ParenExpr: -- return &ast.ParenExpr{ -- Lparen: expr.Lparen, -- Rparen: expr.Rparen, -- X: qualifyTypeExpr(expr.X, qf), +- for i := 0; i < b.N; i++ { +- _, err := BuildPackageGraph(ctx, meta, ids, newParser().parse) +- if err != nil { +- b.Fatal(err) - } +- } +-} - -- case *ast.SelectorExpr: -- if id, ok := expr.X.(*ast.Ident); ok { -- // qualified type -- newName := qf(id.Name) -- if newName == "" { -- return expr.Sel -- } -- return &ast.SelectorExpr{ -- X: &ast.Ident{ -- NamePos: id.NamePos, -- Name: newName, -- }, -- Sel: expr.Sel, -- } -- } -- return expr +-type memoizedParser struct { +- mu sync.Mutex +- files map[span.URI]*futureParse +-} - -- case *ast.StarExpr: -- return &ast.StarExpr{ -- Star: expr.Star, -- X: qualifyTypeExpr(expr.X, qf), -- } +-type futureParse struct { +- done chan struct{} +- pgf *ParsedGoFile +- err error +-} - -- case *ast.StructType: -- return &ast.StructType{ -- Struct: expr.Struct, -- Fields: qualifyFieldList(expr.Fields, qf), -- Incomplete: expr.Incomplete, +-func newParser() *memoizedParser { +- return &memoizedParser{ +- files: make(map[span.URI]*futureParse), +- } +-} +- +-func (p *memoizedParser) parse(ctx context.Context, uri span.URI) (*ParsedGoFile, error) { +- doParse := func(ctx context.Context, uri span.URI) (*ParsedGoFile, error) { +- // TODO(adonovan): hoist this operation outside the benchmark critsec. +- content, err := os.ReadFile(uri.Filename()) +- if err != nil { +- return nil, err - } +- content = astutil.PurgeFuncBodies(content) +- pgf, _ := cache.ParseGoSrc(ctx, token.NewFileSet(), uri, content, source.ParseFull, false) +- return pgf, nil +- } - -- default: -- return expr +- p.mu.Lock() +- fut, ok := p.files[uri] +- if ok { +- p.mu.Unlock() +- select { +- case <-fut.done: +- case <-ctx.Done(): +- return nil, ctx.Err() +- } +- } else { +- fut = &futureParse{done: make(chan struct{})} +- p.files[uri] = fut +- p.mu.Unlock() +- fut.pgf, fut.err = doParse(ctx, uri) +- close(fut.done) - } +- return fut.pgf, fut.err -} - --func qualifyFieldList(fl *ast.FieldList, qf func(string) string) *ast.FieldList { -- if fl == nil { -- return nil +-type mapMetadataSource struct { +- m map[PackageID]*Metadata +-} +- +-func (s mapMetadataSource) Metadata(id PackageID) *Metadata { +- return s.m[id] +-} +- +-// This function is a compressed version of snapshot.load from the +-// internal/lsp/cache package, for use in testing. +-// +-// TODO(rfindley): it may be valuable to extract this logic from the snapshot, +-// since it is otherwise standalone. +-func load(query string, needExport bool) (map[PackageID]string, MetadataSource, error) { +- cfg := &packages.Config{ +- Dir: *dir, +- Mode: packages.NeedName | +- packages.NeedFiles | +- packages.NeedCompiledGoFiles | +- packages.NeedImports | +- packages.NeedDeps | +- packages.NeedTypesSizes | +- packages.NeedModule | +- packages.NeedEmbedFiles | +- packages.LoadMode(packagesinternal.DepsErrors) | +- packages.LoadMode(packagesinternal.ForTest), +- Tests: true, - } -- if fl.List == nil { -- return &ast.FieldList{ -- Closing: fl.Closing, -- Opening: fl.Opening, -- } +- if needExport { +- cfg.Mode |= packages.NeedExportFile // ExportFile is not requested by gopls: this is used to verify reachability - } -- list := make([]*ast.Field, 0, len(fl.List)) -- for _, f := range fl.List { -- list = append(list, &ast.Field{ -- Comment: f.Comment, -- Doc: f.Doc, -- Names: f.Names, -- Tag: f.Tag, -- Type: qualifyTypeExpr(f.Type, qf), -- }) +- pkgs, err := packages.Load(cfg, query) +- if err != nil { +- return nil, nil, err - } -- return &ast.FieldList{ -- Closing: fl.Closing, -- Opening: fl.Opening, -- List: list, +- +- meta := make(map[PackageID]*Metadata) +- var buildMetadata func(pkg *packages.Package) +- buildMetadata = func(pkg *packages.Package) { +- id := PackageID(pkg.ID) +- if meta[id] != nil { +- return +- } +- m := &Metadata{ +- ID: id, +- PkgPath: PackagePath(pkg.PkgPath), +- Name: packageName(pkg.Name), +- ForTest: PackagePath(packagesinternal.GetForTest(pkg)), +- TypesSizes: pkg.TypesSizes, +- LoadDir: cfg.Dir, +- Module: pkg.Module, +- Errors: pkg.Errors, +- DepsErrors: packagesinternal.GetDepsErrors(pkg), +- } +- meta[id] = m +- +- for _, filename := range pkg.CompiledGoFiles { +- m.CompiledGoFiles = append(m.CompiledGoFiles, span.URIFromPath(filename)) +- } +- for _, filename := range pkg.GoFiles { +- m.GoFiles = append(m.GoFiles, span.URIFromPath(filename)) +- } +- +- m.DepsByImpPath = make(map[ImportPath]PackageID) +- m.DepsByPkgPath = make(map[PackagePath]PackageID) +- for importPath, imported := range pkg.Imports { +- importPath := ImportPath(importPath) +- +- // see note in gopls/internal/lsp/cache/load.go for an explanation of this check. +- if importPath != "unsafe" && len(imported.CompiledGoFiles) == 0 { +- m.DepsByImpPath[importPath] = "" // missing +- continue +- } +- +- m.DepsByImpPath[importPath] = PackageID(imported.ID) +- m.DepsByPkgPath[PackagePath(imported.PkgPath)] = PackageID(imported.ID) +- buildMetadata(imported) +- } +- } +- +- exportFiles := make(map[PackageID]string) +- for _, pkg := range pkgs { +- exportFiles[PackageID(pkg.ID)] = pkg.ExportFile +- buildMetadata(pkg) - } +- return exportFiles, &mapMetadataSource{meta}, nil -} -diff -urN a/gopls/internal/lsp/source/util.go b/gopls/internal/lsp/source/util.go ---- a/gopls/internal/lsp/source/util.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/util.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,555 +0,0 @@ --// Copyright 2019 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/source/typerefs/refs.go b/gopls/internal/lsp/source/typerefs/refs.go +--- a/gopls/internal/lsp/source/typerefs/refs.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/typerefs/refs.go 1970-01-01 08:00:00 +@@ -1,832 +0,0 @@ +-// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - --package source +-package typerefs - -import ( -- "context" +- "fmt" - "go/ast" -- "go/printer" - "go/token" -- "go/types" -- "path/filepath" -- "regexp" - "sort" -- "strconv" - "strings" - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/safetoken" -- "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/bug" -- "golang.org/x/tools/internal/tokeninternal" +- "golang.org/x/tools/gopls/internal/astutil" +- "golang.org/x/tools/gopls/internal/lsp/frob" +- "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/internal/typeparams" -) - --// IsGenerated gets and reads the file denoted by uri and reports --// whether it contains a "generated file" comment as described at --// https://golang.org/s/generatedcode. +-// Encode analyzes the Go syntax trees of a package, constructs a +-// reference graph, and uses it to compute, for each exported +-// declaration, the set of exported symbols of directly imported +-// packages that it references, perhaps indirectly. -// --// TODO(adonovan): opt: this function does too much. --// Move snapshot.GetFile into the caller (most of which have already done it). --func IsGenerated(ctx context.Context, snapshot Snapshot, uri span.URI) bool { -- fh, err := snapshot.GetFile(ctx, uri) -- if err != nil { -- return false -- } -- pgf, err := snapshot.ParseGo(ctx, fh, ParseHeader) -- if err != nil { -- return false -- } -- for _, commentGroup := range pgf.File.Comments { -- for _, comment := range commentGroup.List { -- if matched := generatedRx.MatchString(comment.Text); matched { -- // Check if comment is at the beginning of the line in source. -- if safetoken.Position(pgf.Tok, comment.Slash).Column == 1 { -- return true -- } -- } -- } -- } -- return false +-// It returns a serializable index of this information. +-// Use Decode to expand the result. +-func Encode(files []*source.ParsedGoFile, id source.PackageID, imports map[source.ImportPath]*source.Metadata) []byte { +- return index(files, id, imports) -} - --// adjustedObjEnd returns the end position of obj, possibly modified for --// package names. +-// Decode decodes a serializable index of symbol +-// reachability produced by Encode. -// --// TODO(rfindley): eliminate this function, by inlining it at callsites where --// it makes sense. --func adjustedObjEnd(obj types.Object) token.Pos { -- nameLen := len(obj.Name()) -- if pkgName, ok := obj.(*types.PkgName); ok { -- // An imported Go package has a package-local, unqualified name. -- // When the name matches the imported package name, there is no -- // identifier in the import spec with the local package name. -- // -- // For example: -- // import "go/ast" // name "ast" matches package name -- // import a "go/ast" // name "a" does not match package name -- // -- // When the identifier does not appear in the source, have the range -- // of the object be the import path, including quotes. -- if pkgName.Imported().Name() == pkgName.Name() { -- nameLen = len(pkgName.Imported().Path()) + len(`""`) -- } -- } -- return obj.Pos() + token.Pos(nameLen) +-// Because many declarations reference the exact same set of symbols, +-// the results are grouped into equivalence classes. +-// Classes are sorted by Decls[0], ascending. +-// The class with empty reachability is omitted. +-// +-// See the package documentation for more details as to what a +-// reference does (and does not) represent. +-func Decode(pkgIndex *PackageIndex, id source.PackageID, data []byte) []Class { +- return decode(pkgIndex, id, data) -} - --// Matches cgo generated comment as well as the proposed standard: +-// A Class is a reachability equivalence class. -// --// https://golang.org/s/generatedcode --var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`) -- --// FileKindForLang returns the file kind associated with the given language ID, --// or UnknownKind if the language ID is not recognized. --func FileKindForLang(langID string) FileKind { -- switch langID { -- case "go": -- return Go -- case "go.mod": -- return Mod -- case "go.sum": -- return Sum -- case "tmpl", "gotmpl": -- return Tmpl -- case "go.work": -- return Work -- default: -- return UnknownKind -- } +-// It attests that each exported package-level declaration in Decls +-// references (perhaps indirectly) one of the external (imported) +-// symbols in Refs. +-// +-// Because many Decls reach the same Refs, +-// it is more efficient to group them into classes. +-type Class struct { +- Decls []string // sorted set of names of exported decls with same reachability +- Refs []Symbol // set of external symbols, in ascending (PackageID, Name) order -} - --// nodeAtPos returns the index and the node whose position is contained inside --// the node list. --func nodeAtPos(nodes []ast.Node, pos token.Pos) (ast.Node, int) { -- if nodes == nil { -- return nil, -1 -- } -- for i, node := range nodes { -- if node.Pos() <= pos && pos <= node.End() { -- return node, i -- } -- } -- return nil, -1 +-// A Symbol represents an external (imported) symbol +-// referenced by the analyzed package. +-type Symbol struct { +- Package IndexID // w.r.t. PackageIndex passed to decoder +- Name string -} - --// FormatNode returns the "pretty-print" output for an ast node. --func FormatNode(fset *token.FileSet, n ast.Node) string { -- var buf strings.Builder -- if err := printer.Fprint(&buf, fset, n); err != nil { -- return "" -- } -- return buf.String() --} +-// An IndexID is a small integer that uniquely identifies a package within a +-// given PackageIndex. +-type IndexID int - --// FormatNodeFile is like FormatNode, but requires only the token.File for the --// syntax containing the given ast node. --func FormatNodeFile(file *token.File, n ast.Node) string { -- fset := FileSetFor(file) -- return FormatNode(fset, n) +-// -- internals -- +- +-// A symbolSet is a set of symbols used internally during index construction. +-// +-// TODO(adonovan): opt: evaluate unifying Symbol and symbol. +-// (Encode would have to create a private PackageIndex.) +-type symbolSet map[symbol]bool +- +-// A symbol is the internal representation of an external +-// (imported) symbol referenced by the analyzed package. +-type symbol struct { +- pkg source.PackageID +- name string -} - --// FileSetFor returns a new FileSet containing a sequence of new Files with --// the same base, size, and line as the input files, for use in APIs that --// require a FileSet. +-// declNode holds information about a package-level declaration +-// (or more than one with the same name, in ill-typed code). -// --// Precondition: the input files must be non-overlapping, and sorted in order --// of their Base. --func FileSetFor(files ...*token.File) *token.FileSet { -- fset := token.NewFileSet() -- for _, f := range files { -- f2 := fset.AddFile(f.Name(), f.Base(), f.Size()) -- lines := tokeninternal.GetLines(f) -- f2.SetLines(lines) -- } -- return fset +-// It is a node in the symbol reference graph, whose outgoing edges +-// are of two kinds: intRefs and extRefs. +-type declNode struct { +- name string +- rep *declNode // canonical representative of this SCC (initially self) +- +- // outgoing graph edges +- intRefs map[*declNode]bool // to symbols in this package +- extRefs symbolSet // to imported symbols +- extRefsClass int // extRefs equivalence class number (-1 until set at end) +- +- // Tarjan's SCC algorithm +- index, lowlink int32 // Tarjan numbering +- scc int32 // -ve => on stack; 0 => unvisited; +ve => node is root of a found SCC -} - --// Deref returns a pointer's element type, traversing as many levels as needed. --// Otherwise it returns typ. +-// state holds the working state of the Refs algorithm for a single package. -// --// It can return a pointer type for cyclic types (see golang/go#45510). --func Deref(typ types.Type) types.Type { -- var seen map[types.Type]struct{} -- for { -- p, ok := typ.Underlying().(*types.Pointer) -- if !ok { -- return typ -- } -- if _, ok := seen[p.Elem()]; ok { -- return typ -- } +-// The number of distinct symbols referenced by a single package +-// (measured across all of kubernetes), was found to be: +-// - max = 1750. +-// - Several packages reference > 100 symbols. +-// - p95 = 32, p90 = 22, p50 = 8. +-type state struct { +- // numbering of unique symbol sets +- class []symbolSet // unique symbol sets +- classIndex map[string]int // index of above (using SymbolSet.hash as key) - -- typ = p.Elem() +- // Tarjan's SCC algorithm +- index int32 +- stack []*declNode +-} - -- if seen == nil { -- seen = make(map[types.Type]struct{}) -- } -- seen[typ] = struct{}{} +-// getClassIndex returns the small integer (an index into +-// state.class) that identifies the given set. +-func (st *state) getClassIndex(set symbolSet) int { +- key := classKey(set) +- i, ok := st.classIndex[key] +- if !ok { +- i = len(st.class) +- st.classIndex[key] = i +- st.class = append(st.class, set) - } +- return i -} - --func SortDiagnostics(d []*Diagnostic) { -- sort.Slice(d, func(i int, j int) bool { -- return CompareDiagnostic(d[i], d[j]) < 0 +-// appendSorted appends the symbols to syms, sorts by ascending +-// (PackageID, name), and returns the result. +-// The argument must be an empty slice, ideally with capacity len(set). +-func (set symbolSet) appendSorted(syms []symbol) []symbol { +- for sym := range set { +- syms = append(syms, sym) +- } +- sort.Slice(syms, func(i, j int) bool { +- x, y := syms[i], syms[j] +- if x.pkg != y.pkg { +- return x.pkg < y.pkg +- } +- return x.name < y.name - }) +- return syms -} - --func CompareDiagnostic(a, b *Diagnostic) int { -- if r := protocol.CompareRange(a.Range, b.Range); r != 0 { -- return r -- } -- if a.Source < b.Source { -- return -1 -- } -- if a.Source > b.Source { -- return +1 -- } -- if a.Message < b.Message { -- return -1 -- } -- if a.Message > b.Message { -- return +1 +-// classKey returns a key such that equal keys imply equal sets. +-// (e.g. a sorted string representation, or a cryptographic hash of same). +-func classKey(set symbolSet) string { +- // Sort symbols into a stable order. +- // TODO(adonovan): opt: a cheap crypto hash (e.g. BLAKE2b) might +- // make a cheaper map key than a large string. +- // Try using a hasher instead of a builder. +- var s strings.Builder +- for _, sym := range set.appendSorted(make([]symbol, 0, len(set))) { +- fmt.Fprintf(&s, "%s:%s;", sym.pkg, sym.name) - } -- return 0 +- return s.String() -} - --// findFileInDeps finds package metadata containing URI in the transitive --// dependencies of m. When using the Go command, the answer is unique. --// --// TODO(rfindley): refactor to share logic with findPackageInDeps? --func findFileInDeps(s MetadataSource, m *Metadata, uri span.URI) *Metadata { -- seen := make(map[PackageID]bool) -- var search func(*Metadata) *Metadata -- search = func(m *Metadata) *Metadata { -- if seen[m.ID] { -- return nil -- } -- seen[m.ID] = true -- for _, cgf := range m.CompiledGoFiles { -- if cgf == uri { -- return m -- } +-// index builds the reference graph and encodes the index. +-func index(pgfs []*source.ParsedGoFile, id source.PackageID, imports map[source.ImportPath]*source.Metadata) []byte { +- // First pass: gather package-level names and create a declNode for each. +- // +- // In ill-typed code, there may be multiple declarations of the +- // same name; a single declInfo node will represent them all. +- decls := make(map[string]*declNode) +- addDecl := func(id *ast.Ident) { +- if name := id.Name; name != "_" && decls[name] == nil { +- node := &declNode{name: name, extRefsClass: -1} +- node.rep = node +- decls[name] = node - } -- for _, dep := range m.DepsByPkgPath { -- m := s.Metadata(dep) -- if m == nil { -- bug.Reportf("nil metadata for %q", dep) -- continue -- } -- if found := search(m); found != nil { -- return found +- } +- for _, pgf := range pgfs { +- for _, d := range pgf.File.Decls { +- switch d := d.(type) { +- case *ast.GenDecl: +- switch d.Tok { +- case token.TYPE: +- for _, spec := range d.Specs { +- addDecl(spec.(*ast.TypeSpec).Name) +- } +- +- case token.VAR, token.CONST: +- for _, spec := range d.Specs { +- for _, ident := range spec.(*ast.ValueSpec).Names { +- addDecl(ident) +- } +- } +- } +- +- case *ast.FuncDecl: +- // non-method functions +- if d.Recv.NumFields() == 0 { +- addDecl(d.Name) +- } - } - } -- return nil - } -- return search(m) --} - --// UnquoteImportPath returns the unquoted import path of s, --// or "" if the path is not properly quoted. --func UnquoteImportPath(s *ast.ImportSpec) ImportPath { -- path, err := strconv.Unquote(s.Path.Value) -- if err != nil { -- return "" +- // Second pass: process files to collect referring identifiers. +- st := &state{classIndex: make(map[string]int)} +- for _, pgf := range pgfs { +- visitFile(pgf.File, imports, decls) - } -- return ImportPath(path) --} - --// NodeContains returns true if a node encloses a given position pos. --func NodeContains(n ast.Node, pos token.Pos) bool { -- return n != nil && n.Pos() <= pos && pos <= n.End() +- // Find the strong components of the declNode graph +- // using Tarjan's algorithm, and coalesce each component. +- st.index = 1 +- for _, decl := range decls { +- if decl.index == 0 { // unvisited +- st.visit(decl) +- } +- } +- +- // TODO(adonovan): opt: consider compressing the serialized +- // representation by recording not the classes but the DAG of +- // non-trivial union operations (the "pointer equivalence" +- // optimization of Hardekopf & Lin). Unlike that algorithm, +- // which piggybacks on SCC coalescing, in our case it would +- // be better to make a forward traversal from the exported +- // decls, since it avoids visiting unreachable nodes, and +- // results in a dense (not sparse) numbering of the sets. +- +- // Tabulate the unique reachability sets of +- // each exported package member. +- classNames := make(map[int][]string) // set of decls (names) for a given reachability set +- for name, decl := range decls { +- if !ast.IsExported(name) { +- continue +- } +- +- decl = decl.find() +- +- // Skip decls with empty reachability. +- if len(decl.extRefs) == 0 { +- continue +- } +- +- // Canonicalize the set (and memoize). +- class := decl.extRefsClass +- if class < 0 { +- class = st.getClassIndex(decl.extRefs) +- decl.extRefsClass = class +- } +- classNames[class] = append(classNames[class], name) +- } +- +- return encode(classNames, st.class) -} - --// CollectScopes returns all scopes in an ast path, ordered as innermost scope --// first. --func CollectScopes(info *types.Info, path []ast.Node, pos token.Pos) []*types.Scope { -- // scopes[i], where i 0 is consistent with go/types, +- // which reports an error but treats the declaration like a +- // normal function when Recv is non-nil but empty +- // (as in func () f()). +- if d.Recv.NumFields() > 0 { +- // Method. Associate it with the receiver. +- _, id, typeParams := astutil.UnpackRecv(d.Recv.List[0].Type) +- if id != nil { +- var tparams map[string]bool +- if len(typeParams) > 0 { +- tparams = make(map[string]bool) +- for _, tparam := range typeParams { +- if tparam.Name != "_" { +- tparams[tparam.Name] = true +- } +- } +- } +- visit(id, d, tparams) +- } +- } else { +- // Non-method. +- tparams := tparamsMap(typeparams.ForFuncType(d.Type)) +- visit(d.Name, d, tparams) - } -- return name - } -- return p.Name() - } -} - --// requalifier returns a function that re-qualifies identifiers and qualified --// identifiers contained in targetFile using the given metadata qualifier. --func requalifier(s MetadataSource, targetFile *ast.File, targetMeta *Metadata, mq MetadataQualifier) func(string) string { -- qm := map[string]string{ -- "": mq(targetMeta.Name, "", targetMeta.PkgPath), +-// tparamsMap returns a set recording each name declared by the provided field +-// list. It so happens that we only care about names declared by type parameter +-// lists. +-func tparamsMap(tparams *ast.FieldList) map[string]bool { +- if tparams == nil || len(tparams.List) == 0 { +- return nil +- } +- m := make(map[string]bool) +- for _, f := range tparams.List { +- for _, name := range f.Names { +- if name.Name != "_" { +- m[name.Name] = true +- } +- } - } +- return m +-} - -- // Construct mapping of import paths to their defined or implicit names. -- for _, imp := range targetFile.Imports { -- name, pkgName, impPath, pkgPath := importInfo(s, imp, targetMeta) +-// A refVisitor visits referring identifiers and dotted identifiers. +-// +-// For a referring identifier I, name="I" and sel="". For a dotted identifier +-// q.I, name="q" and sel="I". +-type refVisitor = func(name, sel string) - -- // Re-map the target name for the source file. -- qm[name] = mq(pkgName, impPath, pkgPath) -- } +-// visitDeclOrSpec visits referring idents or dotted idents that may affect +-// the type of the declaration at the given node, which must be an ast.Decl or +-// ast.Spec. +-func visitDeclOrSpec(node ast.Node, f refVisitor) { +- // Declarations +- switch n := node.(type) { +- // ImportSpecs should not appear here, and will panic in the default case. - -- return func(name string) string { -- if newName, ok := qm[name]; ok { -- return newName +- case *ast.ValueSpec: +- // Skip Doc, Names, Comments, which do not affect the decl type. +- // Initializers only affect the type of a value spec if the type is unset. +- if n.Type != nil { +- visitExpr(n.Type, f) +- } else { // only need to walk expr list if type is nil +- visitExprList(n.Values, f) - } -- return name +- +- case *ast.TypeSpec: +- // Skip Doc, Name, and Comment, which do not affect the decl type. +- if tparams := typeparams.ForTypeSpec(n); tparams != nil { +- visitFieldList(tparams, f) +- } +- visitExpr(n.Type, f) +- +- case *ast.BadDecl: +- // nothing to do +- +- // We should not reach here with a GenDecl, so panic below in the default case. +- +- case *ast.FuncDecl: +- // Skip Doc, Name, and Body, which do not affect the type. +- // Recv is handled by Refs: methods are associated with their type. +- visitExpr(n.Type, f) +- +- default: +- panic(fmt.Sprintf("unexpected node type %T", node)) - } -} - --// A MetadataQualifier is a function that qualifies an identifier declared in a --// package with the given package name, import path, and package path. +-// visitExpr visits referring idents and dotted idents that may affect the +-// type of expr. -// --// In scenarios where metadata is missing the provided PackageName and --// PackagePath may be empty, but ImportPath must always be non-empty. --type MetadataQualifier func(PackageName, ImportPath, PackagePath) string +-// visitExpr can't reliably distinguish a dotted ident pkg.X from a +-// selection expr.f or T.method. +-func visitExpr(expr ast.Expr, f refVisitor) { +- switch n := expr.(type) { +- // These four cases account for about two thirds of all nodes, +- // so we place them first to shorten the common control paths. +- // (See go.dev/cl/480915.) +- case *ast.Ident: +- f(n.Name, "") - --// MetadataQualifierForFile returns a metadata qualifier that chooses the best --// qualification of an imported package relative to the file f in package with --// metadata m. --func MetadataQualifierForFile(s MetadataSource, f *ast.File, m *Metadata) MetadataQualifier { -- // Record local names for import paths. -- localNames := make(map[ImportPath]string) // local names for imports in f -- for _, imp := range f.Imports { -- name, _, impPath, _ := importInfo(s, imp, m) -- localNames[impPath] = name -- } +- case *ast.BasicLit: +- // nothing to do - -- // Record a package path -> import path mapping. -- inverseDeps := make(map[PackageID]PackagePath) -- for path, id := range m.DepsByPkgPath { -- inverseDeps[id] = path -- } -- importsByPkgPath := make(map[PackagePath]ImportPath) // best import paths by pkgPath -- for impPath, id := range m.DepsByImpPath { -- if id == "" { -- continue +- case *ast.SelectorExpr: +- if ident, ok := n.X.(*ast.Ident); ok { +- f(ident.Name, n.Sel.Name) +- } else { +- visitExpr(n.X, f) +- // Skip n.Sel as we don't care about which field or method is selected, +- // as we'll have recorded an edge to all declarations relevant to the +- // receiver type via visiting n.X above. - } -- pkgPath := inverseDeps[id] -- _, hasPath := importsByPkgPath[pkgPath] -- _, hasImp := localNames[impPath] -- // In rare cases, there may be multiple import paths with the same package -- // path. In such scenarios, prefer an import path that already exists in -- // the file. -- if !hasPath || hasImp { -- importsByPkgPath[pkgPath] = impPath +- +- case *ast.CallExpr: +- visitExpr(n.Fun, f) +- visitExprList(n.Args, f) // args affect types for unsafe.Sizeof or builtins or generics +- +- // Expressions +- case *ast.Ellipsis: +- if n.Elt != nil { +- visitExpr(n.Elt, f) - } -- } - -- return func(pkgName PackageName, impPath ImportPath, pkgPath PackagePath) string { -- // If supplied, translate the package path to an import path in the source -- // package. -- if pkgPath != "" { -- if srcImp := importsByPkgPath[pkgPath]; srcImp != "" { -- impPath = srcImp -- } -- if pkgPath == m.PkgPath { -- return "" -- } +- case *ast.FuncLit: +- visitExpr(n.Type, f) +- // Skip Body, which does not affect the type. +- +- case *ast.CompositeLit: +- if n.Type != nil { +- visitExpr(n.Type, f) - } -- if localName, ok := localNames[impPath]; ok && impPath != "" { -- return string(localName) +- // Skip Elts, which do not affect the type. +- +- case *ast.ParenExpr: +- visitExpr(n.X, f) +- +- case *ast.IndexExpr: +- visitExpr(n.X, f) +- visitExpr(n.Index, f) // may affect type for instantiations +- +- case *typeparams.IndexListExpr: +- visitExpr(n.X, f) +- for _, index := range n.Indices { +- visitExpr(index, f) // may affect the type for instantiations - } -- if pkgName != "" { -- return string(pkgName) +- +- case *ast.SliceExpr: +- visitExpr(n.X, f) +- // skip Low, High, and Max, which do not affect type. +- +- case *ast.TypeAssertExpr: +- // Skip X, as it doesn't actually affect the resulting type of the type +- // assertion. +- if n.Type != nil { +- visitExpr(n.Type, f) - } -- idx := strings.LastIndexByte(string(impPath), '/') -- return string(impPath[idx+1:]) -- } --} - --// importInfo collects information about the import specified by imp, --// extracting its file-local name, package name, import path, and package path. --// --// If metadata is missing for the import, the resulting package name and --// package path may be empty, and the file local name may be guessed based on --// the import path. --// --// Note: previous versions of this helper used a PackageID->PackagePath map --// extracted from m, for extracting package path even in the case where --// metadata for a dep was missing. This should not be necessary, as we should --// always have metadata for IDs contained in DepsByPkgPath. --func importInfo(s MetadataSource, imp *ast.ImportSpec, m *Metadata) (string, PackageName, ImportPath, PackagePath) { -- var ( -- name string // local name -- pkgName PackageName -- impPath = UnquoteImportPath(imp) -- pkgPath PackagePath -- ) +- case *ast.StarExpr: +- visitExpr(n.X, f) - -- // If the import has a local name, use it. -- if imp.Name != nil { -- name = imp.Name.Name -- } +- case *ast.UnaryExpr: +- visitExpr(n.X, f) - -- // Try to find metadata for the import. If successful and there is no local -- // name, the package name is the local name. -- if depID := m.DepsByImpPath[impPath]; depID != "" { -- if depm := s.Metadata(depID); depm != nil { -- if name == "" { -- name = string(depm.Name) -- } -- pkgName = depm.Name -- pkgPath = depm.PkgPath +- case *ast.BinaryExpr: +- visitExpr(n.X, f) +- visitExpr(n.Y, f) +- +- case *ast.KeyValueExpr: +- panic("unreachable") // unreachable, as we don't descend into elts of composite lits. +- +- case *ast.ArrayType: +- if n.Len != nil { +- visitExpr(n.Len, f) - } -- } +- visitExpr(n.Elt, f) - -- // If the local name is still unknown, guess it based on the import path. -- if name == "" { -- idx := strings.LastIndexByte(string(impPath), '/') -- name = string(impPath[idx+1:]) +- case *ast.StructType: +- visitFieldList(n.Fields, f) +- +- case *ast.FuncType: +- if tparams := typeparams.ForFuncType(n); tparams != nil { +- visitFieldList(tparams, f) +- } +- if n.Params != nil { +- visitFieldList(n.Params, f) +- } +- if n.Results != nil { +- visitFieldList(n.Results, f) +- } +- +- case *ast.InterfaceType: +- visitFieldList(n.Methods, f) +- +- case *ast.MapType: +- visitExpr(n.Key, f) +- visitExpr(n.Value, f) +- +- case *ast.ChanType: +- visitExpr(n.Value, f) +- +- case *ast.BadExpr: +- // nothing to do +- +- default: +- panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n)) - } -- return name, pkgName, impPath, pkgPath -} - --// isDirective reports whether c is a comment directive. --// --// Copied and adapted from go/src/go/ast/ast.go. --func isDirective(c string) bool { -- if len(c) < 3 { -- return false -- } -- if c[1] != '/' { -- return false -- } -- //-style comment (no newline at the end) -- c = c[2:] -- if len(c) == 0 { -- // empty line -- return false -- } -- // "//line " is a line directive. -- // (The // has been removed.) -- if strings.HasPrefix(c, "line ") { -- return true +-func visitExprList(list []ast.Expr, f refVisitor) { +- for _, x := range list { +- visitExpr(x, f) - } +-} - -- // "//[a-z0-9]+:[a-z0-9]" -- // (The // has been removed.) -- colon := strings.Index(c, ":") -- if colon <= 0 || colon+1 >= len(c) { -- return false +-func visitFieldList(n *ast.FieldList, f refVisitor) { +- for _, field := range n.List { +- visitExpr(field.Type, f) - } -- for i := 0; i <= colon+1; i++ { -- if i == colon { -- continue +-} +- +-// -- strong component graph construction (plundered from go/pointer) -- +- +-// visit implements the depth-first search of Tarjan's SCC algorithm +-// (see https://doi.org/10.1137/0201010). +-// Precondition: x is canonical. +-func (st *state) visit(x *declNode) { +- checkCanonical(x) +- x.index = st.index +- x.lowlink = st.index +- st.index++ +- +- st.stack = append(st.stack, x) // push +- assert(x.scc == 0, "node revisited") +- x.scc = -1 +- +- for y := range x.intRefs { +- // Loop invariant: x is canonical. +- +- y := y.find() +- +- if x == y { +- continue // nodes already coalesced - } -- b := c[i] -- if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') { -- return false +- +- switch { +- case y.scc > 0: +- // y is already a collapsed SCC +- +- case y.scc < 0: +- // y is on the stack, and thus in the current SCC. +- if y.index < x.lowlink { +- x.lowlink = y.index +- } +- +- default: +- // y is unvisited; visit it now. +- st.visit(y) +- // Note: x and y are now non-canonical. +- +- x = x.find() +- +- if y.lowlink < x.lowlink { +- x.lowlink = y.lowlink +- } - } - } -- return true --} +- checkCanonical(x) - --// InDir checks whether path is in the file tree rooted at dir. --// It checks only the lexical form of the file names. --// It does not consider symbolic links. --// --// Copied from go/src/cmd/go/internal/search/search.go. --func InDir(dir, path string) bool { -- pv := strings.ToUpper(filepath.VolumeName(path)) -- dv := strings.ToUpper(filepath.VolumeName(dir)) -- path = path[len(pv):] -- dir = dir[len(dv):] -- switch { -- default: -- return false -- case pv != dv: -- return false -- case len(path) == len(dir): -- if path == dir { -- return true -- } -- return false -- case dir == "": -- return path != "" -- case len(path) > len(dir): -- if dir[len(dir)-1] == filepath.Separator { -- if path[:len(dir)] == dir { -- return path[len(dir):] != "" +- // Is x the root of an SCC? +- if x.lowlink == x.index { +- // Coalesce all nodes in the SCC. +- for { +- // Pop y from stack. +- i := len(st.stack) - 1 +- y := st.stack[i] +- st.stack = st.stack[:i] +- +- checkCanonical(x) +- checkCanonical(y) +- +- if x == y { +- break // SCC is complete. - } -- return false +- coalesce(x, y) - } -- if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir { -- if len(path) == len(dir)+1 { -- return true +- +- // Accumulate union of extRefs over +- // internal edges (to other SCCs). +- for y := range x.intRefs { +- y := y.find() +- if y == x { +- continue // already coalesced +- } +- assert(y.scc == 1, "edge to non-scc node") +- for z := range y.extRefs { +- if x.extRefs == nil { +- x.extRefs = make(symbolSet) +- } +- x.extRefs[z] = true // extRefs: x U= y - } -- return path[len(dir)+1:] != "" - } -- return false +- +- x.scc = 1 - } -} - --// IsValidImport returns whether importPkgPath is importable --// by pkgPath --func IsValidImport(pkgPath, importPkgPath PackagePath) bool { -- i := strings.LastIndex(string(importPkgPath), "/internal/") -- if i == -1 { -- return true +-// coalesce combines two nodes in the strong component graph. +-// Precondition: x and y are canonical. +-func coalesce(x, y *declNode) { +- // x becomes y's canonical representative. +- y.rep = x +- +- // x accumulates y's internal references. +- for z := range y.intRefs { +- x.intRefs[z] = true - } -- // TODO(rfindley): this looks wrong: IsCommandLineArguments is meant to -- // operate on package IDs, not package paths. -- if IsCommandLineArguments(PackageID(pkgPath)) { -- return true +- y.intRefs = nil +- +- // x accumulates y's external references. +- for z := range y.extRefs { +- if x.extRefs == nil { +- x.extRefs = make(symbolSet) +- } +- x.extRefs[z] = true - } -- // TODO(rfindley): this is wrong. mod.testx/p should not be able to -- // import mod.test/internal: https://go.dev/play/p/-Ca6P-E4V4q -- return strings.HasPrefix(string(pkgPath), string(importPkgPath[:i])) +- y.extRefs = nil -} - --// IsCommandLineArguments reports whether a given value denotes --// "command-line-arguments" package, which is a package with an unknown ID --// created by the go command. It can have a test variant, which is why callers --// should not check that a value equals "command-line-arguments" directly. --func IsCommandLineArguments(id PackageID) bool { -- return strings.Contains(string(id), "command-line-arguments") +-// find returns the canonical node decl. +-// (The nodes form a disjoint set forest.) +-func (decl *declNode) find() *declNode { +- rep := decl.rep +- if rep != decl { +- rep = rep.find() +- decl.rep = rep // simple path compression (no union-by-rank) +- } +- return rep -} - --// embeddedIdent returns the type name identifier for an embedding x, if x in a --// valid embedding. Otherwise, it returns nil. +-const debugSCC = false // enable assertions in strong-component algorithm +- +-func checkCanonical(x *declNode) { +- if debugSCC { +- assert(x == x.find(), "not canonical") +- } +-} +- +-func assert(cond bool, msg string) { +- if debugSCC && !cond { +- panic(msg) +- } +-} +- +-// -- serialization -- +- +-// (The name says gob but in fact we use frob.) +-var classesCodec = frob.CodecFor[gobClasses]() +- +-type gobClasses struct { +- Strings []string // table of strings (PackageIDs and names) +- Classes []gobClass +-} +- +-type gobClass struct { +- Decls []int32 // indices into gobClasses.Strings +- Refs []int32 // list of (package, name) pairs, each an index into gobClasses.Strings +-} +- +-// encode encodes the equivalence classes, +-// (classNames[i], classes[i]), for i in range classes. -// --// Spec: An embedded field must be specified as a type name T or as a pointer --// to a non-interface type name *T --func embeddedIdent(x ast.Expr) *ast.Ident { -- if star, ok := x.(*ast.StarExpr); ok { -- x = star.X +-// With the current encoding, across kubernetes, +-// the encoded size distribution has +-// p50 = 511B, p95 = 4.4KB, max = 108K. +-func encode(classNames map[int][]string, classes []symbolSet) []byte { +- payload := gobClasses{ +- Classes: make([]gobClass, 0, len(classNames)), - } -- switch ix := x.(type) { // check for instantiated receivers -- case *ast.IndexExpr: -- x = ix.X -- case *typeparams.IndexListExpr: -- x = ix.X +- +- // index of unique strings +- strings := make(map[string]int32) +- stringIndex := func(s string) int32 { +- i, ok := strings[s] +- if !ok { +- i = int32(len(payload.Strings)) +- strings[s] = i +- payload.Strings = append(payload.Strings, s) +- } +- return i - } -- switch x := x.(type) { -- case *ast.Ident: -- return x -- case *ast.SelectorExpr: -- if _, ok := x.X.(*ast.Ident); ok { -- return x.Sel +- +- var refs []symbol // recycled temporary +- for class, names := range classNames { +- set := classes[class] +- +- // names, sorted +- sort.Strings(names) +- gobDecls := make([]int32, len(names)) +- for i, name := range names { +- gobDecls[i] = stringIndex(name) - } +- +- // refs, sorted by ascending (PackageID, name) +- gobRefs := make([]int32, 0, 2*len(set)) +- for _, sym := range set.appendSorted(refs[:0]) { +- gobRefs = append(gobRefs, +- stringIndex(string(sym.pkg)), +- stringIndex(sym.name)) +- } +- payload.Classes = append(payload.Classes, gobClass{ +- Decls: gobDecls, +- Refs: gobRefs, +- }) - } -- return nil +- +- return classesCodec.Encode(payload) -} -diff -urN a/gopls/internal/lsp/source/view.go b/gopls/internal/lsp/source/view.go ---- a/gopls/internal/lsp/source/view.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/view.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,857 +0,0 @@ --// Copyright 2018 The Go Authors. All rights reserved. +- +-func decode(pkgIndex *PackageIndex, id source.PackageID, data []byte) []Class { +- var payload gobClasses +- classesCodec.Decode(data, &payload) +- +- classes := make([]Class, len(payload.Classes)) +- for i, gobClass := range payload.Classes { +- decls := make([]string, len(gobClass.Decls)) +- for i, decl := range gobClass.Decls { +- decls[i] = payload.Strings[decl] +- } +- refs := make([]Symbol, len(gobClass.Refs)/2) +- for i := range refs { +- pkgID := pkgIndex.IndexID(source.PackageID(payload.Strings[gobClass.Refs[2*i]])) +- name := payload.Strings[gobClass.Refs[2*i+1]] +- refs[i] = Symbol{Package: pkgID, Name: name} +- } +- classes[i] = Class{ +- Decls: decls, +- Refs: refs, +- } +- } +- +- // Sort by ascending Decls[0]. +- // TODO(adonovan): move sort to encoder. Determinism is good. +- sort.Slice(classes, func(i, j int) bool { +- return classes[i].Decls[0] < classes[j].Decls[0] +- }) +- +- return classes +-} +diff -urN a/gopls/internal/lsp/source/typerefs/refs_test.go b/gopls/internal/lsp/source/typerefs/refs_test.go +--- a/gopls/internal/lsp/source/typerefs/refs_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/typerefs/refs_test.go 1970-01-01 08:00:00 +@@ -1,558 +0,0 @@ +-// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - --package source +-package typerefs_test - -import ( -- "bytes" - "context" -- "crypto/sha256" -- "errors" - "fmt" -- "go/ast" -- "go/scanner" - "go/token" -- "go/types" -- "io" +- "sort" +- "testing" - -- "golang.org/x/mod/modfile" -- "golang.org/x/tools/go/analysis" -- "golang.org/x/tools/go/packages" -- "golang.org/x/tools/go/types/objectpath" -- "golang.org/x/tools/gopls/internal/govulncheck" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/safetoken" -- "golang.org/x/tools/gopls/internal/lsp/source/methodsets" +- "github.com/google/go-cmp/cmp" +- "golang.org/x/tools/gopls/internal/lsp/cache" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/lsp/source/typerefs" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/event/label" -- "golang.org/x/tools/internal/event/tag" -- "golang.org/x/tools/internal/gocommand" -- "golang.org/x/tools/internal/imports" -- "golang.org/x/tools/internal/packagesinternal" +- "golang.org/x/tools/internal/testenv" -) - --// A GlobalSnapshotID uniquely identifies a snapshot within this process and --// increases monotonically with snapshot creation time. --// --// We use a distinct integral type for global IDs to help enforce correct --// usage. --type GlobalSnapshotID uint64 -- --// Snapshot represents the current state for the given view. --type Snapshot interface { -- // SequenceID is the sequence id of this snapshot within its containing -- // view. -- // -- // Relative to their view sequence ids are monotonically increasing, but this -- // does not hold globally: when new views are created their initial snapshot -- // has sequence ID 0. For operations that span multiple views, use global -- // IDs. -- SequenceID() uint64 +-// TestRefs checks that the analysis reports, for each exported member +-// of the test package ("p"), its correct dependencies on exported +-// members of its direct imports (e.g. "ext"). +-func TestRefs(t *testing.T) { +- ctx := context.Background() - -- // GlobalID is a globally unique identifier for this snapshot. Global IDs are -- // monotonic: subsequent snapshots will have higher global ID, though -- // subsequent snapshots in a view may not have adjacent global IDs. -- GlobalID() GlobalSnapshotID +- tests := []struct { +- label string +- srcs []string // source for the local package; package name must be p +- imports map[string]string // for simplicity: importPath -> pkgID/pkgName (we set pkgName == pkgID); 'ext' is always available. +- want map[string][]string // decl name -> id. +- go118 bool // test uses generics +- allowErrs bool // whether we expect parsing errors +- }{ +- { +- label: "empty package", +- want: map[string][]string{}, +- }, +- { +- label: "fields", +- srcs: []string{` +-package p - -- // View returns the View associated with this snapshot. -- View() View +-import "ext" +- +-type A struct{ b B } +-type B func(c C) (d D) +-type C ext.C +-type D ext.D +- +-// Should not be referenced by field names. +-type b ext.B_ +-type c int.C_ +-type d ext.D_ +-`}, +- want: map[string][]string{ +- "A": {"ext.C", "ext.D"}, +- "B": {"ext.C", "ext.D"}, +- "C": {"ext.C"}, +- "D": {"ext.D"}, +- }, +- }, +- { +- label: "embedding", +- srcs: []string{` +-package p - -- // BackgroundContext returns a context used for all background processing -- // on behalf of this snapshot. -- BackgroundContext() context.Context +-import "ext" - -- // ValidBuildConfiguration returns true if there is some error in the -- // user's workspace. In particular, if they are both outside of a module -- // and their GOPATH. -- ValidBuildConfiguration() bool +-type A struct{ +- B +- _ struct { +- C +- } +- D +-} +-type B ext.B +-type C ext.C +-type D interface{ +- B +-} +-`}, +- want: map[string][]string{ +- "A": {"ext.B", "ext.C"}, +- "B": {"ext.B"}, +- "C": {"ext.C"}, +- "D": {"ext.B"}, +- }, +- }, +- { +- label: "constraint embedding", +- srcs: []string{` +-package p - -- // FindFile returns the FileHandle for the given URI, if it is already -- // in the given snapshot. -- FindFile(uri span.URI) FileHandle +-import "ext" - -- // GetFile returns the FileHandle for a given URI, initializing it if it is -- // not already part of the snapshot. -- GetFile(ctx context.Context, uri span.URI) (FileHandle, error) +-type A interface{ +- int | B | ~C +- struct{D} +-} - -- // AwaitInitialized waits until the snapshot's view is initialized. -- AwaitInitialized(ctx context.Context) +-type B ext.B +-type C ext.C +-type D ext.D +-`}, +- want: map[string][]string{ +- "A": {"ext.B", "ext.C", "ext.D"}, +- "B": {"ext.B"}, +- "C": {"ext.C"}, +- "D": {"ext.D"}, +- }, +- go118: true, +- }, +- { +- label: "funcs", +- srcs: []string{` +-package p - -- // IsOpen returns whether the editor currently has a file open. -- IsOpen(uri span.URI) bool +-import "ext" +- +-type A ext.A +-type B ext.B +-const C B = 2 +-func F(A) B { +- return C +-} +-var V = F(W) +-var W A +-`}, +- want: map[string][]string{ +- "A": {"ext.A"}, +- "B": {"ext.B"}, +- "C": {"ext.B"}, +- "F": {"ext.A", "ext.B"}, +- "V": { +- "ext.A", // via F +- "ext.B", // via W: can't be eliminated: F could be builtin or generic +- }, +- "W": {"ext.A"}, +- }, +- }, +- { +- label: "methods", +- srcs: []string{`package p - -- // IgnoredFile reports if a file would be ignored by a `go list` of the whole -- // workspace. -- IgnoredFile(uri span.URI) bool +-import "ext" - -- // Templates returns the .tmpl files -- Templates() map[span.URI]FileHandle +-type A ext.A +-type B ext.B +-`, `package p - -- // ParseGo returns the parsed AST for the file. -- // If the file is not available, returns nil and an error. -- // Position information is added to FileSet(). -- ParseGo(ctx context.Context, fh FileHandle, mode ParseMode) (*ParsedGoFile, error) +-func (A) M(B) +-func (*B) M(A) +-`}, +- want: map[string][]string{ +- "A": {"ext.A", "ext.B"}, +- "B": {"ext.A", "ext.B"}, +- }, +- }, +- { +- label: "initializers", +- srcs: []string{` +-package p - -- // Analyze runs the specified analyzers on the given package at this snapshot. -- Analyze(ctx context.Context, id PackageID, analyzers []*Analyzer) ([]*Diagnostic, error) +-import "ext" - -- // RunGoCommandPiped runs the given `go` command, writing its output -- // to stdout and stderr. Verb, Args, and WorkingDir must be specified. -- // -- // RunGoCommandPiped runs the command serially using gocommand.RunPiped, -- // enforcing that this command executes exclusively to other commands on the -- // server. -- RunGoCommandPiped(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation, stdout, stderr io.Writer) error +-var A b = C // type does not depend on C +-type b ext.B +-var C = d // type does depend on D +-var d b - -- // RunGoCommandDirect runs the given `go` command. Verb, Args, and -- // WorkingDir must be specified. -- RunGoCommandDirect(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, error) +-var e = d + a - -- // RunGoCommands runs a series of `go` commands that updates the go.mod -- // and go.sum file for wd, and returns their updated contents. -- RunGoCommands(ctx context.Context, allowNetwork bool, wd string, run func(invoke func(...string) (*bytes.Buffer, error)) error) (bool, []byte, []byte, error) +-var F = func() B { return E } - -- // RunProcessEnvFunc runs fn with the process env for this snapshot's view. -- // Note: the process env contains cached module and filesystem state. -- RunProcessEnvFunc(ctx context.Context, fn func(*imports.Options) error) error +-var G = struct{ +- A b +- _ [unsafe.Sizeof(ext.V)]int // array size + Sizeof creates edge to a var +- _ [unsafe.Sizeof(G)]int // creates a self edge; doesn't affect output though +-}{} - -- // ModFiles are the go.mod files enclosed in the snapshot's view and known -- // to the snapshot. -- ModFiles() []span.URI +-var H = (D + A + C*C) - -- // ParseMod is used to parse go.mod files. -- ParseMod(ctx context.Context, fh FileHandle) (*ParsedModule, error) +-var I = (A+C).F +-`}, +- want: map[string][]string{ +- "A": {"ext.B"}, +- "C": {"ext.B"}, // via d +- "G": {"ext.B", "ext.V"}, // via b,C +- "H": {"ext.B"}, // via d,A,C +- "I": {"ext.B"}, +- }, +- }, +- { +- label: "builtins", +- srcs: []string{`package p - -- // ModWhy returns the results of `go mod why` for the module specified by -- // the given go.mod file. -- ModWhy(ctx context.Context, fh FileHandle) (map[string]string, error) +-import "ext" - -- // ModTidy returns the results of `go mod tidy` for the module specified by -- // the given go.mod file. -- ModTidy(ctx context.Context, pm *ParsedModule) (*TidiedModule, error) +-var A = new(b) +-type b struct{ ext.B } - -- // ModVuln returns import vulnerability analysis for the given go.mod URI. -- // Concurrent requests are combined into a single command. -- ModVuln(ctx context.Context, modURI span.URI) (*govulncheck.Result, error) +-type C chan d +-type d ext.D - -- // GoModForFile returns the URI of the go.mod file for the given URI. -- GoModForFile(uri span.URI) span.URI +-type S []ext.S +-type t ext.T +-var U = append(([]*S)(nil), new(t)) - -- // WorkFile, if non-empty, is the go.work file for the workspace. -- WorkFile() span.URI +-type X map[k]v +-type k ext.K +-type v ext.V - -- // ParseWork is used to parse go.work files. -- ParseWork(ctx context.Context, fh FileHandle) (*ParsedWorkFile, error) +-var Z = make(map[k]A) - -- // BuiltinFile returns information about the special builtin package. -- BuiltinFile(ctx context.Context) (*ParsedGoFile, error) +-// close, delete, and panic cannot occur outside of statements +-`}, +- want: map[string][]string{ +- "A": {"ext.B"}, +- "C": {"ext.D"}, +- "S": {"ext.S"}, +- "U": {"ext.S", "ext.T"}, // ext.T edge could be eliminated +- "X": {"ext.K", "ext.V"}, +- "Z": {"ext.B", "ext.K"}, +- }, +- }, +- { +- label: "builtin shadowing", +- srcs: []string{`package p - -- // IsBuiltin reports whether uri is part of the builtin package. -- IsBuiltin(ctx context.Context, uri span.URI) bool +-import "ext" - -- // ReverseDependencies returns a new mapping whose entries are -- // the ID and Metadata of each package in the workspace that -- // directly or transitively depend on the package denoted by id, -- // excluding id itself. -- ReverseDependencies(ctx context.Context, id PackageID, transitive bool) (map[PackageID]*Metadata, error) +-var A = new(ext.B) +-func new() c +-type c ext.C +-`}, +- want: map[string][]string{ +- "A": {"ext.B", "ext.C"}, +- }, +- }, +- { +- label: "named forwarding", +- srcs: []string{`package p +- +-import "ext" +- +-type A B +-type B c +-type c ext.C +-`}, +- want: map[string][]string{ +- "A": {"ext.C"}, +- "B": {"ext.C"}, +- }, +- }, +- { +- label: "aliases", +- srcs: []string{`package p +- +-import "ext" +- +-type A = B +-type B = C +-type C = ext.C +-`}, +- want: map[string][]string{ +- "A": {"ext.C"}, +- "B": {"ext.C"}, +- "C": {"ext.C"}, +- }, +- }, +- { +- label: "array length", +- srcs: []string{`package p - -- // ActiveMetadata returns a new, unordered slice containing -- // metadata for all packages considered 'active' in the workspace. -- // -- // In normal memory mode, this is all workspace packages. In degraded memory -- // mode, this is just the reverse transitive closure of open packages. -- ActiveMetadata(ctx context.Context) ([]*Metadata, error) +-import "ext" +-import "unsafe" - -- // AllMetadata returns a new unordered array of metadata for all packages in the workspace. -- AllMetadata(ctx context.Context) ([]*Metadata, error) +-type A [unsafe.Sizeof(ext.B{ext.C})]int +-type A2 [unsafe.Sizeof(ext.B{f:ext.C})]int // use a KeyValueExpr +- +-type D [unsafe.Sizeof(struct{ f E })]int +-type E ext.E +- +-type F [3]G +-type G [ext.C]int +-`}, +- want: map[string][]string{ +- "A": {"ext.B"}, // no ext.C: doesn't enter CompLit +- "A2": {"ext.B"}, // ditto +- "D": {"ext.E"}, +- "E": {"ext.E"}, +- "F": {"ext.C"}, +- "G": {"ext.C"}, +- }, +- }, +- { +- label: "imports", +- srcs: []string{`package p - -- // Symbols returns all symbols in the snapshot. -- Symbols(ctx context.Context) (map[span.URI][]Symbol, error) +-import "ext" - -- // Metadata returns the metadata for the specified package, -- // or nil if it was not found. -- Metadata(id PackageID) *Metadata +-import ( +- "q" +- r2 "r" +- "s" // note: package name is t +- "z" +-) - -- // MetadataForFile returns a new slice containing metadata for each -- // package containing the Go file identified by uri, ordered by the -- // number of CompiledGoFiles (i.e. "narrowest" to "widest" package). -- // The result may include tests and intermediate test variants of -- // importable packages. -- // It returns an error if the context was cancelled. -- MetadataForFile(ctx context.Context, uri span.URI) ([]*Metadata, error) +-type A struct { +- q.Q +- r2.R +- s.S // invalid ref +- z.Z // references both external z.Z as well as package-level type z +-} - -- // TypeCheck parses and type-checks the specified packages, -- // and returns them in the same order as the ids. -- // The resulting packages' types may belong to different importers, -- // so types from different packages are incommensurable. -- TypeCheck(ctx context.Context, ids ...PackageID) ([]Package, error) +-type B struct { +- r.R // invalid ref +- t.T +-} - -- // PackageDiagnostics returns diagnostics for files contained in specified -- // packages. -- // -- // If these diagnostics cannot be loaded from cache, the requested packages -- // may be type-checked. -- PackageDiagnostics(ctx context.Context, ids ...PackageID) (map[span.URI][]*Diagnostic, error) +-var X int = q.V // X={}: no descent into RHS of 'var v T = rhs' +-var Y = q.V.W - -- // References returns cross-references indexes for the specified packages. -- // -- // If these indexes cannot be loaded from cache, the requested packages may -- // be type-checked. -- References(ctx context.Context, ids ...PackageID) ([]XrefIndex, error) +-type z ext.Z +-`}, +- imports: map[string]string{"q": "q", "r": "r", "s": "t", "z": "z"}, +- want: map[string][]string{ +- "A": {"ext.Z", "q.Q", "r.R", "z.Z"}, +- "B": {"t.T"}, +- "Y": {"q.V"}, +- }, +- }, +- { +- label: "import blank", +- srcs: []string{`package p - -- // MethodSets returns method-set indexes for the specified packages. -- // -- // If these indexes cannot be loaded from cache, the requested packages may -- // be type-checked. -- MethodSets(ctx context.Context, ids ...PackageID) ([]*methodsets.Index, error) +-import _ "q" - -- // GetCriticalError returns any critical errors in the workspace. -- // -- // A nil result may mean success, or context cancellation. -- GetCriticalError(ctx context.Context) *CriticalError --} +-type A q.Q +-`}, +- imports: map[string]string{"q": "q"}, +- want: map[string][]string{}, +- }, +- { +- label: "import dot", +- srcs: []string{`package p - --type XrefIndex interface { -- Lookup(targets map[PackagePath]map[objectpath.Path]struct{}) (locs []protocol.Location) --} +-import . "q" - --// SnapshotLabels returns a new slice of labels that should be used for events --// related to a snapshot. --func SnapshotLabels(snapshot Snapshot) []label.Label { -- return []label.Label{tag.Snapshot.Of(snapshot.SequenceID()), tag.Directory.Of(snapshot.View().Folder())} +-type A q.Q // not actually an edge, since q is imported . +-type B struct { +- C // assumed to be an edge to q +- D // resolved to package decl -} - --// PackageForFile is a convenience function that selects a package to --// which this file belongs (narrowest or widest), type-checks it in --// the requested mode (full or workspace), and returns it, along with --// the parse tree of that file. --// --// Type-checking is expensive. Call snapshot.ParseGo if all you need --// is a parse tree, or snapshot.MetadataForFile if you only need metadata. --func PackageForFile(ctx context.Context, snapshot Snapshot, uri span.URI, pkgSel PackageSelector) (Package, *ParsedGoFile, error) { -- metas, err := snapshot.MetadataForFile(ctx, uri) -- if err != nil { -- return nil, nil, err -- } -- if len(metas) == 0 { -- return nil, nil, fmt.Errorf("no package metadata for file %s", uri) -- } -- switch pkgSel { -- case NarrowestPackage: -- metas = metas[:1] -- case WidestPackage: -- metas = metas[len(metas)-1:] -- } -- pkgs, err := snapshot.TypeCheck(ctx, metas[0].ID) -- if err != nil { -- return nil, nil, err -- } -- pkg := pkgs[0] -- pgf, err := pkg.File(uri) -- if err != nil { -- return nil, nil, err // "can't happen" -- } -- return pkg, pgf, err --} - --// PackageSelector sets how a package is selected out from a set of packages --// containing a given file. --type PackageSelector int +-type E error // unexported, therefore must be universe.error +-type F Field +-var G = Field.X +-`, `package p - --const ( -- // NarrowestPackage picks the "narrowest" package for a given file. -- // By "narrowest" package, we mean the package with the fewest number of -- // files that includes the given file. This solves the problem of test -- // variants, as the test will have more files than the non-test package. -- NarrowestPackage PackageSelector = iota +-import "ext" +-import "q" - -- // WidestPackage returns the Package containing the most files. -- // This is useful for something like diagnostics, where we'd prefer to -- // offer diagnostics for as many files as possible. -- WidestPackage --) +-type D ext.D +-`}, +- imports: map[string]string{"q": "q"}, +- want: map[string][]string{ +- "B": {"ext.D", "q.C"}, +- "D": {"ext.D"}, +- "F": {"q.Field"}, +- "G": {"q.Field"}, +- }, +- }, +- { +- label: "typeparams", +- srcs: []string{`package p - --// InvocationFlags represents the settings of a particular go command invocation. --// It is a mode, plus a set of flag bits. --type InvocationFlags int +-import "ext" - --const ( -- // Normal is appropriate for commands that might be run by a user and don't -- // deliberately modify go.mod files, e.g. `go test`. -- Normal InvocationFlags = iota -- // WriteTemporaryModFile is for commands that need information from a -- // modified version of the user's go.mod file, e.g. `go mod tidy` used to -- // generate diagnostics. -- WriteTemporaryModFile -- // LoadWorkspace is for packages.Load, and other operations that should -- // consider the whole workspace at once. -- LoadWorkspace +-type A[T any] struct { +- t T +- b B +-} - -- // AllowNetwork is a flag bit that indicates the invocation should be -- // allowed to access the network. -- AllowNetwork InvocationFlags = 1 << 10 --) +-type B ext.B - --func (m InvocationFlags) Mode() InvocationFlags { -- return m & (AllowNetwork - 1) --} +-func F1[T any](T, B) +-func F2[T C]()(T, B) - --func (m InvocationFlags) AllowNetwork() bool { -- return m&AllowNetwork != 0 --} +-type T ext.T - --// View represents a single workspace. --// This is the level at which we maintain configuration like working directory --// and build tags. --type View interface { -- // Name returns the name this view was constructed with. -- Name() string +-type C ext.C - -- // Folder returns the folder with which this view was created. -- Folder() span.URI +-func F3[T1 ~[]T2, T2 ~[]T3](t1 T1, t2 T2) +-type T3 ext.T3 +-`, `package p - -- // Options returns a copy of the Options for this view. -- Options() *Options +-func (A[B]) M(C) {} +-`}, +- want: map[string][]string{ +- "A": {"ext.B", "ext.C"}, +- "B": {"ext.B"}, +- "C": {"ext.C"}, +- "F1": {"ext.B"}, +- "F2": {"ext.B", "ext.C"}, +- "F3": {"ext.T3"}, +- "T": {"ext.T"}, +- "T3": {"ext.T3"}, +- }, +- go118: true, +- }, +- { +- label: "instances", +- srcs: []string{`package p +- +-import "ext" +- +-type A[T any] ext.A +-type B[T1, T2 any] ext.B +- +-type C A[int] +-type D B[int, A[E]] +-type E ext.E +-`}, +- want: map[string][]string{ +- "A": {"ext.A"}, +- "B": {"ext.B"}, +- "C": {"ext.A"}, +- "D": {"ext.A", "ext.B", "ext.E"}, +- "E": {"ext.E"}, +- }, +- go118: true, +- }, +- { +- label: "duplicate decls", +- srcs: []string{`package p +- +-import "a" +-import "ext" +- +-type a a.A +-type A a +-type b ext.B +-type C a.A +-func (C) Foo(x) {} // invalid parameter, but that does not matter +-type C b +-func (C) Bar(y) {} // invalid parameter, but that does not matter +- +-var x ext.X +-var y ext.Y +-`}, +- imports: map[string]string{"a": "a", "b": "b"}, // "b" import should not matter, since it isn't in this file +- want: map[string][]string{ +- "A": {"a.A"}, +- "C": {"a.A", "ext.B", "ext.X", "ext.Y"}, +- }, +- }, +- { +- label: "invalid decls", +- srcs: []string{`package p - -- // Snapshot returns the current snapshot for the view, and a -- // release function that must be called when the Snapshot is -- // no longer needed. -- // -- // If the view is shut down, the resulting error will be non-nil, and the -- // release function need not be called. -- Snapshot() (Snapshot, func(), error) +-import "ext" - -- // IsGoPrivatePath reports whether target is a private import path, as identified -- // by the GOPRIVATE environment variable. -- IsGoPrivatePath(path string) bool +-type A B - -- // ModuleUpgrades returns known module upgrades for the dependencies of -- // modfile. -- ModuleUpgrades(modfile span.URI) map[string]string +-func () Foo(B){} - -- // RegisterModuleUpgrades registers that upgrades exist for the given modules -- // required by modfile. -- RegisterModuleUpgrades(modfile span.URI, upgrades map[string]string) +-var B struct{ ext.B +-`}, +- want: map[string][]string{ +- "A": {"ext.B"}, +- "B": {"ext.B"}, +- "Foo": {"ext.B"}, +- }, +- allowErrs: true, +- }, +- { +- label: "unmapped receiver", +- srcs: []string{`package p - -- // ClearModuleUpgrades clears all upgrades for the modules in modfile. -- ClearModuleUpgrades(modfile span.URI) +-type P struct{} - -- // Vulnerabilities returns known vulnerabilities for the given modfile. -- // TODO(suzmue): replace command.Vuln with a different type, maybe -- // https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck/govulnchecklib#Summary? -- Vulnerabilities(modfile ...span.URI) map[span.URI]*govulncheck.Result +-func (a) x(P) +-`}, +- want: map[string][]string{}, +- allowErrs: true, +- }, +- { +- label: "SCC special case", +- srcs: []string{`package p +- +-import "ext" +- +-type X Y +-type Y struct { Z; *X } +-type Z map[ext.A]ext.B +-`}, +- want: map[string][]string{ +- "X": {"ext.A", "ext.B"}, +- "Y": {"ext.A", "ext.B"}, +- "Z": {"ext.A", "ext.B"}, +- }, +- allowErrs: true, +- }, +- } - -- // SetVulnerabilities resets the list of vulnerabilities that exists for the given modules -- // required by modfile. -- SetVulnerabilities(modfile span.URI, vulncheckResult *govulncheck.Result) +- for _, test := range tests { +- t.Run(test.label, func(t *testing.T) { +- if test.go118 { +- testenv.NeedsGo1Point(t, 18) +- } - -- // FileKind returns the type of a file. -- // -- // We can't reliably deduce the kind from the file name alone, -- // as some editors can be told to interpret a buffer as -- // language different from the file name heuristic, e.g. that -- // an .html file actually contains Go "html/template" syntax, -- // or even that a .go file contains Python. -- FileKind(FileHandle) FileKind +- var pgfs []*source.ParsedGoFile +- for i, src := range test.srcs { +- uri := span.URI(fmt.Sprintf("file:///%d.go", i)) +- pgf, _ := cache.ParseGoSrc(ctx, token.NewFileSet(), uri, []byte(src), source.ParseFull, false) +- if !test.allowErrs && pgf.ParseErr != nil { +- t.Fatalf("ParseGoSrc(...) returned parse errors: %v", pgf.ParseErr) +- } +- pgfs = append(pgfs, pgf) +- } - -- // GoVersion returns the configured Go version for this view. -- GoVersion() int +- imports := map[source.ImportPath]*source.Metadata{ +- "ext": {ID: "ext", Name: "ext"}, // this one comes for free +- } +- for path, m := range test.imports { +- imports[source.ImportPath(path)] = &source.Metadata{ +- ID: source.PackageID(m), +- Name: source.PackageName(m), +- } +- } - -- // GoVersionString returns the go version string configured for this view. -- // Unlike [GoVersion], this encodes the minor version and commit hash information. -- GoVersionString() string --} +- data := typerefs.Encode(pgfs, "p", imports) - --// A FileSource maps uris to FileHandles. --type FileSource interface { -- // GetFile returns the FileHandle for a given URI. -- GetFile(ctx context.Context, uri span.URI) (FileHandle, error) --} +- got := make(map[string][]string) +- index := typerefs.NewPackageIndex() +- for _, class := range typerefs.Decode(index, "p", data) { +- // We redundantly expand out the name x refs cross product +- // here since that's what the existing tests expect. +- for _, name := range class.Decls { +- var syms []string +- for _, sym := range class.Refs { +- syms = append(syms, fmt.Sprintf("%s.%s", index.DeclaringPackage(sym), sym.Name)) +- } +- sort.Strings(syms) +- got[name] = syms +- } +- } - --// A MetadataSource maps package IDs to metadata. --// --// TODO(rfindley): replace this with a concrete metadata graph, once it is --// exposed from the snapshot. --type MetadataSource interface { -- // Metadata returns Metadata for the given package ID, or nil if it does not -- // exist. -- Metadata(PackageID) *Metadata +- if diff := cmp.Diff(test.want, got); diff != "" { +- t.Errorf("Refs(...) returned unexpected refs (-want +got):\n%s", diff) +- } +- }) +- } -} +diff -urN a/gopls/internal/lsp/source/types_format.go b/gopls/internal/lsp/source/types_format.go +--- a/gopls/internal/lsp/source/types_format.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/types_format.go 1970-01-01 08:00:00 +@@ -1,520 +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. - --// A ParsedGoFile contains the results of parsing a Go file. --type ParsedGoFile struct { -- URI span.URI -- Mode ParseMode -- File *ast.File -- Tok *token.File -- // Source code used to build the AST. It may be different from the -- // actual content of the file if we have fixed the AST. -- Src []byte -- Fixed bool -- Mapper *protocol.Mapper // may map fixed Src, not file content -- ParseErr scanner.ErrorList --} +-package source - --// -- go/token domain convenience helpers -- +-import ( +- "bytes" +- "context" +- "fmt" +- "go/ast" +- "go/doc" +- "go/printer" +- "go/token" +- "go/types" +- "strings" - --// PositionPos returns the token.Pos of protocol position p within the file. --func (pgf *ParsedGoFile) PositionPos(p protocol.Position) (token.Pos, error) { -- offset, err := pgf.Mapper.PositionOffset(p) -- if err != nil { -- return token.NoPos, err -- } -- return safetoken.Pos(pgf.Tok, offset) --} +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" +- "golang.org/x/tools/internal/tokeninternal" +- "golang.org/x/tools/internal/typeparams" +-) - --// PosRange returns a protocol Range for the token.Pos interval in this file. --func (pgf *ParsedGoFile) PosRange(start, end token.Pos) (protocol.Range, error) { -- return pgf.Mapper.PosRange(pgf.Tok, start, end) +-// FormatType returns the detail and kind for a types.Type. +-func FormatType(typ types.Type, qf types.Qualifier) (detail string, kind protocol.CompletionItemKind) { +- if types.IsInterface(typ) { +- detail = "interface{...}" +- kind = protocol.InterfaceCompletion +- } else if _, ok := typ.(*types.Struct); ok { +- detail = "struct{...}" +- kind = protocol.StructCompletion +- } else if typ != typ.Underlying() { +- detail, kind = FormatType(typ.Underlying(), qf) +- } else { +- detail = types.TypeString(typ, qf) +- kind = protocol.ClassCompletion +- } +- return detail, kind -} - --// PosMappedRange returns a MappedRange for the token.Pos interval in this file. --// A MappedRange can be converted to any other form. --func (pgf *ParsedGoFile) PosMappedRange(start, end token.Pos) (protocol.MappedRange, error) { -- return pgf.Mapper.PosMappedRange(pgf.Tok, start, end) +-type signature struct { +- name, doc string +- typeParams, params, results []string +- variadic bool +- needResultParens bool -} - --// PosLocation returns a protocol Location for the token.Pos interval in this file. --func (pgf *ParsedGoFile) PosLocation(start, end token.Pos) (protocol.Location, error) { -- return pgf.Mapper.PosLocation(pgf.Tok, start, end) --} +-func (s *signature) Format() string { +- var b strings.Builder +- b.WriteByte('(') +- for i, p := range s.params { +- if i > 0 { +- b.WriteString(", ") +- } +- b.WriteString(p) +- } +- b.WriteByte(')') - --// NodeRange returns a protocol Range for the ast.Node interval in this file. --func (pgf *ParsedGoFile) NodeRange(node ast.Node) (protocol.Range, error) { -- return pgf.Mapper.NodeRange(pgf.Tok, node) +- // Add space between parameters and results. +- if len(s.results) > 0 { +- b.WriteByte(' ') +- } +- if s.needResultParens { +- b.WriteByte('(') +- } +- for i, r := range s.results { +- if i > 0 { +- b.WriteString(", ") +- } +- b.WriteString(r) +- } +- if s.needResultParens { +- b.WriteByte(')') +- } +- return b.String() -} - --// NodeMappedRange returns a MappedRange for the ast.Node interval in this file. --// A MappedRange can be converted to any other form. --func (pgf *ParsedGoFile) NodeMappedRange(node ast.Node) (protocol.MappedRange, error) { -- return pgf.Mapper.NodeMappedRange(pgf.Tok, node) +-func (s *signature) TypeParams() []string { +- return s.typeParams -} - --// NodeLocation returns a protocol Location for the ast.Node interval in this file. --func (pgf *ParsedGoFile) NodeLocation(node ast.Node) (protocol.Location, error) { -- return pgf.Mapper.PosLocation(pgf.Tok, node.Pos(), node.End()) +-func (s *signature) Params() []string { +- return s.params -} - --// RangePos parses a protocol Range back into the go/token domain. --func (pgf *ParsedGoFile) RangePos(r protocol.Range) (token.Pos, token.Pos, error) { -- start, end, err := pgf.Mapper.RangeOffsets(r) +-// NewBuiltinSignature returns signature for the builtin object with a given +-// name, if a builtin object with the name exists. +-func NewBuiltinSignature(ctx context.Context, s Snapshot, name string) (*signature, error) { +- builtin, err := s.BuiltinFile(ctx) - if err != nil { -- return token.NoPos, token.NoPos, err +- return nil, err - } -- return pgf.Tok.Pos(start), pgf.Tok.Pos(end), nil +- obj := builtin.File.Scope.Lookup(name) +- if obj == nil { +- return nil, fmt.Errorf("no builtin object for %s", name) +- } +- decl, ok := obj.Decl.(*ast.FuncDecl) +- if !ok { +- return nil, fmt.Errorf("no function declaration for builtin: %s", name) +- } +- if decl.Type == nil { +- return nil, fmt.Errorf("no type for builtin decl %s", decl.Name) +- } +- var variadic bool +- if decl.Type.Params.List != nil { +- numParams := len(decl.Type.Params.List) +- lastParam := decl.Type.Params.List[numParams-1] +- if _, ok := lastParam.Type.(*ast.Ellipsis); ok { +- variadic = true +- } +- } +- fset := tokeninternal.FileSetFor(builtin.Tok) +- params, _ := formatFieldList(ctx, fset, decl.Type.Params, variadic) +- results, needResultParens := formatFieldList(ctx, fset, decl.Type.Results, false) +- d := decl.Doc.Text() +- switch s.Options().HoverKind { +- case SynopsisDocumentation: +- d = doc.Synopsis(d) +- case NoDocumentation: +- d = "" +- } +- return &signature{ +- doc: d, +- name: name, +- needResultParens: needResultParens, +- params: params, +- results: results, +- variadic: variadic, +- }, nil -} - --// A ParsedModule contains the results of parsing a go.mod file. --type ParsedModule struct { -- URI span.URI -- File *modfile.File -- Mapper *protocol.Mapper -- ParseErrors []*Diagnostic --} +-// replacer replaces some synthetic "type classes" used in the builtin file +-// with their most common constituent type. +-var replacer = strings.NewReplacer( +- `ComplexType`, `complex128`, +- `FloatType`, `float64`, +- `IntegerType`, `int`, +-) - --// A ParsedWorkFile contains the results of parsing a go.work file. --type ParsedWorkFile struct { -- URI span.URI -- File *modfile.WorkFile -- Mapper *protocol.Mapper -- ParseErrors []*Diagnostic +-func formatFieldList(ctx context.Context, fset *token.FileSet, list *ast.FieldList, variadic bool) ([]string, bool) { +- if list == nil { +- return nil, false +- } +- var writeResultParens bool +- var result []string +- for i := 0; i < len(list.List); i++ { +- if i >= 1 { +- writeResultParens = true +- } +- p := list.List[i] +- cfg := printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 4} +- b := &bytes.Buffer{} +- if err := cfg.Fprint(b, fset, p.Type); err != nil { +- event.Error(ctx, "unable to print type", nil, tag.Type.Of(p.Type)) +- continue +- } +- typ := replacer.Replace(b.String()) +- if len(p.Names) == 0 { +- result = append(result, typ) +- } +- for _, name := range p.Names { +- if name.Name != "" { +- if i == 0 { +- writeResultParens = true +- } +- result = append(result, fmt.Sprintf("%s %s", name.Name, typ)) +- } else { +- result = append(result, typ) +- } +- } +- } +- if variadic { +- result[len(result)-1] = strings.Replace(result[len(result)-1], "[]", "...", 1) +- } +- return result, writeResultParens -} - --// A TidiedModule contains the results of running `go mod tidy` on a module. --type TidiedModule struct { -- // Diagnostics representing changes made by `go mod tidy`. -- Diagnostics []*Diagnostic -- // The bytes of the go.mod file after it was tidied. -- TidiedContent []byte +-// FormatTypeParams turns TypeParamList into its Go representation, such as: +-// [T, Y]. Note that it does not print constraints as this is mainly used for +-// formatting type params in method receivers. +-func FormatTypeParams(tparams *typeparams.TypeParamList) string { +- if tparams == nil || tparams.Len() == 0 { +- return "" +- } +- var buf bytes.Buffer +- buf.WriteByte('[') +- for i := 0; i < tparams.Len(); i++ { +- if i > 0 { +- buf.WriteString(", ") +- } +- buf.WriteString(tparams.At(i).Obj().Name()) +- } +- buf.WriteByte(']') +- return buf.String() -} - --// Metadata represents package metadata retrieved from go/packages. --type Metadata struct { -- ID PackageID -- PkgPath PackagePath -- Name PackageName -- GoFiles []span.URI -- CompiledGoFiles []span.URI -- ForTest PackagePath // package path under test, or "" -- TypesSizes types.Sizes -- Errors []packages.Error -- DepsByImpPath map[ImportPath]PackageID // may contain dups; empty ID => missing -- DepsByPkgPath map[PackagePath]PackageID // values are unique and non-empty -- Module *packages.Module -- DepsErrors []*packagesinternal.PackageError -- Diagnostics []*Diagnostic // processed diagnostics from 'go list' -- LoadDir string // directory from which go/packages was run --} -- --func (m *Metadata) String() string { return string(m.ID) } +-// NewSignature returns formatted signature for a types.Signature struct. +-func NewSignature(ctx context.Context, s Snapshot, pkg Package, sig *types.Signature, comment *ast.CommentGroup, qf types.Qualifier, mq MetadataQualifier) (*signature, error) { +- var tparams []string +- tpList := typeparams.ForSignature(sig) +- for i := 0; i < tpList.Len(); i++ { +- tparam := tpList.At(i) +- // TODO: is it possible to reuse the logic from FormatVarType here? +- s := tparam.Obj().Name() + " " + tparam.Constraint().String() +- tparams = append(tparams, s) +- } - --// IsIntermediateTestVariant reports whether the given package is an --// intermediate test variant, e.g. "net/http [net/url.test]". --// --// Such test variants arise when an x_test package (in this case net/url_test) --// imports a package (in this case net/http) that itself imports the the --// non-x_test package (in this case net/url). --// --// This is done so that the forward transitive closure of net/url_test has --// only one package for the "net/url" import. --// The intermediate test variant exists to hold the test variant import: --// --// net/url_test [net/url.test] --// --// | "net/http" -> net/http [net/url.test] --// | "net/url" -> net/url [net/url.test] --// | ... --// --// net/http [net/url.test] --// --// | "net/url" -> net/url [net/url.test] --// | ... --// --// This restriction propagates throughout the import graph of net/http: for --// every package imported by net/http that imports net/url, there must be an --// intermediate test variant that instead imports "net/url [net/url.test]". --// --// As one can see from the example of net/url and net/http, intermediate test --// variants can result in many additional packages that are essentially (but --// not quite) identical. For this reason, we filter these variants wherever --// possible. --func (m *Metadata) IsIntermediateTestVariant() bool { -- return m.ForTest != "" && m.ForTest != m.PkgPath && m.ForTest+"_test" != m.PkgPath --} +- params := make([]string, 0, sig.Params().Len()) +- for i := 0; i < sig.Params().Len(); i++ { +- el := sig.Params().At(i) +- typ, err := FormatVarType(ctx, s, pkg, el, qf, mq) +- if err != nil { +- return nil, err +- } +- p := typ +- if el.Name() != "" { +- p = el.Name() + " " + typ +- } +- params = append(params, p) +- } - --// RemoveIntermediateTestVariants removes intermediate test variants, modifying the array. --func RemoveIntermediateTestVariants(metas []*Metadata) []*Metadata { -- res := metas[:0] -- for _, m := range metas { -- if !m.IsIntermediateTestVariant() { -- res = append(res, m) +- var needResultParens bool +- results := make([]string, 0, sig.Results().Len()) +- for i := 0; i < sig.Results().Len(); i++ { +- if i >= 1 { +- needResultParens = true +- } +- el := sig.Results().At(i) +- typ, err := FormatVarType(ctx, s, pkg, el, qf, mq) +- if err != nil { +- return nil, err +- } +- if el.Name() == "" { +- results = append(results, typ) +- } else { +- if i == 0 { +- needResultParens = true +- } +- results = append(results, el.Name()+" "+typ) - } - } -- return res +- var d string +- if comment != nil { +- d = comment.Text() +- } +- switch s.Options().HoverKind { +- case SynopsisDocumentation: +- d = doc.Synopsis(d) +- case NoDocumentation: +- d = "" +- } +- return &signature{ +- doc: d, +- typeParams: tparams, +- params: params, +- results: results, +- variadic: sig.Variadic(), +- needResultParens: needResultParens, +- }, nil -} - --var ErrViewExists = errors.New("view already exists for session") -- --// FileModification represents a modification to a file. --type FileModification struct { -- URI span.URI -- Action FileAction -- -- // OnDisk is true if a watched file is changed on disk. -- // If true, Version will be -1 and Text will be nil. -- OnDisk bool -- -- // Version will be -1 and Text will be nil when they are not supplied, -- // specifically on textDocument/didClose and for on-disk changes. -- Version int32 -- Text []byte -- -- // LanguageID is only sent from the language client on textDocument/didOpen. -- LanguageID string --} +-// FormatVarType formats a *types.Var, accounting for type aliases. +-// To do this, it looks in the AST of the file in which the object is declared. +-// On any errors, it always falls back to types.TypeString. +-// +-// TODO(rfindley): this function could return the actual name used in syntax, +-// for better parameter names. +-func FormatVarType(ctx context.Context, snapshot Snapshot, srcpkg Package, obj *types.Var, qf types.Qualifier, mq MetadataQualifier) (string, error) { +- // TODO(rfindley): This looks wrong. The previous comment said: +- // "If the given expr refers to a type parameter, then use the +- // object's Type instead of the type parameter declaration. This helps +- // format the instantiated type as opposed to the original undeclared +- // generic type". +- // +- // But of course, if obj is a type param, we are formatting a generic type +- // and not an instantiated type. Handling for instantiated types must be done +- // at a higher level. +- // +- // Left this during refactoring in order to preserve pre-existing logic. +- if typeparams.IsTypeParam(obj.Type()) { +- return types.TypeString(obj.Type(), qf), nil +- } - --type FileAction int +- if obj.Pkg() == nil || !obj.Pos().IsValid() { +- // This is defensive, though it is extremely unlikely we'll ever have a +- // builtin var. +- return types.TypeString(obj.Type(), qf), nil +- } - --const ( -- UnknownFileAction = FileAction(iota) -- Open -- Change -- Close -- Save -- Create -- Delete -- InvalidateMetadata --) +- // TODO(rfindley): parsing to produce candidates can be costly; consider +- // using faster methods. +- targetpgf, pos, err := parseFull(ctx, snapshot, srcpkg.FileSet(), obj.Pos()) +- if err != nil { +- return "", err // e.g. ctx cancelled +- } - --func (a FileAction) String() string { -- switch a { -- case Open: -- return "Open" -- case Change: -- return "Change" -- case Close: -- return "Close" -- case Save: -- return "Save" -- case Create: -- return "Create" -- case Delete: -- return "Delete" -- case InvalidateMetadata: -- return "InvalidateMetadata" -- default: -- return "Unknown" +- targetMeta := findFileInDeps(snapshot, srcpkg.Metadata(), targetpgf.URI) +- if targetMeta == nil { +- // If we have an object from type-checking, it should exist in a file in +- // the forward transitive closure. +- return "", bug.Errorf("failed to find file %q in deps of %q", targetpgf.URI, srcpkg.Metadata().ID) - } --} - --var ErrTmpModfileUnsupported = errors.New("-modfile is unsupported for this Go version") --var ErrNoModOnDisk = errors.New("go.mod file is not on disk") +- decl, spec, field := findDeclInfo([]*ast.File{targetpgf.File}, pos) - --func IsNonFatalGoModError(err error) bool { -- return err == ErrTmpModfileUnsupported || err == ErrNoModOnDisk --} +- // We can't handle type parameters correctly, so we fall back on TypeString +- // for parameterized decls. +- if decl, _ := decl.(*ast.FuncDecl); decl != nil { +- if typeparams.ForFuncType(decl.Type).NumFields() > 0 { +- return types.TypeString(obj.Type(), qf), nil // in generic function +- } +- if decl.Recv != nil && len(decl.Recv.List) > 0 { +- if x, _, _, _ := typeparams.UnpackIndexExpr(decl.Recv.List[0].Type); x != nil { +- return types.TypeString(obj.Type(), qf), nil // in method of generic type +- } +- } +- } +- if spec, _ := spec.(*ast.TypeSpec); spec != nil && typeparams.ForTypeSpec(spec).NumFields() > 0 { +- return types.TypeString(obj.Type(), qf), nil // in generic type decl +- } - --// ParseMode controls the content of the AST produced when parsing a source file. --type ParseMode int +- if field == nil { +- // TODO(rfindley): we should never reach here from an ordinary var, so +- // should probably return an error here. +- return types.TypeString(obj.Type(), qf), nil +- } +- expr := field.Type - --const ( -- // ParseHeader specifies that the main package declaration and imports are needed. -- // This is the mode used when attempting to examine the package graph structure. -- ParseHeader ParseMode = iota +- rq := requalifier(snapshot, targetpgf.File, targetMeta, mq) - -- // ParseFull specifies the full AST is needed. -- // This is used for files of direct interest where the entire contents must -- // be considered. -- ParseFull --) +- // The type names in the AST may not be correctly qualified. +- // Determine the package name to use based on the package that originated +- // the query and the package in which the type is declared. +- // We then qualify the value by cloning the AST node and editing it. +- expr = qualifyTypeExpr(expr, rq) - --// A FileHandle is an interface to files tracked by the LSP session, which may --// be either files read from disk, or open in the editor session (overlays). --type FileHandle interface { -- // URI is the URI for this file handle. -- // TODO(rfindley): this is not actually well-defined. In some cases, there -- // may be more than one URI that resolve to the same FileHandle. Which one is -- // this? -- URI() span.URI -- // FileIdentity returns a FileIdentity for the file, even if there was an -- // error reading it. -- FileIdentity() FileIdentity -- // Saved reports whether the file has the same content on disk. -- // For on-disk files, this is trivially true. -- Saved() bool -- // Version returns the file version, as defined by the LSP client. -- // For on-disk file handles, Version returns 0. -- Version() int32 -- // Read reads the contents of a file. -- // If the file is not available, returns a nil slice and an error. -- Read() ([]byte, error) +- // If the request came from a different package than the one in which the +- // types are defined, we may need to modify the qualifiers. +- return FormatNodeFile(targetpgf.Tok, expr), nil -} - --// A Hash is a cryptographic digest of the contents of a file. --// (Although at 32B it is larger than a 16B string header, it is smaller --// and has better locality than the string header + 64B of hex digits.) --type Hash [sha256.Size]byte +-// qualifyTypeExpr clones the type expression expr after re-qualifying type +-// names using the given function, which accepts the current syntactic +-// qualifier (possibly "" for unqualified idents), and returns a new qualifier +-// (again, possibly "" if the identifier should be unqualified). +-// +-// The resulting expression may be inaccurate: without type-checking we don't +-// properly account for "." imported identifiers or builtins. +-// +-// TODO(rfindley): add many more tests for this function. +-func qualifyTypeExpr(expr ast.Expr, qf func(string) string) ast.Expr { +- switch expr := expr.(type) { +- case *ast.ArrayType: +- return &ast.ArrayType{ +- Lbrack: expr.Lbrack, +- Elt: qualifyTypeExpr(expr.Elt, qf), +- Len: expr.Len, +- } - --// HashOf returns the hash of some data. --func HashOf(data []byte) Hash { -- return Hash(sha256.Sum256(data)) --} +- case *ast.BinaryExpr: +- if expr.Op != token.OR { +- return expr +- } +- return &ast.BinaryExpr{ +- X: qualifyTypeExpr(expr.X, qf), +- OpPos: expr.OpPos, +- Op: expr.Op, +- Y: qualifyTypeExpr(expr.Y, qf), +- } - --// Hashf returns the hash of a printf-formatted string. --func Hashf(format string, args ...interface{}) Hash { -- // Although this looks alloc-heavy, it is faster than using -- // Fprintf on sha256.New() because the allocations don't escape. -- return HashOf([]byte(fmt.Sprintf(format, args...))) --} +- case *ast.ChanType: +- return &ast.ChanType{ +- Arrow: expr.Arrow, +- Begin: expr.Begin, +- Dir: expr.Dir, +- Value: qualifyTypeExpr(expr.Value, qf), +- } - --// String returns the digest as a string of hex digits. --func (h Hash) String() string { -- return fmt.Sprintf("%64x", [sha256.Size]byte(h)) --} +- case *ast.Ellipsis: +- return &ast.Ellipsis{ +- Ellipsis: expr.Ellipsis, +- Elt: qualifyTypeExpr(expr.Elt, qf), +- } - --// Less returns true if the given hash is less than the other. --func (h Hash) Less(other Hash) bool { -- return bytes.Compare(h[:], other[:]) < 0 --} +- case *ast.FuncType: +- return &ast.FuncType{ +- Func: expr.Func, +- Params: qualifyFieldList(expr.Params, qf), +- Results: qualifyFieldList(expr.Results, qf), +- } - --// XORWith updates *h to *h XOR h2. --func (h *Hash) XORWith(h2 Hash) { -- // Small enough that we don't need crypto/subtle.XORBytes. -- for i := range h { -- h[i] ^= h2[i] -- } --} +- case *ast.Ident: +- // Unqualified type (builtin, package local, or dot-imported). - --// FileIdentity uniquely identifies a file at a version from a FileSystem. --type FileIdentity struct { -- URI span.URI -- Hash Hash // digest of file contents --} +- // Don't qualify names that look like builtins. +- // +- // Without type-checking this may be inaccurate. It could be made accurate +- // by doing syntactic object resolution for the entire package, but that +- // does not seem worthwhile and we generally want to avoid using +- // ast.Object, which may be inaccurate. +- if obj := types.Universe.Lookup(expr.Name); obj != nil { +- return expr +- } - --func (id FileIdentity) String() string { -- return fmt.Sprintf("%s%s", id.URI, id.Hash) --} +- newName := qf("") +- if newName != "" { +- return &ast.SelectorExpr{ +- X: &ast.Ident{ +- NamePos: expr.Pos(), +- Name: newName, +- }, +- Sel: expr, +- } +- } +- return expr - --// FileKind describes the kind of the file in question. --// It can be one of Go,mod, Sum, or Tmpl. --type FileKind int +- case *ast.IndexExpr: +- return &ast.IndexExpr{ +- X: qualifyTypeExpr(expr.X, qf), +- Lbrack: expr.Lbrack, +- Index: qualifyTypeExpr(expr.Index, qf), +- Rbrack: expr.Rbrack, +- } - --const ( -- // UnknownKind is a file type we don't know about. -- UnknownKind = FileKind(iota) +- case *typeparams.IndexListExpr: +- indices := make([]ast.Expr, len(expr.Indices)) +- for i, idx := range expr.Indices { +- indices[i] = qualifyTypeExpr(idx, qf) +- } +- return &typeparams.IndexListExpr{ +- X: qualifyTypeExpr(expr.X, qf), +- Lbrack: expr.Lbrack, +- Indices: indices, +- Rbrack: expr.Rbrack, +- } - -- // Go is a normal go source file. -- Go -- // Mod is a go.mod file. -- Mod -- // Sum is a go.sum file. -- Sum -- // Tmpl is a template file. -- Tmpl -- // Work is a go.work file. -- Work --) +- case *ast.InterfaceType: +- return &ast.InterfaceType{ +- Interface: expr.Interface, +- Methods: qualifyFieldList(expr.Methods, qf), +- Incomplete: expr.Incomplete, +- } - --func (k FileKind) String() string { -- switch k { -- case Go: -- return "go" -- case Mod: -- return "go.mod" -- case Sum: -- return "go.sum" -- case Tmpl: -- return "tmpl" -- case Work: -- return "go.work" -- default: -- return fmt.Sprintf("internal error: unknown file kind %d", k) -- } --} +- case *ast.MapType: +- return &ast.MapType{ +- Map: expr.Map, +- Key: qualifyTypeExpr(expr.Key, qf), +- Value: qualifyTypeExpr(expr.Value, qf), +- } - --// Analyzer represents a go/analysis analyzer with some boolean properties --// that let the user know how to use the analyzer. --type Analyzer struct { -- Analyzer *analysis.Analyzer +- case *ast.ParenExpr: +- return &ast.ParenExpr{ +- Lparen: expr.Lparen, +- Rparen: expr.Rparen, +- X: qualifyTypeExpr(expr.X, qf), +- } - -- // Enabled reports whether the analyzer is enabled. This value can be -- // configured per-analysis in user settings. For staticcheck analyzers, -- // the value of the Staticcheck setting overrides this field. -- // -- // Most clients should use the IsEnabled method. -- Enabled bool +- case *ast.SelectorExpr: +- if id, ok := expr.X.(*ast.Ident); ok { +- // qualified type +- newName := qf(id.Name) +- if newName == "" { +- return expr.Sel +- } +- return &ast.SelectorExpr{ +- X: &ast.Ident{ +- NamePos: id.NamePos, +- Name: newName, +- }, +- Sel: expr.Sel, +- } +- } +- return expr - -- // Fix is the name of the suggested fix name used to invoke the suggested -- // fixes for the analyzer. It is non-empty if we expect this analyzer to -- // provide its fix separately from its diagnostics. That is, we should apply -- // the analyzer's suggested fixes through a Command, not a TextEdit. -- Fix string +- case *ast.StarExpr: +- return &ast.StarExpr{ +- Star: expr.Star, +- X: qualifyTypeExpr(expr.X, qf), +- } - -- // ActionKind is the kind of code action this analyzer produces. If -- // unspecified the type defaults to quickfix. -- ActionKind []protocol.CodeActionKind +- case *ast.StructType: +- return &ast.StructType{ +- Struct: expr.Struct, +- Fields: qualifyFieldList(expr.Fields, qf), +- Incomplete: expr.Incomplete, +- } - -- // Severity is the severity set for diagnostics reported by this -- // analyzer. If left unset it defaults to Warning. -- Severity protocol.DiagnosticSeverity +- default: +- return expr +- } -} - --func (a *Analyzer) String() string { return a.Analyzer.String() } -- --// IsEnabled reports whether this analyzer is enabled by the given options. --func (a Analyzer) IsEnabled(options *Options) bool { -- // Staticcheck analyzers can only be enabled when staticcheck is on. -- if _, ok := options.StaticcheckAnalyzers[a.Analyzer.Name]; ok { -- if !options.Staticcheck { -- return false +-func qualifyFieldList(fl *ast.FieldList, qf func(string) string) *ast.FieldList { +- if fl == nil { +- return nil +- } +- if fl.List == nil { +- return &ast.FieldList{ +- Closing: fl.Closing, +- Opening: fl.Opening, - } - } -- if enabled, ok := options.Analyses[a.Analyzer.Name]; ok { -- return enabled +- list := make([]*ast.Field, 0, len(fl.List)) +- for _, f := range fl.List { +- list = append(list, &ast.Field{ +- Comment: f.Comment, +- Doc: f.Doc, +- Names: f.Names, +- Tag: f.Tag, +- Type: qualifyTypeExpr(f.Type, qf), +- }) +- } +- return &ast.FieldList{ +- Closing: fl.Closing, +- Opening: fl.Opening, +- List: list, - } -- return a.Enabled -} +diff -urN a/gopls/internal/lsp/source/util.go b/gopls/internal/lsp/source/util.go +--- a/gopls/internal/lsp/source/util.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/util.go 1970-01-01 08:00:00 +@@ -1,533 +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. - --// Declare explicit types for package paths, names, and IDs to ensure that we --// never use an ID where a path belongs, and vice versa. If we confused these, --// it would result in confusing errors because package IDs often look like --// package paths. --type ( -- PackageID string // go list's unique identifier for a package (e.g. "vendor/example.com/foo [vendor/example.com/bar.test]") -- PackagePath string // name used to prefix linker symbols (e.g. "vendor/example.com/foo") -- PackageName string // identifier in 'package' declaration (e.g. "foo") -- ImportPath string // path that appears in an import declaration (e.g. "example.com/foo") --) -- --// Package represents a Go package that has been parsed and type-checked. --// --// By design, there is no way to reach from a Package to the Package --// representing one of its dependencies. --// --// Callers must not assume that two Packages share the same --// token.FileSet or types.Importer and thus have commensurable --// token.Pos values or types.Objects. Instead, use stable naming --// schemes, such as (URI, byte offset) for positions, or (PackagePath, --// objectpath.Path) for exported declarations. --type Package interface { -- Metadata() *Metadata -- -- // Results of parsing: -- FileSet() *token.FileSet -- ParseMode() ParseMode -- CompiledGoFiles() []*ParsedGoFile // (borrowed) -- File(uri span.URI) (*ParsedGoFile, error) -- GetSyntax() []*ast.File // (borrowed) -- HasParseErrors() bool -- -- // Results of type checking: -- GetTypes() *types.Package -- GetTypesInfo() *types.Info -- DependencyTypes(PackagePath) *types.Package // nil for indirect dependency of no consequence -- HasTypeErrors() bool -- DiagnosticsForFile(ctx context.Context, s Snapshot, uri span.URI) ([]*Diagnostic, error) --} -- --type unit = struct{} -- --// A CriticalError is a workspace-wide error that generally prevents gopls from --// functioning correctly. In the presence of critical errors, other diagnostics --// in the workspace may not make sense. --type CriticalError struct { -- // MainError is the primary error. Must be non-nil. -- MainError error -- -- // Diagnostics contains any supplemental (structured) diagnostics. -- Diagnostics []*Diagnostic --} -- --// An Diagnostic corresponds to an LSP Diagnostic. --// https://microsoft.github.io/language-server-protocol/specification#diagnostic --type Diagnostic struct { -- URI span.URI -- Range protocol.Range -- Severity protocol.DiagnosticSeverity -- Code string -- CodeHref string -- -- // Source is a human-readable description of the source of the error. -- // Diagnostics generated by an analysis.Analyzer set it to Analyzer.Name. -- Source DiagnosticSource -- -- Message string -- -- Tags []protocol.DiagnosticTag -- Related []protocol.DiagnosticRelatedInformation -- -- // Fields below are used internally to generate quick fixes. They aren't -- // part of the LSP spec and don't leave the server. -- SuggestedFixes []SuggestedFix --} -- --func (d *Diagnostic) String() string { -- return fmt.Sprintf("%v: %s", d.Range, d.Message) --} -- --type DiagnosticSource string -- --const ( -- UnknownError DiagnosticSource = "" -- ListError DiagnosticSource = "go list" -- ParseError DiagnosticSource = "syntax" -- TypeError DiagnosticSource = "compiler" -- ModTidyError DiagnosticSource = "go mod tidy" -- OptimizationDetailsError DiagnosticSource = "optimizer details" -- UpgradeNotification DiagnosticSource = "upgrade available" -- Vulncheck DiagnosticSource = "vulncheck imports" -- Govulncheck DiagnosticSource = "govulncheck" -- TemplateError DiagnosticSource = "template" -- WorkFileError DiagnosticSource = "go.work file" --) -- --func AnalyzerErrorKind(name string) DiagnosticSource { -- return DiagnosticSource(name) --} -diff -urN a/gopls/internal/lsp/source/workspace_symbol.go b/gopls/internal/lsp/source/workspace_symbol.go ---- a/gopls/internal/lsp/source/workspace_symbol.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/workspace_symbol.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,632 +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 source +-package source - -import ( - "context" -- "fmt" +- "go/ast" +- "go/printer" +- "go/token" - "go/types" -- "path" - "path/filepath" - "regexp" -- "runtime" - "sort" +- "strconv" - "strings" -- "unicode" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/safetoken" - "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/event" -- "golang.org/x/tools/internal/fuzzy" +- "golang.org/x/tools/internal/tokeninternal" +- "golang.org/x/tools/internal/typeparams" -) - --// Symbol holds a precomputed symbol value. Note: we avoid using the --// protocol.SymbolInformation struct here in order to reduce the size of each --// symbol. --type Symbol struct { -- Name string -- Kind protocol.SymbolKind -- Range protocol.Range --} -- --// maxSymbols defines the maximum number of symbol results that should ever be --// sent in response to a client. --const maxSymbols = 100 -- --// WorkspaceSymbols matches symbols across all views using the given query, --// according to the match semantics parameterized by matcherType and style. --// --// The workspace symbol method is defined in the spec as follows: --// --// The workspace symbol request is sent from the client to the server to --// list project-wide symbols matching the query string. --// --// It is unclear what "project-wide" means here, but given the parameters of --// workspace/symbol do not include any workspace identifier, then it has to be --// assumed that "project-wide" means "across all workspaces". Hence why --// WorkspaceSymbols receives the views []View. +-// IsGenerated gets and reads the file denoted by uri and reports +-// whether it contains a "generated file" comment as described at +-// https://golang.org/s/generatedcode. -// --// However, it then becomes unclear what it would mean to call WorkspaceSymbols --// with a different configured SymbolMatcher per View. Therefore we assume that --// Session level configuration will define the SymbolMatcher to be used for the --// WorkspaceSymbols method. --func WorkspaceSymbols(ctx context.Context, matcher SymbolMatcher, style SymbolStyle, views []View, query string) ([]protocol.SymbolInformation, error) { -- ctx, done := event.Start(ctx, "source.WorkspaceSymbols") -- defer done() -- if query == "" { -- return nil, nil +-// TODO(adonovan): opt: this function does too much. +-// Move snapshot.ReadFile into the caller (most of which have already done it). +-func IsGenerated(ctx context.Context, snapshot Snapshot, uri span.URI) bool { +- fh, err := snapshot.ReadFile(ctx, uri) +- if err != nil { +- return false - } -- -- var s symbolizer -- switch style { -- case DynamicSymbols: -- s = dynamicSymbolMatch -- case FullyQualifiedSymbols: -- s = fullyQualifiedSymbolMatch -- case PackageQualifiedSymbols: -- s = packageSymbolMatch -- default: -- panic(fmt.Errorf("unknown symbol style: %v", style)) +- pgf, err := snapshot.ParseGo(ctx, fh, ParseHeader) +- if err != nil { +- return false - } -- -- return collectSymbols(ctx, views, matcher, s, query) +- for _, commentGroup := range pgf.File.Comments { +- for _, comment := range commentGroup.List { +- if matched := generatedRx.MatchString(comment.Text); matched { +- // Check if comment is at the beginning of the line in source. +- if safetoken.Position(pgf.Tok, comment.Slash).Column == 1 { +- return true +- } +- } +- } +- } +- return false -} - --// A matcherFunc returns the index and score of a symbol match. +-// adjustedObjEnd returns the end position of obj, possibly modified for +-// package names. -// --// See the comment for symbolCollector for more information. --type matcherFunc func(chunks []string) (int, float64) +-// TODO(rfindley): eliminate this function, by inlining it at callsites where +-// it makes sense. +-func adjustedObjEnd(obj types.Object) token.Pos { +- nameLen := len(obj.Name()) +- if pkgName, ok := obj.(*types.PkgName); ok { +- // An imported Go package has a package-local, unqualified name. +- // When the name matches the imported package name, there is no +- // identifier in the import spec with the local package name. +- // +- // For example: +- // import "go/ast" // name "ast" matches package name +- // import a "go/ast" // name "a" does not match package name +- // +- // When the identifier does not appear in the source, have the range +- // of the object be the import path, including quotes. +- if pkgName.Imported().Name() == pkgName.Name() { +- nameLen = len(pkgName.Imported().Path()) + len(`""`) +- } +- } +- return obj.Pos() + token.Pos(nameLen) +-} - --// A symbolizer returns the best symbol match for a name with pkg, according to --// some heuristic. The symbol name is passed as the slice nameParts of logical --// name pieces. For example, for myType.field the caller can pass either --// []string{"myType.field"} or []string{"myType.", "field"}. --// --// See the comment for symbolCollector for more information. +-// Matches cgo generated comment as well as the proposed standard: -// --// The space argument is an empty slice with spare capacity that may be used --// to allocate the result. --type symbolizer func(space []string, name string, pkg *Metadata, m matcherFunc) ([]string, float64) +-// https://golang.org/s/generatedcode +-var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`) - --func fullyQualifiedSymbolMatch(space []string, name string, pkg *Metadata, matcher matcherFunc) ([]string, float64) { -- if _, score := dynamicSymbolMatch(space, name, pkg, matcher); score > 0 { -- return append(space, string(pkg.PkgPath), ".", name), score +-// FileKindForLang returns the file kind associated with the given language ID, +-// or UnknownKind if the language ID is not recognized. +-func FileKindForLang(langID string) FileKind { +- switch langID { +- case "go": +- return Go +- case "go.mod": +- return Mod +- case "go.sum": +- return Sum +- case "tmpl", "gotmpl": +- return Tmpl +- case "go.work": +- return Work +- default: +- return UnknownKind - } -- return nil, 0 -} - --func dynamicSymbolMatch(space []string, name string, pkg *Metadata, matcher matcherFunc) ([]string, float64) { -- if IsCommandLineArguments(pkg.ID) { -- // command-line-arguments packages have a non-sensical package path, so -- // just use their package name. -- return packageSymbolMatch(space, name, pkg, matcher) +-// nodeAtPos returns the index and the node whose position is contained inside +-// the node list. +-func nodeAtPos(nodes []ast.Node, pos token.Pos) (ast.Node, int) { +- if nodes == nil { +- return nil, -1 - } -- -- var score float64 -- -- endsInPkgName := strings.HasSuffix(string(pkg.PkgPath), string(pkg.Name)) -- -- // If the package path does not end in the package name, we need to check the -- // package-qualified symbol as an extra pass first. -- if !endsInPkgName { -- pkgQualified := append(space, string(pkg.Name), ".", name) -- idx, score := matcher(pkgQualified) -- nameStart := len(pkg.Name) + 1 -- if score > 0 { -- // If our match is contained entirely within the unqualified portion, -- // just return that. -- if idx >= nameStart { -- return append(space, name), score -- } -- // Lower the score for matches that include the package name. -- return pkgQualified, score * 0.8 +- for i, node := range nodes { +- if node.Pos() <= pos && pos <= node.End() { +- return node, i - } - } +- return nil, -1 +-} - -- // Now try matching the fully qualified symbol. -- fullyQualified := append(space, string(pkg.PkgPath), ".", name) -- idx, score := matcher(fullyQualified) -- -- // As above, check if we matched just the unqualified symbol name. -- nameStart := len(pkg.PkgPath) + 1 -- if idx >= nameStart { -- return append(space, name), score -- } -- -- // If our package path ends in the package name, we'll have skipped the -- // initial pass above, so check if we matched just the package-qualified -- // name. -- if endsInPkgName && idx >= 0 { -- pkgStart := len(pkg.PkgPath) - len(pkg.Name) -- if idx >= pkgStart { -- return append(space, string(pkg.Name), ".", name), score -- } +-// FormatNode returns the "pretty-print" output for an ast node. +-func FormatNode(fset *token.FileSet, n ast.Node) string { +- var buf strings.Builder +- if err := printer.Fprint(&buf, fset, n); err != nil { +- return "" - } -- -- // Our match was not contained within the unqualified or package qualified -- // symbol. Return the fully qualified symbol but discount the score. -- return fullyQualified, score * 0.6 +- return buf.String() -} - --func packageSymbolMatch(space []string, name string, pkg *Metadata, matcher matcherFunc) ([]string, float64) { -- qualified := append(space, string(pkg.Name), ".", name) -- if _, s := matcher(qualified); s > 0 { -- return qualified, s -- } -- return nil, 0 +-// FormatNodeFile is like FormatNode, but requires only the token.File for the +-// syntax containing the given ast node. +-func FormatNodeFile(file *token.File, n ast.Node) string { +- fset := tokeninternal.FileSetFor(file) +- return FormatNode(fset, n) -} - --func buildMatcher(matcher SymbolMatcher, query string) matcherFunc { -- switch matcher { -- case SymbolFuzzy: -- return parseQuery(query, newFuzzyMatcher) -- case SymbolFastFuzzy: -- return parseQuery(query, func(query string) matcherFunc { -- return fuzzy.NewSymbolMatcher(query).Match -- }) -- case SymbolCaseSensitive: -- return matchExact(query) -- case SymbolCaseInsensitive: -- q := strings.ToLower(query) -- exact := matchExact(q) -- wrapper := []string{""} -- return func(chunks []string) (int, float64) { -- s := strings.Join(chunks, "") -- wrapper[0] = strings.ToLower(s) -- return exact(wrapper) +-// Deref returns a pointer's element type, traversing as many levels as needed. +-// Otherwise it returns typ. +-// +-// It can return a pointer type for cyclic types (see golang/go#45510). +-func Deref(typ types.Type) types.Type { +- var seen map[types.Type]struct{} +- for { +- p, ok := typ.Underlying().(*types.Pointer) +- if !ok { +- return typ +- } +- if _, ok := seen[p.Elem()]; ok { +- return typ - } -- } -- panic(fmt.Errorf("unknown symbol matcher: %v", matcher)) --} - --func newFuzzyMatcher(query string) matcherFunc { -- fm := fuzzy.NewMatcher(query) -- return func(chunks []string) (int, float64) { -- score := float64(fm.ScoreChunks(chunks)) -- ranges := fm.MatchedRanges() -- if len(ranges) > 0 { -- return ranges[0], score +- typ = p.Elem() +- +- if seen == nil { +- seen = make(map[types.Type]struct{}) - } -- return -1, score +- seen[typ] = struct{}{} - } -} - --// parseQuery parses a field-separated symbol query, extracting the special --// characters listed below, and returns a matcherFunc corresponding to the AND --// of all field queries. --// --// Special characters: --// --// ^ match exact prefix --// $ match exact suffix --// ' match exact --// --// In all three of these special queries, matches are 'smart-cased', meaning --// they are case sensitive if the symbol query contains any upper-case --// characters, and case insensitive otherwise. --func parseQuery(q string, newMatcher func(string) matcherFunc) matcherFunc { -- fields := strings.Fields(q) -- if len(fields) == 0 { -- return func([]string) (int, float64) { return -1, 0 } +-func SortDiagnostics(d []*Diagnostic) { +- sort.Slice(d, func(i int, j int) bool { +- return CompareDiagnostic(d[i], d[j]) < 0 +- }) +-} +- +-func CompareDiagnostic(a, b *Diagnostic) int { +- if r := protocol.CompareRange(a.Range, b.Range); r != 0 { +- return r - } -- var funcs []matcherFunc -- for _, field := range fields { -- var f matcherFunc -- switch { -- case strings.HasPrefix(field, "^"): -- prefix := field[1:] -- f = smartCase(prefix, func(chunks []string) (int, float64) { -- s := strings.Join(chunks, "") -- if strings.HasPrefix(s, prefix) { -- return 0, 1 -- } -- return -1, 0 -- }) -- case strings.HasPrefix(field, "'"): -- exact := field[1:] -- f = smartCase(exact, matchExact(exact)) -- case strings.HasSuffix(field, "$"): -- suffix := field[0 : len(field)-1] -- f = smartCase(suffix, func(chunks []string) (int, float64) { -- s := strings.Join(chunks, "") -- if strings.HasSuffix(s, suffix) { -- return len(s) - len(suffix), 1 -- } -- return -1, 0 -- }) -- default: -- f = newMatcher(field) -- } -- funcs = append(funcs, f) +- if a.Source < b.Source { +- return -1 - } -- if len(funcs) == 1 { -- return funcs[0] +- if a.Source > b.Source { +- return +1 - } -- return comboMatcher(funcs).match +- if a.Message < b.Message { +- return -1 +- } +- if a.Message > b.Message { +- return +1 +- } +- return 0 -} - --func matchExact(exact string) matcherFunc { -- return func(chunks []string) (int, float64) { -- s := strings.Join(chunks, "") -- if idx := strings.LastIndex(s, exact); idx >= 0 { -- return idx, 1 +-// findFileInDeps finds package metadata containing URI in the transitive +-// dependencies of m. When using the Go command, the answer is unique. +-// +-// TODO(rfindley): refactor to share logic with findPackageInDeps? +-func findFileInDeps(s MetadataSource, m *Metadata, uri span.URI) *Metadata { +- seen := make(map[PackageID]bool) +- var search func(*Metadata) *Metadata +- search = func(m *Metadata) *Metadata { +- if seen[m.ID] { +- return nil - } -- return -1, 0 +- seen[m.ID] = true +- for _, cgf := range m.CompiledGoFiles { +- if cgf == uri { +- return m +- } +- } +- for _, dep := range m.DepsByPkgPath { +- m := s.Metadata(dep) +- if m == nil { +- bug.Reportf("nil metadata for %q", dep) +- continue +- } +- if found := search(m); found != nil { +- return found +- } +- } +- return nil - } +- return search(m) -} - --// smartCase returns a matcherFunc that is case-sensitive if q contains any --// upper-case characters, and case-insensitive otherwise. --func smartCase(q string, m matcherFunc) matcherFunc { -- insensitive := strings.ToLower(q) == q -- wrapper := []string{""} -- return func(chunks []string) (int, float64) { -- s := strings.Join(chunks, "") -- if insensitive { -- s = strings.ToLower(s) -- } -- wrapper[0] = s -- return m(wrapper) +-// UnquoteImportPath returns the unquoted import path of s, +-// or "" if the path is not properly quoted. +-func UnquoteImportPath(s *ast.ImportSpec) ImportPath { +- path, err := strconv.Unquote(s.Path.Value) +- if err != nil { +- return "" - } +- return ImportPath(path) -} - --type comboMatcher []matcherFunc +-// NodeContains returns true if a node encloses a given position pos. +-func NodeContains(n ast.Node, pos token.Pos) bool { +- return n != nil && n.Pos() <= pos && pos <= n.End() +-} - --func (c comboMatcher) match(chunks []string) (int, float64) { -- score := 1.0 -- first := 0 -- for _, f := range c { -- idx, s := f(chunks) -- if idx < first { -- first = idx +-// CollectScopes returns all scopes in an ast path, ordered as innermost scope +-// first. +-func CollectScopes(info *types.Info, path []ast.Node, pos token.Pos) []*types.Scope { +- // scopes[i], where i 1? -- for _, v := range views { -- snapshot, release, err := v.Snapshot() -- if err != nil { -- continue // view is shut down; continue with others -- } -- defer release() -- -- // Use the root view URIs for determining (lexically) -- // whether a URI is in any open workspace. -- roots = append(roots, strings.TrimRight(string(v.Folder()), "/")) -- -- filters := v.Options().DirectoryFilters -- filterer := NewFilterer(filters) -- folder := filepath.ToSlash(v.Folder().Filename()) -- symbols, err := snapshot.Symbols(ctx) -- if err != nil { -- return nil, err -- } -- for uri, syms := range symbols { -- norm := filepath.ToSlash(uri.Filename()) -- nm := strings.TrimPrefix(norm, folder) -- if filterer.Disallow(nm) { -- continue -- } -- // Only scan each file once. -- if seen[uri] { -- continue -- } -- mds, err := snapshot.MetadataForFile(ctx, uri) -- if err != nil { -- event.Error(ctx, fmt.Sprintf("missing metadata for %q", uri), err) -- continue -- } -- if len(mds) == 0 { -- // TODO: should use the bug reporting API -- continue -- } -- seen[uri] = true -- work = append(work, symbolFile{uri, mds[0], syms}) +-// Qualifier returns a function that appropriately formats a types.PkgName +-// appearing in a *ast.File. +-func Qualifier(f *ast.File, pkg *types.Package, info *types.Info) types.Qualifier { +- // Construct mapping of import paths to their defined or implicit names. +- imports := make(map[*types.Package]string) +- for _, imp := range f.Imports { +- if pkgname, ok := ImportedPkgName(info, imp); ok { +- imports[pkgname.Imported()] = pkgname.Name() - } - } -- -- // Match symbols in parallel. -- // Each worker has its own symbolStore, -- // which we merge at the end. -- nmatchers := runtime.GOMAXPROCS(-1) // matching is CPU bound -- results := make(chan *symbolStore) -- for i := 0; i < nmatchers; i++ { -- go func(i int) { -- matcher := buildMatcher(matcherType, query) -- store := new(symbolStore) -- // Assign files to workers in round-robin fashion. -- for j := i; j < len(work); j += nmatchers { -- matchFile(store, symbolizer, matcher, roots, work[j]) +- // Define qualifier to replace full package paths with names of the imports. +- return func(p *types.Package) string { +- if p == pkg { +- return "" +- } +- if name, ok := imports[p]; ok { +- if name == "." { +- return "" - } -- results <- store -- }(i) -- } -- -- // Gather and merge results as they arrive. -- var unified symbolStore -- for i := 0; i < nmatchers; i++ { -- store := <-results -- for _, syms := range store.res { -- unified.store(syms) +- return name - } +- return p.Name() - } -- return unified.results(), nil -} - --type Filterer struct { -- // Whether a filter is excluded depends on the operator (first char of the raw filter). -- // Slices filters and excluded then should have the same length. -- filters []*regexp.Regexp -- excluded []bool --} -- --// NewFilterer computes regular expression form of all raw filters --func NewFilterer(rawFilters []string) *Filterer { -- var f Filterer -- for _, filter := range rawFilters { -- filter = path.Clean(filepath.ToSlash(filter)) -- // TODO(dungtuanle): fix: validate [+-] prefix. -- op, prefix := filter[0], filter[1:] -- // convertFilterToRegexp adds "/" at the end of prefix to handle cases where a filter is a prefix of another filter. -- // For example, it prevents [+foobar, -foo] from excluding "foobar". -- f.filters = append(f.filters, convertFilterToRegexp(filepath.ToSlash(prefix))) -- f.excluded = append(f.excluded, op == '-') +-// requalifier returns a function that re-qualifies identifiers and qualified +-// identifiers contained in targetFile using the given metadata qualifier. +-func requalifier(s MetadataSource, targetFile *ast.File, targetMeta *Metadata, mq MetadataQualifier) func(string) string { +- qm := map[string]string{ +- "": mq(targetMeta.Name, "", targetMeta.PkgPath), - } - -- return &f --} -- --// Disallow return true if the path is excluded from the filterer's filters. --func (f *Filterer) Disallow(path string) bool { -- // Ensure trailing but not leading slash. -- path = strings.TrimPrefix(path, "/") -- if !strings.HasSuffix(path, "/") { -- path += "/" -- } +- // Construct mapping of import paths to their defined or implicit names. +- for _, imp := range targetFile.Imports { +- name, pkgName, impPath, pkgPath := importInfo(s, imp, targetMeta) - -- // TODO(adonovan): opt: iterate in reverse and break at first match. -- excluded := false -- for i, filter := range f.filters { -- if filter.MatchString(path) { -- excluded = f.excluded[i] // last match wins -- } +- // Re-map the target name for the source file. +- qm[name] = mq(pkgName, impPath, pkgPath) - } -- return excluded --} - --// convertFilterToRegexp replaces glob-like operator substrings in a string file path to their equivalent regex forms. --// Supporting glob-like operators: --// - **: match zero or more complete path segments --func convertFilterToRegexp(filter string) *regexp.Regexp { -- if filter == "" { -- return regexp.MustCompile(".*") -- } -- var ret strings.Builder -- ret.WriteString("^") -- segs := strings.Split(filter, "/") -- for _, seg := range segs { -- // Inv: seg != "" since path is clean. -- if seg == "**" { -- ret.WriteString(".*") -- } else { -- ret.WriteString(regexp.QuoteMeta(seg)) +- return func(name string) string { +- if newName, ok := qm[name]; ok { +- return newName - } -- ret.WriteString("/") +- return name - } -- pattern := ret.String() -- -- // Remove unnecessary "^.*" prefix, which increased -- // BenchmarkWorkspaceSymbols time by ~20% (even though -- // filter CPU time increased by only by ~2.5%) when the -- // default filter was changed to "**/node_modules". -- pattern = strings.TrimPrefix(pattern, "^.*") -- -- return regexp.MustCompile(pattern) -} - --// symbolFile holds symbol information for a single file. --type symbolFile struct { -- uri span.URI -- md *Metadata -- syms []Symbol --} +-// A MetadataQualifier is a function that qualifies an identifier declared in a +-// package with the given package name, import path, and package path. +-// +-// In scenarios where metadata is missing the provided PackageName and +-// PackagePath may be empty, but ImportPath must always be non-empty. +-type MetadataQualifier func(PackageName, ImportPath, PackagePath) string - --// matchFile scans a symbol file and adds matching symbols to the store. --func matchFile(store *symbolStore, symbolizer symbolizer, matcher matcherFunc, roots []string, i symbolFile) { -- space := make([]string, 0, 3) -- for _, sym := range i.syms { -- symbolParts, score := symbolizer(space, sym.Name, i.md, matcher) +-// MetadataQualifierForFile returns a metadata qualifier that chooses the best +-// qualification of an imported package relative to the file f in package with +-// metadata m. +-func MetadataQualifierForFile(s MetadataSource, f *ast.File, m *Metadata) MetadataQualifier { +- // Record local names for import paths. +- localNames := make(map[ImportPath]string) // local names for imports in f +- for _, imp := range f.Imports { +- name, _, impPath, _ := importInfo(s, imp, m) +- localNames[impPath] = name +- } - -- // Check if the score is too low before applying any downranking. -- if store.tooLow(score) { +- // Record a package path -> import path mapping. +- inverseDeps := make(map[PackageID]PackagePath) +- for path, id := range m.DepsByPkgPath { +- inverseDeps[id] = path +- } +- importsByPkgPath := make(map[PackagePath]ImportPath) // best import paths by pkgPath +- for impPath, id := range m.DepsByImpPath { +- if id == "" { - continue - } +- pkgPath := inverseDeps[id] +- _, hasPath := importsByPkgPath[pkgPath] +- _, hasImp := localNames[impPath] +- // In rare cases, there may be multiple import paths with the same package +- // path. In such scenarios, prefer an import path that already exists in +- // the file. +- if !hasPath || hasImp { +- importsByPkgPath[pkgPath] = impPath +- } +- } - -- // Factors to apply to the match score for the purpose of downranking -- // results. -- // -- // These numbers were crudely calibrated based on trial-and-error using a -- // small number of sample queries. Adjust as necessary. -- // -- // All factors are multiplicative, meaning if more than one applies they are -- // multiplied together. -- const ( -- // nonWorkspaceFactor is applied to symbols outside of any active -- // workspace. Developers are less likely to want to jump to code that they -- // are not actively working on. -- nonWorkspaceFactor = 0.5 -- // nonWorkspaceUnexportedFactor is applied to unexported symbols outside of -- // any active workspace. Since one wouldn't usually jump to unexported -- // symbols to understand a package API, they are particularly irrelevant. -- nonWorkspaceUnexportedFactor = 0.5 -- // every field or method nesting level to access the field decreases -- // the score by a factor of 1.0 - depth*depthFactor, up to a depth of -- // 3. -- depthFactor = 0.2 -- ) -- -- startWord := true -- exported := true -- depth := 0.0 -- for _, r := range sym.Name { -- if startWord && !unicode.IsUpper(r) { -- exported = false +- return func(pkgName PackageName, impPath ImportPath, pkgPath PackagePath) string { +- // If supplied, translate the package path to an import path in the source +- // package. +- if pkgPath != "" { +- if srcImp := importsByPkgPath[pkgPath]; srcImp != "" { +- impPath = srcImp - } -- if r == '.' { -- startWord = true -- depth++ -- } else { -- startWord = false +- if pkgPath == m.PkgPath { +- return "" - } - } -- -- inWorkspace := false -- for _, root := range roots { -- if strings.HasPrefix(string(i.uri), root) { -- inWorkspace = true -- break -- } +- if localName, ok := localNames[impPath]; ok && impPath != "" { +- return string(localName) - } -- -- // Apply downranking based on workspace position. -- if !inWorkspace { -- score *= nonWorkspaceFactor -- if !exported { -- score *= nonWorkspaceUnexportedFactor -- } +- if pkgName != "" { +- return string(pkgName) - } +- idx := strings.LastIndexByte(string(impPath), '/') +- return string(impPath[idx+1:]) +- } +-} - -- // Apply downranking based on symbol depth. -- if depth > 3 { -- depth = 3 -- } -- score *= 1.0 - depth*depthFactor +-// importInfo collects information about the import specified by imp, +-// extracting its file-local name, package name, import path, and package path. +-// +-// If metadata is missing for the import, the resulting package name and +-// package path may be empty, and the file local name may be guessed based on +-// the import path. +-// +-// Note: previous versions of this helper used a PackageID->PackagePath map +-// extracted from m, for extracting package path even in the case where +-// metadata for a dep was missing. This should not be necessary, as we should +-// always have metadata for IDs contained in DepsByPkgPath. +-func importInfo(s MetadataSource, imp *ast.ImportSpec, m *Metadata) (string, PackageName, ImportPath, PackagePath) { +- var ( +- name string // local name +- pkgName PackageName +- impPath = UnquoteImportPath(imp) +- pkgPath PackagePath +- ) - -- if store.tooLow(score) { -- continue -- } +- // If the import has a local name, use it. +- if imp.Name != nil { +- name = imp.Name.Name +- } - -- si := symbolInformation{ -- score: score, -- symbol: strings.Join(symbolParts, ""), -- kind: sym.Kind, -- uri: i.uri, -- rng: sym.Range, -- container: string(i.md.PkgPath), +- // Try to find metadata for the import. If successful and there is no local +- // name, the package name is the local name. +- if depID := m.DepsByImpPath[impPath]; depID != "" { +- if depm := s.Metadata(depID); depm != nil { +- if name == "" { +- name = string(depm.Name) +- } +- pkgName = depm.Name +- pkgPath = depm.PkgPath - } -- store.store(si) - } --} - --type symbolStore struct { -- res [maxSymbols]symbolInformation +- // If the local name is still unknown, guess it based on the import path. +- if name == "" { +- idx := strings.LastIndexByte(string(impPath), '/') +- name = string(impPath[idx+1:]) +- } +- return name, pkgName, impPath, pkgPath -} - --// store inserts si into the sorted results, if si has a high enough score. --func (sc *symbolStore) store(si symbolInformation) { -- if sc.tooLow(si.score) { -- return +-// isDirective reports whether c is a comment directive. +-// +-// Copied and adapted from go/src/go/ast/ast.go. +-func isDirective(c string) bool { +- if len(c) < 3 { +- return false - } -- insertAt := sort.Search(len(sc.res), func(i int) bool { -- // Sort by score, then symbol length, and finally lexically. -- if sc.res[i].score != si.score { -- return sc.res[i].score < si.score +- if c[1] != '/' { +- return false +- } +- //-style comment (no newline at the end) +- c = c[2:] +- if len(c) == 0 { +- // empty line +- return false +- } +- // "//line " is a line directive. +- // (The // has been removed.) +- if strings.HasPrefix(c, "line ") { +- return true +- } +- +- // "//[a-z0-9]+:[a-z0-9]" +- // (The // has been removed.) +- colon := strings.Index(c, ":") +- if colon <= 0 || colon+1 >= len(c) { +- return false +- } +- for i := 0; i <= colon+1; i++ { +- if i == colon { +- continue - } -- if len(sc.res[i].symbol) != len(si.symbol) { -- return len(sc.res[i].symbol) > len(si.symbol) +- b := c[i] +- if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') { +- return false - } -- return sc.res[i].symbol > si.symbol -- }) -- if insertAt < len(sc.res)-1 { -- copy(sc.res[insertAt+1:], sc.res[insertAt:len(sc.res)-1]) - } -- sc.res[insertAt] = si --} -- --func (sc *symbolStore) tooLow(score float64) bool { -- return score <= sc.res[len(sc.res)-1].score +- return true -} - --func (sc *symbolStore) results() []protocol.SymbolInformation { -- var res []protocol.SymbolInformation -- for _, si := range sc.res { -- if si.score <= 0 { -- return res +-// InDir checks whether path is in the file tree rooted at dir. +-// It checks only the lexical form of the file names. +-// It does not consider symbolic links. +-// +-// Copied from go/src/cmd/go/internal/search/search.go. +-func InDir(dir, path string) bool { +- pv := strings.ToUpper(filepath.VolumeName(path)) +- dv := strings.ToUpper(filepath.VolumeName(dir)) +- path = path[len(pv):] +- dir = dir[len(dv):] +- switch { +- default: +- return false +- case pv != dv: +- return false +- case len(path) == len(dir): +- if path == dir { +- return true - } -- res = append(res, si.asProtocolSymbolInformation()) +- return false +- case dir == "": +- return path != "" +- case len(path) > len(dir): +- if dir[len(dir)-1] == filepath.Separator { +- if path[:len(dir)] == dir { +- return path[len(dir):] != "" +- } +- return false +- } +- if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir { +- if len(path) == len(dir)+1 { +- return true +- } +- return path[len(dir)+1:] != "" +- } +- return false - } -- return res -} - --func typeToKind(typ types.Type) protocol.SymbolKind { -- switch typ := typ.Underlying().(type) { -- case *types.Interface: -- return protocol.Interface -- case *types.Struct: -- return protocol.Struct -- case *types.Signature: -- if typ.Recv() != nil { -- return protocol.Method -- } -- return protocol.Function -- case *types.Named: -- return typeToKind(typ.Underlying()) -- case *types.Basic: -- i := typ.Info() -- switch { -- case i&types.IsNumeric != 0: -- return protocol.Number -- case i&types.IsBoolean != 0: -- return protocol.Boolean -- case i&types.IsString != 0: -- return protocol.String -- } +-// IsValidImport returns whether importPkgPath is importable +-// by pkgPath +-func IsValidImport(pkgPath, importPkgPath PackagePath) bool { +- i := strings.LastIndex(string(importPkgPath), "/internal/") +- if i == -1 { +- return true +- } +- // TODO(rfindley): this looks wrong: IsCommandLineArguments is meant to +- // operate on package IDs, not package paths. +- if IsCommandLineArguments(PackageID(pkgPath)) { +- return true - } -- return protocol.Variable +- // TODO(rfindley): this is wrong. mod.testx/p should not be able to +- // import mod.test/internal: https://go.dev/play/p/-Ca6P-E4V4q +- return strings.HasPrefix(string(pkgPath), string(importPkgPath[:i])) -} - --// symbolInformation is a cut-down version of protocol.SymbolInformation that --// allows struct values of this type to be used as map keys. --type symbolInformation struct { -- score float64 -- symbol string -- container string -- kind protocol.SymbolKind -- uri span.URI -- rng protocol.Range +-// IsCommandLineArguments reports whether a given value denotes +-// "command-line-arguments" package, which is a package with an unknown ID +-// created by the go command. It can have a test variant, which is why callers +-// should not check that a value equals "command-line-arguments" directly. +-func IsCommandLineArguments(id PackageID) bool { +- return strings.Contains(string(id), "command-line-arguments") -} - --// asProtocolSymbolInformation converts s to a protocol.SymbolInformation value. +-// embeddedIdent returns the type name identifier for an embedding x, if x in a +-// valid embedding. Otherwise, it returns nil. -// --// TODO: work out how to handle tags if/when they are needed. --func (s symbolInformation) asProtocolSymbolInformation() protocol.SymbolInformation { -- return protocol.SymbolInformation{ -- Name: s.symbol, -- Kind: s.kind, -- Location: protocol.Location{ -- URI: protocol.URIFromSpanURI(s.uri), -- Range: s.rng, -- }, -- ContainerName: s.container, +-// Spec: An embedded field must be specified as a type name T or as a pointer +-// to a non-interface type name *T +-func embeddedIdent(x ast.Expr) *ast.Ident { +- if star, ok := x.(*ast.StarExpr); ok { +- x = star.X +- } +- switch ix := x.(type) { // check for instantiated receivers +- case *ast.IndexExpr: +- x = ix.X +- case *typeparams.IndexListExpr: +- x = ix.X +- } +- switch x := x.(type) { +- case *ast.Ident: +- return x +- case *ast.SelectorExpr: +- if _, ok := x.X.(*ast.Ident); ok { +- return x.Sel +- } - } +- return nil -} -diff -urN a/gopls/internal/lsp/source/workspace_symbol_test.go b/gopls/internal/lsp/source/workspace_symbol_test.go ---- a/gopls/internal/lsp/source/workspace_symbol_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/workspace_symbol_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,136 +0,0 @@ --// Copyright 2020 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/source/view.go b/gopls/internal/lsp/source/view.go +--- a/gopls/internal/lsp/source/view.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/view.go 1970-01-01 08:00:00 +@@ -1,1036 +0,0 @@ +-// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package source - -import ( -- "testing" --) +- "bytes" +- "context" +- "crypto/sha256" +- "encoding/json" +- "errors" +- "fmt" +- "go/ast" +- "go/parser" +- "go/scanner" +- "go/token" +- "go/types" +- "io" - --func TestParseQuery(t *testing.T) { -- tests := []struct { -- query, s string -- wantMatch bool -- }{ -- {"", "anything", false}, -- {"any", "anything", true}, -- {"any$", "anything", false}, -- {"ing$", "anything", true}, -- {"ing$", "anythinG", true}, -- {"inG$", "anything", false}, -- {"^any", "anything", true}, -- {"^any", "Anything", true}, -- {"^Any", "anything", false}, -- {"at", "anything", true}, -- // TODO: this appears to be a bug in the fuzzy matching algorithm. 'At' -- // should cause a case-sensitive match. -- // {"At", "anything", false}, -- {"At", "Anything", true}, -- {"'yth", "Anything", true}, -- {"'yti", "Anything", false}, -- {"'any 'thing", "Anything", true}, -- {"anythn nythg", "Anything", true}, -- {"ntx", "Anything", false}, -- {"anythn", "anything", true}, -- {"ing", "anything", true}, -- {"anythn nythgx", "anything", false}, -- } +- "golang.org/x/mod/modfile" +- "golang.org/x/tools/go/analysis" +- "golang.org/x/tools/go/packages" +- "golang.org/x/tools/go/types/objectpath" +- "golang.org/x/tools/gopls/internal/lsp/progress" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/safetoken" +- "golang.org/x/tools/gopls/internal/lsp/source/methodsets" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/gopls/internal/vulncheck" +- "golang.org/x/tools/internal/event/label" +- "golang.org/x/tools/internal/event/tag" +- "golang.org/x/tools/internal/gocommand" +- "golang.org/x/tools/internal/imports" +- "golang.org/x/tools/internal/packagesinternal" +-) - -- for _, test := range tests { -- matcher := parseQuery(test.query, newFuzzyMatcher) -- if _, score := matcher([]string{test.s}); score > 0 != test.wantMatch { -- t.Errorf("parseQuery(%q) match for %q: %.2g, want match: %t", test.query, test.s, score, test.wantMatch) -- } -- } --} +-// A GlobalSnapshotID uniquely identifies a snapshot within this process and +-// increases monotonically with snapshot creation time. +-// +-// We use a distinct integral type for global IDs to help enforce correct +-// usage. +-type GlobalSnapshotID uint64 - --func TestFiltererDisallow(t *testing.T) { -- tests := []struct { -- filters []string -- included []string -- excluded []string -- }{ -- { -- []string{"+**/c.go"}, -- []string{"a/c.go", "a/b/c.go"}, -- []string{}, -- }, -- { -- []string{"+a/**/c.go"}, -- []string{"a/b/c.go", "a/b/d/c.go", "a/c.go"}, -- []string{}, -- }, -- { -- []string{"-a/c.go", "+a/**"}, -- []string{"a/c.go"}, -- []string{}, -- }, -- { -- []string{"+a/**/c.go", "-**/c.go"}, -- []string{}, -- []string{"a/b/c.go"}, -- }, -- { -- []string{"+a/**/c.go", "-a/**"}, -- []string{}, -- []string{"a/b/c.go"}, -- }, -- { -- []string{"+**/c.go", "-a/**/c.go"}, -- []string{}, -- []string{"a/b/c.go"}, -- }, -- { -- []string{"+foobar", "-foo"}, -- []string{"foobar", "foobar/a"}, -- []string{"foo", "foo/a"}, -- }, -- { -- []string{"+", "-"}, -- []string{}, -- []string{"foobar", "foobar/a", "foo", "foo/a"}, -- }, -- { -- []string{"-", "+"}, -- []string{"foobar", "foobar/a", "foo", "foo/a"}, -- []string{}, -- }, -- { -- []string{"-a/**/b/**/c.go"}, -- []string{}, -- []string{"a/x/y/z/b/f/g/h/c.go"}, -- }, -- // tests for unsupported glob operators -- { -- []string{"+**/c.go", "-a/*/c.go"}, -- []string{"a/b/c.go"}, -- []string{}, -- }, -- { -- []string{"+**/c.go", "-a/?/c.go"}, -- []string{"a/b/c.go"}, -- []string{}, -- }, -- { -- []string{"-b"}, // should only filter paths prefixed with the "b" directory -- []string{"a/b/c.go", "bb"}, -- []string{"b/c/d.go", "b"}, -- }, -- } +-// Snapshot represents the current state for the given view. +-type Snapshot interface { +- // SequenceID is the sequence id of this snapshot within its containing +- // view. +- // +- // Relative to their view sequence ids are monotonically increasing, but this +- // does not hold globally: when new views are created their initial snapshot +- // has sequence ID 0. For operations that span multiple views, use global +- // IDs. +- SequenceID() uint64 - -- for _, test := range tests { -- filterer := NewFilterer(test.filters) -- for _, inc := range test.included { -- if filterer.Disallow(inc) { -- t.Errorf("Filters %v excluded %v, wanted included", test.filters, inc) -- } -- } +- // GlobalID is a globally unique identifier for this snapshot. Global IDs are +- // monotonic: subsequent snapshots will have higher global ID, though +- // subsequent snapshots in a view may not have adjacent global IDs. +- GlobalID() GlobalSnapshotID - -- for _, exc := range test.excluded { -- if !filterer.Disallow(exc) { -- t.Errorf("Filters %v included %v, wanted excluded", test.filters, exc) -- } -- } -- } --} -diff -urN a/gopls/internal/lsp/source/xrefs/xrefs.go b/gopls/internal/lsp/source/xrefs/xrefs.go ---- a/gopls/internal/lsp/source/xrefs/xrefs.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/source/xrefs/xrefs.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,216 +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. +- // FileKind returns the type of a file. +- // +- // We can't reliably deduce the kind from the file name alone, +- // as some editors can be told to interpret a buffer as +- // language different from the file name heuristic, e.g. that +- // an .html file actually contains Go "html/template" syntax, +- // or even that a .go file contains Python. +- FileKind(FileHandle) FileKind - --// Package xrefs defines the serializable index of cross-package --// references that is computed during type checking. --// --// See ../references2.go for the 'references' query. --package xrefs +- // Options returns the options associated with this snapshot. +- Options() *Options - --import ( -- "bytes" -- "encoding/gob" -- "go/ast" -- "go/types" -- "log" -- "sort" +- // View returns the View associated with this snapshot. +- View() View - -- "golang.org/x/tools/go/types/objectpath" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/internal/typesinternal" --) +- // BackgroundContext returns a context used for all background processing +- // on behalf of this snapshot. +- BackgroundContext() context.Context - --// Index constructs a serializable index of outbound cross-references --// for the specified type-checked package. --func Index(files []*source.ParsedGoFile, pkg *types.Package, info *types.Info) []byte { -- // pkgObjects maps each referenced package Q to a mapping: -- // from each referenced symbol in Q to the ordered list -- // of references to that symbol from this package. -- // A nil types.Object indicates a reference -- // to the package as a whole: an import. -- pkgObjects := make(map[*types.Package]map[types.Object]*gobObject) +- // A Snapshot is a caching implementation of FileSource whose +- // ReadFile method returns consistent information about the existence +- // and content of each file throughout its lifetime. +- FileSource - -- // getObjects returns the object-to-references mapping for a package. -- getObjects := func(pkg *types.Package) map[types.Object]*gobObject { -- objects, ok := pkgObjects[pkg] -- if !ok { -- objects = make(map[types.Object]*gobObject) -- pkgObjects[pkg] = objects -- } -- return objects -- } +- // FindFile returns the FileHandle for the given URI, if it is already +- // in the given snapshot. +- // TODO(adonovan): delete this operation; use ReadFile instead. +- FindFile(uri span.URI) FileHandle - -- objectpathFor := typesinternal.NewObjectpathFunc() +- // AwaitInitialized waits until the snapshot's view is initialized. +- AwaitInitialized(ctx context.Context) - -- for fileIndex, pgf := range files { +- // IsOpen returns whether the editor currently has a file open. +- IsOpen(uri span.URI) bool - -- nodeRange := func(n ast.Node) protocol.Range { -- rng, err := pgf.PosRange(n.Pos(), n.End()) -- if err != nil { -- panic(err) // can't fail -- } -- return rng -- } +- // IgnoredFile reports if a file would be ignored by a `go list` of the whole +- // workspace. +- IgnoredFile(uri span.URI) bool - -- ast.Inspect(pgf.File, func(n ast.Node) bool { -- switch n := n.(type) { -- case *ast.Ident: -- // Report a reference for each identifier that -- // uses a symbol exported from another package. -- // (The built-in error.Error method has no package.) -- if n.IsExported() { -- if obj, ok := info.Uses[n]; ok && -- obj.Pkg() != nil && -- obj.Pkg() != pkg { +- // Templates returns the .tmpl files +- Templates() map[span.URI]FileHandle - -- objects := getObjects(obj.Pkg()) -- gobObj, ok := objects[obj] -- if !ok { -- path, err := objectpathFor(obj) -- if err != nil { -- // Capitalized but not exported -- // (e.g. local const/var/type). -- return true -- } -- gobObj = &gobObject{Path: path} -- objects[obj] = gobObj -- } +- // ParseGo returns the parsed AST for the file. +- // If the file is not available, returns nil and an error. +- // Position information is added to FileSet(). +- ParseGo(ctx context.Context, fh FileHandle, mode parser.Mode) (*ParsedGoFile, error) - -- gobObj.Refs = append(gobObj.Refs, gobRef{ -- FileIndex: fileIndex, -- Range: nodeRange(n), -- }) -- } -- } +- // Analyze runs the specified analyzers on the given packages at this snapshot. +- // +- // If the provided tracker is non-nil, it may be used to report progress of +- // the analysis pass. +- Analyze(ctx context.Context, pkgIDs map[PackageID]unit, analyzers []*Analyzer, tracker *progress.Tracker) ([]*Diagnostic, error) - -- case *ast.ImportSpec: -- // Report a reference from each import path -- // string to the imported package. -- var obj types.Object -- if n.Name != nil { -- obj = info.Defs[n.Name] -- } else { -- obj = info.Implicits[n] -- } -- if obj == nil { -- return true // missing import -- } -- objects := getObjects(obj.(*types.PkgName).Imported()) -- gobObj, ok := objects[nil] -- if !ok { -- gobObj = &gobObject{Path: ""} -- objects[nil] = gobObj -- } -- gobObj.Refs = append(gobObj.Refs, gobRef{ -- FileIndex: fileIndex, -- Range: nodeRange(n.Path), -- }) -- } -- return true -- }) -- } +- // RunGoCommandPiped runs the given `go` command, writing its output +- // to stdout and stderr. Verb, Args, and WorkingDir must be specified. +- // +- // RunGoCommandPiped runs the command serially using gocommand.RunPiped, +- // enforcing that this command executes exclusively to other commands on the +- // server. +- RunGoCommandPiped(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation, stdout, stderr io.Writer) error - -- // Flatten the maps into slices, and sort for determinism. -- var packages []*gobPackage -- for p := range pkgObjects { -- objects := pkgObjects[p] -- gp := &gobPackage{ -- PkgPath: source.PackagePath(p.Path()), -- Objects: make([]*gobObject, 0, len(objects)), -- } -- for _, gobObj := range objects { -- gp.Objects = append(gp.Objects, gobObj) -- } -- sort.Slice(gp.Objects, func(i, j int) bool { -- return gp.Objects[i].Path < gp.Objects[j].Path -- }) -- packages = append(packages, gp) -- } -- sort.Slice(packages, func(i, j int) bool { -- return packages[i].PkgPath < packages[j].PkgPath -- }) +- // RunGoCommandDirect runs the given `go` command. Verb, Args, and +- // WorkingDir must be specified. +- RunGoCommandDirect(ctx context.Context, mode InvocationFlags, inv *gocommand.Invocation) (*bytes.Buffer, error) - -- return mustEncode(packages) --} +- // RunGoCommands runs a series of `go` commands that updates the go.mod +- // and go.sum file for wd, and returns their updated contents. +- RunGoCommands(ctx context.Context, allowNetwork bool, wd string, run func(invoke func(...string) (*bytes.Buffer, error)) error) (bool, []byte, []byte, error) - --// Lookup searches a serialized index produced by an indexPackage --// operation on m, and returns the locations of all references from m --// to any object in the target set. Each object is denoted by a pair --// of (package path, object path). --func Lookup(m *source.Metadata, data []byte, targets map[source.PackagePath]map[objectpath.Path]struct{}) (locs []protocol.Location) { +- // RunProcessEnvFunc runs fn with the process env for this snapshot's view. +- // Note: the process env contains cached module and filesystem state. +- RunProcessEnvFunc(ctx context.Context, fn func(context.Context, *imports.Options) error) error - -- // TODO(adonovan): opt: evaluate whether it would be faster to decode -- // in two passes, first with struct { PkgPath string; Objects BLOB } -- // to find the relevant record without decoding the Objects slice, -- // then decode just the desired BLOB into a slice. BLOB would be a -- // type whose Unmarshal method just retains (a copy of) the bytes. -- var packages []gobPackage -- mustDecode(data, &packages) +- // ModFiles are the go.mod files enclosed in the snapshot's view and known +- // to the snapshot. +- ModFiles() []span.URI - -- for _, gp := range packages { -- if objectSet, ok := targets[gp.PkgPath]; ok { -- for _, gobObj := range gp.Objects { -- if _, ok := objectSet[gobObj.Path]; ok { -- for _, ref := range gobObj.Refs { -- uri := m.CompiledGoFiles[ref.FileIndex] -- locs = append(locs, protocol.Location{ -- URI: protocol.URIFromSpanURI(uri), -- Range: ref.Range, -- }) -- } -- } -- } -- } -- } +- // ParseMod is used to parse go.mod files. +- ParseMod(ctx context.Context, fh FileHandle) (*ParsedModule, error) - -- return locs --} +- // ModWhy returns the results of `go mod why` for the module specified by +- // the given go.mod file. +- ModWhy(ctx context.Context, fh FileHandle) (map[string]string, error) - --// -- serialized representation -- +- // ModTidy returns the results of `go mod tidy` for the module specified by +- // the given go.mod file. +- ModTidy(ctx context.Context, pm *ParsedModule) (*TidiedModule, error) - --// The cross-reference index records the location of all references --// from one package to symbols defined in other packages --// (dependencies). It does not record within-package references. --// The index for package P consists of a list of gopPackage records, --// each enumerating references to symbols defined a single dependency, Q. +- // ModVuln returns import vulnerability analysis for the given go.mod URI. +- // Concurrent requests are combined into a single command. +- ModVuln(ctx context.Context, modURI span.URI) (*vulncheck.Result, error) - --// TODO(adonovan): opt: choose a more compact encoding. Gzip reduces --// the gob output to about one third its size, so clearly there's room --// to improve. The gobRef.Range field is the obvious place to begin. --// Even a zero-length slice gob-encodes to ~285 bytes. +- // GoModForFile returns the URI of the go.mod file for the given URI. +- GoModForFile(uri span.URI) span.URI - --// A gobPackage records the set of outgoing references from the index --// package to symbols defined in a dependency package. --type gobPackage struct { -- PkgPath source.PackagePath // defining package (Q) -- Objects []*gobObject // set of Q objects referenced by P --} +- // WorkFile, if non-empty, is the go.work file for the workspace. +- WorkFile() span.URI - --// A gobObject records all references to a particular symbol. --type gobObject struct { -- Path objectpath.Path // symbol name within package; "" => import of package itself -- Refs []gobRef // locations of references within P, in lexical order --} +- // ParseWork is used to parse go.work files. +- ParseWork(ctx context.Context, fh FileHandle) (*ParsedWorkFile, error) - --type gobRef struct { -- FileIndex int // index of enclosing file within P's CompiledGoFiles -- Range protocol.Range // source range of reference --} +- // BuiltinFile returns information about the special builtin package. +- BuiltinFile(ctx context.Context) (*ParsedGoFile, error) - --// -- duplicated from ../../cache/analysis.go -- +- // IsBuiltin reports whether uri is part of the builtin package. +- IsBuiltin(uri span.URI) bool - --func mustEncode(x interface{}) []byte { -- var buf bytes.Buffer -- if err := gob.NewEncoder(&buf).Encode(x); err != nil { -- log.Fatalf("internal error encoding %T: %v", x, err) -- } -- return buf.Bytes() --} +- // CriticalError returns any critical errors in the workspace. +- // +- // A nil result may mean success, or context cancellation. +- CriticalError(ctx context.Context) *CriticalError - --func mustDecode(data []byte, ptr interface{}) { -- if err := gob.NewDecoder(bytes.NewReader(data)).Decode(ptr); err != nil { -- log.Fatalf("internal error decoding %T: %v", ptr, err) -- } --} -diff -urN a/gopls/internal/lsp/symbols.go b/gopls/internal/lsp/symbols.go ---- a/gopls/internal/lsp/symbols.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/symbols.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,60 +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. +- // Symbols returns all symbols in the snapshot. +- // +- // If workspaceOnly is set, this only includes symbols from files in a +- // workspace package. Otherwise, it returns symbols from all loaded packages. +- Symbols(ctx context.Context, workspaceOnly bool) (map[span.URI][]Symbol, error) - --package lsp +- // -- package metadata -- - --import ( -- "context" +- // ReverseDependencies returns a new mapping whose entries are +- // the ID and Metadata of each package in the workspace that +- // directly or transitively depend on the package denoted by id, +- // excluding id itself. +- ReverseDependencies(ctx context.Context, id PackageID, transitive bool) (map[PackageID]*Metadata, error) - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/lsp/template" -- "golang.org/x/tools/internal/event" -- "golang.org/x/tools/internal/event/tag" --) +- // WorkspaceMetadata returns a new, unordered slice containing +- // metadata for all ordinary and test packages (but not +- // intermediate test variants) in the workspace. +- // +- // The workspace is the set of modules typically defined by a +- // go.work file. It is not transitively closed: for example, +- // the standard library is not usually part of the workspace +- // even though every module in the workspace depends on it. +- // +- // Operations that must inspect all the dependencies of the +- // workspace packages should instead use AllMetadata. +- WorkspaceMetadata(ctx context.Context) ([]*Metadata, error) - --func (s *Server) documentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]interface{}, error) { -- ctx, done := event.Start(ctx, "lsp.Server.documentSymbol") -- defer done() +- // AllMetadata returns a new unordered array of metadata for +- // all packages known to this snapshot, which includes the +- // packages of all workspace modules plus their transitive +- // import dependencies. +- // +- // It may also contain ad-hoc packages for standalone files. +- // It includes all test variants. +- AllMetadata(ctx context.Context) ([]*Metadata, error) - -- snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) -- defer release() -- if !ok { -- return []interface{}{}, err -- } -- var docSymbols []protocol.DocumentSymbol -- switch snapshot.View().FileKind(fh) { -- case source.Tmpl: -- docSymbols, err = template.DocumentSymbols(snapshot, fh) -- case source.Go: -- docSymbols, err = source.DocumentSymbols(ctx, snapshot, fh) -- default: -- return []interface{}{}, nil -- } +- // Metadata returns the metadata for the specified package, +- // or nil if it was not found. +- Metadata(id PackageID) *Metadata +- +- // MetadataForFile returns a new slice containing metadata for each +- // package containing the Go file identified by uri, ordered by the +- // number of CompiledGoFiles (i.e. "narrowest" to "widest" package), +- // and secondarily by IsIntermediateTestVariant (false < true). +- // The result may include tests and intermediate test variants of +- // importable packages. +- // It returns an error if the context was cancelled. +- MetadataForFile(ctx context.Context, uri span.URI) ([]*Metadata, error) +- +- // OrphanedFileDiagnostics reports diagnostics for files that have no package +- // associations or which only have only command-line-arguments packages. +- // +- // The caller must not mutate the result. +- OrphanedFileDiagnostics(ctx context.Context) (map[span.URI]*Diagnostic, error) +- +- // -- package type-checking -- +- +- // TypeCheck parses and type-checks the specified packages, +- // and returns them in the same order as the ids. +- // The resulting packages' types may belong to different importers, +- // so types from different packages are incommensurable. +- // +- // In general, clients should never need to type-checked +- // syntax for an intermediate test variant (ITV) package. +- // Callers should apply RemoveIntermediateTestVariants (or +- // equivalent) before this method, or any of the potentially +- // type-checking methods below. +- TypeCheck(ctx context.Context, ids ...PackageID) ([]Package, error) +- +- // PackageDiagnostics returns diagnostics for files contained in specified +- // packages. +- // +- // If these diagnostics cannot be loaded from cache, the requested packages +- // may be type-checked. +- PackageDiagnostics(ctx context.Context, ids ...PackageID) (map[span.URI][]*Diagnostic, error) +- +- // References returns cross-references indexes for the specified packages. +- // +- // If these indexes cannot be loaded from cache, the requested packages may +- // be type-checked. +- References(ctx context.Context, ids ...PackageID) ([]XrefIndex, error) +- +- // MethodSets returns method-set indexes for the specified packages. +- // +- // If these indexes cannot be loaded from cache, the requested packages may +- // be type-checked. +- MethodSets(ctx context.Context, ids ...PackageID) ([]*methodsets.Index, error) +-} +- +-// NarrowestMetadataForFile returns metadata for the narrowest package +-// (the one with the fewest files) that encloses the specified file. +-// The result may be a test variant, but never an intermediate test variant. +-func NarrowestMetadataForFile(ctx context.Context, snapshot Snapshot, uri span.URI) (*Metadata, error) { +- metas, err := snapshot.MetadataForFile(ctx, uri) - if err != nil { -- event.Error(ctx, "DocumentSymbols failed", err, tag.URI.Of(fh.URI())) -- return []interface{}{}, nil +- return nil, err - } -- // Convert the symbols to an interface array. -- // TODO: Remove this once the lsp deprecates SymbolInformation. -- symbols := make([]interface{}, len(docSymbols)) -- for i, s := range docSymbols { -- if snapshot.View().Options().HierarchicalDocumentSymbolSupport { -- symbols[i] = s -- continue -- } -- // If the client does not support hierarchical document symbols, then -- // we need to be backwards compatible for now and return SymbolInformation. -- symbols[i] = protocol.SymbolInformation{ -- Name: s.Name, -- Kind: s.Kind, -- Deprecated: s.Deprecated, -- Location: protocol.Location{ -- URI: params.TextDocument.URI, -- Range: s.Range, -- }, -- } +- RemoveIntermediateTestVariants(&metas) +- if len(metas) == 0 { +- return nil, fmt.Errorf("no package metadata for file %s", uri) - } -- return symbols, nil +- return metas[0], nil -} -diff -urN a/gopls/internal/lsp/template/completion.go b/gopls/internal/lsp/template/completion.go ---- a/gopls/internal/lsp/template/completion.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/template/completion.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,287 +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 template -- --import ( -- "bytes" -- "context" -- "fmt" -- "go/scanner" -- "go/token" -- "strings" +-type XrefIndex interface { +- Lookup(targets map[PackagePath]map[objectpath.Path]struct{}) (locs []protocol.Location) +-} +- +-// SnapshotLabels returns a new slice of labels that should be used for events +-// related to a snapshot. +-func SnapshotLabels(snapshot Snapshot) []label.Label { +- return []label.Label{tag.Snapshot.Of(snapshot.SequenceID()), tag.Directory.Of(snapshot.View().Folder())} +-} +- +-// NarrowestPackageForFile is a convenience function that selects the +-// narrowest non-ITV package to which this file belongs, type-checks +-// it in the requested mode (full or workspace), and returns it, along +-// with the parse tree of that file. +-// +-// The "narrowest" package is the one with the fewest number of files +-// that includes the given file. This solves the problem of test +-// variants, as the test will have more files than the non-test package. +-// (Historically the preference was a parameter but widest was almost +-// never needed.) +-// +-// An intermediate test variant (ITV) package has identical source +-// to a regular package but resolves imports differently. +-// gopls should never need to type-check them. +-// +-// Type-checking is expensive. Call snapshot.ParseGo if all you need +-// is a parse tree, or snapshot.MetadataForFile if you only need metadata. +-func NarrowestPackageForFile(ctx context.Context, snapshot Snapshot, uri span.URI) (Package, *ParsedGoFile, error) { +- metas, err := snapshot.MetadataForFile(ctx, uri) +- if err != nil { +- return nil, nil, err +- } +- RemoveIntermediateTestVariants(&metas) +- if len(metas) == 0 { +- return nil, nil, fmt.Errorf("no package metadata for file %s", uri) +- } +- narrowest := metas[0] +- pkgs, err := snapshot.TypeCheck(ctx, narrowest.ID) +- if err != nil { +- return nil, nil, err +- } +- pkg := pkgs[0] +- pgf, err := pkg.File(uri) +- if err != nil { +- return nil, nil, err // "can't happen" +- } +- return pkg, pgf, err +-} +- +-// InvocationFlags represents the settings of a particular go command invocation. +-// It is a mode, plus a set of flag bits. +-type InvocationFlags int +- +-const ( +- // Normal is appropriate for commands that might be run by a user and don't +- // deliberately modify go.mod files, e.g. `go test`. +- Normal InvocationFlags = iota +- // WriteTemporaryModFile is for commands that need information from a +- // modified version of the user's go.mod file, e.g. `go mod tidy` used to +- // generate diagnostics. +- WriteTemporaryModFile +- // LoadWorkspace is for packages.Load, and other operations that should +- // consider the whole workspace at once. +- LoadWorkspace +- +- // AllowNetwork is a flag bit that indicates the invocation should be +- // allowed to access the network. +- AllowNetwork InvocationFlags = 1 << 10 +-) +- +-func (m InvocationFlags) Mode() InvocationFlags { +- return m & (AllowNetwork - 1) +-} +- +-func (m InvocationFlags) AllowNetwork() bool { +- return m&AllowNetwork != 0 +-} +- +-// View represents a single workspace. +-// This is the level at which we maintain configuration like working directory +-// and build tags. +-type View interface { +- // ID returns a globally unique identifier for this view. +- ID() string +- +- // Name returns the name this view was constructed with. +- Name() string +- +- // Folder returns the folder with which this view was created. +- Folder() span.URI +- +- // Snapshot returns the current snapshot for the view, and a +- // release function that must be called when the Snapshot is +- // no longer needed. +- // +- // If the view is shut down, the resulting error will be non-nil, and the +- // release function need not be called. +- Snapshot() (Snapshot, func(), error) +- +- // IsGoPrivatePath reports whether target is a private import path, as identified +- // by the GOPRIVATE environment variable. +- IsGoPrivatePath(path string) bool +- +- // ModuleUpgrades returns known module upgrades for the dependencies of +- // modfile. +- ModuleUpgrades(modfile span.URI) map[string]string +- +- // RegisterModuleUpgrades registers that upgrades exist for the given modules +- // required by modfile. +- RegisterModuleUpgrades(modfile span.URI, upgrades map[string]string) +- +- // ClearModuleUpgrades clears all upgrades for the modules in modfile. +- ClearModuleUpgrades(modfile span.URI) +- +- // Vulnerabilities returns known vulnerabilities for the given modfile. +- // TODO(suzmue): replace command.Vuln with a different type, maybe +- // https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck/govulnchecklib#Summary? +- Vulnerabilities(modfile ...span.URI) map[span.URI]*vulncheck.Result +- +- // SetVulnerabilities resets the list of vulnerabilities that exists for the given modules +- // required by modfile. +- SetVulnerabilities(modfile span.URI, vulncheckResult *vulncheck.Result) +- +- // GoVersion returns the configured Go version for this view. +- GoVersion() int +- +- // GoVersionString returns the go version string configured for this view. +- // Unlike [GoVersion], this encodes the minor version and commit hash information. +- GoVersionString() string +-} +- +-// A FileSource maps URIs to FileHandles. +-type FileSource interface { +- // ReadFile returns the FileHandle for a given URI, either by +- // reading the content of the file or by obtaining it from a cache. +- // +- // Invariant: ReadFile must only return an error in the case of context +- // cancellation. If ctx.Err() is nil, the resulting error must also be nil. +- ReadFile(ctx context.Context, uri span.URI) (FileHandle, error) +-} +- +-// A MetadataSource maps package IDs to metadata. +-// +-// TODO(rfindley): replace this with a concrete metadata graph, once it is +-// exposed from the snapshot. +-type MetadataSource interface { +- // Metadata returns Metadata for the given package ID, or nil if it does not +- // exist. +- Metadata(PackageID) *Metadata +-} +- +-// A ParsedGoFile contains the results of parsing a Go file. +-type ParsedGoFile struct { +- URI span.URI +- Mode parser.Mode +- File *ast.File +- Tok *token.File +- // Source code used to build the AST. It may be different from the +- // actual content of the file if we have fixed the AST. +- Src []byte +- +- // FixedSrc and Fixed AST report on "fixing" that occurred during parsing of +- // this file. +- // +- // If FixedSrc == true, the source contained in the Src field was modified +- // from the original source to improve parsing. +- // +- // If FixedAST == true, the ast was modified after parsing, and therefore +- // positions encoded in the AST may not accurately represent the content of +- // the Src field. +- // +- // TODO(rfindley): there are many places where we haphazardly use the Src or +- // positions without checking these fields. Audit these places and guard +- // accordingly. After doing so, we may find that we don't need to +- // differentiate FixedSrc and FixedAST. +- FixedSrc bool +- FixedAST bool +- Mapper *protocol.Mapper // may map fixed Src, not file content +- ParseErr scanner.ErrorList +-} +- +-// Fixed reports whether p was "Fixed", meaning that its source or positions +-// may not correlate with the original file. +-func (p ParsedGoFile) Fixed() bool { +- return p.FixedSrc || p.FixedAST +-} +- +-// -- go/token domain convenience helpers -- +- +-// PositionPos returns the token.Pos of protocol position p within the file. +-func (pgf *ParsedGoFile) PositionPos(p protocol.Position) (token.Pos, error) { +- offset, err := pgf.Mapper.PositionOffset(p) +- if err != nil { +- return token.NoPos, err +- } +- return safetoken.Pos(pgf.Tok, offset) +-} +- +-// PosRange returns a protocol Range for the token.Pos interval in this file. +-func (pgf *ParsedGoFile) PosRange(start, end token.Pos) (protocol.Range, error) { +- return pgf.Mapper.PosRange(pgf.Tok, start, end) +-} +- +-// PosMappedRange returns a MappedRange for the token.Pos interval in this file. +-// A MappedRange can be converted to any other form. +-func (pgf *ParsedGoFile) PosMappedRange(start, end token.Pos) (protocol.MappedRange, error) { +- return pgf.Mapper.PosMappedRange(pgf.Tok, start, end) +-} +- +-// PosLocation returns a protocol Location for the token.Pos interval in this file. +-func (pgf *ParsedGoFile) PosLocation(start, end token.Pos) (protocol.Location, error) { +- return pgf.Mapper.PosLocation(pgf.Tok, start, end) +-} +- +-// NodeRange returns a protocol Range for the ast.Node interval in this file. +-func (pgf *ParsedGoFile) NodeRange(node ast.Node) (protocol.Range, error) { +- return pgf.Mapper.NodeRange(pgf.Tok, node) +-} +- +-// NodeMappedRange returns a MappedRange for the ast.Node interval in this file. +-// A MappedRange can be converted to any other form. +-func (pgf *ParsedGoFile) NodeMappedRange(node ast.Node) (protocol.MappedRange, error) { +- return pgf.Mapper.NodeMappedRange(pgf.Tok, node) +-} +- +-// NodeLocation returns a protocol Location for the ast.Node interval in this file. +-func (pgf *ParsedGoFile) NodeLocation(node ast.Node) (protocol.Location, error) { +- return pgf.Mapper.PosLocation(pgf.Tok, node.Pos(), node.End()) +-} +- +-// RangePos parses a protocol Range back into the go/token domain. +-func (pgf *ParsedGoFile) RangePos(r protocol.Range) (token.Pos, token.Pos, error) { +- start, end, err := pgf.Mapper.RangeOffsets(r) +- if err != nil { +- return token.NoPos, token.NoPos, err +- } +- return pgf.Tok.Pos(start), pgf.Tok.Pos(end), nil +-} +- +-// A ParsedModule contains the results of parsing a go.mod file. +-type ParsedModule struct { +- URI span.URI +- File *modfile.File +- Mapper *protocol.Mapper +- ParseErrors []*Diagnostic +-} +- +-// A ParsedWorkFile contains the results of parsing a go.work file. +-type ParsedWorkFile struct { +- URI span.URI +- File *modfile.WorkFile +- Mapper *protocol.Mapper +- ParseErrors []*Diagnostic +-} +- +-// A TidiedModule contains the results of running `go mod tidy` on a module. +-type TidiedModule struct { +- // Diagnostics representing changes made by `go mod tidy`. +- Diagnostics []*Diagnostic +- // The bytes of the go.mod file after it was tidied. +- TidiedContent []byte +-} +- +-// Metadata represents package metadata retrieved from go/packages. +-// The Deps* maps do not contain self-import edges. +-// +-// An ad-hoc package (without go.mod or GOPATH) has its ID, PkgPath, +-// and LoadDir equal to the absolute path of its directory. +-type Metadata struct { +- ID PackageID +- PkgPath PackagePath +- Name PackageName +- +- // these three fields are as defined by go/packages.Package +- GoFiles []span.URI +- CompiledGoFiles []span.URI +- IgnoredFiles []span.URI +- +- ForTest PackagePath // q in a "p [q.test]" package, else "" +- TypesSizes types.Sizes +- Errors []packages.Error // must be set for packages in import cycles +- DepsByImpPath map[ImportPath]PackageID // may contain dups; empty ID => missing +- DepsByPkgPath map[PackagePath]PackageID // values are unique and non-empty +- Module *packages.Module +- DepsErrors []*packagesinternal.PackageError +- Diagnostics []*Diagnostic // processed diagnostics from 'go list' +- LoadDir string // directory from which go/packages was run +- Standalone bool // package synthesized for a standalone file (e.g. ignore-tagged) +-} +- +-func (m *Metadata) String() string { return string(m.ID) } +- +-// IsIntermediateTestVariant reports whether the given package is an +-// intermediate test variant (ITV), e.g. "net/http [net/url.test]". +-// +-// An ITV has identical syntax to the regular variant, but different +-// import metadata (DepsBy{Imp,Pkg}Path). +-// +-// Such test variants arise when an x_test package (in this case net/url_test) +-// imports a package (in this case net/http) that itself imports the +-// non-x_test package (in this case net/url). +-// +-// This is done so that the forward transitive closure of net/url_test has +-// only one package for the "net/url" import. +-// The ITV exists to hold the test variant import: +-// +-// net/url_test [net/url.test] +-// +-// | "net/http" -> net/http [net/url.test] +-// | "net/url" -> net/url [net/url.test] +-// | ... +-// +-// net/http [net/url.test] +-// +-// | "net/url" -> net/url [net/url.test] +-// | ... +-// +-// This restriction propagates throughout the import graph of net/http: for +-// every package imported by net/http that imports net/url, there must be an +-// intermediate test variant that instead imports "net/url [net/url.test]". +-// +-// As one can see from the example of net/url and net/http, intermediate test +-// variants can result in many additional packages that are essentially (but +-// not quite) identical. For this reason, we filter these variants wherever +-// possible. +-// +-// # Why we mostly ignore intermediate test variants +-// +-// In projects with complicated tests, there may be a very large +-// number of ITVs--asymptotically more than the number of ordinary +-// variants. Since they have identical syntax, it is fine in most +-// cases to ignore them since the results of analyzing the ordinary +-// variant suffice. However, this is not entirely sound. +-// +-// Consider this package: +-// +-// // p/p.go -- in all variants of p +-// package p +-// type T struct { io.Closer } +-// +-// // p/p_test.go -- in test variant of p +-// package p +-// func (T) Close() error { ... } +-// +-// The ordinary variant "p" defines T with a Close method promoted +-// from io.Closer. But its test variant "p [p.test]" defines a type T +-// with a Close method from p_test.go. +-// +-// Now consider a package q that imports p, perhaps indirectly. Within +-// it, T.Close will resolve to the first Close method: +-// +-// // q/q.go -- in all variants of q +-// package q +-// import "p" +-// var _ = new(p.T).Close +-// +-// Let's assume p also contains this file defining an external test (xtest): +-// +-// // p/p_x_test.go -- external test of p +-// package p_test +-// import ( "q"; "testing" ) +-// func Test(t *testing.T) { ... } +-// +-// Note that q imports p, but p's xtest imports q. Now, in "q +-// [p.test]", the intermediate test variant of q built for p's +-// external test, T.Close resolves not to the io.Closer.Close +-// interface method, but to the concrete method of T.Close +-// declared in p_test.go. +-// +-// If we now request all references to the T.Close declaration in +-// p_test.go, the result should include the reference from q's ITV. +-// (It's not just methods that can be affected; fields can too, though +-// it requires bizarre code to achieve.) +-// +-// As a matter of policy, gopls mostly ignores this subtlety, +-// because to account for it would require that we type-check every +-// intermediate test variant of p, of which there could be many. +-// Good code doesn't rely on such trickery. +-// +-// Most callers of MetadataForFile call RemoveIntermediateTestVariants +-// to discard them before requesting type checking, or the products of +-// type-checking such as the cross-reference index or method set index. +-// +-// MetadataForFile doesn't do this filtering itself becaused in some +-// cases we need to make a reverse dependency query on the metadata +-// graph, and it's important to include the rdeps of ITVs in that +-// query. But the filtering of ITVs should be applied after that step, +-// before type checking. +-// +-// In general, we should never type check an ITV. +-func (m *Metadata) IsIntermediateTestVariant() bool { +- return m.ForTest != "" && m.ForTest != m.PkgPath && m.ForTest+"_test" != m.PkgPath +-} +- +-// RemoveIntermediateTestVariants removes intermediate test variants, modifying the array. +-// We use a pointer to a slice make it impossible to forget to use the result. +-func RemoveIntermediateTestVariants(pmetas *[]*Metadata) { +- metas := *pmetas +- res := metas[:0] +- for _, m := range metas { +- if !m.IsIntermediateTestVariant() { +- res = append(res, m) +- } +- } +- *pmetas = res +-} +- +-var ErrViewExists = errors.New("view already exists for session") +- +-// FileModification represents a modification to a file. +-type FileModification struct { +- URI span.URI +- Action FileAction +- +- // OnDisk is true if a watched file is changed on disk. +- // If true, Version will be -1 and Text will be nil. +- OnDisk bool +- +- // Version will be -1 and Text will be nil when they are not supplied, +- // specifically on textDocument/didClose and for on-disk changes. +- Version int32 +- Text []byte +- +- // LanguageID is only sent from the language client on textDocument/didOpen. +- LanguageID string +-} +- +-type FileAction int +- +-const ( +- UnknownFileAction = FileAction(iota) +- Open +- Change +- Close +- Save +- Create +- Delete +- InvalidateMetadata +-) +- +-func (a FileAction) String() string { +- switch a { +- case Open: +- return "Open" +- case Change: +- return "Change" +- case Close: +- return "Close" +- case Save: +- return "Save" +- case Create: +- return "Create" +- case Delete: +- return "Delete" +- case InvalidateMetadata: +- return "InvalidateMetadata" +- default: +- return "Unknown" +- } +-} +- +-var ErrTmpModfileUnsupported = errors.New("-modfile is unsupported for this Go version") +-var ErrNoModOnDisk = errors.New("go.mod file is not on disk") +- +-func IsNonFatalGoModError(err error) bool { +- return err == ErrTmpModfileUnsupported || err == ErrNoModOnDisk +-} +- +-// Common parse modes; these should be reused wherever possible to increase +-// cache hits. +-const ( +- // ParseHeader specifies that the main package declaration and imports are needed. +- // This is the mode used when attempting to examine the package graph structure. +- ParseHeader = parser.AllErrors | parser.ParseComments | parser.ImportsOnly | SkipObjectResolution +- +- // ParseFull specifies the full AST is needed. +- // This is used for files of direct interest where the entire contents must +- // be considered. +- ParseFull = parser.AllErrors | parser.ParseComments | SkipObjectResolution +-) +- +-// A FileHandle represents the URI, content, hash, and optional +-// version of a file tracked by the LSP session. +-// +-// File content may be provided by the file system (for Saved files) +-// or from an overlay, for open files with unsaved edits. +-// A FileHandle may record an attempt to read a non-existent file, +-// in which case Content returns an error. +-type FileHandle interface { +- // URI is the URI for this file handle. +- // TODO(rfindley): this is not actually well-defined. In some cases, there +- // may be more than one URI that resolve to the same FileHandle. Which one is +- // this? +- URI() span.URI +- // FileIdentity returns a FileIdentity for the file, even if there was an +- // error reading it. +- FileIdentity() FileIdentity +- // SameContentsOnDisk reports whether the file has the same content on disk: +- // it is false for files open on an editor with unsaved edits. +- SameContentsOnDisk() bool +- // Version returns the file version, as defined by the LSP client. +- // For on-disk file handles, Version returns 0. +- Version() int32 +- // Content returns the contents of a file. +- // If the file is not available, returns a nil slice and an error. +- Content() ([]byte, error) +-} +- +-// A Hash is a cryptographic digest of the contents of a file. +-// (Although at 32B it is larger than a 16B string header, it is smaller +-// and has better locality than the string header + 64B of hex digits.) +-type Hash [sha256.Size]byte +- +-// HashOf returns the hash of some data. +-func HashOf(data []byte) Hash { +- return Hash(sha256.Sum256(data)) +-} +- +-// Hashf returns the hash of a printf-formatted string. +-func Hashf(format string, args ...interface{}) Hash { +- // Although this looks alloc-heavy, it is faster than using +- // Fprintf on sha256.New() because the allocations don't escape. +- return HashOf([]byte(fmt.Sprintf(format, args...))) +-} +- +-// String returns the digest as a string of hex digits. +-func (h Hash) String() string { +- return fmt.Sprintf("%64x", [sha256.Size]byte(h)) +-} +- +-// Less returns true if the given hash is less than the other. +-func (h Hash) Less(other Hash) bool { +- return bytes.Compare(h[:], other[:]) < 0 +-} +- +-// XORWith updates *h to *h XOR h2. +-func (h *Hash) XORWith(h2 Hash) { +- // Small enough that we don't need crypto/subtle.XORBytes. +- for i := range h { +- h[i] ^= h2[i] +- } +-} +- +-// FileIdentity uniquely identifies a file at a version from a FileSystem. +-type FileIdentity struct { +- URI span.URI +- Hash Hash // digest of file contents +-} +- +-func (id FileIdentity) String() string { +- return fmt.Sprintf("%s%s", id.URI, id.Hash) +-} +- +-// FileKind describes the kind of the file in question. +-// It can be one of Go,mod, Sum, or Tmpl. +-type FileKind int +- +-const ( +- // UnknownKind is a file type we don't know about. +- UnknownKind = FileKind(iota) +- +- // Go is a normal go source file. +- Go +- // Mod is a go.mod file. +- Mod +- // Sum is a go.sum file. +- Sum +- // Tmpl is a template file. +- Tmpl +- // Work is a go.work file. +- Work +-) +- +-func (k FileKind) String() string { +- switch k { +- case Go: +- return "go" +- case Mod: +- return "go.mod" +- case Sum: +- return "go.sum" +- case Tmpl: +- return "tmpl" +- case Work: +- return "go.work" +- default: +- return fmt.Sprintf("internal error: unknown file kind %d", k) +- } +-} +- +-// Analyzer represents a go/analysis analyzer with some boolean properties +-// that let the user know how to use the analyzer. +-type Analyzer struct { +- Analyzer *analysis.Analyzer +- +- // Enabled reports whether the analyzer is enabled. This value can be +- // configured per-analysis in user settings. For staticcheck analyzers, +- // the value of the Staticcheck setting overrides this field. +- // +- // Most clients should use the IsEnabled method. +- Enabled bool +- +- // Fix is the name of the suggested fix name used to invoke the suggested +- // fixes for the analyzer. It is non-empty if we expect this analyzer to +- // provide its fix separately from its diagnostics. That is, we should apply +- // the analyzer's suggested fixes through a Command, not a TextEdit. +- Fix string +- +- // fixesDiagnostic reports if a diagnostic from the analyzer can be fixed by Fix. +- // If nil then all diagnostics from the analyzer are assumed to be fixable. +- fixesDiagnostic func(*Diagnostic) bool +- +- // ActionKind is the kind of code action this analyzer produces. If +- // unspecified the type defaults to quickfix. +- ActionKind []protocol.CodeActionKind +- +- // Severity is the severity set for diagnostics reported by this +- // analyzer. If left unset it defaults to Warning. +- Severity protocol.DiagnosticSeverity +- +- // Tag is extra tags (unnecessary, deprecated, etc) for diagnostics +- // reported by this analyzer. +- Tag []protocol.DiagnosticTag +-} +- +-func (a *Analyzer) String() string { return a.Analyzer.String() } +- +-// IsEnabled reports whether this analyzer is enabled by the given options. +-func (a Analyzer) IsEnabled(options *Options) bool { +- // Staticcheck analyzers can only be enabled when staticcheck is on. +- if _, ok := options.StaticcheckAnalyzers[a.Analyzer.Name]; ok { +- if !options.Staticcheck { +- return false +- } +- } +- if enabled, ok := options.Analyses[a.Analyzer.Name]; ok { +- return enabled +- } +- return a.Enabled +-} +- +-// FixesDiagnostic returns true if Analyzer.Fix can fix the Diagnostic. +-func (a Analyzer) FixesDiagnostic(d *Diagnostic) bool { +- if a.fixesDiagnostic == nil { +- return true +- } +- return a.fixesDiagnostic(d) +-} +- +-// Declare explicit types for package paths, names, and IDs to ensure that we +-// never use an ID where a path belongs, and vice versa. If we confused these, +-// it would result in confusing errors because package IDs often look like +-// package paths. +-type ( +- PackageID string // go list's unique identifier for a package (e.g. "vendor/example.com/foo [vendor/example.com/bar.test]") +- PackagePath string // name used to prefix linker symbols (e.g. "vendor/example.com/foo") +- PackageName string // identifier in 'package' declaration (e.g. "foo") +- ImportPath string // path that appears in an import declaration (e.g. "example.com/foo") +-) +- +-// Package represents a Go package that has been parsed and type-checked. +-// +-// By design, there is no way to reach from a Package to the Package +-// representing one of its dependencies. +-// +-// Callers must not assume that two Packages share the same +-// token.FileSet or types.Importer and thus have commensurable +-// token.Pos values or types.Objects. Instead, use stable naming +-// schemes, such as (URI, byte offset) for positions, or (PackagePath, +-// objectpath.Path) for exported declarations. +-type Package interface { +- Metadata() *Metadata +- +- // Results of parsing: +- FileSet() *token.FileSet +- CompiledGoFiles() []*ParsedGoFile // (borrowed) +- File(uri span.URI) (*ParsedGoFile, error) +- GetSyntax() []*ast.File // (borrowed) +- GetParseErrors() []scanner.ErrorList +- +- // Results of type checking: +- GetTypes() *types.Package +- GetTypeErrors() []types.Error +- GetTypesInfo() *types.Info +- DependencyTypes(PackagePath) *types.Package // nil for indirect dependency of no consequence +- DiagnosticsForFile(ctx context.Context, s Snapshot, uri span.URI) ([]*Diagnostic, error) +-} +- +-type unit = struct{} +- +-// A CriticalError is a workspace-wide error that generally prevents gopls from +-// functioning correctly. In the presence of critical errors, other diagnostics +-// in the workspace may not make sense. +-type CriticalError struct { +- // MainError is the primary error. Must be non-nil. +- MainError error +- +- // Diagnostics contains any supplemental (structured) diagnostics. +- Diagnostics []*Diagnostic +-} +- +-// An Diagnostic corresponds to an LSP Diagnostic. +-// https://microsoft.github.io/language-server-protocol/specification#diagnostic +-type Diagnostic struct { +- // TODO(adonovan): should be a protocol.URI, for symmetry. +- URI span.URI // of diagnosed file (not diagnostic documentation) +- Range protocol.Range +- Severity protocol.DiagnosticSeverity +- Code string +- CodeHref string +- +- // Source is a human-readable description of the source of the error. +- // Diagnostics generated by an analysis.Analyzer set it to Analyzer.Name. +- Source DiagnosticSource +- +- Message string +- +- Tags []protocol.DiagnosticTag +- Related []protocol.DiagnosticRelatedInformation +- +- // Fields below are used internally to generate quick fixes. They aren't +- // part of the LSP spec and historically didn't leave the server. +- // +- // Update(2023-05): version 3.16 of the LSP spec included support for the +- // Diagnostic.data field, which holds arbitrary data preserved in the +- // diagnostic for codeAction requests. This field allows bundling additional +- // information for quick-fixes, and gopls can (and should) use this +- // information to avoid re-evaluating diagnostics in code-action handlers. +- // +- // In order to stage this transition incrementally, the 'BundledFixes' field +- // may store a 'bundled' (=json-serialized) form of the associated +- // SuggestedFixes. Not all diagnostics have their fixes bundled. +- BundledFixes *json.RawMessage +- SuggestedFixes []SuggestedFix +-} +- +-func (d *Diagnostic) String() string { +- return fmt.Sprintf("%v: %s", d.Range, d.Message) +-} +- +-type DiagnosticSource string +- +-const ( +- UnknownError DiagnosticSource = "" +- ListError DiagnosticSource = "go list" +- ParseError DiagnosticSource = "syntax" +- TypeError DiagnosticSource = "compiler" +- ModTidyError DiagnosticSource = "go mod tidy" +- OptimizationDetailsError DiagnosticSource = "optimizer details" +- UpgradeNotification DiagnosticSource = "upgrade available" +- Vulncheck DiagnosticSource = "vulncheck imports" +- Govulncheck DiagnosticSource = "govulncheck" +- TemplateError DiagnosticSource = "template" +- WorkFileError DiagnosticSource = "go.work file" +- ConsistencyInfo DiagnosticSource = "consistency" +-) +- +-func AnalyzerErrorKind(name string) DiagnosticSource { +- return DiagnosticSource(name) +-} +diff -urN a/gopls/internal/lsp/source/workspace_symbol.go b/gopls/internal/lsp/source/workspace_symbol.go +--- a/gopls/internal/lsp/source/workspace_symbol.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/workspace_symbol.go 1970-01-01 08:00:00 +@@ -1,611 +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 source +- +-import ( +- "context" +- "fmt" +- "path" +- "path/filepath" +- "regexp" +- "runtime" +- "sort" +- "strings" +- "unicode" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/fuzzy" +-) +- +-// Symbol holds a precomputed symbol value. Note: we avoid using the +-// protocol.SymbolInformation struct here in order to reduce the size of each +-// symbol. +-type Symbol struct { +- Name string +- Kind protocol.SymbolKind +- Range protocol.Range +-} +- +-// maxSymbols defines the maximum number of symbol results that should ever be +-// sent in response to a client. +-const maxSymbols = 100 +- +-// WorkspaceSymbols matches symbols across all views using the given query, +-// according to the match semantics parameterized by matcherType and style. +-// +-// The workspace symbol method is defined in the spec as follows: +-// +-// The workspace symbol request is sent from the client to the server to +-// list project-wide symbols matching the query string. +-// +-// It is unclear what "project-wide" means here, but given the parameters of +-// workspace/symbol do not include any workspace identifier, then it has to be +-// assumed that "project-wide" means "across all workspaces". Hence why +-// WorkspaceSymbols receives the views []View. +-// +-// However, it then becomes unclear what it would mean to call WorkspaceSymbols +-// with a different configured SymbolMatcher per View. Therefore we assume that +-// Session level configuration will define the SymbolMatcher to be used for the +-// WorkspaceSymbols method. +-func WorkspaceSymbols(ctx context.Context, matcher SymbolMatcher, style SymbolStyle, views []View, query string) ([]protocol.SymbolInformation, error) { +- ctx, done := event.Start(ctx, "source.WorkspaceSymbols") +- defer done() +- if query == "" { +- return nil, nil +- } +- +- var s symbolizer +- switch style { +- case DynamicSymbols: +- s = dynamicSymbolMatch +- case FullyQualifiedSymbols: +- s = fullyQualifiedSymbolMatch +- case PackageQualifiedSymbols: +- s = packageSymbolMatch +- default: +- panic(fmt.Errorf("unknown symbol style: %v", style)) +- } +- +- return collectSymbols(ctx, views, matcher, s, query) +-} +- +-// A matcherFunc returns the index and score of a symbol match. +-// +-// See the comment for symbolCollector for more information. +-type matcherFunc func(chunks []string) (int, float64) +- +-// A symbolizer returns the best symbol match for a name with pkg, according to +-// some heuristic. The symbol name is passed as the slice nameParts of logical +-// name pieces. For example, for myType.field the caller can pass either +-// []string{"myType.field"} or []string{"myType.", "field"}. +-// +-// See the comment for symbolCollector for more information. +-// +-// The space argument is an empty slice with spare capacity that may be used +-// to allocate the result. +-type symbolizer func(space []string, name string, pkg *Metadata, m matcherFunc) ([]string, float64) +- +-func fullyQualifiedSymbolMatch(space []string, name string, pkg *Metadata, matcher matcherFunc) ([]string, float64) { +- if _, score := dynamicSymbolMatch(space, name, pkg, matcher); score > 0 { +- return append(space, string(pkg.PkgPath), ".", name), score +- } +- return nil, 0 +-} +- +-func dynamicSymbolMatch(space []string, name string, pkg *Metadata, matcher matcherFunc) ([]string, float64) { +- if IsCommandLineArguments(pkg.ID) { +- // command-line-arguments packages have a non-sensical package path, so +- // just use their package name. +- return packageSymbolMatch(space, name, pkg, matcher) +- } +- +- var score float64 +- +- endsInPkgName := strings.HasSuffix(string(pkg.PkgPath), string(pkg.Name)) +- +- // If the package path does not end in the package name, we need to check the +- // package-qualified symbol as an extra pass first. +- if !endsInPkgName { +- pkgQualified := append(space, string(pkg.Name), ".", name) +- idx, score := matcher(pkgQualified) +- nameStart := len(pkg.Name) + 1 +- if score > 0 { +- // If our match is contained entirely within the unqualified portion, +- // just return that. +- if idx >= nameStart { +- return append(space, name), score +- } +- // Lower the score for matches that include the package name. +- return pkgQualified, score * 0.8 +- } +- } +- +- // Now try matching the fully qualified symbol. +- fullyQualified := append(space, string(pkg.PkgPath), ".", name) +- idx, score := matcher(fullyQualified) +- +- // As above, check if we matched just the unqualified symbol name. +- nameStart := len(pkg.PkgPath) + 1 +- if idx >= nameStart { +- return append(space, name), score +- } +- +- // If our package path ends in the package name, we'll have skipped the +- // initial pass above, so check if we matched just the package-qualified +- // name. +- if endsInPkgName && idx >= 0 { +- pkgStart := len(pkg.PkgPath) - len(pkg.Name) +- if idx >= pkgStart { +- return append(space, string(pkg.Name), ".", name), score +- } +- } +- +- // Our match was not contained within the unqualified or package qualified +- // symbol. Return the fully qualified symbol but discount the score. +- return fullyQualified, score * 0.6 +-} +- +-func packageSymbolMatch(space []string, name string, pkg *Metadata, matcher matcherFunc) ([]string, float64) { +- qualified := append(space, string(pkg.Name), ".", name) +- if _, s := matcher(qualified); s > 0 { +- return qualified, s +- } +- return nil, 0 +-} +- +-func buildMatcher(matcher SymbolMatcher, query string) matcherFunc { +- switch matcher { +- case SymbolFuzzy: +- return parseQuery(query, newFuzzyMatcher) +- case SymbolFastFuzzy: +- return parseQuery(query, func(query string) matcherFunc { +- return fuzzy.NewSymbolMatcher(query).Match +- }) +- case SymbolCaseSensitive: +- return matchExact(query) +- case SymbolCaseInsensitive: +- q := strings.ToLower(query) +- exact := matchExact(q) +- wrapper := []string{""} +- return func(chunks []string) (int, float64) { +- s := strings.Join(chunks, "") +- wrapper[0] = strings.ToLower(s) +- return exact(wrapper) +- } +- } +- panic(fmt.Errorf("unknown symbol matcher: %v", matcher)) +-} +- +-func newFuzzyMatcher(query string) matcherFunc { +- fm := fuzzy.NewMatcher(query) +- return func(chunks []string) (int, float64) { +- score := float64(fm.ScoreChunks(chunks)) +- ranges := fm.MatchedRanges() +- if len(ranges) > 0 { +- return ranges[0], score +- } +- return -1, score +- } +-} +- +-// parseQuery parses a field-separated symbol query, extracting the special +-// characters listed below, and returns a matcherFunc corresponding to the AND +-// of all field queries. +-// +-// Special characters: +-// +-// ^ match exact prefix +-// $ match exact suffix +-// ' match exact +-// +-// In all three of these special queries, matches are 'smart-cased', meaning +-// they are case sensitive if the symbol query contains any upper-case +-// characters, and case insensitive otherwise. +-func parseQuery(q string, newMatcher func(string) matcherFunc) matcherFunc { +- fields := strings.Fields(q) +- if len(fields) == 0 { +- return func([]string) (int, float64) { return -1, 0 } +- } +- var funcs []matcherFunc +- for _, field := range fields { +- var f matcherFunc +- switch { +- case strings.HasPrefix(field, "^"): +- prefix := field[1:] +- f = smartCase(prefix, func(chunks []string) (int, float64) { +- s := strings.Join(chunks, "") +- if strings.HasPrefix(s, prefix) { +- return 0, 1 +- } +- return -1, 0 +- }) +- case strings.HasPrefix(field, "'"): +- exact := field[1:] +- f = smartCase(exact, matchExact(exact)) +- case strings.HasSuffix(field, "$"): +- suffix := field[0 : len(field)-1] +- f = smartCase(suffix, func(chunks []string) (int, float64) { +- s := strings.Join(chunks, "") +- if strings.HasSuffix(s, suffix) { +- return len(s) - len(suffix), 1 +- } +- return -1, 0 +- }) +- default: +- f = newMatcher(field) +- } +- funcs = append(funcs, f) +- } +- if len(funcs) == 1 { +- return funcs[0] +- } +- return comboMatcher(funcs).match +-} +- +-func matchExact(exact string) matcherFunc { +- return func(chunks []string) (int, float64) { +- s := strings.Join(chunks, "") +- if idx := strings.LastIndex(s, exact); idx >= 0 { +- return idx, 1 +- } +- return -1, 0 +- } +-} +- +-// smartCase returns a matcherFunc that is case-sensitive if q contains any +-// upper-case characters, and case-insensitive otherwise. +-func smartCase(q string, m matcherFunc) matcherFunc { +- insensitive := strings.ToLower(q) == q +- wrapper := []string{""} +- return func(chunks []string) (int, float64) { +- s := strings.Join(chunks, "") +- if insensitive { +- s = strings.ToLower(s) +- } +- wrapper[0] = s +- return m(wrapper) +- } +-} +- +-type comboMatcher []matcherFunc +- +-func (c comboMatcher) match(chunks []string) (int, float64) { +- score := 1.0 +- first := 0 +- for _, f := range c { +- idx, s := f(chunks) +- if idx < first { +- first = idx +- } +- score *= s +- } +- return first, score +-} +- +-// collectSymbols calls snapshot.Symbols to walk the syntax trees of +-// all files in the views' current snapshots, and returns a sorted, +-// scored list of symbols that best match the parameters. +-// +-// How it matches symbols is parameterized by two interfaces: +-// - A matcherFunc determines how well a string symbol matches a query. It +-// returns a non-negative score indicating the quality of the match. A score +-// of zero indicates no match. +-// - A symbolizer determines how we extract the symbol for an object. This +-// enables the 'symbolStyle' configuration option. +-func collectSymbols(ctx context.Context, views []View, matcherType SymbolMatcher, symbolizer symbolizer, query string) ([]protocol.SymbolInformation, error) { +- // Extract symbols from all files. +- var work []symbolFile +- var roots []string +- seen := make(map[span.URI]bool) +- // TODO(adonovan): opt: parallelize this loop? How often is len > 1? +- for _, v := range views { +- snapshot, release, err := v.Snapshot() +- if err != nil { +- continue // view is shut down; continue with others +- } +- defer release() +- +- // Use the root view URIs for determining (lexically) +- // whether a URI is in any open workspace. +- roots = append(roots, strings.TrimRight(string(v.Folder()), "/")) +- +- filters := snapshot.Options().DirectoryFilters +- filterer := NewFilterer(filters) +- folder := filepath.ToSlash(v.Folder().Filename()) +- +- workspaceOnly := true +- if snapshot.Options().SymbolScope == AllSymbolScope { +- workspaceOnly = false +- } +- symbols, err := snapshot.Symbols(ctx, workspaceOnly) +- if err != nil { +- return nil, err +- } +- +- for uri, syms := range symbols { +- norm := filepath.ToSlash(uri.Filename()) +- nm := strings.TrimPrefix(norm, folder) +- if filterer.Disallow(nm) { +- continue +- } +- // Only scan each file once. +- if seen[uri] { +- continue +- } +- meta, err := NarrowestMetadataForFile(ctx, snapshot, uri) +- if err != nil { +- event.Error(ctx, fmt.Sprintf("missing metadata for %q", uri), err) +- continue +- } +- seen[uri] = true +- work = append(work, symbolFile{uri, meta, syms}) +- } +- } +- +- // Match symbols in parallel. +- // Each worker has its own symbolStore, +- // which we merge at the end. +- nmatchers := runtime.GOMAXPROCS(-1) // matching is CPU bound +- results := make(chan *symbolStore) +- for i := 0; i < nmatchers; i++ { +- go func(i int) { +- matcher := buildMatcher(matcherType, query) +- store := new(symbolStore) +- // Assign files to workers in round-robin fashion. +- for j := i; j < len(work); j += nmatchers { +- matchFile(store, symbolizer, matcher, roots, work[j]) +- } +- results <- store +- }(i) +- } +- +- // Gather and merge results as they arrive. +- var unified symbolStore +- for i := 0; i < nmatchers; i++ { +- store := <-results +- for _, syms := range store.res { +- unified.store(syms) +- } +- } +- return unified.results(), nil +-} +- +-type Filterer struct { +- // Whether a filter is excluded depends on the operator (first char of the raw filter). +- // Slices filters and excluded then should have the same length. +- filters []*regexp.Regexp +- excluded []bool +-} +- +-// NewFilterer computes regular expression form of all raw filters +-func NewFilterer(rawFilters []string) *Filterer { +- var f Filterer +- for _, filter := range rawFilters { +- filter = path.Clean(filepath.ToSlash(filter)) +- // TODO(dungtuanle): fix: validate [+-] prefix. +- op, prefix := filter[0], filter[1:] +- // convertFilterToRegexp adds "/" at the end of prefix to handle cases where a filter is a prefix of another filter. +- // For example, it prevents [+foobar, -foo] from excluding "foobar". +- f.filters = append(f.filters, convertFilterToRegexp(filepath.ToSlash(prefix))) +- f.excluded = append(f.excluded, op == '-') +- } +- +- return &f +-} +- +-// Disallow return true if the path is excluded from the filterer's filters. +-func (f *Filterer) Disallow(path string) bool { +- // Ensure trailing but not leading slash. +- path = strings.TrimPrefix(path, "/") +- if !strings.HasSuffix(path, "/") { +- path += "/" +- } +- +- // TODO(adonovan): opt: iterate in reverse and break at first match. +- excluded := false +- for i, filter := range f.filters { +- if filter.MatchString(path) { +- excluded = f.excluded[i] // last match wins +- } +- } +- return excluded +-} +- +-// convertFilterToRegexp replaces glob-like operator substrings in a string file path to their equivalent regex forms. +-// Supporting glob-like operators: +-// - **: match zero or more complete path segments +-func convertFilterToRegexp(filter string) *regexp.Regexp { +- if filter == "" { +- return regexp.MustCompile(".*") +- } +- var ret strings.Builder +- ret.WriteString("^") +- segs := strings.Split(filter, "/") +- for _, seg := range segs { +- // Inv: seg != "" since path is clean. +- if seg == "**" { +- ret.WriteString(".*") +- } else { +- ret.WriteString(regexp.QuoteMeta(seg)) +- } +- ret.WriteString("/") +- } +- pattern := ret.String() +- +- // Remove unnecessary "^.*" prefix, which increased +- // BenchmarkWorkspaceSymbols time by ~20% (even though +- // filter CPU time increased by only by ~2.5%) when the +- // default filter was changed to "**/node_modules". +- pattern = strings.TrimPrefix(pattern, "^.*") +- +- return regexp.MustCompile(pattern) +-} +- +-// symbolFile holds symbol information for a single file. +-type symbolFile struct { +- uri span.URI +- md *Metadata +- syms []Symbol +-} +- +-// matchFile scans a symbol file and adds matching symbols to the store. +-func matchFile(store *symbolStore, symbolizer symbolizer, matcher matcherFunc, roots []string, i symbolFile) { +- space := make([]string, 0, 3) +- for _, sym := range i.syms { +- symbolParts, score := symbolizer(space, sym.Name, i.md, matcher) +- +- // Check if the score is too low before applying any downranking. +- if store.tooLow(score) { +- continue +- } +- +- // Factors to apply to the match score for the purpose of downranking +- // results. +- // +- // These numbers were crudely calibrated based on trial-and-error using a +- // small number of sample queries. Adjust as necessary. +- // +- // All factors are multiplicative, meaning if more than one applies they are +- // multiplied together. +- const ( +- // nonWorkspaceFactor is applied to symbols outside the workspace. +- // Developers are less likely to want to jump to code that they +- // are not actively working on. +- nonWorkspaceFactor = 0.5 +- // nonWorkspaceUnexportedFactor is applied to unexported symbols outside +- // the workspace. Since one wouldn't usually jump to unexported +- // symbols to understand a package API, they are particularly irrelevant. +- nonWorkspaceUnexportedFactor = 0.5 +- // every field or method nesting level to access the field decreases +- // the score by a factor of 1.0 - depth*depthFactor, up to a depth of +- // 3. +- // +- // Use a small constant here, as this exists mostly to break ties +- // (e.g. given a type Foo and a field x.Foo, prefer Foo). +- depthFactor = 0.01 +- ) +- +- startWord := true +- exported := true +- depth := 0.0 +- for _, r := range sym.Name { +- if startWord && !unicode.IsUpper(r) { +- exported = false +- } +- if r == '.' { +- startWord = true +- depth++ +- } else { +- startWord = false +- } +- } +- +- // TODO(rfindley): use metadata to determine if the file is in a workspace +- // package, rather than this heuristic. +- inWorkspace := false +- for _, root := range roots { +- if strings.HasPrefix(string(i.uri), root) { +- inWorkspace = true +- break +- } +- } +- +- // Apply downranking based on workspace position. +- if !inWorkspace { +- score *= nonWorkspaceFactor +- if !exported { +- score *= nonWorkspaceUnexportedFactor +- } +- } +- +- // Apply downranking based on symbol depth. +- if depth > 3 { +- depth = 3 +- } +- score *= 1.0 - depth*depthFactor +- +- if store.tooLow(score) { +- continue +- } +- +- si := symbolInformation{ +- score: score, +- symbol: strings.Join(symbolParts, ""), +- kind: sym.Kind, +- uri: i.uri, +- rng: sym.Range, +- container: string(i.md.PkgPath), +- } +- store.store(si) +- } +-} +- +-type symbolStore struct { +- res [maxSymbols]symbolInformation +-} +- +-// store inserts si into the sorted results, if si has a high enough score. +-func (sc *symbolStore) store(si symbolInformation) { +- if sc.tooLow(si.score) { +- return +- } +- insertAt := sort.Search(len(sc.res), func(i int) bool { +- // Sort by score, then symbol length, and finally lexically. +- if sc.res[i].score != si.score { +- return sc.res[i].score < si.score +- } +- if len(sc.res[i].symbol) != len(si.symbol) { +- return len(sc.res[i].symbol) > len(si.symbol) +- } +- return sc.res[i].symbol > si.symbol +- }) +- if insertAt < len(sc.res)-1 { +- copy(sc.res[insertAt+1:], sc.res[insertAt:len(sc.res)-1]) +- } +- sc.res[insertAt] = si +-} +- +-func (sc *symbolStore) tooLow(score float64) bool { +- return score <= sc.res[len(sc.res)-1].score +-} +- +-func (sc *symbolStore) results() []protocol.SymbolInformation { +- var res []protocol.SymbolInformation +- for _, si := range sc.res { +- if si.score <= 0 { +- return res +- } +- res = append(res, si.asProtocolSymbolInformation()) +- } +- return res +-} +- +-// symbolInformation is a cut-down version of protocol.SymbolInformation that +-// allows struct values of this type to be used as map keys. +-type symbolInformation struct { +- score float64 +- symbol string +- container string +- kind protocol.SymbolKind +- uri span.URI +- rng protocol.Range +-} +- +-// asProtocolSymbolInformation converts s to a protocol.SymbolInformation value. +-// +-// TODO: work out how to handle tags if/when they are needed. +-func (s symbolInformation) asProtocolSymbolInformation() protocol.SymbolInformation { +- return protocol.SymbolInformation{ +- Name: s.symbol, +- Kind: s.kind, +- Location: protocol.Location{ +- URI: protocol.URIFromSpanURI(s.uri), +- Range: s.rng, +- }, +- ContainerName: s.container, +- } +-} +diff -urN a/gopls/internal/lsp/source/workspace_symbol_test.go b/gopls/internal/lsp/source/workspace_symbol_test.go +--- a/gopls/internal/lsp/source/workspace_symbol_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/workspace_symbol_test.go 1970-01-01 08:00:00 +@@ -1,136 +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 source +- +-import ( +- "testing" +-) +- +-func TestParseQuery(t *testing.T) { +- tests := []struct { +- query, s string +- wantMatch bool +- }{ +- {"", "anything", false}, +- {"any", "anything", true}, +- {"any$", "anything", false}, +- {"ing$", "anything", true}, +- {"ing$", "anythinG", true}, +- {"inG$", "anything", false}, +- {"^any", "anything", true}, +- {"^any", "Anything", true}, +- {"^Any", "anything", false}, +- {"at", "anything", true}, +- // TODO: this appears to be a bug in the fuzzy matching algorithm. 'At' +- // should cause a case-sensitive match. +- // {"At", "anything", false}, +- {"At", "Anything", true}, +- {"'yth", "Anything", true}, +- {"'yti", "Anything", false}, +- {"'any 'thing", "Anything", true}, +- {"anythn nythg", "Anything", true}, +- {"ntx", "Anything", false}, +- {"anythn", "anything", true}, +- {"ing", "anything", true}, +- {"anythn nythgx", "anything", false}, +- } +- +- for _, test := range tests { +- matcher := parseQuery(test.query, newFuzzyMatcher) +- if _, score := matcher([]string{test.s}); score > 0 != test.wantMatch { +- t.Errorf("parseQuery(%q) match for %q: %.2g, want match: %t", test.query, test.s, score, test.wantMatch) +- } +- } +-} +- +-func TestFiltererDisallow(t *testing.T) { +- tests := []struct { +- filters []string +- included []string +- excluded []string +- }{ +- { +- []string{"+**/c.go"}, +- []string{"a/c.go", "a/b/c.go"}, +- []string{}, +- }, +- { +- []string{"+a/**/c.go"}, +- []string{"a/b/c.go", "a/b/d/c.go", "a/c.go"}, +- []string{}, +- }, +- { +- []string{"-a/c.go", "+a/**"}, +- []string{"a/c.go"}, +- []string{}, +- }, +- { +- []string{"+a/**/c.go", "-**/c.go"}, +- []string{}, +- []string{"a/b/c.go"}, +- }, +- { +- []string{"+a/**/c.go", "-a/**"}, +- []string{}, +- []string{"a/b/c.go"}, +- }, +- { +- []string{"+**/c.go", "-a/**/c.go"}, +- []string{}, +- []string{"a/b/c.go"}, +- }, +- { +- []string{"+foobar", "-foo"}, +- []string{"foobar", "foobar/a"}, +- []string{"foo", "foo/a"}, +- }, +- { +- []string{"+", "-"}, +- []string{}, +- []string{"foobar", "foobar/a", "foo", "foo/a"}, +- }, +- { +- []string{"-", "+"}, +- []string{"foobar", "foobar/a", "foo", "foo/a"}, +- []string{}, +- }, +- { +- []string{"-a/**/b/**/c.go"}, +- []string{}, +- []string{"a/x/y/z/b/f/g/h/c.go"}, +- }, +- // tests for unsupported glob operators +- { +- []string{"+**/c.go", "-a/*/c.go"}, +- []string{"a/b/c.go"}, +- []string{}, +- }, +- { +- []string{"+**/c.go", "-a/?/c.go"}, +- []string{"a/b/c.go"}, +- []string{}, +- }, +- { +- []string{"-b"}, // should only filter paths prefixed with the "b" directory +- []string{"a/b/c.go", "bb"}, +- []string{"b/c/d.go", "b"}, +- }, +- } +- +- for _, test := range tests { +- filterer := NewFilterer(test.filters) +- for _, inc := range test.included { +- if filterer.Disallow(inc) { +- t.Errorf("Filters %v excluded %v, wanted included", test.filters, inc) +- } +- } +- +- for _, exc := range test.excluded { +- if !filterer.Disallow(exc) { +- t.Errorf("Filters %v included %v, wanted excluded", test.filters, exc) +- } +- } +- } +-} +diff -urN a/gopls/internal/lsp/source/xrefs/xrefs.go b/gopls/internal/lsp/source/xrefs/xrefs.go +--- a/gopls/internal/lsp/source/xrefs/xrefs.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/source/xrefs/xrefs.go 1970-01-01 08:00:00 +@@ -1,193 +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 xrefs defines the serializable index of cross-package +-// references that is computed during type checking. +-// +-// See ../references.go for the 'references' query. +-package xrefs +- +-import ( +- "go/ast" +- "go/types" +- "sort" +- +- "golang.org/x/tools/go/types/objectpath" +- "golang.org/x/tools/gopls/internal/lsp/frob" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/internal/typeparams" +-) +- +-// Index constructs a serializable index of outbound cross-references +-// for the specified type-checked package. +-func Index(files []*source.ParsedGoFile, pkg *types.Package, info *types.Info) []byte { +- // pkgObjects maps each referenced package Q to a mapping: +- // from each referenced symbol in Q to the ordered list +- // of references to that symbol from this package. +- // A nil types.Object indicates a reference +- // to the package as a whole: an import. +- pkgObjects := make(map[*types.Package]map[types.Object]*gobObject) +- +- // getObjects returns the object-to-references mapping for a package. +- getObjects := func(pkg *types.Package) map[types.Object]*gobObject { +- objects, ok := pkgObjects[pkg] +- if !ok { +- objects = make(map[types.Object]*gobObject) +- pkgObjects[pkg] = objects +- } +- return objects +- } +- +- objectpathFor := new(objectpath.Encoder).For +- +- for fileIndex, pgf := range files { +- +- nodeRange := func(n ast.Node) protocol.Range { +- rng, err := pgf.PosRange(n.Pos(), n.End()) +- if err != nil { +- panic(err) // can't fail +- } +- return rng +- } +- +- ast.Inspect(pgf.File, func(n ast.Node) bool { +- switch n := n.(type) { +- case *ast.Ident: +- // Report a reference for each identifier that +- // uses a symbol exported from another package. +- // (The built-in error.Error method has no package.) +- if n.IsExported() { +- if obj, ok := info.Uses[n]; ok && +- obj.Pkg() != nil && +- obj.Pkg() != pkg { +- +- // For instantiations of generic methods, +- // use the generic object (see issue #60622). +- if fn, ok := obj.(*types.Func); ok { +- obj = typeparams.OriginMethod(fn) +- } +- +- objects := getObjects(obj.Pkg()) +- gobObj, ok := objects[obj] +- if !ok { +- path, err := objectpathFor(obj) +- if err != nil { +- // Capitalized but not exported +- // (e.g. local const/var/type). +- return true +- } +- gobObj = &gobObject{Path: path} +- objects[obj] = gobObj +- } +- +- gobObj.Refs = append(gobObj.Refs, gobRef{ +- FileIndex: fileIndex, +- Range: nodeRange(n), +- }) +- } +- } +- +- case *ast.ImportSpec: +- // Report a reference from each import path +- // string to the imported package. +- pkgname, ok := source.ImportedPkgName(info, n) +- if !ok { +- return true // missing import +- } +- objects := getObjects(pkgname.Imported()) +- gobObj, ok := objects[nil] +- if !ok { +- gobObj = &gobObject{Path: ""} +- objects[nil] = gobObj +- } +- gobObj.Refs = append(gobObj.Refs, gobRef{ +- FileIndex: fileIndex, +- Range: nodeRange(n.Path), +- }) +- } +- return true +- }) +- } +- +- // Flatten the maps into slices, and sort for determinism. +- var packages []*gobPackage +- for p := range pkgObjects { +- objects := pkgObjects[p] +- gp := &gobPackage{ +- PkgPath: source.PackagePath(p.Path()), +- Objects: make([]*gobObject, 0, len(objects)), +- } +- for _, gobObj := range objects { +- gp.Objects = append(gp.Objects, gobObj) +- } +- sort.Slice(gp.Objects, func(i, j int) bool { +- return gp.Objects[i].Path < gp.Objects[j].Path +- }) +- packages = append(packages, gp) +- } +- sort.Slice(packages, func(i, j int) bool { +- return packages[i].PkgPath < packages[j].PkgPath +- }) +- +- return packageCodec.Encode(packages) +-} +- +-// Lookup searches a serialized index produced by an indexPackage +-// operation on m, and returns the locations of all references from m +-// to any object in the target set. Each object is denoted by a pair +-// of (package path, object path). +-func Lookup(m *source.Metadata, data []byte, targets map[source.PackagePath]map[objectpath.Path]struct{}) (locs []protocol.Location) { +- var packages []*gobPackage +- packageCodec.Decode(data, &packages) +- for _, gp := range packages { +- if objectSet, ok := targets[gp.PkgPath]; ok { +- for _, gobObj := range gp.Objects { +- if _, ok := objectSet[gobObj.Path]; ok { +- for _, ref := range gobObj.Refs { +- uri := m.CompiledGoFiles[ref.FileIndex] +- locs = append(locs, protocol.Location{ +- URI: protocol.URIFromSpanURI(uri), +- Range: ref.Range, +- }) +- } +- } +- } +- } +- } +- +- return locs +-} +- +-// -- serialized representation -- +- +-// The cross-reference index records the location of all references +-// from one package to symbols defined in other packages +-// (dependencies). It does not record within-package references. +-// The index for package P consists of a list of gopPackage records, +-// each enumerating references to symbols defined a single dependency, Q. +- +-// TODO(adonovan): opt: choose a more compact encoding. +-// The gobRef.Range field is the obvious place to begin. +- +-// (The name says gob but in fact we use frob.) +-var packageCodec = frob.CodecFor[[]*gobPackage]() +- +-// A gobPackage records the set of outgoing references from the index +-// package to symbols defined in a dependency package. +-type gobPackage struct { +- PkgPath source.PackagePath // defining package (Q) +- Objects []*gobObject // set of Q objects referenced by P +-} +- +-// A gobObject records all references to a particular symbol. +-type gobObject struct { +- Path objectpath.Path // symbol name within package; "" => import of package itself +- Refs []gobRef // locations of references within P, in lexical order +-} +- +-type gobRef struct { +- FileIndex int // index of enclosing file within P's CompiledGoFiles +- Range protocol.Range // source range of reference +-} +diff -urN a/gopls/internal/lsp/symbols.go b/gopls/internal/lsp/symbols.go +--- a/gopls/internal/lsp/symbols.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/symbols.go 1970-01-01 08:00:00 +@@ -1,60 +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 lsp +- +-import ( +- "context" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/lsp/template" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" +-) +- +-func (s *Server) documentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]interface{}, error) { +- ctx, done := event.Start(ctx, "lsp.Server.documentSymbol", tag.URI.Of(params.TextDocument.URI)) +- defer done() +- +- snapshot, fh, ok, release, err := s.beginFileRequest(ctx, params.TextDocument.URI, source.UnknownKind) +- defer release() +- if !ok { +- return []interface{}{}, err +- } +- var docSymbols []protocol.DocumentSymbol +- switch snapshot.FileKind(fh) { +- case source.Tmpl: +- docSymbols, err = template.DocumentSymbols(snapshot, fh) +- case source.Go: +- docSymbols, err = source.DocumentSymbols(ctx, snapshot, fh) +- default: +- return []interface{}{}, nil +- } +- if err != nil { +- event.Error(ctx, "DocumentSymbols failed", err) +- return []interface{}{}, nil +- } +- // Convert the symbols to an interface array. +- // TODO: Remove this once the lsp deprecates SymbolInformation. +- symbols := make([]interface{}, len(docSymbols)) +- for i, s := range docSymbols { +- if snapshot.Options().HierarchicalDocumentSymbolSupport { +- symbols[i] = s +- continue +- } +- // If the client does not support hierarchical document symbols, then +- // we need to be backwards compatible for now and return SymbolInformation. +- symbols[i] = protocol.SymbolInformation{ +- Name: s.Name, +- Kind: s.Kind, +- Deprecated: s.Deprecated, +- Location: protocol.Location{ +- URI: params.TextDocument.URI, +- Range: s.Range, +- }, +- } +- } +- return symbols, nil +-} +diff -urN a/gopls/internal/lsp/template/completion.go b/gopls/internal/lsp/template/completion.go +--- a/gopls/internal/lsp/template/completion.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/template/completion.go 1970-01-01 08:00:00 +@@ -1,287 +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 template +- +-import ( +- "bytes" +- "context" +- "fmt" +- "go/scanner" +- "go/token" +- "strings" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +-) +- +-// information needed for completion +-type completer struct { +- p *Parsed +- pos protocol.Position +- offset int // offset of the start of the Token +- ctx protocol.CompletionContext +- syms map[string]symbol +-} +- +-func Completion(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, pos protocol.Position, context protocol.CompletionContext) (*protocol.CompletionList, error) { +- all := New(snapshot.Templates()) +- var start int // the beginning of the Token (completed or not) +- syms := make(map[string]symbol) +- var p *Parsed +- for fn, fc := range all.files { +- // collect symbols from all template files +- filterSyms(syms, fc.symbols) +- if fn.Filename() != fh.URI().Filename() { +- continue +- } +- if start = inTemplate(fc, pos); start == -1 { +- return nil, nil +- } +- p = fc +- } +- if p == nil { +- // this cannot happen unless the search missed a template file +- return nil, fmt.Errorf("%s not found", fh.FileIdentity().URI.Filename()) +- } +- c := completer{ +- p: p, +- pos: pos, +- offset: start + len(Left), +- ctx: context, +- syms: syms, +- } +- return c.complete() +-} +- +-func filterSyms(syms map[string]symbol, ns []symbol) { +- for _, xsym := range ns { +- switch xsym.kind { +- case protocol.Method, protocol.Package, protocol.Boolean, protocol.Namespace, +- protocol.Function: +- syms[xsym.name] = xsym // we don't care which symbol we get +- case protocol.Variable: +- if xsym.name != "dot" { +- syms[xsym.name] = xsym +- } +- case protocol.Constant: +- if xsym.name == "nil" { +- syms[xsym.name] = xsym +- } +- } +- } +-} +- +-// return the starting position of the enclosing token, or -1 if none +-func inTemplate(fc *Parsed, pos protocol.Position) int { +- // pos is the pos-th character. if the cursor is at the beginning +- // of the file, pos is 0. That is, we've only seen characters before pos +- // 1. pos might be in a Token, return tk.Start +- // 2. pos might be after an elided but before a Token, return elided +- // 3. return -1 for false +- offset := fc.FromPosition(pos) +- // this could be a binary search, as the tokens are ordered +- for _, tk := range fc.tokens { +- if tk.Start < offset && offset <= tk.End { +- return tk.Start +- } +- } +- for _, x := range fc.elided { +- if x > offset { +- // fc.elided is sorted +- break +- } +- // If the interval [x,offset] does not contain Left or Right +- // then provide completions. (do we need the test for Right?) +- if !bytes.Contains(fc.buf[x:offset], []byte(Left)) && !bytes.Contains(fc.buf[x:offset], []byte(Right)) { +- return x +- } +- } +- return -1 +-} +- +-var ( +- keywords = []string{"if", "with", "else", "block", "range", "template", "end}}", "end"} +- globals = []string{"and", "call", "html", "index", "slice", "js", "len", "not", "or", +- "urlquery", "printf", "println", "print", "eq", "ne", "le", "lt", "ge", "gt"} +-) +- +-// find the completions. start is the offset of either the Token enclosing pos, or where +-// the incomplete token starts. +-// The error return is always nil. +-func (c *completer) complete() (*protocol.CompletionList, error) { +- ans := &protocol.CompletionList{IsIncomplete: true, Items: []protocol.CompletionItem{}} +- start := c.p.FromPosition(c.pos) +- sofar := c.p.buf[c.offset:start] +- if len(sofar) == 0 || sofar[len(sofar)-1] == ' ' || sofar[len(sofar)-1] == '\t' { +- return ans, nil +- } +- // sofar could be parsed by either c.analyzer() or scan(). The latter is precise +- // and slower, but fast enough +- words := scan(sofar) +- // 1. if pattern starts $, show variables +- // 2. if pattern starts ., show methods (and . by itself?) +- // 3. if len(words) == 1, show firstWords (but if it were a |, show functions and globals) +- // 4. ...? (parenthetical expressions, arguments, ...) (packages, namespaces, nil?) +- if len(words) == 0 { +- return nil, nil // if this happens, why were we called? +- } +- pattern := string(words[len(words)-1]) +- if pattern[0] == '$' { +- // should we also return a raw "$"? +- for _, s := range c.syms { +- if s.kind == protocol.Variable && weakMatch(s.name, pattern) > 0 { +- ans.Items = append(ans.Items, protocol.CompletionItem{ +- Label: s.name, +- Kind: protocol.VariableCompletion, +- Detail: "Variable", +- }) +- } +- } +- return ans, nil +- } +- if pattern[0] == '.' { +- for _, s := range c.syms { +- if s.kind == protocol.Method && weakMatch("."+s.name, pattern) > 0 { +- ans.Items = append(ans.Items, protocol.CompletionItem{ +- Label: s.name, +- Kind: protocol.MethodCompletion, +- Detail: "Method/member", +- }) +- } +- } +- return ans, nil +- } +- // could we get completion attempts in strings or numbers, and if so, do we care? +- // globals +- for _, kw := range globals { +- if weakMatch(kw, string(pattern)) != 0 { +- ans.Items = append(ans.Items, protocol.CompletionItem{ +- Label: kw, +- Kind: protocol.KeywordCompletion, +- Detail: "Function", +- }) +- } +- } +- // and functions +- for _, s := range c.syms { +- if s.kind == protocol.Function && weakMatch(s.name, pattern) != 0 { +- ans.Items = append(ans.Items, protocol.CompletionItem{ +- Label: s.name, +- Kind: protocol.FunctionCompletion, +- Detail: "Function", +- }) +- } +- } +- // keywords if we're at the beginning +- if len(words) <= 1 || len(words[len(words)-2]) == 1 && words[len(words)-2][0] == '|' { +- for _, kw := range keywords { +- if weakMatch(kw, string(pattern)) != 0 { +- ans.Items = append(ans.Items, protocol.CompletionItem{ +- Label: kw, +- Kind: protocol.KeywordCompletion, +- Detail: "keyword", +- }) +- } +- } +- } +- return ans, nil +-} +- +-// someday think about comments, strings, backslashes, etc +-// this would repeat some of the template parsing, but because the user is typing +-// there may be no parse tree here. +-// (go/scanner will report 2 tokens for $a, as $ is not a legal go identifier character) +-// (go/scanner is about 2.7 times more expensive) +-func (c *completer) analyze(buf []byte) [][]byte { +- // we want to split on whitespace and before dots +- var working []byte +- var ans [][]byte +- for _, ch := range buf { +- if ch == '.' && len(working) > 0 { +- ans = append(ans, working) +- working = []byte{'.'} +- continue +- } +- if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' { +- if len(working) > 0 { +- ans = append(ans, working) +- working = []byte{} +- continue +- } +- } +- working = append(working, ch) +- } +- if len(working) > 0 { +- ans = append(ans, working) +- } +- ch := buf[len(buf)-1] +- if ch == ' ' || ch == '\t' { +- // avoid completing on whitespace +- ans = append(ans, []byte{ch}) +- } +- return ans +-} +- +-// version of c.analyze that uses go/scanner. +-func scan(buf []byte) []string { +- fset := token.NewFileSet() +- fp := fset.AddFile("", -1, len(buf)) +- var sc scanner.Scanner +- sc.Init(fp, buf, func(pos token.Position, msg string) {}, scanner.ScanComments) +- ans := make([]string, 0, 10) // preallocating gives a measurable savings +- for { +- _, tok, lit := sc.Scan() // tok is an int +- if tok == token.EOF { +- break // done +- } else if tok == token.SEMICOLON && lit == "\n" { +- continue // don't care, but probably can't happen +- } else if tok == token.PERIOD { +- ans = append(ans, ".") // lit is empty +- } else if tok == token.IDENT && len(ans) > 0 && ans[len(ans)-1] == "." { +- ans[len(ans)-1] = "." + lit +- } else if tok == token.IDENT && len(ans) > 0 && ans[len(ans)-1] == "$" { +- ans[len(ans)-1] = "$" + lit +- } else if lit != "" { +- ans = append(ans, lit) +- } +- } +- return ans +-} +- +-// pattern is what the user has typed +-func weakMatch(choice, pattern string) float64 { +- lower := strings.ToLower(choice) +- // for now, use only lower-case everywhere +- pattern = strings.ToLower(pattern) +- // The first char has to match +- if pattern[0] != lower[0] { +- return 0 +- } +- // If they start with ., then the second char has to match +- from := 1 +- if pattern[0] == '.' { +- if len(pattern) < 2 { +- return 1 // pattern just a ., so it matches +- } +- if pattern[1] != lower[1] { +- return 0 +- } +- from = 2 +- } +- // check that all the characters of pattern occur as a subsequence of choice +- i, j := from, from +- for ; i < len(lower) && j < len(pattern); j++ { +- if pattern[j] == lower[i] { +- i++ +- if i >= len(lower) { +- return 0 +- } +- } +- } +- if j < len(pattern) { +- return 0 +- } +- return 1 +-} +diff -urN a/gopls/internal/lsp/template/completion_test.go b/gopls/internal/lsp/template/completion_test.go +--- a/gopls/internal/lsp/template/completion_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/template/completion_test.go 1970-01-01 08:00:00 +@@ -1,102 +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 template +- +-import ( +- "log" +- "sort" +- "strings" +- "testing" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +-) +- +-func init() { +- log.SetFlags(log.Lshortfile) +-} +- +-type tparse struct { +- marked string // ^ shows where to ask for completions. (The user just typed the following character.) +- wanted []string // expected completions +-} +- +-// Test completions in templates that parse enough (if completion needs symbols) +-// Seen characters up to the ^ +-func TestParsed(t *testing.T) { +- var tests = []tparse{ +- {"{{x}}{{12. xx^", nil}, // https://github.com/golang/go/issues/50430 +- {`
    Allocated bytes{{fuint64 .HeapAlloc}}
    `, nil}, +- {"{{i^f}}", []string{"index", "if"}}, +- {"{{if .}}{{e^ {{end}}", []string{"eq", "end}}", "else", "end"}}, +- {"{{foo}}{{f^", []string{"foo"}}, +- {"{{$^}}", []string{"$"}}, +- {"{{$x:=4}}{{$^", []string{"$x"}}, +- {"{{$x:=4}}{{$ ^ ", []string{}}, +- {"{{len .Modified}}{{.^Mo", []string{"Modified"}}, +- {"{{len .Modified}}{{.mf^", []string{"Modified"}}, +- {"{{$^ }}", []string{"$"}}, +- {"{{$a =3}}{{$^", []string{"$a"}}, +- // .two is not good here: fix someday +- {`{{.Modified}}{{.^{{if $.one.two}}xxx{{end}}`, []string{"Modified", "one", "two"}}, +- {`{{.Modified}}{{.o^{{if $.one.two}}xxx{{end}}`, []string{"one"}}, +- {"{{.Modiifed}}{{.one.t^{{if $.one.two}}xxx{{end}}", []string{"two"}}, +- {`{{block "foo" .}}{{i^`, []string{"index", "if"}}, +- {"{{in^{{Internal}}", []string{"index", "Internal", "if"}}, +- // simple number has no completions +- {"{{4^e", []string{}}, +- // simple string has no completions +- {"{{`e^", []string{}}, +- {"{{`No i^", []string{}}, // example of why go/scanner is used +- {"{{xavier}}{{12. x^", []string{"xavier"}}, +- } +- for _, tx := range tests { +- c := testCompleter(t, tx) +- var v []string +- if c != nil { +- ans, _ := c.complete() +- for _, a := range ans.Items { +- v = append(v, a.Label) +- } +- } +- if len(v) != len(tx.wanted) { +- t.Errorf("%q: got %q, wanted %q %d,%d", tx.marked, v, tx.wanted, len(v), len(tx.wanted)) +- continue +- } +- sort.Strings(tx.wanted) +- sort.Strings(v) +- for i := 0; i < len(v); i++ { +- if tx.wanted[i] != v[i] { +- t.Errorf("%q at %d: got %v, wanted %v", tx.marked, i, v, tx.wanted) +- break +- } +- } +- } +-} +- +-func testCompleter(t *testing.T, tx tparse) *completer { +- t.Helper() +- // seen chars up to ^ +- col := strings.Index(tx.marked, "^") +- buf := strings.Replace(tx.marked, "^", "", 1) +- p := parseBuffer([]byte(buf)) +- pos := protocol.Position{Line: 0, Character: uint32(col)} +- if p.ParseErr != nil { +- log.Printf("%q: %v", tx.marked, p.ParseErr) +- } +- offset := inTemplate(p, pos) +- if offset == -1 { +- return nil +- } +- syms := make(map[string]symbol) +- filterSyms(syms, p.symbols) +- c := &completer{ +- p: p, +- pos: protocol.Position{Line: 0, Character: uint32(col)}, +- offset: offset + len(Left), +- ctx: protocol.CompletionContext{TriggerKind: protocol.Invoked}, +- syms: syms, +- } +- return c +-} +diff -urN a/gopls/internal/lsp/template/highlight.go b/gopls/internal/lsp/template/highlight.go +--- a/gopls/internal/lsp/template/highlight.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/template/highlight.go 1970-01-01 08:00:00 +@@ -1,96 +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 template +- +-import ( +- "context" +- "fmt" +- "regexp" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +-) +- +-func Highlight(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, loc protocol.Position) ([]protocol.DocumentHighlight, error) { +- buf, err := fh.Content() +- if err != nil { +- return nil, err +- } +- p := parseBuffer(buf) +- pos := p.FromPosition(loc) +- var ans []protocol.DocumentHighlight +- if p.ParseErr == nil { +- for _, s := range p.symbols { +- if s.start <= pos && pos < s.start+s.length { +- return markSymbols(p, s) +- } +- } +- } +- // these tokens exist whether or not there was a parse error +- // (symbols require a successful parse) +- for _, tok := range p.tokens { +- if tok.Start <= pos && pos < tok.End { +- wordAt := findWordAt(p, pos) +- if len(wordAt) > 0 { +- return markWordInToken(p, wordAt) +- } +- } +- } +- // find the 'word' at pos, etc: someday +- // until then we get the default action, which doesn't respect word boundaries +- return ans, nil +-} +- +-func markSymbols(p *Parsed, sym symbol) ([]protocol.DocumentHighlight, error) { +- var ans []protocol.DocumentHighlight +- for _, s := range p.symbols { +- if s.name == sym.name { +- kind := protocol.Read +- if s.vardef { +- kind = protocol.Write +- } +- ans = append(ans, protocol.DocumentHighlight{ +- Range: p.Range(s.start, s.length), +- Kind: kind, +- }) +- } +- } +- return ans, nil +-} +- +-// A token is {{...}}, and this marks words in the token that equal the give word +-func markWordInToken(p *Parsed, wordAt string) ([]protocol.DocumentHighlight, error) { +- var ans []protocol.DocumentHighlight +- pat, err := regexp.Compile(fmt.Sprintf(`\b%s\b`, wordAt)) +- if err != nil { +- return nil, fmt.Errorf("%q: unmatchable word (%v)", wordAt, err) +- } +- for _, tok := range p.tokens { +- got := pat.FindAllIndex(p.buf[tok.Start:tok.End], -1) +- for i := 0; i < len(got); i++ { +- ans = append(ans, protocol.DocumentHighlight{ +- Range: p.Range(got[i][0], got[i][1]-got[i][0]), +- Kind: protocol.Text, +- }) +- } +- } +- return ans, nil +-} +- +-var wordRe = regexp.MustCompile(`[$]?\w+$`) +-var moreRe = regexp.MustCompile(`^[$]?\w+`) +- +-// findWordAt finds the word the cursor is in (meaning in or just before) +-func findWordAt(p *Parsed, pos int) string { +- if pos >= len(p.buf) { +- return "" // can't happen, as we are called with pos < tok.End +- } +- after := moreRe.Find(p.buf[pos:]) +- if len(after) == 0 { +- return "" // end of the word +- } +- got := wordRe.Find(p.buf[:pos+len(after)]) +- return string(got) +-} +diff -urN a/gopls/internal/lsp/template/implementations.go b/gopls/internal/lsp/template/implementations.go +--- a/gopls/internal/lsp/template/implementations.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/template/implementations.go 1970-01-01 08:00:00 +@@ -1,189 +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 template +- +-import ( +- "context" +- "fmt" +- "regexp" +- "strconv" +- "time" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" +-) +- +-// line number (1-based) and message +-var errRe = regexp.MustCompile(`template.*:(\d+): (.*)`) +- +-// Diagnose returns parse errors. There is only one. +-// The errors are not always helpful. For instance { {end}} +-// will likely point to the end of the file. +-func Diagnose(f source.FileHandle) []*source.Diagnostic { +- // no need for skipTemplate check, as Diagnose is called on the +- // snapshot's template files +- buf, err := f.Content() +- if err != nil { +- // Is a Diagnostic with no Range useful? event.Error also? +- msg := fmt.Sprintf("failed to read %s (%v)", f.URI().Filename(), err) +- d := source.Diagnostic{Message: msg, Severity: protocol.SeverityError, URI: f.URI(), +- Source: source.TemplateError} +- return []*source.Diagnostic{&d} +- } +- p := parseBuffer(buf) +- if p.ParseErr == nil { +- return nil +- } +- unknownError := func(msg string) []*source.Diagnostic { +- s := fmt.Sprintf("malformed template error %q: %s", p.ParseErr.Error(), msg) +- d := source.Diagnostic{ +- Message: s, Severity: protocol.SeverityError, Range: p.Range(p.nls[0], 1), +- URI: f.URI(), Source: source.TemplateError} +- return []*source.Diagnostic{&d} +- } +- // errors look like `template: :40: unexpected "}" in operand` +- // so the string needs to be parsed +- matches := errRe.FindStringSubmatch(p.ParseErr.Error()) +- if len(matches) != 3 { +- msg := fmt.Sprintf("expected 3 matches, got %d (%v)", len(matches), matches) +- return unknownError(msg) +- } +- lineno, err := strconv.Atoi(matches[1]) +- if err != nil { +- msg := fmt.Sprintf("couldn't convert %q to int, %v", matches[1], err) +- return unknownError(msg) +- } +- msg := matches[2] +- d := source.Diagnostic{Message: msg, Severity: protocol.SeverityError, +- Source: source.TemplateError} +- start := p.nls[lineno-1] +- if lineno < len(p.nls) { +- size := p.nls[lineno] - start +- d.Range = p.Range(start, size) +- } else { +- d.Range = p.Range(start, 1) +- } +- return []*source.Diagnostic{&d} +-} +- +-// Definition finds the definitions of the symbol at loc. It +-// does not understand scoping (if any) in templates. This code is +-// for definitions, type definitions, and implementations. +-// Results only for variables and templates. +-func Definition(snapshot source.Snapshot, fh source.FileHandle, loc protocol.Position) ([]protocol.Location, error) { +- x, _, err := symAtPosition(fh, loc) +- if err != nil { +- return nil, err +- } +- sym := x.name +- ans := []protocol.Location{} +- // PJW: this is probably a pattern to abstract +- a := New(snapshot.Templates()) +- for k, p := range a.files { +- for _, s := range p.symbols { +- if !s.vardef || s.name != sym { +- continue +- } +- ans = append(ans, protocol.Location{URI: protocol.DocumentURI(k), Range: p.Range(s.start, s.length)}) +- } +- } +- return ans, nil +-} +- +-func Hover(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, position protocol.Position) (*protocol.Hover, error) { +- sym, p, err := symAtPosition(fh, position) +- if sym == nil || err != nil { +- return nil, err +- } +- ans := protocol.Hover{Range: p.Range(sym.start, sym.length), Contents: protocol.MarkupContent{Kind: protocol.Markdown}} +- switch sym.kind { +- case protocol.Function: +- ans.Contents.Value = fmt.Sprintf("function: %s", sym.name) +- case protocol.Variable: +- ans.Contents.Value = fmt.Sprintf("variable: %s", sym.name) +- case protocol.Constant: +- ans.Contents.Value = fmt.Sprintf("constant %s", sym.name) +- case protocol.Method: // field or method +- ans.Contents.Value = fmt.Sprintf("%s: field or method", sym.name) +- case protocol.Package: // template use, template def (PJW: do we want two?) +- ans.Contents.Value = fmt.Sprintf("template %s\n(add definition)", sym.name) +- case protocol.Namespace: +- ans.Contents.Value = fmt.Sprintf("template %s defined", sym.name) +- case protocol.Number: +- ans.Contents.Value = "number" +- case protocol.String: +- ans.Contents.Value = "string" +- case protocol.Boolean: +- ans.Contents.Value = "boolean" +- default: +- ans.Contents.Value = fmt.Sprintf("oops, sym=%#v", sym) +- } +- return &ans, nil +-} +- +-func References(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, params *protocol.ReferenceParams) ([]protocol.Location, error) { +- sym, _, err := symAtPosition(fh, params.Position) +- if sym == nil || err != nil || sym.name == "" { +- return nil, err +- } +- ans := []protocol.Location{} +- +- a := New(snapshot.Templates()) +- for k, p := range a.files { +- for _, s := range p.symbols { +- if s.name != sym.name { +- continue +- } +- if s.vardef && !params.Context.IncludeDeclaration { +- continue +- } +- ans = append(ans, protocol.Location{URI: protocol.DocumentURI(k), Range: p.Range(s.start, s.length)}) +- } +- } +- // do these need to be sorted? (a.files is a map) +- return ans, nil +-} +- +-func SemanticTokens(ctx context.Context, snapshot source.Snapshot, spn span.URI, add func(line, start, len uint32), d func() []uint32) (*protocol.SemanticTokens, error) { +- fh, err := snapshot.ReadFile(ctx, spn) +- if err != nil { +- return nil, err +- } +- buf, err := fh.Content() +- if err != nil { +- return nil, err +- } +- p := parseBuffer(buf) +- +- for _, t := range p.Tokens() { +- if t.Multiline { +- la, ca := p.LineCol(t.Start) +- lb, cb := p.LineCol(t.End) +- add(la, ca, p.RuneCount(la, ca, 0)) +- for l := la + 1; l < lb; l++ { +- add(l, 0, p.RuneCount(l, 0, 0)) +- } +- add(lb, 0, p.RuneCount(lb, 0, cb)) +- continue +- } +- sz, err := p.TokenSize(t) +- if err != nil { +- return nil, err +- } +- line, col := p.LineCol(t.Start) +- add(line, col, uint32(sz)) +- } +- data := d() +- ans := &protocol.SemanticTokens{ +- Data: data, +- // for small cache, some day. for now, the LSP client ignores this +- // (that is, when the LSP client starts returning these, we can cache) +- ResultID: fmt.Sprintf("%v", time.Now()), +- } +- return ans, nil +-} +- +-// still need to do rename, etc +diff -urN a/gopls/internal/lsp/template/parse.go b/gopls/internal/lsp/template/parse.go +--- a/gopls/internal/lsp/template/parse.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/template/parse.go 1970-01-01 08:00:00 +@@ -1,508 +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 template contains code for dealing with templates +-package template +- +-// template files are small enough that the code reprocesses them each time +-// this may be a bad choice for projects with lots of template files. +- +-// This file contains the parsing code, some debugging printing, and +-// implementations for Diagnose, Definition, Hover, References +- +-import ( +- "bytes" +- "context" +- "fmt" +- "io" +- "log" +- "regexp" +- "runtime" +- "sort" +- "text/template" +- "text/template/parse" +- "unicode/utf8" - - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/event" +-) +- +-var ( +- Left = []byte("{{") +- Right = []byte("}}") -) - --// information needed for completion --type completer struct { -- p *Parsed -- pos protocol.Position -- offset int // offset of the start of the Token -- ctx protocol.CompletionContext -- syms map[string]symbol +-type Parsed struct { +- buf []byte //contents +- lines [][]byte // needed?, other than for debugging? +- elided []int // offsets where Left was replaced by blanks +- +- // tokens are matched Left-Right pairs, computed before trying to parse +- tokens []Token +- +- // result of parsing +- named []*template.Template // the template and embedded templates +- ParseErr error +- symbols []symbol +- stack []parse.Node // used while computing symbols +- +- // for mapping from offsets in buf to LSP coordinates +- // See FromPosition() and LineCol() +- nls []int // offset of newlines before each line (nls[0]==-1) +- lastnl int // last line seen +- check int // used to decide whether to use lastnl or search through nls +- nonASCII bool // are there any non-ascii runes in buf? +-} +- +-// Token is a single {{...}}. More precisely, Left...Right +-type Token struct { +- Start, End int // offset from start of template +- Multiline bool +-} +- +-// All contains the Parse of all the template files +-type All struct { +- files map[span.URI]*Parsed +-} +- +-// New returns the Parses of the snapshot's tmpl files +-// (maybe cache these, but then avoiding import cycles needs code rearrangements) +-func New(tmpls map[span.URI]source.FileHandle) *All { +- all := make(map[span.URI]*Parsed) +- for k, v := range tmpls { +- buf, err := v.Content() +- if err != nil { // PJW: decide what to do with these errors +- log.Printf("failed to read %s (%v)", v.URI().Filename(), err) +- continue +- } +- all[k] = parseBuffer(buf) +- } +- return &All{files: all} +-} +- +-func parseBuffer(buf []byte) *Parsed { +- ans := &Parsed{ +- buf: buf, +- check: -1, +- nls: []int{-1}, +- } +- if len(buf) == 0 { +- return ans +- } +- // how to compute allAscii... +- for _, b := range buf { +- if b >= utf8.RuneSelf { +- ans.nonASCII = true +- break +- } +- } +- if buf[len(buf)-1] != '\n' { +- ans.buf = append(buf, '\n') +- } +- for i, p := range ans.buf { +- if p == '\n' { +- ans.nls = append(ans.nls, i) +- } +- } +- ans.setTokens() // ans.buf may be a new []byte +- ans.lines = bytes.Split(ans.buf, []byte{'\n'}) +- t, err := template.New("").Parse(string(ans.buf)) +- if err != nil { +- funcs := make(template.FuncMap) +- for t == nil && ans.ParseErr == nil { +- // in 1.17 it may be possible to avoid getting this error +- // template: :2: function "foo" not defined +- matches := parseErrR.FindStringSubmatch(err.Error()) +- if len(matches) == 2 { +- // suppress the error by giving it a function with the right name +- funcs[matches[1]] = func() interface{} { return nil } +- t, err = template.New("").Funcs(funcs).Parse(string(ans.buf)) +- continue +- } +- ans.ParseErr = err // unfixed error +- return ans +- } +- } +- ans.named = t.Templates() +- // set the symbols +- for _, t := range ans.named { +- ans.stack = append(ans.stack, t.Root) +- ans.findSymbols() +- if t.Name() != "" { +- // defining a template. The pos is just after {{define...}} (or {{block...}}?) +- at, sz := ans.FindLiteralBefore(int(t.Root.Pos)) +- s := symbol{start: at, length: sz, name: t.Name(), kind: protocol.Namespace, vardef: true} +- ans.symbols = append(ans.symbols, s) +- } +- } +- +- sort.Slice(ans.symbols, func(i, j int) bool { +- left, right := ans.symbols[i], ans.symbols[j] +- if left.start != right.start { +- return left.start < right.start +- } +- if left.vardef != right.vardef { +- return left.vardef +- } +- return left.kind < right.kind +- }) +- return ans -} - --func Completion(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, pos protocol.Position, context protocol.CompletionContext) (*protocol.CompletionList, error) { -- all := New(snapshot.Templates()) -- var start int // the beginning of the Token (completed or not) -- syms := make(map[string]symbol) -- var p *Parsed -- for fn, fc := range all.files { -- // collect symbols from all template files -- filterSyms(syms, fc.symbols) -- if fn.Filename() != fh.URI().Filename() { +-// FindLiteralBefore locates the first preceding string literal +-// returning its position and length in buf +-// or returns -1 if there is none. +-// Assume double-quoted string rather than backquoted string for now. +-func (p *Parsed) FindLiteralBefore(pos int) (int, int) { +- left, right := -1, -1 +- for i := pos - 1; i >= 0; i-- { +- if p.buf[i] != '"' { - continue - } -- if start = inTemplate(fc, pos); start == -1 { -- return nil, nil +- if right == -1 { +- right = i +- continue - } -- p = fc -- } -- if p == nil { -- // this cannot happen unless the search missed a template file -- return nil, fmt.Errorf("%s not found", fh.FileIdentity().URI.Filename()) +- left = i +- break - } -- c := completer{ -- p: p, -- pos: pos, -- offset: start + len(Left), -- ctx: context, -- syms: syms, +- if left == -1 { +- return -1, 0 - } -- return c.complete() +- return left + 1, right - left - 1 -} - --func filterSyms(syms map[string]symbol, ns []symbol) { -- for _, xsym := range ns { -- switch xsym.kind { -- case protocol.Method, protocol.Package, protocol.Boolean, protocol.Namespace, -- protocol.Function: -- syms[xsym.name] = xsym // we don't care which symbol we get -- case protocol.Variable: -- if xsym.name != "dot" { -- syms[xsym.name] = xsym +-var ( +- parseErrR = regexp.MustCompile(`template:.*function "([^"]+)" not defined`) +-) +- +-func (p *Parsed) setTokens() { +- const ( +- // InRaw and InString only occur inside an action (SeenLeft) +- Start = iota +- InRaw +- InString +- SeenLeft +- ) +- state := Start +- var left, oldState int +- for n := 0; n < len(p.buf); n++ { +- c := p.buf[n] +- switch state { +- case InRaw: +- if c == '`' { +- state = oldState - } -- case protocol.Constant: -- if xsym.name == "nil" { -- syms[xsym.name] = xsym +- case InString: +- if c == '"' && !isEscaped(p.buf[:n]) { +- state = oldState +- } +- case SeenLeft: +- if c == '`' { +- oldState = state // it's SeenLeft, but a little clearer this way +- state = InRaw +- continue +- } +- if c == '"' { +- oldState = state +- state = InString +- continue +- } +- if bytes.HasPrefix(p.buf[n:], Right) { +- right := n + len(Right) +- tok := Token{Start: left, +- End: right, +- Multiline: bytes.Contains(p.buf[left:right], []byte{'\n'}), +- } +- p.tokens = append(p.tokens, tok) +- state = Start +- } +- // If we see (unquoted) Left then the original left is probably the user +- // typing. Suppress the original left +- if bytes.HasPrefix(p.buf[n:], Left) { +- p.elideAt(left) +- left = n +- n += len(Left) - 1 // skip the rest +- } +- case Start: +- if bytes.HasPrefix(p.buf[n:], Left) { +- left = n +- state = SeenLeft +- n += len(Left) - 1 // skip the rest (avoids {{{ bug) - } - } - } +- // this error occurs after typing {{ at the end of the file +- if state != Start { +- // Unclosed Left. remove the Left at left +- p.elideAt(left) +- } -} - --// return the starting position of the enclosing token, or -1 if none --func inTemplate(fc *Parsed, pos protocol.Position) int { -- // pos is the pos-th character. if the cursor is at the beginning -- // of the file, pos is 0. That is, we've only seen characters before pos -- // 1. pos might be in a Token, return tk.Start -- // 2. pos might be after an elided but before a Token, return elided -- // 3. return -1 for false -- offset := fc.FromPosition(pos) -- // this could be a binary search, as the tokens are ordered -- for _, tk := range fc.tokens { -- if tk.Start < offset && offset <= tk.End { -- return tk.Start -- } +-func (p *Parsed) elideAt(left int) { +- if p.elided == nil { +- // p.buf is the same buffer that v.Read() returns, so copy it. +- // (otherwise the next time it's parsed, elided information is lost) +- b := make([]byte, len(p.buf)) +- copy(b, p.buf) +- p.buf = b - } -- for _, x := range fc.elided { -- if x > offset { -- // fc.elided is sorted -- break -- } -- // If the interval [x,offset] does not contain Left or Right -- // then provide completions. (do we need the test for Right?) -- if !bytes.Contains(fc.buf[x:offset], []byte(Left)) && !bytes.Contains(fc.buf[x:offset], []byte(Right)) { -- return x +- for i := 0; i < len(Left); i++ { +- p.buf[left+i] = ' ' +- } +- p.elided = append(p.elided, left) +-} +- +-// isEscaped reports whether the byte after buf is escaped +-func isEscaped(buf []byte) bool { +- backSlashes := 0 +- for j := len(buf) - 1; j >= 0 && buf[j] == '\\'; j-- { +- backSlashes++ +- } +- return backSlashes%2 == 1 +-} +- +-func (p *Parsed) Tokens() []Token { +- return p.tokens +-} +- +-// TODO(adonovan): the next 100 lines could perhaps replaced by use of protocol.Mapper. +- +-func (p *Parsed) utf16len(buf []byte) int { +- cnt := 0 +- if !p.nonASCII { +- return len(buf) +- } +- // we need a utf16len(rune), but we don't have it +- for _, r := range string(buf) { +- cnt++ +- if r >= 1<<16 { +- cnt++ - } - } -- return -1 +- return cnt -} - --var ( -- keywords = []string{"if", "with", "else", "block", "range", "template", "end}}", "end"} -- globals = []string{"and", "call", "html", "index", "slice", "js", "len", "not", "or", -- "urlquery", "printf", "println", "print", "eq", "ne", "le", "lt", "ge", "gt"} --) +-func (p *Parsed) TokenSize(t Token) (int, error) { +- if t.Multiline { +- return -1, fmt.Errorf("TokenSize called with Multiline token %#v", t) +- } +- ans := p.utf16len(p.buf[t.Start:t.End]) +- return ans, nil +-} - --// find the completions. start is the offset of either the Token enclosing pos, or where --// the incomplete token starts. --// The error return is always nil. --func (c *completer) complete() (*protocol.CompletionList, error) { -- ans := &protocol.CompletionList{IsIncomplete: true, Items: []protocol.CompletionItem{}} -- start := c.p.FromPosition(c.pos) -- sofar := c.p.buf[c.offset:start] -- if len(sofar) == 0 || sofar[len(sofar)-1] == ' ' || sofar[len(sofar)-1] == '\t' { -- return ans, nil +-// RuneCount counts runes in line l, from col s to e +-// (e==0 for end of line. called only for multiline tokens) +-func (p *Parsed) RuneCount(l, s, e uint32) uint32 { +- start := p.nls[l] + 1 + int(s) +- end := p.nls[l] + 1 + int(e) +- if e == 0 || end > p.nls[l+1] { +- end = p.nls[l+1] - } -- // sofar could be parsed by either c.analyzer() or scan(). The latter is precise -- // and slower, but fast enough -- words := scan(sofar) -- // 1. if pattern starts $, show variables -- // 2. if pattern starts ., show methods (and . by itself?) -- // 3. if len(words) == 1, show firstWords (but if it were a |, show functions and globals) -- // 4. ...? (parenthetical expressions, arguments, ...) (packages, namespaces, nil?) -- if len(words) == 0 { -- return nil, nil // if this happens, why were we called? +- return uint32(utf8.RuneCount(p.buf[start:end])) +-} +- +-// LineCol converts from a 0-based byte offset to 0-based line, col. col in runes +-func (p *Parsed) LineCol(x int) (uint32, uint32) { +- if x < p.check { +- p.lastnl = 0 - } -- pattern := string(words[len(words)-1]) -- if pattern[0] == '$' { -- // should we also return a raw "$"? -- for _, s := range c.syms { -- if s.kind == protocol.Variable && weakMatch(s.name, pattern) > 0 { -- ans.Items = append(ans.Items, protocol.CompletionItem{ -- Label: s.name, -- Kind: protocol.VariableCompletion, -- Detail: "Variable", -- }) -- } +- p.check = x +- for i := p.lastnl; i < len(p.nls); i++ { +- if p.nls[i] <= x { +- continue - } -- return ans, nil -- } -- if pattern[0] == '.' { -- for _, s := range c.syms { -- if s.kind == protocol.Method && weakMatch("."+s.name, pattern) > 0 { -- ans.Items = append(ans.Items, protocol.CompletionItem{ -- Label: s.name, -- Kind: protocol.MethodCompletion, -- Detail: "Method/member", -- }) -- } +- p.lastnl = i +- var count int +- if i > 0 && x == p.nls[i-1] { // \n +- count = 0 +- } else { +- count = p.utf16len(p.buf[p.nls[i-1]+1 : x]) - } -- return ans, nil +- return uint32(i - 1), uint32(count) - } -- // could we get completion attempts in strings or numbers, and if so, do we care? -- // globals -- for _, kw := range globals { -- if weakMatch(kw, string(pattern)) != 0 { -- ans.Items = append(ans.Items, protocol.CompletionItem{ -- Label: kw, -- Kind: protocol.KeywordCompletion, -- Detail: "Function", -- }) -- } +- if x == len(p.buf)-1 { // trailing \n +- return uint32(len(p.nls) - 1), 0 - } -- // and functions -- for _, s := range c.syms { -- if s.kind == protocol.Function && weakMatch(s.name, pattern) != 0 { -- ans.Items = append(ans.Items, protocol.CompletionItem{ -- Label: s.name, -- Kind: protocol.FunctionCompletion, -- Detail: "Function", -- }) +- // shouldn't happen +- for i := 1; i < 4; i++ { +- _, f, l, ok := runtime.Caller(i) +- if !ok { +- break - } +- log.Printf("%d: %s:%d", i, f, l) - } -- // keywords if we're at the beginning -- if len(words) <= 1 || len(words[len(words)-2]) == 1 && words[len(words)-2][0] == '|' { -- for _, kw := range keywords { -- if weakMatch(kw, string(pattern)) != 0 { -- ans.Items = append(ans.Items, protocol.CompletionItem{ -- Label: kw, -- Kind: protocol.KeywordCompletion, -- Detail: "keyword", -- }) -- } -- } +- +- msg := fmt.Errorf("LineCol off the end, %d of %d, nls=%v, %q", x, len(p.buf), p.nls, p.buf[x:]) +- event.Error(context.Background(), "internal error", msg) +- return 0, 0 +-} +- +-// Position produces a protocol.Position from an offset in the template +-func (p *Parsed) Position(pos int) protocol.Position { +- line, col := p.LineCol(pos) +- return protocol.Position{Line: line, Character: col} +-} +- +-func (p *Parsed) Range(x, length int) protocol.Range { +- line, col := p.LineCol(x) +- ans := protocol.Range{ +- Start: protocol.Position{Line: line, Character: col}, +- End: protocol.Position{Line: line, Character: col + uint32(length)}, - } -- return ans, nil +- return ans -} - --// someday think about comments, strings, backslashes, etc --// this would repeat some of the template parsing, but because the user is typing --// there may be no parse tree here. --// (go/scanner will report 2 tokens for $a, as $ is not a legal go identifier character) --// (go/scanner is about 2.7 times more expensive) --func (c *completer) analyze(buf []byte) [][]byte { -- // we want to split on whitespace and before dots -- var working []byte -- var ans [][]byte -- for _, ch := range buf { -- if ch == '.' && len(working) > 0 { -- ans = append(ans, working) -- working = []byte{'.'} -- continue -- } -- if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' { -- if len(working) > 0 { -- ans = append(ans, working) -- working = []byte{} -- continue -- } +-// FromPosition translates a protocol.Position into an offset into the template +-func (p *Parsed) FromPosition(x protocol.Position) int { +- l, c := int(x.Line), int(x.Character) +- if l >= len(p.nls) || p.nls[l]+1 >= len(p.buf) { +- // paranoia to avoid panic. return the largest offset +- return len(p.buf) +- } +- line := p.buf[p.nls[l]+1:] +- cnt := 0 +- for w := range string(line) { +- if cnt >= c { +- return w + p.nls[l] + 1 - } -- working = append(working, ch) +- cnt++ - } -- if len(working) > 0 { -- ans = append(ans, working) +- // do we get here? NO +- pos := int(x.Character) + p.nls[int(x.Line)] + 1 +- event.Error(context.Background(), "internal error", fmt.Errorf("surprise %#v", x)) +- return pos +-} +- +-func symAtPosition(fh source.FileHandle, loc protocol.Position) (*symbol, *Parsed, error) { +- buf, err := fh.Content() +- if err != nil { +- return nil, nil, err - } -- ch := buf[len(buf)-1] -- if ch == ' ' || ch == '\t' { -- // avoid completing on whitespace -- ans = append(ans, []byte{ch}) +- p := parseBuffer(buf) +- pos := p.FromPosition(loc) +- syms := p.SymsAtPos(pos) +- if len(syms) == 0 { +- return nil, p, fmt.Errorf("no symbol found") - } -- return ans +- if len(syms) > 1 { +- log.Printf("Hover: %d syms, not 1 %v", len(syms), syms) +- } +- sym := syms[0] +- return &sym, p, nil -} - --// version of c.analyze that uses go/scanner. --func scan(buf []byte) []string { -- fset := token.NewFileSet() -- fp := fset.AddFile("", -1, len(buf)) -- var sc scanner.Scanner -- sc.Init(fp, buf, func(pos token.Position, msg string) {}, scanner.ScanComments) -- ans := make([]string, 0, 10) // preallocating gives a measurable savings -- for { -- _, tok, lit := sc.Scan() // tok is an int -- if tok == token.EOF { -- break // done -- } else if tok == token.SEMICOLON && lit == "\n" { -- continue // don't care, but probably can't happen -- } else if tok == token.PERIOD { -- ans = append(ans, ".") // lit is empty -- } else if tok == token.IDENT && len(ans) > 0 && ans[len(ans)-1] == "." { -- ans[len(ans)-1] = "." + lit -- } else if tok == token.IDENT && len(ans) > 0 && ans[len(ans)-1] == "$" { -- ans[len(ans)-1] = "$" + lit -- } else if lit != "" { -- ans = append(ans, lit) +-func (p *Parsed) SymsAtPos(pos int) []symbol { +- ans := []symbol{} +- for _, s := range p.symbols { +- if s.start <= pos && pos < s.start+s.length { +- ans = append(ans, s) - } - } - return ans -} - --// pattern is what the user has typed --func weakMatch(choice, pattern string) float64 { -- lower := strings.ToLower(choice) -- // for now, use only lower-case everywhere -- pattern = strings.ToLower(pattern) -- // The first char has to match -- if pattern[0] != lower[0] { -- return 0 +-type wrNode struct { +- p *Parsed +- w io.Writer +-} +- +-// WriteNode is for debugging +-func (p *Parsed) WriteNode(w io.Writer, n parse.Node) { +- wr := wrNode{p: p, w: w} +- wr.writeNode(n, "") +-} +- +-func (wr wrNode) writeNode(n parse.Node, indent string) { +- if n == nil { +- return - } -- // If they start with ., then the second char has to match -- from := 1 -- if pattern[0] == '.' { -- if len(pattern) < 2 { -- return 1 // pattern just a ., so it matches +- at := func(pos parse.Pos) string { +- line, col := wr.p.LineCol(int(pos)) +- return fmt.Sprintf("(%d)%v:%v", pos, line, col) +- } +- switch x := n.(type) { +- case *parse.ActionNode: +- fmt.Fprintf(wr.w, "%sActionNode at %s\n", indent, at(x.Pos)) +- wr.writeNode(x.Pipe, indent+". ") +- case *parse.BoolNode: +- fmt.Fprintf(wr.w, "%sBoolNode at %s, %v\n", indent, at(x.Pos), x.True) +- case *parse.BranchNode: +- fmt.Fprintf(wr.w, "%sBranchNode at %s\n", indent, at(x.Pos)) +- wr.writeNode(x.Pipe, indent+"Pipe. ") +- wr.writeNode(x.List, indent+"List. ") +- wr.writeNode(x.ElseList, indent+"Else. ") +- case *parse.ChainNode: +- fmt.Fprintf(wr.w, "%sChainNode at %s, %v\n", indent, at(x.Pos), x.Field) +- case *parse.CommandNode: +- fmt.Fprintf(wr.w, "%sCommandNode at %s, %d children\n", indent, at(x.Pos), len(x.Args)) +- for _, a := range x.Args { +- wr.writeNode(a, indent+". ") +- } +- //case *parse.CommentNode: // 1.16 +- case *parse.DotNode: +- fmt.Fprintf(wr.w, "%sDotNode at %s\n", indent, at(x.Pos)) +- case *parse.FieldNode: +- fmt.Fprintf(wr.w, "%sFieldNode at %s, %v\n", indent, at(x.Pos), x.Ident) +- case *parse.IdentifierNode: +- fmt.Fprintf(wr.w, "%sIdentifierNode at %s, %v\n", indent, at(x.Pos), x.Ident) +- case *parse.IfNode: +- fmt.Fprintf(wr.w, "%sIfNode at %s\n", indent, at(x.Pos)) +- wr.writeNode(&x.BranchNode, indent+". ") +- case *parse.ListNode: +- if x == nil { +- return // nil BranchNode.ElseList - } -- if pattern[1] != lower[1] { -- return 0 +- fmt.Fprintf(wr.w, "%sListNode at %s, %d children\n", indent, at(x.Pos), len(x.Nodes)) +- for _, n := range x.Nodes { +- wr.writeNode(n, indent+". ") - } -- from = 2 -- } -- // check that all the characters of pattern occur as a subsequence of choice -- i, j := from, from -- for ; i < len(lower) && j < len(pattern); j++ { -- if pattern[j] == lower[i] { -- i++ -- if i >= len(lower) { -- return 0 -- } +- case *parse.NilNode: +- fmt.Fprintf(wr.w, "%sNilNode at %s\n", indent, at(x.Pos)) +- case *parse.NumberNode: +- fmt.Fprintf(wr.w, "%sNumberNode at %s, %s\n", indent, at(x.Pos), x.Text) +- case *parse.PipeNode: +- if x == nil { +- return // {{template "xxx"}} +- } +- fmt.Fprintf(wr.w, "%sPipeNode at %s, %d vars, %d cmds, IsAssign:%v\n", +- indent, at(x.Pos), len(x.Decl), len(x.Cmds), x.IsAssign) +- for _, d := range x.Decl { +- wr.writeNode(d, indent+"Decl. ") +- } +- for _, c := range x.Cmds { +- wr.writeNode(c, indent+"Cmd. ") - } +- case *parse.RangeNode: +- fmt.Fprintf(wr.w, "%sRangeNode at %s\n", indent, at(x.Pos)) +- wr.writeNode(&x.BranchNode, indent+". ") +- case *parse.StringNode: +- fmt.Fprintf(wr.w, "%sStringNode at %s, %s\n", indent, at(x.Pos), x.Quoted) +- case *parse.TemplateNode: +- fmt.Fprintf(wr.w, "%sTemplateNode at %s, %s\n", indent, at(x.Pos), x.Name) +- wr.writeNode(x.Pipe, indent+". ") +- case *parse.TextNode: +- fmt.Fprintf(wr.w, "%sTextNode at %s, len %d\n", indent, at(x.Pos), len(x.Text)) +- case *parse.VariableNode: +- fmt.Fprintf(wr.w, "%sVariableNode at %s, %v\n", indent, at(x.Pos), x.Ident) +- case *parse.WithNode: +- fmt.Fprintf(wr.w, "%sWithNode at %s\n", indent, at(x.Pos)) +- wr.writeNode(&x.BranchNode, indent+". ") - } -- if j < len(pattern) { -- return 0 +-} +- +-var kindNames = []string{"", "File", "Module", "Namespace", "Package", "Class", "Method", "Property", +- "Field", "Constructor", "Enum", "Interface", "Function", "Variable", "Constant", "String", +- "Number", "Boolean", "Array", "Object", "Key", "Null", "EnumMember", "Struct", "Event", +- "Operator", "TypeParameter"} +- +-func kindStr(k protocol.SymbolKind) string { +- n := int(k) +- if n < 1 || n >= len(kindNames) { +- return fmt.Sprintf("?SymbolKind %d?", n) - } -- return 1 +- return kindNames[n] -} -diff -urN a/gopls/internal/lsp/template/completion_test.go b/gopls/internal/lsp/template/completion_test.go ---- a/gopls/internal/lsp/template/completion_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/template/completion_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,102 +0,0 @@ +diff -urN a/gopls/internal/lsp/template/parse_test.go b/gopls/internal/lsp/template/parse_test.go +--- a/gopls/internal/lsp/template/parse_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/template/parse_test.go 1970-01-01 08:00:00 +@@ -1,238 +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. @@ -89424,205 +101705,241 @@ diff -urN a/gopls/internal/lsp/template/completion_test.go b/gopls/internal/lsp/ -package template - -import ( -- "log" -- "sort" - "strings" - "testing" -- -- "golang.org/x/tools/gopls/internal/lsp/protocol" -) - --func init() { -- log.SetFlags(log.Lshortfile) +-type datum struct { +- buf string +- cnt int +- syms []string // the symbols in the parse of buf -} - --type tparse struct { -- marked string // ^ shows where to ask for completions. (The user just typed the following character.) -- wanted []string // expected completions +-var tmpl = []datum{{` +-{{if (foo .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} +-{{$A.X 12}} +-{{foo (.X.Y) 23 ($A.Zü)}} +-{{end}}`, 1, []string{"{7,3,foo,Function,false}", "{12,1,X,Method,false}", +- "{14,1,Y,Method,false}", "{21,2,$A,Variable,true}", "{26,2,,String,false}", +- "{35,1,Z,Method,false}", "{38,2,$A,Variable,false}", +- "{53,2,$A,Variable,false}", "{56,1,X,Method,false}", "{57,2,,Number,false}", +- "{64,3,foo,Function,false}", "{70,1,X,Method,false}", +- "{72,1,Y,Method,false}", "{75,2,,Number,false}", "{80,2,$A,Variable,false}", +- "{83,2,Zü,Method,false}", "{94,3,,Constant,false}"}}, +- +- {`{{define "zzz"}}{{.}}{{end}} +-{{template "zzz"}}`, 2, []string{"{10,3,zzz,Namespace,true}", "{18,1,dot,Variable,false}", +- "{41,3,zzz,Package,false}"}}, +- +- {`{{block "aaa" foo}}b{{end}}`, 2, []string{"{9,3,aaa,Namespace,true}", +- "{9,3,aaa,Package,false}", "{14,3,foo,Function,false}", "{19,1,,Constant,false}"}}, +- {"", 0, nil}, -} - --// Test completions in templates that parse enough (if completion needs symbols) --// Seen characters up to the ^ --func TestParsed(t *testing.T) { -- var tests = []tparse{ -- {"{{x}}{{12. xx^", nil}, // https://github.com/golang/go/issues/50430 -- {`
    `, nil}, -- {"{{i^f}}", []string{"index", "if"}}, -- {"{{if .}}{{e^ {{end}}", []string{"eq", "end}}", "else", "end"}}, -- {"{{foo}}{{f^", []string{"foo"}}, -- {"{{$^}}", []string{"$"}}, -- {"{{$x:=4}}{{$^", []string{"$x"}}, -- {"{{$x:=4}}{{$ ^ ", []string{}}, -- {"{{len .Modified}}{{.^Mo", []string{"Modified"}}, -- {"{{len .Modified}}{{.mf^", []string{"Modified"}}, -- {"{{$^ }}", []string{"$"}}, -- {"{{$a =3}}{{$^", []string{"$a"}}, -- // .two is not good here: fix someday -- {`{{.Modified}}{{.^{{if $.one.two}}xxx{{end}}`, []string{"Modified", "one", "two"}}, -- {`{{.Modified}}{{.o^{{if $.one.two}}xxx{{end}}`, []string{"one"}}, -- {"{{.Modiifed}}{{.one.t^{{if $.one.two}}xxx{{end}}", []string{"two"}}, -- {`{{block "foo" .}}{{i^`, []string{"index", "if"}}, -- {"{{in^{{Internal}}", []string{"index", "Internal", "if"}}, -- // simple number has no completions -- {"{{4^e", []string{}}, -- // simple string has no completions -- {"{{`e^", []string{}}, -- {"{{`No i^", []string{}}, // example of why go/scanner is used -- {"{{xavier}}{{12. x^", []string{"xavier"}}, -- } -- for _, tx := range tests { -- c := testCompleter(t, tx) -- var v []string -- if c != nil { -- ans, _ := c.complete() -- for _, a := range ans.Items { -- v = append(v, a.Label) -- } -- } -- if len(v) != len(tx.wanted) { -- t.Errorf("%q: got %q, wanted %q %d,%d", tx.marked, v, tx.wanted, len(v), len(tx.wanted)) +-func TestSymbols(t *testing.T) { +- for i, x := range tmpl { +- got := parseBuffer([]byte(x.buf)) +- if got.ParseErr != nil { +- t.Errorf("error:%v", got.ParseErr) - continue - } -- sort.Strings(tx.wanted) -- sort.Strings(v) -- for i := 0; i < len(v); i++ { -- if tx.wanted[i] != v[i] { -- t.Errorf("%q at %d: got %v, wanted %v", tx.marked, i, v, tx.wanted) -- break +- if len(got.named) != x.cnt { +- t.Errorf("%d: got %d, expected %d", i, len(got.named), x.cnt) +- } +- for n, s := range got.symbols { +- if s.String() != x.syms[n] { +- t.Errorf("%d: got %s, expected %s", i, s.String(), x.syms[n]) - } - } - } -} - --func testCompleter(t *testing.T, tx tparse) *completer { -- t.Helper() -- // seen chars up to ^ -- col := strings.Index(tx.marked, "^") -- buf := strings.Replace(tx.marked, "^", "", 1) +-func TestWordAt(t *testing.T) { +- want := []string{"", "", "$A", "$A", "", "", "", "", "", "", +- "", "", "", "if", "if", "", "$A", "$A", "", "", +- "B", "", "", "end", "end", "end", "", "", ""} +- p := parseBuffer([]byte("{{$A := .}}{{if $A}}B{{end}}")) +- for i := 0; i < len(p.buf); i++ { +- got := findWordAt(p, i) +- if got != want[i] { +- t.Errorf("for %d, got %q, wanted %q", i, got, want[i]) +- } +- } +-} +- +-func TestNLS(t *testing.T) { +- buf := `{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} +- {{$A.X 12}} +- {{foo (.X.Y) 23 ($A.Z)}} +- {{end}} +- ` - p := parseBuffer([]byte(buf)) -- pos := protocol.Position{Line: 0, Character: uint32(col)} - if p.ParseErr != nil { -- log.Printf("%q: %v", tx.marked, p.ParseErr) +- t.Fatal(p.ParseErr) - } -- offset := inTemplate(p, pos) -- if offset == -1 { -- return nil +- // line 0 doesn't have a \n in front of it +- for i := 1; i < len(p.nls)-1; i++ { +- if buf[p.nls[i]] != '\n' { +- t.Errorf("line %d got %c", i, buf[p.nls[i]]) +- } - } -- syms := make(map[string]symbol) -- filterSyms(syms, p.symbols) -- c := &completer{ -- p: p, -- pos: protocol.Position{Line: 0, Character: uint32(col)}, -- offset: offset + len(Left), -- ctx: protocol.CompletionContext{TriggerKind: protocol.Invoked}, -- syms: syms, +- // fake line at end of file +- if p.nls[len(p.nls)-1] != len(buf) { +- t.Errorf("got %d expected %d", p.nls[len(p.nls)-1], len(buf)) - } -- return c -} -diff -urN a/gopls/internal/lsp/template/highlight.go b/gopls/internal/lsp/template/highlight.go ---- a/gopls/internal/lsp/template/highlight.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/template/highlight.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,96 +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 template -- --import ( -- "context" -- "fmt" -- "regexp" -- -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" --) - --func Highlight(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, loc protocol.Position) ([]protocol.DocumentHighlight, error) { -- buf, err := fh.Read() -- if err != nil { -- return nil, err +-func TestLineCol(t *testing.T) { +- buf := `{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} +- {{$A.X 12}} +- {{foo (.X.Y) 23 ($A.Z)}} +- {{end}}` +- if false { +- t.Error(buf) - } -- p := parseBuffer(buf) -- pos := p.FromPosition(loc) -- var ans []protocol.DocumentHighlight -- if p.ParseErr == nil { -- for _, s := range p.symbols { -- if s.start <= pos && pos < s.start+s.length { -- return markSymbols(p, s) +- for n, cx := range tmpl { +- buf := cx.buf +- p := parseBuffer([]byte(buf)) +- if p.ParseErr != nil { +- t.Fatal(p.ParseErr) +- } +- type loc struct { +- offset int +- l, c uint32 +- } +- saved := []loc{} +- // forwards +- var lastl, lastc uint32 +- for offset := range buf { +- l, c := p.LineCol(offset) +- saved = append(saved, loc{offset, l, c}) +- if l > lastl { +- lastl = l +- if c != 0 { +- t.Errorf("line %d, got %d instead of 0", l, c) +- } +- } +- if c > lastc { +- lastc = c - } - } -- } -- // these tokens exist whether or not there was a parse error -- // (symbols require a successful parse) -- for _, tok := range p.tokens { -- if tok.Start <= pos && pos < tok.End { -- wordAt := findWordAt(p, pos) -- if len(wordAt) > 0 { -- return markWordInToken(p, wordAt) +- lines := strings.Split(buf, "\n") +- mxlen := -1 +- for _, l := range lines { +- if len(l) > mxlen { +- mxlen = len(l) +- } +- } +- if int(lastl) != len(lines)-1 && int(lastc) != mxlen { +- // lastl is 0 if there is only 1 line(?) +- t.Errorf("expected %d, %d, got %d, %d for case %d", len(lines)-1, mxlen, lastl, lastc, n) +- } +- // backwards +- for j := len(saved) - 1; j >= 0; j-- { +- s := saved[j] +- xl, xc := p.LineCol(s.offset) +- if xl != s.l || xc != s.c { +- t.Errorf("at offset %d(%d), got (%d,%d), expected (%d,%d)", s.offset, j, xl, xc, s.l, s.c) - } - } - } -- // find the 'word' at pos, etc: someday -- // until then we get the default action, which doesn't respect word boundaries -- return ans, nil -} - --func markSymbols(p *Parsed, sym symbol) ([]protocol.DocumentHighlight, error) { -- var ans []protocol.DocumentHighlight -- for _, s := range p.symbols { -- if s.name == sym.name { -- kind := protocol.Read -- if s.vardef { -- kind = protocol.Write -- } -- ans = append(ans, protocol.DocumentHighlight{ -- Range: p.Range(s.start, s.length), -- Kind: kind, -- }) +-func TestLineColNL(t *testing.T) { +- buf := "\n\n\n\n\n" +- p := parseBuffer([]byte(buf)) +- if p.ParseErr != nil { +- t.Fatal(p.ParseErr) +- } +- for i := 0; i < len(buf); i++ { +- l, c := p.LineCol(i) +- if c != 0 || int(l) != i+1 { +- t.Errorf("got (%d,%d), expected (%d,0)", l, c, i) - } - } -- return ans, nil -} - --// A token is {{...}}, and this marks words in the token that equal the give word --func markWordInToken(p *Parsed, wordAt string) ([]protocol.DocumentHighlight, error) { -- var ans []protocol.DocumentHighlight -- pat, err := regexp.Compile(fmt.Sprintf(`\b%s\b`, wordAt)) -- if err != nil { -- return nil, fmt.Errorf("%q: unmatchable word (%v)", wordAt, err) +-func TestPos(t *testing.T) { +- buf := ` +- {{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} +- {{$A.X 12}} +- {{foo (.X.Y) 23 ($A.Z)}} +- {{end}}` +- p := parseBuffer([]byte(buf)) +- if p.ParseErr != nil { +- t.Fatal(p.ParseErr) - } -- for _, tok := range p.tokens { -- got := pat.FindAllIndex(p.buf[tok.Start:tok.End], -1) -- for i := 0; i < len(got); i++ { -- ans = append(ans, protocol.DocumentHighlight{ -- Range: p.Range(got[i][0], got[i][1]-got[i][0]), -- Kind: protocol.Text, -- }) +- for pos, r := range buf { +- if r == '\n' { +- continue +- } +- x := p.Position(pos) +- n := p.FromPosition(x) +- if n != pos { +- // once it's wrong, it will be wrong forever +- t.Fatalf("at pos %d (rune %c) got %d {%#v]", pos, r, n, x) +- } +- +- } +-} +-func TestLen(t *testing.T) { +- data := []struct { +- cnt int +- v string +- }{{1, "a"}, {1, "膈"}, {4, "😆🥸"}, {7, "3😀4567"}} +- p := &Parsed{nonASCII: true} +- for _, d := range data { +- got := p.utf16len([]byte(d.v)) +- if got != d.cnt { +- t.Errorf("%v, got %d wanted %d", d, got, d.cnt) - } - } -- return ans, nil -} - --var wordRe = regexp.MustCompile(`[$]?\w+$`) --var moreRe = regexp.MustCompile(`^[$]?\w+`) +-func TestUtf16(t *testing.T) { +- buf := ` +- {{if (foÜx .X.Y)}}😀{{$A := "hi"}}{{.Z $A}}{{else}} +- {{$A.X 12}} +- {{foo (.X.Y) 23 ($A.Z)}} +- {{end}}` +- p := parseBuffer([]byte(buf)) +- if p.nonASCII == false { +- t.Error("expected nonASCII to be true") +- } +-} - --// findWordAt finds the word the cursor is in (meaning in or just before) --func findWordAt(p *Parsed, pos int) string { -- if pos >= len(p.buf) { -- return "" // can't happen, as we are called with pos < tok.End +-type ttest struct { +- tmpl string +- tokCnt int +- elidedCnt int8 +-} +- +-func TestQuotes(t *testing.T) { +- tsts := []ttest{ +- {"{{- /*comment*/ -}}", 1, 0}, +- {"{{/*`\ncomment\n`*/}}", 1, 0}, +- //{"{{foo\nbar}}\n", 1, 0}, // this action spanning lines parses in 1.16 +- {"{{\"{{foo}}{{\"}}", 1, 0}, +- {"{{\n{{- when}}", 1, 1}, // corrected +- {"{{{{if .}}xx{{\n{{end}}", 2, 2}, // corrected - } -- after := moreRe.Find(p.buf[pos:]) -- if len(after) == 0 { -- return "" // end of the word +- for _, s := range tsts { +- p := parseBuffer([]byte(s.tmpl)) +- if len(p.tokens) != s.tokCnt { +- t.Errorf("%q: got %d tokens, expected %d", s, len(p.tokens), s.tokCnt) +- } +- if p.ParseErr != nil { +- t.Errorf("%q: %v", string(p.buf), p.ParseErr) +- } +- if len(p.elided) != int(s.elidedCnt) { +- t.Errorf("%q: elided %d, expected %d", s, len(p.elided), s.elidedCnt) +- } - } -- got := wordRe.Find(p.buf[:pos+len(after)]) -- return string(got) -} -diff -urN a/gopls/internal/lsp/template/implementations.go b/gopls/internal/lsp/template/implementations.go ---- a/gopls/internal/lsp/template/implementations.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/template/implementations.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,189 +0,0 @@ +diff -urN a/gopls/internal/lsp/template/symbols.go b/gopls/internal/lsp/template/symbols.go +--- a/gopls/internal/lsp/template/symbols.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/template/symbols.go 1970-01-01 08:00:00 +@@ -1,230 +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. @@ -89630,24389 +101947,23935 @@ diff -urN a/gopls/internal/lsp/template/implementations.go b/gopls/internal/lsp/ -package template - -import ( +- "bytes" - "context" - "fmt" -- "regexp" -- "strconv" -- "time" +- "text/template/parse" +- "unicode/utf8" - - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/event" -) - --// line number (1-based) and message --var errRe = regexp.MustCompile(`template.*:(\d+): (.*)`) +-// in local coordinates, to be translated to protocol.DocumentSymbol +-type symbol struct { +- start int // for sorting +- length int // in runes (unicode code points) +- name string +- kind protocol.SymbolKind +- vardef bool // is this a variable definition? +- // do we care about selection range, or children? +- // no children yet, and selection range is the same as range +-} - --// Diagnose returns parse errors. There is only one. --// The errors are not always helpful. For instance { {end}} --// will likely point to the end of the file. --func Diagnose(f source.FileHandle) []*source.Diagnostic { -- // no need for skipTemplate check, as Diagnose is called on the -- // snapshot's template files -- buf, err := f.Read() -- if err != nil { -- // Is a Diagnostic with no Range useful? event.Error also? -- msg := fmt.Sprintf("failed to read %s (%v)", f.URI().Filename(), err) -- d := source.Diagnostic{Message: msg, Severity: protocol.SeverityError, URI: f.URI(), -- Source: source.TemplateError} -- return []*source.Diagnostic{&d} -- } -- p := parseBuffer(buf) -- if p.ParseErr == nil { -- return nil -- } -- unknownError := func(msg string) []*source.Diagnostic { -- s := fmt.Sprintf("malformed template error %q: %s", p.ParseErr.Error(), msg) -- d := source.Diagnostic{ -- Message: s, Severity: protocol.SeverityError, Range: p.Range(p.nls[0], 1), -- URI: f.URI(), Source: source.TemplateError} -- return []*source.Diagnostic{&d} -- } -- // errors look like `template: :40: unexpected "}" in operand` -- // so the string needs to be parsed -- matches := errRe.FindStringSubmatch(p.ParseErr.Error()) -- if len(matches) != 3 { -- msg := fmt.Sprintf("expected 3 matches, got %d (%v)", len(matches), matches) -- return unknownError(msg) -- } -- lineno, err := strconv.Atoi(matches[1]) -- if err != nil { -- msg := fmt.Sprintf("couldn't convert %q to int, %v", matches[1], err) -- return unknownError(msg) -- } -- msg := matches[2] -- d := source.Diagnostic{Message: msg, Severity: protocol.SeverityError, -- Source: source.TemplateError} -- start := p.nls[lineno-1] -- if lineno < len(p.nls) { -- size := p.nls[lineno] - start -- d.Range = p.Range(start, size) -- } else { -- d.Range = p.Range(start, 1) -- } -- return []*source.Diagnostic{&d} +-func (s symbol) String() string { +- return fmt.Sprintf("{%d,%d,%s,%s,%v}", s.start, s.length, s.name, s.kind, s.vardef) -} - --// Definition finds the definitions of the symbol at loc. It --// does not understand scoping (if any) in templates. This code is --// for definitions, type definitions, and implementations. --// Results only for variables and templates. --func Definition(snapshot source.Snapshot, fh source.FileHandle, loc protocol.Position) ([]protocol.Location, error) { -- x, _, err := symAtPosition(fh, loc) -- if err != nil { -- return nil, err +-// for FieldNode or VariableNode (or ChainNode?) +-func (p *Parsed) fields(flds []string, x parse.Node) []symbol { +- ans := []symbol{} +- // guessing that there are no embedded blanks allowed. The doc is unclear +- lookfor := "" +- switch x.(type) { +- case *parse.FieldNode: +- for _, f := range flds { +- lookfor += "." + f // quadratic, but probably ok +- } +- case *parse.VariableNode: +- lookfor = flds[0] +- for i := 1; i < len(flds); i++ { +- lookfor += "." + flds[i] +- } +- case *parse.ChainNode: // PJW, what are these? +- for _, f := range flds { +- lookfor += "." + f // quadratic, but probably ok +- } +- default: +- // If these happen they will happen even if gopls is restarted +- // and the users does the same thing, so it is better not to panic. +- // context.Background() is used because we don't have access +- // to any other context. [we could, but it would be complicated] +- event.Log(context.Background(), fmt.Sprintf("%T unexpected in fields()", x)) +- return nil - } -- sym := x.name -- ans := []protocol.Location{} -- // PJW: this is probably a pattern to abstract -- a := New(snapshot.Templates()) -- for k, p := range a.files { -- for _, s := range p.symbols { -- if !s.vardef || s.name != sym { -- continue +- if len(lookfor) == 0 { +- event.Log(context.Background(), fmt.Sprintf("no strings in fields() %#v", x)) +- return nil +- } +- startsAt := int(x.Position()) +- ix := bytes.Index(p.buf[startsAt:], []byte(lookfor)) // HasPrefix? PJW? +- if ix < 0 || ix > len(lookfor) { // lookfor expected to be at start (or so) +- // probably golang.go/#43388, so back up +- startsAt -= len(flds[0]) + 1 +- ix = bytes.Index(p.buf[startsAt:], []byte(lookfor)) // ix might be 1? PJW +- if ix < 0 { +- return ans +- } +- } +- at := ix + startsAt +- for _, f := range flds { +- at += 1 // . +- kind := protocol.Method +- if f[0] == '$' { +- kind = protocol.Variable +- } +- sym := symbol{name: f, kind: kind, start: at, length: utf8.RuneCount([]byte(f))} +- if kind == protocol.Variable && len(p.stack) > 1 { +- if pipe, ok := p.stack[len(p.stack)-2].(*parse.PipeNode); ok { +- for _, y := range pipe.Decl { +- if x == y { +- sym.vardef = true +- } +- } - } -- ans = append(ans, protocol.Location{URI: protocol.DocumentURI(k), Range: p.Range(s.start, s.length)}) - } +- ans = append(ans, sym) +- at += len(f) - } -- return ans, nil +- return ans -} - --func Hover(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, position protocol.Position) (*protocol.Hover, error) { -- sym, p, err := symAtPosition(fh, position) -- if sym == nil || err != nil { -- return nil, err +-func (p *Parsed) findSymbols() { +- if len(p.stack) == 0 { +- return - } -- ans := protocol.Hover{Range: p.Range(sym.start, sym.length), Contents: protocol.MarkupContent{Kind: protocol.Markdown}} -- switch sym.kind { -- case protocol.Function: -- ans.Contents.Value = fmt.Sprintf("function: %s", sym.name) -- case protocol.Variable: -- ans.Contents.Value = fmt.Sprintf("variable: %s", sym.name) -- case protocol.Constant: -- ans.Contents.Value = fmt.Sprintf("constant %s", sym.name) -- case protocol.Method: // field or method -- ans.Contents.Value = fmt.Sprintf("%s: field or method", sym.name) -- case protocol.Package: // template use, template def (PJW: do we want two?) -- ans.Contents.Value = fmt.Sprintf("template %s\n(add definition)", sym.name) -- case protocol.Namespace: -- ans.Contents.Value = fmt.Sprintf("template %s defined", sym.name) -- case protocol.Number: -- ans.Contents.Value = "number" -- case protocol.String: -- ans.Contents.Value = "string" -- case protocol.Boolean: -- ans.Contents.Value = "boolean" -- default: -- ans.Contents.Value = fmt.Sprintf("oops, sym=%#v", sym) +- n := p.stack[len(p.stack)-1] +- pop := func() { +- p.stack = p.stack[:len(p.stack)-1] - } -- return &ans, nil --} -- --func References(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, params *protocol.ReferenceParams) ([]protocol.Location, error) { -- sym, _, err := symAtPosition(fh, params.Position) -- if sym == nil || err != nil || sym.name == "" { -- return nil, err +- if n == nil { // allowing nil simplifies the code +- pop() +- return - } -- ans := []protocol.Location{} -- -- a := New(snapshot.Templates()) -- for k, p := range a.files { -- for _, s := range p.symbols { -- if s.name != sym.name { -- continue -- } -- if s.vardef && !params.Context.IncludeDeclaration { -- continue +- nxt := func(nd parse.Node) { +- p.stack = append(p.stack, nd) +- p.findSymbols() +- } +- switch x := n.(type) { +- case *parse.ActionNode: +- nxt(x.Pipe) +- case *parse.BoolNode: +- // need to compute the length from the value +- msg := fmt.Sprintf("%v", x.True) +- p.symbols = append(p.symbols, symbol{start: int(x.Pos), length: len(msg), kind: protocol.Boolean}) +- case *parse.BranchNode: +- nxt(x.Pipe) +- nxt(x.List) +- nxt(x.ElseList) +- case *parse.ChainNode: +- p.symbols = append(p.symbols, p.fields(x.Field, x)...) +- nxt(x.Node) +- case *parse.CommandNode: +- for _, a := range x.Args { +- nxt(a) +- } +- //case *parse.CommentNode: // go 1.16 +- // log.Printf("implement %d", x.Type()) +- case *parse.DotNode: +- sym := symbol{name: "dot", kind: protocol.Variable, start: int(x.Pos), length: 1} +- p.symbols = append(p.symbols, sym) +- case *parse.FieldNode: +- p.symbols = append(p.symbols, p.fields(x.Ident, x)...) +- case *parse.IdentifierNode: +- sym := symbol{name: x.Ident, kind: protocol.Function, start: int(x.Pos), +- length: utf8.RuneCount([]byte(x.Ident))} +- p.symbols = append(p.symbols, sym) +- case *parse.IfNode: +- nxt(&x.BranchNode) +- case *parse.ListNode: +- if x != nil { // wretched typed nils. Node should have an IfNil +- for _, nd := range x.Nodes { +- nxt(nd) - } -- ans = append(ans, protocol.Location{URI: protocol.DocumentURI(k), Range: p.Range(s.start, s.length)}) - } +- case *parse.NilNode: +- sym := symbol{name: "nil", kind: protocol.Constant, start: int(x.Pos), length: 3} +- p.symbols = append(p.symbols, sym) +- case *parse.NumberNode: +- // no name; ascii +- p.symbols = append(p.symbols, symbol{start: int(x.Pos), length: len(x.Text), kind: protocol.Number}) +- case *parse.PipeNode: +- if x == nil { // {{template "foo"}} +- return +- } +- for _, d := range x.Decl { +- nxt(d) +- } +- for _, c := range x.Cmds { +- nxt(c) +- } +- case *parse.RangeNode: +- nxt(&x.BranchNode) +- case *parse.StringNode: +- // no name +- sz := utf8.RuneCount([]byte(x.Text)) +- p.symbols = append(p.symbols, symbol{start: int(x.Pos), length: sz, kind: protocol.String}) +- case *parse.TemplateNode: // invoking a template +- // x.Pos points to the quote before the name +- p.symbols = append(p.symbols, symbol{name: x.Name, kind: protocol.Package, start: int(x.Pos) + 1, +- length: utf8.RuneCount([]byte(x.Name))}) +- nxt(x.Pipe) +- case *parse.TextNode: +- if len(x.Text) == 1 && x.Text[0] == '\n' { +- break +- } +- // nothing to report, but build one for hover +- sz := utf8.RuneCount([]byte(x.Text)) +- p.symbols = append(p.symbols, symbol{start: int(x.Pos), length: sz, kind: protocol.Constant}) +- case *parse.VariableNode: +- p.symbols = append(p.symbols, p.fields(x.Ident, x)...) +- case *parse.WithNode: +- nxt(&x.BranchNode) +- - } -- // do these need to be sorted? (a.files is a map) -- return ans, nil +- pop() -} - --func SemanticTokens(ctx context.Context, snapshot source.Snapshot, spn span.URI, add func(line, start, len uint32), d func() []uint32) (*protocol.SemanticTokens, error) { -- fh, err := snapshot.GetFile(ctx, spn) -- if err != nil { -- return nil, err -- } -- buf, err := fh.Read() +-// DocumentSymbols returns a hierarchy of the symbols defined in a template file. +-// (The hierarchy is flat. SymbolInformation might be better.) +-func DocumentSymbols(snapshot source.Snapshot, fh source.FileHandle) ([]protocol.DocumentSymbol, error) { +- buf, err := fh.Content() - if err != nil { - return nil, err - } - p := parseBuffer(buf) -- -- for _, t := range p.Tokens() { -- if t.Multiline { -- la, ca := p.LineCol(t.Start) -- lb, cb := p.LineCol(t.End) -- add(la, ca, p.RuneCount(la, ca, 0)) -- for l := la + 1; l < lb; l++ { -- add(l, 0, p.RuneCount(l, 0, 0)) -- } -- add(lb, 0, p.RuneCount(lb, 0, cb)) +- if p.ParseErr != nil { +- return nil, p.ParseErr +- } +- var ans []protocol.DocumentSymbol +- for _, s := range p.symbols { +- if s.kind == protocol.Constant { - continue - } -- sz, err := p.TokenSize(t) -- if err != nil { -- return nil, err +- d := kindStr(s.kind) +- if d == "Namespace" { +- d = "Template" - } -- line, col := p.LineCol(t.Start) -- add(line, col, uint32(sz)) -- } -- data := d() -- ans := &protocol.SemanticTokens{ -- Data: data, -- // for small cache, some day. for now, the LSP client ignores this -- // (that is, when the LSP client starts returning these, we can cache) -- ResultID: fmt.Sprintf("%v", time.Now()), +- if s.vardef { +- d += "(def)" +- } else { +- d += "(use)" +- } +- r := p.Range(s.start, s.length) +- y := protocol.DocumentSymbol{ +- Name: s.name, +- Detail: d, +- Kind: s.kind, +- Range: r, +- SelectionRange: r, // or should this be the entire {{...}}? +- } +- ans = append(ans, y) - } - return ans, nil -} +diff -urN a/gopls/internal/lsp/testdata/%percent/perc%ent.go b/gopls/internal/lsp/testdata/%percent/perc%ent.go +--- a/gopls/internal/lsp/testdata/%percent/perc%ent.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/%percent/perc%ent.go 1970-01-01 08:00:00 +@@ -1 +0,0 @@ +-package percent +diff -urN a/gopls/internal/lsp/testdata/addimport/addimport.go.golden b/gopls/internal/lsp/testdata/addimport/addimport.go.golden +--- a/gopls/internal/lsp/testdata/addimport/addimport.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/addimport/addimport.go.golden 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +--- addimport -- +-package addimport //@addimport("", "bytes") - --// still need to do rename, etc -diff -urN a/gopls/internal/lsp/template/parse.go b/gopls/internal/lsp/template/parse.go ---- a/gopls/internal/lsp/template/parse.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/template/parse.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,508 +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. +-import "bytes" - --// Package template contains code for dealing with templates --package template +-func main() {} - --// template files are small enough that the code reprocesses them each time --// this may be a bad choice for projects with lots of template files. +diff -urN a/gopls/internal/lsp/testdata/addimport/addimport.go.in b/gopls/internal/lsp/testdata/addimport/addimport.go.in +--- a/gopls/internal/lsp/testdata/addimport/addimport.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/addimport/addimport.go.in 1970-01-01 08:00:00 +@@ -1,3 +0,0 @@ +-package addimport //@addimport("", "bytes") - --// This file contains the parsing code, some debugging printing, and --// implementations for Diagnose, Definition, Hover, References +-func main() {} +diff -urN a/gopls/internal/lsp/testdata/address/address.go b/gopls/internal/lsp/testdata/address/address.go +--- a/gopls/internal/lsp/testdata/address/address.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/address/address.go 1970-01-01 08:00:00 +@@ -1,78 +0,0 @@ +-package address - --import ( -- "bytes" -- "context" -- "fmt" -- "io" -- "log" -- "regexp" -- "runtime" -- "sort" -- "text/template" -- "text/template/parse" -- "unicode/utf8" +-func wantsPtr(*int) {} +-func wantsVariadicPtr(...*int) {} - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/event" --) +-func wantsVariadic(...int) {} - --var ( -- Left = []byte("{{") -- Right = []byte("}}") --) +-type foo struct{ c int } //@item(addrFieldC, "c", "int", "field") - --type Parsed struct { -- buf []byte //contents -- lines [][]byte // needed?, other than for debugging? -- elided []int // offsets where Left was replaced by blanks +-func _() { +- var ( +- a string //@item(addrA, "a", "string", "var") +- b int //@item(addrB, "b", "int", "var") +- ) - -- // tokens are matched Left-Right pairs, computed before trying to parse -- tokens []Token +- wantsPtr() //@rank(")", addrB, addrA),snippet(")", addrB, "&b", "&b") +- wantsPtr(&b) //@snippet(")", addrB, "b", "b") - -- // result of parsing -- named []*template.Template // the template and embedded templates -- ParseErr error -- symbols []symbol -- stack []parse.Node // used while computing symbols +- wantsVariadicPtr() //@rank(")", addrB, addrA),snippet(")", addrB, "&b", "&b") - -- // for mapping from offsets in buf to LSP coordinates -- // See FromPosition() and LineCol() -- nls []int // offset of newlines before each line (nls[0]==-1) -- lastnl int // last line seen -- check int // used to decide whether to use lastnl or search through nls -- nonASCII bool // are there any non-ascii runes in buf? +- var s foo +- s.c //@item(addrDeepC, "s.c", "int", "field") +- wantsPtr() //@snippet(")", addrDeepC, "&s.c", "&s.c") +- wantsPtr(s) //@snippet(")", addrDeepC, "&s.c", "&s.c") +- wantsPtr(&s) //@snippet(")", addrDeepC, "s.c", "s.c") +- +- // don't add "&" in item (it gets added as an additional edit) +- wantsPtr(&s.c) //@snippet(")", addrFieldC, "c", "c") +- +- // check dereferencing as well +- var c *int //@item(addrCPtr, "c", "*int", "var") +- var _ int = _ //@rank("_ //", addrCPtr, addrA),snippet("_ //", addrCPtr, "*c", "*c") +- +- wantsVariadic() //@rank(")", addrCPtr, addrA),snippet(")", addrCPtr, "*c", "*c") +- +- var d **int //@item(addrDPtr, "d", "**int", "var") +- var _ int = _ //@rank("_ //", addrDPtr, addrA),snippet("_ //", addrDPtr, "**d", "**d") +- +- type namedPtr *int +- var np namedPtr //@item(addrNamedPtr, "np", "namedPtr", "var") +- +- var _ int = _ //@rank("_ //", addrNamedPtr, addrA) +- +- // don't get tripped up by recursive pointer type +- type dontMessUp *dontMessUp +- var dmu *dontMessUp //@item(addrDMU, "dmu", "*dontMessUp", "var") +- +- var _ int = dmu //@complete(" //", addrDMU) -} - --// Token is a single {{...}}. More precisely, Left...Right --type Token struct { -- Start, End int // offset from start of template -- Multiline bool +-func (f foo) ptr() *foo { return &f } +- +-func _() { +- getFoo := func() foo { return foo{} } +- +- // not addressable +- getFoo().c //@item(addrGetFooC, "getFoo().c", "int", "field") +- +- // addressable +- getFoo().ptr().c //@item(addrGetFooPtrC, "getFoo().ptr().c", "int", "field") +- +- wantsPtr() //@rank(addrGetFooPtrC, addrGetFooC),snippet(")", addrGetFooPtrC, "&getFoo().ptr().c", "&getFoo().ptr().c") +- wantsPtr(&g) //@rank(addrGetFooPtrC, addrGetFooC),snippet(")", addrGetFooPtrC, "getFoo().ptr().c", "getFoo().ptr().c") -} - --// All contains the Parse of all the template files --type All struct { -- files map[span.URI]*Parsed +-type nested struct { +- f foo -} - --// New returns the Parses of the snapshot's tmpl files --// (maybe cache these, but then avoiding import cycles needs code rearrangements) --func New(tmpls map[span.URI]source.FileHandle) *All { -- all := make(map[span.URI]*Parsed) -- for k, v := range tmpls { -- buf, err := v.Read() -- if err != nil { // PJW: decide what to do with these errors -- log.Printf("failed to read %s (%v)", v.URI().Filename(), err) -- continue -- } -- all[k] = parseBuffer(buf) -- } -- return &All{files: all} +-func _() { +- getNested := func() nested { return nested{} } +- +- getNested().f.c //@item(addrNestedC, "getNested().f.c", "int", "field") +- getNested().f.ptr().c //@item(addrNestedPtrC, "getNested().f.ptr().c", "int", "field") +- +- // addrNestedC is not addressable, so rank lower +- wantsPtr(getNestedfc) //@fuzzy(")", addrNestedPtrC, addrNestedC) -} +diff -urN a/gopls/internal/lsp/testdata/anon/anon.go.in b/gopls/internal/lsp/testdata/anon/anon.go.in +--- a/gopls/internal/lsp/testdata/anon/anon.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/anon/anon.go.in 1970-01-01 08:00:00 +@@ -1,23 +0,0 @@ +-package anon - --func parseBuffer(buf []byte) *Parsed { -- ans := &Parsed{ -- buf: buf, -- check: -1, -- nls: []int{-1}, -- } -- if len(buf) == 0 { -- return ans -- } -- // how to compute allAscii... -- for _, b := range buf { -- if b >= utf8.RuneSelf { -- ans.nonASCII = true -- break -- } -- } -- if buf[len(buf)-1] != '\n' { -- ans.buf = append(buf, '\n') -- } -- for i, p := range ans.buf { -- if p == '\n' { -- ans.nls = append(ans.nls, i) -- } -- } -- ans.setTokens() // ans.buf may be a new []byte -- ans.lines = bytes.Split(ans.buf, []byte{'\n'}) -- t, err := template.New("").Parse(string(ans.buf)) -- if err != nil { -- funcs := make(template.FuncMap) -- for t == nil && ans.ParseErr == nil { -- // in 1.17 it may be possible to avoid getting this error -- // template: :2: function "foo" not defined -- matches := parseErrR.FindStringSubmatch(err.Error()) -- if len(matches) == 2 { -- // suppress the error by giving it a function with the right name -- funcs[matches[1]] = func() interface{} { return nil } -- t, err = template.New("").Funcs(funcs).Parse(string(ans.buf)) -- continue -- } -- ans.ParseErr = err // unfixed error -- return ans -- } +-func _() { +- for _, _ := range []struct { +- i, j int //@item(anonI, "i", "int", "field"),item(anonJ, "j", "int", "field") +- }{ +- { +- i: 1, +- //@complete("", anonJ) +- }, +- { +- //@complete("", anonI, anonJ) +- }, +- } { +- continue - } -- ans.named = t.Templates() -- // set the symbols -- for _, t := range ans.named { -- ans.stack = append(ans.stack, t.Root) -- ans.findSymbols() -- if t.Name() != "" { -- // defining a template. The pos is just after {{define...}} (or {{block...}}?) -- at, sz := ans.FindLiteralBefore(int(t.Root.Pos)) -- s := symbol{start: at, length: sz, name: t.Name(), kind: protocol.Namespace, vardef: true} -- ans.symbols = append(ans.symbols, s) -- } +- +- s := struct{ f int }{ } //@item(anonF, "f", "int", "field"),item(structS, "s", "struct{...}", "var"),complete(" }", anonF) +- +- _ = map[struct{ x int }]int{ //@item(anonX, "x", "int", "field") +- struct{ x int }{ }: 1, //@complete(" }", anonX, structS) - } +-} +diff -urN a/gopls/internal/lsp/testdata/append/append.go b/gopls/internal/lsp/testdata/append/append.go +--- a/gopls/internal/lsp/testdata/append/append.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/append/append.go 1970-01-01 08:00:00 +@@ -1,38 +0,0 @@ +-package append - -- sort.Slice(ans.symbols, func(i, j int) bool { -- left, right := ans.symbols[i], ans.symbols[j] -- if left.start != right.start { -- return left.start < right.start -- } -- if left.vardef != right.vardef { -- return left.vardef -- } -- return left.kind < right.kind -- }) -- return ans +-func foo([]string) {} +-func bar(...string) {} +- +-func _() { +- var ( +- aInt []int //@item(appendInt, "aInt", "[]int", "var") +- aStrings []string //@item(appendStrings, "aStrings", "[]string", "var") +- aString string //@item(appendString, "aString", "string", "var") +- ) +- +- append(aStrings, a) //@rank(")", appendString, appendInt) +- var _ interface{} = append(aStrings, a) //@rank(")", appendString, appendInt) +- var _ []string = append(oops, a) //@rank(")", appendString, appendInt) +- +- foo(append()) //@rank("))", appendStrings, appendInt),rank("))", appendStrings, appendString) +- foo(append([]string{}, a)) //@rank("))", appendStrings, appendInt),rank("))", appendString, appendInt),snippet("))", appendStrings, "aStrings...", "aStrings...") +- foo(append([]string{}, "", a)) //@rank("))", appendString, appendInt),rank("))", appendString, appendStrings) +- +- // Don't add "..." to append() argument. +- bar(append()) //@snippet("))", appendStrings, "aStrings", "aStrings") +- +- type baz struct{} +- baz{} //@item(appendBazLiteral, "baz{}", "", "var") +- var bazzes []baz //@item(appendBazzes, "bazzes", "[]baz", "var") +- var bazzy baz //@item(appendBazzy, "bazzy", "baz", "var") +- bazzes = append(bazzes, ba) //@rank(")", appendBazzy, appendBazLiteral, appendBazzes) +- +- var b struct{ b []baz } +- b.b //@item(appendNestedBaz, "b.b", "[]baz", "field") +- b.b = append(b.b, b) //@rank(")", appendBazzy, appendBazLiteral, appendNestedBaz) +- +- var aStringsPtr *[]string //@item(appendStringsPtr, "aStringsPtr", "*[]string", "var") +- foo(append([]string{}, a)) //@snippet("))", appendStringsPtr, "*aStringsPtr...", "*aStringsPtr...") +- +- foo(append([]string{}, *a)) //@snippet("))", appendStringsPtr, "aStringsPtr...", "aStringsPtr...") -} +diff -urN a/gopls/internal/lsp/testdata/append/append2.go.in b/gopls/internal/lsp/testdata/append/append2.go.in +--- a/gopls/internal/lsp/testdata/append/append2.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/append/append2.go.in 1970-01-01 08:00:00 +@@ -1,5 +0,0 @@ +-package append - --// FindLiteralBefore locates the first preceding string literal --// returning its position and length in buf --// or returns -1 if there is none. --// Assume double-quoted string rather than backquoted string for now. --func (p *Parsed) FindLiteralBefore(pos int) (int, int) { -- left, right := -1, -1 -- for i := pos - 1; i >= 0; i-- { -- if p.buf[i] != '"' { -- continue -- } -- if right == -1 { -- right = i -- continue -- } -- left = i -- break -- } -- if left == -1 { -- return -1, 0 -- } -- return left + 1, right - left - 1 +-func _() { +- _ = append(a, struct) //@complete(")") -} +\ No newline at end of file +diff -urN a/gopls/internal/lsp/testdata/assign/assign.go.in b/gopls/internal/lsp/testdata/assign/assign.go.in +--- a/gopls/internal/lsp/testdata/assign/assign.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/assign/assign.go.in 1970-01-01 08:00:00 +@@ -1,26 +0,0 @@ +-package assign - --var ( -- parseErrR = regexp.MustCompile(`template:.*function "([^"]+)" not defined`) --) +-import "golang.org/lsptests/assign/internal/secret" - --func (p *Parsed) setTokens() { -- const ( -- // InRaw and InString only occur inside an action (SeenLeft) -- Start = iota -- InRaw -- InString -- SeenLeft +-func _() { +- secret.Hello() +- var ( +- myInt int //@item(assignInt, "myInt", "int", "var") +- myStr string //@item(assignStr, "myStr", "string", "var") - ) -- state := Start -- var left, oldState int -- for n := 0; n < len(p.buf); n++ { -- c := p.buf[n] -- switch state { -- case InRaw: -- if c == '`' { -- state = oldState -- } -- case InString: -- if c == '"' && !isEscaped(p.buf[:n]) { -- state = oldState -- } -- case SeenLeft: -- if c == '`' { -- oldState = state // it's SeenLeft, but a little clearer this way -- state = InRaw -- continue -- } -- if c == '"' { -- oldState = state -- state = InString -- continue -- } -- if bytes.HasPrefix(p.buf[n:], Right) { -- right := n + len(Right) -- tok := Token{Start: left, -- End: right, -- Multiline: bytes.Contains(p.buf[left:right], []byte{'\n'}), -- } -- p.tokens = append(p.tokens, tok) -- state = Start -- } -- // If we see (unquoted) Left then the original left is probably the user -- // typing. Suppress the original left -- if bytes.HasPrefix(p.buf[n:], Left) { -- p.elideAt(left) -- left = n -- n += len(Left) - 1 // skip the rest -- } -- case Start: -- if bytes.HasPrefix(p.buf[n:], Left) { -- left = n -- state = SeenLeft -- n += len(Left) - 1 // skip the rest (avoids {{{ bug) -- } -- } -- } -- // this error occurs after typing {{ at the end of the file -- if state != Start { -- // Unclosed Left. remove the Left at left -- p.elideAt(left) -- } +- +- var _ string = my //@rank(" //", assignStr, assignInt) +- var _ string = //@rank(" //", assignStr, assignInt) -} - --func (p *Parsed) elideAt(left int) { -- if p.elided == nil { -- // p.buf is the same buffer that v.Read() returns, so copy it. -- // (otherwise the next time it's parsed, elided information is lost) -- b := make([]byte, len(p.buf)) -- copy(b, p.buf) -- p.buf = b -- } -- for i := 0; i < len(Left); i++ { -- p.buf[left+i] = ' ' -- } -- p.elided = append(p.elided, left) +-func _() { +- var a string = a //@complete(" //") -} - --// isEscaped reports whether the byte after buf is escaped --func isEscaped(buf []byte) bool { -- backSlashes := 0 -- for j := len(buf) - 1; j >= 0 && buf[j] == '\\'; j-- { -- backSlashes++ +-func _() { +- fooBar := fooBa //@complete(" //"),item(assignFooBar, "fooBar", "", "var") +- abc, fooBar := 123, fooBa //@complete(" //", assignFooBar) +- { +- fooBar := fooBa //@complete(" //", assignFooBar) - } -- return backSlashes%2 == 1 -} +diff -urN a/gopls/internal/lsp/testdata/assign/internal/secret/secret.go b/gopls/internal/lsp/testdata/assign/internal/secret/secret.go +--- a/gopls/internal/lsp/testdata/assign/internal/secret/secret.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/assign/internal/secret/secret.go 1970-01-01 08:00:00 +@@ -1,3 +0,0 @@ +-package secret - --func (p *Parsed) Tokens() []Token { -- return p.tokens +-func Hello() {} +\ No newline at end of file +diff -urN a/gopls/internal/lsp/testdata/basiclit/basiclit.go b/gopls/internal/lsp/testdata/basiclit/basiclit.go +--- a/gopls/internal/lsp/testdata/basiclit/basiclit.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/basiclit/basiclit.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package basiclit +- +-func _() { +- var a int // something for lexical completions +- +- _ = "hello." //@complete(".") +- +- _ = 1 //@complete(" //") +- +- _ = 1. //@complete(".") +- +- _ = 'a' //@complete("' ") -} +diff -urN a/gopls/internal/lsp/testdata/builtins/builtin_args.go b/gopls/internal/lsp/testdata/builtins/builtin_args.go +--- a/gopls/internal/lsp/testdata/builtins/builtin_args.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/builtins/builtin_args.go 1970-01-01 08:00:00 +@@ -1,62 +0,0 @@ +-package builtins - --// TODO(adonovan): the next 100 lines could perhaps replaced by use of protocol.Mapper. +-func _() { +- var ( +- aSlice []int //@item(builtinSlice, "aSlice", "[]int", "var") +- aMap map[string]int //@item(builtinMap, "aMap", "map[string]int", "var") +- aString string //@item(builtinString, "aString", "string", "var") +- aArray [0]int //@item(builtinArray, "aArray", "[0]int", "var") +- aArrayPtr *[0]int //@item(builtinArrayPtr, "aArrayPtr", "*[0]int", "var") +- aChan chan int //@item(builtinChan, "aChan", "chan int", "var") +- aPtr *int //@item(builtinPtr, "aPtr", "*int", "var") +- aInt int //@item(builtinInt, "aInt", "int", "var") +- ) - --func (p *Parsed) utf16len(buf []byte) int { -- cnt := 0 -- if !p.nonASCII { -- return len(buf) +- type ( +- aSliceType []int //@item(builtinSliceType, "aSliceType", "[]int", "type") +- aChanType chan int //@item(builtinChanType, "aChanType", "chan int", "type") +- aMapType map[string]int //@item(builtinMapType, "aMapType", "map[string]int", "type") +- ) +- +- close() //@rank(")", builtinChan, builtinSlice) +- +- append() //@rank(")", builtinSlice, builtinChan) +- +- var _ []byte = append([]byte(nil), ""...) //@rank(") //") +- +- copy() //@rank(")", builtinSlice, builtinChan) +- copy(aSlice, aS) //@rank(")", builtinSlice, builtinString) +- copy(aS, aSlice) //@rank(",", builtinSlice, builtinString) +- +- delete() //@rank(")", builtinMap, builtinChan) +- delete(aMap, aS) //@rank(")", builtinString, builtinSlice) +- +- aMapFunc := func() map[int]int { //@item(builtinMapFunc, "aMapFunc", "func() map[int]int", "var") +- return nil - } -- // we need a utf16len(rune), but we don't have it -- for _, r := range string(buf) { -- cnt++ -- if r >= 1<<16 { -- cnt++ -- } +- delete() //@rank(")", builtinMapFunc, builtinSlice) +- +- len() //@rank(")", builtinSlice, builtinInt),rank(")", builtinMap, builtinInt),rank(")", builtinString, builtinInt),rank(")", builtinArray, builtinInt),rank(")", builtinArrayPtr, builtinPtr),rank(")", builtinChan, builtinInt) +- +- cap() //@rank(")", builtinSlice, builtinMap),rank(")", builtinArray, builtinString),rank(")", builtinArrayPtr, builtinPtr),rank(")", builtinChan, builtinInt) +- +- make() //@rank(")", builtinMapType, int),rank(")", builtinChanType, int),rank(")", builtinSliceType, int),rank(")", builtinMapType, int) +- make(aSliceType, a) //@rank(")", builtinInt, builtinSlice) +- +- type myInt int +- var mi myInt //@item(builtinMyInt, "mi", "myInt", "var") +- make(aSliceType, m) //@snippet(")", builtinMyInt, "mi", "mi") +- +- var _ []int = make() //@rank(")", builtinSliceType, builtinMapType) +- +- type myStruct struct{} //@item(builtinStructType, "myStruct", "struct{...}", "struct") +- var _ *myStruct = new() //@rank(")", builtinStructType, int) +- +- for k := range a { //@rank(" {", builtinSlice, builtinInt),rank(" {", builtinString, builtinInt),rank(" {", builtinChan, builtinInt),rank(" {", builtinArray, builtinInt),rank(" {", builtinArrayPtr, builtinInt),rank(" {", builtinMap, builtinInt), - } -- return cnt --} - --func (p *Parsed) TokenSize(t Token) (int, error) { -- if t.Multiline { -- return -1, fmt.Errorf("TokenSize called with Multiline token %#v", t) +- for k, v := range a { //@rank(" {", builtinSlice, builtinChan) - } -- ans := p.utf16len(p.buf[t.Start:t.End]) -- return ans, nil +- +- <-a //@rank(" //", builtinChan, builtinInt) -} +diff -urN a/gopls/internal/lsp/testdata/builtins/builtin_types.go b/gopls/internal/lsp/testdata/builtins/builtin_types.go +--- a/gopls/internal/lsp/testdata/builtins/builtin_types.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/builtins/builtin_types.go 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package builtins - --// RuneCount counts runes in line l, from col s to e --// (e==0 for end of line. called only for multiline tokens) --func (p *Parsed) RuneCount(l, s, e uint32) uint32 { -- start := p.nls[l] + 1 + int(s) -- end := p.nls[l] + 1 + int(e) -- if e == 0 || end > p.nls[l+1] { -- end = p.nls[l+1] -- } -- return uint32(utf8.RuneCount(p.buf[start:end])) +-func _() { +- var _ []bool //@item(builtinBoolSliceType, "[]bool", "[]bool", "type") +- +- var _ []bool = make() //@rank(")", builtinBoolSliceType, int) +- +- var _ []bool = make([], 0) //@rank(",", bool, int) +- +- var _ [][]bool = make([][], 0) //@rank(",", bool, int) -} +diff -urN a/gopls/internal/lsp/testdata/builtins/builtins.go b/gopls/internal/lsp/testdata/builtins/builtins.go +--- a/gopls/internal/lsp/testdata/builtins/builtins.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/builtins/builtins.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package builtins - --// LineCol converts from a 0-based byte offset to 0-based line, col. col in runes --func (p *Parsed) LineCol(x int) (uint32, uint32) { -- if x < p.check { -- p.lastnl = 0 -- } -- p.check = x -- for i := p.lastnl; i < len(p.nls); i++ { -- if p.nls[i] <= x { -- continue -- } -- p.lastnl = i -- var count int -- if i > 0 && x == p.nls[i-1] { // \n -- count = 0 -- } else { -- count = p.utf16len(p.buf[p.nls[i-1]+1 : x]) -- } -- return uint32(i - 1), uint32(count) -- } -- if x == len(p.buf)-1 { // trailing \n -- return uint32(len(p.nls) - 1), 0 -- } -- // shouldn't happen -- for i := 1; i < 4; i++ { -- _, f, l, ok := runtime.Caller(i) -- if !ok { -- break -- } -- log.Printf("%d: %s:%d", i, f, l) -- } +-// Definitions of builtin completion items that are still used in tests. - -- msg := fmt.Errorf("LineCol off the end, %d of %d, nls=%v, %q", x, len(p.buf), p.nls, p.buf[x:]) -- event.Error(context.Background(), "internal error", msg) -- return 0, 0 +-/* bool */ //@item(bool, "bool", "", "type") +-/* complex(r float64, i float64) */ //@item(complex, "complex", "func(r float64, i float64) complex128", "func") +-/* float32 */ //@item(float32, "float32", "", "type") +-/* float64 */ //@item(float64, "float64", "", "type") +-/* imag(c complex128) float64 */ //@item(imag, "imag", "func(c complex128) float64", "func") +-/* int */ //@item(int, "int", "", "type") +-/* iota */ //@item(iota, "iota", "", "const") +-/* string */ //@item(string, "string", "", "type") +-/* true */ //@item(_true, "true", "", "const") +diff -urN a/gopls/internal/lsp/testdata/builtins/constants.go b/gopls/internal/lsp/testdata/builtins/constants.go +--- a/gopls/internal/lsp/testdata/builtins/constants.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/builtins/constants.go 1970-01-01 08:00:00 +@@ -1,19 +0,0 @@ +-package builtins +- +-func _() { +- const ( +- foo = iota //@complete(" //", iota) +- ) +- +- iota //@complete(" //") +- +- var iota int //@item(iotaVar, "iota", "int", "var") +- +- iota //@complete(" //", iotaVar) -} - --// Position produces a protocol.Position from an offset in the template --func (p *Parsed) Position(pos int) protocol.Position { -- line, col := p.LineCol(pos) -- return protocol.Position{Line: line, Character: col} +-func _() { +- var twoRedUpEnd bool //@item(TRUEVar, "twoRedUpEnd", "bool", "var") +- +- var _ bool = true //@rank(" //", _true, TRUEVar) -} +diff -urN a/gopls/internal/lsp/testdata/callhierarchy/callhierarchy.go b/gopls/internal/lsp/testdata/callhierarchy/callhierarchy.go +--- a/gopls/internal/lsp/testdata/callhierarchy/callhierarchy.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/callhierarchy/callhierarchy.go 1970-01-01 08:00:00 +@@ -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. - --func (p *Parsed) Range(x, length int) protocol.Range { -- line, col := p.LineCol(x) -- ans := protocol.Range{ -- Start: protocol.Position{Line: line, Character: col}, -- End: protocol.Position{Line: line, Character: col + uint32(length)}, -- } -- return ans +-package callhierarchy +- +-import "golang.org/lsptests/callhierarchy/outgoing" +- +-func a() { //@mark(hierarchyA, "a") +- D() -} - --// FromPosition translates a protocol.Position into an offset into the template --func (p *Parsed) FromPosition(x protocol.Position) int { -- l, c := int(x.Line), int(x.Character) -- if l >= len(p.nls) || p.nls[l]+1 >= len(p.buf) { -- // paranoia to avoid panic. return the largest offset -- return len(p.buf) -- } -- line := p.buf[p.nls[l]+1:] -- cnt := 0 -- for w := range string(line) { -- if cnt >= c { -- return w + p.nls[l] + 1 -- } -- cnt++ -- } -- // do we get here? NO -- pos := int(x.Character) + p.nls[int(x.Line)] + 1 -- event.Error(context.Background(), "internal error", fmt.Errorf("surprise %#v", x)) -- return pos +-func b() { //@mark(hierarchyB, "b") +- D() -} - --func symAtPosition(fh source.FileHandle, loc protocol.Position) (*symbol, *Parsed, error) { -- buf, err := fh.Read() -- if err != nil { -- return nil, nil, err -- } -- p := parseBuffer(buf) -- pos := p.FromPosition(loc) -- syms := p.SymsAtPos(pos) -- if len(syms) == 0 { -- return nil, p, fmt.Errorf("no symbol found") -- } -- if len(syms) > 1 { -- log.Printf("Hover: %d syms, not 1 %v", len(syms), syms) -- } -- sym := syms[0] -- return &sym, p, nil +-// C is an exported function +-func C() { //@mark(hierarchyC, "C") +- D() +- D() -} - --func (p *Parsed) SymsAtPos(pos int) []symbol { -- ans := []symbol{} -- for _, s := range p.symbols { -- if s.start <= pos && pos < s.start+s.length { -- ans = append(ans, s) -- } -- } -- return ans +-// To test hierarchy across function literals +-var x = func() { //@mark(hierarchyLiteral, "func"),mark(hierarchyLiteralOut, "x") +- D() -} - --type wrNode struct { -- p *Parsed -- w io.Writer +-// D is exported to test incoming/outgoing calls across packages +-func D() { //@mark(hierarchyD, "D"),incomingcalls(hierarchyD, hierarchyA, hierarchyB, hierarchyC, hierarchyLiteral, incomingA),outgoingcalls(hierarchyD, hierarchyE, hierarchyF, hierarchyG, hierarchyLiteralOut, outgoingB, hierarchyFoo, hierarchyH, hierarchyI, hierarchyJ, hierarchyK) +- e() +- x() +- F() +- outgoing.B() +- foo := func() {} //@mark(hierarchyFoo, "foo"),incomingcalls(hierarchyFoo, hierarchyD),outgoingcalls(hierarchyFoo) +- foo() +- +- func() { +- g() +- }() +- +- var i Interface = impl{} +- i.H() +- i.I() +- +- s := Struct{} +- s.J() +- s.K() -} - --// WriteNode is for debugging --func (p *Parsed) WriteNode(w io.Writer, n parse.Node) { -- wr := wrNode{p: p, w: w} -- wr.writeNode(n, "") +-func e() {} //@mark(hierarchyE, "e") +- +-// F is an exported function +-func F() {} //@mark(hierarchyF, "F") +- +-func g() {} //@mark(hierarchyG, "g") +- +-type Interface interface { +- H() //@mark(hierarchyH, "H") +- I() //@mark(hierarchyI, "I") -} - --func (wr wrNode) writeNode(n parse.Node, indent string) { -- if n == nil { -- return -- } -- at := func(pos parse.Pos) string { -- line, col := wr.p.LineCol(int(pos)) -- return fmt.Sprintf("(%d)%v:%v", pos, line, col) -- } -- switch x := n.(type) { -- case *parse.ActionNode: -- fmt.Fprintf(wr.w, "%sActionNode at %s\n", indent, at(x.Pos)) -- wr.writeNode(x.Pipe, indent+". ") -- case *parse.BoolNode: -- fmt.Fprintf(wr.w, "%sBoolNode at %s, %v\n", indent, at(x.Pos), x.True) -- case *parse.BranchNode: -- fmt.Fprintf(wr.w, "%sBranchNode at %s\n", indent, at(x.Pos)) -- wr.writeNode(x.Pipe, indent+"Pipe. ") -- wr.writeNode(x.List, indent+"List. ") -- wr.writeNode(x.ElseList, indent+"Else. ") -- case *parse.ChainNode: -- fmt.Fprintf(wr.w, "%sChainNode at %s, %v\n", indent, at(x.Pos), x.Field) -- case *parse.CommandNode: -- fmt.Fprintf(wr.w, "%sCommandNode at %s, %d children\n", indent, at(x.Pos), len(x.Args)) -- for _, a := range x.Args { -- wr.writeNode(a, indent+". ") -- } -- //case *parse.CommentNode: // 1.16 -- case *parse.DotNode: -- fmt.Fprintf(wr.w, "%sDotNode at %s\n", indent, at(x.Pos)) -- case *parse.FieldNode: -- fmt.Fprintf(wr.w, "%sFieldNode at %s, %v\n", indent, at(x.Pos), x.Ident) -- case *parse.IdentifierNode: -- fmt.Fprintf(wr.w, "%sIdentifierNode at %s, %v\n", indent, at(x.Pos), x.Ident) -- case *parse.IfNode: -- fmt.Fprintf(wr.w, "%sIfNode at %s\n", indent, at(x.Pos)) -- wr.writeNode(&x.BranchNode, indent+". ") -- case *parse.ListNode: -- if x == nil { -- return // nil BranchNode.ElseList -- } -- fmt.Fprintf(wr.w, "%sListNode at %s, %d children\n", indent, at(x.Pos), len(x.Nodes)) -- for _, n := range x.Nodes { -- wr.writeNode(n, indent+". ") -- } -- case *parse.NilNode: -- fmt.Fprintf(wr.w, "%sNilNode at %s\n", indent, at(x.Pos)) -- case *parse.NumberNode: -- fmt.Fprintf(wr.w, "%sNumberNode at %s, %s\n", indent, at(x.Pos), x.Text) -- case *parse.PipeNode: -- if x == nil { -- return // {{template "xxx"}} -- } -- fmt.Fprintf(wr.w, "%sPipeNode at %s, %d vars, %d cmds, IsAssign:%v\n", -- indent, at(x.Pos), len(x.Decl), len(x.Cmds), x.IsAssign) -- for _, d := range x.Decl { -- wr.writeNode(d, indent+"Decl. ") -- } -- for _, c := range x.Cmds { -- wr.writeNode(c, indent+"Cmd. ") -- } -- case *parse.RangeNode: -- fmt.Fprintf(wr.w, "%sRangeNode at %s\n", indent, at(x.Pos)) -- wr.writeNode(&x.BranchNode, indent+". ") -- case *parse.StringNode: -- fmt.Fprintf(wr.w, "%sStringNode at %s, %s\n", indent, at(x.Pos), x.Quoted) -- case *parse.TemplateNode: -- fmt.Fprintf(wr.w, "%sTemplateNode at %s, %s\n", indent, at(x.Pos), x.Name) -- wr.writeNode(x.Pipe, indent+". ") -- case *parse.TextNode: -- fmt.Fprintf(wr.w, "%sTextNode at %s, len %d\n", indent, at(x.Pos), len(x.Text)) -- case *parse.VariableNode: -- fmt.Fprintf(wr.w, "%sVariableNode at %s, %v\n", indent, at(x.Pos), x.Ident) -- case *parse.WithNode: -- fmt.Fprintf(wr.w, "%sWithNode at %s\n", indent, at(x.Pos)) -- wr.writeNode(&x.BranchNode, indent+". ") -- } +-type impl struct{} +- +-func (i impl) H() {} +-func (i impl) I() {} +- +-type Struct struct { +- J func() //@mark(hierarchyJ, "J") +- K func() //@mark(hierarchyK, "K") -} +diff -urN a/gopls/internal/lsp/testdata/callhierarchy/incoming/incoming.go b/gopls/internal/lsp/testdata/callhierarchy/incoming/incoming.go +--- a/gopls/internal/lsp/testdata/callhierarchy/incoming/incoming.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/callhierarchy/incoming/incoming.go 1970-01-01 08:00:00 +@@ -1,12 +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. - --var kindNames = []string{"", "File", "Module", "Namespace", "Package", "Class", "Method", "Property", -- "Field", "Constructor", "Enum", "Interface", "Function", "Variable", "Constant", "String", -- "Number", "Boolean", "Array", "Object", "Key", "Null", "EnumMember", "Struct", "Event", -- "Operator", "TypeParameter"} +-package incoming - --func kindStr(k protocol.SymbolKind) string { -- n := int(k) -- if n < 1 || n >= len(kindNames) { -- return fmt.Sprintf("?SymbolKind %d?", n) -- } -- return kindNames[n] +-import "golang.org/lsptests/callhierarchy" +- +-// A is exported to test incoming calls across packages +-func A() { //@mark(incomingA, "A") +- callhierarchy.D() -} -diff -urN a/gopls/internal/lsp/template/parse_test.go b/gopls/internal/lsp/template/parse_test.go ---- a/gopls/internal/lsp/template/parse_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/template/parse_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,238 +0,0 @@ --// Copyright 2021 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/lsp/testdata/callhierarchy/outgoing/outgoing.go b/gopls/internal/lsp/testdata/callhierarchy/outgoing/outgoing.go +--- a/gopls/internal/lsp/testdata/callhierarchy/outgoing/outgoing.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/callhierarchy/outgoing/outgoing.go 1970-01-01 08:00:00 +@@ -1,9 +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 template -- --import ( -- "strings" -- "testing" --) +-package outgoing - --type datum struct { -- buf string -- cnt int -- syms []string // the symbols in the parse of buf +-// B is exported to test outgoing calls across packages +-func B() { //@mark(outgoingB, "B") -} +diff -urN a/gopls/internal/lsp/testdata/casesensitive/casesensitive.go b/gopls/internal/lsp/testdata/casesensitive/casesensitive.go +--- a/gopls/internal/lsp/testdata/casesensitive/casesensitive.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/casesensitive/casesensitive.go 1970-01-01 08:00:00 +@@ -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. - --var tmpl = []datum{{` --{{if (foo .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} --{{$A.X 12}} --{{foo (.X.Y) 23 ($A.Zü)}} --{{end}}`, 1, []string{"{7,3,foo,Function,false}", "{12,1,X,Method,false}", -- "{14,1,Y,Method,false}", "{21,2,$A,Variable,true}", "{26,2,,String,false}", -- "{35,1,Z,Method,false}", "{38,2,$A,Variable,false}", -- "{53,2,$A,Variable,false}", "{56,1,X,Method,false}", "{57,2,,Number,false}", -- "{64,3,foo,Function,false}", "{70,1,X,Method,false}", -- "{72,1,Y,Method,false}", "{75,2,,Number,false}", "{80,2,$A,Variable,false}", -- "{83,2,Zü,Method,false}", "{94,3,,Constant,false}"}}, +-package casesensitive - -- {`{{define "zzz"}}{{.}}{{end}} --{{template "zzz"}}`, 2, []string{"{10,3,zzz,Namespace,true}", "{18,1,dot,Variable,false}", -- "{41,3,zzz,Package,false}"}}, +-func _() { +- var lower int //@item(lower, "lower", "int", "var") +- var Upper int //@item(upper, "Upper", "int", "var") - -- {`{{block "aaa" foo}}b{{end}}`, 2, []string{"{9,3,aaa,Namespace,true}", -- "{9,3,aaa,Package,false}", "{14,3,foo,Function,false}", "{19,1,,Constant,false}"}}, -- {"", 0, nil}, --} +- l //@casesensitive(" //", lower) +- U //@casesensitive(" //", upper) - --func TestSymbols(t *testing.T) { -- for i, x := range tmpl { -- got := parseBuffer([]byte(x.buf)) -- if got.ParseErr != nil { -- t.Errorf("error:%v", got.ParseErr) -- continue -- } -- if len(got.named) != x.cnt { -- t.Errorf("%d: got %d, expected %d", i, len(got.named), x.cnt) -- } -- for n, s := range got.symbols { -- if s.String() != x.syms[n] { -- t.Errorf("%d: got %s, expected %s", i, s.String(), x.syms[n]) -- } -- } -- } +- L //@casesensitive(" //") +- u //@casesensitive(" //") -} +diff -urN a/gopls/internal/lsp/testdata/cast/cast.go.in b/gopls/internal/lsp/testdata/cast/cast.go.in +--- a/gopls/internal/lsp/testdata/cast/cast.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/cast/cast.go.in 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package cast - --func TestWordAt(t *testing.T) { -- want := []string{"", "", "$A", "$A", "", "", "", "", "", "", -- "", "", "", "if", "if", "", "$A", "$A", "", "", -- "B", "", "", "end", "end", "end", "", "", ""} -- p := parseBuffer([]byte("{{$A := .}}{{if $A}}B{{end}}")) -- for i := 0; i < len(p.buf); i++ { -- got := findWordAt(p, i) -- if got != want[i] { -- t.Errorf("for %d, got %q, wanted %q", i, got, want[i]) -- } -- } +-func _() { +- foo := struct{x int}{x: 1} //@item(x_field, "x", "int", "field") +- _ = float64(foo.x) //@complete("x", x_field) -} - --func TestNLS(t *testing.T) { -- buf := `{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} -- {{$A.X 12}} -- {{foo (.X.Y) 23 ($A.Z)}} -- {{end}} -- ` -- p := parseBuffer([]byte(buf)) -- if p.ParseErr != nil { -- t.Fatal(p.ParseErr) -- } -- // line 0 doesn't have a \n in front of it -- for i := 1; i < len(p.nls)-1; i++ { -- if buf[p.nls[i]] != '\n' { -- t.Errorf("line %d got %c", i, buf[p.nls[i]]) -- } -- } -- // fake line at end of file -- if p.nls[len(p.nls)-1] != len(buf) { -- t.Errorf("got %d expected %d", p.nls[len(p.nls)-1], len(buf)) -- } +-func _() { +- foo := struct{x int}{x: 1} +- _ = float64(foo. //@complete(" /", x_field) -} +\ No newline at end of file +diff -urN a/gopls/internal/lsp/testdata/channel/channel.go b/gopls/internal/lsp/testdata/channel/channel.go +--- a/gopls/internal/lsp/testdata/channel/channel.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/channel/channel.go 1970-01-01 08:00:00 +@@ -1,25 +0,0 @@ +-package channel - --func TestLineCol(t *testing.T) { -- buf := `{{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} -- {{$A.X 12}} -- {{foo (.X.Y) 23 ($A.Z)}} -- {{end}}` -- if false { -- t.Error(buf) -- } -- for n, cx := range tmpl { -- buf := cx.buf -- p := parseBuffer([]byte(buf)) -- if p.ParseErr != nil { -- t.Fatal(p.ParseErr) -- } -- type loc struct { -- offset int -- l, c uint32 -- } -- saved := []loc{} -- // forwards -- var lastl, lastc uint32 -- for offset := range buf { -- l, c := p.LineCol(offset) -- saved = append(saved, loc{offset, l, c}) -- if l > lastl { -- lastl = l -- if c != 0 { -- t.Errorf("line %d, got %d instead of 0", l, c) -- } -- } -- if c > lastc { -- lastc = c -- } -- } -- lines := strings.Split(buf, "\n") -- mxlen := -1 -- for _, l := range lines { -- if len(l) > mxlen { -- mxlen = len(l) -- } -- } -- if int(lastl) != len(lines)-1 && int(lastc) != mxlen { -- // lastl is 0 if there is only 1 line(?) -- t.Errorf("expected %d, %d, got %d, %d for case %d", len(lines)-1, mxlen, lastl, lastc, n) -- } -- // backwards -- for j := len(saved) - 1; j >= 0; j-- { -- s := saved[j] -- xl, xc := p.LineCol(s.offset) -- if xl != s.l || xc != s.c { -- t.Errorf("at offset %d(%d), got (%d,%d), expected (%d,%d)", s.offset, j, xl, xc, s.l, s.c) -- } -- } -- } --} +-func _() { +- var ( +- aa = "123" //@item(channelAA, "aa", "string", "var") +- ab = 123 //@item(channelAB, "ab", "int", "var") +- ) - --func TestLineColNL(t *testing.T) { -- buf := "\n\n\n\n\n" -- p := parseBuffer([]byte(buf)) -- if p.ParseErr != nil { -- t.Fatal(p.ParseErr) -- } -- for i := 0; i < len(buf); i++ { -- l, c := p.LineCol(i) -- if c != 0 || int(l) != i+1 { -- t.Errorf("got (%d,%d), expected (%d,0)", l, c, i) -- } +- { +- type myChan chan int +- var mc myChan +- mc <- a //@complete(" //", channelAB, channelAA) - } --} - --func TestPos(t *testing.T) { -- buf := ` -- {{if (foÜx .X.Y)}}{{$A := "hi"}}{{.Z $A}}{{else}} -- {{$A.X 12}} -- {{foo (.X.Y) 23 ($A.Z)}} -- {{end}}` -- p := parseBuffer([]byte(buf)) -- if p.ParseErr != nil { -- t.Fatal(p.ParseErr) +- { +- var ac chan int //@item(channelAC, "ac", "chan int", "var") +- a <- a //@complete(" <-", channelAC, channelAA, channelAB) - } -- for pos, r := range buf { -- if r == '\n' { -- continue -- } -- x := p.Position(pos) -- n := p.FromPosition(x) -- if n != pos { -- // once it's wrong, it will be wrong forever -- t.Fatalf("at pos %d (rune %c) got %d {%#v]", pos, r, n, x) -- } - +- { +- var foo chan int //@item(channelFoo, "foo", "chan int", "var") +- wantsInt := func(int) {} //@item(channelWantsInt, "wantsInt", "func(int)", "var") +- wantsInt(<-) //@rank(")", channelFoo, channelAB) - } -} --func TestLen(t *testing.T) { -- data := []struct { -- cnt int -- v string -- }{{1, "a"}, {1, "膈"}, {4, "😆🥸"}, {7, "3😀4567"}} -- p := &Parsed{nonASCII: true} -- for _, d := range data { -- got := p.utf16len([]byte(d.v)) -- if got != d.cnt { -- t.Errorf("%v, got %d wanted %d", d, got, d.cnt) -- } -- } --} +diff -urN a/gopls/internal/lsp/testdata/comment_completion/comment_completion.go.in b/gopls/internal/lsp/testdata/comment_completion/comment_completion.go.in +--- a/gopls/internal/lsp/testdata/comment_completion/comment_completion.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/comment_completion/comment_completion.go.in 1970-01-01 08:00:00 +@@ -1,70 +0,0 @@ +-package comment_completion - --func TestUtf16(t *testing.T) { -- buf := ` -- {{if (foÜx .X.Y)}}😀{{$A := "hi"}}{{.Z $A}}{{else}} -- {{$A.X 12}} -- {{foo (.X.Y) 23 ($A.Z)}} -- {{end}}` -- p := parseBuffer([]byte(buf)) -- if p.nonASCII == false { -- t.Error("expected nonASCII to be true") -- } --} +-var p bool - --type ttest struct { -- tmpl string -- tokCnt int -- elidedCnt int8 --} +-//@complete(re"$") - --func TestQuotes(t *testing.T) { -- tsts := []ttest{ -- {"{{- /*comment*/ -}}", 1, 0}, -- {"{{/*`\ncomment\n`*/}}", 1, 0}, -- //{"{{foo\nbar}}\n", 1, 0}, // this action spanning lines parses in 1.16 -- {"{{\"{{foo}}{{\"}}", 1, 0}, -- {"{{\n{{- when}}", 1, 1}, // corrected -- {"{{{{if .}}xx{{\n{{end}}", 2, 2}, // corrected +-func _() { +- var a int +- +- switch a { +- case 1: +- //@complete(re"$") +- _ = a - } -- for _, s := range tsts { -- p := parseBuffer([]byte(s.tmpl)) -- if len(p.tokens) != s.tokCnt { -- t.Errorf("%q: got %d tokens, expected %d", s, len(p.tokens), s.tokCnt) -- } -- if p.ParseErr != nil { -- t.Errorf("%q: %v", string(p.buf), p.ParseErr) -- } -- if len(p.elided) != int(s.elidedCnt) { -- t.Errorf("%q: elided %d, expected %d", s, len(p.elided), s.elidedCnt) -- } +- +- var b chan int +- select { +- case <-b: +- //@complete(re"$") +- _ = b - } --} -diff -urN a/gopls/internal/lsp/template/symbols.go b/gopls/internal/lsp/template/symbols.go ---- a/gopls/internal/lsp/template/symbols.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/template/symbols.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,230 +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 template +- var ( +- //@complete(re"$") +- _ = a +- ) +-} - --import ( -- "bytes" -- "context" -- "fmt" -- "text/template/parse" -- "unicode/utf8" +-// //@complete(" ", variableC) +-var C string //@item(variableC, "C", "string", "var") //@complete(" ", variableC) - -- "golang.org/x/tools/internal/event" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" --) +-// //@complete(" ", constant) +-const Constant = "example" //@item(constant, "Constant", "string", "const") //@complete(" ", constant) - --// in local coordinates, to be translated to protocol.DocumentSymbol --type symbol struct { -- start int // for sorting -- length int // in runes (unicode code points) -- name string -- kind protocol.SymbolKind -- vardef bool // is this a variable definition? -- // do we care about selection range, or children? -- // no children yet, and selection range is the same as range +-// //@complete(" ", structType, fieldB, fieldA) +-type StructType struct { //@item(structType, "StructType", "struct{...}", "struct") //@complete(" ", structType, fieldA, fieldB) +- // //@complete(" ", fieldA, structType, fieldB) +- A string //@item(fieldA, "A", "string", "field") //@complete(" ", fieldA, structType, fieldB) +- b int //@item(fieldB, "b", "int", "field") //@complete(" ", fieldB, structType, fieldA) -} - --func (s symbol) String() string { -- return fmt.Sprintf("{%d,%d,%s,%s,%v}", s.start, s.length, s.name, s.kind, s.vardef) +-// //@complete(" ", method, structRecv, paramX, resultY, fieldB, fieldA) +-func (structType *StructType) Method(X int) (Y int) { //@item(structRecv, "structType", "*StructType", "var"),item(method, "Method", "func(X int) (Y int)", "method"),item(paramX, "X", "int", "var"),item(resultY, "Y", "int", "var") +- // //@complete(" ", method, structRecv, paramX, resultY, fieldB, fieldA) +- return -} - --// for FieldNode or VariableNode (or ChainNode?) --func (p *Parsed) fields(flds []string, x parse.Node) []symbol { -- ans := []symbol{} -- // guessing that there are no embedded blanks allowed. The doc is unclear -- lookfor := "" -- switch x.(type) { -- case *parse.FieldNode: -- for _, f := range flds { -- lookfor += "." + f // quadratic, but probably ok -- } -- case *parse.VariableNode: -- lookfor = flds[0] -- for i := 1; i < len(flds); i++ { -- lookfor += "." + flds[i] -- } -- case *parse.ChainNode: // PJW, what are these? -- for _, f := range flds { -- lookfor += "." + f // quadratic, but probably ok -- } -- default: -- // If these happen they will happen even if gopls is restarted -- // and the users does the same thing, so it is better not to panic. -- // context.Background() is used because we don't have access -- // to any other context. [we could, but it would be complicated] -- event.Log(context.Background(), fmt.Sprintf("%T unexpected in fields()", x)) -- return nil -- } -- if len(lookfor) == 0 { -- event.Log(context.Background(), fmt.Sprintf("no strings in fields() %#v", x)) -- return nil -- } -- startsAt := int(x.Position()) -- ix := bytes.Index(p.buf[startsAt:], []byte(lookfor)) // HasPrefix? PJW? -- if ix < 0 || ix > len(lookfor) { // lookfor expected to be at start (or so) -- // probably golang.go/#43388, so back up -- startsAt -= len(flds[0]) + 1 -- ix = bytes.Index(p.buf[startsAt:], []byte(lookfor)) // ix might be 1? PJW -- if ix < 0 { -- return ans -- } -- } -- at := ix + startsAt -- for _, f := range flds { -- at += 1 // . -- kind := protocol.Method -- if f[0] == '$' { -- kind = protocol.Variable -- } -- sym := symbol{name: f, kind: kind, start: at, length: utf8.RuneCount([]byte(f))} -- if kind == protocol.Variable && len(p.stack) > 1 { -- if pipe, ok := p.stack[len(p.stack)-2].(*parse.PipeNode); ok { -- for _, y := range pipe.Decl { -- if x == y { -- sym.vardef = true -- } -- } -- } -- } -- ans = append(ans, sym) -- at += len(f) -- } -- return ans --} +-// //@complete(" ", newType) +-type NewType string //@item(newType, "NewType", "string", "type") //@complete(" ", newType) - --func (p *Parsed) findSymbols() { -- if len(p.stack) == 0 { -- return -- } -- n := p.stack[len(p.stack)-1] -- pop := func() { -- p.stack = p.stack[:len(p.stack)-1] -- } -- if n == nil { // allowing nil simplifies the code -- pop() -- return -- } -- nxt := func(nd parse.Node) { -- p.stack = append(p.stack, nd) -- p.findSymbols() -- } -- switch x := n.(type) { -- case *parse.ActionNode: -- nxt(x.Pipe) -- case *parse.BoolNode: -- // need to compute the length from the value -- msg := fmt.Sprintf("%v", x.True) -- p.symbols = append(p.symbols, symbol{start: int(x.Pos), length: len(msg), kind: protocol.Boolean}) -- case *parse.BranchNode: -- nxt(x.Pipe) -- nxt(x.List) -- nxt(x.ElseList) -- case *parse.ChainNode: -- p.symbols = append(p.symbols, p.fields(x.Field, x)...) -- nxt(x.Node) -- case *parse.CommandNode: -- for _, a := range x.Args { -- nxt(a) -- } -- //case *parse.CommentNode: // go 1.16 -- // log.Printf("implement %d", x.Type()) -- case *parse.DotNode: -- sym := symbol{name: "dot", kind: protocol.Variable, start: int(x.Pos), length: 1} -- p.symbols = append(p.symbols, sym) -- case *parse.FieldNode: -- p.symbols = append(p.symbols, p.fields(x.Ident, x)...) -- case *parse.IdentifierNode: -- sym := symbol{name: x.Ident, kind: protocol.Function, start: int(x.Pos), -- length: utf8.RuneCount([]byte(x.Ident))} -- p.symbols = append(p.symbols, sym) -- case *parse.IfNode: -- nxt(&x.BranchNode) -- case *parse.ListNode: -- if x != nil { // wretched typed nils. Node should have an IfNil -- for _, nd := range x.Nodes { -- nxt(nd) -- } -- } -- case *parse.NilNode: -- sym := symbol{name: "nil", kind: protocol.Constant, start: int(x.Pos), length: 3} -- p.symbols = append(p.symbols, sym) -- case *parse.NumberNode: -- // no name; ascii -- p.symbols = append(p.symbols, symbol{start: int(x.Pos), length: len(x.Text), kind: protocol.Number}) -- case *parse.PipeNode: -- if x == nil { // {{template "foo"}} -- return -- } -- for _, d := range x.Decl { -- nxt(d) -- } -- for _, c := range x.Cmds { -- nxt(c) -- } -- case *parse.RangeNode: -- nxt(&x.BranchNode) -- case *parse.StringNode: -- // no name -- sz := utf8.RuneCount([]byte(x.Text)) -- p.symbols = append(p.symbols, symbol{start: int(x.Pos), length: sz, kind: protocol.String}) -- case *parse.TemplateNode: // invoking a template -- // x.Pos points to the quote before the name -- p.symbols = append(p.symbols, symbol{name: x.Name, kind: protocol.Package, start: int(x.Pos) + 1, -- length: utf8.RuneCount([]byte(x.Name))}) -- nxt(x.Pipe) -- case *parse.TextNode: -- if len(x.Text) == 1 && x.Text[0] == '\n' { -- break -- } -- // nothing to report, but build one for hover -- sz := utf8.RuneCount([]byte(x.Text)) -- p.symbols = append(p.symbols, symbol{start: int(x.Pos), length: sz, kind: protocol.Constant}) -- case *parse.VariableNode: -- p.symbols = append(p.symbols, p.fields(x.Ident, x)...) -- case *parse.WithNode: -- nxt(&x.BranchNode) +-// //@complete(" ", testInterface, testA, testB) +-type TestInterface interface { //@item(testInterface, "TestInterface", "interface{...}", "interface") +- // //@complete(" ", testA, testInterface, testB) +- TestA(L string) (M int) //@item(testA, "TestA", "func(L string) (M int)", "method"),item(paramL, "L", "var", "string"),item(resM, "M", "var", "int") //@complete(" ", testA, testInterface, testB) +- TestB(N int) bool //@item(testB, "TestB", "func(N int) bool", "method"),item(paramN, "N", "var", "int") //@complete(" ", testB, testInterface, testA) +-} - +-// //@complete(" ", function) +-func Function() int { //@item(function, "Function", "func() int", "func") //@complete(" ", function) +- // //@complete(" ", function) +- return 0 +-} +- +-// This tests multiline block comments and completion with prefix +-// Lorem Ipsum Multili//@complete("Multi", multiline) +-// Lorem ipsum dolor sit ametom +-func Multiline() int { //@item(multiline, "Multiline", "func() int", "func") +- // //@complete(" ", multiline) +- return 0 +-} +diff -urN a/gopls/internal/lsp/testdata/complit/complit.go.in b/gopls/internal/lsp/testdata/complit/complit.go.in +--- a/gopls/internal/lsp/testdata/complit/complit.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/complit/complit.go.in 1970-01-01 08:00:00 +@@ -1,90 +0,0 @@ +-package complit +- +-// general completions +- +-type position struct { //@item(structPosition, "position", "struct{...}", "struct") +- X, Y int //@item(fieldX, "X", "int", "field"),item(fieldY, "Y", "int", "field") +-} +- +-func _() { +- _ = position{ +- //@complete("", fieldX, fieldY, structPosition) +- } +- _ = position{ +- X: 1, +- //@complete("", fieldY) +- } +- _ = position{ +- //@complete("", fieldX) +- Y: 1, +- } +- _ = []*position{ +- { +- //@complete("", fieldX, fieldY, structPosition) +- }, - } -- pop() -} - --// DocumentSymbols returns a hierarchy of the symbols defined in a template file. --// (The hierarchy is flat. SymbolInformation might be better.) --func DocumentSymbols(snapshot source.Snapshot, fh source.FileHandle) ([]protocol.DocumentSymbol, error) { -- buf, err := fh.Read() -- if err != nil { -- return nil, err +-func _() { +- var ( +- aa string //@item(aaVar, "aa", "string", "var") +- ab int //@item(abVar, "ab", "int", "var") +- ) +- +- _ = map[int]int{ +- a: a, //@complete(":", abVar, aaVar),complete(",", abVar, aaVar) - } -- p := parseBuffer(buf) -- if p.ParseErr != nil { -- return nil, p.ParseErr +- +- _ = map[int]int{ +- //@complete("", abVar, aaVar, structPosition) - } -- var ans []protocol.DocumentSymbol -- for _, s := range p.symbols { -- if s.kind == protocol.Constant { -- continue -- } -- d := kindStr(s.kind) -- if d == "Namespace" { -- d = "Template" -- } -- if s.vardef { -- d += "(def)" -- } else { -- d += "(use)" -- } -- r := p.Range(s.start, s.length) -- y := protocol.DocumentSymbol{ -- Name: s.name, -- Detail: d, -- Kind: s.kind, -- Range: r, -- SelectionRange: r, // or should this be the entire {{...}}? -- } -- ans = append(ans, y) +- +- _ = []string{a: ""} //@complete(":", abVar, aaVar) +- _ = [1]string{a: ""} //@complete(":", abVar, aaVar) +- +- _ = position{X: a} //@complete("}", abVar, aaVar) +- _ = position{a} //@complete("}", abVar, aaVar) +- _ = position{a, } //@complete("}", abVar, aaVar, structPosition) +- +- _ = []int{a} //@complete("}", abVar, aaVar) +- _ = [1]int{a} //@complete("}", abVar, aaVar) +- +- type myStruct struct { +- AA int //@item(fieldAA, "AA", "int", "field") +- AB string //@item(fieldAB, "AB", "string", "field") - } -- return ans, nil +- +- _ = myStruct{ +- AB: a, //@complete(",", aaVar, abVar) +- } +- +- var s myStruct +- +- _ = map[int]string{1: "" + s.A} //@complete("}", fieldAB, fieldAA) +- _ = map[int]string{1: (func(i int) string { return "" })(s.A)} //@complete(")}", fieldAA, fieldAB) +- _ = map[int]string{1: func() string { s.A }} //@complete(" }", fieldAA, fieldAB) +- +- _ = position{s.A} //@complete("}", fieldAA, fieldAB) +- +- var X int //@item(varX, "X", "int", "var") +- _ = position{X} //@complete("}", fieldX, varX) -} -diff -urN a/gopls/internal/lsp/testdata/addimport/addimport.go.golden b/gopls/internal/lsp/testdata/addimport/addimport.go.golden ---- a/gopls/internal/lsp/testdata/addimport/addimport.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/addimport/addimport.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ ---- addimport -- --package addimport //@addimport("", "bytes") - --import "bytes" +-func _() { +- type foo struct{} //@item(complitFoo, "foo", "struct{...}", "struct") - --func main() {} +- var _ *foo = &fo{} //@snippet("{", complitFoo, "foo", "foo") +- var _ *foo = fo{} //@snippet("{", complitFoo, "&foo", "&foo") - -diff -urN a/gopls/internal/lsp/testdata/addimport/addimport.go.in b/gopls/internal/lsp/testdata/addimport/addimport.go.in ---- a/gopls/internal/lsp/testdata/addimport/addimport.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/addimport/addimport.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ --package addimport //@addimport("", "bytes") +- struct { a, b *foo }{ +- a: &fo{}, //@rank("{", complitFoo) +- b: fo{}, //@snippet("{", complitFoo, "&foo", "&foo") +- } +-} - --func main() {} -diff -urN a/gopls/internal/lsp/testdata/address/address.go b/gopls/internal/lsp/testdata/address/address.go ---- a/gopls/internal/lsp/testdata/address/address.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/address/address.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,78 +0,0 @@ --package address +-func _() { +- _ := position{ +- X: 1, //@complete("X", fieldX),complete(" 1", structPosition) +- Y: , //@complete(":", fieldY),complete(" ,", structPosition) +- } +-} +diff -urN a/gopls/internal/lsp/testdata/constant/constant.go b/gopls/internal/lsp/testdata/constant/constant.go +--- a/gopls/internal/lsp/testdata/constant/constant.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/constant/constant.go 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +-package constant - --func wantsPtr(*int) {} --func wantsVariadicPtr(...*int) {} +-const x = 1 //@item(constX, "x", "int", "const") - --func wantsVariadic(...int) {} +-const ( +- a int = iota << 2 //@item(constA, "a", "int", "const") +- b //@item(constB, "b", "int", "const") +- c //@item(constC, "c", "int", "const") +-) - --type foo struct{ c int } //@item(addrFieldC, "c", "int", "field") +-func _() { +- const y = "hi" //@item(constY, "y", "string", "const") +- //@complete("", constY, constA, constB, constC, constX) +-} +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_for.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_for.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_for.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_for.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package danglingstmt - -func _() { -- var ( -- a string //@item(addrA, "a", "string", "var") -- b int //@item(addrB, "b", "int", "var") -- ) +- for bar //@rank(" //", danglingBar) +-} - -- wantsPtr() //@rank(")", addrB, addrA),snippet(")", addrB, "&b", "&b") -- wantsPtr(&b) //@snippet(")", addrB, "b", "b") +-func bar() bool { //@item(danglingBar, "bar", "func() bool", "func") +- return true +-} +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package danglingstmt - -- wantsVariadicPtr() //@rank(")", addrB, addrA),snippet(")", addrB, "&b", "&b") +-func _() { +- for i := bar //@rank(" //", danglingBar2) +-} - -- var s foo -- s.c //@item(addrDeepC, "s.c", "int", "field") -- wantsPtr() //@snippet(")", addrDeepC, "&s.c", "&s.c") -- wantsPtr(s) //@snippet(")", addrDeepC, "&s.c", "&s.c") -- wantsPtr(&s) //@snippet(")", addrDeepC, "s.c", "s.c") +-func bar2() int { //@item(danglingBar2, "bar2", "func() int", "func") +- return 0 +-} +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package danglingstmt - -- // don't add "&" in item (it gets added as an additional edit) -- wantsPtr(&s.c) //@snippet(")", addrFieldC, "c", "c") +-func _() { +- for i := bar3(); i > bar //@rank(" //", danglingBar3) +-} - -- // check dereferencing as well -- var c *int //@item(addrCPtr, "c", "*int", "var") -- var _ int = _ //@rank("_ //", addrCPtr, addrA),snippet("_ //", addrCPtr, "*c", "*c") +-func bar3() int { //@item(danglingBar3, "bar3", "func() int", "func") +- return 0 +-} +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond_post.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond_post.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond_post.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond_post.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package danglingstmt - -- wantsVariadic() //@rank(")", addrCPtr, addrA),snippet(")", addrCPtr, "*c", "*c") +-func _() { +- for i := bar4(); i > bar4(); i += bar //@rank(" //", danglingBar4) +-} - -- var d **int //@item(addrDPtr, "d", "**int", "var") -- var _ int = _ //@rank("_ //", addrDPtr, addrA),snippet("_ //", addrDPtr, "**d", "**d") +-func bar4() int { //@item(danglingBar4, "bar4", "func() int", "func") +- return 0 +-} +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_if.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_if.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_if.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_if.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package danglingstmt - -- type namedPtr *int -- var np namedPtr //@item(addrNamedPtr, "np", "namedPtr", "var") +-func _() { +- if foo //@rank(" //", danglingFoo) +-} - -- var _ int = _ //@rank("_ //", addrNamedPtr, addrA) +-func foo() bool { //@item(danglingFoo, "foo", "func() bool", "func") +- return true +-} +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_eof.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_eof.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_eof.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_eof.go 1970-01-01 08:00:00 +@@ -1,8 +0,0 @@ +-package danglingstmt - -- // don't get tripped up by recursive pointer type -- type dontMessUp *dontMessUp -- var dmu *dontMessUp //@item(addrDMU, "dmu", "*dontMessUp", "var") +-func bar5() bool { //@item(danglingBar5, "bar5", "func() bool", "func") +- return true +-} - -- var _ int = dmu //@complete(" //", addrDMU) +-func _() { +- if b //@rank(" //", danglingBar5) +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package danglingstmt +- +-func _() { +- if i := foo //@rank(" //", danglingFoo2) -} - --func (f foo) ptr() *foo { return &f } +-func foo2() bool { //@item(danglingFoo2, "foo2", "func() bool", "func") +- return true +-} +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init_cond.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init_cond.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init_cond.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init_cond.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package danglingstmt - -func _() { -- getFoo := func() foo { return foo{} } +- if i := 123; foo //@rank(" //", danglingFoo3) +-} - -- // not addressable -- getFoo().c //@item(addrGetFooC, "getFoo().c", "int", "field") +-func foo3() bool { //@item(danglingFoo3, "foo3", "func() bool", "func") +- return true +-} +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_multiline_if.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_multiline_if.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_multiline_if.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_multiline_if.go 1970-01-01 08:00:00 +@@ -1,10 +0,0 @@ +-package danglingstmt - -- // addressable -- getFoo().ptr().c //@item(addrGetFooPtrC, "getFoo().ptr().c", "int", "field") +-func walrus() bool { //@item(danglingWalrus, "walrus", "func() bool", "func") +- return true +-} - -- wantsPtr() //@rank(addrGetFooPtrC, addrGetFooC),snippet(")", addrGetFooPtrC, "&getFoo().ptr().c", "&getFoo().ptr().c") -- wantsPtr(&g) //@rank(addrGetFooPtrC, addrGetFooC),snippet(")", addrGetFooPtrC, "getFoo().ptr().c", "getFoo().ptr().c") +-func _() { +- if true && +- walrus //@complete(" //", danglingWalrus) -} +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_1.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_1.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_1.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_1.go 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package danglingstmt - --type nested struct { -- f foo +-func _() { +- x. //@rank(" //", danglingI) -} - +-var x struct { i int } //@item(danglingI, "i", "int", "field") +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_2.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_2.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_2.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_2.go 1970-01-01 08:00:00 +@@ -1,10 +0,0 @@ +-package danglingstmt +- +-// TODO: re-enable this test, which was broken when the foo package was removed. +-// (we can replicate the relevant definitions in the new marker test) +-// import "golang.org/lsptests/foo" +- -func _() { -- getNested := func() nested { return nested{} } +- foo. // rank(" //", Foo) +- var _ = []string{foo.} // rank("}", Foo) +-} +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package danglingstmt - -- getNested().f.c //@item(addrNestedC, "getNested().f.c", "int", "field") -- getNested().f.ptr().c //@item(addrNestedPtrC, "getNested().f.ptr().c", "int", "field") +-func _() { +- switch i := baz //@rank(" //", danglingBaz) +-} - -- // addrNestedC is not addressable, so rank lower -- wantsPtr(getNestedfc) //@fuzzy(")", addrNestedPtrC, addrNestedC) +-func baz() int { //@item(danglingBaz, "baz", "func() int", "func") +- return 0 -} -diff -urN a/gopls/internal/lsp/testdata/analyzer/bad_test.go b/gopls/internal/lsp/testdata/analyzer/bad_test.go ---- a/gopls/internal/lsp/testdata/analyzer/bad_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/analyzer/bad_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ --package analyzer +diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init_tag.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init_tag.go +--- a/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init_tag.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init_tag.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package danglingstmt - --import ( -- "fmt" -- "sync" -- "testing" -- "time" --) +-func _() { +- switch i := 0; baz //@rank(" //", danglingBaz2) +-} - --func Testbad(t *testing.T) { //@diag("", "tests", "Testbad has malformed name: first letter after 'Test' must not be lowercase", "warning") -- var x sync.Mutex -- _ = x //@diag("x", "copylocks", "assignment copies lock value to _: sync.Mutex", "warning") +-func baz2() int { //@item(danglingBaz2, "baz2", "func() int", "func") +- return 0 +-} +diff -urN a/gopls/internal/lsp/testdata/deep/deep.go b/gopls/internal/lsp/testdata/deep/deep.go +--- a/gopls/internal/lsp/testdata/deep/deep.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/deep/deep.go 1970-01-01 08:00:00 +@@ -1,142 +0,0 @@ +-package deep +- +-import "context" - -- printfWrapper("%s") //@diag(re`printfWrapper\(.*\)`, "printf", "golang.org/lsptests/analyzer.printfWrapper format %s reads arg #1, but call has 0 args", "warning") +-type deepA struct { +- b deepB //@item(deepBField, "b", "deepB", "field") -} - --func printfWrapper(format string, args ...interface{}) { -- fmt.Printf(format, args...) +-type deepB struct { -} - +-func wantsDeepB(deepB) {} +- -func _() { -- now := time.Now() -- fmt.Println(now.Format("2006-02-01")) //@diag("2006-02-01", "timeformat", "2006-02-01 should be 2006-01-02", "warning") +- var a deepA //@item(deepAVar, "a", "deepA", "var") +- a.b //@item(deepABField, "a.b", "deepB", "field") +- wantsDeepB(a) //@deep(")", deepABField, deepAVar) +- +- deepA{a} //@snippet("}", deepABField, "a.b", "a.b") -} -diff -urN a/gopls/internal/lsp/testdata/anon/anon.go.in b/gopls/internal/lsp/testdata/anon/anon.go.in ---- a/gopls/internal/lsp/testdata/anon/anon.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/anon/anon.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,23 +0,0 @@ --package anon +- +-func wantsContext(context.Context) {} - -func _() { -- for _, _ := range []struct { -- i, j int //@item(anonI, "i", "int", "field"),item(anonJ, "j", "int", "field") -- }{ -- { -- i: 1, -- //@complete("", anonJ) -- }, -- { -- //@complete("", anonI, anonJ) -- }, -- } { -- continue -- } +- context.Background() //@item(ctxBackground, "context.Background", "func() context.Context", "func", "Background returns a non-nil, empty Context.") +- context.TODO() //@item(ctxTODO, "context.TODO", "func() context.Context", "func", "TODO returns a non-nil, empty Context.") - -- s := struct{ f int }{ } //@item(anonF, "f", "int", "field"),item(structS, "s", "struct{...}", "var"),complete(" }", anonF) +- wantsContext(c) //@rank(")", ctxBackground),rank(")", ctxTODO) +-} - -- _ = map[struct{ x int }]int{ //@item(anonX, "x", "int", "field") -- struct{ x int }{ }: 1, //@complete(" }", anonX, structS) +-func _() { +- var cork struct{ err error } +- cork.err //@item(deepCorkErr, "cork.err", "error", "field") +- context //@item(deepContextPkg, "context", "\"context\"", "package") +- var _ error = co //@rank(" //", deepCorkErr, deepContextPkg) +-} +- +-func _() { +- // deepCircle is circular. +- type deepCircle struct { +- *deepCircle - } +- var circle deepCircle //@item(deepCircle, "circle", "deepCircle", "var") +- circle.deepCircle //@item(deepCircleField, "circle.deepCircle", "*deepCircle", "field") +- var _ deepCircle = circ //@deep(" //", deepCircle, deepCircleField),snippet(" //", deepCircleField, "*circle.deepCircle", "*circle.deepCircle") -} -diff -urN a/gopls/internal/lsp/testdata/append/append2.go.in b/gopls/internal/lsp/testdata/append/append2.go.in ---- a/gopls/internal/lsp/testdata/append/append2.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/append/append2.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,5 +0,0 @@ --package append - -func _() { -- _ = append(a, struct) //@complete(")") +- type deepEmbedC struct { +- } +- type deepEmbedB struct { +- deepEmbedC +- } +- type deepEmbedA struct { +- deepEmbedB +- } +- +- wantsC := func(deepEmbedC) {} +- +- var a deepEmbedA //@item(deepEmbedA, "a", "deepEmbedA", "var") +- a.deepEmbedB //@item(deepEmbedB, "a.deepEmbedB", "deepEmbedB", "field") +- a.deepEmbedC //@item(deepEmbedC, "a.deepEmbedC", "deepEmbedC", "field") +- wantsC(a) //@deep(")", deepEmbedC, deepEmbedA, deepEmbedB) -} -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/append/append.go b/gopls/internal/lsp/testdata/append/append.go ---- a/gopls/internal/lsp/testdata/append/append.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/append/append.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,38 +0,0 @@ --package append - --func foo([]string) {} --func bar(...string) {} +-func _() { +- type nested struct { +- a int +- n *nested //@item(deepNestedField, "n", "*nested", "field") +- } +- +- nested{ +- a: 123, //@deep(" //", deepNestedField) +- } +-} - -func _() { -- var ( -- aInt []int //@item(appendInt, "aInt", "[]int", "var") -- aStrings []string //@item(appendStrings, "aStrings", "[]string", "var") -- aString string //@item(appendString, "aString", "string", "var") -- ) +- var a struct { +- b struct { +- c int +- } +- d int +- } - -- append(aStrings, a) //@rank(")", appendString, appendInt) -- var _ interface{} = append(aStrings, a) //@rank(")", appendString, appendInt) -- var _ []string = append(oops, a) //@rank(")", appendString, appendInt) +- a.d //@item(deepAD, "a.d", "int", "field") +- a.b.c //@item(deepABC, "a.b.c", "int", "field") +- a.b //@item(deepAB, "a.b", "struct{...}", "field") +- a //@item(deepA, "a", "struct{...}", "var") - -- foo(append()) //@rank("))", appendStrings, appendInt),rank("))", appendStrings, appendString) -- foo(append([]string{}, a)) //@rank("))", appendStrings, appendInt),rank("))", appendString, appendInt),snippet("))", appendStrings, "aStrings...", "aStrings...") -- foo(append([]string{}, "", a)) //@rank("))", appendString, appendInt),rank("))", appendString, appendStrings) +- // "a.d" should be ranked above the deeper "a.b.c" +- var i int +- i = a //@deep(" //", deepAD, deepABC, deepA, deepAB) +-} - -- // Don't add "..." to append() argument. -- bar(append()) //@snippet("))", appendStrings, "aStrings", "aStrings") +-type foo struct { +- b bar +-} - -- type baz struct{} -- baz{} //@item(appendBazLiteral, "baz{}", "", "var") -- var bazzes []baz //@item(appendBazzes, "bazzes", "[]baz", "var") -- var bazzy baz //@item(appendBazzy, "bazzy", "baz", "var") -- bazzes = append(bazzes, ba) //@rank(")", appendBazzy, appendBazLiteral, appendBazzes) +-func (f foo) bar() bar { +- return f.b +-} - -- var b struct{ b []baz } -- b.b //@item(appendNestedBaz, "b.b", "[]baz", "field") -- b.b = append(b.b, b) //@rank(")", appendBazzy, appendBazLiteral, appendNestedBaz) +-func (f foo) barPtr() *bar { +- return &f.b +-} - -- var aStringsPtr *[]string //@item(appendStringsPtr, "aStringsPtr", "*[]string", "var") -- foo(append([]string{}, a)) //@snippet("))", appendStringsPtr, "*aStringsPtr...", "*aStringsPtr...") +-type bar struct{} - -- foo(append([]string{}, *a)) //@snippet("))", appendStringsPtr, "aStringsPtr...", "aStringsPtr...") +-func (b bar) valueReceiver() int { +- return 0 -} -diff -urN a/gopls/internal/lsp/testdata/arraytype/array_type.go.in b/gopls/internal/lsp/testdata/arraytype/array_type.go.in ---- a/gopls/internal/lsp/testdata/arraytype/array_type.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/arraytype/array_type.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,50 +0,0 @@ --package arraytype - --import ( -- "golang.org/lsptests/foo" --) +-func (b *bar) ptrReceiver() int { +- return 0 +-} - -func _() { - var ( -- val string //@item(atVal, "val", "string", "var") +- i int +- f foo - ) - -- // disabled - see issue #54822 -- [] // complete(" //", PackageFoo) +- f.bar().valueReceiver //@item(deepBarValue, "f.bar().valueReceiver", "func() int", "method") +- f.barPtr().ptrReceiver //@item(deepBarPtrPtr, "f.barPtr().ptrReceiver", "func() int", "method") +- f.barPtr().valueReceiver //@item(deepBarPtrValue, "f.barPtr().valueReceiver", "func() int", "method") - -- []val //@complete(" //") +- i = fbar //@fuzzy(" //", deepBarValue, deepBarPtrPtr, deepBarPtrValue) +-} - -- []foo.StructFoo //@complete(" //", StructFoo) +-func (b baz) Thing() struct{ val int } { +- return b.thing +-} - -- []foo.StructFoo(nil) //@complete("(", StructFoo) +-type baz struct { +- thing struct{ val int } +-} +- +-func (b baz) _() { +- b.Thing().val //@item(deepBazMethVal, "b.Thing().val", "int", "field") +- b.thing.val //@item(deepBazFieldVal, "b.thing.val", "int", "field") +- var _ int = bval //@rank(" //", deepBazFieldVal, deepBazMethVal) +-} +diff -urN a/gopls/internal/lsp/testdata/embeddirective/embed.txt b/gopls/internal/lsp/testdata/embeddirective/embed.txt +--- a/gopls/internal/lsp/testdata/embeddirective/embed.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/embeddirective/embed.txt 1970-01-01 08:00:00 +@@ -1 +0,0 @@ +-text +diff -urN a/gopls/internal/lsp/testdata/embeddirective/fix_import.go b/gopls/internal/lsp/testdata/embeddirective/fix_import.go +--- a/gopls/internal/lsp/testdata/embeddirective/fix_import.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/embeddirective/fix_import.go 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +-// Copyright 2023 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. - -- []*foo.StructFoo //@complete(" //", StructFoo) +-package embeddirective - -- [...]foo.StructFoo //@complete(" //", StructFoo) +-import ( +- "io" +- "os" +-) - -- [2][][4]foo.StructFoo //@complete(" //", StructFoo) +-//go:embed embed.txt //@suggestedfix("//go:embed", "quickfix", "") +-var t string - -- []struct { f []foo.StructFoo } //@complete(" }", StructFoo) +-func unused() { +- _ = os.Stdin +- _ = io.EOF -} +diff -urN a/gopls/internal/lsp/testdata/embeddirective/fix_import.go.golden b/gopls/internal/lsp/testdata/embeddirective/fix_import.go.golden +--- a/gopls/internal/lsp/testdata/embeddirective/fix_import.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/embeddirective/fix_import.go.golden 1970-01-01 08:00:00 +@@ -1,21 +0,0 @@ +--- suggestedfix_fix_import_12_1 -- +-// Copyright 2023 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. - --func _() { -- type myInt int //@item(atMyInt, "myInt", "int", "type") -- -- var mark []myInt //@item(atMark, "mark", "[]myInt", "var") +-package embeddirective - -- var s []myInt //@item(atS, "s", "[]myInt", "var") -- s = []m //@complete(" //", atMyInt) -- // disabled - see issue #54822 -- s = [] // complete(" //", atMyInt, PackageFoo) +-import ( +- _ "embed" +- "io" +- "os" +-) - -- var a [1]myInt -- a = [1]m //@complete(" //", atMyInt) +-//go:embed embed.txt //@suggestedfix("//go:embed", "quickfix", "") +-var t string - -- var ds [][]myInt -- ds = [][]m //@complete(" //", atMyInt) +-func unused() { +- _ = os.Stdin +- _ = io.EOF -} - --func _() { -- var b [0]byte //@item(atByte, "b", "[0]byte", "var") -- var _ []byte = b //@snippet(" //", atByte, "b[:]", "b[:]") --} -diff -urN a/gopls/internal/lsp/testdata/assign/assign.go.in b/gopls/internal/lsp/testdata/assign/assign.go.in ---- a/gopls/internal/lsp/testdata/assign/assign.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/assign/assign.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,26 +0,0 @@ --package assign +diff -urN a/gopls/internal/lsp/testdata/errors/errors.go b/gopls/internal/lsp/testdata/errors/errors.go +--- a/gopls/internal/lsp/testdata/errors/errors.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/errors/errors.go 1970-01-01 08:00:00 +@@ -1,10 +0,0 @@ +-package errors - --import "golang.org/lsptests/assign/internal/secret" +-import ( +- "golang.org/lsptests/types" +-) - -func _() { -- secret.Hello() -- var ( -- myInt int //@item(assignInt, "myInt", "int", "var") -- myStr string //@item(assignStr, "myStr", "string", "var") -- ) -- -- var _ string = my //@rank(" //", assignStr, assignInt) -- var _ string = //@rank(" //", assignStr, assignInt) +- bob.Bob() //@complete(".") +- types.b //@complete(" //", Bob_interface) -} +diff -urN a/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go b/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go +--- a/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go 1970-01-01 08:00:00 +@@ -1,24 +0,0 @@ +-package extract - --func _() { -- var a string = a //@complete(" //") +-type A struct { +- x int +- y int -} - --func _() { -- fooBar := fooBa //@complete(" //"),item(assignFooBar, "fooBar", "", "var") -- abc, fooBar := 123, fooBa //@complete(" //", assignFooBar) -- { -- fooBar := fooBa //@complete(" //", assignFooBar) -- } +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} -diff -urN a/gopls/internal/lsp/testdata/assign/internal/secret/secret.go b/gopls/internal/lsp/testdata/assign/internal/secret/secret.go ---- a/gopls/internal/lsp/testdata/assign/internal/secret/secret.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/assign/internal/secret/secret.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ --package secret - --func Hello() {} -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/bad/bad0.go b/gopls/internal/lsp/testdata/bad/bad0.go ---- a/gopls/internal/lsp/testdata/bad/bad0.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/bad/bad0.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ --//go:build go1.11 --// +build go1.11 -- --package bad +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - --import _ "golang.org/lsptests/assign/internal/secret" //@diag("\"golang.org/lsptests/assign/internal/secret\"", "compiler", "could not import golang.org/lsptests/assign/internal/secret \\(invalid use of internal package \"golang.org/lsptests/assign/internal/secret\"\\)", "error") +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - --func stuff() { //@item(stuff, "stuff", "func()", "func") -- x := "heeeeyyyy" -- random2(x) //@diag("x", "compiler", "cannot use x \\(variable of type string\\) as int value in argument to random2", "error") -- random2(1) //@complete("dom", random, random2, random3) -- y := 3 //@diag("y", "compiler", "y declared (and|but) not used", "error") +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") -} +diff -urN a/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go.golden b/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go.golden +--- a/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go.golden 1970-01-01 08:00:00 +@@ -1,364 +0,0 @@ +--- functionextraction_extract_basic_13_2 -- +-package extract - --type bob struct { //@item(bob, "bob", "struct{...}", "struct") +-type A struct { - x int +- y int -} - --func _() { -- var q int -- _ = &bob{ -- f: q, //@diag("f: q", "compiler", "unknown field f in struct literal", "error") -- } +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} -diff -urN a/gopls/internal/lsp/testdata/bad/bad1.go b/gopls/internal/lsp/testdata/bad/bad1.go ---- a/gopls/internal/lsp/testdata/bad/bad1.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/bad/bad1.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,34 +0,0 @@ --//go:build go1.11 --// +build go1.11 -- --package bad -- --// See #36637 --type stateFunc func() stateFunc //@item(stateFunc, "stateFunc", "func() stateFunc", "type") - --var a unknown //@item(global_a, "a", "unknown", "var"),diag("unknown", "compiler", "(undeclared name|undefined): unknown", "error") +-func (a *A) AddP() int { +- sum := newFunction(a) //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - --func random() int { //@item(random, "random", "func() int", "func") -- //@complete("", global_a, bob, random, random2, random3, stateFunc, stuff) -- return 0 +-func newFunction(a *A) int { +- sum := a.x + a.y +- return sum -} - --func random2(y int) int { //@item(random2, "random2", "func(y int) int", "func"),item(bad_y_param, "y", "int", "var") -- x := 6 //@item(x, "x", "int", "var"),diag("x", "compiler", "x declared (and|but) not used", "error") -- var q blah //@item(q, "q", "blah", "var"),diag("q", "compiler", "q declared (and|but) not used", "error"),diag("blah", "compiler", "(undeclared name|undefined): blah", "error") -- var t **blob //@item(t, "t", "**blob", "var"),diag("t", "compiler", "t declared (and|but) not used", "error"),diag("blob", "compiler", "(undeclared name|undefined): blob", "error") -- //@complete("", q, t, x, bad_y_param, global_a, bob, random, random2, random3, stateFunc, stuff) +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - -- return y +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") -} - --func random3(y ...int) { //@item(random3, "random3", "func(y ...int)", "func"),item(y_variadic_param, "y", "[]int", "var") -- //@complete("", y_variadic_param, global_a, bob, random, random2, random3, stateFunc, stuff) +--- functionextraction_extract_basic_14_2 -- +-package extract - -- var ch chan (favType1) //@item(ch, "ch", "chan (favType1)", "var"),diag("ch", "compiler", "ch declared (and|but) not used", "error"),diag("favType1", "compiler", "(undeclared name|undefined): favType1", "error") -- var m map[keyType]int //@item(m, "m", "map[keyType]int", "var"),diag("m", "compiler", "m declared (and|but) not used", "error"),diag("keyType", "compiler", "(undeclared name|undefined): keyType", "error") -- var arr []favType2 //@item(arr, "arr", "[]favType2", "var"),diag("arr", "compiler", "arr declared (and|but) not used", "error"),diag("favType2", "compiler", "(undeclared name|undefined): favType2", "error") -- var fn1 func() badResult //@item(fn1, "fn1", "func() badResult", "var"),diag("fn1", "compiler", "fn1 declared (and|but) not used", "error"),diag("badResult", "compiler", "(undeclared name|undefined): badResult", "error") -- var fn2 func(badParam) //@item(fn2, "fn2", "func(badParam)", "var"),diag("fn2", "compiler", "fn2 declared (and|but) not used", "error"),diag("badParam", "compiler", "(undeclared name|undefined): badParam", "error") -- //@complete("", arr, ch, fn1, fn2, m, y_variadic_param, global_a, bob, random, random2, random3, stateFunc, stuff) +-type A struct { +- x int +- y int -} -diff -urN a/gopls/internal/lsp/testdata/badstmt/badstmt_2.go.in b/gopls/internal/lsp/testdata/badstmt/badstmt_2.go.in ---- a/gopls/internal/lsp/testdata/badstmt/badstmt_2.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/badstmt/badstmt_2.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package badstmt -- --import ( -- "golang.org/lsptests/foo" --) - --func _() { -- defer func() { foo. } //@rank(" }", Foo) +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} -diff -urN a/gopls/internal/lsp/testdata/badstmt/badstmt_3.go.in b/gopls/internal/lsp/testdata/badstmt/badstmt_3.go.in ---- a/gopls/internal/lsp/testdata/badstmt/badstmt_3.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/badstmt/badstmt_3.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package badstmt - --import ( -- "golang.org/lsptests/foo" --) +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return newFunction(sum) //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - --func _() { -- go foo. //@rank(" //", Foo, IntFoo),snippet(" //", Foo, "Foo()", "Foo()") +-func newFunction(sum int) int { +- return sum -} -diff -urN a/gopls/internal/lsp/testdata/badstmt/badstmt_4.go.in b/gopls/internal/lsp/testdata/badstmt/badstmt_4.go.in ---- a/gopls/internal/lsp/testdata/badstmt/badstmt_4.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/badstmt/badstmt_4.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package badstmt - --import ( -- "golang.org/lsptests/foo" --) +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - --func _() { -- go func() { -- defer foo. //@rank(" //", Foo, IntFoo) -- } +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") -} -diff -urN a/gopls/internal/lsp/testdata/badstmt/badstmt.go.in b/gopls/internal/lsp/testdata/badstmt/badstmt.go.in ---- a/gopls/internal/lsp/testdata/badstmt/badstmt.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/badstmt/badstmt.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,29 +0,0 @@ --package badstmt - --import ( -- "golang.org/lsptests/foo" --) +--- functionextraction_extract_basic_18_2 -- +-package extract - --// The nonewvars expectation asserts that the go/analysis framework ran. --// See comments in noparse. +-type A struct { +- x int +- y int +-} - --func _(x int) { -- defer foo.F //@complete(" //", Foo),diag(" //", "syntax", "function must be invoked in defer statement|expression in defer must be function call", "error") -- defer foo.F //@complete(" //", Foo) -- x := 123 //@diag(":=", "nonewvars", "no new variables", "warning") +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} - --func _() { -- switch true { -- case true: -- go foo.F //@complete(" //", Foo) -- } +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") -} - --func _() { -- defer func() { -- foo.F //@complete(" //", Foo),snippet(" //", Foo, "Foo()", "Foo()") +-func (a A) XLessThanY() bool { +- return newFunction(a) //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - -- foo. //@rank(" //", Foo) -- } +-func newFunction(a A) bool { +- return a.x < a.y -} -diff -urN a/gopls/internal/lsp/testdata/bar/bar.go.in b/gopls/internal/lsp/testdata/bar/bar.go.in ---- a/gopls/internal/lsp/testdata/bar/bar.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/bar/bar.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,47 +0,0 @@ --// +build go1.11 - --package bar +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - --import ( -- "golang.org/lsptests/foo" //@item(foo, "foo", "\"golang.org/lsptests/foo\"", "package") --) +--- functionextraction_extract_basic_22_2 -- +-package extract - --func helper(i foo.IntFoo) {} //@item(helper, "helper", "func(i foo.IntFoo)", "func") +-type A struct { +- x int +- y int +-} - --func _() { -- help //@complete("l", helper) -- _ = foo.StructFoo{} //@complete("S", IntFoo, StructFoo) +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} - --// Bar is a function. --func Bar() { //@item(Bar, "Bar", "func()", "func", "Bar is a function.") -- foo.Foo() //@complete("F", Foo, IntFoo, StructFoo) -- var _ foo.IntFoo //@complete("I", IntFoo, StructFoo) -- foo.() //@complete("(", Foo, IntFoo, StructFoo) +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") -} - --func _() { -- var Valentine int //@item(Valentine, "Valentine", "int", "var") +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - -- _ = foo.StructFoo{ -- Valu //@complete(" //", Value) -- } -- _ = foo.StructFoo{ -- Va //@complete("a", Value, Valentine) -- } -- _ = foo.StructFoo{ -- Value: 5, //@complete("a", Value) -- } -- _ = foo.StructFoo{ -- //@complete("", Value, Valentine, foo, helper, Bar) -- } -- _ = foo.StructFoo{ -- Value: Valen //@complete("le", Valentine) -- } -- _ = foo.StructFoo{ -- Value: //@complete(" //", Valentine, foo, helper, Bar) -- } -- _ = foo.StructFoo{ -- Value: //@complete(" ", Valentine, foo, helper, Bar) -- } +-func (a A) Add() int { +- sum := newFunction(a) //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") -} -diff -urN a/gopls/internal/lsp/testdata/basiclit/basiclit.go b/gopls/internal/lsp/testdata/basiclit/basiclit.go ---- a/gopls/internal/lsp/testdata/basiclit/basiclit.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/basiclit/basiclit.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package basiclit - --func _() { -- var a int // something for lexical completions +-func newFunction(a A) int { +- sum := a.x + a.y +- return sum +-} - -- _ = "hello." //@complete(".") +--- functionextraction_extract_basic_23_2 -- +-package extract - -- _ = 1 //@complete(" //") +-type A struct { +- x int +- y int +-} - -- _ = 1. //@complete(".") +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - -- _ = 'a' //@complete("' ") +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") -} -diff -urN a/gopls/internal/lsp/testdata/baz/baz.go.in b/gopls/internal/lsp/testdata/baz/baz.go.in ---- a/gopls/internal/lsp/testdata/baz/baz.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/baz/baz.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,33 +0,0 @@ --// +build go1.11 - --package baz +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - --import ( -- "golang.org/lsptests/bar" +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return newFunction(sum) //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - -- f "golang.org/lsptests/foo" --) +-func newFunction(sum int) int { +- return sum +-} - --var FooStruct f.StructFoo +--- functionextraction_extract_basic_9_2 -- +-package extract - --func Baz() { -- defer bar.Bar() //@complete("B", Bar) -- // TODO(rstambler): Test completion here. -- defer bar.B -- var x f.IntFoo //@complete("n", IntFoo),typdef("x", IntFoo) -- bar.Bar() //@complete("B", Bar) +-type A struct { +- x int +- y int -} - --func _() { -- bob := f.StructFoo{Value: 5} -- if x := bob. //@complete(" //", Value) -- switch true == false { -- case true: -- if x := bob. //@complete(" //", Value) -- case false: -- } -- if x := bob.Va //@complete("a", Value) -- switch true == true { -- default: -- } +-func (a *A) XLessThanYP() bool { +- return newFunction(a) //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} -diff -urN a/gopls/internal/lsp/testdata/builtins/builtin_args.go b/gopls/internal/lsp/testdata/builtins/builtin_args.go ---- a/gopls/internal/lsp/testdata/builtins/builtin_args.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/builtins/builtin_args.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,62 +0,0 @@ --package builtins -- --func _() { -- var ( -- aSlice []int //@item(builtinSlice, "aSlice", "[]int", "var") -- aMap map[string]int //@item(builtinMap, "aMap", "map[string]int", "var") -- aString string //@item(builtinString, "aString", "string", "var") -- aArray [0]int //@item(builtinArray, "aArray", "[0]int", "var") -- aArrayPtr *[0]int //@item(builtinArrayPtr, "aArrayPtr", "*[0]int", "var") -- aChan chan int //@item(builtinChan, "aChan", "chan int", "var") -- aPtr *int //@item(builtinPtr, "aPtr", "*int", "var") -- aInt int //@item(builtinInt, "aInt", "int", "var") -- ) - -- type ( -- aSliceType []int //@item(builtinSliceType, "aSliceType", "[]int", "type") -- aChanType chan int //@item(builtinChanType, "aChanType", "chan int", "type") -- aMapType map[string]int //@item(builtinMapType, "aMapType", "map[string]int", "type") -- ) +-func newFunction(a *A) bool { +- return a.x < a.y +-} - -- close() //@rank(")", builtinChan, builtinSlice) +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - -- append() //@rank(")", builtinSlice, builtinChan) +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - -- var _ []byte = append([]byte(nil), ""...) //@rank(") //") +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - -- copy() //@rank(")", builtinSlice, builtinChan) -- copy(aSlice, aS) //@rank(")", builtinSlice, builtinString) -- copy(aS, aSlice) //@rank(",", builtinSlice, builtinString) +--- methodextraction_extract_basic_13_2 -- +-package extract - -- delete() //@rank(")", builtinMap, builtinChan) -- delete(aMap, aS) //@rank(")", builtinString, builtinSlice) +-type A struct { +- x int +- y int +-} - -- aMapFunc := func() map[int]int { //@item(builtinMapFunc, "aMapFunc", "func() map[int]int", "var") -- return nil -- } -- delete() //@rank(")", builtinMapFunc, builtinSlice) +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - -- len() //@rank(")", builtinSlice, builtinInt),rank(")", builtinMap, builtinInt),rank(")", builtinString, builtinInt),rank(")", builtinArray, builtinInt),rank(")", builtinArrayPtr, builtinPtr),rank(")", builtinChan, builtinInt) +-func (a *A) AddP() int { +- sum := a.newMethod() //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - -- cap() //@rank(")", builtinSlice, builtinMap),rank(")", builtinArray, builtinString),rank(")", builtinArrayPtr, builtinPtr),rank(")", builtinChan, builtinInt) +-func (a *A) newMethod() int { +- sum := a.x + a.y +- return sum +-} - -- make() //@rank(")", builtinMapType, int),rank(")", builtinChanType, int),rank(")", builtinSliceType, int),rank(")", builtinMapType, int) -- make(aSliceType, a) //@rank(")", builtinInt, builtinSlice) +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - -- type myInt int -- var mi myInt //@item(builtinMyInt, "mi", "myInt", "var") -- make(aSliceType, m) //@snippet(")", builtinMyInt, "mi", "mi") +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - -- var _ []int = make() //@rank(")", builtinSliceType, builtinMapType) +--- methodextraction_extract_basic_14_2 -- +-package extract - -- type myStruct struct{} //@item(builtinStructType, "myStruct", "struct{...}", "struct") -- var _ *myStruct = new() //@rank(")", builtinStructType, int) +-type A struct { +- x int +- y int +-} - -- for k := range a { //@rank(" {", builtinSlice, builtinInt),rank(" {", builtinString, builtinInt),rank(" {", builtinChan, builtinInt),rank(" {", builtinArray, builtinInt),rank(" {", builtinArrayPtr, builtinInt),rank(" {", builtinMap, builtinInt), -- } +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - -- for k, v := range a { //@rank(" {", builtinSlice, builtinChan) -- } +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return a.newMethod(sum) //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - -- <-a //@rank(" //", builtinChan, builtinInt) +-func (*A) newMethod(sum int) int { +- return sum -} -diff -urN a/gopls/internal/lsp/testdata/builtins/builtin_go117.go b/gopls/internal/lsp/testdata/builtins/builtin_go117.go ---- a/gopls/internal/lsp/testdata/builtins/builtin_go117.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/builtins/builtin_go117.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --//go:build !go1.18 --// +build !go1.18 - --package builtins +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - --func _() { -- //@complete("", append, bool, byte, cap, close, complex, complex128, complex64, copy, delete, error, _false, float32, float64, imag, int, int16, int32, int64, int8, len, make, new, panic, print, println, real, recover, rune, string, _true, uint, uint16, uint32, uint64, uint8, uintptr, _nil) +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") -} -diff -urN a/gopls/internal/lsp/testdata/builtins/builtin_go118.go b/gopls/internal/lsp/testdata/builtins/builtin_go118.go ---- a/gopls/internal/lsp/testdata/builtins/builtin_go118.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/builtins/builtin_go118.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --//go:build go1.18 && !go1.21 --// +build go1.18,!go1.21 - --package builtins +--- methodextraction_extract_basic_18_2 -- +-package extract - --func _() { -- //@complete("", any, append, bool, byte, cap, close, comparable, complex, complex128, complex64, copy, delete, error, _false, float32, float64, imag, int, int16, int32, int64, int8, len, make, new, panic, print, println, real, recover, rune, string, _true, uint, uint16, uint32, uint64, uint8, uintptr, _nil) +-type A struct { +- x int +- y int -} -diff -urN a/gopls/internal/lsp/testdata/builtins/builtin_go121.go b/gopls/internal/lsp/testdata/builtins/builtin_go121.go ---- a/gopls/internal/lsp/testdata/builtins/builtin_go121.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/builtins/builtin_go121.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --//go:build go1.21 --// +build go1.21 -- --package builtins - --func _() { -- //@complete("", any, append, bool, byte, cap, clear, close, comparable, complex, complex128, complex64, copy, delete, error, _false, float32, float64, imag, int, int16, int32, int64, int8, len, make, new, panic, print, println, real, recover, rune, string, _true, uint, uint16, uint32, uint64, uint8, uintptr, _nil) +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} -diff -urN a/gopls/internal/lsp/testdata/builtins/builtins.go b/gopls/internal/lsp/testdata/builtins/builtins.go ---- a/gopls/internal/lsp/testdata/builtins/builtins.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/builtins/builtins.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,47 +0,0 @@ --package builtins - --// Definitions of builtin completion items. +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - --/* any */ //@item(any, "any", "", "interface") --/* Create markers for builtin types. Only for use by this test. --/* append(slice []Type, elems ...Type) []Type */ //@item(append, "append", "func(slice []Type, elems ...Type) []Type", "func") --/* bool */ //@item(bool, "bool", "", "type") --/* byte */ //@item(byte, "byte", "", "type") --/* cap(v Type) int */ //@item(cap, "cap", "func(v Type) int", "func") --/* clear[T interface{ ~[]Type | ~map[Type]Type1 }](t T) */ //@item(clear, "clear", "func(t T)", "func") --/* close(c chan<- Type) */ //@item(close, "close", "func(c chan<- Type)", "func") --/* comparable */ //@item(comparable, "comparable", "", "interface") --/* complex(r float64, i float64) */ //@item(complex, "complex", "func(r float64, i float64) complex128", "func") --/* complex128 */ //@item(complex128, "complex128", "", "type") --/* complex64 */ //@item(complex64, "complex64", "", "type") --/* copy(dst []Type, src []Type) int */ //@item(copy, "copy", "func(dst []Type, src []Type) int", "func") --/* delete(m map[Type]Type1, key Type) */ //@item(delete, "delete", "func(m map[Type]Type1, key Type)", "func") --/* error */ //@item(error, "error", "", "interface") --/* false */ //@item(_false, "false", "", "const") --/* float32 */ //@item(float32, "float32", "", "type") --/* float64 */ //@item(float64, "float64", "", "type") --/* imag(c complex128) float64 */ //@item(imag, "imag", "func(c complex128) float64", "func") --/* int */ //@item(int, "int", "", "type") --/* int16 */ //@item(int16, "int16", "", "type") --/* int32 */ //@item(int32, "int32", "", "type") --/* int64 */ //@item(int64, "int64", "", "type") --/* int8 */ //@item(int8, "int8", "", "type") --/* iota */ //@item(iota, "iota", "", "const") --/* len(v Type) int */ //@item(len, "len", "func(v Type) int", "func") --/* make(t Type, size ...int) Type */ //@item(make, "make", "func(t Type, size ...int) Type", "func") --/* new(Type) *Type */ //@item(new, "new", "func(Type) *Type", "func") --/* nil */ //@item(_nil, "nil", "", "var") --/* panic(v interface{}) */ //@item(panic, "panic", "func(v interface{})", "func") --/* print(args ...Type) */ //@item(print, "print", "func(args ...Type)", "func") --/* println(args ...Type) */ //@item(println, "println", "func(args ...Type)", "func") --/* real(c complex128) float64 */ //@item(real, "real", "func(c complex128) float64", "func") --/* recover() interface{} */ //@item(recover, "recover", "func() interface{}", "func") --/* rune */ //@item(rune, "rune", "", "type") --/* string */ //@item(string, "string", "", "type") --/* true */ //@item(_true, "true", "", "const") --/* uint */ //@item(uint, "uint", "", "type") --/* uint16 */ //@item(uint16, "uint16", "", "type") --/* uint32 */ //@item(uint32, "uint32", "", "type") --/* uint64 */ //@item(uint64, "uint64", "", "type") --/* uint8 */ //@item(uint8, "uint8", "", "type") --/* uintptr */ //@item(uintptr, "uintptr", "", "type") -diff -urN a/gopls/internal/lsp/testdata/builtins/builtin_types.go b/gopls/internal/lsp/testdata/builtins/builtin_types.go ---- a/gopls/internal/lsp/testdata/builtins/builtin_types.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/builtins/builtin_types.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package builtins +-func (a A) XLessThanY() bool { +- return a.newMethod() //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - --func _() { -- var _ []bool //@item(builtinBoolSliceType, "[]bool", "[]bool", "type") +-func (a A) newMethod() bool { +- return a.x < a.y +-} - -- var _ []bool = make() //@rank(")", builtinBoolSliceType, int) +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - -- var _ []bool = make([], 0) //@rank(",", bool, int) +--- methodextraction_extract_basic_22_2 -- +-package extract - -- var _ [][]bool = make([][], 0) //@rank(",", bool, int) +-type A struct { +- x int +- y int -} -diff -urN a/gopls/internal/lsp/testdata/builtins/constants.go b/gopls/internal/lsp/testdata/builtins/constants.go ---- a/gopls/internal/lsp/testdata/builtins/constants.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/builtins/constants.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,19 +0,0 @@ --package builtins -- --func _() { -- const ( -- foo = iota //@complete(" //", iota) -- ) - -- iota //@complete(" //") +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - -- var iota int //@item(iotaVar, "iota", "int", "var") +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - -- iota //@complete(" //", iotaVar) +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} - --func _() { -- var twoRedUpEnd bool //@item(TRUEVar, "twoRedUpEnd", "bool", "var") +-func (a A) Add() int { +- sum := a.newMethod() //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - -- var _ bool = true //@rank(" //", _true, TRUEVar) +-func (a A) newMethod() int { +- sum := a.x + a.y +- return sum -} -diff -urN a/gopls/internal/lsp/testdata/callhierarchy/callhierarchy.go b/gopls/internal/lsp/testdata/callhierarchy/callhierarchy.go ---- a/gopls/internal/lsp/testdata/callhierarchy/callhierarchy.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/callhierarchy/callhierarchy.go 1970-01-01 00:00:00.000000000 +0000 -@@ -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 callhierarchy +--- methodextraction_extract_basic_23_2 -- +-package extract - --import "golang.org/lsptests/callhierarchy/outgoing" +-type A struct { +- x int +- y int +-} - --func a() { //@mark(hierarchyA, "a") -- D() +-func (a *A) XLessThanYP() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} - --func b() { //@mark(hierarchyB, "b") -- D() +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") -} - --// C is an exported function --func C() { //@mark(hierarchyC, "C") -- D() -- D() +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} - --// To test hierarchy across function literals --var x = func() { //@mark(hierarchyLiteral, "func"),mark(hierarchyLiteralOut, "x") -- D() +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return a.newMethod(sum) //@extractmethod("return", "sum"),extractfunc("return", "sum") -} - --// D is exported to test incoming/outgoing calls across packages --func D() { //@mark(hierarchyD, "D"),incomingcalls(hierarchyD, hierarchyA, hierarchyB, hierarchyC, hierarchyLiteral, incomingA),outgoingcalls(hierarchyD, hierarchyE, hierarchyF, hierarchyG, hierarchyLiteralOut, outgoingB, hierarchyFoo, hierarchyH, hierarchyI, hierarchyJ, hierarchyK) -- e() -- x() -- F() -- outgoing.B() -- foo := func() {} //@mark(hierarchyFoo, "foo"),incomingcalls(hierarchyFoo, hierarchyD),outgoingcalls(hierarchyFoo) -- foo() +-func (A) newMethod(sum int) int { +- return sum +-} - -- func() { -- g() -- }() +--- methodextraction_extract_basic_9_2 -- +-package extract - -- var i Interface = impl{} -- i.H() -- i.I() +-type A struct { +- x int +- y int +-} - -- s := Struct{} -- s.J() -- s.K() +-func (a *A) XLessThanYP() bool { +- return a.newMethod() //@extractmethod("return", "a.y"),extractfunc("return", "a.y") -} - --func e() {} //@mark(hierarchyE, "e") +-func (a *A) newMethod() bool { +- return a.x < a.y +-} - --// F is an exported function --func F() {} //@mark(hierarchyF, "F") +-func (a *A) AddP() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-} - --func g() {} //@mark(hierarchyG, "g") +-func (a A) XLessThanY() bool { +- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-} - --type Interface interface { -- H() //@mark(hierarchyH, "H") -- I() //@mark(hierarchyI, "I") +-func (a A) Add() int { +- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") +- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") -} - --type impl struct{} +diff -urN a/gopls/internal/lsp/testdata/extract/extract_method/extract_context.go b/gopls/internal/lsp/testdata/extract/extract_method/extract_context.go +--- a/gopls/internal/lsp/testdata/extract/extract_method/extract_context.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/extract/extract_method/extract_context.go 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ +-package extract - --func (i impl) H() {} --func (i impl) I() {} +-import "context" - --type Struct struct { -- J func() //@mark(hierarchyJ, "J") -- K func() //@mark(hierarchyK, "K") +-type B struct { +- x int +- y int -} -diff -urN a/gopls/internal/lsp/testdata/callhierarchy/incoming/incoming.go b/gopls/internal/lsp/testdata/callhierarchy/incoming/incoming.go ---- a/gopls/internal/lsp/testdata/callhierarchy/incoming/incoming.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/callhierarchy/incoming/incoming.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +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 incoming +-func (b *B) AddP(ctx context.Context) (int, error) { +- sum := b.x + b.y +- return sum, ctx.Err() //@extractmethod("return", "ctx.Err()"),extractfunc("return", "ctx.Err()") +-} - --import "golang.org/lsptests/callhierarchy" +-func (b *B) LongList(ctx context.Context) (int, error) { +- p1 := 1 +- p2 := 1 +- p3 := 1 +- return p1 + p2 + p3, ctx.Err() //@extractmethod("return", "ctx.Err()"),extractfunc("return", "ctx.Err()") +-} +diff -urN a/gopls/internal/lsp/testdata/extract/extract_method/extract_context.go.golden b/gopls/internal/lsp/testdata/extract/extract_method/extract_context.go.golden +--- a/gopls/internal/lsp/testdata/extract/extract_method/extract_context.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/extract/extract_method/extract_context.go.golden 1970-01-01 08:00:00 +@@ -1,52 +0,0 @@ +--- methodextraction_extract_context_12_2 -- +-package extract - --// A is exported to test incoming calls across packages --func A() { //@mark(incomingA, "A") -- callhierarchy.D() +-import "context" +- +-type B struct { +- x int +- y int -} -diff -urN a/gopls/internal/lsp/testdata/callhierarchy/outgoing/outgoing.go b/gopls/internal/lsp/testdata/callhierarchy/outgoing/outgoing.go ---- a/gopls/internal/lsp/testdata/callhierarchy/outgoing/outgoing.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/callhierarchy/outgoing/outgoing.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +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 outgoing +-func (b *B) AddP(ctx context.Context) (int, error) { +- sum := b.x + b.y +- return b.newMethod(ctx, sum) //@extractmethod("return", "ctx.Err()"),extractfunc("return", "ctx.Err()") +-} - --// B is exported to test outgoing calls across packages --func B() { //@mark(outgoingB, "B") +-func (*B) newMethod(ctx context.Context, sum int) (int, error) { +- return sum, ctx.Err() -} -diff -urN a/gopls/internal/lsp/testdata/casesensitive/casesensitive.go b/gopls/internal/lsp/testdata/casesensitive/casesensitive.go ---- a/gopls/internal/lsp/testdata/casesensitive/casesensitive.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/casesensitive/casesensitive.go 1970-01-01 00:00:00.000000000 +0000 -@@ -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 casesensitive +-func (b *B) LongList(ctx context.Context) (int, error) { +- p1 := 1 +- p2 := 1 +- p3 := 1 +- return p1 + p2 + p3, ctx.Err() //@extractmethod("return", "ctx.Err()"),extractfunc("return", "ctx.Err()") +-} - --func _() { -- var lower int //@item(lower, "lower", "int", "var") -- var Upper int //@item(upper, "Upper", "int", "var") +--- methodextraction_extract_context_19_2 -- +-package extract - -- l //@casesensitive(" //", lower) -- U //@casesensitive(" //", upper) +-import "context" - -- L //@casesensitive(" //") -- u //@casesensitive(" //") +-type B struct { +- x int +- y int -} -diff -urN a/gopls/internal/lsp/testdata/cast/cast.go.in b/gopls/internal/lsp/testdata/cast/cast.go.in ---- a/gopls/internal/lsp/testdata/cast/cast.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/cast/cast.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package cast - --func _() { -- foo := struct{x int}{x: 1} //@item(x_field, "x", "int", "field") -- _ = float64(foo.x) //@complete("x", x_field) +-func (b *B) AddP(ctx context.Context) (int, error) { +- sum := b.x + b.y +- return sum, ctx.Err() //@extractmethod("return", "ctx.Err()"),extractfunc("return", "ctx.Err()") -} - --func _() { -- foo := struct{x int}{x: 1} -- _ = float64(foo. //@complete(" /", x_field) +-func (b *B) LongList(ctx context.Context) (int, error) { +- p1 := 1 +- p2 := 1 +- p3 := 1 +- return b.newMethod(ctx, p1, p2, p3) //@extractmethod("return", "ctx.Err()"),extractfunc("return", "ctx.Err()") -} -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/cgo/declarecgo.go b/gopls/internal/lsp/testdata/cgo/declarecgo.go ---- a/gopls/internal/lsp/testdata/cgo/declarecgo.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/cgo/declarecgo.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,27 +0,0 @@ --package cgo -- --/* --#include --#include - --void myprint(char* s) { -- printf("%s\n", s); +-func (*B) newMethod(ctx context.Context, p1 int, p2 int, p3 int) (int, error) { +- return p1 + p2 + p3, ctx.Err() -} --*/ --import "C" - --import ( -- "fmt" -- "unsafe" --) +diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go b/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go +--- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go 1970-01-01 08:00:00 +@@ -1,6 +0,0 @@ +-package extract - --func Example() { //@mark(funccgoexample, "Example"),item(funccgoexample, "Example", "func()", "func") -- fmt.Println() -- cs := C.CString("Hello from stdio\n") -- C.myprint(cs) -- C.free(unsafe.Pointer(cs)) +-func _() { +- var _ = 1 + 2 //@suggestedfix("1", "refactor.extract", "") +- var _ = 3 + 4 //@suggestedfix("3 + 4", "refactor.extract", "") -} +diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go.golden b/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go.golden +--- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go.golden 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +--- suggestedfix_extract_basic_lit_4_10 -- +-package extract - -func _() { -- Example() //@godef("ample", funccgoexample),complete("ample", funccgoexample) +- x := 1 +- var _ = x + 2 //@suggestedfix("1", "refactor.extract", "") +- var _ = 3 + 4 //@suggestedfix("3 + 4", "refactor.extract", "") -} -diff -urN a/gopls/internal/lsp/testdata/cgo/declarecgo.go.golden b/gopls/internal/lsp/testdata/cgo/declarecgo.go.golden ---- a/gopls/internal/lsp/testdata/cgo/declarecgo.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/cgo/declarecgo.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,30 +0,0 @@ ---- funccgoexample-definition -- --cgo/declarecgo.go:18:6-13: defined here as ```go --func Example() --``` - --[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/cgo#Example) ---- funccgoexample-definition-json -- --{ -- "span": { -- "uri": "file://cgo/declarecgo.go", -- "start": { -- "line": 18, -- "column": 6, -- "offset": 151 -- }, -- "end": { -- "line": 18, -- "column": 13, -- "offset": 158 -- } -- }, -- "description": "```go\nfunc Example()\n```\n\n[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/cgo#Example)" +--- suggestedfix_extract_basic_lit_5_10 -- +-package extract +- +-func _() { +- var _ = 1 + 2 //@suggestedfix("1", "refactor.extract", "") +- x := 3 + 4 +- var _ = x //@suggestedfix("3 + 4", "refactor.extract", "") -} - ---- funccgoexample-hoverdef -- --```go --func Example() --``` +diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go b/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go +--- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package extract - --[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/cgo#Example) -diff -urN a/gopls/internal/lsp/testdata/cgo/declarecgo_nocgo.go b/gopls/internal/lsp/testdata/cgo/declarecgo_nocgo.go ---- a/gopls/internal/lsp/testdata/cgo/declarecgo_nocgo.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/cgo/declarecgo_nocgo.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --//+build !cgo +-import "strconv" - --package cgo +-func _() { +- x0 := append([]int{}, 1) //@suggestedfix("append([]int{}, 1)", "refactor.extract", "") +- str := "1" +- b, err := strconv.Atoi(str) //@suggestedfix("strconv.Atoi(str)", "refactor.extract", "") +-} +diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go.golden b/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go.golden +--- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go.golden 1970-01-01 08:00:00 +@@ -1,24 +0,0 @@ +--- suggestedfix_extract_func_call_6_8 -- +-package extract - --// Set a dummy marker to keep the test framework happy. The tests should be skipped. --var _ = "Example" //@mark(funccgoexample, "Example"),godef("ample", funccgoexample),complete("ample", funccgoexample) -diff -urN a/gopls/internal/lsp/testdata/cgoimport/usecgo.go.golden b/gopls/internal/lsp/testdata/cgoimport/usecgo.go.golden ---- a/gopls/internal/lsp/testdata/cgoimport/usecgo.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/cgoimport/usecgo.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,30 +0,0 @@ ---- funccgoexample-definition -- --cgo/declarecgo.go:18:6-13: defined here as ```go --func cgo.Example() --``` +-import "strconv" - --[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/cgo#Example) ---- funccgoexample-definition-json -- --{ -- "span": { -- "uri": "file://cgo/declarecgo.go", -- "start": { -- "line": 18, -- "column": 6, -- "offset": 151 -- }, -- "end": { -- "line": 18, -- "column": 13, -- "offset": 158 -- } -- }, -- "description": "```go\nfunc cgo.Example()\n```\n\n[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/cgo#Example)" +-func _() { +- x := append([]int{}, 1) +- x0 := x //@suggestedfix("append([]int{}, 1)", "refactor.extract", "") +- str := "1" +- b, err := strconv.Atoi(str) //@suggestedfix("strconv.Atoi(str)", "refactor.extract", "") -} - ---- funccgoexample-hoverdef -- --```go --func cgo.Example() --``` -- --[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/cgo#Example) -diff -urN a/gopls/internal/lsp/testdata/cgoimport/usecgo.go.in b/gopls/internal/lsp/testdata/cgoimport/usecgo.go.in ---- a/gopls/internal/lsp/testdata/cgoimport/usecgo.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/cgoimport/usecgo.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package cgoimport +--- suggestedfix_extract_func_call_8_12 -- +-package extract - --import ( -- "golang.org/lsptests/cgo" --) +-import "strconv" - -func _() { -- cgo.Example() //@godef("ample", funccgoexample),complete("ample", funccgoexample) +- x0 := append([]int{}, 1) //@suggestedfix("append([]int{}, 1)", "refactor.extract", "") +- str := "1" +- x, x1 := strconv.Atoi(str) +- b, err := x, x1 //@suggestedfix("strconv.Atoi(str)", "refactor.extract", "") -} -diff -urN a/gopls/internal/lsp/testdata/channel/channel.go b/gopls/internal/lsp/testdata/channel/channel.go ---- a/gopls/internal/lsp/testdata/channel/channel.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/channel/channel.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,25 +0,0 @@ --package channel - --func _() { -- var ( -- aa = "123" //@item(channelAA, "aa", "string", "var") -- ab = 123 //@item(channelAB, "ab", "int", "var") -- ) +diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go b/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go +--- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package extract - -- { -- type myChan chan int -- var mc myChan -- mc <- a //@complete(" //", channelAB, channelAA) -- } +-import "go/ast" - -- { -- var ac chan int //@item(channelAC, "ac", "chan int", "var") -- a <- a //@complete(" <-", channelAC, channelAA, channelAB) +-func _() { +- x0 := 0 +- if true { +- y := ast.CompositeLit{} //@suggestedfix("ast.CompositeLit{}", "refactor.extract", "") +- } +- if true { +- x1 := !false //@suggestedfix("!false", "refactor.extract", "") - } +-} +diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go.golden b/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go.golden +--- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go.golden 1970-01-01 08:00:00 +@@ -1,32 +0,0 @@ +--- suggestedfix_extract_scope_11_9 -- +-package extract - -- { -- var foo chan int //@item(channelFoo, "foo", "chan int", "var") -- wantsInt := func(int) {} //@item(channelWantsInt, "wantsInt", "func(int)", "var") -- wantsInt(<-) //@rank(")", channelFoo, channelAB) +-import "go/ast" +- +-func _() { +- x0 := 0 +- if true { +- y := ast.CompositeLit{} //@suggestedfix("ast.CompositeLit{}", "refactor.extract", "") +- } +- if true { +- x := !false +- x1 := x //@suggestedfix("!false", "refactor.extract", "") - } -} -diff -urN a/gopls/internal/lsp/testdata/codelens/codelens_test.go b/gopls/internal/lsp/testdata/codelens/codelens_test.go ---- a/gopls/internal/lsp/testdata/codelens/codelens_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/codelens/codelens_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,16 +0,0 @@ --package codelens //@codelens("package codelens", "run file benchmarks", "test") - --import "testing" +--- suggestedfix_extract_scope_8_8 -- +-package extract - --func TestMain(m *testing.M) {} // no code lens for TestMain +-import "go/ast" - --func TestFuncWithCodeLens(t *testing.T) { //@codelens("func", "run test", "test") +-func _() { +- x0 := 0 +- if true { +- x := ast.CompositeLit{} +- y := x //@suggestedfix("ast.CompositeLit{}", "refactor.extract", "") +- } +- if true { +- x1 := !false //@suggestedfix("!false", "refactor.extract", "") +- } -} - --func thisShouldNotHaveACodeLens(t *testing.T) { --} +diff -urN a/gopls/internal/lsp/testdata/fieldlist/field_list.go b/gopls/internal/lsp/testdata/fieldlist/field_list.go +--- a/gopls/internal/lsp/testdata/fieldlist/field_list.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fieldlist/field_list.go 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ +-package fieldlist - --func BenchmarkFuncWithCodeLens(b *testing.B) { //@codelens("func", "run benchmark", "test") --} +-var myInt int //@item(flVar, "myInt", "int", "var") +-type myType int //@item(flType, "myType", "int", "type") - --func helper() {} // expect no code lens -diff -urN a/gopls/internal/lsp/testdata/comment_completion/comment_completion.go.in b/gopls/internal/lsp/testdata/comment_completion/comment_completion.go.in ---- a/gopls/internal/lsp/testdata/comment_completion/comment_completion.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/comment_completion/comment_completion.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,70 +0,0 @@ --package comment_completion +-func (my) _() {} //@complete(") _", flType) +-func (my my) _() {} //@complete(" my)"),complete(") _", flType) - --var p bool +-func (myType) _() {} //@complete(") {", flType) - --//@complete(re"$") +-func (myType) _(my my) {} //@complete(" my)"),complete(") {", flType) - --func _() { -- var a int +-func (myType) _() my {} //@complete(" {", flType) - -- switch a { -- case 1: -- //@complete(re"$") -- _ = a -- } +-func (myType) _() (my my) {} //@complete(" my"),complete(") {", flType) - -- var b chan int -- select { -- case <-b: -- //@complete(re"$") -- _ = b +-func _() { +- var _ struct { +- //@complete("", flType) +- m my //@complete(" my"),complete(" //", flType) - } - -- var ( -- //@complete(re"$") -- _ = a -- ) +- var _ interface { +- //@complete("", flType) +- m() my //@complete("("),complete(" //", flType) +- } -} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/a.go b/gopls/internal/lsp/testdata/fillstruct/a.go +--- a/gopls/internal/lsp/testdata/fillstruct/a.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/a.go 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ +-package fillstruct - --// //@complete(" ", variableC) --var C string //@item(variableC, "C", "string", "var") //@complete(" ", variableC) -- --// //@complete(" ", constant) --const Constant = "example" //@item(constant, "Constant", "string", "const") //@complete(" ", constant) +-import ( +- "golang.org/lsptests/fillstruct/data" +-) - --// //@complete(" ", structType, fieldB, fieldA) --type StructType struct { //@item(structType, "StructType", "struct{...}", "struct") //@complete(" ", structType, fieldA, fieldB) -- // //@complete(" ", fieldA, structType, fieldB) -- A string //@item(fieldA, "A", "string", "field") //@complete(" ", fieldA, structType, fieldB) -- b int //@item(fieldB, "b", "int", "field") //@complete(" ", fieldB, structType, fieldA) +-type basicStruct struct { +- foo int -} - --// //@complete(" ", method, structRecv, paramX, resultY, fieldB, fieldA) --func (structType *StructType) Method(X int) (Y int) { //@item(structRecv, "structType", "*StructType", "var"),item(method, "Method", "func(X int) (Y int)", "method"),item(paramX, "X", "int", "var"),item(resultY, "Y", "int", "var") -- // //@complete(" ", method, structRecv, paramX, resultY, fieldB, fieldA) -- return +-var _ = basicStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type twoArgStruct struct { +- foo int +- bar string -} - --// //@complete(" ", newType) --type NewType string //@item(newType, "NewType", "string", "type") //@complete(" ", newType) +-var _ = twoArgStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --// //@complete(" ", testInterface, testA, testB) --type TestInterface interface { //@item(testInterface, "TestInterface", "interface{...}", "interface") -- // //@complete(" ", testA, testInterface, testB) -- TestA(L string) (M int) //@item(testA, "TestA", "func(L string) (M int)", "method"),item(paramL, "L", "var", "string"),item(resM, "M", "var", "int") //@complete(" ", testA, testInterface, testB) -- TestB(N int) bool //@item(testB, "TestB", "func(N int) bool", "method"),item(paramN, "N", "var", "int") //@complete(" ", testB, testInterface, testA) +-type nestedStruct struct { +- bar string +- basic basicStruct -} - --// //@complete(" ", function) --func Function() int { //@item(function, "Function", "func() int", "func") //@complete(" ", function) -- // //@complete(" ", function) -- return 0 --} +-var _ = nestedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --// This tests multiline block comments and completion with prefix --// Lorem Ipsum Multili//@complete("Multi", multiline) --// Lorem ipsum dolor sit ametom --func Multiline() int { //@item(multiline, "Multiline", "func() int", "func") -- // //@complete(" ", multiline) -- return 0 +-var _ = data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/fillstruct/a.go.golden b/gopls/internal/lsp/testdata/fillstruct/a.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/a.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/a.go.golden 1970-01-01 08:00:00 +@@ -1,126 +0,0 @@ +--- suggestedfix_a_11_21 -- +-package fillstruct +- +-import ( +- "golang.org/lsptests/fillstruct/data" +-) +- +-type basicStruct struct { +- foo int -} -diff -urN a/gopls/internal/lsp/testdata/complit/complit.go.in b/gopls/internal/lsp/testdata/complit/complit.go.in ---- a/gopls/internal/lsp/testdata/complit/complit.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/complit/complit.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,90 +0,0 @@ --package complit - --// general completions +-var _ = basicStruct{ +- foo: 0, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --type position struct { //@item(structPosition, "position", "struct{...}", "struct") -- X, Y int //@item(fieldX, "X", "int", "field"),item(fieldY, "Y", "int", "field") +-type twoArgStruct struct { +- foo int +- bar string -} - --func _() { -- _ = position{ -- //@complete("", fieldX, fieldY, structPosition) -- } -- _ = position{ -- X: 1, -- //@complete("", fieldY) -- } -- _ = position{ -- //@complete("", fieldX) -- Y: 1, -- } -- _ = []*position{ -- { -- //@complete("", fieldX, fieldY, structPosition) -- }, -- } +-var _ = twoArgStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type nestedStruct struct { +- bar string +- basic basicStruct -} - --func _() { -- var ( -- aa string //@item(aaVar, "aa", "string", "var") -- ab int //@item(abVar, "ab", "int", "var") -- ) +-var _ = nestedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- _ = map[int]int{ -- a: a, //@complete(":", abVar, aaVar),complete(",", abVar, aaVar) -- } +-var _ = data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- _ = map[int]int{ -- //@complete("", abVar, aaVar, structPosition) -- } +--- suggestedfix_a_18_22 -- +-package fillstruct - -- _ = []string{a: ""} //@complete(":", abVar, aaVar) -- _ = [1]string{a: ""} //@complete(":", abVar, aaVar) +-import ( +- "golang.org/lsptests/fillstruct/data" +-) - -- _ = position{X: a} //@complete("}", abVar, aaVar) -- _ = position{a} //@complete("}", abVar, aaVar) -- _ = position{a, } //@complete("}", abVar, aaVar, structPosition) +-type basicStruct struct { +- foo int +-} +- +-var _ = basicStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type twoArgStruct struct { +- foo int +- bar string +-} - -- _ = []int{a} //@complete("}", abVar, aaVar) -- _ = [1]int{a} //@complete("}", abVar, aaVar) +-var _ = twoArgStruct{ +- foo: 0, +- bar: "", +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- type myStruct struct { -- AA int //@item(fieldAA, "AA", "int", "field") -- AB string //@item(fieldAB, "AB", "string", "field") -- } +-type nestedStruct struct { +- bar string +- basic basicStruct +-} - -- _ = myStruct{ -- AB: a, //@complete(",", aaVar, abVar) -- } +-var _ = nestedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- var s myStruct +-var _ = data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- _ = map[int]string{1: "" + s.A} //@complete("}", fieldAB, fieldAA) -- _ = map[int]string{1: (func(i int) string { return "" })(s.A)} //@complete(")}", fieldAA, fieldAB) -- _ = map[int]string{1: func() string { s.A }} //@complete(" }", fieldAA, fieldAB) +--- suggestedfix_a_25_22 -- +-package fillstruct - -- _ = position{s.A} //@complete("}", fieldAA, fieldAB) +-import ( +- "golang.org/lsptests/fillstruct/data" +-) - -- var X int //@item(varX, "X", "int", "var") -- _ = position{X} //@complete("}", fieldX, varX) +-type basicStruct struct { +- foo int -} - --func _() { -- type foo struct{} //@item(complitFoo, "foo", "struct{...}", "struct") -- -- var _ *foo = &fo{} //@snippet("{", complitFoo, "foo", "foo") -- var _ *foo = fo{} //@snippet("{", complitFoo, "&foo", "&foo") +-var _ = basicStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- struct { a, b *foo }{ -- a: &fo{}, //@rank("{", complitFoo) -- b: fo{}, //@snippet("{", complitFoo, "&foo", "&foo") -- } +-type twoArgStruct struct { +- foo int +- bar string -} - --func _() { -- _ := position{ -- X: 1, //@complete("X", fieldX),complete(" 1", structPosition) -- Y: , //@complete(":", fieldY),complete(" ,", structPosition) -- } +-var _ = twoArgStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type nestedStruct struct { +- bar string +- basic basicStruct -} -diff -urN a/gopls/internal/lsp/testdata/constant/constant.go b/gopls/internal/lsp/testdata/constant/constant.go ---- a/gopls/internal/lsp/testdata/constant/constant.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/constant/constant.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ --package constant - --const x = 1 //@item(constX, "x", "int", "const") +-var _ = nestedStruct{ +- bar: "", +- basic: basicStruct{}, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --const ( -- a int = iota << 2 //@item(constA, "a", "int", "const") -- b //@item(constB, "b", "int", "const") -- c //@item(constC, "c", "int", "const") +-var _ = data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +--- suggestedfix_a_27_16 -- +-package fillstruct +- +-import ( +- "golang.org/lsptests/fillstruct/data" -) - --func _() { -- const y = "hi" //@item(constY, "y", "string", "const") -- //@complete("", constY, constA, constB, constC, constX) +-type basicStruct struct { +- foo int -} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_for.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_for.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_for.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_for.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package danglingstmt - --func _() { -- for bar //@rank(" //", danglingBar) --} +-var _ = basicStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func bar() bool { //@item(danglingBar, "bar", "func() bool", "func") -- return true +-type twoArgStruct struct { +- foo int +- bar string -} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package danglingstmt - --func _() { -- for i := bar3(); i > bar //@rank(" //", danglingBar3) --} +-var _ = twoArgStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func bar3() int { //@item(danglingBar3, "bar3", "func() int", "func") -- return 0 +-type nestedStruct struct { +- bar string +- basic basicStruct -} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond_post.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond_post.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond_post.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init_cond_post.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package danglingstmt - --func _() { -- for i := bar4(); i > bar4(); i += bar //@rank(" //", danglingBar4) --} +-var _ = nestedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func bar4() int { //@item(danglingBar4, "bar4", "func() int", "func") -- return 0 --} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_for_init.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package danglingstmt +-var _ = data.B{ +- ExportedInt: 0, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- for i := bar //@rank(" //", danglingBar2) +diff -urN a/gopls/internal/lsp/testdata/fillstruct/a2.go b/gopls/internal/lsp/testdata/fillstruct/a2.go +--- a/gopls/internal/lsp/testdata/fillstruct/a2.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/a2.go 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +-package fillstruct +- +-type typedStruct struct { +- m map[string]int +- s []int +- c chan int +- c1 <-chan int +- a [2]string -} - --func bar2() int { //@item(danglingBar2, "bar2", "func() int", "func") -- return 0 +-var _ = typedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStruct struct { +- fn func(i int) int -} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_eof.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_eof.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_eof.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_eof.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package danglingstmt - --func bar5() bool { //@item(danglingBar5, "bar5", "func() bool", "func") -- return true +-var _ = funStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStructCompex struct { +- fn func(i int, s string) (string, int) -} - --func _() { -- if b //@rank(" //", danglingBar5) -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_if.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_if.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_if.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_if.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package danglingstmt +-var _ = funStructCompex{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- if foo //@rank(" //", danglingFoo) +-type funStructEmpty struct { +- fn func() -} - --func foo() bool { //@item(danglingFoo, "foo", "func() bool", "func") -- return true +-var _ = funStructEmpty{} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/fillstruct/a2.go.golden b/gopls/internal/lsp/testdata/fillstruct/a2.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/a2.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/a2.go.golden 1970-01-01 08:00:00 +@@ -1,139 +0,0 @@ +--- suggestedfix_a2_11_21 -- +-package fillstruct +- +-type typedStruct struct { +- m map[string]int +- s []int +- c chan int +- c1 <-chan int +- a [2]string -} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init_cond.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init_cond.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init_cond.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init_cond.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package danglingstmt - --func _() { -- if i := 123; foo //@rank(" //", danglingFoo3) +-var _ = typedStruct{ +- m: map[string]int{}, +- s: []int{}, +- c: make(chan int), +- c1: make(<-chan int), +- a: [2]string{}, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStruct struct { +- fn func(i int) int -} - --func foo3() bool { //@item(danglingFoo3, "foo3", "func() bool", "func") -- return true +-var _ = funStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStructCompex struct { +- fn func(i int, s string) (string, int) -} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_if_init.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package danglingstmt - --func _() { -- if i := foo //@rank(" //", danglingFoo2) +-var _ = funStructCompex{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStructEmpty struct { +- fn func() -} - --func foo2() bool { //@item(danglingFoo2, "foo2", "func() bool", "func") -- return true +-var _ = funStructEmpty{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +--- suggestedfix_a2_17_19 -- +-package fillstruct +- +-type typedStruct struct { +- m map[string]int +- s []int +- c chan int +- c1 <-chan int +- a [2]string -} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_multiline_if.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_multiline_if.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_multiline_if.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_multiline_if.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package danglingstmt - --func walrus() bool { //@item(danglingWalrus, "walrus", "func() bool", "func") -- return true +-var _ = typedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStruct struct { +- fn func(i int) int -} - --func _() { -- if true && -- walrus //@complete(" //", danglingWalrus) +-var _ = funStruct{ +- fn: func(i int) int { +- }, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStructCompex struct { +- fn func(i int, s string) (string, int) -} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_1.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_1.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_1.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_1.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package danglingstmt - --func _() { -- x. //@rank(" //", danglingI) +-var _ = funStructCompex{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStructEmpty struct { +- fn func() -} - --var x struct { i int } //@item(danglingI, "i", "int", "field") -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_2.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_2.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_2.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_selector_2.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package danglingstmt +-var _ = funStructEmpty{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --import "golang.org/lsptests/foo" +--- suggestedfix_a2_23_25 -- +-package fillstruct - --func _() { -- foo. //@rank(" //", Foo) -- var _ = []string{foo.} //@rank("}", Foo) +-type typedStruct struct { +- m map[string]int +- s []int +- c chan int +- c1 <-chan int +- a [2]string -} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package danglingstmt - --func _() { -- switch i := baz //@rank(" //", danglingBaz) --} +-var _ = typedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func baz() int { //@item(danglingBaz, "baz", "func() int", "func") -- return 0 +-type funStruct struct { +- fn func(i int) int -} -diff -urN a/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init_tag.go b/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init_tag.go ---- a/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init_tag.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/danglingstmt/dangling_switch_init_tag.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package danglingstmt - --func _() { -- switch i := 0; baz //@rank(" //", danglingBaz2) +-var _ = funStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStructCompex struct { +- fn func(i int, s string) (string, int) -} - --func baz2() int { //@item(danglingBaz2, "baz2", "func() int", "func") -- return 0 +-var _ = funStructCompex{ +- fn: func(i int, s string) (string, int) { +- }, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStructEmpty struct { +- fn func() -} -diff -urN a/gopls/internal/lsp/testdata/deep/deep.go b/gopls/internal/lsp/testdata/deep/deep.go ---- a/gopls/internal/lsp/testdata/deep/deep.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/deep/deep.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,142 +0,0 @@ --package deep - --import "context" +-var _ = funStructEmpty{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --type deepA struct { -- b deepB //@item(deepBField, "b", "deepB", "field") +--- suggestedfix_a2_29_24 -- +-package fillstruct +- +-type typedStruct struct { +- m map[string]int +- s []int +- c chan int +- c1 <-chan int +- a [2]string -} - --type deepB struct { +-var _ = typedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStruct struct { +- fn func(i int) int -} - --func wantsDeepB(deepB) {} +-var _ = funStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- var a deepA //@item(deepAVar, "a", "deepA", "var") -- a.b //@item(deepABField, "a.b", "deepB", "field") -- wantsDeepB(a) //@deep(")", deepABField, deepAVar) +-type funStructCompex struct { +- fn func(i int, s string) (string, int) +-} - -- deepA{a} //@snippet("}", deepABField, "a.b", "a.b") +-var _ = funStructCompex{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type funStructEmpty struct { +- fn func() -} - --func wantsContext(context.Context) {} +-var _ = funStructEmpty{ +- fn: func() { +- }, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- context.Background() //@item(ctxBackground, "context.Background", "func() context.Context", "func", "Background returns a non-nil, empty Context.") -- context.TODO() //@item(ctxTODO, "context.TODO", "func() context.Context", "func", "TODO returns a non-nil, empty Context.") +diff -urN a/gopls/internal/lsp/testdata/fillstruct/a3.go b/gopls/internal/lsp/testdata/fillstruct/a3.go +--- a/gopls/internal/lsp/testdata/fillstruct/a3.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/a3.go 1970-01-01 08:00:00 +@@ -1,42 +0,0 @@ +-package fillstruct - -- wantsContext(c) //@rank(")", ctxBackground),rank(")", ctxTODO) --} +-import ( +- "go/ast" +- "go/token" +-) - --func _() { -- var cork struct{ err error } -- cork.err //@item(deepCorkErr, "cork.err", "error", "field") -- context //@item(deepContextPkg, "context", "\"context\"", "package") -- var _ error = co //@rank(" //", deepCorkErr, deepContextPkg) +-type Foo struct { +- A int -} - --func _() { -- // deepCircle is circular. -- type deepCircle struct { -- *deepCircle -- } -- var circle deepCircle //@item(deepCircle, "circle", "deepCircle", "var") -- circle.deepCircle //@item(deepCircleField, "circle.deepCircle", "*deepCircle", "field") -- var _ deepCircle = circ //@deep(" //", deepCircle, deepCircleField),snippet(" //", deepCircleField, "*circle.deepCircle", "*circle.deepCircle") +-type Bar struct { +- X *Foo +- Y *Foo -} - --func _() { -- type deepEmbedC struct { -- } -- type deepEmbedB struct { -- deepEmbedC -- } -- type deepEmbedA struct { -- deepEmbedB -- } +-var _ = Bar{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- wantsC := func(deepEmbedC) {} +-type importedStruct struct { +- m map[*ast.CompositeLit]ast.Field +- s []ast.BadExpr +- a [3]token.Token +- c chan ast.EmptyStmt +- fn func(ast_decl ast.DeclStmt) ast.Ellipsis +- st ast.CompositeLit +-} - -- var a deepEmbedA //@item(deepEmbedA, "a", "deepEmbedA", "var") -- a.deepEmbedB //@item(deepEmbedB, "a.deepEmbedB", "deepEmbedB", "field") -- a.deepEmbedC //@item(deepEmbedC, "a.deepEmbedC", "deepEmbedC", "field") -- wantsC(a) //@deep(")", deepEmbedC, deepEmbedA, deepEmbedB) +-var _ = importedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type pointerBuiltinStruct struct { +- b *bool +- s *string +- i *int -} - --func _() { -- type nested struct { -- a int -- n *nested //@item(deepNestedField, "n", "*nested", "field") -- } +-var _ = pointerBuiltinStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- nested{ -- a: 123, //@deep(" //", deepNestedField) -- } +-var _ = []ast.BasicLit{ +- {}, //@suggestedfix("}", "refactor.rewrite", "Fill") -} - --func _() { -- var a struct { -- b struct { -- c int -- } -- d int -- } +-var _ = []ast.BasicLit{{}} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/fillstruct/a3.go.golden b/gopls/internal/lsp/testdata/fillstruct/a3.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/a3.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/a3.go.golden 1970-01-01 08:00:00 +@@ -1,243 +0,0 @@ +--- suggestedfix_a3_17_13 -- +-package fillstruct - -- a.d //@item(deepAD, "a.d", "int", "field") -- a.b.c //@item(deepABC, "a.b.c", "int", "field") -- a.b //@item(deepAB, "a.b", "struct{...}", "field") -- a //@item(deepA, "a", "struct{...}", "var") +-import ( +- "go/ast" +- "go/token" +-) - -- // "a.d" should be ranked above the deeper "a.b.c" -- var i int -- i = a //@deep(" //", deepAD, deepABC, deepA, deepAB) +-type Foo struct { +- A int -} - --type foo struct { -- b bar +-type Bar struct { +- X *Foo +- Y *Foo -} - --func (f foo) bar() bar { -- return f.b +-var _ = Bar{ +- X: &Foo{}, +- Y: &Foo{}, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type importedStruct struct { +- m map[*ast.CompositeLit]ast.Field +- s []ast.BadExpr +- a [3]token.Token +- c chan ast.EmptyStmt +- fn func(ast_decl ast.DeclStmt) ast.Ellipsis +- st ast.CompositeLit -} - --func (f foo) barPtr() *bar { -- return &f.b +-var _ = importedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type pointerBuiltinStruct struct { +- b *bool +- s *string +- i *int -} - --type bar struct{} +-var _ = pointerBuiltinStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func (b bar) valueReceiver() int { -- return 0 +-var _ = []ast.BasicLit{ +- {}, //@suggestedfix("}", "refactor.rewrite", "Fill") -} - --func (b *bar) ptrReceiver() int { -- return 0 +-var _ = []ast.BasicLit{{}} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +--- suggestedfix_a3_28_24 -- +-package fillstruct +- +-import ( +- "go/ast" +- "go/token" +-) +- +-type Foo struct { +- A int -} - --func _() { -- var ( -- i int -- f foo -- ) +-type Bar struct { +- X *Foo +- Y *Foo +-} - -- f.bar().valueReceiver //@item(deepBarValue, "f.bar().valueReceiver", "func() int", "method") -- f.barPtr().ptrReceiver //@item(deepBarPtrPtr, "f.barPtr().ptrReceiver", "func() int", "method") -- f.barPtr().valueReceiver //@item(deepBarPtrValue, "f.barPtr().valueReceiver", "func() int", "method") +-var _ = Bar{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type importedStruct struct { +- m map[*ast.CompositeLit]ast.Field +- s []ast.BadExpr +- a [3]token.Token +- c chan ast.EmptyStmt +- fn func(ast_decl ast.DeclStmt) ast.Ellipsis +- st ast.CompositeLit +-} +- +-var _ = importedStruct{ +- m: map[*ast.CompositeLit]ast.Field{}, +- s: []ast.BadExpr{}, +- a: [3]token.Token{}, +- c: make(chan ast.EmptyStmt), +- fn: func(ast_decl ast.DeclStmt) ast.Ellipsis { +- }, +- st: ast.CompositeLit{}, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- i = fbar //@fuzzy(" //", deepBarValue, deepBarPtrPtr, deepBarPtrValue) +-type pointerBuiltinStruct struct { +- b *bool +- s *string +- i *int -} - --func (b baz) Thing() struct{ val int } { -- return b.thing --} +-var _ = pointerBuiltinStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --type baz struct { -- thing struct{ val int } +-var _ = []ast.BasicLit{ +- {}, //@suggestedfix("}", "refactor.rewrite", "Fill") -} - --func (b baz) _() { -- b.Thing().val //@item(deepBazMethVal, "b.Thing().val", "int", "field") -- b.thing.val //@item(deepBazFieldVal, "b.thing.val", "int", "field") -- var _ int = bval //@rank(" //", deepBazFieldVal, deepBazMethVal) --} -diff -urN a/gopls/internal/lsp/testdata/errors/errors.go b/gopls/internal/lsp/testdata/errors/errors.go ---- a/gopls/internal/lsp/testdata/errors/errors.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/errors/errors.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package errors +-var _ = []ast.BasicLit{{}} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +--- suggestedfix_a3_36_30 -- +-package fillstruct - -import ( -- "golang.org/lsptests/types" +- "go/ast" +- "go/token" -) - --func _() { -- bob.Bob() //@complete(".") -- types.b //@complete(" //", Bob_interface) +-type Foo struct { +- A int -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_args_returns.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_args_returns.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_args_returns.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_args_returns.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package extract -- --func _() { -- a := 1 -- a = 5 //@mark(exSt0, "a") -- a = a + 2 //@mark(exEn0, "2") -- //@extractfunc(exSt0, exEn0) -- b := a * 2 //@mark(exB, " b") -- _ = 3 + 4 //@mark(exEnd, "4") -- //@extractfunc(exB, exEnd) --} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_args_returns.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_args_returns.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_args_returns.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_args_returns.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,37 +0,0 @@ ---- functionextraction_extract_args_returns_5_2 -- --package extract - --func _() { -- a := 1 -- //@mark(exSt0, "a") -- a = newFunction(a) //@mark(exEn0, "2") -- //@extractfunc(exSt0, exEn0) -- b := a * 2 //@mark(exB, " b") -- _ = 3 + 4 //@mark(exEnd, "4") -- //@extractfunc(exB, exEnd) +-type Bar struct { +- X *Foo +- Y *Foo -} - --func newFunction(a int) int { -- a = 5 -- a = a + 2 -- return a +-var _ = Bar{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-type importedStruct struct { +- m map[*ast.CompositeLit]ast.Field +- s []ast.BadExpr +- a [3]token.Token +- c chan ast.EmptyStmt +- fn func(ast_decl ast.DeclStmt) ast.Ellipsis +- st ast.CompositeLit -} - ---- functionextraction_extract_args_returns_8_1 -- --package extract +-var _ = importedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- a := 1 -- a = 5 //@mark(exSt0, "a") -- a = a + 2 //@mark(exEn0, "2") -- //@extractfunc(exSt0, exEn0) -- //@mark(exB, " b") -- newFunction(a) //@mark(exEnd, "4") -- //@extractfunc(exB, exEnd) +-type pointerBuiltinStruct struct { +- b *bool +- s *string +- i *int -} - --func newFunction(a int) { -- b := a * 2 -- _ = 3 + 4 +-var _ = pointerBuiltinStruct{ +- b: new(bool), +- s: new(string), +- i: new(int), +-} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-var _ = []ast.BasicLit{ +- {}, //@suggestedfix("}", "refactor.rewrite", "Fill") -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_basic_comment.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_basic_comment.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_basic_comment.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_basic_comment.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ --package extract +-var _ = []ast.BasicLit{{}} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- a := /* comment in the middle of a line */ 1 //@mark(exSt18, "a") -- // Comment on its own line //@mark(exSt19, "Comment") -- _ = 3 + 4 //@mark(exEn18, "4"),mark(exEn19, "4"),mark(exSt20, "_") -- // Comment right after 3 + 4 +--- suggestedfix_a3_39_3 -- +-package fillstruct - -- // Comment after with space //@mark(exEn20, "Comment") +-import ( +- "go/ast" +- "go/token" +-) - -- //@extractfunc(exSt18, exEn18),extractfunc(exSt19, exEn19),extractfunc(exSt20, exEn20) +-type Foo struct { +- A int -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_basic_comment.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_basic_comment.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_basic_comment.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_basic_comment.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,57 +0,0 @@ ---- functionextraction_extract_basic_comment_4_2 -- --package extract -- --func _() { -- /* comment in the middle of a line */ -- //@mark(exSt18, "a") -- // Comment on its own line //@mark(exSt19, "Comment") -- newFunction() //@mark(exEn18, "4"),mark(exEn19, "4"),mark(exSt20, "_") -- // Comment right after 3 + 4 -- -- // Comment after with space //@mark(exEn20, "Comment") - -- //@extractfunc(exSt18, exEn18),extractfunc(exSt19, exEn19),extractfunc(exSt20, exEn20) +-type Bar struct { +- X *Foo +- Y *Foo -} - --func newFunction() { -- a := 1 +-var _ = Bar{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- _ = 3 + 4 +-type importedStruct struct { +- m map[*ast.CompositeLit]ast.Field +- s []ast.BadExpr +- a [3]token.Token +- c chan ast.EmptyStmt +- fn func(ast_decl ast.DeclStmt) ast.Ellipsis +- st ast.CompositeLit -} - ---- functionextraction_extract_basic_comment_5_5 -- --package extract -- --func _() { -- a := /* comment in the middle of a line */ 1 //@mark(exSt18, "a") -- // Comment on its own line //@mark(exSt19, "Comment") -- newFunction() //@mark(exEn18, "4"),mark(exEn19, "4"),mark(exSt20, "_") -- // Comment right after 3 + 4 -- -- // Comment after with space //@mark(exEn20, "Comment") +-var _ = importedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- //@extractfunc(exSt18, exEn18),extractfunc(exSt19, exEn19),extractfunc(exSt20, exEn20) +-type pointerBuiltinStruct struct { +- b *bool +- s *string +- i *int -} - --func newFunction() { -- _ = 3 + 4 +-var _ = pointerBuiltinStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +-var _ = []ast.BasicLit{ +- { +- ValuePos: 0, +- Kind: 0, +- Value: "", +- }, //@suggestedfix("}", "refactor.rewrite", "Fill") -} - ---- functionextraction_extract_basic_comment_6_2 -- --package extract +-var _ = []ast.BasicLit{{}} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- a := /* comment in the middle of a line */ 1 //@mark(exSt18, "a") -- // Comment on its own line //@mark(exSt19, "Comment") -- newFunction() //@mark(exEn18, "4"),mark(exEn19, "4"),mark(exSt20, "_") -- // Comment right after 3 + 4 +--- suggestedfix_a3_42_25 -- +-package fillstruct - -- // Comment after with space //@mark(exEn20, "Comment") +-import ( +- "go/ast" +- "go/token" +-) - -- //@extractfunc(exSt18, exEn18),extractfunc(exSt19, exEn19),extractfunc(exSt20, exEn20) +-type Foo struct { +- A int -} - --func newFunction() { -- _ = 3 + 4 +-type Bar struct { +- X *Foo +- Y *Foo -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_basic.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_basic.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_basic.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_basic.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package extract -- --func _() { //@mark(exSt25, "{") -- a := 1 //@mark(exSt1, "a") -- _ = 3 + 4 //@mark(exEn1, "4") -- //@extractfunc(exSt1, exEn1) -- //@extractfunc(exSt25, exEn25) --} //@mark(exEn25, "}") -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_basic.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_basic.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_basic.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_basic.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,30 +0,0 @@ ---- functionextraction_extract_basic_3_10 -- --package extract +-var _ = Bar{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { //@mark(exSt25, "{") -- //@mark(exSt1, "a") -- newFunction() //@mark(exEn1, "4") -- //@extractfunc(exSt1, exEn1) -- //@extractfunc(exSt25, exEn25) +-type importedStruct struct { +- m map[*ast.CompositeLit]ast.Field +- s []ast.BadExpr +- a [3]token.Token +- c chan ast.EmptyStmt +- fn func(ast_decl ast.DeclStmt) ast.Ellipsis +- st ast.CompositeLit -} - --func newFunction() { -- a := 1 -- _ = 3 + 4 --} //@mark(exEn25, "}") +-var _ = importedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - ---- functionextraction_extract_basic_4_2 -- --package extract +-type pointerBuiltinStruct struct { +- b *bool +- s *string +- i *int +-} +- +-var _ = pointerBuiltinStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { //@mark(exSt25, "{") -- //@mark(exSt1, "a") -- newFunction() //@mark(exEn1, "4") -- //@extractfunc(exSt1, exEn1) -- //@extractfunc(exSt25, exEn25) +-var _ = []ast.BasicLit{ +- {}, //@suggestedfix("}", "refactor.rewrite", "Fill") -} - --func newFunction() { -- a := 1 -- _ = 3 + 4 --} //@mark(exEn25, "}") +-var _ = []ast.BasicLit{{ +- ValuePos: 0, +- Kind: 0, +- Value: "", +-}} //@suggestedfix("}", "refactor.rewrite", "Fill") - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_issue_44813.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_issue_44813.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_issue_44813.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_issue_44813.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package extract +diff -urN a/gopls/internal/lsp/testdata/fillstruct/a4.go b/gopls/internal/lsp/testdata/fillstruct/a4.go +--- a/gopls/internal/lsp/testdata/fillstruct/a4.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/a4.go 1970-01-01 08:00:00 +@@ -1,39 +0,0 @@ +-package fillstruct - --import "fmt" +-import "go/ast" - --func main() { -- x := []rune{} //@mark(exSt9, "x") -- s := "HELLO" -- for _, c := range s { -- x = append(x, c) -- } //@mark(exEn9, "}") -- //@extractfunc(exSt9, exEn9) -- fmt.Printf("%x\n", x) +-type iStruct struct { +- X int -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_issue_44813.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_issue_44813.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_issue_44813.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_issue_44813.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,21 +0,0 @@ ---- functionextraction_extract_issue_44813_6_2 -- --package extract - --import "fmt" +-type sStruct struct { +- str string +-} - --func main() { -- //@mark(exSt9, "x") -- x := newFunction() //@mark(exEn9, "}") -- //@extractfunc(exSt9, exEn9) -- fmt.Printf("%x\n", x) +-type multiFill struct { +- num int +- strin string +- arr []int -} - --func newFunction() []rune { -- x := []rune{} -- s := "HELLO" -- for _, c := range s { -- x = append(x, c) -- } -- return x +-type assignStruct struct { +- n ast.Node -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_redefine.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_redefine.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_redefine.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_redefine.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package extract +-func fill() { +- var x int +- var _ = iStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --import "strconv" +- var s string +- var _ = sStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- i, err := strconv.Atoi("1") -- u, err := strconv.Atoi("2") //@extractfunc("u", ")") -- if i == u || err == nil { -- return +- var n int +- _ = []int{} +- if true { +- arr := []int{1, 2} - } +- var _ = multiFill{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +- var node *ast.CompositeLit +- var _ = assignStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_redefine.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_redefine.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_redefine.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_redefine.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ ---- functionextraction_extract_redefine_7_2 -- --package extract +diff -urN a/gopls/internal/lsp/testdata/fillstruct/a4.go.golden b/gopls/internal/lsp/testdata/fillstruct/a4.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/a4.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/a4.go.golden 1970-01-01 08:00:00 +@@ -1,174 +0,0 @@ +--- suggestedfix_a4_25_18 -- +-package fillstruct - --import "strconv" +-import "go/ast" - --func _() { -- i, err := strconv.Atoi("1") -- u, err := newFunction() //@extractfunc("u", ")") -- if i == u || err == nil { -- return -- } +-type iStruct struct { +- X int -} - --func newFunction() (int, error) { -- u, err := strconv.Atoi("2") -- return u, err +-type sStruct struct { +- str string -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package extract -- --func _() bool { -- x := 1 -- if x == 0 { //@mark(exSt2, "if") -- return true -- } //@mark(exEn2, "}") -- return false -- //@extractfunc(exSt2, exEn2) +-type multiFill struct { +- num int +- strin string +- arr []int -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,21 +0,0 @@ ---- functionextraction_extract_return_basic_5_2 -- --package extract - --func _() bool { -- x := 1 -- //@mark(exSt2, "if") -- shouldReturn, returnValue := newFunction(x) -- if shouldReturn { -- return returnValue -- } //@mark(exEn2, "}") -- return false -- //@extractfunc(exSt2, exEn2) +-type assignStruct struct { +- n ast.Node -} - --func newFunction(x int) (bool, bool) { -- if x == 0 { -- return true, true -- } -- return false, false --} +-func fill() { +- var x int +- var _ = iStruct{ +- X: x, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic_nonnested.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic_nonnested.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic_nonnested.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic_nonnested.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package extract +- var s string +- var _ = sStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() bool { -- x := 1 //@mark(exSt13, "x") -- if x == 0 { -- return true +- var n int +- _ = []int{} +- if true { +- arr := []int{1, 2} - } -- return false //@mark(exEn13, "false") -- //@extractfunc(exSt13, exEn13) --} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic_nonnested.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic_nonnested.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic_nonnested.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_basic_nonnested.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,17 +0,0 @@ ---- functionextraction_extract_return_basic_nonnested_4_2 -- --package extract -- --func _() bool { -- //@mark(exSt13, "x") -- return newFunction() //@mark(exEn13, "false") -- //@extractfunc(exSt13, exEn13) --} +- var _ = multiFill{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func newFunction() bool { -- x := 1 -- if x == 0 { -- return true -- } -- return false +- var node *ast.CompositeLit +- var _ = assignStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,17 +0,0 @@ --package extract +--- suggestedfix_a4_28_18 -- +-package fillstruct - --import "fmt" +-import "go/ast" - --func _() (int, string, error) { -- x := 1 -- y := "hello" -- z := "bye" //@mark(exSt3, "z") -- if y == z { -- return x, y, fmt.Errorf("same") -- } else { -- z = "hi" -- return x, z, nil -- } //@mark(exEn3, "}") -- return x, z, nil -- //@extractfunc(exSt3, exEn3) +-type iStruct struct { +- X int -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,28 +0,0 @@ ---- functionextraction_extract_return_complex_8_2 -- --package extract -- --import "fmt" - --func _() (int, string, error) { -- x := 1 -- y := "hello" -- //@mark(exSt3, "z") -- z, shouldReturn, returnValue, returnValue1, returnValue2 := newFunction(y, x) -- if shouldReturn { -- return returnValue, returnValue1, returnValue2 -- } //@mark(exEn3, "}") -- return x, z, nil -- //@extractfunc(exSt3, exEn3) +-type sStruct struct { +- str string -} - --func newFunction(y string, x int) (string, bool, int, string, error) { -- z := "bye" -- if y == z { -- return "", true, x, y, fmt.Errorf("same") -- } else { -- z = "hi" -- return "", true, x, z, nil -- } -- return z, false, 0, "", nil +-type multiFill struct { +- num int +- strin string +- arr []int -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex_nonnested.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex_nonnested.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex_nonnested.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex_nonnested.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,17 +0,0 @@ --package extract -- --import "fmt" -- --func _() (int, string, error) { -- x := 1 -- y := "hello" -- z := "bye" //@mark(exSt10, "z") -- if y == z { -- return x, y, fmt.Errorf("same") -- } else { -- z = "hi" -- return x, z, nil -- } -- return x, z, nil //@mark(exEn10, "nil") -- //@extractfunc(exSt10, exEn10) +-type assignStruct struct { +- n ast.Node -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex_nonnested.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex_nonnested.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex_nonnested.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_complex_nonnested.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ ---- functionextraction_extract_return_complex_nonnested_8_2 -- --package extract - --import "fmt" +-func fill() { +- var x int +- var _ = iStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() (int, string, error) { -- x := 1 -- y := "hello" -- //@mark(exSt10, "z") -- return newFunction(y, x) //@mark(exEn10, "nil") -- //@extractfunc(exSt10, exEn10) --} +- var s string +- var _ = sStruct{ +- str: s, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") - --func newFunction(y string, x int) (int, string, error) { -- z := "bye" -- if y == z { -- return x, y, fmt.Errorf("same") -- } else { -- z = "hi" -- return x, z, nil +- var n int +- _ = []int{} +- if true { +- arr := []int{1, 2} - } -- return x, z, nil +- var _ = multiFill{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +- var node *ast.CompositeLit +- var _ = assignStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package extract +--- suggestedfix_a4_35_20 -- +-package fillstruct - -import "go/ast" - --func _() { -- ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool { -- if n == nil { //@mark(exSt4, "if") -- return true -- } //@mark(exEn4, "}") -- return false -- }) -- //@extractfunc(exSt4, exEn4) +-type iStruct struct { +- X int -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ ---- functionextraction_extract_return_func_lit_7_3 -- --package extract -- --import "go/ast" - --func _() { -- ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool { -- //@mark(exSt4, "if") -- shouldReturn, returnValue := newFunction(n) -- if shouldReturn { -- return returnValue -- } //@mark(exEn4, "}") -- return false -- }) -- //@extractfunc(exSt4, exEn4) +-type sStruct struct { +- str string -} - --func newFunction(n ast.Node) (bool, bool) { -- if n == nil { -- return true, true -- } -- return false, false +-type multiFill struct { +- num int +- strin string +- arr []int -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit_nonnested.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit_nonnested.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit_nonnested.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit_nonnested.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package extract -- --import "go/ast" -- --func _() { -- ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool { -- if n == nil { //@mark(exSt11, "if") -- return true -- } -- return false //@mark(exEn11, "false") -- }) -- //@extractfunc(exSt11, exEn11) +-type assignStruct struct { +- n ast.Node -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit_nonnested.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit_nonnested.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit_nonnested.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_func_lit_nonnested.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,20 +0,0 @@ ---- functionextraction_extract_return_func_lit_nonnested_7_3 -- --package extract - --import "go/ast" +-func fill() { +- var x int +- var _ = iStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool { -- //@mark(exSt11, "if") -- return newFunction(n) //@mark(exEn11, "false") -- }) -- //@extractfunc(exSt11, exEn11) --} +- var s string +- var _ = sStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func newFunction(n ast.Node) bool { -- if n == nil { -- return true +- var n int +- _ = []int{} +- if true { +- arr := []int{1, 2} - } -- return false +- var _ = multiFill{ +- num: n, +- strin: s, +- arr: []int{}, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") +- +- var node *ast.CompositeLit +- var _ = assignStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ --package extract +--- suggestedfix_a4_38_23 -- +-package fillstruct - --func _() string { -- x := 1 -- if x == 0 { //@mark(exSt5, "if") -- x = 3 -- return "a" -- } //@mark(exEn5, "}") -- x = 2 -- return "b" -- //@extractfunc(exSt5, exEn5) --} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,23 +0,0 @@ ---- functionextraction_extract_return_init_5_2 -- --package extract +-import "go/ast" - --func _() string { -- x := 1 -- //@mark(exSt5, "if") -- shouldReturn, returnValue := newFunction(x) -- if shouldReturn { -- return returnValue -- } //@mark(exEn5, "}") -- x = 2 -- return "b" -- //@extractfunc(exSt5, exEn5) +-type iStruct struct { +- X int -} - --func newFunction(x int) (bool, string) { -- if x == 0 { -- x = 3 -- return true, "a" -- } -- return false, "" +-type sStruct struct { +- str string -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init_nonnested.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init_nonnested.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init_nonnested.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init_nonnested.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ --package extract -- --func _() string { -- x := 1 -- if x == 0 { //@mark(exSt12, "if") -- x = 3 -- return "a" -- } -- x = 2 -- return "b" //@mark(exEn12, "\"b\"") -- //@extractfunc(exSt12, exEn12) +-type multiFill struct { +- num int +- strin string +- arr []int -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init_nonnested.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init_nonnested.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init_nonnested.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_return_init_nonnested.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,19 +0,0 @@ ---- functionextraction_extract_return_init_nonnested_5_2 -- --package extract - --func _() string { -- x := 1 -- //@mark(exSt12, "if") -- return newFunction(x) //@mark(exEn12, "\"b\"") -- //@extractfunc(exSt12, exEn12) +-type assignStruct struct { +- n ast.Node -} - --func newFunction(x int) string { -- if x == 0 { -- x = 3 -- return "a" +-func fill() { +- var x int +- var _ = iStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +- var s string +- var _ = sStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +- var n int +- _ = []int{} +- if true { +- arr := []int{1, 2} - } -- x = 2 -- return "b" +- var _ = multiFill{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- +- var node *ast.CompositeLit +- var _ = assignStruct{ +- n: node, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_scope.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_scope.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_scope.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_scope.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package extract +diff -urN a/gopls/internal/lsp/testdata/fillstruct/data/a.go b/gopls/internal/lsp/testdata/fillstruct/data/a.go +--- a/gopls/internal/lsp/testdata/fillstruct/data/a.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/data/a.go 1970-01-01 08:00:00 +@@ -1,6 +0,0 @@ +-package data +- +-type B struct { +- ExportedInt int +- unexportedInt int +-} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct.go +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct.go 1970-01-01 08:00:00 +@@ -1,26 +0,0 @@ +-package fillstruct - --func _() { -- newFunction := 1 -- a := newFunction //@extractfunc("a", "newFunction") +-type StructA struct { +- unexportedIntField int +- ExportedIntField int +- MapA map[int]string +- Array []int +- StructB -} - --func newFunction1() int { -- return 1 +-type StructA2 struct { +- B *StructB -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_scope.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_scope.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_scope.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_scope.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,16 +0,0 @@ ---- functionextraction_extract_scope_5_2 -- --package extract - --func _() { -- newFunction := 1 -- newFunction2(newFunction) //@extractfunc("a", "newFunction") +-type StructA3 struct { +- B StructB -} - --func newFunction2(newFunction int) { -- a := newFunction +-func fill() { +- a := StructA{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- b := StructA2{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- c := StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- if true { +- _ = StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- } -} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct.go.golden 1970-01-01 08:00:00 +@@ -1,124 +0,0 @@ +--- suggestedfix_fill_struct_20_15 -- +-package fillstruct - --func newFunction1() int { -- return 1 +-type StructA struct { +- unexportedIntField int +- ExportedIntField int +- MapA map[int]string +- Array []int +- StructB -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_initialization.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_initialization.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_initialization.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_initialization.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package extract -- --func _() { -- var a []int -- a = append(a, 2) //@mark(exSt6, "a") -- b := 4 //@mark(exEn6, "4") -- //@extractfunc(exSt6, exEn6) -- a = append(a, b) +-type StructA2 struct { +- B *StructB -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_initialization.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_initialization.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_initialization.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_initialization.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,17 +0,0 @@ ---- functionextraction_extract_smart_initialization_5_2 -- --package extract - --func _() { -- var a []int -- //@mark(exSt6, "a") -- a, b := newFunction(a) //@mark(exEn6, "4") -- //@extractfunc(exSt6, exEn6) -- a = append(a, b) +-type StructA3 struct { +- B StructB -} - --func newFunction(a []int) ([]int, int) { -- a = append(a, 2) -- b := 4 -- return a, b +-func fill() { +- a := StructA{ +- unexportedIntField: 0, +- ExportedIntField: 0, +- MapA: map[int]string{}, +- Array: []int{}, +- StructB: StructB{}, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") +- b := StructA2{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- c := StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- if true { +- _ = StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- } -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_return.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_return.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_return.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_return.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package extract +--- suggestedfix_fill_struct_21_16 -- +-package fillstruct - --func _() { -- var b []int -- var a int -- a = 2 //@mark(exSt7, "a") -- b = []int{} -- b = append(b, a) //@mark(exEn7, ")") -- b[0] = 1 -- //@extractfunc(exSt7, exEn7) +-type StructA struct { +- unexportedIntField int +- ExportedIntField int +- MapA map[int]string +- Array []int +- StructB -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_return.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_return.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_return.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_smart_return.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,19 +0,0 @@ ---- functionextraction_extract_smart_return_6_2 -- --package extract - --func _() { -- var b []int -- var a int -- //@mark(exSt7, "a") -- b = newFunction(a, b) //@mark(exEn7, ")") -- b[0] = 1 -- //@extractfunc(exSt7, exEn7) +-type StructA2 struct { +- B *StructB -} - --func newFunction(a int, b []int) []int { -- a = 2 -- b = []int{} -- b = append(b, a) -- return b +-type StructA3 struct { +- B StructB -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_unnecessary_param.go b/gopls/internal/lsp/testdata/extract/extract_function/extract_unnecessary_param.go ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_unnecessary_param.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_unnecessary_param.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ --package extract -- --func _() { -- var b []int -- var a int -- a := 2 //@mark(exSt8, "a") -- b = []int{} -- b = append(b, a) //@mark(exEn8, ")") -- b[0] = 1 -- if a == 2 { -- return +-func fill() { +- a := StructA{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- b := StructA2{ +- B: &StructB{}, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") +- c := StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- if true { +- _ = StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") - } -- //@extractfunc(exSt8, exEn8) -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_function/extract_unnecessary_param.go.golden b/gopls/internal/lsp/testdata/extract/extract_function/extract_unnecessary_param.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_function/extract_unnecessary_param.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_function/extract_unnecessary_param.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,22 +0,0 @@ ---- functionextraction_extract_unnecessary_param_6_2 -- --package extract - --func _() { -- var b []int -- var a int -- //@mark(exSt8, "a") -- a, b = newFunction(b) //@mark(exEn8, ")") -- b[0] = 1 -- if a == 2 { -- return -- } -- //@extractfunc(exSt8, exEn8) --} +--- suggestedfix_fill_struct_22_16 -- +-package fillstruct - --func newFunction(b []int) (int, []int) { -- a := 2 -- b = []int{} -- b = append(b, a) -- return a, b +-type StructA struct { +- unexportedIntField int +- ExportedIntField int +- MapA map[int]string +- Array []int +- StructB -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go b/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go ---- a/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ --package extract -- --type A struct { -- x int -- y int +-type StructA2 struct { +- B *StructB -} - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-type StructA3 struct { +- B StructB -} - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-func fill() { +- a := StructA{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- b := StructA2{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- c := StructA3{ +- B: StructB{}, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") +- if true { +- _ = StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- } -} - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +--- suggestedfix_fill_struct_24_16 -- +-package fillstruct +- +-type StructA struct { +- unexportedIntField int +- ExportedIntField int +- MapA map[int]string +- Array []int +- StructB -} - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-type StructA2 struct { +- B *StructB -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go.golden b/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_method/extract_basic.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,364 +0,0 @@ ---- functionextraction_extract_basic_13_2 -- --package extract - --type A struct { -- x int -- y int +-type StructA3 struct { +- B StructB -} - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-func fill() { +- a := StructA{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- b := StructA2{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- c := StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- if true { +- _ = StructA3{ +- B: StructB{}, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") +- } -} - --func (a *A) AddP() int { -- sum := newFunction(a) //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +-package fillstruct +- +-type StructAnon struct { +- a struct{} +- b map[string]interface{} +- c map[string]struct { +- d int +- e bool +- } -} - --func newFunction(a *A) int { -- sum := a.x + a.y -- return sum +-func fill() { +- _ := StructAnon{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go.golden 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ +--- suggestedfix_fill_struct_anon_13_18 -- +-package fillstruct - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-type StructAnon struct { +- a struct{} +- b map[string]interface{} +- c map[string]struct { +- d int +- e bool +- } -} - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-func fill() { +- _ := StructAnon{ +- a: struct{}{}, +- b: map[string]interface{}{}, +- c: map[string]struct{d int; e bool}{}, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") -} - ---- functionextraction_extract_basic_14_2 -- --package extract +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +-package fillstruct - --type A struct { -- x int -- y int +-type StructB struct { +- StructC -} - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-type StructC struct { +- unexportedInt int -} - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return newFunction(sum) //@extractmethod("return", "sum"),extractfunc("return", "sum") +-func nested() { +- c := StructB{ +- StructC: StructC{}, //@suggestedfix("}", "refactor.rewrite", "Fill") +- } -} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go.golden 1970-01-01 08:00:00 +@@ -1,19 +0,0 @@ +--- suggestedfix_fill_struct_nested_13_20 -- +-package fillstruct - --func newFunction(sum int) int { -- return sum +-type StructB struct { +- StructC -} - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-type StructC struct { +- unexportedInt int -} - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-func nested() { +- c := StructB{ +- StructC: StructC{ +- unexportedInt: 0, +- }, //@suggestedfix("}", "refactor.rewrite", "Fill") +- } -} - ---- functionextraction_extract_basic_18_2 -- --package extract +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +-package fillstruct - --type A struct { -- x int -- y int --} +-import ( +- h2 "net/http" - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") --} +- "golang.org/lsptests/fillstruct/data" +-) - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-func unexported() { +- a := data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- _ = h2.Client{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go.golden 1970-01-01 08:00:00 +@@ -1,36 +0,0 @@ +--- suggestedfix_fill_struct_package_10_14 -- +-package fillstruct - --func (a A) XLessThanY() bool { -- return newFunction(a) //@extractmethod("return", "a.y"),extractfunc("return", "a.y") --} +-import ( +- h2 "net/http" - --func newFunction(a A) bool { -- return a.x < a.y --} +- "golang.org/lsptests/fillstruct/data" +-) - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-func unexported() { +- a := data.B{ +- ExportedInt: 0, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") +- _ = h2.Client{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} - ---- functionextraction_extract_basic_22_2 -- --package extract +--- suggestedfix_fill_struct_package_11_16 -- +-package fillstruct - --type A struct { -- x int -- y int --} +-import ( +- h2 "net/http" - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") --} +- "golang.org/lsptests/fillstruct/data" +-) - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-func unexported() { +- a := data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- _ = h2.Client{ +- Transport: nil, +- CheckRedirect: func(req *h2.Request, via []*h2.Request) error { +- }, +- Jar: nil, +- Timeout: 0, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") -} - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") --} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go 1970-01-01 08:00:00 +@@ -1,24 +0,0 @@ +-package fillstruct - --func (a A) Add() int { -- sum := newFunction(a) //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-type StructPartialA struct { +- PrefilledInt int +- UnfilledInt int +- StructPartialB -} - --func newFunction(a A) int { -- sum := a.x + a.y -- return sum +-type StructPartialB struct { +- PrefilledInt int +- UnfilledInt int -} - ---- functionextraction_extract_basic_23_2 -- --package extract +-func fill() { +- a := StructPartialA{ +- PrefilledInt: 5, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") +- b := StructPartialB{ +- /* this comment should disappear */ +- PrefilledInt: 7, // This comment should be blown away. +- /* As should +- this one */ +- } //@suggestedfix("}", "refactor.rewrite", "Fill") +-} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go.golden 1970-01-01 08:00:00 +@@ -1,52 +0,0 @@ +--- suggestedfix_fill_struct_partial_17_2 -- +-package fillstruct - --type A struct { -- x int -- y int +-type StructPartialA struct { +- PrefilledInt int +- UnfilledInt int +- StructPartialB -} - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-type StructPartialB struct { +- PrefilledInt int +- UnfilledInt int -} - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-func fill() { +- a := StructPartialA{ +- PrefilledInt: 5, +- UnfilledInt: 0, +- StructPartialB: StructPartialB{}, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") +- b := StructPartialB{ +- /* this comment should disappear */ +- PrefilledInt: 7, // This comment should be blown away. +- /* As should +- this one */ +- } //@suggestedfix("}", "refactor.rewrite", "Fill") -} - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +--- suggestedfix_fill_struct_partial_23_2 -- +-package fillstruct +- +-type StructPartialA struct { +- PrefilledInt int +- UnfilledInt int +- StructPartialB -} - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return newFunction(sum) //@extractmethod("return", "sum"),extractfunc("return", "sum") +-type StructPartialB struct { +- PrefilledInt int +- UnfilledInt int -} - --func newFunction(sum int) int { -- return sum +-func fill() { +- a := StructPartialA{ +- PrefilledInt: 5, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") +- b := StructPartialB{ +- PrefilledInt: 7, +- UnfilledInt: 0, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") -} - ---- functionextraction_extract_basic_9_2 -- --package extract -- --type A struct { -- x int -- y int --} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package fillstruct - --func (a *A) XLessThanYP() bool { -- return newFunction(a) //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-type StructD struct { +- ExportedIntField int -} - --func newFunction(a *A) bool { -- return a.x < a.y +-func spaces() { +- d := StructD{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go.golden 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +--- suggestedfix_fill_struct_spaces_8_15 -- +-package fillstruct - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-type StructD struct { +- ExportedIntField int -} - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-func spaces() { +- d := StructD{ +- ExportedIntField: 0, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") -} - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") --} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +-package fillstruct - ---- methodextraction_extract_basic_13_2 -- --package extract +-import "unsafe" - --type A struct { +-type unsafeStruct struct { - x int -- y int +- p unsafe.Pointer -} - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-func fill() { +- _ := unsafeStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go.golden 1970-01-01 08:00:00 +@@ -1,17 +0,0 @@ +--- suggestedfix_fill_struct_unsafe_11_20 -- +-package fillstruct - --func (a *A) AddP() int { -- sum := a.newMethod() //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") --} +-import "unsafe" - --func (a *A) newMethod() int { -- sum := a.x + a.y -- return sum +-type unsafeStruct struct { +- x int +- p unsafe.Pointer -} - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-func fill() { +- _ := unsafeStruct{ +- x: 0, +- p: nil, +- } //@suggestedfix("}", "refactor.rewrite", "Fill") -} - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") --} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/typeparams.go b/gopls/internal/lsp/testdata/fillstruct/typeparams.go +--- a/gopls/internal/lsp/testdata/fillstruct/typeparams.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/typeparams.go 1970-01-01 08:00:00 +@@ -1,37 +0,0 @@ +-//go:build go1.18 +-// +build go1.18 - ---- methodextraction_extract_basic_14_2 -- --package extract +-package fillstruct - --type A struct { -- x int -- y int --} +-type emptyStructWithTypeParams[A any] struct{} - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") --} +-var _ = emptyStructWithTypeParams[int]{} // no suggested fix - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return a.newMethod(sum) //@extractmethod("return", "sum"),extractfunc("return", "sum") +-type basicStructWithTypeParams[T any] struct { +- foo T -} - --func (*A) newMethod(sum int) int { -- return sum --} +-var _ = basicStructWithTypeParams[int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-type twoArgStructWithTypeParams[F, B any] struct { +- foo F +- bar B -} - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") --} +-var _ = twoArgStructWithTypeParams[string, int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") - ---- methodextraction_extract_basic_18_2 -- --package extract +-var _ = twoArgStructWithTypeParams[int, string]{ +- bar: "bar", +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --type A struct { -- x int -- y int +-type nestedStructWithTypeParams struct { +- bar string +- basic basicStructWithTypeParams[int] -} - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") --} +-var _ = nestedStructWithTypeParams{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-func _[T any]() { +- type S struct{ t T } +- _ = S{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} +diff -urN a/gopls/internal/lsp/testdata/fillstruct/typeparams.go.golden b/gopls/internal/lsp/testdata/fillstruct/typeparams.go.golden +--- a/gopls/internal/lsp/testdata/fillstruct/typeparams.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fillstruct/typeparams.go.golden 1970-01-01 08:00:00 +@@ -1,206 +0,0 @@ +--- suggestedfix_typeparams_14_40 -- +-//go:build go1.18 +-// +build go1.18 - --func (a A) XLessThanY() bool { -- return a.newMethod() //@extractmethod("return", "a.y"),extractfunc("return", "a.y") --} +-package fillstruct - --func (a A) newMethod() bool { -- return a.x < a.y --} +-type emptyStructWithTypeParams[A any] struct{} - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-var _ = emptyStructWithTypeParams[int]{} // no suggested fix +- +-type basicStructWithTypeParams[T any] struct { +- foo T -} - ---- methodextraction_extract_basic_22_2 -- --package extract +-var _ = basicStructWithTypeParams[int]{ +- foo: 0, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --type A struct { -- x int -- y int +-type twoArgStructWithTypeParams[F, B any] struct { +- foo F +- bar B -} - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") --} +-var _ = twoArgStructWithTypeParams[string, int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") --} +-var _ = twoArgStructWithTypeParams[int, string]{ +- bar: "bar", +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-type nestedStructWithTypeParams struct { +- bar string +- basic basicStructWithTypeParams[int] -} - --func (a A) Add() int { -- sum := a.newMethod() //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") --} +-var _ = nestedStructWithTypeParams{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func (a A) newMethod() int { -- sum := a.x + a.y -- return sum +-func _[T any]() { +- type S struct{ t T } +- _ = S{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} - ---- methodextraction_extract_basic_23_2 -- --package extract +--- suggestedfix_typeparams_21_49 -- +-//go:build go1.18 +-// +build go1.18 - --type A struct { -- x int -- y int --} +-package fillstruct - --func (a *A) XLessThanYP() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") --} +-type emptyStructWithTypeParams[A any] struct{} - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") --} +-var _ = emptyStructWithTypeParams[int]{} // no suggested fix - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-type basicStructWithTypeParams[T any] struct { +- foo T -} - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return a.newMethod(sum) //@extractmethod("return", "sum"),extractfunc("return", "sum") --} +-var _ = basicStructWithTypeParams[int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func (A) newMethod(sum int) int { -- return sum +-type twoArgStructWithTypeParams[F, B any] struct { +- foo F +- bar B -} - ---- methodextraction_extract_basic_9_2 -- --package extract +-var _ = twoArgStructWithTypeParams[string, int]{ +- foo: "", +- bar: 0, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --type A struct { -- x int -- y int --} +-var _ = twoArgStructWithTypeParams[int, string]{ +- bar: "bar", +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func (a *A) XLessThanYP() bool { -- return a.newMethod() //@extractmethod("return", "a.y"),extractfunc("return", "a.y") +-type nestedStructWithTypeParams struct { +- bar string +- basic basicStructWithTypeParams[int] -} - --func (a *A) newMethod() bool { -- return a.x < a.y --} +-var _ = nestedStructWithTypeParams{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func (a *A) AddP() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") +-func _[T any]() { +- type S struct{ t T } +- _ = S{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} - --func (a A) XLessThanY() bool { -- return a.x < a.y //@extractmethod("return", "a.y"),extractfunc("return", "a.y") --} +--- suggestedfix_typeparams_25_1 -- +-//go:build go1.18 +-// +build go1.18 - --func (a A) Add() int { -- sum := a.x + a.y //@extractmethod("sum", "a.y"),extractfunc("sum", "a.y") -- return sum //@extractmethod("return", "sum"),extractfunc("return", "sum") --} +-package fillstruct - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go b/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go ---- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --package extract +-type emptyStructWithTypeParams[A any] struct{} - --func _() { -- var _ = 1 + 2 //@suggestedfix("1", "refactor.extract", "") -- var _ = 3 + 4 //@suggestedfix("3 + 4", "refactor.extract", "") --} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go.golden b/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_basic_lit.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ ---- suggestedfix_extract_basic_lit_4_10 -- --package extract +-var _ = emptyStructWithTypeParams[int]{} // no suggested fix - --func _() { -- x := 1 -- var _ = x + 2 //@suggestedfix("1", "refactor.extract", "") -- var _ = 3 + 4 //@suggestedfix("3 + 4", "refactor.extract", "") +-type basicStructWithTypeParams[T any] struct { +- foo T -} - ---- suggestedfix_extract_basic_lit_5_10 -- --package extract +-var _ = basicStructWithTypeParams[int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- var _ = 1 + 2 //@suggestedfix("1", "refactor.extract", "") -- x := 3 + 4 -- var _ = x //@suggestedfix("3 + 4", "refactor.extract", "") +-type twoArgStructWithTypeParams[F, B any] struct { +- foo F +- bar B -} - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go b/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go ---- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package extract +-var _ = twoArgStructWithTypeParams[string, int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --import "strconv" +-var _ = twoArgStructWithTypeParams[int, string]{ +- foo: 0, +- bar: "bar", +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- x0 := append([]int{}, 1) //@suggestedfix("append([]int{}, 1)", "refactor.extract", "") -- str := "1" -- b, err := strconv.Atoi(str) //@suggestedfix("strconv.Atoi(str)", "refactor.extract", "") +-type nestedStructWithTypeParams struct { +- bar string +- basic basicStructWithTypeParams[int] -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go.golden b/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_func_call.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ ---- suggestedfix_extract_func_call_6_8 -- --package extract - --import "strconv" +-var _ = nestedStructWithTypeParams{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- x := append([]int{}, 1) -- x0 := x //@suggestedfix("append([]int{}, 1)", "refactor.extract", "") -- str := "1" -- b, err := strconv.Atoi(str) //@suggestedfix("strconv.Atoi(str)", "refactor.extract", "") +-func _[T any]() { +- type S struct{ t T } +- _ = S{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} - ---- suggestedfix_extract_func_call_8_12 -- --package extract -- --import "strconv" +--- suggestedfix_typeparams_32_36 -- +-//go:build go1.18 +-// +build go1.18 - --func _() { -- x0 := append([]int{}, 1) //@suggestedfix("append([]int{}, 1)", "refactor.extract", "") -- str := "1" -- x, x1 := strconv.Atoi(str) -- b, err := x, x1 //@suggestedfix("strconv.Atoi(str)", "refactor.extract", "") --} +-package fillstruct - -diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go b/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go ---- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package extract +-type emptyStructWithTypeParams[A any] struct{} - --import "go/ast" +-var _ = emptyStructWithTypeParams[int]{} // no suggested fix - --func _() { -- x0 := 0 -- if true { -- y := ast.CompositeLit{} //@suggestedfix("ast.CompositeLit{}", "refactor.extract", "") -- } -- if true { -- x1 := !false //@suggestedfix("!false", "refactor.extract", "") -- } +-type basicStructWithTypeParams[T any] struct { +- foo T -} -diff -urN a/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go.golden b/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go.golden ---- a/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/extract/extract_variable/extract_scope.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,32 +0,0 @@ ---- suggestedfix_extract_scope_11_9 -- --package extract - --import "go/ast" +-var _ = basicStructWithTypeParams[int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- x0 := 0 -- if true { -- y := ast.CompositeLit{} //@suggestedfix("ast.CompositeLit{}", "refactor.extract", "") -- } -- if true { -- x := !false -- x1 := x //@suggestedfix("!false", "refactor.extract", "") -- } +-type twoArgStructWithTypeParams[F, B any] struct { +- foo F +- bar B -} - ---- suggestedfix_extract_scope_8_8 -- --package extract +-var _ = twoArgStructWithTypeParams[string, int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --import "go/ast" +-var _ = twoArgStructWithTypeParams[int, string]{ +- bar: "bar", +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --func _() { -- x0 := 0 -- if true { -- x := ast.CompositeLit{} -- y := x //@suggestedfix("ast.CompositeLit{}", "refactor.extract", "") -- } -- if true { -- x1 := !false //@suggestedfix("!false", "refactor.extract", "") -- } +-type nestedStructWithTypeParams struct { +- bar string +- basic basicStructWithTypeParams[int] -} - -diff -urN a/gopls/internal/lsp/testdata/fieldlist/field_list.go b/gopls/internal/lsp/testdata/fieldlist/field_list.go ---- a/gopls/internal/lsp/testdata/fieldlist/field_list.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fieldlist/field_list.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,27 +0,0 @@ --package fieldlist +-var _ = nestedStructWithTypeParams{ +- bar: "", +- basic: basicStructWithTypeParams{}, +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --var myInt int //@item(flVar, "myInt", "int", "var") --type myType int //@item(flType, "myType", "int", "type") +-func _[T any]() { +- type S struct{ t T } +- _ = S{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-} - --func (my) _() {} //@complete(") _", flType) --func (my my) _() {} //@complete(" my)"),complete(") _", flType) +--- suggestedfix_typeparams_36_8 -- +-//go:build go1.18 +-// +build go1.18 - --func (myType) _() {} //@complete(") {", flType) +-package fillstruct - --func (myType) _(my my) {} //@complete(" my)"),complete(") {", flType) +-type emptyStructWithTypeParams[A any] struct{} - --func (myType) _() my {} //@complete(" {", flType) +-var _ = emptyStructWithTypeParams[int]{} // no suggested fix - --func (myType) _() (my my) {} //@complete(" my"),complete(") {", flType) +-type basicStructWithTypeParams[T any] struct { +- foo T +-} - --func _() { -- var _ struct { -- //@complete("", flType) -- m my //@complete(" my"),complete(" //", flType) -- } +-var _ = basicStructWithTypeParams[int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- var _ interface { -- //@complete("", flType) -- m() my //@complete("("),complete(" //", flType) -- } +-type twoArgStructWithTypeParams[F, B any] struct { +- foo F +- bar B -} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/a2.go b/gopls/internal/lsp/testdata/fillstruct/a2.go ---- a/gopls/internal/lsp/testdata/fillstruct/a2.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/a2.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,29 +0,0 @@ --package fillstruct - --type typedStruct struct { -- m map[string]int -- s []int -- c chan int -- c1 <-chan int -- a [2]string --} +-var _ = twoArgStructWithTypeParams[string, int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --var _ = typedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-var _ = twoArgStructWithTypeParams[int, string]{ +- bar: "bar", +-} //@suggestedfix("}", "refactor.rewrite", "Fill") - --type funStruct struct { -- fn func(i int) int +-type nestedStructWithTypeParams struct { +- bar string +- basic basicStructWithTypeParams[int] -} - --var _ = funStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-var _ = nestedStructWithTypeParams{} //@suggestedfix("}", "refactor.rewrite", "Fill") - --type funStructCompex struct { -- fn func(i int, s string) (string, int) +-func _[T any]() { +- type S struct{ t T } +- _ = S{ +- t: *new(T), +- } //@suggestedfix("}", "refactor.rewrite", "Fill") -} - --var _ = funStructCompex{} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/func_rank/func_rank.go.in b/gopls/internal/lsp/testdata/func_rank/func_rank.go.in +--- a/gopls/internal/lsp/testdata/func_rank/func_rank.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/func_rank/func_rank.go.in 1970-01-01 08:00:00 +@@ -1,70 +0,0 @@ +-package func_rank - --type funStructEmpty struct { -- fn func() --} +-import "net/http" - --var _ = funStructEmpty{} //@suggestedfix("}", "refactor.rewrite", "Fill") -diff -urN a/gopls/internal/lsp/testdata/fillstruct/a2.go.golden b/gopls/internal/lsp/testdata/fillstruct/a2.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/a2.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/a2.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,139 +0,0 @@ ---- suggestedfix_a2_11_21 -- --package fillstruct +-var stringAVar = "var" //@item(stringAVar, "stringAVar", "string", "var") +-func stringBFunc() string { return "str" } //@item(stringBFunc, "stringBFunc", "func() string", "func") +-type stringer struct{} //@item(stringer, "stringer", "struct{...}", "struct") - --type typedStruct struct { -- m map[string]int -- s []int -- c chan int -- c1 <-chan int -- a [2]string --} +-func _() stringer //@complete("tr", stringer) +- +-func _(val stringer) {} //@complete("tr", stringer) +- +-func (stringer) _() {} //@complete("tr", stringer) +- +-func _() { +- var s struct { +- AA int //@item(rankAA, "AA", "int", "field") +- AB string //@item(rankAB, "AB", "string", "field") +- AC int //@item(rankAC, "AC", "int", "field") +- } +- fnStr := func(string) {} +- fnStr(s.A) //@complete(")", rankAB, rankAA, rankAC) +- fnStr("" + s.A) //@complete(")", rankAB, rankAA, rankAC) - --var _ = typedStruct{ -- m: map[string]int{}, -- s: []int{}, -- c: make(chan int), -- c1: make(<-chan int), -- a: [2]string{}, --} //@suggestedfix("}", "refactor.rewrite", "Fill") +- fnInt := func(int) {} +- fnInt(-s.A) //@complete(")", rankAA, rankAC, rankAB) - --type funStruct struct { -- fn func(i int) int --} +- // no expected type +- fnInt(func() int { s.A }) //@complete(" }", rankAA, rankAB, rankAC) +- fnInt(s.A()) //@complete("()", rankAA, rankAC, rankAB) +- fnInt([]int{}[s.A]) //@complete("])", rankAA, rankAC, rankAB) +- fnInt([]int{}[:s.A]) //@complete("])", rankAA, rankAC, rankAB) - --var _ = funStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- fnInt(s.A.(int)) //@complete(".(", rankAA, rankAC, rankAB) - --type funStructCompex struct { -- fn func(i int, s string) (string, int) --} +- fnPtr := func(*string) {} +- fnPtr(&s.A) //@complete(")", rankAB, rankAA, rankAC) - --var _ = funStructCompex{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- var aaPtr *string //@item(rankAAPtr, "aaPtr", "*string", "var") +- var abPtr *int //@item(rankABPtr, "abPtr", "*int", "var") +- fnInt(*a) //@complete(")", rankABPtr, rankAAPtr) - --type funStructEmpty struct { -- fn func() +- _ = func() string { +- return s.A //@complete(" //", rankAB, rankAA, rankAC) +- } -} - --var _ = funStructEmpty{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- ---- suggestedfix_a2_17_19 -- --package fillstruct +-type foo struct { +- fooPrivateField int //@item(rankFooPrivField, "fooPrivateField", "int", "field") +- FooPublicField int //@item(rankFooPubField, "FooPublicField", "int", "field") +-} - --type typedStruct struct { -- m map[string]int -- s []int -- c chan int -- c1 <-chan int -- a [2]string +-func (foo) fooPrivateMethod() int { //@item(rankFooPrivMeth, "fooPrivateMethod", "func() int", "method") +- return 0 -} - --var _ = typedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func (foo) FooPublicMethod() int { //@item(rankFooPubMeth, "FooPublicMethod", "func() int", "method") +- return 0 +-} - --type funStruct struct { -- fn func(i int) int +-func _() { +- var _ int = foo{}. //@rank(" //", rankFooPrivField, rankFooPubField),rank(" //", rankFooPrivMeth, rankFooPubMeth),rank(" //", rankFooPrivField, rankFooPrivMeth) -} - --var _ = funStruct{ -- fn: func(i int) int { -- }, --} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func _() { +- HandleFunc //@item(httpHandleFunc, "HandleFunc", "func(pattern string, handler func(http.ResponseWriter, *http.Request))", "func") +- HandlerFunc //@item(httpHandlerFunc, "HandlerFunc", "func(http.ResponseWriter, *http.Request)", "type") - --type funStructCompex struct { -- fn func(i int, s string) (string, int) +- http.HandleFunc //@rank(" //", httpHandleFunc, httpHandlerFunc) -} +diff -urN a/gopls/internal/lsp/testdata/funcsig/func_sig.go b/gopls/internal/lsp/testdata/funcsig/func_sig.go +--- a/gopls/internal/lsp/testdata/funcsig/func_sig.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/funcsig/func_sig.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package funcsig - --var _ = funStructCompex{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-type someType int //@item(sigSomeType, "someType", "int", "type") - --type funStructEmpty struct { -- fn func() +-// Don't complete "foo" in signature. +-func (foo someType) _() { //@item(sigFoo, "foo", "someType", "var"),complete(") {", sigSomeType) +- +- //@complete("", sigFoo, sigSomeType) -} +diff -urN a/gopls/internal/lsp/testdata/funcvalue/func_value.go b/gopls/internal/lsp/testdata/funcvalue/func_value.go +--- a/gopls/internal/lsp/testdata/funcvalue/func_value.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/funcvalue/func_value.go 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ +-package funcvalue - --var _ = funStructEmpty{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func fooFunc() int { //@item(fvFooFunc, "fooFunc", "func() int", "func") +- return 0 +-} - ---- suggestedfix_a2_23_25 -- --package fillstruct +-var _ = fooFunc() //@item(fvFooFuncCall, "fooFunc", "func() int", "func") - --type typedStruct struct { -- m map[string]int -- s []int -- c chan int -- c1 <-chan int -- a [2]string +-var fooVar = func() int { //@item(fvFooVar, "fooVar", "func() int", "var") +- return 0 -} - --var _ = typedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-var _ = fooVar() //@item(fvFooVarCall, "fooVar", "func() int", "var") - --type funStruct struct { -- fn func(i int) int --} +-type myFunc func() int - --var _ = funStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-var fooType myFunc = fooVar //@item(fvFooType, "fooType", "myFunc", "var") - --type funStructCompex struct { -- fn func(i int, s string) (string, int) --} +-var _ = fooType() //@item(fvFooTypeCall, "fooType", "func() int", "var") - --var _ = funStructCompex{ -- fn: func(i int, s string) (string, int) { -- }, --} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func _() { +- var f func() int +- f = foo //@complete(" //", fvFooFunc, fvFooType, fvFooVar) - --type funStructEmpty struct { -- fn func() +- var i int +- i = foo //@complete(" //", fvFooFuncCall, fvFooTypeCall, fvFooVarCall) -} +diff -urN a/gopls/internal/lsp/testdata/fuzzymatch/fuzzymatch.go b/gopls/internal/lsp/testdata/fuzzymatch/fuzzymatch.go +--- a/gopls/internal/lsp/testdata/fuzzymatch/fuzzymatch.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/fuzzymatch/fuzzymatch.go 1970-01-01 08:00:00 +@@ -1,48 +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. - --var _ = funStructEmpty{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-package fuzzy - ---- suggestedfix_a2_29_24 -- --package fillstruct +-func _() { +- var a struct { +- fabar int +- fooBar string +- } - --type typedStruct struct { -- m map[string]int -- s []int -- c chan int -- c1 <-chan int -- a [2]string --} +- a.fabar //@item(fuzzFabarField, "a.fabar", "int", "field") +- a.fooBar //@item(fuzzFooBarField, "a.fooBar", "string", "field") - --var _ = typedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- afa //@fuzzy(" //", fuzzFabarField, fuzzFooBarField) +- afb //@fuzzy(" //", fuzzFooBarField, fuzzFabarField) - --type funStruct struct { -- fn func(i int) int --} +- fab //@fuzzy(" //", fuzzFabarField) - --var _ = funStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- var myString string +- myString = af //@fuzzy(" //", fuzzFooBarField, fuzzFabarField) - --type funStructCompex struct { -- fn func(i int, s string) (string, int) --} +- var b struct { +- c struct { +- d struct { +- e struct { +- abc string +- } +- abc float32 +- } +- abc bool +- } +- abc int +- } - --var _ = funStructCompex{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- b.abc //@item(fuzzABCInt, "b.abc", "int", "field") +- b.c.abc //@item(fuzzABCbool, "b.c.abc", "bool", "field") +- b.c.d.abc //@item(fuzzABCfloat, "b.c.d.abc", "float32", "field") +- b.c.d.e.abc //@item(fuzzABCstring, "b.c.d.e.abc", "string", "field") - --type funStructEmpty struct { -- fn func() --} +- // in depth order by default +- abc //@fuzzy(" //", fuzzABCInt, fuzzABCbool, fuzzABCfloat) - --var _ = funStructEmpty{ -- fn: func() { -- }, --} //@suggestedfix("}", "refactor.rewrite", "Fill") +- // deep candidate that matches expected type should still ranked first +- var s string +- s = abc //@fuzzy(" //", fuzzABCstring, fuzzABCInt, fuzzABCbool) +-} +diff -urN a/gopls/internal/lsp/testdata/good/good0.go b/gopls/internal/lsp/testdata/good/good0.go +--- a/gopls/internal/lsp/testdata/good/good0.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/good/good0.go 1970-01-01 08:00:00 +@@ -1,6 +0,0 @@ +-package good - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/a3.go b/gopls/internal/lsp/testdata/fillstruct/a3.go ---- a/gopls/internal/lsp/testdata/fillstruct/a3.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/a3.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,42 +0,0 @@ --package fillstruct +-func stuff() { //@item(good_stuff, "stuff", "func()", "func"),prepare("stu", "stuff", "stuff") +- x := 5 +- random2(x) //@prepare("dom", "random2", "random2") +-} +diff -urN a/gopls/internal/lsp/testdata/good/good1.go b/gopls/internal/lsp/testdata/good/good1.go +--- a/gopls/internal/lsp/testdata/good/good1.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/good/good1.go 1970-01-01 08:00:00 +@@ -1,21 +0,0 @@ +-package good - -import ( -- "go/ast" -- "go/token" +- "golang.org/lsptests/types" //@item(types_import, "types", "\"golang.org/lsptests/types\"", "package") -) - --type Foo struct { -- A int +-func random() int { //@item(good_random, "random", "func() int", "func") +- _ = "random() int" //@prepare("random", "", "") +- y := 6 + 7 //@prepare("7", "", "") +- return y //@prepare("return", "","") -} - --type Bar struct { -- X *Foo -- Y *Foo +-func random2(y int) int { //@item(good_random2, "random2", "func(y int) int", "func"),item(good_y_param, "y", "int", "var") +- //@complete("", good_y_param, types_import, good_random, good_random2, good_stuff) +- var b types.Bob = &types.X{} //@prepare("ypes","types", "types") +- if _, ok := b.(*types.X); ok { //@complete("X", X_struct, Y_struct, Bob_interface, CoolAlias) +- _ = 0 // suppress "empty branch" diagnostic +- } +- +- return y -} +diff -urN a/gopls/internal/lsp/testdata/importedcomplit/imported_complit.go.in b/gopls/internal/lsp/testdata/importedcomplit/imported_complit.go.in +--- a/gopls/internal/lsp/testdata/importedcomplit/imported_complit.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/importedcomplit/imported_complit.go.in 1970-01-01 08:00:00 +@@ -1,54 +0,0 @@ +-package importedcomplit - --var _ = Bar{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import ( +- // TODO(rfindley): re-enable after moving to new framework +- // "golang.org/lsptests/foo" - --type importedStruct struct { -- m map[*ast.CompositeLit]ast.Field -- s []ast.BadExpr -- a [3]token.Token -- c chan ast.EmptyStmt -- fn func(ast_decl ast.DeclStmt) ast.Ellipsis -- st ast.CompositeLit --} +- // import completions (separate blocks to avoid comment alignment) +- "crypto/elli" //@complete("\" //", cryptoImport) - --var _ = importedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- "fm" //@complete("\" //", fmtImport) - --type pointerBuiltinStruct struct { -- b *bool -- s *string -- i *int --} +- "go/pars" //@complete("\" //", parserImport) - --var _ = pointerBuiltinStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- namedParser "go/pars" //@complete("\" //", parserImport) - --var _ = []ast.BasicLit{ -- {}, //@suggestedfix("}", "refactor.rewrite", "Fill") --} +- "golang.org/lspte" //@complete("\" //", lsptestsImport) - --var _ = []ast.BasicLit{{}} //@suggestedfix("}", "refactor.rewrite", "Fill") -diff -urN a/gopls/internal/lsp/testdata/fillstruct/a3.go.golden b/gopls/internal/lsp/testdata/fillstruct/a3.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/a3.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/a3.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,243 +0,0 @@ ---- suggestedfix_a3_17_13 -- --package fillstruct +- "golang.org/lsptests/sign" //@complete("\" //", signatureImport) - --import ( -- "go/ast" -- "go/token" +- "golang.org/lsptests/sign" //@complete("ests", lsptestsImport) +- +- "golang.org/lsptests/signa" //@complete("na\" //", signatureImport) -) - --type Foo struct { -- A int --} +-func _() { +- var V int //@item(icVVar, "V", "int", "var") - --type Bar struct { -- X *Foo -- Y *Foo +- // TODO(rfindley): re-enable after moving to new framework +- // _ = foo.StructFoo{V} // complete("}", Value, icVVar) -} - --var _ = Bar{ -- X: &Foo{}, -- Y: &Foo{}, --} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func _() { +- var ( +- aa string //@item(icAAVar, "aa", "string", "var") +- ab int //@item(icABVar, "ab", "int", "var") +- ) - --type importedStruct struct { -- m map[*ast.CompositeLit]ast.Field -- s []ast.BadExpr -- a [3]token.Token -- c chan ast.EmptyStmt -- fn func(ast_decl ast.DeclStmt) ast.Ellipsis -- st ast.CompositeLit --} +- // TODO(rfindley): re-enable after moving to new framework +- // _ = foo.StructFoo{a} // complete("}", abVar, aaVar) - --var _ = importedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- var s struct { +- AA string //@item(icFieldAA, "AA", "string", "field") +- AB int //@item(icFieldAB, "AB", "int", "field") +- } - --type pointerBuiltinStruct struct { -- b *bool -- s *string -- i *int +- // TODO(rfindley): re-enable after moving to new framework +- //_ = foo.StructFoo{s.} // complete("}", icFieldAB, icFieldAA) -} - --var _ = pointerBuiltinStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-/* "fmt" */ //@item(fmtImport, "fmt", "\"fmt\"", "package") +-/* "go/parser" */ //@item(parserImport, "parser", "\"go/parser\"", "package") +-/* "golang.org/lsptests/signature" */ //@item(signatureImport, "signature", "\"golang.org/lsptests/signature\"", "package") +-/* "golang.org/lsptests/" */ //@item(lsptestsImport, "lsptests/", "\"golang.org/lsptests/\"", "package") +-/* "crypto/elliptic" */ //@item(cryptoImport, "elliptic", "\"crypto/elliptic\"", "package") +diff -urN a/gopls/internal/lsp/testdata/index/index.go b/gopls/internal/lsp/testdata/index/index.go +--- a/gopls/internal/lsp/testdata/index/index.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/index/index.go 1970-01-01 08:00:00 +@@ -1,25 +0,0 @@ +-package index - --var _ = []ast.BasicLit{ -- {}, //@suggestedfix("}", "refactor.rewrite", "Fill") --} +-func _() { +- var ( +- aa = "123" //@item(indexAA, "aa", "string", "var") +- ab = 123 //@item(indexAB, "ab", "int", "var") +- ) - --var _ = []ast.BasicLit{{}} //@suggestedfix("}", "refactor.rewrite", "Fill") +- var foo [1]int +- foo[a] //@complete("]", indexAB, indexAA) +- foo[:a] //@complete("]", indexAB, indexAA) +- a[:a] //@complete("[", indexAA, indexAB) +- a[a] //@complete("[", indexAA, indexAB) - ---- suggestedfix_a3_28_24 -- --package fillstruct +- var bar map[string]int +- bar[a] //@complete("]", indexAA, indexAB) - --import ( -- "go/ast" -- "go/token" --) +- type myMap map[string]int +- var baz myMap +- baz[a] //@complete("]", indexAA, indexAB) - --type Foo struct { -- A int +- type myInt int +- var mi myInt //@item(indexMyInt, "mi", "myInt", "var") +- foo[m] //@snippet("]", indexMyInt, "mi", "mi") -} +diff -urN a/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go b/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go +--- a/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ +-package inlayHint //@inlayHint("package") - --type Bar struct { -- X *Foo -- Y *Foo --} +-import "fmt" - --var _ = Bar{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func fieldNames() { +- for _, c := range []struct { +- in, want string +- }{ +- struct{ in, want string }{"Hello, world", "dlrow ,olleH"}, +- {"Hello, 世界", "界世 ,olleH"}, +- {"", ""}, +- } { +- fmt.Println(c.in == c.want) +- } +-} - --type importedStruct struct { -- m map[*ast.CompositeLit]ast.Field -- s []ast.BadExpr -- a [3]token.Token -- c chan ast.EmptyStmt -- fn func(ast_decl ast.DeclStmt) ast.Ellipsis -- st ast.CompositeLit +-func fieldNamesPointers() { +- for _, c := range []*struct { +- in, want string +- }{ +- &struct{ in, want string }{"Hello, world", "dlrow ,olleH"}, +- {"Hello, 世界", "界世 ,olleH"}, +- {"", ""}, +- } { +- fmt.Println(c.in == c.want) +- } -} +diff -urN a/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go.golden b/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go.golden +--- a/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go.golden 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +--- inlayHint -- +-package inlayHint //@inlayHint("package") - --var _ = importedStruct{ -- m: map[*ast.CompositeLit]ast.Field{}, -- s: []ast.BadExpr{}, -- a: [3]token.Token{}, -- c: make(chan ast.EmptyStmt), -- fn: func(ast_decl ast.DeclStmt) ast.Ellipsis { -- }, -- st: ast.CompositeLit{}, --} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import "fmt" - --type pointerBuiltinStruct struct { -- b *bool -- s *string -- i *int +-func fieldNames() { +- for _< int>, c< struct{in string; want string}> := range []struct { +- in, want string +- }{ +- struct{ in, want string }{"Hello, world", "dlrow ,olleH"}, +- {"Hello, 世界", "界世 ,olleH"}, +- {"", ""}, +- } { +- fmt.Println(c.in == c.want) +- } -} - --var _ = pointerBuiltinStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --var _ = []ast.BasicLit{ -- {}, //@suggestedfix("}", "refactor.rewrite", "Fill") +-func fieldNamesPointers() { +- for _< int>, c< *struct{in string; want string}> := range []*struct { +- in, want string +- }{ +- &struct{ in, want string }{"Hello, world", "dlrow ,olleH"}, +- <&struct{in string; want string}>{"Hello, 世界", "界世 ,olleH"}, +- <&struct{in string; want string}>{"", ""}, +- } { +- fmt.Println(c.in == c.want) +- } -} - --var _ = []ast.BasicLit{{}} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/inlay_hint/constant_values.go b/gopls/internal/lsp/testdata/inlay_hint/constant_values.go +--- a/gopls/internal/lsp/testdata/inlay_hint/constant_values.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/inlay_hint/constant_values.go 1970-01-01 08:00:00 +@@ -1,45 +0,0 @@ +-package inlayHint //@inlayHint("package") - ---- suggestedfix_a3_36_30 -- --package fillstruct +-const True = true - --import ( -- "go/ast" -- "go/token" --) +-type Kind int - --type Foo struct { -- A int --} +-const ( +- KindNone Kind = iota +- KindPrint +- KindPrintf +- KindErrorf +-) - --type Bar struct { -- X *Foo -- Y *Foo --} +-const ( +- u = iota * 4 +- v float64 = iota * 42 +- w = iota * 42 +-) - --var _ = Bar{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-const ( +- a, b = 1, 2 +- c, d +- e, f = 5 * 5, "hello" + "world" +- g, h +- i, j = true, f +-) - --type importedStruct struct { -- m map[*ast.CompositeLit]ast.Field -- s []ast.BadExpr -- a [3]token.Token -- c chan ast.EmptyStmt -- fn func(ast_decl ast.DeclStmt) ast.Ellipsis -- st ast.CompositeLit --} +-// No hint +-const ( +- Int = 3 +- Float = 3.14 +- Bool = true +- Rune = '3' +- Complex = 2.7i +- String = "Hello, world!" +-) - --var _ = importedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-var ( +- varInt = 3 +- varFloat = 3.14 +- varBool = true +- varRune = '3' + '4' +- varComplex = 2.7i +- varString = "Hello, world!" +-) +diff -urN a/gopls/internal/lsp/testdata/inlay_hint/constant_values.go.golden b/gopls/internal/lsp/testdata/inlay_hint/constant_values.go.golden +--- a/gopls/internal/lsp/testdata/inlay_hint/constant_values.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/inlay_hint/constant_values.go.golden 1970-01-01 08:00:00 +@@ -1,47 +0,0 @@ +--- inlayHint -- +-package inlayHint //@inlayHint("package") - --type pointerBuiltinStruct struct { -- b *bool -- s *string -- i *int --} +-const True = true - --var _ = pointerBuiltinStruct{ -- b: new(bool), -- s: new(string), -- i: new(int), --} //@suggestedfix("}", "refactor.rewrite", "Fill") +-type Kind int - --var _ = []ast.BasicLit{ -- {}, //@suggestedfix("}", "refactor.rewrite", "Fill") --} +-const ( +- KindNone Kind = iota< = 0> +- KindPrint< = 1> +- KindPrintf< = 2> +- KindErrorf< = 3> +-) - --var _ = []ast.BasicLit{{}} //@suggestedfix("}", "refactor.rewrite", "Fill") +-const ( +- u = iota * 4< = 0> +- v float64 = iota * 42< = 42> +- w = iota * 42< = 84> +-) - ---- suggestedfix_a3_39_3 -- --package fillstruct +-const ( +- a, b = 1, 2 +- c, d< = 1, 2> +- e, f = 5 * 5, "hello" + "world"< = 25, "helloworld"> +- g, h< = 25, "helloworld"> +- i, j = true, f< = true, "helloworld"> +-) - --import ( -- "go/ast" -- "go/token" +-// No hint +-const ( +- Int = 3 +- Float = 3.14 +- Bool = true +- Rune = '3' +- Complex = 2.7i +- String = "Hello, world!" -) - --type Foo struct { -- A int --} +-var ( +- varInt = 3 +- varFloat = 3.14 +- varBool = true +- varRune = '3' + '4' +- varComplex = 2.7i +- varString = "Hello, world!" +-) - --type Bar struct { -- X *Foo -- Y *Foo --} +diff -urN a/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go b/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go +--- a/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go 1970-01-01 08:00:00 +@@ -1,50 +0,0 @@ +-package inlayHint //@inlayHint("package") - --var _ = Bar{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import "fmt" - --type importedStruct struct { -- m map[*ast.CompositeLit]ast.Field -- s []ast.BadExpr -- a [3]token.Token -- c chan ast.EmptyStmt -- fn func(ast_decl ast.DeclStmt) ast.Ellipsis -- st ast.CompositeLit +-func hello(name string) string { +- return "Hello " + name -} - --var _ = importedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --type pointerBuiltinStruct struct { -- b *bool -- s *string -- i *int +-func helloWorld() string { +- return hello("World") -} - --var _ = pointerBuiltinStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-type foo struct{} - --var _ = []ast.BasicLit{ -- { -- ValuePos: 0, -- Kind: 0, -- Value: "", -- }, //@suggestedfix("}", "refactor.rewrite", "Fill") +-func (*foo) bar(baz string, qux int) int { +- if baz != "" { +- return qux + 1 +- } +- return qux -} - --var _ = []ast.BasicLit{{}} //@suggestedfix("}", "refactor.rewrite", "Fill") -- ---- suggestedfix_a3_42_25 -- --package fillstruct +-func kase(foo int, bar bool, baz ...string) { +- fmt.Println(foo, bar, baz) +-} - --import ( -- "go/ast" -- "go/token" --) +-func kipp(foo string, bar, baz string) { +- fmt.Println(foo, bar, baz) +-} - --type Foo struct { -- A int +-func plex(foo, bar string, baz string) { +- fmt.Println(foo, bar, baz) -} - --type Bar struct { -- X *Foo -- Y *Foo +-func tars(foo string, bar, baz string) { +- fmt.Println(foo, bar, baz) -} - --var _ = Bar{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func foobar() { +- var x foo +- x.bar("", 1) +- kase(0, true, "c", "d", "e") +- kipp("a", "b", "c") +- plex("a", "b", "c") +- tars("a", "b", "c") +- foo, bar, baz := "a", "b", "c" +- kipp(foo, bar, baz) +- plex("a", bar, baz) +- tars(foo+foo, (bar), "c") - --type importedStruct struct { -- m map[*ast.CompositeLit]ast.Field -- s []ast.BadExpr -- a [3]token.Token -- c chan ast.EmptyStmt -- fn func(ast_decl ast.DeclStmt) ast.Ellipsis -- st ast.CompositeLit -} +diff -urN a/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go.golden b/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go.golden +--- a/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go.golden 1970-01-01 08:00:00 +@@ -1,52 +0,0 @@ +--- inlayHint -- +-package inlayHint //@inlayHint("package") - --var _ = importedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import "fmt" - --type pointerBuiltinStruct struct { -- b *bool -- s *string -- i *int +-func hello(name string) string { +- return "Hello " + name -} - --var _ = pointerBuiltinStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --var _ = []ast.BasicLit{ -- {}, //@suggestedfix("}", "refactor.rewrite", "Fill") +-func helloWorld() string { +- return hello("World") -} - --var _ = []ast.BasicLit{{ -- ValuePos: 0, -- Kind: 0, -- Value: "", --}} //@suggestedfix("}", "refactor.rewrite", "Fill") +-type foo struct{} - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/a4.go b/gopls/internal/lsp/testdata/fillstruct/a4.go ---- a/gopls/internal/lsp/testdata/fillstruct/a4.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/a4.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,39 +0,0 @@ --package fillstruct +-func (*foo) bar(baz string, qux int) int { +- if baz != "" { +- return qux + 1 +- } +- return qux +-} - --import "go/ast" +-func kase(foo int, bar bool, baz ...string) { +- fmt.Println(foo, bar, baz) +-} - --type iStruct struct { -- X int +-func kipp(foo string, bar, baz string) { +- fmt.Println(foo, bar, baz) -} - --type sStruct struct { -- str string +-func plex(foo, bar string, baz string) { +- fmt.Println(foo, bar, baz) -} - --type multiFill struct { -- num int -- strin string -- arr []int +-func tars(foo string, bar, baz string) { +- fmt.Println(foo, bar, baz) -} - --type assignStruct struct { -- n ast.Node +-func foobar() { +- var x foo +- x.bar("", 1) +- kase(0, true, "c", "d", "e") +- kipp("a", "b", "c") +- plex("a", "b", "c") +- tars("a", "b", "c") +- foo< string>, bar< string>, baz< string> := "a", "b", "c" +- kipp(foo, bar, baz) +- plex("a", bar, baz) +- tars(foo+foo, (bar), "c") +- -} - --func fill() { -- var x int -- var _ = iStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/inlay_hint/type_params.go b/gopls/internal/lsp/testdata/inlay_hint/type_params.go +--- a/gopls/internal/lsp/testdata/inlay_hint/type_params.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/inlay_hint/type_params.go 1970-01-01 08:00:00 +@@ -1,45 +0,0 @@ +-//go:build go1.18 +-// +build go1.18 - -- var s string -- var _ = sStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-package inlayHint //@inlayHint("package") - -- var n int -- _ = []int{} -- if true { -- arr := []int{1, 2} +-func main() { +- ints := map[string]int64{ +- "first": 34, +- "second": 12, - } -- var _ = multiFill{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- var node *ast.CompositeLit -- var _ = assignStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") --} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/a4.go.golden b/gopls/internal/lsp/testdata/fillstruct/a4.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/a4.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/a4.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,174 +0,0 @@ ---- suggestedfix_a4_25_18 -- --package fillstruct +- floats := map[string]float64{ +- "first": 35.98, +- "second": 26.99, +- } - --import "go/ast" +- SumIntsOrFloats[string, int64](ints) +- SumIntsOrFloats[string, float64](floats) - --type iStruct struct { -- X int --} +- SumIntsOrFloats(ints) +- SumIntsOrFloats(floats) - --type sStruct struct { -- str string +- SumNumbers(ints) +- SumNumbers(floats) -} - --type multiFill struct { -- num int -- strin string -- arr []int +-type Number interface { +- int64 | float64 -} - --type assignStruct struct { -- n ast.Node +-func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V { +- var s V +- for _, v := range m { +- s += v +- } +- return s -} - --func fill() { -- var x int -- var _ = iStruct{ -- X: x, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") +-func SumNumbers[K comparable, V Number](m map[K]V) V { +- var s V +- for _, v := range m { +- s += v +- } +- return s +-} +diff -urN a/gopls/internal/lsp/testdata/inlay_hint/type_params.go.golden b/gopls/internal/lsp/testdata/inlay_hint/type_params.go.golden +--- a/gopls/internal/lsp/testdata/inlay_hint/type_params.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/inlay_hint/type_params.go.golden 1970-01-01 08:00:00 +@@ -1,47 +0,0 @@ +--- inlayHint -- +-//go:build go1.18 +-// +build go1.18 - -- var s string -- var _ = sStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-package inlayHint //@inlayHint("package") - -- var n int -- _ = []int{} -- if true { -- arr := []int{1, 2} +-func main() { +- ints< map[string]int64> := map[string]int64{ +- "first": 34, +- "second": 12, - } -- var _ = multiFill{} //@suggestedfix("}", "refactor.rewrite", "Fill") - -- var node *ast.CompositeLit -- var _ = assignStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") --} +- floats< map[string]float64> := map[string]float64{ +- "first": 35.98, +- "second": 26.99, +- } - ---- suggestedfix_a4_28_18 -- --package fillstruct +- SumIntsOrFloats[string, int64](ints) +- SumIntsOrFloats[string, float64](floats) - --import "go/ast" +- SumIntsOrFloats<[string, int64]>(ints) +- SumIntsOrFloats<[string, float64]>(floats) - --type iStruct struct { -- X int +- SumNumbers<[string, int64]>(ints) +- SumNumbers<[string, float64]>(floats) -} - --type sStruct struct { -- str string +-type Number interface { +- int64 | float64 -} - --type multiFill struct { -- num int -- strin string -- arr []int +-func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V { +- var s V +- for _< K>, v< V> := range m { +- s += v +- } +- return s -} - --type assignStruct struct { -- n ast.Node +-func SumNumbers[K comparable, V Number](m map[K]V) V { +- var s V +- for _< K>, v< V> := range m { +- s += v +- } +- return s -} - --func fill() { -- var x int -- var _ = iStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/inlay_hint/variable_types.go b/gopls/internal/lsp/testdata/inlay_hint/variable_types.go +--- a/gopls/internal/lsp/testdata/inlay_hint/variable_types.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/inlay_hint/variable_types.go 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ +-package inlayHint //@inlayHint("package") - -- var s string -- var _ = sStruct{ -- str: s, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") +-func assignTypes() { +- i, j := 0, len([]string{})-1 +- println(i, j) +-} - -- var n int -- _ = []int{} -- if true { -- arr := []int{1, 2} +-func rangeTypes() { +- for k, v := range []string{} { +- println(k, v) - } -- var _ = multiFill{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- -- var node *ast.CompositeLit -- var _ = assignStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -} - ---- suggestedfix_a4_35_20 -- --package fillstruct +-func funcLitType() { +- myFunc := func(a string) string { return "" } +-} - --import "go/ast" +-func compositeLitType() { +- foo := map[string]interface{}{"": ""} +-} +diff -urN a/gopls/internal/lsp/testdata/inlay_hint/variable_types.go.golden b/gopls/internal/lsp/testdata/inlay_hint/variable_types.go.golden +--- a/gopls/internal/lsp/testdata/inlay_hint/variable_types.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/inlay_hint/variable_types.go.golden 1970-01-01 08:00:00 +@@ -1,22 +0,0 @@ +--- inlayHint -- +-package inlayHint //@inlayHint("package") - --type iStruct struct { -- X int +-func assignTypes() { +- i< int>, j< int> := 0, len([]string{})-1 +- println(i, j) -} - --type sStruct struct { -- str string +-func rangeTypes() { +- for k< int>, v< string> := range []string{} { +- println(k, v) +- } -} - --type multiFill struct { -- num int -- strin string -- arr []int +-func funcLitType() { +- myFunc< func(a string) string> := func(a string) string { return "" } -} - --type assignStruct struct { -- n ast.Node +-func compositeLitType() { +- foo< map[string]interface{}> := map[string]interface{}{"": ""} -} - --func fill() { -- var x int -- var _ = iStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/interfacerank/interface_rank.go b/gopls/internal/lsp/testdata/interfacerank/interface_rank.go +--- a/gopls/internal/lsp/testdata/interfacerank/interface_rank.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/interfacerank/interface_rank.go 1970-01-01 08:00:00 +@@ -1,23 +0,0 @@ +-package interfacerank - -- var s string -- var _ = sStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-type foo interface { +- foo() +-} - -- var n int -- _ = []int{} -- if true { -- arr := []int{1, 2} -- } -- var _ = multiFill{ -- num: n, -- strin: s, -- arr: []int{}, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") +-type fooImpl int - -- var node *ast.CompositeLit -- var _ = assignStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") --} +-func (*fooImpl) foo() {} - ---- suggestedfix_a4_38_23 -- --package fillstruct +-func wantsFoo(foo) {} - --import "go/ast" +-func _() { +- var ( +- aa string //@item(irAA, "aa", "string", "var") +- ab *fooImpl //@item(irAB, "ab", "*fooImpl", "var") +- ) - --type iStruct struct { -- X int --} +- wantsFoo(a) //@complete(")", irAB, irAA) - --type sStruct struct { -- str string +- var ac fooImpl //@item(irAC, "ac", "fooImpl", "var") +- wantsFoo(&a) //@complete(")", irAC, irAA, irAB) -} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/boolean.go b/gopls/internal/lsp/testdata/invertifcondition/boolean.go +--- a/gopls/internal/lsp/testdata/invertifcondition/boolean.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/boolean.go 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +-package invertifcondition - --type multiFill struct { -- num int -- strin string -- arr []int --} +-import ( +- "fmt" +-) - --type assignStruct struct { -- n ast.Node +-func Boolean() { +- b := true +- if b { //@suggestedfix("if b", "refactor.rewrite", "") +- fmt.Println("A") +- } else { +- fmt.Println("B") +- } -} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/boolean.go.golden b/gopls/internal/lsp/testdata/invertifcondition/boolean.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/boolean.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/boolean.go.golden 1970-01-01 08:00:00 +@@ -1,16 +0,0 @@ +--- suggestedfix_boolean_9_2 -- +-package invertifcondition - --func fill() { -- var x int -- var _ = iStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- -- var s string -- var _ = sStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import ( +- "fmt" +-) - -- var n int -- _ = []int{} -- if true { -- arr := []int{1, 2} +-func Boolean() { +- b := true +- if !b { +- fmt.Println("B") +- } else { //@suggestedfix("if b", "refactor.rewrite", "") +- fmt.Println("A") - } -- var _ = multiFill{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- -- var node *ast.CompositeLit -- var _ = assignStruct{ -- n: node, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") -} - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/a.go b/gopls/internal/lsp/testdata/fillstruct/a.go ---- a/gopls/internal/lsp/testdata/fillstruct/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/a.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,27 +0,0 @@ --package fillstruct +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/boolean_fn.go b/gopls/internal/lsp/testdata/invertifcondition/boolean_fn.go +--- a/gopls/internal/lsp/testdata/invertifcondition/boolean_fn.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/boolean_fn.go 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +-package invertifcondition - -import ( -- "golang.org/lsptests/fillstruct/data" +- "fmt" +- "os" -) - --type basicStruct struct { -- foo int +-func BooleanFn() { +- if os.IsPathSeparator('X') { //@suggestedfix("if os.IsPathSeparator('X')", "refactor.rewrite", "") +- fmt.Println("A") +- } else { +- fmt.Println("B") +- } -} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/boolean_fn.go.golden b/gopls/internal/lsp/testdata/invertifcondition/boolean_fn.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/boolean_fn.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/boolean_fn.go.golden 1970-01-01 08:00:00 +@@ -1,16 +0,0 @@ +--- suggestedfix_boolean_fn_9_2 -- +-package invertifcondition - --var _ = basicStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import ( +- "fmt" +- "os" +-) - --type twoArgStruct struct { -- foo int -- bar string +-func BooleanFn() { +- if !os.IsPathSeparator('X') { +- fmt.Println("B") +- } else { //@suggestedfix("if os.IsPathSeparator('X')", "refactor.rewrite", "") +- fmt.Println("A") +- } -} - --var _ = twoArgStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --type nestedStruct struct { -- bar string -- basic basicStruct --} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/dont_remove_parens.go b/gopls/internal/lsp/testdata/invertifcondition/dont_remove_parens.go +--- a/gopls/internal/lsp/testdata/invertifcondition/dont_remove_parens.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/dont_remove_parens.go 1970-01-01 08:00:00 +@@ -1,16 +0,0 @@ +-package invertifcondition - --var _ = nestedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import ( +- "fmt" +-) - --var _ = data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") -diff -urN a/gopls/internal/lsp/testdata/fillstruct/a.go.golden b/gopls/internal/lsp/testdata/fillstruct/a.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/a.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,126 +0,0 @@ ---- suggestedfix_a_11_21 -- --package fillstruct +-func DontRemoveParens() { +- a := false +- b := true +- if !(a || +- b) { //@suggestedfix("b", "refactor.rewrite", "") +- fmt.Println("A") +- } else { +- fmt.Println("B") +- } +-} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/dont_remove_parens.go.golden b/gopls/internal/lsp/testdata/invertifcondition/dont_remove_parens.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/dont_remove_parens.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/dont_remove_parens.go.golden 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +--- suggestedfix_dont_remove_parens_11_3 -- +-package invertifcondition - -import ( -- "golang.org/lsptests/fillstruct/data" +- "fmt" -) - --type basicStruct struct { -- foo int +-func DontRemoveParens() { +- a := false +- b := true +- if (a || +- b) { +- fmt.Println("B") +- } else { //@suggestedfix("b", "refactor.rewrite", "") +- fmt.Println("A") +- } -} - --var _ = basicStruct{ -- foo: 0, --} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/else_if.go b/gopls/internal/lsp/testdata/invertifcondition/else_if.go +--- a/gopls/internal/lsp/testdata/invertifcondition/else_if.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/else_if.go 1970-01-01 08:00:00 +@@ -1,22 +0,0 @@ +-package invertifcondition - --type twoArgStruct struct { -- foo int -- bar string --} +-import ( +- "fmt" +- "os" +-) - --var _ = twoArgStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func ElseIf() { +- // No inversion expected when there's not else clause +- if len(os.Args) > 2 { +- fmt.Println("A") +- } - --type nestedStruct struct { -- bar string -- basic basicStruct +- // No inversion expected for else-if, that would become unreadable +- if len(os.Args) > 2 { +- fmt.Println("A") +- } else if os.Args[0] == "X" { //@suggestedfix(re"if os.Args.0. == .X.", "refactor.rewrite", "") +- fmt.Println("B") +- } else { +- fmt.Println("C") +- } -} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/else_if.go.golden b/gopls/internal/lsp/testdata/invertifcondition/else_if.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/else_if.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/else_if.go.golden 1970-01-01 08:00:00 +@@ -1,24 +0,0 @@ +--- suggestedfix_else_if_17_9 -- +-package invertifcondition - --var _ = nestedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import ( +- "fmt" +- "os" +-) - --var _ = data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func ElseIf() { +- // No inversion expected when there's not else clause +- if len(os.Args) > 2 { +- fmt.Println("A") +- } - ---- suggestedfix_a_18_22 -- --package fillstruct +- // No inversion expected for else-if, that would become unreadable +- if len(os.Args) > 2 { +- fmt.Println("A") +- } else if os.Args[0] != "X" { +- fmt.Println("C") +- } else { //@suggestedfix(re"if os.Args.0. == .X.", "refactor.rewrite", "") +- fmt.Println("B") +- } +-} +- +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/greater_than.go b/gopls/internal/lsp/testdata/invertifcondition/greater_than.go +--- a/gopls/internal/lsp/testdata/invertifcondition/greater_than.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/greater_than.go 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +-package invertifcondition - -import ( -- "golang.org/lsptests/fillstruct/data" +- "fmt" +- "os" -) - --type basicStruct struct { -- foo int +-func GreaterThan() { +- if len(os.Args) > 2 { //@suggestedfix("i", "refactor.rewrite", "") +- fmt.Println("A") +- } else { +- fmt.Println("B") +- } -} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/greater_than.go.golden b/gopls/internal/lsp/testdata/invertifcondition/greater_than.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/greater_than.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/greater_than.go.golden 1970-01-01 08:00:00 +@@ -1,16 +0,0 @@ +--- suggestedfix_greater_than_9_2 -- +-package invertifcondition - --var _ = basicStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import ( +- "fmt" +- "os" +-) - --type twoArgStruct struct { -- foo int -- bar string +-func GreaterThan() { +- if len(os.Args) <= 2 { +- fmt.Println("B") +- } else { //@suggestedfix("i", "refactor.rewrite", "") +- fmt.Println("A") +- } -} - --var _ = twoArgStruct{ -- foo: 0, -- bar: "", --} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/not_boolean.go b/gopls/internal/lsp/testdata/invertifcondition/not_boolean.go +--- a/gopls/internal/lsp/testdata/invertifcondition/not_boolean.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/not_boolean.go 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +-package invertifcondition - --type nestedStruct struct { -- bar string -- basic basicStruct +-import ( +- "fmt" +-) +- +-func NotBoolean() { +- b := true +- if !b { //@suggestedfix("if !b", "refactor.rewrite", "") +- fmt.Println("A") +- } else { +- fmt.Println("B") +- } -} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/not_boolean.go.golden b/gopls/internal/lsp/testdata/invertifcondition/not_boolean.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/not_boolean.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/not_boolean.go.golden 1970-01-01 08:00:00 +@@ -1,16 +0,0 @@ +--- suggestedfix_not_boolean_9_2 -- +-package invertifcondition - --var _ = nestedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import ( +- "fmt" +-) - --var _ = data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func NotBoolean() { +- b := true +- if b { +- fmt.Println("B") +- } else { //@suggestedfix("if !b", "refactor.rewrite", "") +- fmt.Println("A") +- } +-} - ---- suggestedfix_a_25_22 -- --package fillstruct +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/remove_else.go b/gopls/internal/lsp/testdata/invertifcondition/remove_else.go +--- a/gopls/internal/lsp/testdata/invertifcondition/remove_else.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/remove_else.go 1970-01-01 08:00:00 +@@ -1,16 +0,0 @@ +-package invertifcondition - -import ( -- "golang.org/lsptests/fillstruct/data" +- "fmt" -) - --type basicStruct struct { -- foo int +-func RemoveElse() { +- if true { //@suggestedfix("if true", "refactor.rewrite", "") +- fmt.Println("A") +- } else { +- fmt.Println("B") +- return +- } +- +- fmt.Println("C") -} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/remove_else.go.golden b/gopls/internal/lsp/testdata/invertifcondition/remove_else.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/remove_else.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/remove_else.go.golden 1970-01-01 08:00:00 +@@ -1,19 +0,0 @@ +--- suggestedfix_remove_else_8_2 -- +-package invertifcondition - --var _ = basicStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import ( +- "fmt" +-) - --type twoArgStruct struct { -- foo int -- bar string --} +-func RemoveElse() { +- if false { +- fmt.Println("B") +- return +- } - --var _ = twoArgStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- //@suggestedfix("if true", "refactor.rewrite", "") +- fmt.Println("A") - --type nestedStruct struct { -- bar string -- basic basicStruct +- fmt.Println("C") -} - --var _ = nestedStruct{ -- bar: "", -- basic: basicStruct{}, --} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/remove_parens.go b/gopls/internal/lsp/testdata/invertifcondition/remove_parens.go +--- a/gopls/internal/lsp/testdata/invertifcondition/remove_parens.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/remove_parens.go 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +-package invertifcondition - --var _ = data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import ( +- "fmt" +-) - ---- suggestedfix_a_27_16 -- --package fillstruct +-func RemoveParens() { +- b := true +- if !(b) { //@suggestedfix("if", "refactor.rewrite", "") +- fmt.Println("A") +- } else { +- fmt.Println("B") +- } +-} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/remove_parens.go.golden b/gopls/internal/lsp/testdata/invertifcondition/remove_parens.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/remove_parens.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/remove_parens.go.golden 1970-01-01 08:00:00 +@@ -1,16 +0,0 @@ +--- suggestedfix_remove_parens_9_2 -- +-package invertifcondition - -import ( -- "golang.org/lsptests/fillstruct/data" +- "fmt" -) - --type basicStruct struct { -- foo int +-func RemoveParens() { +- b := true +- if b { +- fmt.Println("B") +- } else { //@suggestedfix("if", "refactor.rewrite", "") +- fmt.Println("A") +- } -} - --var _ = basicStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/semicolon.go b/gopls/internal/lsp/testdata/invertifcondition/semicolon.go +--- a/gopls/internal/lsp/testdata/invertifcondition/semicolon.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/semicolon.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package invertifcondition - --type twoArgStruct struct { -- foo int -- bar string +-import ( +- "fmt" +-) +- +-func Semicolon() { +- if _, err := fmt.Println("x"); err != nil { //@suggestedfix("if", "refactor.rewrite", "") +- fmt.Println("A") +- } else { +- fmt.Println("B") +- } -} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/semicolon.go.golden b/gopls/internal/lsp/testdata/invertifcondition/semicolon.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/semicolon.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/semicolon.go.golden 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +--- suggestedfix_semicolon_8_2 -- +-package invertifcondition - --var _ = twoArgStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import ( +- "fmt" +-) - --type nestedStruct struct { -- bar string -- basic basicStruct +-func Semicolon() { +- if _, err := fmt.Println("x"); err == nil { +- fmt.Println("B") +- } else { //@suggestedfix("if", "refactor.rewrite", "") +- fmt.Println("A") +- } -} - --var _ = nestedStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --var _ = data.B{ -- ExportedInt: 0, --} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/semicolon_and.go b/gopls/internal/lsp/testdata/invertifcondition/semicolon_and.go +--- a/gopls/internal/lsp/testdata/invertifcondition/semicolon_and.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/semicolon_and.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package invertifcondition - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/data/a.go b/gopls/internal/lsp/testdata/fillstruct/data/a.go ---- a/gopls/internal/lsp/testdata/fillstruct/data/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/data/a.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --package data -- --type B struct { -- ExportedInt int -- unexportedInt int --} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ --package fillstruct +-import ( +- "fmt" +-) - --type StructAnon struct { -- a struct{} -- b map[string]interface{} -- c map[string]struct { -- d int -- e bool +-func SemicolonAnd() { +- if n, err := fmt.Println("x"); err != nil && n > 0 { //@suggestedfix("f", "refactor.rewrite", "") +- fmt.Println("A") +- } else { +- fmt.Println("B") - } -} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/semicolon_and.go.golden b/gopls/internal/lsp/testdata/invertifcondition/semicolon_and.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/semicolon_and.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/semicolon_and.go.golden 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +--- suggestedfix_semicolon_and_8_3 -- +-package invertifcondition - --func fill() { -- _ := StructAnon{} //@suggestedfix("}", "refactor.rewrite", "Fill") --} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_anon.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,20 +0,0 @@ ---- suggestedfix_fill_struct_anon_13_18 -- --package fillstruct +-import ( +- "fmt" +-) - --type StructAnon struct { -- a struct{} -- b map[string]interface{} -- c map[string]struct { -- d int -- e bool +-func SemicolonAnd() { +- if n, err := fmt.Println("x"); err == nil || n <= 0 { +- fmt.Println("B") +- } else { //@suggestedfix("f", "refactor.rewrite", "") +- fmt.Println("A") - } -} - --func fill() { -- _ := StructAnon{ -- a: struct{}{}, -- b: map[string]interface{}{}, -- c: map[string]struct{d int; e bool}{}, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") --} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/semicolon_or.go b/gopls/internal/lsp/testdata/invertifcondition/semicolon_or.go +--- a/gopls/internal/lsp/testdata/invertifcondition/semicolon_or.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/semicolon_or.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package invertifcondition - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct.go ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,26 +0,0 @@ --package fillstruct +-import ( +- "fmt" +-) - --type StructA struct { -- unexportedIntField int -- ExportedIntField int -- MapA map[int]string -- Array []int -- StructB +-func SemicolonOr() { +- if n, err := fmt.Println("x"); err != nil || n < 5 { //@suggestedfix(re"if n, err := fmt.Println..x..; err != nil .. n < 5", "refactor.rewrite", "") +- fmt.Println("A") +- } else { +- fmt.Println("B") +- } -} +diff -urN a/gopls/internal/lsp/testdata/invertifcondition/semicolon_or.go.golden b/gopls/internal/lsp/testdata/invertifcondition/semicolon_or.go.golden +--- a/gopls/internal/lsp/testdata/invertifcondition/semicolon_or.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/invertifcondition/semicolon_or.go.golden 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +--- suggestedfix_semicolon_or_8_2 -- +-package invertifcondition - --type StructA2 struct { -- B *StructB +-import ( +- "fmt" +-) +- +-func SemicolonOr() { +- if n, err := fmt.Println("x"); err == nil && n >= 5 { +- fmt.Println("B") +- } else { //@suggestedfix(re"if n, err := fmt.Println..x..; err != nil .. n < 5", "refactor.rewrite", "") +- fmt.Println("A") +- } -} - --type StructA3 struct { -- B StructB +diff -urN a/gopls/internal/lsp/testdata/issues/issue56505.go b/gopls/internal/lsp/testdata/issues/issue56505.go +--- a/gopls/internal/lsp/testdata/issues/issue56505.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/issues/issue56505.go 1970-01-01 08:00:00 +@@ -1,8 +0,0 @@ +-package issues +- +-// Test for golang/go#56505: completion on variables of type *error should not +-// panic. +-func _() { +- var e *error +- e.x //@complete(" //") -} +diff -urN a/gopls/internal/lsp/testdata/keywords/accidental_keywords.go.in b/gopls/internal/lsp/testdata/keywords/accidental_keywords.go.in +--- a/gopls/internal/lsp/testdata/keywords/accidental_keywords.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/keywords/accidental_keywords.go.in 1970-01-01 08:00:00 +@@ -1,31 +0,0 @@ +-package keywords - --func fill() { -- a := StructA{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- b := StructA2{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- c := StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- if true { -- _ = StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- } +-// non-matching candidate - shouldn't show up as completion +-var apple = "apple" +- +-func _() { +- foo.bar() // insert some extra statements to exercise our AST surgery +- variance := 123 //@item(kwVariance, "variance", "int", "var") +- foo.bar() +- println(var) //@complete(")", kwVariance) -} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,124 +0,0 @@ ---- suggestedfix_fill_struct_20_15 -- --package fillstruct - --type StructA struct { -- unexportedIntField int -- ExportedIntField int -- MapA map[int]string -- Array []int -- StructB +-func _() { +- foo.bar() +- var s struct { variance int } //@item(kwVarianceField, "variance", "int", "field") +- foo.bar() +- s.var //@complete(" //", kwVarianceField) -} - --type StructA2 struct { -- B *StructB +-func _() { +- channel := 123 //@item(kwChannel, "channel", "int", "var") +- chan //@complete(" //", kwChannel) +- foo.bar() -} - --type StructA3 struct { -- B StructB +-func _() { +- foo.bar() +- var typeName string //@item(kwTypeName, "typeName", "string", "var") +- foo.bar() +- type //@complete(" //", kwTypeName) -} +diff -urN a/gopls/internal/lsp/testdata/keywords/empty_select.go b/gopls/internal/lsp/testdata/keywords/empty_select.go +--- a/gopls/internal/lsp/testdata/keywords/empty_select.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/keywords/empty_select.go 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package keywords - --func fill() { -- a := StructA{ -- unexportedIntField: 0, -- ExportedIntField: 0, -- MapA: map[int]string{}, -- Array: []int{}, -- StructB: StructB{}, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") -- b := StructA2{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- c := StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- if true { -- _ = StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func _() { +- select { +- c //@complete(" //", case) - } -} +diff -urN a/gopls/internal/lsp/testdata/keywords/empty_switch.go b/gopls/internal/lsp/testdata/keywords/empty_switch.go +--- a/gopls/internal/lsp/testdata/keywords/empty_switch.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/keywords/empty_switch.go 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package keywords - ---- suggestedfix_fill_struct_21_16 -- --package fillstruct +-func _() { +- switch { +- //@complete("", case, default) +- } - --type StructA struct { -- unexportedIntField int -- ExportedIntField int -- MapA map[int]string -- Array []int -- StructB +- switch test.(type) { +- d //@complete(" //", default) +- } -} +diff -urN a/gopls/internal/lsp/testdata/keywords/keywords.go b/gopls/internal/lsp/testdata/keywords/keywords.go +--- a/gopls/internal/lsp/testdata/keywords/keywords.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/keywords/keywords.go 1970-01-01 08:00:00 +@@ -1,100 +0,0 @@ +-package keywords - --type StructA2 struct { -- B *StructB --} +-//@rank("", type),rank("", func),rank("", var),rank("", const),rank("", import) - --type StructA3 struct { -- B StructB --} +-func _() { +- var test int //@rank(" //", int, interface) +- var tChan chan int +- var _ m //@complete(" //", map) +- var _ f //@complete(" //", func) +- var _ c //@complete(" //", chan) - --func fill() { -- a := StructA{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- b := StructA2{ -- B: &StructB{}, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") -- c := StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- if true { -- _ = StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- } --} +- var _ str //@rank(" //", string, struct) - ---- suggestedfix_fill_struct_22_16 -- --package fillstruct +- type _ int //@rank(" //", interface, int) - --type StructA struct { -- unexportedIntField int -- ExportedIntField int -- MapA map[int]string -- Array []int -- StructB --} +- type _ str //@rank(" //", struct, string) - --type StructA2 struct { -- B *StructB --} +- switch test { +- case 1: // TODO: trying to complete case here will break because the parser won't return *ast.Ident +- b //@complete(" //", break) +- case 2: +- f //@complete(" //", fallthrough, for) +- r //@complete(" //", return) +- d //@complete(" //", default, defer) +- c //@complete(" //", case, const) +- } - --type StructA3 struct { -- B StructB --} +- switch test.(type) { +- case fo: //@complete(":") +- case int: +- b //@complete(" //", break) +- case int32: +- f //@complete(" //", for) +- d //@complete(" //", default, defer) +- r //@complete(" //", return) +- c //@complete(" //", case, const) +- } - --func fill() { -- a := StructA{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- b := StructA2{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- c := StructA3{ -- B: StructB{}, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") -- if true { -- _ = StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- select { +- case <-tChan: +- b //@complete(" //", break) +- c //@complete(" //", case, const) - } --} - ---- suggestedfix_fill_struct_24_16 -- --package fillstruct +- for index := 0; index < test; index++ { +- c //@complete(" //", const, continue) +- b //@complete(" //", break) +- } - --type StructA struct { -- unexportedIntField int -- ExportedIntField int -- MapA map[int]string -- Array []int -- StructB --} +- for range []int{} { +- c //@complete(" //", const, continue) +- b //@complete(" //", break) +- } - --type StructA2 struct { -- B *StructB --} +- // Test function level keywords - --type StructA3 struct { -- B StructB --} +- //Using 2 characters to test because map output order is random +- sw //@complete(" //", switch) +- se //@complete(" //", select) - --func fill() { -- a := StructA{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- b := StructA2{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- c := StructA3{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- if true { -- _ = StructA3{ -- B: StructB{}, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") -- } +- f //@complete(" //", for) +- d //@complete(" //", defer) +- g //@rank(" //", go),rank(" //", goto) +- r //@complete(" //", return) +- i //@complete(" //", if) +- e //@complete(" //", else) +- v //@complete(" //", var) +- c //@complete(" //", const) +- +- for i := r //@complete(" //", range) -} - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,15 +0,0 @@ --package fillstruct +-/* package */ //@item(package, "package", "", "keyword") +-/* import */ //@item(import, "import", "", "keyword") +-/* func */ //@item(func, "func", "", "keyword") +-/* type */ //@item(type, "type", "", "keyword") +-/* var */ //@item(var, "var", "", "keyword") +-/* const */ //@item(const, "const", "", "keyword") +-/* break */ //@item(break, "break", "", "keyword") +-/* default */ //@item(default, "default", "", "keyword") +-/* case */ //@item(case, "case", "", "keyword") +-/* defer */ //@item(defer, "defer", "", "keyword") +-/* go */ //@item(go, "go", "", "keyword") +-/* for */ //@item(for, "for", "", "keyword") +-/* if */ //@item(if, "if", "", "keyword") +-/* else */ //@item(else, "else", "", "keyword") +-/* switch */ //@item(switch, "switch", "", "keyword") +-/* select */ //@item(select, "select", "", "keyword") +-/* fallthrough */ //@item(fallthrough, "fallthrough", "", "keyword") +-/* continue */ //@item(continue, "continue", "", "keyword") +-/* return */ //@item(return, "return", "", "keyword") +-/* var */ //@item(var, "var", "", "keyword") +-/* const */ //@item(const, "const", "", "keyword") +-/* goto */ //@item(goto, "goto", "", "keyword") +-/* struct */ //@item(struct, "struct", "", "keyword") +-/* interface */ //@item(interface, "interface", "", "keyword") +-/* map */ //@item(map, "map", "", "keyword") +-/* func */ //@item(func, "func", "", "keyword") +-/* chan */ //@item(chan, "chan", "", "keyword") +-/* range */ //@item(range, "range", "", "keyword") +diff -urN a/gopls/internal/lsp/testdata/labels/labels.go b/gopls/internal/lsp/testdata/labels/labels.go +--- a/gopls/internal/lsp/testdata/labels/labels.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/labels/labels.go 1970-01-01 08:00:00 +@@ -1,49 +0,0 @@ +-package labels - --type StructB struct { -- StructC --} +-func _() { +- goto F //@complete(" //", label1, label5) - --type StructC struct { -- unexportedInt int --} +-Foo1: //@item(label1, "Foo1", "label", "const") +- for a, b := range []int{} { +- Foo2: //@item(label2, "Foo2", "label", "const") +- switch { +- case true: +- break F //@complete(" //", label2, label1) - --func nested() { -- c := StructB{ -- StructC: StructC{}, //@suggestedfix("}", "refactor.rewrite", "Fill") -- } --} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_nested.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,19 +0,0 @@ ---- suggestedfix_fill_struct_nested_13_20 -- --package fillstruct +- continue F //@complete(" //", label1) - --type StructB struct { -- StructC --} +- { +- FooUnjumpable: +- } +- +- goto F //@complete(" //", label1, label2, label4, label5) +- +- func() { +- goto F //@complete(" //", label3) +- +- break F //@complete(" //") +- +- continue F //@complete(" //") +- +- Foo3: //@item(label3, "Foo3", "label", "const") +- }() +- } +- +- Foo4: //@item(label4, "Foo4", "label", "const") +- switch interface{}(a).(type) { +- case int: +- break F //@complete(" //", label4, label1) +- } +- } - --type StructC struct { -- unexportedInt int --} +- break F //@complete(" //") - --func nested() { -- c := StructB{ -- StructC: StructC{ -- unexportedInt: 0, -- }, //@suggestedfix("}", "refactor.rewrite", "Fill") +- continue F //@complete(" //") +- +-Foo5: //@item(label5, "Foo5", "label", "const") +- for { +- break F //@complete(" //", label5) - } --} - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ --package fillstruct +- return +-} +diff -urN a/gopls/internal/lsp/testdata/links/links.go b/gopls/internal/lsp/testdata/links/links.go +--- a/gopls/internal/lsp/testdata/links/links.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/links/links.go 1970-01-01 08:00:00 +@@ -1,26 +0,0 @@ +-package links - -import ( -- h2 "net/http" +- "fmt" //@link(`fmt`,"https://pkg.go.dev/fmt") - -- "golang.org/lsptests/fillstruct/data" +- "golang.org/lsptests/foo" //@link(`golang.org/lsptests/foo`,`https://pkg.go.dev/golang.org/lsptests/foo`) +- +- _ "database/sql" //@link(`database/sql`, `https://pkg.go.dev/database/sql`) -) - --func unexported() { -- a := data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- _ = h2.Client{} //@suggestedfix("}", "refactor.rewrite", "Fill") --} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_package.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,36 +0,0 @@ ---- suggestedfix_fill_struct_package_10_14 -- --package fillstruct +-var ( +- _ fmt.Formatter +- _ foo.StructFoo +- _ errors.Formatter +-) - --import ( -- h2 "net/http" +-// Foo function +-func Foo() string { +- /*https://example.com/comment */ //@link("https://example.com/comment","https://example.com/comment") - -- "golang.org/lsptests/fillstruct/data" --) +- url := "https://example.com/string_literal" //@link("https://example.com/string_literal","https://example.com/string_literal") +- return url - --func unexported() { -- a := data.B{ -- ExportedInt: 0, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") -- _ = h2.Client{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- // TODO(golang/go#1234): Link the relevant issue. //@link("golang/go#1234", "https://github.com/golang/go/issues/1234") +- // TODO(microsoft/vscode-go#12): Another issue. //@link("microsoft/vscode-go#12", "https://github.com/microsoft/vscode-go/issues/12") -} +diff -urN a/gopls/internal/lsp/testdata/maps/maps.go.in b/gopls/internal/lsp/testdata/maps/maps.go.in +--- a/gopls/internal/lsp/testdata/maps/maps.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/maps/maps.go.in 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +-package maps - ---- suggestedfix_fill_struct_package_11_16 -- --package fillstruct +-func _() { +- var aVar int //@item(mapVar, "aVar", "int", "var") - --import ( -- h2 "net/http" +- // not comparabale +- type aSlice []int //@item(mapSliceType, "aSlice", "[]int", "type") - -- "golang.org/lsptests/fillstruct/data" --) +- *aSlice //@item(mapSliceTypePtr, "*aSlice", "[]int", "type") - --func unexported() { -- a := data.B{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- _ = h2.Client{ -- Transport: nil, -- CheckRedirect: func(req *h2.Request, via []*h2.Request) error { -- }, -- Jar: nil, -- Timeout: 0, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") --} +- // comparable +- type aStruct struct{} //@item(mapStructType, "aStruct", "struct{...}", "struct") - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ --package fillstruct +- map[]a{} //@complete("]", mapSliceType, mapStructType),snippet("]", mapSliceType, "*aSlice", "*aSlice") - --type StructPartialA struct { -- PrefilledInt int -- UnfilledInt int -- StructPartialB +- map[a]a{} //@complete("]", mapSliceType, mapStructType) +- map[a]a{} //@complete("{", mapSliceType, mapStructType) -} +diff -urN a/gopls/internal/lsp/testdata/missingfunction/channels.go b/gopls/internal/lsp/testdata/missingfunction/channels.go +--- a/gopls/internal/lsp/testdata/missingfunction/channels.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/channels.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package missingfunction - --type StructPartialB struct { -- PrefilledInt int -- UnfilledInt int +-func channels(s string) { +- undefinedChannels(c()) //@suggestedfix("undefinedChannels", "quickfix", "") -} - --func fill() { -- a := StructPartialA{ -- PrefilledInt: 5, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") -- b := StructPartialB{ -- /* this comment should disappear */ -- PrefilledInt: 7, // This comment should be blown away. -- /* As should -- this one */ -- } //@suggestedfix("}", "refactor.rewrite", "Fill") +-func c() (<-chan string, chan string) { +- return make(<-chan string), make(chan string) -} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_partial.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,52 +0,0 @@ ---- suggestedfix_fill_struct_partial_17_2 -- --package fillstruct +diff -urN a/gopls/internal/lsp/testdata/missingfunction/channels.go.golden b/gopls/internal/lsp/testdata/missingfunction/channels.go.golden +--- a/gopls/internal/lsp/testdata/missingfunction/channels.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/channels.go.golden 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +--- suggestedfix_channels_4_2 -- +-package missingfunction - --type StructPartialA struct { -- PrefilledInt int -- UnfilledInt int -- StructPartialB +-func channels(s string) { +- undefinedChannels(c()) //@suggestedfix("undefinedChannels", "quickfix", "") -} - --type StructPartialB struct { -- PrefilledInt int -- UnfilledInt int +-func undefinedChannels(ch1 <-chan string, ch2 chan string) { +- panic("unimplemented") -} - --func fill() { -- a := StructPartialA{ -- PrefilledInt: 5, -- UnfilledInt: 0, -- StructPartialB: StructPartialB{}, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") -- b := StructPartialB{ -- /* this comment should disappear */ -- PrefilledInt: 7, // This comment should be blown away. -- /* As should -- this one */ -- } //@suggestedfix("}", "refactor.rewrite", "Fill") +-func c() (<-chan string, chan string) { +- return make(<-chan string), make(chan string) -} - ---- suggestedfix_fill_struct_partial_23_2 -- --package fillstruct +diff -urN a/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go b/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go +--- a/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go 1970-01-01 08:00:00 +@@ -1,6 +0,0 @@ +-package missingfunction - --type StructPartialA struct { -- PrefilledInt int -- UnfilledInt int -- StructPartialB +-func consecutiveParams() { +- var s string +- undefinedConsecutiveParams(s, s) //@suggestedfix("undefinedConsecutiveParams", "quickfix", "") -} +diff -urN a/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go.golden b/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go.golden +--- a/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go.golden 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +--- suggestedfix_consecutive_params_5_2 -- +-package missingfunction - --type StructPartialB struct { -- PrefilledInt int -- UnfilledInt int +-func consecutiveParams() { +- var s string +- undefinedConsecutiveParams(s, s) //@suggestedfix("undefinedConsecutiveParams", "quickfix", "") -} - --func fill() { -- a := StructPartialA{ -- PrefilledInt: 5, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") -- b := StructPartialB{ -- PrefilledInt: 7, -- UnfilledInt: 0, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") +-func undefinedConsecutiveParams(s1, s2 string) { +- panic("unimplemented") -} - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package fillstruct -- --type StructD struct { -- ExportedIntField int --} +diff -urN a/gopls/internal/lsp/testdata/missingfunction/error_param.go b/gopls/internal/lsp/testdata/missingfunction/error_param.go +--- a/gopls/internal/lsp/testdata/missingfunction/error_param.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/error_param.go 1970-01-01 08:00:00 +@@ -1,6 +0,0 @@ +-package missingfunction - --func spaces() { -- d := StructD{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func errorParam() { +- var err error +- undefinedErrorParam(err) //@suggestedfix("undefinedErrorParam", "quickfix", "") -} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_spaces.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ ---- suggestedfix_fill_struct_spaces_8_15 -- --package fillstruct +diff -urN a/gopls/internal/lsp/testdata/missingfunction/error_param.go.golden b/gopls/internal/lsp/testdata/missingfunction/error_param.go.golden +--- a/gopls/internal/lsp/testdata/missingfunction/error_param.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/error_param.go.golden 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +--- suggestedfix_error_param_5_2 -- +-package missingfunction - --type StructD struct { -- ExportedIntField int +-func errorParam() { +- var err error +- undefinedErrorParam(err) //@suggestedfix("undefinedErrorParam", "quickfix", "") -} - --func spaces() { -- d := StructD{ -- ExportedIntField: 0, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") +-func undefinedErrorParam(err error) { +- panic("unimplemented") -} - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go b/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ --package fillstruct -- --import "unsafe" +diff -urN a/gopls/internal/lsp/testdata/missingfunction/literals.go b/gopls/internal/lsp/testdata/missingfunction/literals.go +--- a/gopls/internal/lsp/testdata/missingfunction/literals.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/literals.go 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package missingfunction - --type unsafeStruct struct { -- x int -- p unsafe.Pointer --} +-type T struct{} - --func fill() { -- _ := unsafeStruct{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func literals() { +- undefinedLiterals("hey compiler", T{}, &T{}) //@suggestedfix("undefinedLiterals", "quickfix", "") -} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go.golden b/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/fill_struct_unsafe.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,17 +0,0 @@ ---- suggestedfix_fill_struct_unsafe_11_20 -- --package fillstruct +diff -urN a/gopls/internal/lsp/testdata/missingfunction/literals.go.golden b/gopls/internal/lsp/testdata/missingfunction/literals.go.golden +--- a/gopls/internal/lsp/testdata/missingfunction/literals.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/literals.go.golden 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +--- suggestedfix_literals_6_2 -- +-package missingfunction - --import "unsafe" +-type T struct{} - --type unsafeStruct struct { -- x int -- p unsafe.Pointer +-func literals() { +- undefinedLiterals("hey compiler", T{}, &T{}) //@suggestedfix("undefinedLiterals", "quickfix", "") -} - --func fill() { -- _ := unsafeStruct{ -- x: 0, -- p: nil, -- } //@suggestedfix("}", "refactor.rewrite", "Fill") +-func undefinedLiterals(s string, t1 T, t2 *T) { +- panic("unimplemented") -} - -diff -urN a/gopls/internal/lsp/testdata/fillstruct/typeparams.go b/gopls/internal/lsp/testdata/fillstruct/typeparams.go ---- a/gopls/internal/lsp/testdata/fillstruct/typeparams.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/typeparams.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,37 +0,0 @@ --//go:build go1.18 --// +build go1.18 -- --package fillstruct -- --type emptyStructWithTypeParams[A any] struct{} +diff -urN a/gopls/internal/lsp/testdata/missingfunction/operation.go b/gopls/internal/lsp/testdata/missingfunction/operation.go +--- a/gopls/internal/lsp/testdata/missingfunction/operation.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/operation.go 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package missingfunction - --var _ = emptyStructWithTypeParams[int]{} // no suggested fix +-import "time" - --type basicStructWithTypeParams[T any] struct { -- foo T +-func operation() { +- undefinedOperation(10 * time.Second) //@suggestedfix("undefinedOperation", "quickfix", "") -} +diff -urN a/gopls/internal/lsp/testdata/missingfunction/operation.go.golden b/gopls/internal/lsp/testdata/missingfunction/operation.go.golden +--- a/gopls/internal/lsp/testdata/missingfunction/operation.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/operation.go.golden 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +--- suggestedfix_operation_6_2 -- +-package missingfunction - --var _ = basicStructWithTypeParams[int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-import "time" - --type twoArgStructWithTypeParams[F, B any] struct { -- foo F -- bar B +-func operation() { +- undefinedOperation(10 * time.Second) //@suggestedfix("undefinedOperation", "quickfix", "") -} - --var _ = twoArgStructWithTypeParams[string, int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --var _ = twoArgStructWithTypeParams[int, string]{ -- bar: "bar", --} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --type nestedStructWithTypeParams struct { -- bar string -- basic basicStructWithTypeParams[int] +-func undefinedOperation(duration time.Duration) { +- panic("unimplemented") -} - --var _ = nestedStructWithTypeParams{} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/missingfunction/selector.go b/gopls/internal/lsp/testdata/missingfunction/selector.go +--- a/gopls/internal/lsp/testdata/missingfunction/selector.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/selector.go 1970-01-01 08:00:00 +@@ -1,6 +0,0 @@ +-package missingfunction - --func _[T any]() { -- type S struct{ t T } -- _ = S{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func selector() { +- m := map[int]bool{} +- undefinedSelector(m[1]) //@suggestedfix("undefinedSelector", "quickfix", "") -} -diff -urN a/gopls/internal/lsp/testdata/fillstruct/typeparams.go.golden b/gopls/internal/lsp/testdata/fillstruct/typeparams.go.golden ---- a/gopls/internal/lsp/testdata/fillstruct/typeparams.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fillstruct/typeparams.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,206 +0,0 @@ ---- suggestedfix_typeparams_14_40 -- --//go:build go1.18 --// +build go1.18 -- --package fillstruct -- --type emptyStructWithTypeParams[A any] struct{} -- --var _ = emptyStructWithTypeParams[int]{} // no suggested fix +diff -urN a/gopls/internal/lsp/testdata/missingfunction/selector.go.golden b/gopls/internal/lsp/testdata/missingfunction/selector.go.golden +--- a/gopls/internal/lsp/testdata/missingfunction/selector.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/selector.go.golden 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +--- suggestedfix_selector_5_2 -- +-package missingfunction - --type basicStructWithTypeParams[T any] struct { -- foo T +-func selector() { +- m := map[int]bool{} +- undefinedSelector(m[1]) //@suggestedfix("undefinedSelector", "quickfix", "") -} - --var _ = basicStructWithTypeParams[int]{ -- foo: 0, --} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --type twoArgStructWithTypeParams[F, B any] struct { -- foo F -- bar B +-func undefinedSelector(b bool) { +- panic("unimplemented") -} - --var _ = twoArgStructWithTypeParams[string, int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --var _ = twoArgStructWithTypeParams[int, string]{ -- bar: "bar", --} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/missingfunction/slice.go b/gopls/internal/lsp/testdata/missingfunction/slice.go +--- a/gopls/internal/lsp/testdata/missingfunction/slice.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/slice.go 1970-01-01 08:00:00 +@@ -1,5 +0,0 @@ +-package missingfunction - --type nestedStructWithTypeParams struct { -- bar string -- basic basicStructWithTypeParams[int] +-func slice() { +- undefinedSlice([]int{1, 2}) //@suggestedfix("undefinedSlice", "quickfix", "") -} +diff -urN a/gopls/internal/lsp/testdata/missingfunction/slice.go.golden b/gopls/internal/lsp/testdata/missingfunction/slice.go.golden +--- a/gopls/internal/lsp/testdata/missingfunction/slice.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/slice.go.golden 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +--- suggestedfix_slice_4_2 -- +-package missingfunction - --var _ = nestedStructWithTypeParams{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --func _[T any]() { -- type S struct{ t T } -- _ = S{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func slice() { +- undefinedSlice([]int{1, 2}) //@suggestedfix("undefinedSlice", "quickfix", "") -} - ---- suggestedfix_typeparams_21_49 -- --//go:build go1.18 --// +build go1.18 -- --package fillstruct -- --type emptyStructWithTypeParams[A any] struct{} +-func undefinedSlice(i []int) { +- panic("unimplemented") +-} - --var _ = emptyStructWithTypeParams[int]{} // no suggested fix +diff -urN a/gopls/internal/lsp/testdata/missingfunction/tuple.go b/gopls/internal/lsp/testdata/missingfunction/tuple.go +--- a/gopls/internal/lsp/testdata/missingfunction/tuple.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/tuple.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package missingfunction - --type basicStructWithTypeParams[T any] struct { -- foo T +-func tuple() { +- undefinedTuple(b()) //@suggestedfix("undefinedTuple", "quickfix", "") -} - --var _ = basicStructWithTypeParams[int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --type twoArgStructWithTypeParams[F, B any] struct { -- foo F -- bar B +-func b() (string, error) { +- return "", nil -} +diff -urN a/gopls/internal/lsp/testdata/missingfunction/tuple.go.golden b/gopls/internal/lsp/testdata/missingfunction/tuple.go.golden +--- a/gopls/internal/lsp/testdata/missingfunction/tuple.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/tuple.go.golden 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +--- suggestedfix_tuple_4_2 -- +-package missingfunction - --var _ = twoArgStructWithTypeParams[string, int]{ -- foo: "", -- bar: 0, --} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func tuple() { +- undefinedTuple(b()) //@suggestedfix("undefinedTuple", "quickfix", "") +-} - --var _ = twoArgStructWithTypeParams[int, string]{ -- bar: "bar", --} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func undefinedTuple(s string, err error) { +- panic("unimplemented") +-} - --type nestedStructWithTypeParams struct { -- bar string -- basic basicStructWithTypeParams[int] +-func b() (string, error) { +- return "", nil -} - --var _ = nestedStructWithTypeParams{} //@suggestedfix("}", "refactor.rewrite", "Fill") +diff -urN a/gopls/internal/lsp/testdata/missingfunction/unique_params.go b/gopls/internal/lsp/testdata/missingfunction/unique_params.go +--- a/gopls/internal/lsp/testdata/missingfunction/unique_params.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/unique_params.go 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package missingfunction - --func _[T any]() { -- type S struct{ t T } -- _ = S{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func uniqueArguments() { +- var s string +- var i int +- undefinedUniqueArguments(s, i, s) //@suggestedfix("undefinedUniqueArguments", "quickfix", "") -} +diff -urN a/gopls/internal/lsp/testdata/missingfunction/unique_params.go.golden b/gopls/internal/lsp/testdata/missingfunction/unique_params.go.golden +--- a/gopls/internal/lsp/testdata/missingfunction/unique_params.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/missingfunction/unique_params.go.golden 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +--- suggestedfix_unique_params_6_2 -- +-package missingfunction - ---- suggestedfix_typeparams_25_1 -- --//go:build go1.18 --// +build go1.18 +-func uniqueArguments() { +- var s string +- var i int +- undefinedUniqueArguments(s, i, s) //@suggestedfix("undefinedUniqueArguments", "quickfix", "") +-} - --package fillstruct +-func undefinedUniqueArguments(s1 string, i int, s2 string) { +- panic("unimplemented") +-} - --type emptyStructWithTypeParams[A any] struct{} +diff -urN a/gopls/internal/lsp/testdata/multireturn/multi_return.go.in b/gopls/internal/lsp/testdata/multireturn/multi_return.go.in +--- a/gopls/internal/lsp/testdata/multireturn/multi_return.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/multireturn/multi_return.go.in 1970-01-01 08:00:00 +@@ -1,48 +0,0 @@ +-package multireturn - --var _ = emptyStructWithTypeParams[int]{} // no suggested fix +-func f0() {} //@item(multiF0, "f0", "func()", "func") - --type basicStructWithTypeParams[T any] struct { -- foo T --} +-func f1(int) int { return 0 } //@item(multiF1, "f1", "func(int) int", "func") - --var _ = basicStructWithTypeParams[int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func f2(int, int) (int, int) { return 0, 0 } //@item(multiF2, "f2", "func(int, int) (int, int)", "func") - --type twoArgStructWithTypeParams[F, B any] struct { -- foo F -- bar B --} +-func f2Str(string, string) (string, string) { return "", "" } //@item(multiF2Str, "f2Str", "func(string, string) (string, string)", "func") - --var _ = twoArgStructWithTypeParams[string, int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func f3(int, int, int) (int, int, int) { return 0, 0, 0 } //@item(multiF3, "f3", "func(int, int, int) (int, int, int)", "func") - --var _ = twoArgStructWithTypeParams[int, string]{ -- foo: 0, -- bar: "bar", --} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func _() { +- _ := f //@rank(" //", multiF1, multiF2) - --type nestedStructWithTypeParams struct { -- bar string -- basic basicStructWithTypeParams[int] --} +- _, _ := f //@rank(" //", multiF2, multiF0),rank(" //", multiF1, multiF0) - --var _ = nestedStructWithTypeParams{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- _, _ := _, f //@rank(" //", multiF1, multiF2),rank(" //", multiF1, multiF0) - --func _[T any]() { -- type S struct{ t T } -- _ = S{} //@suggestedfix("}", "refactor.rewrite", "Fill") --} +- _, _ := f, abc //@rank(", abc", multiF1, multiF2) - ---- suggestedfix_typeparams_32_36 -- --//go:build go1.18 --// +build go1.18 +- f1() //@rank(")", multiF1, multiF0) +- f1(f) //@rank(")", multiF1, multiF2) +- f2(f) //@rank(")", multiF2, multiF3),rank(")", multiF1, multiF3) +- f2(1, f) //@rank(")", multiF1, multiF2),rank(")", multiF1, multiF0) +- f2(1, ) //@rank(")", multiF1, multiF2),rank(")", multiF1, multiF0) +- f2Str() //@rank(")", multiF2Str, multiF2) - --package fillstruct +- var i int +- i, _ := f //@rank(" //", multiF2, multiF2Str) - --type emptyStructWithTypeParams[A any] struct{} +- var s string +- _, s := f //@rank(" //", multiF2Str, multiF2) - --var _ = emptyStructWithTypeParams[int]{} // no suggested fix +- banana, s = f //@rank(" //", multiF2, multiF3) - --type basicStructWithTypeParams[T any] struct { -- foo T +- var variadic func(int, ...int) +- variadic() //@rank(")", multiF1, multiF0),rank(")", multiF2, multiF0),rank(")", multiF3, multiF0) -} - --var _ = basicStructWithTypeParams[int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func _() { +- var baz func(...interface{}) - --type twoArgStructWithTypeParams[F, B any] struct { -- foo F -- bar B --} +- var otterNap func() (int, int) //@item(multiTwo, "otterNap", "func() (int, int)", "var") +- var one int //@item(multiOne, "one", "int", "var") - --var _ = twoArgStructWithTypeParams[string, int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- baz(on) //@rank(")", multiOne, multiTwo) +-} +diff -urN a/gopls/internal/lsp/testdata/nested_complit/nested_complit.go.in b/gopls/internal/lsp/testdata/nested_complit/nested_complit.go.in +--- a/gopls/internal/lsp/testdata/nested_complit/nested_complit.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/nested_complit/nested_complit.go.in 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +-package nested_complit - --var _ = twoArgStructWithTypeParams[int, string]{ -- bar: "bar", --} //@suggestedfix("}", "refactor.rewrite", "Fill") +-type ncFoo struct {} //@item(structNCFoo, "ncFoo", "struct{...}", "struct") - --type nestedStructWithTypeParams struct { -- bar string -- basic basicStructWithTypeParams[int] +-type ncBar struct { //@item(structNCBar, "ncBar", "struct{...}", "struct") +- baz []ncFoo -} - --var _ = nestedStructWithTypeParams{ -- bar: "", -- basic: basicStructWithTypeParams{}, --} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --func _[T any]() { -- type S struct{ t T } -- _ = S{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func _() { +- []ncFoo{} //@item(litNCFoo, "[]ncFoo{}", "", "var") +- _ := ncBar{ +- // disabled - see issue #54822 +- baz: [] // complete(" //", structNCFoo, structNCBar) +- } -} +diff -urN a/gopls/internal/lsp/testdata/printf/printf.go b/gopls/internal/lsp/testdata/printf/printf.go +--- a/gopls/internal/lsp/testdata/printf/printf.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/printf/printf.go 1970-01-01 08:00:00 +@@ -1,33 +0,0 @@ +-package printf - ---- suggestedfix_typeparams_36_8 -- --//go:build go1.18 --// +build go1.18 +-import "fmt" - --package fillstruct +-func myPrintf(string, ...interface{}) {} - --type emptyStructWithTypeParams[A any] struct{} +-func _() { +- var ( +- aInt int //@item(printfInt, "aInt", "int", "var") +- aFloat float64 //@item(printfFloat, "aFloat", "float64", "var") +- aString string //@item(printfString, "aString", "string", "var") +- aBytes []byte //@item(printfBytes, "aBytes", "[]byte", "var") +- aStringer fmt.Stringer //@item(printfStringer, "aStringer", "fmt.Stringer", "var") +- aError error //@item(printfError, "aError", "error", "var") +- aBool bool //@item(printfBool, "aBool", "bool", "var") +- ) - --var _ = emptyStructWithTypeParams[int]{} // no suggested fix +- myPrintf("%d", a) //@rank(")", printfInt, printfFloat) +- myPrintf("%s", a) //@rank(")", printfString, printfInt),rank(")", printfBytes, printfInt),rank(")", printfStringer, printfInt),rank(")", printfError, printfInt) +- myPrintf("%w", a) //@rank(")", printfError, printfInt) +- myPrintf("%x %[1]b", a) //@rank(")", printfInt, printfString) - --type basicStructWithTypeParams[T any] struct { -- foo T --} +- fmt.Printf("%t", a) //@rank(")", printfBool, printfInt) - --var _ = basicStructWithTypeParams[int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") +- fmt.Fprintf(nil, "%f", a) //@rank(")", printfFloat, printfInt) - --type twoArgStructWithTypeParams[F, B any] struct { -- foo F -- bar B +- fmt.Sprintf("%[2]q %[1]*.[3]*[4]f", +- a, //@rank(",", printfInt, printfFloat) +- a, //@rank(",", printfString, printfFloat) +- a, //@rank(",", printfInt, printfFloat) +- a, //@rank(",", printfFloat, printfInt) +- ) -} +diff -urN a/gopls/internal/lsp/testdata/rank/assign_rank.go.in b/gopls/internal/lsp/testdata/rank/assign_rank.go.in +--- a/gopls/internal/lsp/testdata/rank/assign_rank.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rank/assign_rank.go.in 1970-01-01 08:00:00 +@@ -1,19 +0,0 @@ +-package rank - --var _ = twoArgStructWithTypeParams[string, int]{} //@suggestedfix("}", "refactor.rewrite", "Fill") -- --var _ = twoArgStructWithTypeParams[int, string]{ -- bar: "bar", --} //@suggestedfix("}", "refactor.rewrite", "Fill") +-var ( +- apple int = 3 //@item(apple, "apple", "int", "var") +- pear string = "hello" //@item(pear, "pear", "string", "var") +-) - --type nestedStructWithTypeParams struct { -- bar string -- basic basicStructWithTypeParams[int] +-func _() { +- orange := 1 //@item(orange, "orange", "int", "var") +- grape := "hello" //@item(grape, "grape", "string", "var") +- orange, grape = 2, "hello" //@complete(" \"", grape, pear, orange, apple) -} - --var _ = nestedStructWithTypeParams{} //@suggestedfix("}", "refactor.rewrite", "Fill") +-func _() { +- var pineapple int //@item(pineapple, "pineapple", "int", "var") +- pineapple = 1 //@complete(" 1", pineapple, apple, pear) - --func _[T any]() { -- type S struct{ t T } -- _ = S{ -- t: *new(T), -- } //@suggestedfix("}", "refactor.rewrite", "Fill") +- y := //@complete(" /", pineapple, apple, pear) -} +diff -urN a/gopls/internal/lsp/testdata/rank/binexpr_rank.go.in b/gopls/internal/lsp/testdata/rank/binexpr_rank.go.in +--- a/gopls/internal/lsp/testdata/rank/binexpr_rank.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rank/binexpr_rank.go.in 1970-01-01 08:00:00 +@@ -1,8 +0,0 @@ +-package rank - -diff -urN a/gopls/internal/lsp/testdata/folding/a.go b/gopls/internal/lsp/testdata/folding/a.go ---- a/gopls/internal/lsp/testdata/folding/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/folding/a.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,75 +0,0 @@ --package folding //@fold("package") -- --import ( -- "fmt" -- _ "log" --) +-func _() { +- _ = 5 + ; //@complete(" ;", apple, pear) +- y := + 5; //@complete(" +", apple, pear) - --import _ "os" +- if 6 == {} //@complete(" {", apple, pear) +-} +diff -urN a/gopls/internal/lsp/testdata/rank/boolexpr_rank.go b/gopls/internal/lsp/testdata/rank/boolexpr_rank.go +--- a/gopls/internal/lsp/testdata/rank/boolexpr_rank.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rank/boolexpr_rank.go 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package rank - --// bar is a function. --// With a multiline doc comment. --func bar() string { -- /* This is a single line comment */ -- switch { -- case true: -- if true { -- fmt.Println("true") -- } else { -- fmt.Println("false") -- } -- case false: -- fmt.Println("false") -- default: -- fmt.Println("default") +-func _() { +- someRandomBoolFunc := func() bool { //@item(boolExprFunc, "someRandomBoolFunc", "func() bool", "var") +- return true - } -- /* This is a multiline -- block -- comment */ - -- /* This is a multiline -- block -- comment */ -- // Followed by another comment. -- _ = []int{ -- 1, -- 2, -- 3, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{ -- "a": 1, -- "b": 2, -- "c": 3, -- } -- type T struct { -- f string -- g int -- h string -- } -- _ = T{ -- f: "j", -- g: 4, -- h: "i", -- } -- x, y := make(chan bool), make(chan bool) -- select { -- case val := <-x: -- if val { -- fmt.Println("true from x") -- } else { -- fmt.Println("false from x") -- } -- case <-y: -- fmt.Println("y") -- default: -- fmt.Println("default") +- var foo, bar int //@item(boolExprBar, "bar", "int", "var") +- if foo == 123 && b { //@rank(" {", boolExprBar, boolExprFunc) - } -- // This is a multiline comment -- // that is not a doc comment. -- return ` --this string --is not indented` -} -diff -urN a/gopls/internal/lsp/testdata/folding/a.go.golden b/gopls/internal/lsp/testdata/folding/a.go.golden ---- a/gopls/internal/lsp/testdata/folding/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/folding/a.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,722 +0,0 @@ ---- foldingRange-0 -- --package folding //@fold("package") -- --import (<>) -- --import _ "os" -- --// bar is a function.<> --func bar(<>) string {<>} -- ---- foldingRange-1 -- --package folding //@fold("package") -- --import ( -- "fmt" -- _ "log" --) +diff -urN a/gopls/internal/lsp/testdata/rank/convert_rank.go.in b/gopls/internal/lsp/testdata/rank/convert_rank.go.in +--- a/gopls/internal/lsp/testdata/rank/convert_rank.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rank/convert_rank.go.in 1970-01-01 08:00:00 +@@ -1,54 +0,0 @@ +-package rank - --import _ "os" +-import "time" - --// bar is a function. --// With a multiline doc comment. --func bar() string { -- /* This is a single line comment */ -- switch {<>} -- /* This is a multiline<> +-func _() { +- type strList []string +- wantsStrList := func(strList) {} - -- /* This is a multiline<> -- _ = []int{<>} -- _ = [2]string{<>} -- _ = map[string]int{<>} -- type T struct {<>} -- _ = T{<>} -- x, y := make(<>), make(<>) -- select {<>} -- // This is a multiline comment<> -- return <> +- var ( +- convA string //@item(convertA, "convA", "string", "var") +- convB []string //@item(convertB, "convB", "[]string", "var") +- ) +- wantsStrList(strList(conv)) //@complete("))", convertB, convertA) -} - ---- foldingRange-2 -- --package folding //@fold("package") +-func _() { +- type myInt int - --import ( -- "fmt" -- _ "log" --) +- const ( +- convC = "hi" //@item(convertC, "convC", "string", "const") +- convD = 123 //@item(convertD, "convD", "int", "const") +- convE int = 123 //@item(convertE, "convE", "int", "const") +- convF string = "there" //@item(convertF, "convF", "string", "const") +- convG myInt = 123 //@item(convertG, "convG", "myInt", "const") +- ) - --import _ "os" +- var foo int +- foo = conv //@rank(" //", convertE, convertD) - --// bar is a function. --// With a multiline doc comment. --func bar() string { -- /* This is a single line comment */ -- switch { -- case true:<> -- case false:<> -- default:<> -- } -- /* This is a multiline -- block -- comment */ +- var mi myInt +- mi = conv //@rank(" //", convertG, convertD, convertE) +- mi + conv //@rank(" //", convertG, convertD, convertE) - -- /* This is a multiline -- block -- comment */ -- // Followed by another comment. -- _ = []int{ -- 1, -- 2, -- 3, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{ -- "a": 1, -- "b": 2, -- "c": 3, -- } -- type T struct { -- f string -- g int -- h string -- } -- _ = T{ -- f: "j", -- g: 4, -- h: "i", -- } -- x, y := make(chan bool), make(chan bool) -- select { -- case val := <-x:<> -- case <-y:<> -- default:<> -- } -- // This is a multiline comment -- // that is not a doc comment. -- return ` --this string --is not indented` --} +- 1 + conv //@rank(" //", convertD, convertC),rank(" //", convertE, convertC),rank(" //", convertG, convertC) - ---- foldingRange-3 -- --package folding //@fold("package") +- type myString string +- var ms myString +- ms = conv //@rank(" //", convertC, convertF) - --import ( -- "fmt" -- _ "log" --) +- type myUint uint32 +- var mu myUint +- mu = conv //@rank(" //", convertD, convertE) - --import _ "os" +- // don't downrank constants when assigning to interface{} +- var _ interface{} = c //@rank(" //", convertD, complex) - --// bar is a function. --// With a multiline doc comment. --func bar() string { -- /* This is a single line comment */ -- switch { -- case true: -- if true {<>} else {<>} -- case false: -- fmt.Println(<>) -- default: -- fmt.Println(<>) -- } -- /* This is a multiline -- block -- comment */ +- var _ time.Duration = conv //@rank(" //", convertD, convertE),snippet(" //", convertE, "time.Duration(convE)", "time.Duration(convE)") - -- /* This is a multiline -- block -- comment */ -- // Followed by another comment. -- _ = []int{ -- 1, -- 2, -- 3, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{ -- "a": 1, -- "b": 2, -- "c": 3, -- } -- type T struct { -- f string -- g int -- h string -- } -- _ = T{ -- f: "j", -- g: 4, -- h: "i", -- } -- x, y := make(chan bool), make(chan bool) -- select { -- case val := <-x: -- if val {<>} else {<>} -- case <-y: -- fmt.Println(<>) -- default: -- fmt.Println(<>) -- } -- // This is a multiline comment -- // that is not a doc comment. -- return ` --this string --is not indented` --} +- var convP myInt //@item(convertP, "convP", "myInt", "var") +- var _ *int = conv //@snippet(" //", convertP, "(*int)(&convP)", "(*int)(&convP)") - ---- foldingRange-4 -- --package folding //@fold("package") +- var ff float64 //@item(convertFloat, "ff", "float64", "var") +- f == convD //@snippet(" =", convertFloat, "ff", "ff") +-} +diff -urN a/gopls/internal/lsp/testdata/rank/struct/struct_rank.go b/gopls/internal/lsp/testdata/rank/struct/struct_rank.go +--- a/gopls/internal/lsp/testdata/rank/struct/struct_rank.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rank/struct/struct_rank.go 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package struct_rank - --import ( -- "fmt" -- _ "log" --) +-type foo struct { +- c int //@item(c_rank, "c", "int", "field") +- b int //@item(b_rank, "b", "int", "field") +- a int //@item(a_rank, "a", "int", "field") +-} - --import _ "os" +-func f() { +- foo := foo{} //@rank("}", c_rank, b_rank, a_rank) +-} +diff -urN a/gopls/internal/lsp/testdata/rank/switch_rank.go.in b/gopls/internal/lsp/testdata/rank/switch_rank.go.in +--- a/gopls/internal/lsp/testdata/rank/switch_rank.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rank/switch_rank.go.in 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +-package rank - --// bar is a function. --// With a multiline doc comment. --func bar() string { -- /* This is a single line comment */ -- switch { -- case true: -- if true { -- fmt.Println(<>) -- } else { -- fmt.Println(<>) -- } -- case false: -- fmt.Println("false") -- default: -- fmt.Println("default") -- } -- /* This is a multiline -- block -- comment */ +-import "time" - -- /* This is a multiline -- block -- comment */ -- // Followed by another comment. -- _ = []int{ -- 1, -- 2, -- 3, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{ -- "a": 1, -- "b": 2, -- "c": 3, -- } -- type T struct { -- f string -- g int -- h string -- } -- _ = T{ -- f: "j", -- g: 4, -- h: "i", -- } -- x, y := make(chan bool), make(chan bool) -- select { -- case val := <-x: -- if val { -- fmt.Println(<>) -- } else { -- fmt.Println(<>) -- } -- case <-y: -- fmt.Println("y") -- default: -- fmt.Println("default") +-func _() { +- switch pear { +- case _: //@rank("_", pear, apple) - } -- // This is a multiline comment -- // that is not a doc comment. -- return ` --this string --is not indented` --} -- ---- foldingRange-comment-0 -- --package folding //@fold("package") - --import ( -- "fmt" -- _ "log" --) +- time.Monday //@item(timeMonday, "time.Monday", "time.Weekday", "const"),item(monday ,"Monday", "time.Weekday", "const") +- time.Friday //@item(timeFriday, "time.Friday", "time.Weekday", "const"),item(friday ,"Friday", "time.Weekday", "const") - --import _ "os" +- now := time.Now() +- now.Weekday //@item(nowWeekday, "now.Weekday", "func() time.Weekday", "method") - --// bar is a function.<> --func bar() string { -- /* This is a single line comment */ -- switch { -- case true: -- if true { -- fmt.Println("true") -- } else { -- fmt.Println("false") -- } -- case false: -- fmt.Println("false") -- default: -- fmt.Println("default") -- } -- /* This is a multiline<> +- then := time.Now() +- then.Weekday //@item(thenWeekday, "then.Weekday", "func() time.Weekday", "method") - -- /* This is a multiline<> -- _ = []int{ -- 1, -- 2, -- 3, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{ -- "a": 1, -- "b": 2, -- "c": 3, -- } -- type T struct { -- f string -- g int -- h string -- } -- _ = T{ -- f: "j", -- g: 4, -- h: "i", -- } -- x, y := make(chan bool), make(chan bool) -- select { -- case val := <-x: -- if val { -- fmt.Println("true from x") -- } else { -- fmt.Println("false from x") -- } -- case <-y: -- fmt.Println("y") -- default: -- fmt.Println("default") +- switch time.Weekday(0) { +- case time.Monday, time.Tuesday: +- case time.Wednesday, time.Thursday: +- case time.Saturday, time.Sunday: +- case t: //@rank(":", timeFriday, timeMonday) +- case time.: //@rank(":", friday, monday) +- +- case now.Weekday(): +- case week: //@rank(":", thenWeekday, nowWeekday) - } -- // This is a multiline comment<> -- return ` --this string --is not indented` -} +diff -urN a/gopls/internal/lsp/testdata/rank/type_assert_rank.go.in b/gopls/internal/lsp/testdata/rank/type_assert_rank.go.in +--- a/gopls/internal/lsp/testdata/rank/type_assert_rank.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rank/type_assert_rank.go.in 1970-01-01 08:00:00 +@@ -1,8 +0,0 @@ +-package rank - ---- foldingRange-imports-0 -- --package folding //@fold("package") +-func _() { +- type flower int //@item(flower, "flower", "int", "type") +- var fig string //@item(fig, "fig", "string", "var") - --import (<>) +- _ = interface{}(nil).(f) //@complete(") //", flower) +-} +diff -urN a/gopls/internal/lsp/testdata/rank/type_switch_rank.go.in b/gopls/internal/lsp/testdata/rank/type_switch_rank.go.in +--- a/gopls/internal/lsp/testdata/rank/type_switch_rank.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rank/type_switch_rank.go.in 1970-01-01 08:00:00 +@@ -1,31 +0,0 @@ +-package rank - --import _ "os" +-import ( +- "fmt" +- "go/ast" +-) - --// bar is a function. --// With a multiline doc comment. --func bar() string { -- /* This is a single line comment */ -- switch { -- case true: -- if true { -- fmt.Println("true") -- } else { -- fmt.Println("false") -- } -- case false: -- fmt.Println("false") -- default: -- fmt.Println("default") -- } -- /* This is a multiline -- block -- comment */ +-func _() { +- type basket int //@item(basket, "basket", "int", "type") +- var banana string //@item(banana, "banana", "string", "var") - -- /* This is a multiline -- block -- comment */ -- // Followed by another comment. -- _ = []int{ -- 1, -- 2, -- 3, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{ -- "a": 1, -- "b": 2, -- "c": 3, -- } -- type T struct { -- f string -- g int -- h string +- switch interface{}(pear).(type) { +- case b: //@complete(":", basket) +- b //@complete(" //", banana, basket) - } -- _ = T{ -- f: "j", -- g: 4, -- h: "i", +- +- Ident //@item(astIdent, "Ident", "struct{...}", "struct") +- IfStmt //@item(astIfStmt, "IfStmt", "struct{...}", "struct") +- +- switch ast.Node(nil).(type) { +- case *ast.Ident: +- case *ast.I: //@rank(":", astIfStmt, astIdent) - } -- x, y := make(chan bool), make(chan bool) -- select { -- case val := <-x: -- if val { -- fmt.Println("true from x") -- } else { -- fmt.Println("false from x") -- } -- case <-y: -- fmt.Println("y") -- default: -- fmt.Println("default") +- +- Stringer //@item(fmtStringer, "Stringer", "interface{...}", "interface") +- GoStringer //@item(fmtGoStringer, "GoStringer", "interface{...}", "interface") +- +- switch interface{}(nil).(type) { +- case fmt.Stringer: //@rank(":", fmtStringer, fmtGoStringer) - } -- // This is a multiline comment -- // that is not a doc comment. -- return ` --this string --is not indented` -} +diff -urN a/gopls/internal/lsp/testdata/rename/a/random.go.golden b/gopls/internal/lsp/testdata/rename/a/random.go.golden +--- a/gopls/internal/lsp/testdata/rename/a/random.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/a/random.go.golden 1970-01-01 08:00:00 +@@ -1,616 +0,0 @@ +--- GetSum-rename -- +-package a - ---- foldingRange-lineFolding-0 -- --package folding //@fold("package") -- --import (<> +-import ( +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") -) - --import _ "os" +-func Random() int { +- y := 6 + 7 +- return y +-} - --// bar is a function.<> --func bar() string {<> +-func Random2(y int) int { //@rename("y", "z") +- return y -} - ---- foldingRange-lineFolding-1 -- --package folding //@fold("package") +-type Pos struct { +- x, y int +-} - --import ( -- "fmt" -- _ "log" --) +-func (p *Pos) GetSum() int { +- return p.x + p.y //@rename("x", "myX") +-} - --import _ "os" +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.GetSum() //@rename("Sum", "GetSum") +-} - --// bar is a function. --// With a multiline doc comment. --func bar() string { -- /* This is a single line comment */ -- switch {<> -- } -- /* This is a multiline<> +-func sw() { +- var x interface{} - -- /* This is a multiline<> -- _ = []int{<>, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{<>, -- } -- type T struct {<> -- } -- _ = T{<>, -- } -- x, y := make(chan bool), make(chan bool) -- select {<> +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") - } -- // This is a multiline comment<> -- return <> -} - ---- foldingRange-lineFolding-2 -- --package folding //@fold("package") +--- f2name-rename -- +-package a - -import ( -- "fmt" -- _ "log" +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2name "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") -) - --import _ "os" +-func Random() int { +- y := 6 + 7 +- return y +-} - --// bar is a function. --// With a multiline doc comment. --func bar() string { -- /* This is a single line comment */ -- switch { -- case true:<> -- case false:<> -- default:<> -- } -- /* This is a multiline -- block -- comment */ +-func Random2(y int) int { //@rename("y", "z") +- return y +-} - -- /* This is a multiline -- block -- comment */ -- // Followed by another comment. -- _ = []int{ -- 1, -- 2, -- 3, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{ -- "a": 1, -- "b": 2, -- "c": 3, -- } -- type T struct { -- f string -- g int -- h string -- } -- _ = T{ -- f: "j", -- g: 4, -- h: "i", -- } -- x, y := make(chan bool), make(chan bool) -- select { -- case val := <-x:<> -- case <-y:<> -- default:<> +-type Pos struct { +- x, y int +-} +- +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") +-} +- +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} +- +-func sw() { +- var x interface{} +- +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") +- default: +- f2name.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") - } -- // This is a multiline comment -- // that is not a doc comment. -- return ` --this string --is not indented` -} - ---- foldingRange-lineFolding-3 -- --package folding //@fold("package") +--- f2y-rename -- +-package a - -import ( -- "fmt" -- _ "log" +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2y "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") -) - --import _ "os" +-func Random() int { +- y := 6 + 7 +- return y +-} - --// bar is a function. --// With a multiline doc comment. --func bar() string { -- /* This is a single line comment */ -- switch { -- case true: -- if true {<> -- } else {<> -- } -- case false: -- fmt.Println("false") -- default: -- fmt.Println("default") -- } -- /* This is a multiline -- block -- comment */ +-func Random2(y int) int { //@rename("y", "z") +- return y +-} - -- /* This is a multiline -- block -- comment */ -- // Followed by another comment. -- _ = []int{ -- 1, -- 2, -- 3, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{ -- "a": 1, -- "b": 2, -- "c": 3, -- } -- type T struct { -- f string -- g int -- h string -- } -- _ = T{ -- f: "j", -- g: 4, -- h: "i", -- } -- x, y := make(chan bool), make(chan bool) -- select { -- case val := <-x: -- if val {<> -- } else {<> -- } -- case <-y: -- fmt.Println("y") +-type Pos struct { +- x, y int +-} +- +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") +-} +- +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} +- +-func sw() { +- var x interface{} +- +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") - default: -- fmt.Println("default") +- f2y.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") - } -- // This is a multiline comment -- // that is not a doc comment. -- return ` --this string --is not indented` -} - ---- foldingRange-lineFolding-comment-0 -- --package folding //@fold("package") +--- fmt2-rename -- +-package a - -import ( -- "fmt" -- _ "log" +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- fmt2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") -) - --import _ "os" +-func Random() int { +- y := 6 + 7 +- return y +-} - --// bar is a function.<> --func bar() string { -- /* This is a single line comment */ -- switch { -- case true: -- if true { -- fmt.Println("true") -- } else { -- fmt.Println("false") -- } -- case false: -- fmt.Println("false") -- default: -- fmt.Println("default") -- } -- /* This is a multiline<> +-func Random2(y int) int { //@rename("y", "z") +- return y +-} - -- /* This is a multiline<> -- _ = []int{ -- 1, -- 2, -- 3, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{ -- "a": 1, -- "b": 2, -- "c": 3, -- } -- type T struct { -- f string -- g int -- h string -- } -- _ = T{ -- f: "j", -- g: 4, -- h: "i", -- } -- x, y := make(chan bool), make(chan bool) -- select { -- case val := <-x: -- if val { -- fmt.Println("true from x") -- } else { -- fmt.Println("false from x") -- } -- case <-y: -- fmt.Println("y") +-type Pos struct { +- x, y int +-} +- +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") +-} +- +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} +- +-func sw() { +- var x interface{} +- +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") - default: -- fmt.Println("default") +- fmt2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") - } -- // This is a multiline comment<> -- return ` --this string --is not indented` -} - ---- foldingRange-lineFolding-imports-0 -- --package folding //@fold("package") +--- fmty-rename -- +-package a - --import (<> +-import ( +- lg "log" +- fmty "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") -) - --import _ "os" +-func Random() int { +- y := 6 + 7 +- return y +-} - --// bar is a function. --// With a multiline doc comment. --func bar() string { -- /* This is a single line comment */ -- switch { -- case true: -- if true { -- fmt.Println("true") -- } else { -- fmt.Println("false") -- } -- case false: -- fmt.Println("false") -- default: -- fmt.Println("default") -- } -- /* This is a multiline -- block -- comment */ +-func Random2(y int) int { //@rename("y", "z") +- return y +-} - -- /* This is a multiline -- block -- comment */ -- // Followed by another comment. -- _ = []int{ -- 1, -- 2, -- 3, -- } -- _ = [2]string{"d", -- "e", -- } -- _ = map[string]int{ -- "a": 1, -- "b": 2, -- "c": 3, -- } -- type T struct { -- f string -- g int -- h string -- } -- _ = T{ -- f: "j", -- g: 4, -- h: "i", -- } -- x, y := make(chan bool), make(chan bool) -- select { -- case val := <-x: -- if val { -- fmt.Println("true from x") -- } else { -- fmt.Println("false from x") -- } -- case <-y: -- fmt.Println("y") +-type Pos struct { +- x, y int +-} +- +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") +-} +- +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} +- +-func sw() { +- var x interface{} +- +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- fmty.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") - default: -- fmt.Println("default") +- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") - } -- // This is a multiline comment -- // that is not a doc comment. -- return ` --this string --is not indented` -} - -diff -urN a/gopls/internal/lsp/testdata/folding/bad.go.golden b/gopls/internal/lsp/testdata/folding/bad.go.golden ---- a/gopls/internal/lsp/testdata/folding/bad.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/folding/bad.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,81 +0,0 @@ ---- foldingRange-0 -- --package folding //@fold("package") +--- format-rename -- +-package a - --import (<>) +-import ( +- lg "log" +- format "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") +-) - --import (<>) -- --// badBar is a function. --func badBar(<>) string {<>} +-func Random() int { +- y := 6 + 7 +- return y +-} - ---- foldingRange-1 -- --package folding //@fold("package") +-func Random2(y int) int { //@rename("y", "z") +- return y +-} - --import ( "fmt" -- _ "log" --) +-type Pos struct { +- x, y int +-} - --import ( -- _ "os" ) -- --// badBar is a function. --func badBar() string { x := true -- if x {<>} else {<>} -- return +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") -} - ---- foldingRange-2 -- --package folding //@fold("package") +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} - --import ( "fmt" -- _ "log" +-func sw() { +- var x interface{} +- +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- format.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- } +-} +- +--- log-rename -- +-package a +- +-import ( +- "log" +- "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") -) - --import ( -- _ "os" ) -- --// badBar is a function. --func badBar() string { x := true -- if x { -- // This is the only foldable thing in this file when lineFoldingOnly -- fmt.Println(<>) -- } else { -- fmt.Println(<>) } -- return +-func Random() int { +- y := 6 + 7 +- return y -} - ---- foldingRange-imports-0 -- --package folding //@fold("package") +-func Random2(y int) int { //@rename("y", "z") +- return y +-} - --import (<>) +-type Pos struct { +- x, y int +-} - --import (<>) -- --// badBar is a function. --func badBar() string { x := true -- if x { -- // This is the only foldable thing in this file when lineFoldingOnly -- fmt.Println("true") -- } else { -- fmt.Println("false") } -- return +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") -} - ---- foldingRange-lineFolding-0 -- --package folding //@fold("package") +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} - --import ( "fmt" -- _ "log" --) +-func sw() { +- var x interface{} - --import ( -- _ "os" ) -- --// badBar is a function. --func badBar() string { x := true -- if x {<> -- } else { -- fmt.Println("false") } -- return +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- log.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- } -} - -diff -urN a/gopls/internal/lsp/testdata/folding/bad.go.in b/gopls/internal/lsp/testdata/folding/bad.go.in ---- a/gopls/internal/lsp/testdata/folding/bad.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/folding/bad.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ --package folding //@fold("package") +--- myX-rename -- +-package a - --import ( "fmt" -- _ "log" +-import ( +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") -) - --import ( -- _ "os" ) -- --// badBar is a function. --func badBar() string { x := true -- if x { -- // This is the only foldable thing in this file when lineFoldingOnly -- fmt.Println("true") -- } else { -- fmt.Println("false") } -- return +-func Random() int { +- y := 6 + 7 +- return y -} -diff -urN a/gopls/internal/lsp/testdata/foo/foo.go b/gopls/internal/lsp/testdata/foo/foo.go ---- a/gopls/internal/lsp/testdata/foo/foo.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/foo/foo.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,30 +0,0 @@ --package foo //@mark(PackageFoo, "foo"),item(PackageFoo, "foo", "\"golang.org/lsptests/foo\"", "package") - --type StructFoo struct { //@item(StructFoo, "StructFoo", "struct{...}", "struct") -- Value int //@item(Value, "Value", "int", "field") +-func Random2(y int) int { //@rename("y", "z") +- return y -} - --// Pre-set this marker, as we don't have a "source" for it in this package. --/* Error() */ //@item(Error, "Error", "func() string", "method") +-type Pos struct { +- myX, y int +-} - --func Foo() { //@item(Foo, "Foo", "func()", "func") -- var err error -- err.Error() //@complete("E", Error) +-func (p *Pos) Sum() int { +- return p.myX + p.y //@rename("x", "myX") -} - -func _() { -- var sFoo StructFoo //@mark(sFoo1, "sFoo"),complete("t", StructFoo) -- if x := sFoo; x.Value == 1 { //@mark(sFoo2, "sFoo"),complete("V", Value),typdef("sFoo", StructFoo),refs("sFo", sFoo1, sFoo2) -- return -- } +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") -} - --func _() { -- shadowed := 123 -- { -- shadowed := "hi" //@item(shadowed, "shadowed", "string", "var"),refs("shadowed", shadowed) -- sha //@complete("a", shadowed) +-func sw() { +- var x interface{} +- +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") - } -} - --type IntFoo int //@item(IntFoo, "IntFoo", "int", "type") -diff -urN a/gopls/internal/lsp/testdata/format/bad_format.go.golden b/gopls/internal/lsp/testdata/format/bad_format.go.golden ---- a/gopls/internal/lsp/testdata/format/bad_format.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/format/bad_format.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,21 +0,0 @@ ---- gofmt -- --package format //@format("package") +--- pos-rename -- +-package a - -import ( -- "fmt" -- "log" -- "runtime" +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") -) - --func hello() { +-func Random() int { +- y := 6 + 7 +- return y +-} - -- var x int //@diag("x", "compiler", "x declared (and|but) not used", "error") +-func Random2(y int) int { //@rename("y", "z") +- return y -} - --func hi() { -- runtime.GOROOT() -- fmt.Printf("") +-type Pos struct { +- x, y int +-} - -- log.Printf("") +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") -} - -diff -urN a/gopls/internal/lsp/testdata/format/bad_format.go.in b/gopls/internal/lsp/testdata/format/bad_format.go.in ---- a/gopls/internal/lsp/testdata/format/bad_format.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/format/bad_format.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,22 +0,0 @@ --package format //@format("package") +-func _() { +- var pos Pos //@rename("p", "pos") +- _ = pos.Sum() //@rename("Sum", "GetSum") +-} +- +-func sw() { +- var x interface{} +- +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- } +-} +- +--- y0-rename -- +-package a - -import ( -- "runtime" -- "fmt" -- "log" +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") -) - --func hello() { +-func Random() int { +- y := 6 + 7 +- return y +-} - +-func Random2(y int) int { //@rename("y", "z") +- return y +-} - +-type Pos struct { +- x, y int +-} - +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") +-} - -- var x int //@diag("x", "compiler", "x declared (and|but) not used", "error") +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") -} - --func hi() { -- runtime.GOROOT() -- fmt.Printf("") +-func sw() { +- var x interface{} - -- log.Printf("") +- switch y0 := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y0) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y0) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y0) //@rename("y", "y3"),rename("f2","fmt2") +- } -} -diff -urN a/gopls/internal/lsp/testdata/format/good_format.go b/gopls/internal/lsp/testdata/format/good_format.go ---- a/gopls/internal/lsp/testdata/format/good_format.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/format/good_format.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package format //@format("package") +- +--- y1-rename -- +-package a - -import ( -- "log" +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") -) - --func goodbye() { -- log.Printf("byeeeee") +-func Random() int { +- y := 6 + 7 +- return y -} -diff -urN a/gopls/internal/lsp/testdata/format/good_format.go.golden b/gopls/internal/lsp/testdata/format/good_format.go.golden ---- a/gopls/internal/lsp/testdata/format/good_format.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/format/good_format.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ ---- gofmt -- --package format //@format("package") - --import ( -- "log" --) +-func Random2(y int) int { //@rename("y", "z") +- return y +-} - --func goodbye() { -- log.Printf("byeeeee") +-type Pos struct { +- x, y int -} - -diff -urN a/gopls/internal/lsp/testdata/format/newline_format.go.golden b/gopls/internal/lsp/testdata/format/newline_format.go.golden ---- a/gopls/internal/lsp/testdata/format/newline_format.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/format/newline_format.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,4 +0,0 @@ ---- gofmt -- --package format //@format("package") --func _() {} +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") +-} - -diff -urN a/gopls/internal/lsp/testdata/format/newline_format.go.in b/gopls/internal/lsp/testdata/format/newline_format.go.in ---- a/gopls/internal/lsp/testdata/format/newline_format.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/format/newline_format.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,2 +0,0 @@ --package format //@format("package") --func _() {} -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/format/one_line.go.golden b/gopls/internal/lsp/testdata/format/one_line.go.golden ---- a/gopls/internal/lsp/testdata/format/one_line.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/format/one_line.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ ---- gofmt -- --package format //@format("package") +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} - -diff -urN a/gopls/internal/lsp/testdata/format/one_line.go.in b/gopls/internal/lsp/testdata/format/one_line.go.in ---- a/gopls/internal/lsp/testdata/format/one_line.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/format/one_line.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1 +0,0 @@ --package format //@format("package") -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/func_rank/func_rank.go.in b/gopls/internal/lsp/testdata/func_rank/func_rank.go.in ---- a/gopls/internal/lsp/testdata/func_rank/func_rank.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/func_rank/func_rank.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,70 +0,0 @@ --package func_rank +-func sw() { +- var x interface{} - --import "net/http" +- switch y1 := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y1) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y1) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y1) //@rename("y", "y3"),rename("f2","fmt2") +- } +-} - --var stringAVar = "var" //@item(stringAVar, "stringAVar", "string", "var") --func stringBFunc() string { return "str" } //@item(stringBFunc, "stringBFunc", "func() string", "func") --type stringer struct{} //@item(stringer, "stringer", "struct{...}", "struct") +--- y2-rename -- +-package a - --func _() stringer //@complete("tr", stringer) +-import ( +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") +-) - --func _(val stringer) {} //@complete("tr", stringer) +-func Random() int { +- y := 6 + 7 +- return y +-} - --func (stringer) _() {} //@complete("tr", stringer) +-func Random2(y int) int { //@rename("y", "z") +- return y +-} +- +-type Pos struct { +- x, y int +-} +- +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") +-} - -func _() { -- var s struct { -- AA int //@item(rankAA, "AA", "int", "field") -- AB string //@item(rankAB, "AB", "string", "field") -- AC int //@item(rankAC, "AC", "int", "field") +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} +- +-func sw() { +- var x interface{} +- +- switch y2 := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y2) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y2) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y2) //@rename("y", "y3"),rename("f2","fmt2") - } -- fnStr := func(string) {} -- fnStr(s.A) //@complete(")", rankAB, rankAA, rankAC) -- fnStr("" + s.A) //@complete(")", rankAB, rankAA, rankAC) +-} - -- fnInt := func(int) {} -- fnInt(-s.A) //@complete(")", rankAA, rankAC, rankAB) +--- y3-rename -- +-package a - -- // no expected type -- fnInt(func() int { s.A }) //@complete(" }", rankAA, rankAB, rankAC) -- fnInt(s.A()) //@complete("()", rankAA, rankAC, rankAB) -- fnInt([]int{}[s.A]) //@complete("])", rankAA, rankAC, rankAB) -- fnInt([]int{}[:s.A]) //@complete("])", rankAA, rankAC, rankAB) +-import ( +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") +-) - -- fnInt(s.A.(int)) //@complete(".(", rankAA, rankAC, rankAB) +-func Random() int { +- y := 6 + 7 +- return y +-} - -- fnPtr := func(*string) {} -- fnPtr(&s.A) //@complete(")", rankAB, rankAA, rankAC) +-func Random2(y int) int { //@rename("y", "z") +- return y +-} - -- var aaPtr *string //@item(rankAAPtr, "aaPtr", "*string", "var") -- var abPtr *int //@item(rankABPtr, "abPtr", "*int", "var") -- fnInt(*a) //@complete(")", rankABPtr, rankAAPtr) +-type Pos struct { +- x, y int +-} - -- _ = func() string { -- return s.A //@complete(" //", rankAB, rankAA, rankAC) +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") +-} +- +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} +- +-func sw() { +- var x interface{} +- +- switch y3 := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y3) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y3) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y3) //@rename("y", "y3"),rename("f2","fmt2") - } -} - --type foo struct { -- fooPrivateField int //@item(rankFooPrivField, "fooPrivateField", "int", "field") -- FooPublicField int //@item(rankFooPubField, "FooPublicField", "int", "field") +--- z-rename -- +-package a +- +-import ( +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") +-) +- +-func Random() int { +- y := 6 + 7 +- return y -} - --func (foo) fooPrivateMethod() int { //@item(rankFooPrivMeth, "fooPrivateMethod", "func() int", "method") -- return 0 +-func Random2(z int) int { //@rename("y", "z") +- return z -} - --func (foo) FooPublicMethod() int { //@item(rankFooPubMeth, "FooPublicMethod", "func() int", "method") -- return 0 +-type Pos struct { +- x, y int -} - --func _() { -- var _ int = foo{}. //@rank(" //", rankFooPrivField, rankFooPubField),rank(" //", rankFooPrivMeth, rankFooPubMeth),rank(" //", rankFooPrivField, rankFooPrivMeth) +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") -} - -func _() { -- HandleFunc //@item(httpHandleFunc, "HandleFunc", "func(pattern string, handler func(http.ResponseWriter, *http.Request))", "func") -- HandlerFunc //@item(httpHandlerFunc, "HandlerFunc", "func(http.ResponseWriter, *http.Request)", "type") +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} - -- http.HandleFunc //@rank(" //", httpHandleFunc, httpHandlerFunc) +-func sw() { +- var x interface{} +- +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- } -} -diff -urN a/gopls/internal/lsp/testdata/funcsig/func_sig.go b/gopls/internal/lsp/testdata/funcsig/func_sig.go ---- a/gopls/internal/lsp/testdata/funcsig/func_sig.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/funcsig/func_sig.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package funcsig - --type someType int //@item(sigSomeType, "someType", "int", "type") +diff -urN a/gopls/internal/lsp/testdata/rename/a/random.go.in b/gopls/internal/lsp/testdata/rename/a/random.go.in +--- a/gopls/internal/lsp/testdata/rename/a/random.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/a/random.go.in 1970-01-01 08:00:00 +@@ -1,42 +0,0 @@ +-package a - --// Don't complete "foo" in signature. --func (foo someType) _() { //@item(sigFoo, "foo", "someType", "var"),complete(") {", sigSomeType) +-import ( +- lg "log" +- "fmt" //@rename("fmt", "fmty") +- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") +-) - -- //@complete("", sigFoo, sigSomeType) +-func Random() int { +- y := 6 + 7 +- return y -} -diff -urN a/gopls/internal/lsp/testdata/funcvalue/func_value.go b/gopls/internal/lsp/testdata/funcvalue/func_value.go ---- a/gopls/internal/lsp/testdata/funcvalue/func_value.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/funcvalue/func_value.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,27 +0,0 @@ --package funcvalue - --func fooFunc() int { //@item(fvFooFunc, "fooFunc", "func() int", "func") -- return 0 +-func Random2(y int) int { //@rename("y", "z") +- return y +-} +- +-type Pos struct { +- x, y int +-} +- +-func (p *Pos) Sum() int { +- return p.x + p.y //@rename("x", "myX") +-} +- +-func _() { +- var p Pos //@rename("p", "pos") +- _ = p.Sum() //@rename("Sum", "GetSum") +-} +- +-func sw() { +- var x interface{} +- +- switch y := x.(type) { //@rename("y", "y0") +- case int: +- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") +- case string: +- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") +- default: +- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- } -} +diff -urN a/gopls/internal/lsp/testdata/rename/b/b.go b/gopls/internal/lsp/testdata/rename/b/b.go +--- a/gopls/internal/lsp/testdata/rename/b/b.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/b/b.go 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ +-package b - --var _ = fooFunc() //@item(fvFooFuncCall, "fooFunc", "func() int", "func") +-var c int //@rename("int", "uint") - --var fooVar = func() int { //@item(fvFooVar, "fooVar", "func() int", "var") -- return 0 +-func _() { +- a := 1 //@rename("a", "error") +- a = 2 +- _ = a -} - --var _ = fooVar() //@item(fvFooVarCall, "fooVar", "func() int", "var") -- --type myFunc func() int +-var ( +- // Hello there. +- // Foo does the thing. +- Foo int //@rename("Foo", "Bob") +-) - --var fooType myFunc = fooVar //@item(fvFooType, "fooType", "myFunc", "var") +-/* +-Hello description +-*/ +-func Hello() {} //@rename("Hello", "Goodbye") +diff -urN a/gopls/internal/lsp/testdata/rename/b/b.go.golden b/gopls/internal/lsp/testdata/rename/b/b.go.golden +--- a/gopls/internal/lsp/testdata/rename/b/b.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/b/b.go.golden 1970-01-01 08:00:00 +@@ -1,78 +0,0 @@ +--- Bob-rename -- +-package b - --var _ = fooType() //@item(fvFooTypeCall, "fooType", "func() int", "var") +-var c int //@rename("int", "uint") - -func _() { -- var f func() int -- f = foo //@complete(" //", fvFooFunc, fvFooType, fvFooVar) -- -- var i int -- i = foo //@complete(" //", fvFooFuncCall, fvFooTypeCall, fvFooVarCall) +- a := 1 //@rename("a", "error") +- a = 2 +- _ = a -} -diff -urN a/gopls/internal/lsp/testdata/fuzzymatch/fuzzymatch.go b/gopls/internal/lsp/testdata/fuzzymatch/fuzzymatch.go ---- a/gopls/internal/lsp/testdata/fuzzymatch/fuzzymatch.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/fuzzymatch/fuzzymatch.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,48 +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 fuzzy +-var ( +- // Hello there. +- // Bob does the thing. +- Bob int //@rename("Foo", "Bob") +-) - --func _() { -- var a struct { -- fabar int -- fooBar string -- } +-/* +-Hello description +-*/ +-func Hello() {} //@rename("Hello", "Goodbye") - -- a.fabar //@item(fuzzFabarField, "a.fabar", "int", "field") -- a.fooBar //@item(fuzzFooBarField, "a.fooBar", "string", "field") +--- Goodbye-rename -- +-b.go: +-package b - -- afa //@fuzzy(" //", fuzzFabarField, fuzzFooBarField) -- afb //@fuzzy(" //", fuzzFooBarField, fuzzFabarField) +-var c int //@rename("int", "uint") - -- fab //@fuzzy(" //", fuzzFabarField) +-func _() { +- a := 1 //@rename("a", "error") +- a = 2 +- _ = a +-} - -- var myString string -- myString = af //@fuzzy(" //", fuzzFooBarField, fuzzFabarField) +-var ( +- // Hello there. +- // Foo does the thing. +- Foo int //@rename("Foo", "Bob") +-) - -- var b struct { -- c struct { -- d struct { -- e struct { -- abc string -- } -- abc float32 -- } -- abc bool -- } -- abc int -- } +-/* +-Goodbye description +-*/ +-func Goodbye() {} //@rename("Hello", "Goodbye") - -- b.abc //@item(fuzzABCInt, "b.abc", "int", "field") -- b.c.abc //@item(fuzzABCbool, "b.c.abc", "bool", "field") -- b.c.d.abc //@item(fuzzABCfloat, "b.c.d.abc", "float32", "field") -- b.c.d.e.abc //@item(fuzzABCstring, "b.c.d.e.abc", "string", "field") +-c.go: +-package c - -- // in depth order by default -- abc //@fuzzy(" //", fuzzABCInt, fuzzABCbool, fuzzABCfloat) +-import "golang.org/lsptests/rename/b" - -- // deep candidate that matches expected type should still ranked first -- var s string -- s = abc //@fuzzy(" //", fuzzABCstring, fuzzABCInt, fuzzABCbool) +-func _() { +- b.Goodbye() //@rename("Hello", "Goodbye") -} -diff -urN a/gopls/internal/lsp/testdata/generate/generate.go b/gopls/internal/lsp/testdata/generate/generate.go ---- a/gopls/internal/lsp/testdata/generate/generate.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/generate/generate.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,4 +0,0 @@ --package generate -- --//go:generate echo Hi //@ codelens("//go:generate", "run go generate", "generate"), codelens("//go:generate", "run go generate ./...", "generate") --//go:generate echo I shall have no CodeLens -diff -urN a/gopls/internal/lsp/testdata/generated/generated.go b/gopls/internal/lsp/testdata/generated/generated.go ---- a/gopls/internal/lsp/testdata/generated/generated.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/generated/generated.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package generated - --// Code generated by generator.go. DO NOT EDIT. +--- error-rename -- +-package b - --func _() { -- var y int //@diag("y", "compiler", "y declared (and|but) not used", "error") --} -diff -urN a/gopls/internal/lsp/testdata/generated/generator.go b/gopls/internal/lsp/testdata/generated/generator.go ---- a/gopls/internal/lsp/testdata/generated/generator.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/generated/generator.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,5 +0,0 @@ --package generated +-var c int //@rename("int", "uint") - -func _() { -- var x int //@diag("x", "compiler", "x declared (and|but) not used", "error") +- error := 1 //@rename("a", "error") +- error = 2 +- _ = error -} -diff -urN a/gopls/internal/lsp/testdata/godef/a/a_x_test.go b/gopls/internal/lsp/testdata/godef/a/a_x_test.go ---- a/gopls/internal/lsp/testdata/godef/a/a_x_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/a/a_x_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package a_test - --import ( -- "testing" +-var ( +- // Hello there. +- // Foo does the thing. +- Foo int //@rename("Foo", "Bob") -) - --func TestA2(t *testing.T) { //@TestA2,godef(TestA2, TestA2) -- Nonexistant() //@diag("Nonexistant", "compiler", "(undeclared name|undefined): Nonexistant", "error") --} -diff -urN a/gopls/internal/lsp/testdata/godef/a/a_x_test.go.golden b/gopls/internal/lsp/testdata/godef/a/a_x_test.go.golden ---- a/gopls/internal/lsp/testdata/godef/a/a_x_test.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/a/a_x_test.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,26 +0,0 @@ ---- TestA2-definition -- --godef/a/a_x_test.go:7:6-12: defined here as ```go --func TestA2(t *testing.T) --``` ---- TestA2-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/a_x_test.go", -- "start": { -- "line": 7, -- "column": 6, -- "offset": 44 -- }, -- "end": { -- "line": 7, -- "column": 12, -- "offset": 50 -- } -- }, -- "description": "```go\nfunc TestA2(t *testing.T)\n```" --} -- ---- TestA2-hoverdef -- --```go --func TestA2(t *testing.T) --``` -diff -urN a/gopls/internal/lsp/testdata/godef/a/d.go b/gopls/internal/lsp/testdata/godef/a/d.go ---- a/gopls/internal/lsp/testdata/godef/a/d.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/a/d.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,69 +0,0 @@ --package a //@mark(a, "a "),hoverdef("a ", a) +-/* +-Hello description +-*/ +-func Hello() {} //@rename("Hello", "Goodbye") - --import "fmt" +--- uint-rename -- +-int is built in and cannot be renamed +diff -urN a/gopls/internal/lsp/testdata/rename/bad/bad.go.golden b/gopls/internal/lsp/testdata/rename/bad/bad.go.golden +--- a/gopls/internal/lsp/testdata/rename/bad/bad.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/bad/bad.go.golden 1970-01-01 08:00:00 +@@ -1,2 +0,0 @@ +--- rFunc-rename -- +-renaming "sFunc" to "rFunc" not possible because "golang.org/lsptests/rename/bad" has errors +diff -urN a/gopls/internal/lsp/testdata/rename/bad/bad.go.in b/gopls/internal/lsp/testdata/rename/bad/bad.go.in +--- a/gopls/internal/lsp/testdata/rename/bad/bad.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/bad/bad.go.in 1970-01-01 08:00:00 +@@ -1,8 +0,0 @@ +-package bad - --type Thing struct { //@Thing -- Member string //@Member +-type myStruct struct { -} - --var Other Thing //@Other -- --func Things(val []string) []Thing { //@Things -- return nil +-func (s *myStruct) sFunc() bool { //@rename("sFunc", "rFunc") +- return s.Bad -} +diff -urN a/gopls/internal/lsp/testdata/rename/bad/bad_test.go.in b/gopls/internal/lsp/testdata/rename/bad/bad_test.go.in +--- a/gopls/internal/lsp/testdata/rename/bad/bad_test.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/bad/bad_test.go.in 1970-01-01 08:00:00 +@@ -1 +0,0 @@ +-package bad +\ No newline at end of file +diff -urN a/gopls/internal/lsp/testdata/rename/c/c.go b/gopls/internal/lsp/testdata/rename/c/c.go +--- a/gopls/internal/lsp/testdata/rename/c/c.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/c/c.go 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package c - --func (t Thing) Method(i int) string { //@Method -- return t.Member --} +-import "golang.org/lsptests/rename/b" - --func (t Thing) Method3() { +-func _() { +- b.Hello() //@rename("Hello", "Goodbye") -} +diff -urN a/gopls/internal/lsp/testdata/rename/c/c.go.golden b/gopls/internal/lsp/testdata/rename/c/c.go.golden +--- a/gopls/internal/lsp/testdata/rename/c/c.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/c/c.go.golden 1970-01-01 08:00:00 +@@ -1,32 +0,0 @@ +--- Goodbye-rename -- +-b.go: +-package b - --func (t *Thing) Method2(i int, j int) (error, string) { -- return nil, t.Member --} +-var c int //@rename("int", "uint") - --func (t *Thing) private() { +-func _() { +- a := 1 //@rename("a", "error") +- a = 2 +- _ = a -} - --func useThings() { -- t := Thing{ //@mark(aStructType, "ing") -- Member: "string", //@mark(fMember, "ember") -- } -- fmt.Print(t.Member) //@mark(aMember, "ember") -- fmt.Print(Other) //@mark(aVar, "ther") -- Things() //@mark(aFunc, "ings") -- t.Method() //@mark(aMethod, "eth") --} +-var ( +- // Hello there. +- // Foo does the thing. +- Foo int //@rename("Foo", "Bob") +-) - --type NextThing struct { //@NextThing -- Thing -- Value int --} +-/* +-Goodbye description +-*/ +-func Goodbye() {} //@rename("Hello", "Goodbye") - --func (n NextThing) another() string { -- return n.Member --} +-c.go: +-package c - --// Shadows Thing.Method3 --func (n *NextThing) Method3() int { -- return n.Value +-import "golang.org/lsptests/rename/b" +- +-func _() { +- b.Goodbye() //@rename("Hello", "Goodbye") -} - --var nextThing NextThing //@hoverdef("NextThing", NextThing) +diff -urN a/gopls/internal/lsp/testdata/rename/c/c2.go b/gopls/internal/lsp/testdata/rename/c/c2.go +--- a/gopls/internal/lsp/testdata/rename/c/c2.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/c/c2.go 1970-01-01 08:00:00 +@@ -1,4 +0,0 @@ +-package c - --/*@ --godef(aStructType, Thing) --godef(aMember, Member) --godef(aVar, Other) --godef(aFunc, Things) --godef(aMethod, Method) --godef(fMember, Member) --godef(Member, Member) -- --//param --//package name --//const --//anon field +-//go:embed Static/* +-var Static embed.FS //@rename("Static", "static") +\ No newline at end of file +diff -urN a/gopls/internal/lsp/testdata/rename/c/c2.go.golden b/gopls/internal/lsp/testdata/rename/c/c2.go.golden +--- a/gopls/internal/lsp/testdata/rename/c/c2.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/c/c2.go.golden 1970-01-01 08:00:00 +@@ -1,5 +0,0 @@ +--- static-rename -- +-package c - --*/ -diff -urN a/gopls/internal/lsp/testdata/godef/a/d.go.golden b/gopls/internal/lsp/testdata/godef/a/d.go.golden ---- a/gopls/internal/lsp/testdata/godef/a/d.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/a/d.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,191 +0,0 @@ ---- Member-definition -- --godef/a/d.go:6:2-8: defined here as ```go --field Member string --``` +-//go:embed Static/* +-var static embed.FS //@rename("Static", "static") +diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go b/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go +--- a/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package another - --@Member +-type ( +- I interface{ F() } +- C struct{ I } +-) - +-func (C) g() - --[`(a.Thing).Member` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing.Member) ---- Member-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/d.go", -- "start": { -- "line": 6, -- "column": 2, -- "offset": 90 -- }, -- "end": { -- "line": 6, -- "column": 8, -- "offset": 96 -- } -- }, -- "description": "```go\nfield Member string\n```\n\n@Member\n\n\n[`(a.Thing).Member` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing.Member)" +-func _() { +- var x I = C{} +- x.F() //@rename("F", "G") -} +diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go.golden b/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go.golden +--- a/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go.golden 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +--- G-rename -- +-package another - ---- Member-hoverdef -- --```go --field Member string --``` +-type ( +- I interface{ G() } +- C struct{ I } +-) - --@Member +-func (C) g() - +-func _() { +- var x I = C{} +- x.G() //@rename("F", "G") +-} - --[`(a.Thing).Member` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing.Member) ---- Method-definition -- --godef/a/d.go:15:16-22: defined here as ```go --func (Thing).Method(i int) string --``` +diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go b/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go +--- a/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package crosspkg +- +-func Foo() { //@rename("Foo", "Dolphin") - --[`(a.Thing).Method` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing.Method) ---- Method-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/d.go", -- "start": { -- "line": 15, -- "column": 16, -- "offset": 219 -- }, -- "end": { -- "line": 15, -- "column": 22, -- "offset": 225 -- } -- }, -- "description": "```go\nfunc (Thing).Method(i int) string\n```\n\n[`(a.Thing).Method` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing.Method)" -} - ---- Method-hoverdef -- --```go --func (Thing).Method(i int) string --``` +-var Bar int //@rename("Bar", "Tomato") +diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go.golden b/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go.golden +--- a/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go.golden 1970-01-01 08:00:00 +@@ -1,40 +0,0 @@ +--- Dolphin-rename -- +-crosspkg.go: +-package crosspkg - --[`(a.Thing).Method` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing.Method) ---- NextThing-hoverdef -- --```go --type NextThing struct { -- Thing -- Value int --} +-func Dolphin() { //@rename("Foo", "Dolphin") - --func (*NextThing).Method3() int --func (NextThing).another() string --``` +-} - --[`a.NextThing` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#NextThing) ---- Other-definition -- --godef/a/d.go:9:5-10: defined here as ```go --var Other Thing --``` +-var Bar int //@rename("Bar", "Tomato") - --@Other +-other.go: +-package other - +-import "golang.org/lsptests/rename/crosspkg" - --[`a.Other` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Other) ---- Other-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/d.go", -- "start": { -- "line": 9, -- "column": 5, -- "offset": 121 -- }, -- "end": { -- "line": 9, -- "column": 10, -- "offset": 126 -- } -- }, -- "description": "```go\nvar Other Thing\n```\n\n@Other\n\n\n[`a.Other` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Other)" +-func Other() { +- crosspkg.Bar +- crosspkg.Dolphin() //@rename("Foo", "Flamingo") -} - ---- Other-hoverdef -- --```go --var Other Thing --``` -- --@Other +--- Tomato-rename -- +-crosspkg.go: +-package crosspkg - +-func Foo() { //@rename("Foo", "Dolphin") - --[`a.Other` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Other) ---- Thing-definition -- --godef/a/d.go:5:6-11: defined here as ```go --type Thing struct { -- Member string //@Member -} - --func (Thing).Method(i int) string --func (*Thing).Method2(i int, j int) (error, string) --func (Thing).Method3() --func (*Thing).private() --``` +-var Tomato int //@rename("Bar", "Tomato") - --[`a.Thing` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing) ---- Thing-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/d.go", -- "start": { -- "line": 5, -- "column": 6, -- "offset": 65 -- }, -- "end": { -- "line": 5, -- "column": 11, -- "offset": 70 -- } -- }, -- "description": "```go\ntype Thing struct {\n\tMember string //@Member\n}\n\nfunc (Thing).Method(i int) string\nfunc (*Thing).Method2(i int, j int) (error, string)\nfunc (Thing).Method3()\nfunc (*Thing).private()\n```\n\n[`a.Thing` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing)" --} +-other.go: +-package other - ---- Thing-hoverdef -- --```go --type Thing struct { -- Member string //@Member +-import "golang.org/lsptests/rename/crosspkg" +- +-func Other() { +- crosspkg.Tomato +- crosspkg.Foo() //@rename("Foo", "Flamingo") -} - --func (Thing).Method(i int) string --func (*Thing).Method2(i int, j int) (error, string) --func (Thing).Method3() --func (*Thing).private() --``` +diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go b/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go +--- a/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go 1970-01-01 08:00:00 +@@ -1,8 +0,0 @@ +-package other - --[`a.Thing` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing) ---- Things-definition -- --godef/a/d.go:11:6-12: defined here as ```go --func Things(val []string) []Thing --``` +-import "golang.org/lsptests/rename/crosspkg" - --[`a.Things` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Things) ---- Things-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/d.go", -- "start": { -- "line": 11, -- "column": 6, -- "offset": 148 -- }, -- "end": { -- "line": 11, -- "column": 12, -- "offset": 154 -- } -- }, -- "description": "```go\nfunc Things(val []string) []Thing\n```\n\n[`a.Things` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Things)" +-func Other() { +- crosspkg.Bar +- crosspkg.Foo() //@rename("Foo", "Flamingo") -} +diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go.golden b/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go.golden +--- a/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go.golden 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ +--- Flamingo-rename -- +-crosspkg.go: +-package crosspkg - ---- Things-hoverdef -- --```go --func Things(val []string) []Thing --``` -- --[`a.Things` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Things) ---- a-hoverdef -- --Package a is a package for testing go to definition. +-func Flamingo() { //@rename("Foo", "Dolphin") - -diff -urN a/gopls/internal/lsp/testdata/godef/a/f.go b/gopls/internal/lsp/testdata/godef/a/f.go ---- a/gopls/internal/lsp/testdata/godef/a/f.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/a/f.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,16 +0,0 @@ --// Package a is a package for testing go to definition. --package a +-} - --import "fmt" +-var Bar int //@rename("Bar", "Tomato") - --func TypeStuff() { //@Stuff -- var x string +-other.go: +-package other - -- switch y := interface{}(x).(type) { //@mark(switchY, "y"),godef("y", switchY) -- case int: //@mark(intY, "int") -- fmt.Printf("%v", y) //@hoverdef("y", intY) -- case string: //@mark(stringY, "string") -- fmt.Printf("%v", y) //@hoverdef("y", stringY) -- } +-import "golang.org/lsptests/rename/crosspkg" - --} -diff -urN a/gopls/internal/lsp/testdata/godef/a/f.go.golden b/gopls/internal/lsp/testdata/godef/a/f.go.golden ---- a/gopls/internal/lsp/testdata/godef/a/f.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/a/f.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,34 +0,0 @@ ---- intY-hoverdef -- --```go --var y int --``` ---- stringY-hoverdef -- --```go --var y string --``` ---- switchY-definition -- --godef/a/f.go:8:9-10: defined here as ```go --var y interface{} --``` ---- switchY-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/f.go", -- "start": { -- "line": 8, -- "column": 9, -- "offset": 76 -- }, -- "end": { -- "line": 8, -- "column": 10, -- "offset": 77 -- } -- }, -- "description": "```go\nvar y interface{}\n```" +-func Other() { +- crosspkg.Bar +- crosspkg.Flamingo() //@rename("Foo", "Flamingo") -} - ---- switchY-hoverdef -- --```go --var y interface{} --``` -diff -urN a/gopls/internal/lsp/testdata/godef/a/g.go b/gopls/internal/lsp/testdata/godef/a/g.go ---- a/gopls/internal/lsp/testdata/godef/a/g.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/a/g.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --package a +diff -urN a/gopls/internal/lsp/testdata/rename/generics/embedded.go b/gopls/internal/lsp/testdata/rename/generics/embedded.go +--- a/gopls/internal/lsp/testdata/rename/generics/embedded.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/generics/embedded.go 1970-01-01 08:00:00 +@@ -1,10 +0,0 @@ +-//go:build go1.18 +-// +build go1.18 - --import "time" +-package generics - --// dur is a constant of type time.Duration. --const dur = 15*time.Minute + 10*time.Second + 350*time.Millisecond //@dur,hoverdef("dur", dur) -diff -urN a/gopls/internal/lsp/testdata/godef/a/g.go.golden b/gopls/internal/lsp/testdata/godef/a/g.go.golden ---- a/gopls/internal/lsp/testdata/godef/a/g.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/a/g.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ ---- dur-hoverdef -- --```go --const dur time.Duration = 910350000000 // 15m10.35s --``` +-type foo[P any] int //@rename("foo","bar") - --dur is a constant of type time.Duration. +-var x struct{ foo[int] } - -diff -urN a/gopls/internal/lsp/testdata/godef/a/h.go b/gopls/internal/lsp/testdata/godef/a/h.go ---- a/gopls/internal/lsp/testdata/godef/a/h.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/a/h.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,147 +0,0 @@ --package a +-var _ = x.foo +diff -urN a/gopls/internal/lsp/testdata/rename/generics/embedded.go.golden b/gopls/internal/lsp/testdata/rename/generics/embedded.go.golden +--- a/gopls/internal/lsp/testdata/rename/generics/embedded.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/generics/embedded.go.golden 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +--- bar-rename -- +-//go:build go1.18 +-// +build go1.18 - --func _() { -- type s struct { -- nested struct { -- // nested number -- number int64 //@mark(nestedNumber, "number") -- } -- nested2 []struct { -- // nested string -- str string //@mark(nestedString, "str") -- } -- x struct { -- x struct { -- x struct { -- x struct { -- x struct { -- // nested map -- m map[string]float64 //@mark(nestedMap, "m") -- } -- } -- } -- } -- } -- } +-package generics - -- var t s -- _ = t.nested.number //@hoverdef("number", nestedNumber) -- _ = t.nested2[0].str //@hoverdef("str", nestedString) -- _ = t.x.x.x.x.x.m //@hoverdef("m", nestedMap) --} +-type bar[P any] int //@rename("foo","bar") - --func _() { -- var s struct { -- // a field -- a int //@mark(structA, "a") -- // b nested struct -- b struct { //@mark(structB, "b") -- // c field of nested struct -- c int //@mark(structC, "c") -- } -- } -- _ = s.a //@hoverdef("a", structA) -- _ = s.b //@hoverdef("b", structB) -- _ = s.b.c //@hoverdef("c", structC) +-var x struct{ bar[int] } - -- var arr []struct { -- // d field -- d int //@mark(arrD, "d") -- // e nested struct -- e struct { //@mark(arrE, "e") -- // f field of nested struct -- f int //@mark(arrF, "f") -- } -- } -- _ = arr[0].d //@hoverdef("d", arrD) -- _ = arr[0].e //@hoverdef("e", arrE) -- _ = arr[0].e.f //@hoverdef("f", arrF) +-var _ = x.bar - -- var complex []struct { -- c <-chan map[string][]struct { -- // h field -- h int //@mark(complexH, "h") -- // i nested struct -- i struct { //@mark(complexI, "i") -- // j field of nested struct -- j int //@mark(complexJ, "j") -- } -- } -- } -- _ = (<-complex[0].c)["0"][0].h //@hoverdef("h", complexH) -- _ = (<-complex[0].c)["0"][0].i //@hoverdef("i", complexI) -- _ = (<-complex[0].c)["0"][0].i.j //@hoverdef("j", complexJ) +diff -urN a/gopls/internal/lsp/testdata/rename/generics/generics.go b/gopls/internal/lsp/testdata/rename/generics/generics.go +--- a/gopls/internal/lsp/testdata/rename/generics/generics.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/generics/generics.go 1970-01-01 08:00:00 +@@ -1,25 +0,0 @@ +-//go:build go1.18 +-// +build go1.18 - -- var mapWithStructKey map[struct { -- // X key field -- x []string //@mark(mapStructKeyX, "x") -- }]int -- for k := range mapWithStructKey { -- _ = k.x //@hoverdef("x", mapStructKeyX) -- } +-package generics - -- var mapWithStructKeyAndValue map[struct { -- // Y key field -- y string //@mark(mapStructKeyY, "y") -- }]struct { -- // X value field -- x string //@mark(mapStructValueX, "x") -- } -- for k, v := range mapWithStructKeyAndValue { -- // TODO: we don't show docs for y field because both map key and value -- // are structs. And in this case, we parse only map value -- _ = k.y //@hoverdef("y", mapStructKeyY) -- _ = v.x //@hoverdef("x", mapStructValueX) -- } +-type G[P any] struct { +- F int +-} - -- var i []map[string]interface { -- // open method comment -- open() error //@mark(openMethod, "open") -- } -- i[0]["1"].open() //@hoverdef("open", openMethod) +-func (G[_]) M() {} +- +-func F[P any](P) { +- var p P //@rename("P", "Q") +- _ = p -} - -func _() { -- test := struct { -- // test description -- desc string //@mark(testDescription, "desc") -- }{} -- _ = test.desc //@hoverdef("desc", testDescription) +- var x G[int] //@rename("G", "H") +- _ = x.F //@rename("F", "K") +- x.M() //@rename("M", "N") - -- for _, tt := range []struct { -- // test input -- in map[string][]struct { //@mark(testInput, "in") -- // test key -- key string //@mark(testInputKey, "key") -- // test value -- value interface{} //@mark(testInputValue, "value") -- } -- result struct { -- v <-chan struct { -- // expected test value -- value int //@mark(testResultValue, "value") -- } -- } -- }{} { -- _ = tt.in //@hoverdef("in", testInput) -- _ = tt.in["0"][0].key //@hoverdef("key", testInputKey) -- _ = tt.in["0"][0].value //@hoverdef("value", testInputValue) +- var y G[string] +- _ = y.F +- y.M() +-} +diff -urN a/gopls/internal/lsp/testdata/rename/generics/generics.go.golden b/gopls/internal/lsp/testdata/rename/generics/generics.go.golden +--- a/gopls/internal/lsp/testdata/rename/generics/generics.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/generics/generics.go.golden 1970-01-01 08:00:00 +@@ -1,108 +0,0 @@ +--- H-rename -- +-//go:build go1.18 +-// +build go1.18 - -- _ = (<-tt.result.v).value //@hoverdef("value", testResultValue) -- } +-package generics +- +-type H[P any] struct { +- F int +-} +- +-func (H[_]) M() {} +- +-func F[P any](P) { +- var p P //@rename("P", "Q") +- _ = p -} - -func _() { -- getPoints := func() []struct { -- // X coord -- x int //@mark(returnX, "x") -- // Y coord -- y int //@mark(returnY, "y") -- } { -- return nil -- } +- var x H[int] //@rename("G", "H") +- _ = x.F //@rename("F", "K") +- x.M() //@rename("M", "N") - -- r := getPoints() -- r[0].x //@hoverdef("x", returnX) -- r[0].y //@hoverdef("y", returnY) +- var y H[string] +- _ = y.F +- y.M() -} -diff -urN a/gopls/internal/lsp/testdata/godef/a/h.go.golden b/gopls/internal/lsp/testdata/godef/a/h.go.golden ---- a/gopls/internal/lsp/testdata/godef/a/h.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/a/h.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,161 +0,0 @@ ---- arrD-hoverdef -- --```go --field d int --``` - --d field +--- K-rename -- +-//go:build go1.18 +-// +build go1.18 - ---- arrE-hoverdef -- --```go --field e struct{f int} --``` +-package generics - --e nested struct +-type G[P any] struct { +- K int +-} - ---- arrF-hoverdef -- --```go --field f int --``` +-func (G[_]) M() {} - --f field of nested struct +-func F[P any](P) { +- var p P //@rename("P", "Q") +- _ = p +-} - ---- complexH-hoverdef -- --```go --field h int --``` +-func _() { +- var x G[int] //@rename("G", "H") +- _ = x.K //@rename("F", "K") +- x.M() //@rename("M", "N") - --h field +- var y G[string] +- _ = y.K +- y.M() +-} - ---- complexI-hoverdef -- --```go --field i struct{j int} --``` +--- N-rename -- +-//go:build go1.18 +-// +build go1.18 - --i nested struct +-package generics - ---- complexJ-hoverdef -- --```go --field j int --``` +-type G[P any] struct { +- F int +-} - --j field of nested struct +-func (G[_]) N() {} - ---- mapStructKeyX-hoverdef -- --```go --field x []string --``` +-func F[P any](P) { +- var p P //@rename("P", "Q") +- _ = p +-} - --X key field +-func _() { +- var x G[int] //@rename("G", "H") +- _ = x.F //@rename("F", "K") +- x.N() //@rename("M", "N") - ---- mapStructKeyY-hoverdef -- --```go --field y string --``` +- var y G[string] +- _ = y.F +- y.N() +-} - --Y key field +--- Q-rename -- +-//go:build go1.18 +-// +build go1.18 - ---- mapStructValueX-hoverdef -- --```go --field x string --``` +-package generics - --X value field +-type G[P any] struct { +- F int +-} - ---- nestedMap-hoverdef -- --```go --field m map[string]float64 --``` +-func (G[_]) M() {} - --nested map +-func F[Q any](Q) { +- var p Q //@rename("P", "Q") +- _ = p +-} - ---- nestedNumber-hoverdef -- --```go --field number int64 --``` +-func _() { +- var x G[int] //@rename("G", "H") +- _ = x.F //@rename("F", "K") +- x.M() //@rename("M", "N") - --nested number +- var y G[string] +- _ = y.F +- y.M() +-} - ---- nestedString-hoverdef -- --```go --field str string --``` +diff -urN a/gopls/internal/lsp/testdata/rename/generics/unions.go b/gopls/internal/lsp/testdata/rename/generics/unions.go +--- a/gopls/internal/lsp/testdata/rename/generics/unions.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/generics/unions.go 1970-01-01 08:00:00 +@@ -1,10 +0,0 @@ +-//go:build go1.18 +-// +build go1.18 - --nested string +-package generics - ---- openMethod-hoverdef -- --```go --func (interface).open() error --``` +-type T string //@rename("T", "R") - --open method comment +-type C interface { +- T | ~int //@rename("T", "S") +-} +diff -urN a/gopls/internal/lsp/testdata/rename/generics/unions.go.golden b/gopls/internal/lsp/testdata/rename/generics/unions.go.golden +--- a/gopls/internal/lsp/testdata/rename/generics/unions.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/generics/unions.go.golden 1970-01-01 08:00:00 +@@ -1,24 +0,0 @@ +--- R-rename -- +-//go:build go1.18 +-// +build go1.18 - ---- returnX-hoverdef -- --```go --field x int --``` +-package generics - --X coord +-type R string //@rename("T", "R") - ---- returnY-hoverdef -- --```go --field y int --``` +-type C interface { +- R | ~int //@rename("T", "S") +-} - --Y coord +--- S-rename -- +-//go:build go1.18 +-// +build go1.18 - ---- structA-hoverdef -- --```go --field a int --``` +-package generics - --a field +-type S string //@rename("T", "R") - ---- structB-hoverdef -- --```go --field b struct{c int} --``` +-type C interface { +- S | ~int //@rename("T", "S") +-} - --b nested struct +diff -urN a/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.golden b/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.golden +--- a/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.golden 1970-01-01 08:00:00 +@@ -1,10 +0,0 @@ +--- bar-rename -- +-package issue39614 - ---- structC-hoverdef -- --```go --field c int --``` +-func fn() { +- var bar bool //@rename("foo","bar") +- make(map[string]bool +- if true { +- } +-} - --c field of nested struct +diff -urN a/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.in b/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.in +--- a/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.in 1970-01-01 08:00:00 +@@ -1,8 +0,0 @@ +-package issue39614 - ---- testDescription-hoverdef -- --```go --field desc string --``` +-func fn() { +- var foo bool //@rename("foo","bar") +- make(map[string]bool +- if true { +- } +-} +diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/1.go b/gopls/internal/lsp/testdata/rename/issue42134/1.go +--- a/gopls/internal/lsp/testdata/rename/issue42134/1.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue42134/1.go 1970-01-01 08:00:00 +@@ -1,8 +0,0 @@ +-package issue42134 - --test description +-func _() { +- // foo computes things. +- foo := func() {} - ---- testInput-hoverdef -- --```go --field in map[string][]struct{key string; value interface{}} --``` +- foo() //@rename("foo", "bar") +-} +diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/1.go.golden b/gopls/internal/lsp/testdata/rename/issue42134/1.go.golden +--- a/gopls/internal/lsp/testdata/rename/issue42134/1.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue42134/1.go.golden 1970-01-01 08:00:00 +@@ -1,10 +0,0 @@ +--- bar-rename -- +-package issue42134 - --test input +-func _() { +- // bar computes things. +- bar := func() {} - ---- testInputKey-hoverdef -- --```go --field key string --``` +- bar() //@rename("foo", "bar") +-} - --test key +diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/2.go b/gopls/internal/lsp/testdata/rename/issue42134/2.go +--- a/gopls/internal/lsp/testdata/rename/issue42134/2.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue42134/2.go 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +-package issue42134 - ---- testInputValue-hoverdef -- --```go --field value interface{} --``` +-import "fmt" - --test value +-func _() { +- // minNumber is a min number. +- // Second line. +- minNumber := min(1, 2) +- fmt.Println(minNumber) //@rename("minNumber", "res") +-} - ---- testResultValue-hoverdef -- --```go --field value int --``` +-func min(a, b int) int { return a } +diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/2.go.golden b/gopls/internal/lsp/testdata/rename/issue42134/2.go.golden +--- a/gopls/internal/lsp/testdata/rename/issue42134/2.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue42134/2.go.golden 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +--- res-rename -- +-package issue42134 - --expected test value +-import "fmt" - -diff -urN a/gopls/internal/lsp/testdata/godef/b/e.go b/gopls/internal/lsp/testdata/godef/b/e.go ---- a/gopls/internal/lsp/testdata/godef/b/e.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/b/e.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,31 +0,0 @@ --package b +-func _() { +- // res is a min number. +- // Second line. +- res := min(1, 2) +- fmt.Println(res) //@rename("minNumber", "res") +-} - --import ( -- "fmt" +-func min(a, b int) int { return a } - -- "golang.org/lsptests/godef/a" --) +diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/3.go b/gopls/internal/lsp/testdata/rename/issue42134/3.go +--- a/gopls/internal/lsp/testdata/rename/issue42134/3.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue42134/3.go 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package issue42134 - --func useThings() { -- t := a.Thing{} //@mark(bStructType, "ing") -- fmt.Print(t.Member) //@mark(bMember, "ember") -- fmt.Print(a.Other) //@mark(bVar, "ther") -- a.Things() //@mark(bFunc, "ings") +-func _() { +- /* +- tests contains test cases +- */ +- tests := []struct { //@rename("tests", "testCases") +- in, out string +- }{} +- _ = tests -} -- --/*@ --godef(bStructType, Thing) --godef(bMember, Member) --godef(bVar, Other) --godef(bFunc, Things) --*/ +diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/3.go.golden b/gopls/internal/lsp/testdata/rename/issue42134/3.go.golden +--- a/gopls/internal/lsp/testdata/rename/issue42134/3.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue42134/3.go.golden 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +--- testCases-rename -- +-package issue42134 - -func _() { -- var x interface{} //@mark(eInterface, "interface{}") -- switch x := x.(type) { //@hoverdef("x", eInterface) -- case string: //@mark(eString, "string") -- fmt.Println(x) //@hoverdef("x", eString) -- case int: //@mark(eInt, "int") -- fmt.Println(x) //@hoverdef("x", eInt) -- } +- /* +- testCases contains test cases +- */ +- testCases := []struct { //@rename("tests", "testCases") +- in, out string +- }{} +- _ = testCases -} -diff -urN a/gopls/internal/lsp/testdata/godef/b/e.go.golden b/gopls/internal/lsp/testdata/godef/b/e.go.golden ---- a/gopls/internal/lsp/testdata/godef/b/e.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/b/e.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,156 +0,0 @@ ---- Member-definition -- --godef/a/d.go:6:2-8: defined here as ```go --field Member string --``` - --@Member +diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/4.go b/gopls/internal/lsp/testdata/rename/issue42134/4.go +--- a/gopls/internal/lsp/testdata/rename/issue42134/4.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue42134/4.go 1970-01-01 08:00:00 +@@ -1,8 +0,0 @@ +-package issue42134 - +-func _() { +- // a is equal to 5. Comment must stay the same - --[`(a.Thing).Member` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing.Member) ---- Member-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/d.go", -- "start": { -- "line": 6, -- "column": 2, -- "offset": 90 -- }, -- "end": { -- "line": 6, -- "column": 8, -- "offset": 96 -- } -- }, -- "description": "```go\nfield Member string\n```\n\n@Member\n\n\n[`(a.Thing).Member` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing.Member)" +- a := 5 +- _ = a //@rename("a", "b") -} +diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/4.go.golden b/gopls/internal/lsp/testdata/rename/issue42134/4.go.golden +--- a/gopls/internal/lsp/testdata/rename/issue42134/4.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue42134/4.go.golden 1970-01-01 08:00:00 +@@ -1,10 +0,0 @@ +--- b-rename -- +-package issue42134 - ---- Member-hoverdef -- --```go --field Member string --``` +-func _() { +- // a is equal to 5. Comment must stay the same - --@Member +- b := 5 +- _ = b //@rename("a", "b") +-} - +diff -urN a/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.golden b/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.golden +--- a/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.golden 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +--- bar-rename -- +-package issue43616 - --[`(a.Thing).Member` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing.Member) ---- Other-definition -- --godef/a/d.go:9:5-10: defined here as ```go --var a.Other a.Thing --``` +-type bar int //@rename("foo","bar"),prepare("oo","foo","foo") - --@Other +-var x struct{ bar } //@rename("foo","baz") - +-var _ = x.bar //@rename("foo","quux") - --[`a.Other` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Other) ---- Other-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/d.go", -- "start": { -- "line": 9, -- "column": 5, -- "offset": 121 -- }, -- "end": { -- "line": 9, -- "column": 10, -- "offset": 126 -- } -- }, -- "description": "```go\nvar a.Other a.Thing\n```\n\n@Other\n\n\n[`a.Other` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Other)" --} +--- baz-rename -- +-can't rename embedded fields: rename the type directly or name the field +--- quux-rename -- +-can't rename embedded fields: rename the type directly or name the field +diff -urN a/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.in b/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.in +--- a/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.in 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package issue43616 - ---- Other-hoverdef -- --```go --var a.Other a.Thing --``` +-type foo int //@rename("foo","bar"),prepare("oo","foo","foo") - --@Other +-var x struct{ foo } //@rename("foo","baz") - +-var _ = x.foo //@rename("foo","quux") +diff -urN a/gopls/internal/lsp/testdata/rename/shadow/shadow.go b/gopls/internal/lsp/testdata/rename/shadow/shadow.go +--- a/gopls/internal/lsp/testdata/rename/shadow/shadow.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/shadow/shadow.go 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ +-package shadow - --[`a.Other` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Other) ---- Thing-definition -- --godef/a/d.go:5:6-11: defined here as ```go --type Thing struct { -- Member string //@Member +-func _() { +- a := true +- b, c, _ := A(), B(), D() //@rename("A", "a"),rename("B", "b"),rename("b", "c"),rename("D", "d") +- d := false +- _, _, _, _ = a, b, c, d -} - --func (a.Thing).Method(i int) string --func (*a.Thing).Method2(i int, j int) (error, string) --func (a.Thing).Method3() --``` -- --[`a.Thing` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing) ---- Thing-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/d.go", -- "start": { -- "line": 5, -- "column": 6, -- "offset": 65 -- }, -- "end": { -- "line": 5, -- "column": 11, -- "offset": 70 -- } -- }, -- "description": "```go\ntype Thing struct {\n\tMember string //@Member\n}\n\nfunc (a.Thing).Method(i int) string\nfunc (*a.Thing).Method2(i int, j int) (error, string)\nfunc (a.Thing).Method3()\n```\n\n[`a.Thing` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing)" +-func A() int { +- return 0 -} - ---- Thing-hoverdef -- --```go --type Thing struct { -- Member string //@Member +-func B() int { +- return 0 -} - --func (a.Thing).Method(i int) string --func (*a.Thing).Method2(i int, j int) (error, string) --func (a.Thing).Method3() --``` -- --[`a.Thing` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Thing) ---- Things-definition -- --godef/a/d.go:11:6-12: defined here as ```go --func a.Things(val []string) []a.Thing --``` -- --[`a.Things` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Things) ---- Things-definition-json -- --{ -- "span": { -- "uri": "file://godef/a/d.go", -- "start": { -- "line": 11, -- "column": 6, -- "offset": 148 -- }, -- "end": { -- "line": 11, -- "column": 12, -- "offset": 154 -- } -- }, -- "description": "```go\nfunc a.Things(val []string) []a.Thing\n```\n\n[`a.Things` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Things)" +-func D() int { +- return 0 -} +diff -urN a/gopls/internal/lsp/testdata/rename/shadow/shadow.go.golden b/gopls/internal/lsp/testdata/rename/shadow/shadow.go.golden +--- a/gopls/internal/lsp/testdata/rename/shadow/shadow.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/shadow/shadow.go.golden 1970-01-01 08:00:00 +@@ -1,51 +0,0 @@ +--- a-rename -- +-shadow/shadow.go:10:6: renaming this func "A" to "a" +-shadow/shadow.go:5:13: would cause this reference to become shadowed +-shadow/shadow.go:4:2: by this intervening var definition +--- b-rename -- +-package shadow - ---- Things-hoverdef -- --```go --func a.Things(val []string) []a.Thing --``` +-func _() { +- a := true +- b, c, _ := A(), b(), D() //@rename("A", "a"),rename("B", "b"),rename("b", "c"),rename("D", "d") +- d := false +- _, _, _, _ = a, b, c, d +-} - --[`a.Things` on pkg.go.dev](https://pkg.go.dev/golang.org/lsptests/godef/a#Things) ---- eInt-hoverdef -- --```go --var x int --``` ---- eInterface-hoverdef -- --```go --var x interface{} --``` ---- eString-hoverdef -- --```go --var x string --``` -diff -urN a/gopls/internal/lsp/testdata/godef/broken/unclosedIf.go.golden b/gopls/internal/lsp/testdata/godef/broken/unclosedIf.go.golden ---- a/gopls/internal/lsp/testdata/godef/broken/unclosedIf.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/broken/unclosedIf.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,31 +0,0 @@ ---- myUnclosedIf-definition -- --godef/broken/unclosedIf.go:7:7-19: defined here as ```go --var myUnclosedIf string --``` +-func A() int { +- return 0 +-} - --@myUnclosedIf ---- myUnclosedIf-definition-json -- --{ -- "span": { -- "uri": "file://godef/broken/unclosedIf.go", -- "start": { -- "line": 7, -- "column": 7, -- "offset": 68 -- }, -- "end": { -- "line": 7, -- "column": 19, -- "offset": 80 -- } -- }, -- "description": "```go\nvar myUnclosedIf string\n```\n\n@myUnclosedIf" +-func b() int { +- return 0 -} - ---- myUnclosedIf-hoverdef -- --```go --var myUnclosedIf string --``` +-func D() int { +- return 0 +-} - --@myUnclosedIf +--- c-rename -- +-shadow/shadow.go:5:2: renaming this var "b" to "c" +-shadow/shadow.go:5:5: conflicts with var in same block +--- d-rename -- +-package shadow - -diff -urN a/gopls/internal/lsp/testdata/godef/broken/unclosedIf.go.in b/gopls/internal/lsp/testdata/godef/broken/unclosedIf.go.in ---- a/gopls/internal/lsp/testdata/godef/broken/unclosedIf.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/godef/broken/unclosedIf.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package broken +-func _() { +- a := true +- b, c, _ := A(), B(), d() //@rename("A", "a"),rename("B", "b"),rename("b", "c"),rename("D", "d") +- d := false +- _, _, _, _ = a, b, c, d +-} - --import "fmt" +-func A() int { +- return 0 +-} - --func unclosedIf() { -- if false { -- var myUnclosedIf string //@myUnclosedIf -- fmt.Printf("s = %v\n", myUnclosedIf) //@godef("my", myUnclosedIf) +-func B() int { +- return 0 -} -diff -urN a/gopls/internal/lsp/testdata/good/good0.go b/gopls/internal/lsp/testdata/good/good0.go ---- a/gopls/internal/lsp/testdata/good/good0.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/good/good0.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --package good //@diag("package", "no_diagnostics", "", "error") - --func stuff() { //@item(good_stuff, "stuff", "func()", "func"),prepare("stu", "stuff", "stuff") -- x := 5 -- random2(x) //@prepare("dom", "random2", "random2") +-func d() int { +- return 0 -} -diff -urN a/gopls/internal/lsp/testdata/good/good1.go b/gopls/internal/lsp/testdata/good/good1.go ---- a/gopls/internal/lsp/testdata/good/good1.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/good/good1.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,21 +0,0 @@ --package good //@diag("package", "no_diagnostics", "", "error") - --import ( -- "golang.org/lsptests/types" //@item(types_import, "types", "\"golang.org/lsptests/types\"", "package") --) +diff -urN a/gopls/internal/lsp/testdata/rename/testy/testy.go b/gopls/internal/lsp/testdata/rename/testy/testy.go +--- a/gopls/internal/lsp/testdata/rename/testy/testy.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/testy/testy.go 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package testy - --func random() int { //@item(good_random, "random", "func() int", "func") -- _ = "random() int" //@prepare("random", "", "") -- y := 6 + 7 //@prepare("7", "", "") -- return y //@prepare("return", "","") +-type tt int //@rename("tt", "testyType") +- +-func a() { +- foo := 42 //@rename("foo", "bar") -} +diff -urN a/gopls/internal/lsp/testdata/rename/testy/testy.go.golden b/gopls/internal/lsp/testdata/rename/testy/testy.go.golden +--- a/gopls/internal/lsp/testdata/rename/testy/testy.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/testy/testy.go.golden 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +--- bar-rename -- +-package testy - --func random2(y int) int { //@item(good_random2, "random2", "func(y int) int", "func"),item(good_y_param, "y", "int", "var") -- //@complete("", good_y_param, types_import, good_random, good_random2, good_stuff) -- var b types.Bob = &types.X{} //@prepare("ypes","types", "types") -- if _, ok := b.(*types.X); ok { //@complete("X", X_struct, Y_struct, Bob_interface, CoolAlias) -- _ = 0 // suppress "empty branch" diagnostic -- } +-type tt int //@rename("tt", "testyType") - -- return y +-func a() { +- bar := 42 //@rename("foo", "bar") -} -diff -urN a/gopls/internal/lsp/testdata/highlights/highlights.go b/gopls/internal/lsp/testdata/highlights/highlights.go ---- a/gopls/internal/lsp/testdata/highlights/highlights.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/highlights/highlights.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,151 +0,0 @@ --package highlights - --import ( -- "fmt" //@mark(fmtImp, "\"fmt\""),highlight(fmtImp, fmtImp, fmt1, fmt2, fmt3, fmt4) -- h2 "net/http" //@mark(hImp, "h2"),highlight(hImp, hImp, hUse) -- "sort" --) +--- testyType-rename -- +-package testy - --type F struct{ bar int } //@mark(barDeclaration, "bar"),highlight(barDeclaration, barDeclaration, bar1, bar2, bar3) +-type testyType int //@rename("tt", "testyType") - --func _() F { -- return F{ -- bar: 123, //@mark(bar1, "bar"),highlight(bar1, barDeclaration, bar1, bar2, bar3) -- } +-func a() { +- foo := 42 //@rename("foo", "bar") -} - --var foo = F{bar: 52} //@mark(fooDeclaration, "foo"),mark(bar2, "bar"),highlight(fooDeclaration, fooDeclaration, fooUse),highlight(bar2, barDeclaration, bar1, bar2, bar3) -- --func Print() { //@mark(printFunc, "Print"),highlight(printFunc, printFunc, printTest) -- _ = h2.Client{} //@mark(hUse, "h2"),highlight(hUse, hImp, hUse) +diff -urN a/gopls/internal/lsp/testdata/rename/testy/testy_test.go b/gopls/internal/lsp/testdata/rename/testy/testy_test.go +--- a/gopls/internal/lsp/testdata/rename/testy/testy_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/testy/testy_test.go 1970-01-01 08:00:00 +@@ -1,8 +0,0 @@ +-package testy - -- fmt.Println(foo) //@mark(fooUse, "foo"),highlight(fooUse, fooDeclaration, fooUse),mark(fmt1, "fmt"),highlight(fmt1, fmtImp, fmt1, fmt2, fmt3, fmt4) -- fmt.Print("yo") //@mark(printSep, "Print"),highlight(printSep, printSep, print1, print2),mark(fmt2, "fmt"),highlight(fmt2, fmtImp, fmt1, fmt2, fmt3, fmt4) --} +-import "testing" - --func (x *F) Inc() { //@mark(xRightDecl, "x"),mark(xLeftDecl, " *"),highlight(xRightDecl, xRightDecl, xUse),highlight(xLeftDecl, xRightDecl, xUse) -- x.bar++ //@mark(xUse, "x"),mark(bar3, "bar"),highlight(xUse, xRightDecl, xUse),highlight(bar3, barDeclaration, bar1, bar2, bar3) +-func TestSomething(t *testing.T) { +- var x int //@rename("x", "testyX") +- a() //@rename("a", "b") -} +diff -urN a/gopls/internal/lsp/testdata/rename/testy/testy_test.go.golden b/gopls/internal/lsp/testdata/rename/testy/testy_test.go.golden +--- a/gopls/internal/lsp/testdata/rename/testy/testy_test.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/rename/testy/testy_test.go.golden 1970-01-01 08:00:00 +@@ -1,30 +0,0 @@ +--- b-rename -- +-testy.go: +-package testy - --func testFunctions() { -- fmt.Print("main start") //@mark(print1, "Print"),highlight(print1, printSep, print1, print2),mark(fmt3, "fmt"),highlight(fmt3, fmtImp, fmt1, fmt2, fmt3, fmt4) -- fmt.Print("ok") //@mark(print2, "Print"),highlight(print2, printSep, print1, print2),mark(fmt4, "fmt"),highlight(fmt4, fmtImp, fmt1, fmt2, fmt3, fmt4) -- Print() //@mark(printTest, "Print"),highlight(printTest, printFunc, printTest) --} +-type tt int //@rename("tt", "testyType") - --func toProtocolHighlight(rngs []int) []DocumentHighlight { //@mark(doc1, "DocumentHighlight"),mark(docRet1, "[]DocumentHighlight"),highlight(doc1, docRet1, doc1, doc2, doc3, result) -- result := make([]DocumentHighlight, 0, len(rngs)) //@mark(doc2, "DocumentHighlight"),highlight(doc2, doc1, doc2, doc3) -- for _, rng := range rngs { -- result = append(result, DocumentHighlight{ //@mark(doc3, "DocumentHighlight"),highlight(doc3, doc1, doc2, doc3) -- Range: rng, -- }) -- } -- return result //@mark(result, "result") +-func b() { +- foo := 42 //@rename("foo", "bar") -} - --func testForLoops() { -- for i := 0; i < 10; i++ { //@mark(forDecl1, "for"),highlight(forDecl1, forDecl1, brk1, cont1) -- if i > 8 { -- break //@mark(brk1, "break"),highlight(brk1, forDecl1, brk1, cont1) -- } -- if i < 2 { -- for j := 1; j < 10; j++ { //@mark(forDecl2, "for"),highlight(forDecl2, forDecl2, cont2) -- if j < 3 { -- for k := 1; k < 10; k++ { //@mark(forDecl3, "for"),highlight(forDecl3, forDecl3, cont3) -- if k < 3 { -- continue //@mark(cont3, "continue"),highlight(cont3, forDecl3, cont3) -- } -- } -- continue //@mark(cont2, "continue"),highlight(cont2, forDecl2, cont2) -- } -- } -- continue //@mark(cont1, "continue"),highlight(cont1, forDecl1, brk1, cont1) -- } -- } +-testy_test.go: +-package testy - -- arr := []int{} -- for i := range arr { //@mark(forDecl4, "for"),highlight(forDecl4, forDecl4, brk4, cont4) -- if i > 8 { -- break //@mark(brk4, "break"),highlight(brk4, forDecl4, brk4, cont4) -- } -- if i < 4 { -- continue //@mark(cont4, "continue"),highlight(cont4, forDecl4, brk4, cont4) -- } -- } +-import "testing" - --Outer: -- for i := 0; i < 10; i++ { //@mark(forDecl5, "for"),highlight(forDecl5, forDecl5, brk5, brk6, brk8) -- break //@mark(brk5, "break"),highlight(brk5, forDecl5, brk5, brk6, brk8) -- for { //@mark(forDecl6, "for"),highlight(forDecl6, forDecl6, cont5) -- if i == 1 { -- break Outer //@mark(brk6, "break Outer"),highlight(brk6, forDecl5, brk5, brk6, brk8) -- } -- switch i { //@mark(switch1, "switch"),highlight(switch1, switch1, brk7) -- case 5: -- break //@mark(brk7, "break"),highlight(brk7, switch1, brk7) -- case 6: -- continue //@mark(cont5, "continue"),highlight(cont5, forDecl6, cont5) -- case 7: -- break Outer //@mark(brk8, "break Outer"),highlight(brk8, forDecl5, brk5, brk6, brk8) -- } -- } -- } +-func TestSomething(t *testing.T) { +- var x int //@rename("x", "testyX") +- b() //@rename("a", "b") -} - --func testSwitch() { -- var i, j int -- --L1: -- for { //@mark(forDecl7, "for"),highlight(forDecl7, forDecl7, brk10, cont6) -- L2: -- switch i { //@mark(switch2, "switch"),highlight(switch2, switch2, brk11, brk12, brk13) -- case 1: -- switch j { //@mark(switch3, "switch"),highlight(switch3, switch3, brk9) -- case 1: -- break //@mark(brk9, "break"),highlight(brk9, switch3, brk9) -- case 2: -- break L1 //@mark(brk10, "break L1"),highlight(brk10, forDecl7, brk10, cont6) -- case 3: -- break L2 //@mark(brk11, "break L2"),highlight(brk11, switch2, brk11, brk12, brk13) -- default: -- continue //@mark(cont6, "continue"),highlight(cont6, forDecl7, brk10, cont6) -- } -- case 2: -- break //@mark(brk12, "break"),highlight(brk12, switch2, brk11, brk12, brk13) -- default: -- break L2 //@mark(brk13, "break L2"),highlight(brk13, switch2, brk11, brk12, brk13) -- } -- } --} +--- testyX-rename -- +-package testy - --func testReturn() bool { //@mark(func1, "func"),mark(bool1, "bool"),highlight(func1, func1, fullRet11, fullRet12),highlight(bool1, bool1, false1, bool2, true1) -- if 1 < 2 { -- return false //@mark(ret11, "return"),mark(fullRet11, "return false"),mark(false1, "false"),highlight(ret11, func1, fullRet11, fullRet12) -- } -- candidates := []int{} -- sort.SliceStable(candidates, func(i, j int) bool { //@mark(func2, "func"),mark(bool2, "bool"),highlight(func2, func2, fullRet2) -- return candidates[i] > candidates[j] //@mark(ret2, "return"),mark(fullRet2, "return candidates[i] > candidates[j]"),highlight(ret2, func2, fullRet2) -- }) -- return true //@mark(ret12, "return"),mark(fullRet12, "return true"),mark(true1, "true"),highlight(ret12, func1, fullRet11, fullRet12) --} +-import "testing" - --func testReturnFields() float64 { //@mark(retVal1, "float64"),highlight(retVal1, retVal1, retVal11, retVal21) -- if 1 < 2 { -- return 20.1 //@mark(retVal11, "20.1"),highlight(retVal11, retVal1, retVal11, retVal21) -- } -- z := 4.3 //@mark(zDecl, "z") -- return z //@mark(retVal21, "z"),highlight(retVal21, retVal1, retVal11, zDecl, retVal21) +-func TestSomething(t *testing.T) { +- var testyX int //@rename("x", "testyX") +- a() //@rename("a", "b") -} - --func testReturnMultipleFields() (float32, string) { //@mark(retVal31, "float32"),mark(retVal32, "string"),highlight(retVal31, retVal31, retVal41, retVal51),highlight(retVal32, retVal32, retVal42, retVal52) -- y := "im a var" //@mark(yDecl, "y"), -- if 1 < 2 { -- return 20.1, y //@mark(retVal41, "20.1"),mark(retVal42, "y"),highlight(retVal41, retVal31, retVal41, retVal51),highlight(retVal42, retVal32, yDecl, retVal42, retVal52) -- } -- return 4.9, "test" //@mark(retVal51, "4.9"),mark(retVal52, "\"test\""),highlight(retVal51, retVal31, retVal41, retVal51),highlight(retVal52, retVal32, retVal42, retVal52) --} +diff -urN a/gopls/internal/lsp/testdata/selectionrange/foo.go b/gopls/internal/lsp/testdata/selectionrange/foo.go +--- a/gopls/internal/lsp/testdata/selectionrange/foo.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/selectionrange/foo.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package foo - --func testReturnFunc() int32 { //@mark(retCall, "int32") -- mulch := 1 //@mark(mulchDec, "mulch"),highlight(mulchDec, mulchDec, mulchRet) -- return int32(mulch) //@mark(mulchRet, "mulch"),mark(retFunc, "int32"),mark(retTotal, "int32(mulch)"),highlight(mulchRet, mulchDec, mulchRet),highlight(retFunc, retCall, retFunc, retTotal) --} -diff -urN a/gopls/internal/lsp/testdata/implementation/implementation_generics.go b/gopls/internal/lsp/testdata/implementation/implementation_generics.go ---- a/gopls/internal/lsp/testdata/implementation/implementation_generics.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/implementation/implementation_generics.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,16 +0,0 @@ --//go:build go1.18 --// +build go1.18 +-import "time" - --package implementation +-func Bar(x, y int, t time.Time) int { +- zs := []int{1, 2, 3} //@selectionrange("1") - --// -- generics -- +- for _, z := range zs { +- x = x + z + y + zs[1] //@selectionrange("1") +- } - --type GenIface[T any] interface { //@mark(GenIface, "GenIface"),implementations("GenIface", GC) -- F(int, string, T) //@mark(GenIfaceF, "F"),implementations("F", GCF) +- return x + y //@selectionrange("+") -} +diff -urN a/gopls/internal/lsp/testdata/selectionrange/foo.go.golden b/gopls/internal/lsp/testdata/selectionrange/foo.go.golden +--- a/gopls/internal/lsp/testdata/selectionrange/foo.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/selectionrange/foo.go.golden 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +--- selectionrange_foo_12_11 -- +-Ranges 0: +- 11:8-11:13 "x + y" +- 11:1-11:13 "return x + y" +- 4:36-12:1 "{\\n\tzs := []int{...ionrange(\"+\")\\n}" +- 4:0-12:1 "func Bar(x, y i...ionrange(\"+\")\\n}" +- 0:0-12:1 "package foo\\n\\nim...ionrange(\"+\")\\n}" - --type GenConc[U any] int //@mark(GenConc, "GenConc"),implementations("GenConc", GI) -- --func (GenConc[V]) F(int, string, V) {} //@mark(GenConcF, "F"),implementations("F", GIF) +--- selectionrange_foo_6_14 -- +-Ranges 0: +- 5:13-5:14 "1" +- 5:7-5:21 "[]int{1, 2, 3}" +- 5:1-5:21 "zs := []int{1, 2, 3}" +- 4:36-12:1 "{\\n\tzs := []int{...ionrange(\"+\")\\n}" +- 4:0-12:1 "func Bar(x, y i...ionrange(\"+\")\\n}" +- 0:0-12:1 "package foo\\n\\nim...ionrange(\"+\")\\n}" - --type GenConcString struct{ GenConc[string] } //@mark(GenConcString, "GenConcString"),implementations(GenConcString, GIString) -diff -urN a/gopls/internal/lsp/testdata/implementation/implementation.go b/gopls/internal/lsp/testdata/implementation/implementation.go ---- a/gopls/internal/lsp/testdata/implementation/implementation.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/implementation/implementation.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,37 +0,0 @@ --package implementation +--- selectionrange_foo_9_22 -- +-Ranges 0: +- 8:21-8:22 "1" +- 8:18-8:23 "zs[1]" +- 8:6-8:23 "x + z + y + zs[1]" +- 8:2-8:23 "x = x + z + y + zs[1]" +- 7:22-9:2 "{\\n\t\tx = x + z +...onrange(\"1\")\\n\t}" +- 7:1-9:2 "for _, z := ran...onrange(\"1\")\\n\t}" +- 4:36-12:1 "{\\n\tzs := []int{...ionrange(\"+\")\\n}" +- 4:0-12:1 "func Bar(x, y i...ionrange(\"+\")\\n}" +- 0:0-12:1 "package foo\\n\\nim...ionrange(\"+\")\\n}" - --import "golang.org/lsptests/implementation/other" +diff -urN a/gopls/internal/lsp/testdata/semantic/README.md b/gopls/internal/lsp/testdata/semantic/README.md +--- a/gopls/internal/lsp/testdata/semantic/README.md 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/semantic/README.md 1970-01-01 08:00:00 +@@ -1,2 +0,0 @@ +-The golden files are the output of `gopls semtok `, with `-- semantic --` +-inserted as the first line (the spaces are mandatory) and an extra newline at the end. +diff -urN a/gopls/internal/lsp/testdata/semantic/a.go b/gopls/internal/lsp/testdata/semantic/a.go +--- a/gopls/internal/lsp/testdata/semantic/a.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/semantic/a.go 1970-01-01 08:00:00 +@@ -1,81 +0,0 @@ +-package semantictokens //@ semantic("") - --type ImpP struct{} //@ImpP,implementations("ImpP", Laugher, OtherLaugher) +-import ( +- _ "encoding/utf8" +- utf "encoding/utf8" +- "fmt" //@ semantic("fmt") +- . "fmt" +- "unicode/utf8" +-) - --func (*ImpP) Laugh() { //@mark(LaughP, "Laugh"),implementations("Laugh", Laugh, OtherLaugh) --} +-var ( +- a = fmt.Print +- b []string = []string{"foo"} +- c1 chan int +- c2 <-chan int +- c3 = make([]chan<- int) +- b = A{X: 23} +- m map[bool][3]*float64 +-) - --type ImpS struct{} //@ImpS,implementations("ImpS", Laugher, OtherLaugher) +-const ( +- xx F = iota +- yy = xx + 3 +- zz = "" +- ww = "not " + zz +-) - --func (ImpS) Laugh() { //@mark(LaughS, "Laugh"),implementations("Laugh", Laugh, OtherLaugh) +-type A struct { +- X int `foof` -} -- --type Laugher interface { //@Laugher,implementations("Laugher", ImpP, OtherImpP, ImpS, OtherImpS, embedsImpP) -- Laugh() //@Laugh,implementations("Laugh", LaughP, OtherLaughP, LaughS, OtherLaughS) +-type B interface { +- A +- sad(int) bool -} - --type Foo struct { //@implementations("Foo", Joker) -- other.Foo --} +-type F int - --type Joker interface { //@Joker -- Joke() //@Joke,implementations("Joke", ImpJoker) +-func (a *A) f() bool { +- var z string +- x := "foo" +- a(x) +- y := "bar" + x +- switch z { +- case "xx": +- default: +- } +- select { +- case z := <-c3[0]: +- default: +- } +- for k, v := range m { +- return (!k) && v[0] == nil +- } +- c2 <- A.X +- w := b[4:] +- j := len(x) +- j-- +- q := []interface{}{j, 23i, &y} +- g(q...) +- return true -} - --type cryer int //@implementations("cryer", Cryer) +-func g(vv ...interface{}) { +- ff := func() {} +- defer ff() +- go utf.RuneCount("") +- go utf8.RuneCount(vv.(string)) +- if true { +- } else { +- } +-Never: +- for i := 0; i < 10; { +- break Never +- } +- _, ok := vv[0].(A) +- if !ok { +- switch x := vv[0].(type) { +- } +- goto Never +- } +-} +diff -urN a/gopls/internal/lsp/testdata/semantic/a.go.golden b/gopls/internal/lsp/testdata/semantic/a.go.golden +--- a/gopls/internal/lsp/testdata/semantic/a.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/semantic/a.go.golden 1970-01-01 08:00:00 +@@ -1,83 +0,0 @@ +--- semantic -- +-/*⇒7,keyword,[]*/package /*⇒14,namespace,[]*/semantictokens /*⇒16,comment,[]*///@ semantic("") - --func (cryer) Cry(other.CryType) {} //@mark(CryImpl, "Cry"),implementations("Cry", Cry) +-/*⇒6,keyword,[]*/import ( +- _ "encoding/utf8" +- /*⇒3,namespace,[]*/utf "encoding/utf8" +- "fmt"/*⇐3,namespace,[]*/ /*⇒19,comment,[]*///@ semantic("fmt") +- . "fmt" +- "unicode/utf8"/*⇐4,namespace,[]*/ +-) - --type Empty interface{} //@implementations("Empty") +-/*⇒3,keyword,[]*/var ( +- /*⇒1,variable,[definition]*/a = /*⇒3,namespace,[]*/fmt./*⇒5,function,[]*/Print +- /*⇒1,variable,[definition]*/b []/*⇒6,type,[defaultLibrary]*/string = []/*⇒6,type,[defaultLibrary]*/string{/*⇒5,string,[]*/"foo"} +- /*⇒2,variable,[definition]*/c1 /*⇒4,keyword,[]*/chan /*⇒3,type,[defaultLibrary]*/int +- /*⇒2,variable,[definition]*/c2 /*⇒2,operator,[]*/<-/*⇒4,keyword,[]*/chan /*⇒3,type,[defaultLibrary]*/int +- /*⇒2,variable,[definition]*/c3 = /*⇒4,function,[defaultLibrary]*/make([]/*⇒4,keyword,[]*/chan/*⇒2,operator,[]*/<- /*⇒3,type,[defaultLibrary]*/int) +- /*⇒1,variable,[definition]*/b = /*⇒1,type,[]*/A{/*⇒1,variable,[]*/X: /*⇒2,number,[]*/23} +- /*⇒1,variable,[definition]*/m /*⇒3,keyword,[]*/map[/*⇒4,type,[defaultLibrary]*/bool][/*⇒1,number,[]*/3]/*⇒1,operator,[]*/*/*⇒7,type,[defaultLibrary]*/float64 +-) - --var _ interface{ Joke() } //@implementations("Joke", ImpJoker) +-/*⇒5,keyword,[]*/const ( +- /*⇒2,variable,[definition readonly]*/xx /*⇒1,type,[]*/F = /*⇒4,variable,[readonly]*/iota +- /*⇒2,variable,[definition readonly]*/yy = /*⇒2,variable,[readonly]*/xx /*⇒1,operator,[]*/+ /*⇒1,number,[]*/3 +- /*⇒2,variable,[definition readonly]*/zz = /*⇒2,string,[]*/"" +- /*⇒2,variable,[definition readonly]*/ww = /*⇒6,string,[]*/"not " /*⇒1,operator,[]*/+ /*⇒2,variable,[readonly]*/zz +-) - --type embedsImpP struct { //@embedsImpP -- ImpP //@implementations("ImpP", Laugher, OtherLaugher) +-/*⇒4,keyword,[]*/type /*⇒1,type,[definition]*/A /*⇒6,keyword,[]*/struct { +- /*⇒1,variable,[definition]*/X /*⇒3,type,[defaultLibrary]*/int /*⇒6,string,[]*/`foof` +-} +-/*⇒4,keyword,[]*/type /*⇒1,type,[definition]*/B /*⇒9,keyword,[]*/interface { +- /*⇒1,type,[]*/A +- /*⇒3,method,[definition]*/sad(/*⇒3,type,[defaultLibrary]*/int) /*⇒4,type,[defaultLibrary]*/bool -} -diff -urN a/gopls/internal/lsp/testdata/implementation/other/other_generics.go b/gopls/internal/lsp/testdata/implementation/other/other_generics.go ---- a/gopls/internal/lsp/testdata/implementation/other/other_generics.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/implementation/other/other_generics.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,16 +0,0 @@ --//go:build go1.18 --// +build go1.18 - --package other +-/*⇒4,keyword,[]*/type /*⇒1,type,[definition]*/F /*⇒3,type,[defaultLibrary]*/int - --// -- generics (limited support) -- +-/*⇒4,keyword,[]*/func (/*⇒1,variable,[]*/a /*⇒1,operator,[]*/*/*⇒1,type,[]*/A) /*⇒1,method,[definition]*/f() /*⇒4,type,[defaultLibrary]*/bool { +- /*⇒3,keyword,[]*/var /*⇒1,variable,[definition]*/z /*⇒6,type,[defaultLibrary]*/string +- /*⇒1,variable,[definition]*/x /*⇒2,operator,[]*/:= /*⇒5,string,[]*/"foo" +- /*⇒1,variable,[]*/a(/*⇒1,variable,[]*/x) +- /*⇒1,variable,[definition]*/y /*⇒2,operator,[]*/:= /*⇒5,string,[]*/"bar" /*⇒1,operator,[]*/+ /*⇒1,variable,[]*/x +- /*⇒6,keyword,[]*/switch /*⇒1,variable,[]*/z { +- /*⇒4,keyword,[]*/case /*⇒4,string,[]*/"xx": +- /*⇒7,keyword,[]*/default: +- } +- /*⇒6,keyword,[]*/select { +- /*⇒4,keyword,[]*/case /*⇒1,variable,[definition]*/z /*⇒2,operator,[]*/:= /*⇒2,operator,[]*/<-/*⇒2,variable,[]*/c3[/*⇒1,number,[]*/0]: +- /*⇒7,keyword,[]*/default: +- } +- /*⇒3,keyword,[]*/for /*⇒1,variable,[definition]*/k, /*⇒1,variable,[definition]*/v := /*⇒5,keyword,[]*/range /*⇒1,variable,[]*/m { +- /*⇒6,keyword,[]*/return (/*⇒1,operator,[]*/!/*⇒1,variable,[]*/k) /*⇒2,operator,[]*/&& /*⇒1,variable,[]*/v[/*⇒1,number,[]*/0] /*⇒2,operator,[]*/== /*⇒3,variable,[readonly defaultLibrary]*/nil +- } +- /*⇒2,variable,[]*/c2 /*⇒2,operator,[]*/<- /*⇒1,type,[]*/A./*⇒1,variable,[]*/X +- /*⇒1,variable,[definition]*/w /*⇒2,operator,[]*/:= /*⇒1,variable,[]*/b[/*⇒1,number,[]*/4:] +- /*⇒1,variable,[definition]*/j /*⇒2,operator,[]*/:= /*⇒3,function,[defaultLibrary]*/len(/*⇒1,variable,[]*/x) +- /*⇒1,variable,[]*/j/*⇒2,operator,[]*/-- +- /*⇒1,variable,[definition]*/q /*⇒2,operator,[]*/:= []/*⇒9,keyword,[]*/interface{}{/*⇒1,variable,[]*/j, /*⇒3,number,[]*/23i, /*⇒1,operator,[]*/&/*⇒1,variable,[]*/y} +- /*⇒1,function,[]*/g(/*⇒1,variable,[]*/q/*⇒3,operator,[]*/...) +- /*⇒6,keyword,[]*/return /*⇒4,variable,[readonly]*/true +-} - --type GI[T any] interface { //@mark(GI, "GI"),implementations("GI", GenConc) -- F(int, string, T) //@mark(GIF, "F"),implementations("F", GenConcF) +-/*⇒4,keyword,[]*/func /*⇒1,function,[definition]*/g(/*⇒2,parameter,[definition]*/vv /*⇒3,operator,[]*/.../*⇒9,keyword,[]*/interface{}) { +- /*⇒2,variable,[definition]*/ff /*⇒2,operator,[]*/:= /*⇒4,keyword,[]*/func() {} +- /*⇒5,keyword,[]*/defer /*⇒2,function,[]*/ff() +- /*⇒2,keyword,[]*/go /*⇒3,namespace,[]*/utf./*⇒9,function,[]*/RuneCount(/*⇒2,string,[]*/"") +- /*⇒2,keyword,[]*/go /*⇒4,namespace,[]*/utf8./*⇒9,function,[]*/RuneCount(/*⇒2,parameter,[]*/vv.(/*⇒6,type,[]*/string)) +- /*⇒2,keyword,[]*/if /*⇒4,variable,[readonly]*/true { +- } /*⇒4,keyword,[]*/else { +- } +-/*⇒5,parameter,[definition]*/Never: +- /*⇒3,keyword,[]*/for /*⇒1,variable,[definition]*/i /*⇒2,operator,[]*/:= /*⇒1,number,[]*/0; /*⇒1,variable,[]*/i /*⇒1,operator,[]*/< /*⇒2,number,[]*/10; { +- /*⇒5,keyword,[]*/break Never +- } +- _, /*⇒2,variable,[definition]*/ok /*⇒2,operator,[]*/:= /*⇒2,parameter,[]*/vv[/*⇒1,number,[]*/0].(/*⇒1,type,[]*/A) +- /*⇒2,keyword,[]*/if /*⇒1,operator,[]*/!/*⇒2,variable,[]*/ok { +- /*⇒6,keyword,[]*/switch /*⇒1,variable,[definition]*/x /*⇒2,operator,[]*/:= /*⇒2,parameter,[]*/vv[/*⇒1,number,[]*/0].(/*⇒4,keyword,[]*/type) { +- } +- /*⇒4,keyword,[]*/goto Never +- } -} - --type GIString GI[string] //@mark(GIString, "GIString"),implementations("GIString", GenConcString) +diff -urN a/gopls/internal/lsp/testdata/semantic/b.go b/gopls/internal/lsp/testdata/semantic/b.go +--- a/gopls/internal/lsp/testdata/semantic/b.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/semantic/b.go 1970-01-01 08:00:00 +@@ -1,38 +0,0 @@ +-package semantictokens //@ semantic("") - --type GC[U any] int //@mark(GC, "GC"),implementations("GC", GenIface) +-func f(x ...interface{}) { +-} - --func (GC[V]) F(int, string, V) {} //@mark(GCF, "F"),implementations("F", GenIfaceF) -diff -urN a/gopls/internal/lsp/testdata/implementation/other/other.go b/gopls/internal/lsp/testdata/implementation/other/other.go ---- a/gopls/internal/lsp/testdata/implementation/other/other.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/implementation/other/other.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,27 +0,0 @@ --package other +-func weirⰀd() { /*😀*/ // comment +- const ( +- snil = nil +- nil = true +- true = false +- false = snil +- cmd = `foof` +- double = iota +- iota = copy +- four = (len(cmd)/2 < 5) +- five = four +- ) +- f(cmd, nil, double, iota) +-} - --type ImpP struct{} //@mark(OtherImpP, "ImpP") +-/* - --func (*ImpP) Laugh() { //@mark(OtherLaughP, "Laugh") +-multiline */ /* +-multiline +-*/ +-type AA int +-type BB struct { +- AA +-} +-type CC struct { +- AA int -} +-type D func(aa AA) (BB error) +-type E func(AA) BB - --type ImpS struct{} //@mark(OtherImpS, "ImpS") +-var a chan<- chan int +-var b chan<- <-chan int +-var c <-chan <-chan int +diff -urN a/gopls/internal/lsp/testdata/semantic/b.go.golden b/gopls/internal/lsp/testdata/semantic/b.go.golden +--- a/gopls/internal/lsp/testdata/semantic/b.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/semantic/b.go.golden 1970-01-01 08:00:00 +@@ -1,40 +0,0 @@ +--- semantic -- +-/*⇒7,keyword,[]*/package /*⇒14,namespace,[]*/semantictokens /*⇒16,comment,[]*///@ semantic("") - --func (ImpS) Laugh() { //@mark(OtherLaughS, "Laugh") +-/*⇒4,keyword,[]*/func /*⇒1,function,[definition]*/f(/*⇒1,parameter,[definition]*/x /*⇒3,operator,[]*/.../*⇒9,keyword,[]*/interface{}) { -} - --type ImpI interface { //@mark(OtherLaugher, "ImpI") -- Laugh() //@mark(OtherLaugh, "Laugh") +-/*⇒4,keyword,[]*/func /*⇒6,function,[definition]*/weirⰀd() { /*⇒5,comment,[]*//*😀*/ /*⇒10,comment,[]*/// comment +- /*⇒5,keyword,[]*/const ( +- /*⇒4,variable,[definition readonly]*/snil = /*⇒3,variable,[readonly defaultLibrary]*/nil +- /*⇒3,variable,[definition readonly]*/nil = /*⇒4,variable,[readonly]*/true +- /*⇒4,variable,[definition readonly]*/true = /*⇒5,variable,[readonly]*/false +- /*⇒5,variable,[definition readonly]*/false = /*⇒4,variable,[readonly]*/snil +- /*⇒3,variable,[definition readonly]*/cmd = /*⇒6,string,[]*/`foof` +- /*⇒6,variable,[definition readonly]*/double = /*⇒4,variable,[readonly]*/iota +- /*⇒4,variable,[definition readonly]*/iota = /*⇒4,function,[defaultLibrary]*/copy +- /*⇒4,variable,[definition readonly]*/four = (/*⇒3,function,[defaultLibrary]*/len(/*⇒3,variable,[readonly]*/cmd)/*⇒1,operator,[]*// /*⇒1,number,[]*/2 /*⇒1,operator,[]*/< /*⇒1,number,[]*/5) +- /*⇒4,variable,[definition readonly]*/five = /*⇒4,variable,[readonly]*/four +- ) +- /*⇒1,function,[]*/f(/*⇒3,variable,[readonly]*/cmd, /*⇒3,variable,[readonly]*/nil, /*⇒6,variable,[readonly]*/double, /*⇒4,variable,[readonly]*/iota) -} - --type Foo struct { //@implementations("Foo", Joker) +-/*⇒2,comment,[]*//* +-/*⇒0,comment,[]*/ +-/*⇒12,comment,[]*/multiline */ /*⇒2,comment,[]*//* +-/*⇒9,comment,[]*/multiline +-/*⇒2,comment,[]*/*/ +-/*⇒4,keyword,[]*/type /*⇒2,type,[definition]*/AA /*⇒3,type,[defaultLibrary]*/int +-/*⇒4,keyword,[]*/type /*⇒2,type,[definition]*/BB /*⇒6,keyword,[]*/struct { +- /*⇒2,type,[]*/AA -} -- --func (Foo) Joke() { //@mark(ImpJoker, "Joke"),implementations("Joke", Joke) +-/*⇒4,keyword,[]*/type /*⇒2,type,[definition]*/CC /*⇒6,keyword,[]*/struct { +- /*⇒2,variable,[definition]*/AA /*⇒3,type,[defaultLibrary]*/int -} +-/*⇒4,keyword,[]*/type /*⇒1,type,[definition]*/D /*⇒4,keyword,[]*/func(/*⇒2,parameter,[definition]*/aa /*⇒2,type,[]*/AA) (/*⇒2,parameter,[definition]*/BB /*⇒5,type,[]*/error) +-/*⇒4,keyword,[]*/type /*⇒1,type,[definition]*/E /*⇒4,keyword,[]*/func(/*⇒2,type,[]*/AA) /*⇒2,type,[]*/BB - --type CryType int +-/*⇒3,keyword,[]*/var /*⇒1,variable,[definition]*/a /*⇒4,keyword,[]*/chan/*⇒2,operator,[]*/<- /*⇒4,keyword,[]*/chan /*⇒3,type,[defaultLibrary]*/int +-/*⇒3,keyword,[]*/var /*⇒1,variable,[definition]*/b /*⇒4,keyword,[]*/chan/*⇒2,operator,[]*/<- /*⇒2,operator,[]*/<-/*⇒4,keyword,[]*/chan /*⇒3,type,[defaultLibrary]*/int +-/*⇒3,keyword,[]*/var /*⇒1,variable,[definition]*/c /*⇒2,operator,[]*/<-/*⇒4,keyword,[]*/chan /*⇒2,operator,[]*/<-/*⇒4,keyword,[]*/chan /*⇒3,type,[defaultLibrary]*/int - --type Cryer interface { //@Cryer -- Cry(CryType) //@Cry,implementations("Cry", CryImpl) --} -diff -urN a/gopls/internal/lsp/testdata/implementation/other/other_test.go b/gopls/internal/lsp/testdata/implementation/other/other_test.go ---- a/gopls/internal/lsp/testdata/implementation/other/other_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/implementation/other/other_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package other +diff -urN a/gopls/internal/lsp/testdata/semantic/semantic_test.go b/gopls/internal/lsp/testdata/semantic/semantic_test.go +--- a/gopls/internal/lsp/testdata/semantic/semantic_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/semantic/semantic_test.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package semantictokens - -import ( +- "os" - "testing" -) - --// This exists so the other.test package comes into existence. +-func TestSemanticTokens(t *testing.T) { +- a, _ := os.Getwd() +- // climb up to find internal/lsp +- // find all the .go files - --func TestOther(t *testing.T) { -} -diff -urN a/gopls/internal/lsp/testdata/importedcomplit/imported_complit.go.in b/gopls/internal/lsp/testdata/importedcomplit/imported_complit.go.in ---- a/gopls/internal/lsp/testdata/importedcomplit/imported_complit.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/importedcomplit/imported_complit.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,42 +0,0 @@ --package importedcomplit +diff -urN a/gopls/internal/lsp/testdata/signature/signature.go b/gopls/internal/lsp/testdata/signature/signature.go +--- a/gopls/internal/lsp/testdata/signature/signature.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/signature/signature.go 1970-01-01 08:00:00 +@@ -1,85 +0,0 @@ +-// Package signature has tests for signature help. +-package signature - -import ( -- "golang.org/lsptests/foo" -- -- // import completions -- "fm" //@complete("\" //", fmtImport) -- "go/pars" //@complete("\" //", parserImport) -- "golang.org/lsptests/signa" //@complete("na\" //", signatureImport) -- "golang.org/lspte" //@complete("\" //", lsptestsImport) -- "crypto/elli" //@complete("\" //", cryptoImport) -- "golang.org/lsptests/sign" //@complete("\" //", signatureImport) -- "golang.org/lsptests/sign" //@complete("ests", lsptestsImport) -- namedParser "go/pars" //@complete("\" //", parserImport) +- "bytes" +- "encoding/json" +- "math/big" -) - --func _() { -- var V int //@item(icVVar, "V", "int", "var") -- _ = foo.StructFoo{V} //@complete("}", Value, icVVar) +-func Foo(a string, b int) (c bool) { +- return -} - --func _() { -- var ( -- aa string //@item(icAAVar, "aa", "string", "var") -- ab int //@item(icABVar, "ab", "int", "var") -- ) -- -- _ = foo.StructFoo{a} //@complete("}", abVar, aaVar) -- -- var s struct { -- AA string //@item(icFieldAA, "AA", "string", "field") -- AB int //@item(icFieldAB, "AB", "int", "field") -- } -- -- _ = foo.StructFoo{s.} //@complete("}", icFieldAB, icFieldAA) +-func Bar(float64, ...byte) { -} - --/* "fmt" */ //@item(fmtImport, "fmt", "\"fmt\"", "package") --/* "go/parser" */ //@item(parserImport, "parser", "\"go/parser\"", "package") --/* "golang.org/lsptests/signature" */ //@item(signatureImport, "signature", "\"golang.org/lsptests/signature\"", "package") --/* "golang.org/lsptests/" */ //@item(lsptestsImport, "lsptests/", "\"golang.org/lsptests/\"", "package") --/* "crypto/elliptic" */ //@item(cryptoImport, "elliptic", "\"crypto/elliptic\"", "package") -diff -urN a/gopls/internal/lsp/testdata/imports/add_import.go.golden b/gopls/internal/lsp/testdata/imports/add_import.go.golden ---- a/gopls/internal/lsp/testdata/imports/add_import.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/add_import.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ ---- goimports -- --package imports //@import("package") -- --import ( -- "bytes" -- "fmt" --) +-type myStruct struct{} - --func _() { -- fmt.Println("") -- bytes.NewBuffer(nil) +-func (*myStruct) foo(e *json.Decoder) (*big.Int, error) { +- return nil, nil -} - -diff -urN a/gopls/internal/lsp/testdata/imports/add_import.go.in b/gopls/internal/lsp/testdata/imports/add_import.go.in ---- a/gopls/internal/lsp/testdata/imports/add_import.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/add_import.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package imports //@import("package") -- --import ( -- "fmt" --) -- --func _() { -- fmt.Println("") -- bytes.NewBuffer(nil) --} -diff -urN a/gopls/internal/lsp/testdata/imports/good_imports.go.golden b/gopls/internal/lsp/testdata/imports/good_imports.go.golden ---- a/gopls/internal/lsp/testdata/imports/good_imports.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/good_imports.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ ---- goimports -- --package imports //@import("package") +-type MyType struct{} - --import "fmt" +-type MyFunc func(foo int) string - --func _() { --fmt.Println("") --} +-type Alias = int +-type OtherAlias = int +-type StringAlias = string - -diff -urN a/gopls/internal/lsp/testdata/imports/good_imports.go.in b/gopls/internal/lsp/testdata/imports/good_imports.go.in ---- a/gopls/internal/lsp/testdata/imports/good_imports.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/good_imports.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package imports //@import("package") +-func AliasSlice(a []*Alias) (b Alias) { return 0 } +-func AliasMap(a map[*Alias]StringAlias) (b, c map[*Alias]StringAlias) { return nil, nil } +-func OtherAliasMap(a, b map[Alias]OtherAlias) map[Alias]OtherAlias { return nil } - --import "fmt" +-func Qux() { +- Foo("foo", 123) //@signature("(", "Foo(a string, b int) (c bool)", 0) +- Foo("foo", 123) //@signature("123", "Foo(a string, b int) (c bool)", 1) +- Foo("foo", 123) //@signature(",", "Foo(a string, b int) (c bool)", 0) +- Foo("foo", 123) //@signature(" 1", "Foo(a string, b int) (c bool)", 1) +- Foo("foo", 123) //@signature(")", "Foo(a string, b int) (c bool)", 1) - --func _() { --fmt.Println("") --} -diff -urN a/gopls/internal/lsp/testdata/imports/issue35458.go.golden b/gopls/internal/lsp/testdata/imports/issue35458.go.golden ---- a/gopls/internal/lsp/testdata/imports/issue35458.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/issue35458.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,20 +0,0 @@ ---- goimports -- --// package doc --package imports //@import("package") +- Bar(13.37, 0x13) //@signature("13.37", "Bar(float64, ...byte)", 0) +- Bar(13.37, 0x37) //@signature("0x37", "Bar(float64, ...byte)", 1) +- Bar(13.37, 1, 2, 3, 4) //@signature("4", "Bar(float64, ...byte)", 1) - +- fn := func(hi, there string) func(i int) rune { +- return func(int) rune { return 0 } +- } - +- fn("hi", "there") //@signature("hi", "", 0) +- fn("hi", "there") //@signature(",", "fn(hi string, there string) func(i int) rune", 0) +- fn("hi", "there")(1) //@signature("1", "func(i int) rune", 0) - +- fnPtr := &fn +- (*fnPtr)("hi", "there") //@signature(",", "func(hi string, there string) func(i int) rune", 0) - +- var fnIntf interface{} = Foo +- fnIntf.(func(string, int) bool)("hi", 123) //@signature("123", "func(string, int) bool", 1) - +- (&bytes.Buffer{}).Next(2) //@signature("2", "Next(n int) []byte", 0) - --func _() { -- println("Hello, world!") --} +- myFunc := MyFunc(func(n int) string { return "" }) +- myFunc(123) //@signature("123", "myFunc(foo int) string", 0) - +- var ms myStruct +- ms.foo(nil) //@signature("nil", "foo(e *json.Decoder) (*big.Int, error)", 0) - +- _ = make([]int, 1, 2) //@signature("2", "make(t Type, size ...int) Type", 1) - +- Foo(myFunc(123), 456) //@signature("myFunc", "Foo(a string, b int) (c bool)", 0) +- Foo(myFunc(123), 456) //@signature("123", "myFunc(foo int) string", 0) - +- panic("oops!") //@signature(")", "panic(v interface{})", 0) +- println("hello", "world") //@signature(",", "println(args ...Type)", 0) - +- Hello(func() { +- //@signature("//", "", 0) +- }) - +- AliasSlice() //@signature(")", "AliasSlice(a []*Alias) (b Alias)", 0) +- AliasMap() //@signature(")", "AliasMap(a map[*Alias]StringAlias) (b map[*Alias]StringAlias, c map[*Alias]StringAlias)", 0) +- OtherAliasMap() //@signature(")", "OtherAliasMap(a map[Alias]OtherAlias, b map[Alias]OtherAlias) map[Alias]OtherAlias", 0) +-} - +-func Hello(func()) {} +diff -urN a/gopls/internal/lsp/testdata/signature/signature.go.golden b/gopls/internal/lsp/testdata/signature/signature.go.golden +--- a/gopls/internal/lsp/testdata/signature/signature.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/signature/signature.go.golden 1970-01-01 08:00:00 +@@ -1,53 +0,0 @@ +--- AliasMap(a map[*Alias]StringAlias) (b map[*Alias]StringAlias, c map[*Alias]StringAlias)-signature -- +-AliasMap(a map[*Alias]StringAlias) (b map[*Alias]StringAlias, c map[*Alias]StringAlias) - -diff -urN a/gopls/internal/lsp/testdata/imports/issue35458.go.in b/gopls/internal/lsp/testdata/imports/issue35458.go.in ---- a/gopls/internal/lsp/testdata/imports/issue35458.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/issue35458.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,23 +0,0 @@ +--- AliasSlice(a []*Alias) (b Alias)-signature -- +-AliasSlice(a []*Alias) (b Alias) - +--- Bar(float64, ...byte)-signature -- +-Bar(float64, ...byte) - +--- Foo(a string, b int) (c bool)-signature -- +-Foo(a string, b int) (c bool) - +--- Next(n int) []byte-signature -- +-Next(n int) []byte - +-Next returns a slice containing the next n bytes from the buffer, advancing the buffer as if the bytes had been returned by Read. - --// package doc --package imports //@import("package") +--- OtherAliasMap(a map[Alias]OtherAlias, b map[Alias]OtherAlias) map[Alias]OtherAlias-signature -- +-OtherAliasMap(a map[Alias]OtherAlias, b map[Alias]OtherAlias) map[Alias]OtherAlias - +--- fn(hi string, there string) func(i int) rune-signature -- +-fn(hi string, there string) func(i int) rune - +--- foo(e *json.Decoder) (*big.Int, error)-signature -- +-foo(e *json.Decoder) (*big.Int, error) - +--- func(hi string, there string) func(i int) rune-signature -- +-func(hi string, there string) func(i int) rune - +--- func(i int) rune-signature -- +-func(i int) rune - +--- func(string, int) bool-signature -- +-func(string, int) bool - --func _() { -- println("Hello, world!") --} +--- make(t Type, size ...int) Type-signature -- +-make(t Type, size ...int) Type - +-The make built-in function allocates and initializes an object of type slice, map, or chan (only). - +--- myFunc(foo int) string-signature -- +-myFunc(foo int) string - +--- panic(v interface{})-signature -- +-panic(v any) - +-The panic built-in function stops normal execution of the current goroutine. - +--- println(args ...Type)-signature -- +-println(args ...Type) - +-The println built-in function formats its arguments in an implementation-specific way and writes the result to standard error. - -diff -urN a/gopls/internal/lsp/testdata/imports/multiple_blocks.go.golden b/gopls/internal/lsp/testdata/imports/multiple_blocks.go.golden ---- a/gopls/internal/lsp/testdata/imports/multiple_blocks.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/multiple_blocks.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ ---- goimports -- --package imports //@import("package") +diff -urN a/gopls/internal/lsp/testdata/signature/signature2.go.golden b/gopls/internal/lsp/testdata/signature/signature2.go.golden +--- a/gopls/internal/lsp/testdata/signature/signature2.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/signature/signature2.go.golden 1970-01-01 08:00:00 +@@ -1,3 +0,0 @@ +--- Foo(a string, b int) (c bool)-signature -- +-Foo(a string, b int) (c bool) - --import "fmt" +diff -urN a/gopls/internal/lsp/testdata/signature/signature2.go.in b/gopls/internal/lsp/testdata/signature/signature2.go.in +--- a/gopls/internal/lsp/testdata/signature/signature2.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/signature/signature2.go.in 1970-01-01 08:00:00 +@@ -1,5 +0,0 @@ +-package signature - -func _() { -- fmt.Println("") +- Foo(//@signature("//", "Foo(a string, b int) (c bool)", 0) -} +diff -urN a/gopls/internal/lsp/testdata/signature/signature3.go.golden b/gopls/internal/lsp/testdata/signature/signature3.go.golden +--- a/gopls/internal/lsp/testdata/signature/signature3.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/signature/signature3.go.golden 1970-01-01 08:00:00 +@@ -1,3 +0,0 @@ +--- Foo(a string, b int) (c bool)-signature -- +-Foo(a string, b int) (c bool) - -diff -urN a/gopls/internal/lsp/testdata/imports/multiple_blocks.go.in b/gopls/internal/lsp/testdata/imports/multiple_blocks.go.in ---- a/gopls/internal/lsp/testdata/imports/multiple_blocks.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/multiple_blocks.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package imports //@import("package") -- --import "fmt" -- --import "bytes" +diff -urN a/gopls/internal/lsp/testdata/signature/signature3.go.in b/gopls/internal/lsp/testdata/signature/signature3.go.in +--- a/gopls/internal/lsp/testdata/signature/signature3.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/signature/signature3.go.in 1970-01-01 08:00:00 +@@ -1,5 +0,0 @@ +-package signature - -func _() { -- fmt.Println("") +- Foo("hello",//@signature("//", "Foo(a string, b int) (c bool)", 1) -} -diff -urN a/gopls/internal/lsp/testdata/imports/needs_imports.go.golden b/gopls/internal/lsp/testdata/imports/needs_imports.go.golden ---- a/gopls/internal/lsp/testdata/imports/needs_imports.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/needs_imports.go.golden 1970-01-01 00:00:00.000000000 +0000 +\ No newline at end of file +diff -urN a/gopls/internal/lsp/testdata/signature/signature_test.go b/gopls/internal/lsp/testdata/signature/signature_test.go +--- a/gopls/internal/lsp/testdata/signature/signature_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/signature/signature_test.go 1970-01-01 08:00:00 @@ -1,13 +0,0 @@ ---- goimports -- --package imports //@import("package") +-package signature_test - -import ( -- "fmt" -- "log" +- "testing" +- +- sig "golang.org/lsptests/signature" -) - --func goodbye() { -- fmt.Printf("HI") -- log.Printf("byeeeee") +-func TestSignature(t *testing.T) { +- sig.AliasSlice() //@signature(")", "AliasSlice(a []*sig.Alias) (b sig.Alias)", 0) +- sig.AliasMap() //@signature(")", "AliasMap(a map[*sig.Alias]sig.StringAlias) (b map[*sig.Alias]sig.StringAlias, c map[*sig.Alias]sig.StringAlias)", 0) +- sig.OtherAliasMap() //@signature(")", "OtherAliasMap(a map[sig.Alias]sig.OtherAlias, b map[sig.Alias]sig.OtherAlias) map[sig.Alias]sig.OtherAlias", 0) -} +diff -urN a/gopls/internal/lsp/testdata/signature/signature_test.go.golden b/gopls/internal/lsp/testdata/signature/signature_test.go.golden +--- a/gopls/internal/lsp/testdata/signature/signature_test.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/signature/signature_test.go.golden 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +--- AliasMap(a map[*sig.Alias]sig.StringAlias) (b map[*sig.Alias]sig.StringAlias, c map[*sig.Alias]sig.StringAlias)-signature -- +-AliasMap(a map[*sig.Alias]sig.StringAlias) (b map[*sig.Alias]sig.StringAlias, c map[*sig.Alias]sig.StringAlias) - -diff -urN a/gopls/internal/lsp/testdata/imports/needs_imports.go.in b/gopls/internal/lsp/testdata/imports/needs_imports.go.in ---- a/gopls/internal/lsp/testdata/imports/needs_imports.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/needs_imports.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --package imports //@import("package") +--- AliasSlice(a []*sig.Alias) (b sig.Alias)-signature -- +-AliasSlice(a []*sig.Alias) (b sig.Alias) - --func goodbye() { -- fmt.Printf("HI") -- log.Printf("byeeeee") +--- OtherAliasMap(a map[sig.Alias]sig.OtherAlias, b map[sig.Alias]sig.OtherAlias) map[sig.Alias]sig.OtherAlias-signature -- +-OtherAliasMap(a map[sig.Alias]sig.OtherAlias, b map[sig.Alias]sig.OtherAlias) map[sig.Alias]sig.OtherAlias +- +diff -urN a/gopls/internal/lsp/testdata/snippets/func_snippets118.go.in b/gopls/internal/lsp/testdata/snippets/func_snippets118.go.in +--- a/gopls/internal/lsp/testdata/snippets/func_snippets118.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/snippets/func_snippets118.go.in 1970-01-01 08:00:00 +@@ -1,19 +0,0 @@ +-// +build go1.18 +-//go:build go1.18 +- +-package snippets +- +-type SyncMap[K comparable, V any] struct{} +- +-func NewSyncMap[K comparable, V any]() (result *SyncMap[K, V]) { //@item(NewSyncMap, "NewSyncMap", "", "") +- return -} -diff -urN a/gopls/internal/lsp/testdata/imports/remove_import.go.golden b/gopls/internal/lsp/testdata/imports/remove_import.go.golden ---- a/gopls/internal/lsp/testdata/imports/remove_import.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/remove_import.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ ---- goimports -- --package imports //@import("package") - --import ( -- "fmt" --) +-func Identity[P ~int](p P) P { //@item(Identity, "Identity", "", "") +- return p +-} - -func _() { -- fmt.Println("") +- _ = NewSyncM //@snippet(" //", NewSyncMap, "NewSyncMap[${1:}]()", "NewSyncMap[${1:K comparable}, ${2:V any}]()") +- _ = Identi //@snippet(" //", Identity, "Identity[${1:}](${2:})", "Identity[${1:P ~int}](${2:p P})") -} -- -diff -urN a/gopls/internal/lsp/testdata/imports/remove_import.go.in b/gopls/internal/lsp/testdata/imports/remove_import.go.in ---- a/gopls/internal/lsp/testdata/imports/remove_import.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/remove_import.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package imports //@import("package") +diff -urN a/gopls/internal/lsp/testdata/snippets/literal.go b/gopls/internal/lsp/testdata/snippets/literal.go +--- a/gopls/internal/lsp/testdata/snippets/literal.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/snippets/literal.go 1970-01-01 08:00:00 +@@ -1,22 +0,0 @@ +-package snippets - -import ( -- "bytes" -- "fmt" +- "golang.org/lsptests/signature" +- t "golang.org/lsptests/types" -) - --func _() { -- fmt.Println("") +-type structy struct { +- x signature.MyType +-} +- +-func X(_ map[signature.Alias]t.CoolAlias) (map[signature.Alias]t.CoolAlias) { +- return nil -} -diff -urN a/gopls/internal/lsp/testdata/imports/remove_imports.go.golden b/gopls/internal/lsp/testdata/imports/remove_imports.go.golden ---- a/gopls/internal/lsp/testdata/imports/remove_imports.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/remove_imports.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ ---- goimports -- --package imports //@import("package") - -func _() { +- X() //@signature(")", "X(_ map[signature.Alias]t.CoolAlias) map[signature.Alias]t.CoolAlias", 0) +- _ = signature.MyType{} //@item(literalMyType, "signature.MyType{}", "", "var") +- s := structy{ +- x: //@snippet(" //", literalMyType, "signature.MyType{\\}", "signature.MyType{\\}") +- } -} +\ No newline at end of file +diff -urN a/gopls/internal/lsp/testdata/snippets/literal.go.golden b/gopls/internal/lsp/testdata/snippets/literal.go.golden +--- a/gopls/internal/lsp/testdata/snippets/literal.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/snippets/literal.go.golden 1970-01-01 08:00:00 +@@ -1,3 +0,0 @@ +--- X(_ map[signature.Alias]t.CoolAlias) map[signature.Alias]t.CoolAlias-signature -- +-X(_ map[signature.Alias]t.CoolAlias) map[signature.Alias]t.CoolAlias - -diff -urN a/gopls/internal/lsp/testdata/imports/remove_imports.go.in b/gopls/internal/lsp/testdata/imports/remove_imports.go.in ---- a/gopls/internal/lsp/testdata/imports/remove_imports.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/remove_imports.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package imports //@import("package") +diff -urN a/gopls/internal/lsp/testdata/snippets/postfix.go b/gopls/internal/lsp/testdata/snippets/postfix.go +--- a/gopls/internal/lsp/testdata/snippets/postfix.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/snippets/postfix.go 1970-01-01 08:00:00 +@@ -1,43 +0,0 @@ +-package snippets - --import ( -- "bytes" -- "fmt" --) +-// These tests check that postfix completions do and do not show up in +-// certain cases. Tests for the postfix completion contents are under +-// regtest. - -func _() { --} -diff -urN a/gopls/internal/lsp/testdata/imports/two_lines.go.golden b/gopls/internal/lsp/testdata/imports/two_lines.go.golden ---- a/gopls/internal/lsp/testdata/imports/two_lines.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/two_lines.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,4 +0,0 @@ ---- goimports -- --package main --func main() {} //@import("main") +- /* append! */ //@item(postfixAppend, "append!", "append and re-assign slice", "snippet") +- var foo []int +- foo.append //@rank(" //", postfixAppend) - -diff -urN a/gopls/internal/lsp/testdata/imports/two_lines.go.in b/gopls/internal/lsp/testdata/imports/two_lines.go.in ---- a/gopls/internal/lsp/testdata/imports/two_lines.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/imports/two_lines.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,2 +0,0 @@ --package main --func main() {} //@import("main") -diff -urN a/gopls/internal/lsp/testdata/index/index.go b/gopls/internal/lsp/testdata/index/index.go ---- a/gopls/internal/lsp/testdata/index/index.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/index/index.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,25 +0,0 @@ --package index +- []int{}.append //@complete(" //") - --func _() { -- var ( -- aa = "123" //@item(indexAA, "aa", "string", "var") -- ab = 123 //@item(indexAB, "ab", "int", "var") -- ) +- []int{}.last //@complete(" //") - -- var foo [1]int -- foo[a] //@complete("]", indexAB, indexAA) -- foo[:a] //@complete("]", indexAB, indexAA) -- a[:a] //@complete("[", indexAA, indexAB) -- a[a] //@complete("[", indexAA, indexAB) +- /* copy! */ //@item(postfixCopy, "copy!", "duplicate slice", "snippet") - -- var bar map[string]int -- bar[a] //@complete("]", indexAA, indexAB) +- foo.copy //@rank(" //", postfixCopy) - -- type myMap map[string]int -- var baz myMap -- baz[a] //@complete("]", indexAA, indexAB) +- var s struct{ i []int } +- s.i.copy //@rank(" //", postfixCopy) - -- type myInt int -- var mi myInt //@item(indexMyInt, "mi", "myInt", "var") -- foo[m] //@snippet("]", indexMyInt, "mi", "mi") +- var _ []int = s.i.copy //@complete(" //") +- +- var blah func() []int +- blah().append //@complete(" //") -} -diff -urN a/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go b/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go ---- a/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,27 +0,0 @@ --package inlayHint //@inlayHint("package") - --import "fmt" +-func _() { +- /* append! */ //@item(postfixAppend, "append!", "append and re-assign slice", "snippet") +- /* last! */ //@item(postfixLast, "last!", "s[len(s)-1]", "snippet") +- /* print! */ //@item(postfixPrint, "print!", "print to stdout", "snippet") +- /* range! */ //@item(postfixRange, "range!", "range over slice", "snippet") +- /* reverse! */ //@item(postfixReverse, "reverse!", "reverse slice", "snippet") +- /* sort! */ //@item(postfixSort, "sort!", "sort.Slice()", "snippet") +- /* var! */ //@item(postfixVar, "var!", "assign to variable", "snippet") +- /* ifnotnil! */ //@item(postfixIfNotNil, "ifnotnil!", "if expr != nil", "snippet") - --func fieldNames() { -- for _, c := range []struct { -- in, want string -- }{ -- struct{ in, want string }{"Hello, world", "dlrow ,olleH"}, -- {"Hello, 世界", "界世 ,olleH"}, -- {"", ""}, -- } { -- fmt.Println(c.in == c.want) -- } --} +- var foo []int +- foo. //@complete(" //", postfixAppend, postfixCopy, postfixIfNotNil, postfixLast, postfixPrint, postfixRange, postfixReverse, postfixSort, postfixVar) - --func fieldNamesPointers() { -- for _, c := range []*struct { -- in, want string -- }{ -- &struct{ in, want string }{"Hello, world", "dlrow ,olleH"}, -- {"Hello, 世界", "界世 ,olleH"}, -- {"", ""}, -- } { -- fmt.Println(c.in == c.want) -- } +- foo = nil -} -diff -urN a/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go.golden b/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go.golden ---- a/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/inlay_hint/composite_literals.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,29 +0,0 @@ ---- inlayHint -- --package inlayHint //@inlayHint("package") +diff -urN a/gopls/internal/lsp/testdata/snippets/snippets.go.golden b/gopls/internal/lsp/testdata/snippets/snippets.go.golden +--- a/gopls/internal/lsp/testdata/snippets/snippets.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/snippets/snippets.go.golden 1970-01-01 08:00:00 +@@ -1,3 +0,0 @@ +--- baz(at AliasType, b bool)-signature -- +-baz(at AliasType, b bool) - --import "fmt" +diff -urN a/gopls/internal/lsp/testdata/snippets/snippets.go.in b/gopls/internal/lsp/testdata/snippets/snippets.go.in +--- a/gopls/internal/lsp/testdata/snippets/snippets.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/snippets/snippets.go.in 1970-01-01 08:00:00 +@@ -1,64 +0,0 @@ +-package snippets - --func fieldNames() { -- for _< int>, c< struct{in string; want string}> := range []struct { -- in, want string -- }{ -- struct{ in, want string }{"Hello, world", "dlrow ,olleH"}, -- {"Hello, 世界", "界世 ,olleH"}, -- {"", ""}, -- } { -- fmt.Println(c.in == c.want) -- } --} +-// Pre-set this marker, as we don't have a "source" for it in this package. +-/* Error() */ //@item(Error, "Error", "func() string", "method") - --func fieldNamesPointers() { -- for _< int>, c< *struct{in string; want string}> := range []*struct { -- in, want string -- }{ -- &struct{ in, want string }{"Hello, world", "dlrow ,olleH"}, -- <&struct{in string; want string}>{"Hello, 世界", "界世 ,olleH"}, -- <&struct{in string; want string}>{"", ""}, -- } { -- fmt.Println(c.in == c.want) -- } +-type AliasType = int //@item(sigAliasType, "AliasType", "AliasType", "type") +- +-func foo(i int, b bool) {} //@item(snipFoo, "foo", "func(i int, b bool)", "func") +-func bar(fn func()) func() {} //@item(snipBar, "bar", "func(fn func())", "func") +-func baz(at AliasType, b bool) {} //@item(snipBaz, "baz", "func(at AliasType, b bool)", "func") +- +-type Foo struct { +- Bar int //@item(snipFieldBar, "Bar", "int", "field") +- Func func(at AliasType) error //@item(snipFieldFunc, "Func", "func(at AliasType) error", "field") -} - -diff -urN a/gopls/internal/lsp/testdata/inlay_hint/constant_values.go b/gopls/internal/lsp/testdata/inlay_hint/constant_values.go ---- a/gopls/internal/lsp/testdata/inlay_hint/constant_values.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/inlay_hint/constant_values.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,45 +0,0 @@ --package inlayHint //@inlayHint("package") +-func (Foo) Baz() func() {} //@item(snipMethodBaz, "Baz", "func() func()", "method") +-func (Foo) BazBar() func() {} //@item(snipMethodBazBar, "BazBar", "func() func()", "method") +-func (Foo) BazBaz(at AliasType) func() {} //@item(snipMethodBazBaz, "BazBaz", "func(at AliasType) func()", "method") - --const True = true +-func _() { +- f //@snippet(" //", snipFoo, "foo(${1:})", "foo(${1:i int}, ${2:b bool})") - --type Kind int +- bar //@snippet(" //", snipBar, "bar(${1:})", "bar(${1:fn func()})") - --const ( -- KindNone Kind = iota -- KindPrint -- KindPrintf -- KindErrorf --) +- baz //@snippet(" //", snipBaz, "baz(${1:})", "baz(${1:at AliasType}, ${2:b bool})") +- baz() //@signature("(", "baz(at AliasType, b bool)", 0) - --const ( -- u = iota * 4 -- v float64 = iota * 42 -- w = iota * 42 --) +- bar(nil) //@snippet("(", snipBar, "bar", "bar") +- bar(ba) //@snippet(")", snipBar, "bar(${1:})", "bar(${1:fn func()})") +- var f Foo +- bar(f.Ba) //@snippet(")", snipMethodBaz, "Baz()", "Baz()") +- (bar)(nil) //@snippet(")", snipBar, "bar(${1:})", "bar(${1:fn func()})") +- (f.Ba)() //@snippet(")", snipMethodBaz, "Baz()", "Baz()") - --const ( -- a, b = 1, 2 -- c, d -- e, f = 5 * 5, "hello" + "world" -- g, h -- i, j = true, f --) +- Foo{ +- B //@snippet(" //", snipFieldBar, "Bar: ${1:},", "Bar: ${1:int},") +- } - --// No hint --const ( -- Int = 3 -- Float = 3.14 -- Bool = true -- Rune = '3' -- Complex = 2.7i -- String = "Hello, world!" --) +- Foo{ +- F //@snippet(" //", snipFieldFunc, "Func: ${1:},", "Func: ${1:func(at AliasType) error},") +- } - --var ( -- varInt = 3 -- varFloat = 3.14 -- varBool = true -- varRune = '3' + '4' -- varComplex = 2.7i -- varString = "Hello, world!" --) -diff -urN a/gopls/internal/lsp/testdata/inlay_hint/constant_values.go.golden b/gopls/internal/lsp/testdata/inlay_hint/constant_values.go.golden ---- a/gopls/internal/lsp/testdata/inlay_hint/constant_values.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/inlay_hint/constant_values.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,47 +0,0 @@ ---- inlayHint -- --package inlayHint //@inlayHint("package") +- Foo{B} //@snippet("}", snipFieldBar, "Bar: ${1:}", "Bar: ${1:int}") +- Foo{} //@snippet("}", snipFieldBar, "Bar: ${1:}", "Bar: ${1:int}") - --const True = true +- Foo{Foo{}.B} //@snippet("} ", snipFieldBar, "Bar", "Bar") - --type Kind int +- var err error +- err.Error() //@snippet("E", Error, "Error()", "Error()") +- f.Baz() //@snippet("B", snipMethodBaz, "Baz()", "Baz()") - --const ( -- KindNone Kind = iota< = 0> -- KindPrint< = 1> -- KindPrintf< = 2> -- KindErrorf< = 3> --) +- f.Baz() //@snippet("(", snipMethodBazBar, "BazBar", "BazBar") - --const ( -- u = iota * 4< = 0> -- v float64 = iota * 42< = 42> -- w = iota * 42< = 84> --) +- f.Baz() //@snippet("B", snipMethodBazBaz, "BazBaz(${1:})", "BazBaz(${1:at AliasType})") +-} - --const ( -- a, b = 1, 2 -- c, d< = 1, 2> -- e, f = 5 * 5, "hello" + "world"< = 25, "helloworld"> -- g, h< = 25, "helloworld"> -- i, j = true, f< = true, "helloworld"> --) +-func _() { +- type bar struct { +- a int +- b float64 //@item(snipBarB, "b", "float64", "field") +- } +- bar{b} //@snippet("}", snipBarB, "b: ${1:}", "b: ${1:float64}") +-} +diff -urN a/gopls/internal/lsp/testdata/statements/append.go b/gopls/internal/lsp/testdata/statements/append.go +--- a/gopls/internal/lsp/testdata/statements/append.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/statements/append.go 1970-01-01 08:00:00 +@@ -1,42 +0,0 @@ +-package statements - --// No hint --const ( -- Int = 3 -- Float = 3.14 -- Bool = true -- Rune = '3' -- Complex = 2.7i -- String = "Hello, world!" --) +-func _() { +- type mySlice []int - --var ( -- varInt = 3 -- varFloat = 3.14 -- varBool = true -- varRune = '3' + '4' -- varComplex = 2.7i -- varString = "Hello, world!" --) +- var ( +- abc []int //@item(stmtABC, "abc", "[]int", "var") +- abcdef mySlice //@item(stmtABCDEF, "abcdef", "mySlice", "var") +- ) - -diff -urN a/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go b/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go ---- a/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,50 +0,0 @@ --package inlayHint //@inlayHint("package") +- /* abcdef = append(abcdef, ) */ //@item(stmtABCDEFAssignAppend, "abcdef = append(abcdef, )", "", "func") - --import "fmt" +- // don't offer "abc = append(abc, )" because "abc" isn't necessarily +- // better than "abcdef". +- abc //@complete(" //", stmtABC, stmtABCDEF) - --func hello(name string) string { -- return "Hello " + name --} +- abcdef //@complete(" //", stmtABCDEF, stmtABCDEFAssignAppend) - --func helloWorld() string { -- return hello("World") +- /* append(abc, ) */ //@item(stmtABCAppend, "append(abc, )", "", "func") +- +- abc = app //@snippet(" //", stmtABCAppend, "append(abc, ${1:})", "append(abc, ${1:})") -} - --type foo struct{} +-func _() { +- var s struct{ xyz []int } - --func (*foo) bar(baz string, qux int) int { -- if baz != "" { -- return qux + 1 -- } -- return qux --} +- /* xyz = append(s.xyz, ) */ //@item(stmtXYZAppend, "xyz = append(s.xyz, )", "", "func") - --func kase(foo int, bar bool, baz ...string) { -- fmt.Println(foo, bar, baz) --} +- s.x //@snippet(" //", stmtXYZAppend, "xyz = append(s.xyz, ${1:})", "xyz = append(s.xyz, ${1:})") - --func kipp(foo string, bar, baz string) { -- fmt.Println(foo, bar, baz) --} +- /* s.xyz = append(s.xyz, ) */ //@item(stmtDeepXYZAppend, "s.xyz = append(s.xyz, )", "", "func") - --func plex(foo, bar string, baz string) { -- fmt.Println(foo, bar, baz) +- sx //@snippet(" //", stmtDeepXYZAppend, "s.xyz = append(s.xyz, ${1:})", "s.xyz = append(s.xyz, ${1:})") -} - --func tars(foo string, bar, baz string) { -- fmt.Println(foo, bar, baz) +-func _() { +- var foo [][]int +- +- /* append(foo[0], ) */ //@item(stmtFooAppend, "append(foo[0], )", "", "func") +- +- foo[0] = app //@complete(" //"),snippet(" //", stmtFooAppend, "append(foo[0], ${1:})", "append(foo[0], ${1:})") -} +diff -urN a/gopls/internal/lsp/testdata/statements/if_err_check_return.go b/gopls/internal/lsp/testdata/statements/if_err_check_return.go +--- a/gopls/internal/lsp/testdata/statements/if_err_check_return.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/statements/if_err_check_return.go 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ +-package statements - --func foobar() { -- var x foo -- x.bar("", 1) -- kase(0, true, "c", "d", "e") -- kipp("a", "b", "c") -- plex("a", "b", "c") -- tars("a", "b", "c") -- foo, bar, baz := "a", "b", "c" -- kipp(foo, bar, baz) -- plex("a", bar, baz) -- tars(foo+foo, (bar), "c") +-import ( +- "bytes" +- "io" +- "os" +-) +- +-func one() (int, float32, io.Writer, *int, []int, bytes.Buffer, error) { +- /* if err != nil { return err } */ //@item(stmtOneIfErrReturn, "if err != nil { return err }", "", "") +- /* err != nil { return err } */ //@item(stmtOneErrReturn, "err != nil { return err }", "", "") +- +- _, err := os.Open("foo") +- //@snippet("", stmtOneIfErrReturn, "", "if err != nil {\n\treturn 0, 0, nil, nil, nil, bytes.Buffer{\\}, ${1:err}\n\\}") +- +- _, err = os.Open("foo") +- i //@snippet(" //", stmtOneIfErrReturn, "", "if err != nil {\n\treturn 0, 0, nil, nil, nil, bytes.Buffer{\\}, ${1:err}\n\\}") - --} -diff -urN a/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go.golden b/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go.golden ---- a/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/inlay_hint/parameter_names.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,52 +0,0 @@ ---- inlayHint -- --package inlayHint //@inlayHint("package") +- _, err = os.Open("foo") +- if er //@snippet(" //", stmtOneErrReturn, "", "err != nil {\n\treturn 0, 0, nil, nil, nil, bytes.Buffer{\\}, ${1:err}\n\\}") - --import "fmt" +- _, err = os.Open("foo") +- if //@snippet(" //", stmtOneIfErrReturn, "", "if err != nil {\n\treturn 0, 0, nil, nil, nil, bytes.Buffer{\\}, ${1:err}\n\\}") - --func hello(name string) string { -- return "Hello " + name +- _, err = os.Open("foo") +- if //@snippet("//", stmtOneIfErrReturn, "", "if err != nil {\n\treturn 0, 0, nil, nil, nil, bytes.Buffer{\\}, ${1:err}\n\\}") -} +diff -urN a/gopls/internal/lsp/testdata/statements/if_err_check_return_2.go b/gopls/internal/lsp/testdata/statements/if_err_check_return_2.go +--- a/gopls/internal/lsp/testdata/statements/if_err_check_return_2.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/statements/if_err_check_return_2.go 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +-package statements - --func helloWorld() string { -- return hello("World") --} +-import "os" - --type foo struct{} +-func two() error { +- var s struct{ err error } - --func (*foo) bar(baz string, qux int) int { -- if baz != "" { -- return qux + 1 -- } -- return qux --} +- /* if s.err != nil { return s.err } */ //@item(stmtTwoIfErrReturn, "if s.err != nil { return s.err }", "", "") - --func kase(foo int, bar bool, baz ...string) { -- fmt.Println(foo, bar, baz) +- _, s.err = os.Open("foo") +- //@snippet("", stmtTwoIfErrReturn, "", "if s.err != nil {\n\treturn ${1:s.err}\n\\}") -} +diff -urN a/gopls/internal/lsp/testdata/statements/if_err_check_test.go b/gopls/internal/lsp/testdata/statements/if_err_check_test.go +--- a/gopls/internal/lsp/testdata/statements/if_err_check_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/statements/if_err_check_test.go 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ +-package statements - --func kipp(foo string, bar, baz string) { -- fmt.Println(foo, bar, baz) --} +-import ( +- "os" +- "testing" +-) - --func plex(foo, bar string, baz string) { -- fmt.Println(foo, bar, baz) --} +-func TestErr(t *testing.T) { +- /* if err != nil { t.Fatal(err) } */ //@item(stmtOneIfErrTFatal, "if err != nil { t.Fatal(err) }", "", "") - --func tars(foo string, bar, baz string) { -- fmt.Println(foo, bar, baz) +- _, err := os.Open("foo") +- //@snippet("", stmtOneIfErrTFatal, "", "if err != nil {\n\tt.Fatal(err)\n\\}") -} - --func foobar() { -- var x foo -- x.bar("", 1) -- kase(0, true, "c", "d", "e") -- kipp("a", "b", "c") -- plex("a", "b", "c") -- tars("a", "b", "c") -- foo< string>, bar< string>, baz< string> := "a", "b", "c" -- kipp(foo, bar, baz) -- plex("a", bar, baz) -- tars(foo+foo, (bar), "c") +-func BenchmarkErr(b *testing.B) { +- /* if err != nil { b.Fatal(err) } */ //@item(stmtOneIfErrBFatal, "if err != nil { b.Fatal(err) }", "", "") - +- _, err := os.Open("foo") +- //@snippet("", stmtOneIfErrBFatal, "", "if err != nil {\n\tb.Fatal(err)\n\\}") -} +diff -urN a/gopls/internal/lsp/testdata/stub/other/other.go b/gopls/internal/lsp/testdata/stub/other/other.go +--- a/gopls/internal/lsp/testdata/stub/other/other.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/other/other.go 1970-01-01 08:00:00 +@@ -1,10 +0,0 @@ +-package other - -diff -urN a/gopls/internal/lsp/testdata/inlay_hint/type_params.go b/gopls/internal/lsp/testdata/inlay_hint/type_params.go ---- a/gopls/internal/lsp/testdata/inlay_hint/type_params.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/inlay_hint/type_params.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,45 +0,0 @@ --//go:build go1.18 --// +build go1.18 +-import ( +- "bytes" +- renamed_context "context" +-) - --package inlayHint //@inlayHint("package") +-type Interface interface { +- Get(renamed_context.Context) *bytes.Buffer +-} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_add_selector.go b/gopls/internal/lsp/testdata/stub/stub_add_selector.go +--- a/gopls/internal/lsp/testdata/stub/stub_add_selector.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_add_selector.go 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +-package stub - --func main() { -- ints := map[string]int64{ -- "first": 34, -- "second": 12, -- } +-import "io" - -- floats := map[string]float64{ -- "first": 35.98, -- "second": 26.99, -- } +-// This file tests that if an interface +-// method references a type from its own package +-// then our implementation must add the import/package selector +-// in the concrete method if the concrete type is outside of the interface +-// package +-var _ io.ReaderFrom = &readerFrom{} //@suggestedfix("&readerFrom", "quickfix", "") - -- SumIntsOrFloats[string, int64](ints) -- SumIntsOrFloats[string, float64](floats) +-type readerFrom struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_add_selector.go.golden b/gopls/internal/lsp/testdata/stub/stub_add_selector.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_add_selector.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_add_selector.go.golden 1970-01-01 08:00:00 +@@ -1,19 +0,0 @@ +--- suggestedfix_stub_add_selector_10_23 -- +-package stub - -- SumIntsOrFloats(ints) -- SumIntsOrFloats(floats) +-import "io" - -- SumNumbers(ints) -- SumNumbers(floats) --} +-// This file tests that if an interface +-// method references a type from its own package +-// then our implementation must add the import/package selector +-// in the concrete method if the concrete type is outside of the interface +-// package +-var _ io.ReaderFrom = &readerFrom{} //@suggestedfix("&readerFrom", "quickfix", "") - --type Number interface { -- int64 | float64 --} +-type readerFrom struct{} - --func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V { -- var s V -- for _, v := range m { -- s += v -- } -- return s +-// ReadFrom implements io.ReaderFrom. +-func (*readerFrom) ReadFrom(r io.Reader) (n int64, err error) { +- panic("unimplemented") -} - --func SumNumbers[K comparable, V Number](m map[K]V) V { -- var s V -- for _, v := range m { -- s += v -- } -- return s --} -diff -urN a/gopls/internal/lsp/testdata/inlay_hint/type_params.go.golden b/gopls/internal/lsp/testdata/inlay_hint/type_params.go.golden ---- a/gopls/internal/lsp/testdata/inlay_hint/type_params.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/inlay_hint/type_params.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,47 +0,0 @@ ---- inlayHint -- --//go:build go1.18 --// +build go1.18 +diff -urN a/gopls/internal/lsp/testdata/stub/stub_assign.go b/gopls/internal/lsp/testdata/stub/stub_assign.go +--- a/gopls/internal/lsp/testdata/stub/stub_assign.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_assign.go 1970-01-01 08:00:00 +@@ -1,10 +0,0 @@ +-package stub - --package inlayHint //@inlayHint("package") +-import "io" - -func main() { -- ints< map[string]int64> := map[string]int64{ -- "first": 34, -- "second": 12, -- } -- -- floats< map[string]float64> := map[string]float64{ -- "first": 35.98, -- "second": 26.99, -- } +- var br io.ByteWriter +- br = &byteWriter{} //@suggestedfix("&", "quickfix", "") +-} - -- SumIntsOrFloats[string, int64](ints) -- SumIntsOrFloats[string, float64](floats) +-type byteWriter struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_assign.go.golden b/gopls/internal/lsp/testdata/stub/stub_assign.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_assign.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_assign.go.golden 1970-01-01 08:00:00 +@@ -1,17 +0,0 @@ +--- suggestedfix_stub_assign_7_7 -- +-package stub - -- SumIntsOrFloats<[string, int64]>(ints) -- SumIntsOrFloats<[string, float64]>(floats) +-import "io" - -- SumNumbers<[string, int64]>(ints) -- SumNumbers<[string, float64]>(floats) +-func main() { +- var br io.ByteWriter +- br = &byteWriter{} //@suggestedfix("&", "quickfix", "") -} - --type Number interface { -- int64 | float64 --} +-type byteWriter struct{} - --func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V { -- var s V -- for _< K>, v< V> := range m { -- s += v -- } -- return s +-// WriteByte implements io.ByteWriter. +-func (*byteWriter) WriteByte(c byte) error { +- panic("unimplemented") -} - --func SumNumbers[K comparable, V Number](m map[K]V) V { -- var s V -- for _< K>, v< V> := range m { -- s += v -- } -- return s --} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go b/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go +--- a/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package stub - -diff -urN a/gopls/internal/lsp/testdata/inlay_hint/variable_types.go b/gopls/internal/lsp/testdata/inlay_hint/variable_types.go ---- a/gopls/internal/lsp/testdata/inlay_hint/variable_types.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/inlay_hint/variable_types.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,20 +0,0 @@ --package inlayHint //@inlayHint("package") +-import "io" - --func assignTypes() { -- i, j := 0, len([]string{})-1 -- println(i, j) +-func main() { +- var br io.ByteWriter +- var i int +- i, br = 1, &multiByteWriter{} //@suggestedfix("&", "quickfix", "") -} - --func rangeTypes() { -- for k, v := range []string{} { -- println(k, v) -- } --} +-type multiByteWriter struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go.golden b/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go.golden 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +--- suggestedfix_stub_assign_multivars_8_13 -- +-package stub - --func funcLitType() { -- myFunc := func(a string) string { return "" } +-import "io" +- +-func main() { +- var br io.ByteWriter +- var i int +- i, br = 1, &multiByteWriter{} //@suggestedfix("&", "quickfix", "") -} - --func compositeLitType() { -- foo := map[string]interface{}{"": ""} +-type multiByteWriter struct{} +- +-// WriteByte implements io.ByteWriter. +-func (*multiByteWriter) WriteByte(c byte) error { +- panic("unimplemented") -} -diff -urN a/gopls/internal/lsp/testdata/inlay_hint/variable_types.go.golden b/gopls/internal/lsp/testdata/inlay_hint/variable_types.go.golden ---- a/gopls/internal/lsp/testdata/inlay_hint/variable_types.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/inlay_hint/variable_types.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,22 +0,0 @@ ---- inlayHint -- --package inlayHint //@inlayHint("package") - --func assignTypes() { -- i< int>, j< int> := 0, len([]string{})-1 -- println(i, j) +diff -urN a/gopls/internal/lsp/testdata/stub/stub_call_expr.go b/gopls/internal/lsp/testdata/stub/stub_call_expr.go +--- a/gopls/internal/lsp/testdata/stub/stub_call_expr.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_call_expr.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package stub +- +-func main() { +- check(&callExpr{}) //@suggestedfix("&", "quickfix", "") -} - --func rangeTypes() { -- for k< int>, v< string> := range []string{} { -- println(k, v) +-func check(err error) { +- if err != nil { +- panic(err) - } -} - --func funcLitType() { -- myFunc< func(a string) string> := func(a string) string { return "" } +-type callExpr struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_call_expr.go.golden b/gopls/internal/lsp/testdata/stub/stub_call_expr.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_call_expr.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_call_expr.go.golden 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ +--- suggestedfix_stub_call_expr_4_8 -- +-package stub +- +-func main() { +- check(&callExpr{}) //@suggestedfix("&", "quickfix", "") -} - --func compositeLitType() { -- foo< map[string]interface{}> := map[string]interface{}{"": ""} +-func check(err error) { +- if err != nil { +- panic(err) +- } -} - -diff -urN a/gopls/internal/lsp/testdata/interfacerank/interface_rank.go b/gopls/internal/lsp/testdata/interfacerank/interface_rank.go ---- a/gopls/internal/lsp/testdata/interfacerank/interface_rank.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/interfacerank/interface_rank.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,23 +0,0 @@ --package interfacerank +-type callExpr struct{} - --type foo interface { -- foo() +-// Error implements error. +-func (*callExpr) Error() string { +- panic("unimplemented") -} - --type fooImpl int -- --func (*fooImpl) foo() {} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_embedded.go b/gopls/internal/lsp/testdata/stub/stub_embedded.go +--- a/gopls/internal/lsp/testdata/stub/stub_embedded.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_embedded.go 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +-package stub - --func wantsFoo(foo) {} +-import ( +- "io" +- "sort" +-) - --func _() { -- var ( -- aa string //@item(irAA, "aa", "string", "var") -- ab *fooImpl //@item(irAB, "ab", "*fooImpl", "var") -- ) +-var _ embeddedInterface = (*embeddedConcrete)(nil) //@suggestedfix("(", "quickfix", "") - -- wantsFoo(a) //@complete(")", irAB, irAA) +-type embeddedConcrete struct{} - -- var ac fooImpl //@item(irAC, "ac", "fooImpl", "var") -- wantsFoo(&a) //@complete(")", irAC, irAA, irAB) +-type embeddedInterface interface { +- sort.Interface +- io.Reader -} -diff -urN a/gopls/internal/lsp/testdata/issues/issue56505.go b/gopls/internal/lsp/testdata/issues/issue56505.go ---- a/gopls/internal/lsp/testdata/issues/issue56505.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/issues/issue56505.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package issues +diff -urN a/gopls/internal/lsp/testdata/stub/stub_embedded.go.golden b/gopls/internal/lsp/testdata/stub/stub_embedded.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_embedded.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_embedded.go.golden 1970-01-01 08:00:00 +@@ -1,37 +0,0 @@ +--- suggestedfix_stub_embedded_8_27 -- +-package stub - --// Test for golang/go#56505: completion on variables of type *error should not --// panic. --func _() { -- var e *error -- e.x //@complete(" //") --} -diff -urN a/gopls/internal/lsp/testdata/keywords/accidental_keywords.go.in b/gopls/internal/lsp/testdata/keywords/accidental_keywords.go.in ---- a/gopls/internal/lsp/testdata/keywords/accidental_keywords.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/keywords/accidental_keywords.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,31 +0,0 @@ --package keywords +-import ( +- "io" +- "sort" +-) - --// non-matching candidate - shouldn't show up as completion --var apple = "apple" +-var _ embeddedInterface = (*embeddedConcrete)(nil) //@suggestedfix("(", "quickfix", "") - --func _() { -- foo.bar() // insert some extra statements to exercise our AST surgery -- variance := 123 //@item(kwVariance, "variance", "int", "var") -- foo.bar() -- println(var) //@complete(")", kwVariance) --} +-type embeddedConcrete struct{} - --func _() { -- foo.bar() -- var s struct { variance int } //@item(kwVarianceField, "variance", "int", "field") -- foo.bar() -- s.var //@complete(" //", kwVarianceField) +-// Len implements embeddedInterface. +-func (*embeddedConcrete) Len() int { +- panic("unimplemented") -} - --func _() { -- channel := 123 //@item(kwChannel, "channel", "int", "var") -- chan //@complete(" //", kwChannel) -- foo.bar() +-// Less implements embeddedInterface. +-func (*embeddedConcrete) Less(i int, j int) bool { +- panic("unimplemented") -} - --func _() { -- foo.bar() -- var typeName string //@item(kwTypeName, "typeName", "string", "var") -- foo.bar() -- type //@complete(" //", kwTypeName) +-// Read implements embeddedInterface. +-func (*embeddedConcrete) Read(p []byte) (n int, err error) { +- panic("unimplemented") -} -diff -urN a/gopls/internal/lsp/testdata/keywords/empty_select.go b/gopls/internal/lsp/testdata/keywords/empty_select.go ---- a/gopls/internal/lsp/testdata/keywords/empty_select.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/keywords/empty_select.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package keywords - --func _() { -- select { -- c //@complete(" //", case) -- } +-// Swap implements embeddedInterface. +-func (*embeddedConcrete) Swap(i int, j int) { +- panic("unimplemented") -} -diff -urN a/gopls/internal/lsp/testdata/keywords/empty_switch.go b/gopls/internal/lsp/testdata/keywords/empty_switch.go ---- a/gopls/internal/lsp/testdata/keywords/empty_switch.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/keywords/empty_switch.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package keywords -- --func _() { -- switch { -- //@complete("", case, default) -- } - -- switch test.(type) { -- d //@complete(" //", default) -- } +-type embeddedInterface interface { +- sort.Interface +- io.Reader -} -diff -urN a/gopls/internal/lsp/testdata/keywords/keywords.go b/gopls/internal/lsp/testdata/keywords/keywords.go ---- a/gopls/internal/lsp/testdata/keywords/keywords.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/keywords/keywords.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,100 +0,0 @@ --package keywords -- --//@rank("", type),rank("", func),rank("", var),rank("", const),rank("", import) -- --func _() { -- var test int //@rank(" //", int, interface) -- var tChan chan int -- var _ m //@complete(" //", map) -- var _ f //@complete(" //", func) -- var _ c //@complete(" //", chan) -- -- var _ str //@rank(" //", string, struct) -- -- type _ int //@rank(" //", interface, int) -- -- type _ str //@rank(" //", struct, string) -- -- switch test { -- case 1: // TODO: trying to complete case here will break because the parser won't return *ast.Ident -- b //@complete(" //", break) -- case 2: -- f //@complete(" //", fallthrough, for) -- r //@complete(" //", return) -- d //@complete(" //", default, defer) -- c //@complete(" //", case, const) -- } -- -- switch test.(type) { -- case fo: //@complete(":") -- case int: -- b //@complete(" //", break) -- case int32: -- f //@complete(" //", for) -- d //@complete(" //", default, defer) -- r //@complete(" //", return) -- c //@complete(" //", case, const) -- } -- -- select { -- case <-tChan: -- b //@complete(" //", break) -- c //@complete(" //", case, const) -- } - -- for index := 0; index < test; index++ { -- c //@complete(" //", const, continue) -- b //@complete(" //", break) -- } +diff -urN a/gopls/internal/lsp/testdata/stub/stub_err.go b/gopls/internal/lsp/testdata/stub/stub_err.go +--- a/gopls/internal/lsp/testdata/stub/stub_err.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_err.go 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package stub - -- for range []int{} { -- c //@complete(" //", const, continue) -- b //@complete(" //", break) -- } +-func main() { +- var br error = &customErr{} //@suggestedfix("&", "quickfix", "") +-} - -- // Test function level keywords +-type customErr struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_err.go.golden b/gopls/internal/lsp/testdata/stub/stub_err.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_err.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_err.go.golden 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +--- suggestedfix_stub_err_4_17 -- +-package stub - -- //Using 2 characters to test because map output order is random -- sw //@complete(" //", switch) -- se //@complete(" //", select) +-func main() { +- var br error = &customErr{} //@suggestedfix("&", "quickfix", "") +-} - -- f //@complete(" //", for) -- d //@complete(" //", defer) -- g //@rank(" //", go),rank(" //", goto) -- r //@complete(" //", return) -- i //@complete(" //", if) -- e //@complete(" //", else) -- v //@complete(" //", var) -- c //@complete(" //", const) +-type customErr struct{} - -- for i := r //@complete(" //", range) +-// Error implements error. +-func (*customErr) Error() string { +- panic("unimplemented") -} - --/* package */ //@item(package, "package", "", "keyword") --/* import */ //@item(import, "import", "", "keyword") --/* func */ //@item(func, "func", "", "keyword") --/* type */ //@item(type, "type", "", "keyword") --/* var */ //@item(var, "var", "", "keyword") --/* const */ //@item(const, "const", "", "keyword") --/* break */ //@item(break, "break", "", "keyword") --/* default */ //@item(default, "default", "", "keyword") --/* case */ //@item(case, "case", "", "keyword") --/* defer */ //@item(defer, "defer", "", "keyword") --/* go */ //@item(go, "go", "", "keyword") --/* for */ //@item(for, "for", "", "keyword") --/* if */ //@item(if, "if", "", "keyword") --/* else */ //@item(else, "else", "", "keyword") --/* switch */ //@item(switch, "switch", "", "keyword") --/* select */ //@item(select, "select", "", "keyword") --/* fallthrough */ //@item(fallthrough, "fallthrough", "", "keyword") --/* continue */ //@item(continue, "continue", "", "keyword") --/* return */ //@item(return, "return", "", "keyword") --/* var */ //@item(var, "var", "", "keyword") --/* const */ //@item(const, "const", "", "keyword") --/* goto */ //@item(goto, "goto", "", "keyword") --/* struct */ //@item(struct, "struct", "", "keyword") --/* interface */ //@item(interface, "interface", "", "keyword") --/* map */ //@item(map, "map", "", "keyword") --/* func */ //@item(func, "func", "", "keyword") --/* chan */ //@item(chan, "chan", "", "keyword") --/* range */ //@item(range, "range", "", "keyword") -diff -urN a/gopls/internal/lsp/testdata/labels/labels.go b/gopls/internal/lsp/testdata/labels/labels.go ---- a/gopls/internal/lsp/testdata/labels/labels.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/labels/labels.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,49 +0,0 @@ --package labels +diff -urN a/gopls/internal/lsp/testdata/stub/stub_function_return.go b/gopls/internal/lsp/testdata/stub/stub_function_return.go +--- a/gopls/internal/lsp/testdata/stub/stub_function_return.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_function_return.go 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package stub +- +-import ( +- "io" +-) - --func _() { -- goto F //@complete(" //", label1, label5) +-func newCloser() io.Closer { +- return closer{} //@suggestedfix("c", "quickfix", "") +-} - --Foo1: //@item(label1, "Foo1", "label", "const") -- for a, b := range []int{} { -- Foo2: //@item(label2, "Foo2", "label", "const") -- switch { -- case true: -- break F //@complete(" //", label2, label1) +-type closer struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_function_return.go.golden b/gopls/internal/lsp/testdata/stub/stub_function_return.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_function_return.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_function_return.go.golden 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +--- suggestedfix_stub_function_return_8_9 -- +-package stub - -- continue F //@complete(" //", label1) +-import ( +- "io" +-) - -- { -- FooUnjumpable: -- } +-func newCloser() io.Closer { +- return closer{} //@suggestedfix("c", "quickfix", "") +-} - -- goto F //@complete(" //", label1, label2, label4, label5) +-type closer struct{} - -- func() { -- goto F //@complete(" //", label3) +-// Close implements io.Closer. +-func (closer) Close() error { +- panic("unimplemented") +-} - -- break F //@complete(" //") +diff -urN a/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go b/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go +--- a/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +-//go:build go1.18 +-// +build go1.18 - -- continue F //@complete(" //") +-package stub - -- Foo3: //@item(label3, "Foo3", "label", "const") -- }() -- } +-import "io" - -- Foo4: //@item(label4, "Foo4", "label", "const") -- switch interface{}(a).(type) { -- case int: -- break F //@complete(" //", label4, label1) -- } -- } +-// This file tests that that the stub method generator accounts for concrete +-// types that have type parameters defined. +-var _ io.ReaderFrom = &genReader[string, int]{} //@suggestedfix("&genReader", "quickfix", "Implement io.ReaderFrom") - -- break F //@complete(" //") +-type genReader[T, Y any] struct { +- T T +- Y Y +-} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go.golden b/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go.golden 1970-01-01 08:00:00 +@@ -1,22 +0,0 @@ +--- suggestedfix_stub_generic_receiver_10_23 -- +-//go:build go1.18 +-// +build go1.18 - -- continue F //@complete(" //") +-package stub - --Foo5: //@item(label5, "Foo5", "label", "const") -- for { -- break F //@complete(" //", label5) -- } +-import "io" - -- return +-// This file tests that that the stub method generator accounts for concrete +-// types that have type parameters defined. +-var _ io.ReaderFrom = &genReader[string, int]{} //@suggestedfix("&genReader", "quickfix", "Implement io.ReaderFrom") +- +-type genReader[T, Y any] struct { +- T T +- Y Y -} -diff -urN a/gopls/internal/lsp/testdata/links/links.go b/gopls/internal/lsp/testdata/links/links.go ---- a/gopls/internal/lsp/testdata/links/links.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/links/links.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,26 +0,0 @@ --package links - --import ( -- "fmt" //@link(`fmt`,"https://pkg.go.dev/fmt") +-// ReadFrom implements io.ReaderFrom. +-func (*genReader[T, Y]) ReadFrom(r io.Reader) (n int64, err error) { +- panic("unimplemented") +-} - -- "golang.org/lsptests/foo" //@link(`golang.org/lsptests/foo`,`https://pkg.go.dev/golang.org/lsptests/foo`) +diff -urN a/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go b/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go +--- a/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +-package stub - -- _ "database/sql" //@link(`database/sql`, `https://pkg.go.dev/database/sql`) +-import ( +- "compress/zlib" +- . "io" +- _ "io" -) - +-// This file tests that dot-imports and underscore imports +-// are properly ignored and that a new import is added to +-// reference method types +- -var ( -- _ fmt.Formatter -- _ foo.StructFoo -- _ errors.Formatter +- _ Reader +- _ zlib.Resetter = (*ignoredResetter)(nil) //@suggestedfix("(", "quickfix", "") -) - --// Foo function --func Foo() string { -- /*https://example.com/comment */ //@link("https://example.com/comment","https://example.com/comment") +-type ignoredResetter struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go.golden b/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go.golden 1970-01-01 08:00:00 +@@ -1,25 +0,0 @@ +--- suggestedfix_stub_ignored_imports_15_20 -- +-package stub - -- url := "https://example.com/string_literal" //@link("https://example.com/string_literal","https://example.com/string_literal") -- return url +-import ( +- "compress/zlib" +- . "io" +- _ "io" +-) - -- // TODO(golang/go#1234): Link the relevant issue. //@link("golang/go#1234", "https://github.com/golang/go/issues/1234") -- // TODO(microsoft/vscode-go#12): Another issue. //@link("microsoft/vscode-go#12", "https://github.com/microsoft/vscode-go/issues/12") --} -diff -urN a/gopls/internal/lsp/testdata/maps/maps.go.in b/gopls/internal/lsp/testdata/maps/maps.go.in ---- a/gopls/internal/lsp/testdata/maps/maps.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/maps/maps.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ --package maps +-// This file tests that dot-imports and underscore imports +-// are properly ignored and that a new import is added to +-// reference method types - --func _() { -- var aVar int //@item(mapVar, "aVar", "int", "var") +-var ( +- _ Reader +- _ zlib.Resetter = (*ignoredResetter)(nil) //@suggestedfix("(", "quickfix", "") +-) - -- // not comparabale -- type aSlice []int //@item(mapSliceType, "aSlice", "[]int", "type") +-type ignoredResetter struct{} - -- *aSlice //@item(mapSliceTypePtr, "*aSlice", "[]int", "type") +-// Reset implements zlib.Resetter. +-func (*ignoredResetter) Reset(r Reader, dict []byte) error { +- panic("unimplemented") +-} - -- // comparable -- type aStruct struct{} //@item(mapStructType, "aStruct", "struct{...}", "struct") +diff -urN a/gopls/internal/lsp/testdata/stub/stub_issue2606.go b/gopls/internal/lsp/testdata/stub/stub_issue2606.go +--- a/gopls/internal/lsp/testdata/stub/stub_issue2606.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_issue2606.go 1970-01-01 08:00:00 +@@ -1,7 +0,0 @@ +-package stub - -- map[]a{} //@complete("]", mapSliceType, mapStructType),snippet("]", mapSliceType, "*aSlice", "*aSlice") +-type I interface{ error } - -- map[a]a{} //@complete("]", mapSliceType, mapStructType) -- map[a]a{} //@complete("{", mapSliceType, mapStructType) --} -diff -urN a/gopls/internal/lsp/testdata/missingfunction/channels.go b/gopls/internal/lsp/testdata/missingfunction/channels.go ---- a/gopls/internal/lsp/testdata/missingfunction/channels.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/channels.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package missingfunction +-type C int - --func channels(s string) { -- undefinedChannels(c()) //@suggestedfix("undefinedChannels", "quickfix", "") --} +-var _ I = C(0) //@suggestedfix("C", "quickfix", "") +diff -urN a/gopls/internal/lsp/testdata/stub/stub_issue2606.go.golden b/gopls/internal/lsp/testdata/stub/stub_issue2606.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_issue2606.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_issue2606.go.golden 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +--- suggestedfix_stub_issue2606_7_11 -- +-package stub - --func c() (<-chan string, chan string) { -- return make(<-chan string), make(chan string) --} -diff -urN a/gopls/internal/lsp/testdata/missingfunction/channels.go.golden b/gopls/internal/lsp/testdata/missingfunction/channels.go.golden ---- a/gopls/internal/lsp/testdata/missingfunction/channels.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/channels.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,15 +0,0 @@ ---- suggestedfix_channels_4_2 -- --package missingfunction +-type I interface{ error } - --func channels(s string) { -- undefinedChannels(c()) //@suggestedfix("undefinedChannels", "quickfix", "") --} +-type C int - --func undefinedChannels(ch1 <-chan string, ch2 chan string) { +-// Error implements I. +-func (C) Error() string { - panic("unimplemented") -} - --func c() (<-chan string, chan string) { -- return make(<-chan string), make(chan string) --} +-var _ I = C(0) //@suggestedfix("C", "quickfix", "") - -diff -urN a/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go b/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go ---- a/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --package missingfunction +diff -urN a/gopls/internal/lsp/testdata/stub/stub_multi_var.go b/gopls/internal/lsp/testdata/stub/stub_multi_var.go +--- a/gopls/internal/lsp/testdata/stub/stub_multi_var.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_multi_var.go 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package stub - --func consecutiveParams() { -- var s string -- undefinedConsecutiveParams(s, s) //@suggestedfix("undefinedConsecutiveParams", "quickfix", "") --} -diff -urN a/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go.golden b/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go.golden ---- a/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/consecutive_params.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ ---- suggestedfix_consecutive_params_5_2 -- --package missingfunction +-import "io" - --func consecutiveParams() { -- var s string -- undefinedConsecutiveParams(s, s) //@suggestedfix("undefinedConsecutiveParams", "quickfix", "") --} +-// This test ensures that a variable declaration that +-// has multiple values on the same line can still be +-// analyzed correctly to target the interface implementation +-// diagnostic. +-var one, two, three io.Reader = nil, &multiVar{}, nil //@suggestedfix("&", "quickfix", "") - --func undefinedConsecutiveParams(s1, s2 string) { -- panic("unimplemented") --} +-type multiVar struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_multi_var.go.golden b/gopls/internal/lsp/testdata/stub/stub_multi_var.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_multi_var.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_multi_var.go.golden 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +--- suggestedfix_stub_multi_var_9_38 -- +-package stub - -diff -urN a/gopls/internal/lsp/testdata/missingfunction/error_param.go b/gopls/internal/lsp/testdata/missingfunction/error_param.go ---- a/gopls/internal/lsp/testdata/missingfunction/error_param.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/error_param.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --package missingfunction +-import "io" - --func errorParam() { -- var err error -- undefinedErrorParam(err) //@suggestedfix("undefinedErrorParam", "quickfix", "") --} -diff -urN a/gopls/internal/lsp/testdata/missingfunction/error_param.go.golden b/gopls/internal/lsp/testdata/missingfunction/error_param.go.golden ---- a/gopls/internal/lsp/testdata/missingfunction/error_param.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/error_param.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ ---- suggestedfix_error_param_5_2 -- --package missingfunction +-// This test ensures that a variable declaration that +-// has multiple values on the same line can still be +-// analyzed correctly to target the interface implementation +-// diagnostic. +-var one, two, three io.Reader = nil, &multiVar{}, nil //@suggestedfix("&", "quickfix", "") - --func errorParam() { -- var err error -- undefinedErrorParam(err) //@suggestedfix("undefinedErrorParam", "quickfix", "") --} +-type multiVar struct{} - --func undefinedErrorParam(err error) { +-// Read implements io.Reader. +-func (*multiVar) Read(p []byte) (n int, err error) { - panic("unimplemented") -} - -diff -urN a/gopls/internal/lsp/testdata/missingfunction/literals.go b/gopls/internal/lsp/testdata/missingfunction/literals.go ---- a/gopls/internal/lsp/testdata/missingfunction/literals.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/literals.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package missingfunction +diff -urN a/gopls/internal/lsp/testdata/stub/stub_pointer.go b/gopls/internal/lsp/testdata/stub/stub_pointer.go +--- a/gopls/internal/lsp/testdata/stub/stub_pointer.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_pointer.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package stub - --type T struct{} +-import "io" - --func literals() { -- undefinedLiterals("hey compiler", T{}, &T{}) //@suggestedfix("undefinedLiterals", "quickfix", "") +-func getReaderFrom() io.ReaderFrom { +- return &pointerImpl{} //@suggestedfix("&", "quickfix", "") -} -diff -urN a/gopls/internal/lsp/testdata/missingfunction/literals.go.golden b/gopls/internal/lsp/testdata/missingfunction/literals.go.golden ---- a/gopls/internal/lsp/testdata/missingfunction/literals.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/literals.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ ---- suggestedfix_literals_6_2 -- --package missingfunction - --type T struct{} +-type pointerImpl struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_pointer.go.golden b/gopls/internal/lsp/testdata/stub/stub_pointer.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_pointer.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_pointer.go.golden 1970-01-01 08:00:00 +@@ -1,16 +0,0 @@ +--- suggestedfix_stub_pointer_6_9 -- +-package stub - --func literals() { -- undefinedLiterals("hey compiler", T{}, &T{}) //@suggestedfix("undefinedLiterals", "quickfix", "") +-import "io" +- +-func getReaderFrom() io.ReaderFrom { +- return &pointerImpl{} //@suggestedfix("&", "quickfix", "") -} - --func undefinedLiterals(s string, t1 T, t2 *T) { +-type pointerImpl struct{} +- +-// ReadFrom implements io.ReaderFrom. +-func (*pointerImpl) ReadFrom(r io.Reader) (n int64, err error) { - panic("unimplemented") -} - -diff -urN a/gopls/internal/lsp/testdata/missingfunction/operation.go b/gopls/internal/lsp/testdata/missingfunction/operation.go ---- a/gopls/internal/lsp/testdata/missingfunction/operation.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/operation.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package missingfunction +diff -urN a/gopls/internal/lsp/testdata/stub/stub_renamed_import.go b/gopls/internal/lsp/testdata/stub/stub_renamed_import.go +--- a/gopls/internal/lsp/testdata/stub/stub_renamed_import.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_renamed_import.go 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package stub - --import "time" +-import ( +- "compress/zlib" +- myio "io" +-) - --func operation() { -- undefinedOperation(10 * time.Second) //@suggestedfix("undefinedOperation", "quickfix", "") +-var _ zlib.Resetter = &myIO{} //@suggestedfix("&", "quickfix", "") +-var _ myio.Reader +- +-type myIO struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_renamed_import.go.golden b/gopls/internal/lsp/testdata/stub/stub_renamed_import.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_renamed_import.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_renamed_import.go.golden 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +--- suggestedfix_stub_renamed_import_8_23 -- +-package stub +- +-import ( +- "compress/zlib" +- myio "io" +-) +- +-var _ zlib.Resetter = &myIO{} //@suggestedfix("&", "quickfix", "") +-var _ myio.Reader +- +-type myIO struct{} +- +-// Reset implements zlib.Resetter. +-func (*myIO) Reset(r myio.Reader, dict []byte) error { +- panic("unimplemented") -} -diff -urN a/gopls/internal/lsp/testdata/missingfunction/operation.go.golden b/gopls/internal/lsp/testdata/missingfunction/operation.go.golden ---- a/gopls/internal/lsp/testdata/missingfunction/operation.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/operation.go.golden 1970-01-01 00:00:00.000000000 +0000 +- +diff -urN a/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go b/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go +--- a/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go 1970-01-01 08:00:00 @@ -1,13 +0,0 @@ ---- suggestedfix_operation_6_2 -- --package missingfunction +-package stub - --import "time" +-import ( +- "golang.org/lsptests/stub/other" +-) - --func operation() { -- undefinedOperation(10 * time.Second) //@suggestedfix("undefinedOperation", "quickfix", "") --} +-// This file tests that if an interface +-// method references an import from its own package +-// that the concrete type does not yet import, and that import happens +-// to be renamed, then we prefer the renaming of the interface. +-var _ other.Interface = &otherInterfaceImpl{} //@suggestedfix("&otherInterfaceImpl", "quickfix", "") - --func undefinedOperation(duration time.Duration) { +-type otherInterfaceImpl struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go.golden b/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go.golden 1970-01-01 08:00:00 +@@ -1,22 +0,0 @@ +--- suggestedfix_stub_renamed_import_iface_11_25 -- +-package stub +- +-import ( +- "bytes" +- "context" +- "golang.org/lsptests/stub/other" +-) +- +-// This file tests that if an interface +-// method references an import from its own package +-// that the concrete type does not yet import, and that import happens +-// to be renamed, then we prefer the renaming of the interface. +-var _ other.Interface = &otherInterfaceImpl{} //@suggestedfix("&otherInterfaceImpl", "quickfix", "") +- +-type otherInterfaceImpl struct{} +- +-// Get implements other.Interface. +-func (*otherInterfaceImpl) Get(context.Context) *bytes.Buffer { - panic("unimplemented") -} - -diff -urN a/gopls/internal/lsp/testdata/missingfunction/selector.go b/gopls/internal/lsp/testdata/missingfunction/selector.go ---- a/gopls/internal/lsp/testdata/missingfunction/selector.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/selector.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --package missingfunction +diff -urN a/gopls/internal/lsp/testdata/stub/stub_stdlib.go b/gopls/internal/lsp/testdata/stub/stub_stdlib.go +--- a/gopls/internal/lsp/testdata/stub/stub_stdlib.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_stdlib.go 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-package stub - --func selector() { -- m := map[int]bool{} -- undefinedSelector(m[1]) //@suggestedfix("undefinedSelector", "quickfix", "") --} -diff -urN a/gopls/internal/lsp/testdata/missingfunction/selector.go.golden b/gopls/internal/lsp/testdata/missingfunction/selector.go.golden ---- a/gopls/internal/lsp/testdata/missingfunction/selector.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/selector.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ ---- suggestedfix_selector_5_2 -- --package missingfunction +-import ( +- "io" +-) - --func selector() { -- m := map[int]bool{} -- undefinedSelector(m[1]) //@suggestedfix("undefinedSelector", "quickfix", "") --} +-var _ io.Writer = writer{} //@suggestedfix("w", "quickfix", "") - --func undefinedSelector(b bool) { +-type writer struct{} +diff -urN a/gopls/internal/lsp/testdata/stub/stub_stdlib.go.golden b/gopls/internal/lsp/testdata/stub/stub_stdlib.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_stdlib.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_stdlib.go.golden 1970-01-01 08:00:00 +@@ -1,16 +0,0 @@ +--- suggestedfix_stub_stdlib_7_19 -- +-package stub +- +-import ( +- "io" +-) +- +-var _ io.Writer = writer{} //@suggestedfix("w", "quickfix", "") +- +-type writer struct{} +- +-// Write implements io.Writer. +-func (writer) Write(p []byte) (n int, err error) { - panic("unimplemented") -} - -diff -urN a/gopls/internal/lsp/testdata/missingfunction/slice.go b/gopls/internal/lsp/testdata/missingfunction/slice.go ---- a/gopls/internal/lsp/testdata/missingfunction/slice.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/slice.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,5 +0,0 @@ --package missingfunction +diff -urN a/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go b/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go +--- a/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ +-package stub - --func slice() { -- undefinedSlice([]int{1, 2}) //@suggestedfix("undefinedSlice", "quickfix", "") --} -diff -urN a/gopls/internal/lsp/testdata/missingfunction/slice.go.golden b/gopls/internal/lsp/testdata/missingfunction/slice.go.golden ---- a/gopls/internal/lsp/testdata/missingfunction/slice.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/slice.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ ---- suggestedfix_slice_4_2 -- --package missingfunction +-// Regression test for Issue #56825: file corrupted by insertion of +-// methods after TypeSpec in a parenthesized TypeDecl. - --func slice() { -- undefinedSlice([]int{1, 2}) //@suggestedfix("undefinedSlice", "quickfix", "") --} +-import "io" - --func undefinedSlice(i []int) { -- panic("unimplemented") +-func newReadCloser() io.ReadCloser { +- return rdcloser{} //@suggestedfix("rd", "quickfix", "") -} - -diff -urN a/gopls/internal/lsp/testdata/missingfunction/tuple.go b/gopls/internal/lsp/testdata/missingfunction/tuple.go ---- a/gopls/internal/lsp/testdata/missingfunction/tuple.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/tuple.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package missingfunction +-type ( +- A int +- rdcloser struct{} +- B int +-) - --func tuple() { -- undefinedTuple(b()) //@suggestedfix("undefinedTuple", "quickfix", "") +-func _() { +- // Local types can't be stubbed as there's nowhere to put the methods. +- // The suggestedfix assertion can't express this yet. TODO(adonovan): support it. +- type local struct{} +- var _ io.ReadCloser = local{} // want error: `local type "local" cannot be stubbed` -} - --func b() (string, error) { -- return "", nil +-type ( +- C int +-) +diff -urN a/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go.golden b/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go.golden +--- a/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go.golden 1970-01-01 08:00:00 +@@ -1,39 +0,0 @@ +--- suggestedfix_stub_typedecl_group_9_9 -- +-package stub +- +-// Regression test for Issue #56825: file corrupted by insertion of +-// methods after TypeSpec in a parenthesized TypeDecl. +- +-import "io" +- +-func newReadCloser() io.ReadCloser { +- return rdcloser{} //@suggestedfix("rd", "quickfix", "") -} -diff -urN a/gopls/internal/lsp/testdata/missingfunction/tuple.go.golden b/gopls/internal/lsp/testdata/missingfunction/tuple.go.golden ---- a/gopls/internal/lsp/testdata/missingfunction/tuple.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/tuple.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,15 +0,0 @@ ---- suggestedfix_tuple_4_2 -- --package missingfunction - --func tuple() { -- undefinedTuple(b()) //@suggestedfix("undefinedTuple", "quickfix", "") +-type ( +- A int +- rdcloser struct{} +- B int +-) +- +-// Close implements io.ReadCloser. +-func (rdcloser) Close() error { +- panic("unimplemented") -} - --func undefinedTuple(s string, err error) { +-// Read implements io.ReadCloser. +-func (rdcloser) Read(p []byte) (n int, err error) { - panic("unimplemented") -} - --func b() (string, error) { -- return "", nil +-func _() { +- // Local types can't be stubbed as there's nowhere to put the methods. +- // The suggestedfix assertion can't express this yet. TODO(adonovan): support it. +- type local struct{} +- var _ io.ReadCloser = local{} // want error: `local type "local" cannot be stubbed` -} - -diff -urN a/gopls/internal/lsp/testdata/missingfunction/unique_params.go b/gopls/internal/lsp/testdata/missingfunction/unique_params.go ---- a/gopls/internal/lsp/testdata/missingfunction/unique_params.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/unique_params.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package missingfunction +-type ( +- C int +-) - --func uniqueArguments() { -- var s string -- var i int -- undefinedUniqueArguments(s, i, s) //@suggestedfix("undefinedUniqueArguments", "quickfix", "") --} -diff -urN a/gopls/internal/lsp/testdata/missingfunction/unique_params.go.golden b/gopls/internal/lsp/testdata/missingfunction/unique_params.go.golden ---- a/gopls/internal/lsp/testdata/missingfunction/unique_params.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/missingfunction/unique_params.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ ---- suggestedfix_unique_params_6_2 -- --package missingfunction +diff -urN a/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go b/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go +--- a/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go 1970-01-01 08:00:00 +@@ -1,11 +0,0 @@ +-package suggestedfix - --func uniqueArguments() { -- var s string -- var i int -- undefinedUniqueArguments(s, i, s) //@suggestedfix("undefinedUniqueArguments", "quickfix", "") +-import ( +- "log" +-) +- +-func goodbye() { +- s := "hiiiiiii" +- s = s //@suggestedfix("s = s", "quickfix", "") +- log.Print(s) -} +diff -urN a/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go.golden b/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go.golden +--- a/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go.golden 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +--- suggestedfix_has_suggested_fix_9_2 -- +-package suggestedfix - --func undefinedUniqueArguments(s1 string, i int, s2 string) { -- panic("unimplemented") +-import ( +- "log" +-) +- +-func goodbye() { +- s := "hiiiiiii" +- //@suggestedfix("s = s", "quickfix", "") +- log.Print(s) -} - -diff -urN a/gopls/internal/lsp/testdata/multireturn/multi_return.go.in b/gopls/internal/lsp/testdata/multireturn/multi_return.go.in ---- a/gopls/internal/lsp/testdata/multireturn/multi_return.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/multireturn/multi_return.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,48 +0,0 @@ --package multireturn +diff -urN a/gopls/internal/lsp/testdata/summary.txt.golden b/gopls/internal/lsp/testdata/summary.txt.golden +--- a/gopls/internal/lsp/testdata/summary.txt.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/summary.txt.golden 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +--- summary -- +-CallHierarchyCount = 2 +-CompletionsCount = 194 +-CompletionSnippetCount = 74 +-DeepCompletionsCount = 5 +-FuzzyCompletionsCount = 8 +-RankedCompletionsCount = 166 +-CaseSensitiveCompletionsCount = 4 +-SemanticTokenCount = 3 +-SuggestedFixCount = 80 +-MethodExtractionCount = 8 +-InlayHintsCount = 5 +-RenamesCount = 48 +-PrepareRenamesCount = 7 +-SignaturesCount = 32 +-LinksCount = 7 +-SelectionRangesCount = 3 - --func f0() {} //@item(multiF0, "f0", "func()", "func") +diff -urN a/gopls/internal/lsp/testdata/typeassert/type_assert.go b/gopls/internal/lsp/testdata/typeassert/type_assert.go +--- a/gopls/internal/lsp/testdata/typeassert/type_assert.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/typeassert/type_assert.go 1970-01-01 08:00:00 +@@ -1,24 +0,0 @@ +-package typeassert - --func f1(int) int { return 0 } //@item(multiF1, "f1", "func(int) int", "func") +-type abc interface { //@item(abcIntf, "abc", "interface{...}", "interface") +- abc() +-} - --func f2(int, int) (int, int) { return 0, 0 } //@item(multiF2, "f2", "func(int, int) (int, int)", "func") +-type abcImpl struct{} //@item(abcImpl, "abcImpl", "struct{...}", "struct") +-func (abcImpl) abc() - --func f2Str(string, string) (string, string) { return "", "" } //@item(multiF2Str, "f2Str", "func(string, string) (string, string)", "func") +-type abcPtrImpl struct{} //@item(abcPtrImpl, "abcPtrImpl", "struct{...}", "struct") +-func (*abcPtrImpl) abc() - --func f3(int, int, int) (int, int, int) { return 0, 0, 0 } //@item(multiF3, "f3", "func(int, int, int) (int, int, int)", "func") +-type abcNotImpl struct{} //@item(abcNotImpl, "abcNotImpl", "struct{...}", "struct") - -func _() { -- _ := f //@rank(" //", multiF1, multiF2) -- -- _, _ := f //@rank(" //", multiF2, multiF0),rank(" //", multiF1, multiF0) +- var a abc +- switch a.(type) { +- case ab: //@complete(":", abcImpl, abcPtrImpl, abcIntf, abcNotImpl) +- case *ab: //@complete(":", abcImpl, abcPtrImpl, abcIntf, abcNotImpl) +- } - -- _, _ := _, f //@rank(" //", multiF1, multiF2),rank(" //", multiF1, multiF0) +- a.(ab) //@complete(")", abcImpl, abcPtrImpl, abcIntf, abcNotImpl) +- a.(*ab) //@complete(")", abcImpl, abcPtrImpl, abcIntf, abcNotImpl) +-} +diff -urN a/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go b/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go +--- a/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go 1970-01-01 08:00:00 +@@ -1,5 +0,0 @@ +-package typeerrors - -- _, _ := f, abc //@rank(", abc", multiF1, multiF2) +-func x() { return nil } //@suggestedfix("nil", "quickfix", "") - -- f1() //@rank(")", multiF1, multiF0) -- f1(f) //@rank(")", multiF1, multiF2) -- f2(f) //@rank(")", multiF2, multiF3),rank(")", multiF1, multiF3) -- f2(1, f) //@rank(")", multiF1, multiF2),rank(")", multiF1, multiF0) -- f2(1, ) //@rank(")", multiF1, multiF2),rank(")", multiF1, multiF0) -- f2Str() //@rank(")", multiF2Str, multiF2) +-func y() { return nil, "hello" } //@suggestedfix("nil", "quickfix", "") +diff -urN a/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go.golden b/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go.golden +--- a/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go.golden 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go.golden 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +--- suggestedfix_noresultvalues_3_19 -- +-package typeerrors - -- var i int -- i, _ := f //@rank(" //", multiF2, multiF2Str) +-func x() { return } //@suggestedfix("nil", "quickfix", "") - -- var s string -- _, s := f //@rank(" //", multiF2Str, multiF2) +-func y() { return nil, "hello" } //@suggestedfix("nil", "quickfix", "") - -- banana, s = f //@rank(" //", multiF2, multiF3) +--- suggestedfix_noresultvalues_5_19 -- +-package typeerrors - -- var variadic func(int, ...int) -- variadic() //@rank(")", multiF1, multiF0),rank(")", multiF2, multiF0),rank(")", multiF3, multiF0) --} +-func x() { return nil } //@suggestedfix("nil", "quickfix", "") - --func _() { -- var baz func(...interface{}) +-func y() { return } //@suggestedfix("nil", "quickfix", "") - -- var otterNap func() (int, int) //@item(multiTwo, "otterNap", "func() (int, int)", "var") -- var one int //@item(multiOne, "one", "int", "var") +diff -urN a/gopls/internal/lsp/testdata/typemods/type_mods.go b/gopls/internal/lsp/testdata/typemods/type_mods.go +--- a/gopls/internal/lsp/testdata/typemods/type_mods.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/typemods/type_mods.go 1970-01-01 08:00:00 +@@ -1,21 +0,0 @@ +-package typemods - -- baz(on) //@rank(")", multiOne, multiTwo) +-func fooFunc() func() int { //@item(modFooFunc, "fooFunc", "func() func() int", "func") +- return func() int { +- return 0 +- } -} -diff -urN a/gopls/internal/lsp/testdata/nested_complit/nested_complit.go.in b/gopls/internal/lsp/testdata/nested_complit/nested_complit.go.in ---- a/gopls/internal/lsp/testdata/nested_complit/nested_complit.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/nested_complit/nested_complit.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,15 +0,0 @@ --package nested_complit - --type ncFoo struct {} //@item(structNCFoo, "ncFoo", "struct{...}", "struct") -- --type ncBar struct { //@item(structNCBar, "ncBar", "struct{...}", "struct") -- baz []ncFoo +-func fooPtr() *int { //@item(modFooPtr, "fooPtr", "func() *int", "func") +- return nil -} - -func _() { -- []ncFoo{} //@item(litNCFoo, "[]ncFoo{}", "", "var") -- _ := ncBar{ -- // disabled - see issue #54822 -- baz: [] // complete(" //", structNCFoo, structNCBar) -- } +- var _ int = foo //@snippet(" //", modFooFunc, "fooFunc()()", "fooFunc()()"),snippet(" //", modFooPtr, "*fooPtr()", "*fooPtr()") -} -diff -urN a/gopls/internal/lsp/testdata/nodisk/empty b/gopls/internal/lsp/testdata/nodisk/empty ---- a/gopls/internal/lsp/testdata/nodisk/empty 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/nodisk/empty 1970-01-01 00:00:00.000000000 +0000 -@@ -1 +0,0 @@ --an empty file so that this directory exists -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/nodisk/nodisk.overlay.go b/gopls/internal/lsp/testdata/nodisk/nodisk.overlay.go ---- a/gopls/internal/lsp/testdata/nodisk/nodisk.overlay.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/nodisk/nodisk.overlay.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package nodisk -- --import ( -- "golang.org/lsptests/foo" --) - -func _() { -- foo.Foo() //@complete("F", Foo, IntFoo, StructFoo) --} -diff -urN a/gopls/internal/lsp/testdata/noparse/noparse.go.in b/gopls/internal/lsp/testdata/noparse/noparse.go.in ---- a/gopls/internal/lsp/testdata/noparse/noparse.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/noparse/noparse.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ --package noparse -- --// The type error was chosen carefully to exercise a type-error analyzer. --// We use the 'nonewvars' analyzer because the other candidates are tricky: --// --// - The 'unusedvariable' analyzer is disabled by default, so it is not --// consistently enabled across Test{LSP,CommandLine} tests, which --// both process this file. --// - The 'undeclaredname' analyzer depends on the text of the go/types --// "undeclared name" error, which changed in go1.20. --// - The 'noresultvalues' analyzer produces a diagnostic containing newlines, --// which breaks the parser used by TestCommandLine. --// --// This comment is all that remains of my afternoon. -- --func bye(x int) { -- x := 123 //@diag(":=", "nonewvars", "no new variables", "warning") --} +- var m map[int][]chan int //@item(modMapChanPtr, "m", "map[int]chan *int", "var") - --func stuff() { -- +- var _ int = m //@snippet(" //", modMapChanPtr, "<-m[${1:}][${2:}]", "<-m[${1:}][${2:}]") -} +diff -urN a/gopls/internal/lsp/testdata/typeparams/type_params.go b/gopls/internal/lsp/testdata/typeparams/type_params.go +--- a/gopls/internal/lsp/testdata/typeparams/type_params.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/typeparams/type_params.go 1970-01-01 08:00:00 +@@ -1,61 +0,0 @@ +-//go:build go1.18 +-// +build go1.18 - --func .() {} //@diag(".", "syntax", "expected 'IDENT', found '.'", "error") -diff -urN a/gopls/internal/lsp/testdata/noparse_format/noparse_format.go.golden b/gopls/internal/lsp/testdata/noparse_format/noparse_format.go.golden ---- a/gopls/internal/lsp/testdata/noparse_format/noparse_format.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/noparse_format/noparse_format.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,2 +0,0 @@ ---- gofmt -- -- -diff -urN a/gopls/internal/lsp/testdata/noparse_format/noparse_format.go.in b/gopls/internal/lsp/testdata/noparse_format/noparse_format.go.in ---- a/gopls/internal/lsp/testdata/noparse_format/noparse_format.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/noparse_format/noparse_format.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ --// +build go1.11 -- --package noparse_format //@format("package") +-package typeparams - --// The nonewvars expectation asserts that the go/analysis framework ran. --// See comments in badstmt. +-func one[a int | string]() {} +-func two[a int | string, b float64 | int]() {} - --func what() { -- var hi func() -- if { hi() //@diag("{", "syntax", "missing condition in if statement", "error") -- } -- hi := nil //@diag(":=", "nonewvars", "no new variables", "warning") +-func _() { +- one[]() //@rank("]", string, float64) +- two[]() //@rank("]", int, float64) +- two[int, f]() //@rank("]", float64, float32) -} - -diff -urN a/gopls/internal/lsp/testdata/noparse_format/parse_format.go.golden b/gopls/internal/lsp/testdata/noparse_format/parse_format.go.golden ---- a/gopls/internal/lsp/testdata/noparse_format/parse_format.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/noparse_format/parse_format.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ ---- gofmt -- --package noparse_format //@format("package") +-func slices[a []int | []float64]() {} //@item(tpInts, "[]int", "[]int", "type"),item(tpFloats, "[]float64", "[]float64", "type") - -func _() { -- f() +- slices[]() //@rank("]", tpInts),rank("]", tpFloats) -} - -diff -urN a/gopls/internal/lsp/testdata/noparse_format/parse_format.go.in b/gopls/internal/lsp/testdata/noparse_format/parse_format.go.in ---- a/gopls/internal/lsp/testdata/noparse_format/parse_format.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/noparse_format/parse_format.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,5 +0,0 @@ --package noparse_format //@format("package") +-type s[a int | string] struct{} - -func _() { --f() +- s[]{} //@rank("]", int, float64) -} -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/%percent/perc%ent.go b/gopls/internal/lsp/testdata/%percent/perc%ent.go ---- a/gopls/internal/lsp/testdata/%percent/perc%ent.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/%percent/perc%ent.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1 +0,0 @@ --package percent -diff -urN a/gopls/internal/lsp/testdata/printf/printf.go b/gopls/internal/lsp/testdata/printf/printf.go ---- a/gopls/internal/lsp/testdata/printf/printf.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/printf/printf.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,33 +0,0 @@ --package printf -- --import "fmt" - --func myPrintf(string, ...interface{}) {} +-func takesGeneric[a int | string](s[a]) { +- "s[a]{}" //@item(tpInScopeLit, "s[a]{}", "", "var") +- takesGeneric() //@rank(")", tpInScopeLit),snippet(")", tpInScopeLit, "s[a]{\\}", "s[a]{\\}") +-} - -func _() { -- var ( -- aInt int //@item(printfInt, "aInt", "int", "var") -- aFloat float64 //@item(printfFloat, "aFloat", "float64", "var") -- aString string //@item(printfString, "aString", "string", "var") -- aBytes []byte //@item(printfBytes, "aBytes", "[]byte", "var") -- aStringer fmt.Stringer //@item(printfStringer, "aStringer", "fmt.Stringer", "var") -- aError error //@item(printfError, "aError", "error", "var") -- aBool bool //@item(printfBool, "aBool", "bool", "var") -- ) +- s[int]{} //@item(tpInstLit, "s[int]{}", "", "var") +- takesGeneric[int]() //@rank(")", tpInstLit),snippet(")", tpInstLit, "s[int]{\\}", "s[int]{\\}") - -- myPrintf("%d", a) //@rank(")", printfInt, printfFloat) -- myPrintf("%s", a) //@rank(")", printfString, printfInt),rank(")", printfBytes, printfInt),rank(")", printfStringer, printfInt),rank(")", printfError, printfInt) -- myPrintf("%w", a) //@rank(")", printfError, printfInt) -- myPrintf("%x %[1]b", a) //@rank(")", printfInt, printfString) +- "s[...]{}" //@item(tpUninstLit, "s[...]{}", "", "var") +- takesGeneric() //@rank(")", tpUninstLit),snippet(")", tpUninstLit, "s[${1:}]{\\}", "s[${1:a}]{\\}") +-} - -- fmt.Printf("%t", a) //@rank(")", printfBool, printfInt) +-func returnTP[A int | float64](a A) A { //@item(returnTP, "returnTP", "something", "func") +- return a +-} - -- fmt.Fprintf(nil, "%f", a) //@rank(")", printfFloat, printfInt) +-func _() { +- // disabled - see issue #54822 +- var _ int = returnTP // snippet(" //", returnTP, "returnTP[${1:}](${2:})", "returnTP[${1:A int|float64}](${2:a A})") - -- fmt.Sprintf("%[2]q %[1]*.[3]*[4]f", -- a, //@rank(",", printfInt, printfFloat) -- a, //@rank(",", printfString, printfFloat) -- a, //@rank(",", printfInt, printfFloat) -- a, //@rank(",", printfFloat, printfInt) -- ) +- var aa int //@item(tpInt, "aa", "int", "var") +- var ab float64 //@item(tpFloat, "ab", "float64", "var") +- returnTP[int](a) //@rank(")", tpInt, tpFloat) -} -diff -urN a/gopls/internal/lsp/testdata/rank/assign_rank.go.in b/gopls/internal/lsp/testdata/rank/assign_rank.go.in ---- a/gopls/internal/lsp/testdata/rank/assign_rank.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rank/assign_rank.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,19 +0,0 @@ --package rank - --var ( -- apple int = 3 //@item(apple, "apple", "int", "var") -- pear string = "hello" //@item(pear, "pear", "string", "var") --) +-func takesFunc[T any](func(T) T) { +- var _ func(t T) T = f //@snippet(" //", tpLitFunc, "func(t T) T {$0\\}", "func(t T) T {$0\\}") +-} - -func _() { -- orange := 1 //@item(orange, "orange", "int", "var") -- grape := "hello" //@item(grape, "grape", "string", "var") -- orange, grape = 2, "hello" //@complete(" \"", grape, pear, orange, apple) +- _ = "func(...) {}" //@item(tpLitFunc, "func(...) {}", "", "var") +- takesFunc() //@snippet(")", tpLitFunc, "func(${1:}) ${2:} {$0\\}", "func(${1:t} ${2:T}) ${3:T} {$0\\}") +- takesFunc[int]() //@snippet(")", tpLitFunc, "func(i int) int {$0\\}", "func(${1:i} int) int {$0\\}") -} +diff -urN a/gopls/internal/lsp/testdata/types/types.go b/gopls/internal/lsp/testdata/types/types.go +--- a/gopls/internal/lsp/testdata/types/types.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/types/types.go 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +-package types - --func _() { -- var pineapple int //@item(pineapple, "pineapple", "int", "var") -- pineapple = 1 //@complete(" 1", pineapple, apple, pear) +-type CoolAlias = int //@item(CoolAlias, "CoolAlias", "int", "type") - -- y := //@complete(" /", pineapple, apple, pear) +-type X struct { //@item(X_struct, "X", "struct{...}", "struct") +- x int -} -diff -urN a/gopls/internal/lsp/testdata/rank/binexpr_rank.go.in b/gopls/internal/lsp/testdata/rank/binexpr_rank.go.in ---- a/gopls/internal/lsp/testdata/rank/binexpr_rank.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rank/binexpr_rank.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package rank - --func _() { -- _ = 5 + ; //@complete(" ;", apple, pear) -- y := + 5; //@complete(" +", apple, pear) +-type Y struct { //@item(Y_struct, "Y", "struct{...}", "struct") +- y int +-} - -- if 6 == {} //@complete(" {", apple, pear) +-type Bob interface { //@item(Bob_interface, "Bob", "interface{...}", "interface") +- Bobby() -} -diff -urN a/gopls/internal/lsp/testdata/rank/boolexpr_rank.go b/gopls/internal/lsp/testdata/rank/boolexpr_rank.go ---- a/gopls/internal/lsp/testdata/rank/boolexpr_rank.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rank/boolexpr_rank.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package rank - --func _() { -- someRandomBoolFunc := func() bool { //@item(boolExprFunc, "someRandomBoolFunc", "func() bool", "var") -- return true -- } +-func (*X) Bobby() {} +-func (*Y) Bobby() {} +diff -urN a/gopls/internal/lsp/testdata/unresolved/unresolved.go.in b/gopls/internal/lsp/testdata/unresolved/unresolved.go.in +--- a/gopls/internal/lsp/testdata/unresolved/unresolved.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/unresolved/unresolved.go.in 1970-01-01 08:00:00 +@@ -1,6 +0,0 @@ +-package unresolved - -- var foo, bar int //@item(boolExprBar, "bar", "int", "var") -- if foo == 123 && b { //@rank(" {", boolExprBar, boolExprFunc) -- } +-func foo(interface{}) { +- // don't crash on fake "resolved" type +- foo(func(i, j f //@complete(" //") -} -diff -urN a/gopls/internal/lsp/testdata/rank/convert_rank.go.in b/gopls/internal/lsp/testdata/rank/convert_rank.go.in ---- a/gopls/internal/lsp/testdata/rank/convert_rank.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rank/convert_rank.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,54 +0,0 @@ --package rank -- --import "time" +diff -urN a/gopls/internal/lsp/testdata/unsafe/unsafe.go b/gopls/internal/lsp/testdata/unsafe/unsafe.go +--- a/gopls/internal/lsp/testdata/unsafe/unsafe.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/unsafe/unsafe.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-package unsafe +- +-import ( +- "unsafe" +-) +- +-// Pre-set this marker, as we don't have a "source" for it in this package. +-/* unsafe.Sizeof */ //@item(Sizeof, "Sizeof", "invalid type", "text") +- +-func _() { +- x := struct{}{} +- _ = unsafe.Sizeof(x) //@complete("z", Sizeof) +-} +diff -urN a/gopls/internal/lsp/testdata/variadic/variadic.go.in b/gopls/internal/lsp/testdata/variadic/variadic.go.in +--- a/gopls/internal/lsp/testdata/variadic/variadic.go.in 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/variadic/variadic.go.in 1970-01-01 08:00:00 +@@ -1,38 +0,0 @@ +-package variadic - --func _() { -- type strList []string -- wantsStrList := func(strList) {} +-func foo(i int, strs ...string) {} - -- var ( -- convA string //@item(convertA, "convA", "string", "var") -- convB []string //@item(convertB, "convB", "[]string", "var") -- ) -- wantsStrList(strList(conv)) //@complete("))", convertB, convertA) +-func bar() []string { //@item(vFunc, "bar", "func() []string", "func") +- return nil -} - -func _() { -- type myInt int -- -- const ( -- convC = "hi" //@item(convertC, "convC", "string", "const") -- convD = 123 //@item(convertD, "convD", "int", "const") -- convE int = 123 //@item(convertE, "convE", "int", "const") -- convF string = "there" //@item(convertF, "convF", "string", "const") -- convG myInt = 123 //@item(convertG, "convG", "myInt", "const") +- var ( +- i int //@item(vInt, "i", "int", "var") +- s string //@item(vStr, "s", "string", "var") +- ss []string //@item(vStrSlice, "ss", "[]string", "var") +- v interface{} //@item(vIntf, "v", "interface{}", "var") - ) - -- var foo int -- foo = conv //@rank(" //", convertE, convertD) -- -- var mi myInt -- mi = conv //@rank(" //", convertG, convertD, convertE) -- mi + conv //@rank(" //", convertG, convertD, convertE) -- -- 1 + conv //@rank(" //", convertD, convertC),rank(" //", convertE, convertC),rank(" //", convertG, convertC) -- -- type myString string -- var ms myString -- ms = conv //@rank(" //", convertC, convertF) -- -- type myUint uint32 -- var mu myUint -- mu = conv //@rank(" //", convertD, convertE) -- -- // don't downrank constants when assigning to interface{} -- var _ interface{} = c //@rank(" //", convertD, complex) -- -- var _ time.Duration = conv //@rank(" //", convertD, convertE),snippet(" //", convertE, "time.Duration(convE)", "time.Duration(convE)") +- foo() //@rank(")", vInt, vStr),rank(")", vInt, vStrSlice) +- foo(123, ) //@rank(")", vStr, vInt),rank(")", vStrSlice, vInt) +- foo(123, "", ) //@rank(")", vStr, vInt),rank(")", vStr, vStrSlice) +- foo(123, s, "") //@rank(", \"", vStr, vStrSlice) - -- var convP myInt //@item(convertP, "convP", "myInt", "var") -- var _ *int = conv //@snippet(" //", convertP, "(*int)(&convP)", "(*int)(&convP)") +- // snippet will add the "..." for you +- foo(123, ) //@snippet(")", vStrSlice, "ss...", "ss..."),snippet(")", vFunc, "bar()...", "bar()..."),snippet(")", vStr, "s", "s") - -- var ff float64 //@item(convertFloat, "ff", "float64", "var") -- f == convD //@snippet(" =", convertFloat, "ff", "ff") +- // don't add "..." for interface{} +- foo(123, ) //@snippet(")", vIntf, "v", "v") -} -diff -urN a/gopls/internal/lsp/testdata/rank/struct/struct_rank.go b/gopls/internal/lsp/testdata/rank/struct/struct_rank.go ---- a/gopls/internal/lsp/testdata/rank/struct/struct_rank.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rank/struct/struct_rank.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package struct_rank - --type foo struct { -- c int //@item(c_rank, "c", "int", "field") -- b int //@item(b_rank, "b", "int", "field") -- a int //@item(a_rank, "a", "int", "field") --} +-func qux(...func()) {} +-func f() {} //@item(vVarArg, "f", "func()", "func") - --func f() { -- foo := foo{} //@rank("}", c_rank, b_rank, a_rank) +-func _() { +- qux(f) //@snippet(")", vVarArg, "f", "f") -} -diff -urN a/gopls/internal/lsp/testdata/rank/switch_rank.go.in b/gopls/internal/lsp/testdata/rank/switch_rank.go.in ---- a/gopls/internal/lsp/testdata/rank/switch_rank.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rank/switch_rank.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,29 +0,0 @@ --package rank -- --import "time" - -func _() { -- switch pear { -- case _: //@rank("_", pear, apple) -- } -- -- time.Monday //@item(timeMonday, "time.Monday", "time.Weekday", "const"),item(monday ,"Monday", "time.Weekday", "const") -- time.Friday //@item(timeFriday, "time.Friday", "time.Weekday", "const"),item(friday ,"Friday", "time.Weekday", "const") +- foo(0, []string{}...) //@complete(")") +-} +diff -urN a/gopls/internal/lsp/testdata/variadic/variadic_intf.go b/gopls/internal/lsp/testdata/variadic/variadic_intf.go +--- a/gopls/internal/lsp/testdata/variadic/variadic_intf.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/testdata/variadic/variadic_intf.go 1970-01-01 08:00:00 +@@ -1,21 +0,0 @@ +-package variadic - -- now := time.Now() -- now.Weekday //@item(nowWeekday, "now.Weekday", "func() time.Weekday", "method") +-type baz interface { +- baz() +-} - -- then := time.Now() -- then.Weekday //@item(thenWeekday, "then.Weekday", "func() time.Weekday", "method") +-func wantsBaz(...baz) {} - -- switch time.Weekday(0) { -- case time.Monday, time.Tuesday: -- case time.Wednesday, time.Thursday: -- case time.Saturday, time.Sunday: -- case t: //@rank(":", timeFriday, timeMonday) -- case time.: //@rank(":", friday, monday) +-type bazImpl int - -- case now.Weekday(): -- case week: //@rank(":", thenWeekday, nowWeekday) -- } --} -diff -urN a/gopls/internal/lsp/testdata/rank/type_assert_rank.go.in b/gopls/internal/lsp/testdata/rank/type_assert_rank.go.in ---- a/gopls/internal/lsp/testdata/rank/type_assert_rank.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rank/type_assert_rank.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package rank +-func (bazImpl) baz() {} - -func _() { -- type flower int //@item(flower, "flower", "int", "type") -- var fig string //@item(fig, "fig", "string", "var") +- var ( +- impls []bazImpl //@item(vImplSlice, "impls", "[]bazImpl", "var") +- impl bazImpl //@item(vImpl, "impl", "bazImpl", "var") +- bazes []baz //@item(vIntfSlice, "bazes", "[]baz", "var") +- ) - -- _ = interface{}(nil).(f) //@complete(") //", flower) +- wantsBaz() //@rank(")", vImpl, vImplSlice),rank(")", vIntfSlice, vImplSlice) -} -diff -urN a/gopls/internal/lsp/testdata/rank/type_switch_rank.go.in b/gopls/internal/lsp/testdata/rank/type_switch_rank.go.in ---- a/gopls/internal/lsp/testdata/rank/type_switch_rank.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rank/type_switch_rank.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,31 +0,0 @@ --package rank +diff -urN a/gopls/internal/lsp/tests/README.md b/gopls/internal/lsp/tests/README.md +--- a/gopls/internal/lsp/tests/README.md 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/tests/README.md 1970-01-01 08:00:00 +@@ -1,66 +0,0 @@ +-# Testing - --import ( -- "fmt" -- "go/ast" --) +-LSP has "marker tests" defined in `internal/lsp/testdata`, as well as +-traditional tests. - --func _() { -- type basket int //@item(basket, "basket", "int", "type") -- var banana string //@item(banana, "banana", "string", "var") +-## Marker tests - -- switch interface{}(pear).(type) { -- case b: //@complete(":", basket) -- b //@complete(" //", banana, basket) -- } +-Marker tests have a standard input file, like +-`internal/lsp/testdata/foo/bar.go`, and some may have a corresponding golden +-file, like `internal/lsp/testdata/foo/bar.go.golden`. The former is the "input" +-and the latter is the expected output. - -- Ident //@item(astIdent, "Ident", "struct{...}", "struct") -- IfStmt //@item(astIfStmt, "IfStmt", "struct{...}", "struct") +-Each input file contains annotations like +-`//@suggestedfix("}", "refactor.rewrite", "Fill anonymous struct")`. These annotations are interpreted by +-test runners to perform certain actions. The expected output after those actions +-is encoded in the golden file. - -- switch ast.Node(nil).(type) { -- case *ast.Ident: -- case *ast.I: //@rank(":", astIfStmt, astIdent) -- } +-When tests are run, each annotation results in a new subtest, which is encoded +-in the golden file with a heading like, - -- Stringer //@item(fmtStringer, "Stringer", "interface{...}", "interface") -- GoStringer //@item(fmtGoStringer, "GoStringer", "interface{...}", "interface") +-```bash +--- suggestedfix_bar_11_21 -- +-// expected contents go here +--- suggestedfix_bar_13_20 -- +-// expected contents go here +-``` - -- switch interface{}(nil).(type) { -- case fmt.Stringer: //@rank(":", fmtStringer, fmtGoStringer) -- } --} -diff -urN a/gopls/internal/lsp/testdata/references/another/another.go b/gopls/internal/lsp/testdata/references/another/another.go ---- a/gopls/internal/lsp/testdata/references/another/another.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/references/another/another.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --// Package another has another type. --package another +-The format of these headings vary: they are defined by the +-[`Golden`](https://pkg.go.dev/golang.org/x/tools/gopls/internal/lsp/tests#Data.Golden) +-function for each annotation. In the case above, the format is: annotation +-name, file name, annotation line location, annotation character location. - --import ( -- other "golang.org/lsptests/references/other" --) +-So, if `internal/lsp/testdata/foo/bar.go` has three `suggestedfix` annotations, +-the golden file should have three headers with `suggestedfix_bar_xx_yy` +-headings. - --func _() { -- xes := other.GetXes() -- for _, x := range xes { //@mark(defX, "x") -- _ = x.Y //@mark(useX, "x"),mark(anotherXY, "Y"),refs("Y", typeXY, anotherXY, GetXesY),refs(".", defX, useX),refs("x", defX, useX) -- } --} -diff -urN a/gopls/internal/lsp/testdata/references/interfaces/interfaces.go b/gopls/internal/lsp/testdata/references/interfaces/interfaces.go ---- a/gopls/internal/lsp/testdata/references/interfaces/interfaces.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/references/interfaces/interfaces.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,34 +0,0 @@ --package interfaces +-To see a list of all available annotations, see the exported "expectations" in +-[tests.go](https://github.com/golang/tools/blob/299f270db45902e93469b1152fafed034bb3f033/internal/lsp/tests/tests.go#L418-L447). - --type first interface { -- common() //@mark(firCommon, "common"),refs("common", firCommon, xCommon, zCommon) -- firstMethod() //@mark(firMethod, "firstMethod"),refs("firstMethod", firMethod, xfMethod, zfMethod) --} +-To run marker tests, - --type second interface { -- common() //@mark(secCommon, "common"),refs("common", secCommon, yCommon, zCommon) -- secondMethod() //@mark(secMethod, "secondMethod"),refs("secondMethod", secMethod, ysMethod, zsMethod) --} +-```bash +-cd /path/to/tools - --type s struct {} +-# The marker tests are located in "internal/lsp", "internal/lsp/cmd, and +-# "internal/lsp/source". +-go test ./internal/lsp/... +-``` - --func (*s) common() {} //@mark(sCommon, "common"),refs("common", sCommon, xCommon, yCommon, zCommon) +-There are quite a lot of marker tests, so to run one individually, pass the test +-path and heading into a -run argument: - --func (*s) firstMethod() {} //@mark(sfMethod, "firstMethod"),refs("firstMethod", sfMethod, xfMethod, zfMethod) +-```bash +-cd /path/to/tools +-go test ./internal/lsp/... -v -run TestLSP/Modules/SuggestedFix/bar_11_21 +-``` - --func (*s) secondMethod() {} //@mark(ssMethod, "secondMethod"),refs("secondMethod", ssMethod, ysMethod, zsMethod) +-## Resetting marker tests - --func main() { -- var x first = &s{} -- var y second = &s{} +-Sometimes, a change is made to lsp that requires a change to multiple golden +-files. When this happens, you can run, - -- x.common() //@mark(xCommon, "common"),refs("common", firCommon, xCommon, zCommon) -- x.firstMethod() //@mark(xfMethod, "firstMethod"),refs("firstMethod", firMethod, xfMethod, zfMethod) -- y.common() //@mark(yCommon, "common"),refs("common", secCommon, yCommon, zCommon) -- y.secondMethod() //@mark(ysMethod, "secondMethod"),refs("secondMethod", secMethod, ysMethod, zsMethod) +-```bash +-cd /path/to/tools +-./internal/lsp/reset_golden.sh +-``` +diff -urN a/gopls/internal/lsp/tests/compare/text.go b/gopls/internal/lsp/tests/compare/text.go +--- a/gopls/internal/lsp/tests/compare/text.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/tests/compare/text.go 1970-01-01 08:00:00 +@@ -1,49 +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. - -- var z *s = &s{} -- z.firstMethod() //@mark(zfMethod, "firstMethod"),refs("firstMethod", sfMethod, xfMethod, zfMethod) -- z.secondMethod() //@mark(zsMethod, "secondMethod"),refs("secondMethod", ssMethod, ysMethod, zsMethod) -- z.common() //@mark(zCommon, "common"),refs("common", sCommon, xCommon, yCommon, zCommon) --} -diff -urN a/gopls/internal/lsp/testdata/references/other/other.go b/gopls/internal/lsp/testdata/references/other/other.go ---- a/gopls/internal/lsp/testdata/references/other/other.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/references/other/other.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,19 +0,0 @@ --package other +-package compare - -import ( -- references "golang.org/lsptests/references" --) +- "bytes" - --func GetXes() []references.X { -- return []references.X{ -- { -- Y: 1, //@mark(GetXesY, "Y"),refs("Y", typeXY, GetXesY, anotherXY) -- }, -- } --} +- "golang.org/x/tools/internal/diff" +-) - --func _() { -- references.Q = "hello" //@mark(assignExpQ, "Q") -- bob := func(_ string) {} -- bob(references.Q) //@mark(bobExpQ, "Q") +-// Text returns a formatted unified diff of the edits to go from want to +-// got, returning "" if and only if want == got. +-// +-// This function is intended for use in testing, and panics if any error occurs +-// while computing the diff. It is not sufficiently tested for production use. +-func Text(want, got string) string { +- return NamedText("want", "got", want, got) -} -diff -urN a/gopls/internal/lsp/testdata/references/refs.go b/gopls/internal/lsp/testdata/references/refs.go ---- a/gopls/internal/lsp/testdata/references/refs.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/references/refs.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,53 +0,0 @@ --// Package refs is a package used to test find references. --package refs - --import "os" //@mark(osDecl, `"os"`),refs("os", osDecl, osUse) -- --type i int //@mark(typeI, "i"),refs("i", typeI, argI, returnI, embeddedI) +-// NamedText is like text, but allows passing custom names of the 'want' and +-// 'got' content. +-func NamedText(wantName, gotName, want, got string) string { +- if want == got { +- return "" +- } - --type X struct { -- Y int //@mark(typeXY, "Y") --} +- // Add newlines to avoid verbose newline messages ("No newline at end of file"). +- unified := diff.Unified(wantName, gotName, want+"\n", got+"\n") - --func _(_ i) []bool { //@mark(argI, "i") -- return nil --} +- // Defensively assert that we get an actual diff, so that we guarantee the +- // invariant that we return "" if and only if want == got. +- // +- // This is probably unnecessary, but convenient. +- if unified == "" { +- panic("empty diff for non-identical input") +- } - --func _(_ []byte) i { //@mark(returnI, "i") -- return 0 +- return unified -} - --var q string //@mark(declQ, "q"),refs("q", declQ, assignQ, bobQ) -- --var Q string //@mark(declExpQ, "Q"),refs("Q", declExpQ, assignExpQ, bobExpQ) -- --func _() { -- q = "hello" //@mark(assignQ, "q") -- bob := func(_ string) {} -- bob(q) //@mark(bobQ, "q") +-// Bytes is like Text but using byte slices. +-func Bytes(want, got []byte) string { +- if bytes.Equal(want, got) { +- return "" // common case +- } +- return Text(string(want), string(got)) -} +diff -urN a/gopls/internal/lsp/tests/compare/text_test.go b/gopls/internal/lsp/tests/compare/text_test.go +--- a/gopls/internal/lsp/tests/compare/text_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/tests/compare/text_test.go 1970-01-01 08:00:00 +@@ -1,28 +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. - --type e struct { -- i //@mark(embeddedI, "i"),refs("i", embeddedI, embeddedIUse) --} +-package compare_test - --func _() { -- _ = e{}.i //@mark(embeddedIUse, "i") --} +-import ( +- "testing" - --const ( -- foo = iota //@refs("iota") +- "golang.org/x/tools/gopls/internal/lsp/tests/compare" -) - --func _(x interface{}) { -- // We use the _ prefix because the markers inhabit a single -- // namespace and yDecl is already used in ../highlights/highlights.go. -- switch _y := x.(type) { //@mark(_yDecl, "_y"),refs("_y", _yDecl, _yInt, _yDefault) -- case int: -- println(_y) //@mark(_yInt, "_y"),refs("_y", _yDecl, _yInt, _yDefault) -- default: -- println(_y) //@mark(_yDefault, "_y") +-func TestText(t *testing.T) { +- tests := []struct { +- got, want, wantDiff string +- }{ +- {"", "", ""}, +- {"equal", "equal", ""}, +- {"a", "b", "--- want\n+++ got\n@@ -1 +1 @@\n-b\n+a\n"}, +- {"a\nd\nc\n", "a\nb\nc\n", "--- want\n+++ got\n@@ -1,4 +1,4 @@\n a\n-b\n+d\n c\n \n"}, - } - -- os.Getwd() //@mark(osUse, "os") +- for _, test := range tests { +- if gotDiff := compare.Text(test.want, test.got); gotDiff != test.wantDiff { +- t.Errorf("compare.Text(%q, %q) =\n%q, want\n%q", test.want, test.got, gotDiff, test.wantDiff) +- } +- } -} -diff -urN a/gopls/internal/lsp/testdata/references/refs_test.go b/gopls/internal/lsp/testdata/references/refs_test.go ---- a/gopls/internal/lsp/testdata/references/refs_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/references/refs_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package references -- --import ( -- "testing" --) +diff -urN a/gopls/internal/lsp/tests/markdown_go118.go b/gopls/internal/lsp/tests/markdown_go118.go +--- a/gopls/internal/lsp/tests/markdown_go118.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/tests/markdown_go118.go 1970-01-01 08:00:00 +@@ -1,69 +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. - --// This test exists to bring the test package into existence. +-//go:build !go1.19 +-// +build !go1.19 - --func TestReferences(t *testing.T) { --} -diff -urN a/gopls/internal/lsp/testdata/rename/a/random.go.golden b/gopls/internal/lsp/testdata/rename/a/random.go.golden ---- a/gopls/internal/lsp/testdata/rename/a/random.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/a/random.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,616 +0,0 @@ ---- GetSum-rename -- --package a +-package tests - -import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") +- "regexp" +- "strings" +- +- "golang.org/x/tools/gopls/internal/lsp/tests/compare" -) - --func Random() int { -- y := 6 + 7 -- return y +-// DiffMarkdown compares two markdown strings produced by parsing go doc +-// comments. +-// +-// For go1.19 and later, markdown conversion is done using go/doc/comment. +-// Compared to the newer version, the older version has extra escapes, and +-// treats code blocks slightly differently. +-func DiffMarkdown(want, got string) string { +- want = normalizeMarkdown(want) +- got = normalizeMarkdown(got) +- return compare.Text(want, got) -} - --func Random2(y int) int { //@rename("y", "z") -- return y --} +-// normalizeMarkdown normalizes whitespace and escaping of the input string, to +-// eliminate differences between the Go 1.18 and Go 1.19 generated markdown for +-// doc comments. Note that it does not normalize to either the 1.18 or 1.19 +-// formatting: it simplifies both so that they may be compared. +-// +-// This function may need to be adjusted as we encounter more differences in +-// the generated text. +-// +-// TODO(rfindley): this function doesn't correctly handle the case of +-// multi-line docstrings. +-func normalizeMarkdown(input string) string { +- input = strings.TrimSpace(input) - --type Pos struct { -- x, y int --} +- // For simplicity, eliminate blank lines. +- input = regexp.MustCompile("\n+").ReplaceAllString(input, "\n") - --func (p *Pos) GetSum() int { -- return p.x + p.y //@rename("x", "myX") --} +- // Replace common escaped characters with their unescaped version. +- // +- // This list may not be exhaustive: it was just sufficient to make tests +- // pass. +- input = strings.NewReplacer( +- `\\`, ``, +- `\@`, `@`, +- `\(`, `(`, +- `\)`, `)`, +- `\{`, `{`, +- `\}`, `}`, +- `\"`, `"`, +- `\.`, `.`, +- `\-`, `-`, +- `\'`, `'`, +- `\+`, `+`, +- `\~`, `~`, +- `\=`, `=`, +- `\:`, `:`, +- `\?`, `?`, +- `\n\n\n`, `\n\n`, // Note that these are *escaped* newlines. +- ).Replace(input) - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.GetSum() //@rename("Sum", "GetSum") +- return input -} +diff -urN a/gopls/internal/lsp/tests/markdown_go119.go b/gopls/internal/lsp/tests/markdown_go119.go +--- a/gopls/internal/lsp/tests/markdown_go119.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/tests/markdown_go119.go 1970-01-01 08:00:00 +@@ -1,22 +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. - --func sw() { -- var x interface{} -- -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") -- } --} +-//go:build go1.19 +-// +build go1.19 - ---- f2name-rename -- --package a +-package tests - -import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2name "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") +- "golang.org/x/tools/gopls/internal/lsp/tests/compare" -) - --func Random() int { -- y := 6 + 7 -- return y +-// DiffMarkdown compares two markdown strings produced by parsing go doc +-// comments. +-// +-// For go1.19 and later, markdown conversion is done using go/doc/comment. +-// Compared to the newer version, the older version has extra escapes, and +-// treats code blocks slightly differently. +-func DiffMarkdown(want, got string) string { +- return compare.Text(want, got) -} +diff -urN a/gopls/internal/lsp/tests/tests.go b/gopls/internal/lsp/tests/tests.go +--- a/gopls/internal/lsp/tests/tests.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/tests/tests.go 1970-01-01 08:00:00 +@@ -1,956 +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. - --func Random2(y int) int { //@rename("y", "z") -- return y --} +-// Package tests exports functionality to be used across a variety of gopls tests. +-package tests - --type Pos struct { -- x, y int --} +-import ( +- "bytes" +- "context" +- "flag" +- "fmt" +- "go/ast" +- "go/token" +- "io" +- "os" +- "path/filepath" +- "regexp" +- "sort" +- "strconv" +- "strings" +- "sync" +- "testing" +- "time" - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") --} +- "golang.org/x/tools/go/expect" +- "golang.org/x/tools/go/packages" +- "golang.org/x/tools/go/packages/packagestest" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/safetoken" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/lsp/source/completion" +- "golang.org/x/tools/gopls/internal/lsp/tests/compare" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/testenv" +- "golang.org/x/tools/internal/typeparams" +- "golang.org/x/tools/txtar" +-) - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") --} +-const ( +- overlayFileSuffix = ".overlay" +- goldenFileSuffix = ".golden" +- inFileSuffix = ".in" +- summaryFile = "summary.txt" - --func sw() { -- var x interface{} +- // The module path containing the testdata packages. +- // +- // Warning: the length of this module path matters, as we have bumped up +- // against command-line limitations on windows (golang/go#54800). +- testModule = "golang.org/lsptests" +-) - -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- f2name.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") -- } --} +-var UpdateGolden = flag.Bool("golden", false, "Update golden files") - ---- f2y-rename -- --package a +-// These type names apparently avoid the need to repeat the +-// type in the field name and the make() expression. +-type CallHierarchy = map[span.Span]*CallHierarchyResult +-type CompletionItems = map[token.Pos]*completion.CompletionItem +-type Completions = map[span.Span][]Completion +-type CompletionSnippets = map[span.Span][]CompletionSnippet +-type DeepCompletions = map[span.Span][]Completion +-type FuzzyCompletions = map[span.Span][]Completion +-type CaseSensitiveCompletions = map[span.Span][]Completion +-type RankCompletions = map[span.Span][]Completion +-type SemanticTokens = []span.Span +-type SuggestedFixes = map[span.Span][]SuggestedFix +-type MethodExtractions = map[span.Span]span.Span +-type Renames = map[span.Span]string +-type PrepareRenames = map[span.Span]*source.PrepareItem +-type InlayHints = []span.Span +-type Signatures = map[span.Span]*protocol.SignatureHelp +-type Links = map[span.URI][]Link +-type AddImport = map[span.URI]string +-type SelectionRanges = []span.Span - --import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2y "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) +-type Data struct { +- Config packages.Config +- Exported *packagestest.Exported +- CallHierarchy CallHierarchy +- CompletionItems CompletionItems +- Completions Completions +- CompletionSnippets CompletionSnippets +- DeepCompletions DeepCompletions +- FuzzyCompletions FuzzyCompletions +- CaseSensitiveCompletions CaseSensitiveCompletions +- RankCompletions RankCompletions +- SemanticTokens SemanticTokens +- SuggestedFixes SuggestedFixes +- MethodExtractions MethodExtractions +- Renames Renames +- InlayHints InlayHints +- PrepareRenames PrepareRenames +- Signatures Signatures +- Links Links +- AddImport AddImport +- SelectionRanges SelectionRanges - --func Random() int { -- y := 6 + 7 -- return y --} +- fragments map[string]string +- dir string +- golden map[string]*Golden +- mode string - --func Random2(y int) int { //@rename("y", "z") -- return y --} +- ModfileFlagAvailable bool - --type Pos struct { -- x, y int +- mappersMu sync.Mutex +- mappers map[span.URI]*protocol.Mapper -} - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") +-// The Tests interface abstracts the LSP-based implementation of the marker +-// test operators appearing in files beneath ../testdata/. +-// +-// TODO(adonovan): reduce duplication; see https://github.com/golang/go/issues/54845. +-// There is only one implementation (*runner in ../lsp_test.go), so +-// we can abolish the interface now. +-type Tests interface { +- CallHierarchy(*testing.T, span.Span, *CallHierarchyResult) +- Completion(*testing.T, span.Span, Completion, CompletionItems) +- CompletionSnippet(*testing.T, span.Span, CompletionSnippet, bool, CompletionItems) +- DeepCompletion(*testing.T, span.Span, Completion, CompletionItems) +- FuzzyCompletion(*testing.T, span.Span, Completion, CompletionItems) +- CaseSensitiveCompletion(*testing.T, span.Span, Completion, CompletionItems) +- RankCompletion(*testing.T, span.Span, Completion, CompletionItems) +- SemanticTokens(*testing.T, span.Span) +- SuggestedFix(*testing.T, span.Span, []SuggestedFix, int) +- MethodExtraction(*testing.T, span.Span, span.Span) +- InlayHints(*testing.T, span.Span) +- Rename(*testing.T, span.Span, string) +- PrepareRename(*testing.T, span.Span, *source.PrepareItem) +- SignatureHelp(*testing.T, span.Span, *protocol.SignatureHelp) +- Link(*testing.T, span.URI, []Link) +- AddImport(*testing.T, span.URI, string) +- SelectionRanges(*testing.T, span.Span) -} - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") --} +-type CompletionTestType int - --func sw() { -- var x interface{} +-const ( +- // Default runs the standard completion tests. +- CompletionDefault = CompletionTestType(iota) - -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- f2y.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") -- } --} +- // Deep tests deep completion. +- CompletionDeep - ---- fmt2-rename -- --package a +- // Fuzzy tests deep completion and fuzzy matching. +- CompletionFuzzy - --import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- fmt2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") +- // CaseSensitive tests case sensitive completion. +- CompletionCaseSensitive +- +- // CompletionRank candidates in test must be valid and in the right relative order. +- CompletionRank -) - --func Random() int { -- y := 6 + 7 -- return y +-type Completion struct { +- CompletionItems []token.Pos -} - --func Random2(y int) int { //@rename("y", "z") -- return y +-type CompletionSnippet struct { +- CompletionItem token.Pos +- PlainSnippet string +- PlaceholderSnippet string -} - --type Pos struct { -- x, y int +-type CallHierarchyResult struct { +- IncomingCalls, OutgoingCalls []protocol.CallHierarchyItem -} - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") +-type Link struct { +- Src span.Span +- Target string +- NotePosition token.Position -} - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") +-type SuggestedFix struct { +- ActionKind, Title string -} - --func sw() { -- var x interface{} -- -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- fmt2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") -- } +-type Golden struct { +- Filename string +- Archive *txtar.Archive +- Modified bool -} - ---- fmty-rename -- --package a +-func Context(t testing.TB) context.Context { +- return context.Background() +-} - --import ( -- lg "log" -- fmty "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) +-func DefaultOptions(o *source.Options) { +- o.SupportedCodeActions = map[source.FileKind]map[protocol.CodeActionKind]bool{ +- source.Go: { +- protocol.SourceOrganizeImports: true, +- protocol.QuickFix: true, +- protocol.RefactorRewrite: true, +- protocol.RefactorInline: true, +- protocol.RefactorExtract: true, +- protocol.SourceFixAll: true, +- }, +- source.Mod: { +- protocol.SourceOrganizeImports: true, +- }, +- source.Sum: {}, +- source.Work: {}, +- source.Tmpl: {}, +- } +- o.InsertTextFormat = protocol.SnippetTextFormat +- o.CompletionBudget = time.Minute +- o.HierarchicalDocumentSymbolSupport = true +- o.SemanticTokens = true +- o.InternalOptions.NewDiff = "new" - --func Random() int { -- y := 6 + 7 -- return y +- // Enable all inlay hints. +- if o.Hints == nil { +- o.Hints = make(map[string]bool) +- } +- for name := range source.AllInlayHints { +- o.Hints[name] = true +- } -} - --func Random2(y int) int { //@rename("y", "z") -- return y +-func RunTests(t *testing.T, dataDir string, includeMultiModule bool, f func(*testing.T, *Data)) { +- t.Helper() +- modes := []string{"Modules", "GOPATH"} +- if includeMultiModule { +- modes = append(modes, "MultiModule") +- } +- for _, mode := range modes { +- t.Run(mode, func(t *testing.T) { +- datum := load(t, mode, dataDir) +- t.Helper() +- f(t, datum) +- }) +- } -} - --type Pos struct { -- x, y int --} +-func load(t testing.TB, mode string, dir string) *Data { +- datum := &Data{ +- CallHierarchy: make(CallHierarchy), +- CompletionItems: make(CompletionItems), +- Completions: make(Completions), +- CompletionSnippets: make(CompletionSnippets), +- DeepCompletions: make(DeepCompletions), +- FuzzyCompletions: make(FuzzyCompletions), +- RankCompletions: make(RankCompletions), +- CaseSensitiveCompletions: make(CaseSensitiveCompletions), +- Renames: make(Renames), +- PrepareRenames: make(PrepareRenames), +- SuggestedFixes: make(SuggestedFixes), +- MethodExtractions: make(MethodExtractions), +- Signatures: make(Signatures), +- Links: make(Links), +- AddImport: make(AddImport), - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") --} +- dir: dir, +- fragments: map[string]string{}, +- golden: map[string]*Golden{}, +- mode: mode, +- mappers: map[span.URI]*protocol.Mapper{}, +- } - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") --} +- if !*UpdateGolden { +- summary := filepath.Join(filepath.FromSlash(dir), summaryFile+goldenFileSuffix) +- if _, err := os.Stat(summary); os.IsNotExist(err) { +- t.Fatalf("could not find golden file summary.txt in %#v", dir) +- } +- archive, err := txtar.ParseFile(summary) +- if err != nil { +- t.Fatalf("could not read golden file %v/%v: %v", dir, summary, err) +- } +- datum.golden[summaryFile] = &Golden{ +- Filename: summary, +- Archive: archive, +- } +- } - --func sw() { -- var x interface{} +- files := packagestest.MustCopyFileTree(dir) +- // Prune test cases that exercise generics. +- if !typeparams.Enabled { +- for name := range files { +- if strings.Contains(name, "_generics") { +- delete(files, name) +- } +- } +- } +- overlays := map[string][]byte{} +- for fragment, operation := range files { +- if trimmed := strings.TrimSuffix(fragment, goldenFileSuffix); trimmed != fragment { +- delete(files, fragment) +- goldFile := filepath.Join(dir, fragment) +- archive, err := txtar.ParseFile(goldFile) +- if err != nil { +- t.Fatalf("could not read golden file %v: %v", fragment, err) +- } +- datum.golden[trimmed] = &Golden{ +- Filename: goldFile, +- Archive: archive, +- } +- } else if trimmed := strings.TrimSuffix(fragment, inFileSuffix); trimmed != fragment { +- delete(files, fragment) +- files[trimmed] = operation +- } else if index := strings.Index(fragment, overlayFileSuffix); index >= 0 { +- delete(files, fragment) +- partial := fragment[:index] + fragment[index+len(overlayFileSuffix):] +- contents, err := os.ReadFile(filepath.Join(dir, fragment)) +- if err != nil { +- t.Fatal(err) +- } +- overlays[partial] = contents +- } +- } - -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- fmty.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- modules := []packagestest.Module{ +- { +- Name: testModule, +- Files: files, +- Overlay: overlays, +- }, - } --} +- switch mode { +- case "Modules": +- datum.Exported = packagestest.Export(t, packagestest.Modules, modules) +- case "GOPATH": +- datum.Exported = packagestest.Export(t, packagestest.GOPATH, modules) +- case "MultiModule": +- files := map[string]interface{}{} +- for k, v := range modules[0].Files { +- files[filepath.Join("testmodule", k)] = v +- } +- modules[0].Files = files - ---- format-rename -- --package a +- overlays := map[string][]byte{} +- for k, v := range modules[0].Overlay { +- overlays[filepath.Join("testmodule", k)] = v +- } +- modules[0].Overlay = overlays - --import ( -- lg "log" -- format "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) +- golden := map[string]*Golden{} +- for k, v := range datum.golden { +- if k == summaryFile { +- golden[k] = v +- } else { +- golden[filepath.Join("testmodule", k)] = v +- } +- } +- datum.golden = golden - --func Random() int { -- y := 6 + 7 -- return y --} +- datum.Exported = packagestest.Export(t, packagestest.Modules, modules) +- default: +- panic("unknown mode " + mode) +- } - --func Random2(y int) int { //@rename("y", "z") -- return y --} +- for _, m := range modules { +- for fragment := range m.Files { +- filename := datum.Exported.File(m.Name, fragment) +- datum.fragments[filename] = fragment +- } +- } - --type Pos struct { -- x, y int --} +- // Turn off go/packages debug logging. +- datum.Exported.Config.Logf = nil +- datum.Config.Logf = nil - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") --} +- // Merge the exported.Config with the view.Config. +- datum.Config = *datum.Exported.Config +- datum.Config.Fset = token.NewFileSet() +- datum.Config.Context = Context(nil) +- datum.Config.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) { +- panic("ParseFile should not be called") +- } - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") --} +- // Do a first pass to collect special markers for completion and workspace symbols. +- if err := datum.Exported.Expect(map[string]interface{}{ +- "item": func(name string, r packagestest.Range, _ []string) { +- datum.Exported.Mark(name, r) +- }, +- "symbol": func(name string, r packagestest.Range, _ []string) { +- datum.Exported.Mark(name, r) +- }, +- }); err != nil { +- t.Fatal(err) +- } - --func sw() { -- var x interface{} +- // Collect any data that needs to be used by subsequent tests. +- if err := datum.Exported.Expect(map[string]interface{}{ +- "item": datum.collectCompletionItems, +- "complete": datum.collectCompletions(CompletionDefault), +- "deep": datum.collectCompletions(CompletionDeep), +- "fuzzy": datum.collectCompletions(CompletionFuzzy), +- "casesensitive": datum.collectCompletions(CompletionCaseSensitive), +- "rank": datum.collectCompletions(CompletionRank), +- "snippet": datum.collectCompletionSnippets, +- "semantic": datum.collectSemanticTokens, +- "inlayHint": datum.collectInlayHints, +- "rename": datum.collectRenames, +- "prepare": datum.collectPrepareRenames, +- "signature": datum.collectSignatures, +- "link": datum.collectLinks, +- "suggestedfix": datum.collectSuggestedFixes, +- "extractmethod": datum.collectMethodExtractions, +- "incomingcalls": datum.collectIncomingCalls, +- "outgoingcalls": datum.collectOutgoingCalls, +- "addimport": datum.collectAddImports, +- "selectionrange": datum.collectSelectionRanges, +- }); err != nil { +- t.Fatal(err) +- } - -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- format.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- if mode == "MultiModule" { +- if err := moveFile(filepath.Join(datum.Config.Dir, "go.mod"), filepath.Join(datum.Config.Dir, "testmodule/go.mod")); err != nil { +- t.Fatal(err) +- } - } +- +- return datum -} - ---- log-rename -- --package a +-// moveFile moves the file at oldpath to newpath, by renaming if possible +-// or copying otherwise. +-func moveFile(oldpath, newpath string) (err error) { +- renameErr := os.Rename(oldpath, newpath) +- if renameErr == nil { +- return nil +- } - --import ( -- "log" -- "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) +- src, err := os.Open(oldpath) +- if err != nil { +- return err +- } +- defer func() { +- src.Close() +- if err == nil { +- err = os.Remove(oldpath) +- } +- }() - --func Random() int { -- y := 6 + 7 -- return y --} +- perm := os.ModePerm +- fi, err := src.Stat() +- if err == nil { +- perm = fi.Mode().Perm() +- } - --func Random2(y int) int { //@rename("y", "z") -- return y --} +- dst, err := os.OpenFile(newpath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) +- if err != nil { +- return err +- } - --type Pos struct { -- x, y int +- _, err = io.Copy(dst, src) +- if closeErr := dst.Close(); err == nil { +- err = closeErr +- } +- return err -} - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") --} +-func Run(t *testing.T, tests Tests, data *Data) { +- t.Helper() +- checkData(t, data) - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") --} +- eachCompletion := func(t *testing.T, cases map[span.Span][]Completion, test func(*testing.T, span.Span, Completion, CompletionItems)) { +- t.Helper() - --func sw() { -- var x interface{} +- for src, exp := range cases { +- for i, e := range exp { +- t.Run(SpanName(src)+"_"+strconv.Itoa(i), func(t *testing.T) { +- t.Helper() +- if strings.Contains(t.Name(), "cgo") { +- testenv.NeedsTool(t, "cgo") +- } +- test(t, src, e, data.CompletionItems) +- }) +- } - -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- log.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- } - } --} - ---- myX-rename -- --package a +- t.Run("CallHierarchy", func(t *testing.T) { +- t.Helper() +- for spn, callHierarchyResult := range data.CallHierarchy { +- t.Run(SpanName(spn), func(t *testing.T) { +- t.Helper() +- tests.CallHierarchy(t, spn, callHierarchyResult) +- }) +- } +- }) - --import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) +- t.Run("Completion", func(t *testing.T) { +- t.Helper() +- eachCompletion(t, data.Completions, tests.Completion) +- }) - --func Random() int { -- y := 6 + 7 -- return y --} +- t.Run("CompletionSnippets", func(t *testing.T) { +- t.Helper() +- for _, placeholders := range []bool{true, false} { +- for src, expecteds := range data.CompletionSnippets { +- for i, expected := range expecteds { +- name := SpanName(src) + "_" + strconv.Itoa(i+1) +- if placeholders { +- name += "_placeholders" +- } - --func Random2(y int) int { //@rename("y", "z") -- return y --} +- t.Run(name, func(t *testing.T) { +- t.Helper() +- tests.CompletionSnippet(t, src, expected, placeholders, data.CompletionItems) +- }) +- } +- } +- } +- }) - --type Pos struct { -- myX, y int --} +- t.Run("DeepCompletion", func(t *testing.T) { +- t.Helper() +- eachCompletion(t, data.DeepCompletions, tests.DeepCompletion) +- }) - --func (p *Pos) Sum() int { -- return p.myX + p.y //@rename("x", "myX") --} +- t.Run("FuzzyCompletion", func(t *testing.T) { +- t.Helper() +- eachCompletion(t, data.FuzzyCompletions, tests.FuzzyCompletion) +- }) - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") --} +- t.Run("CaseSensitiveCompletion", func(t *testing.T) { +- t.Helper() +- eachCompletion(t, data.CaseSensitiveCompletions, tests.CaseSensitiveCompletion) +- }) - --func sw() { -- var x interface{} +- t.Run("RankCompletions", func(t *testing.T) { +- t.Helper() +- eachCompletion(t, data.RankCompletions, tests.RankCompletion) +- }) - -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") -- } --} +- t.Run("SemanticTokens", func(t *testing.T) { +- t.Helper() +- for _, spn := range data.SemanticTokens { +- t.Run(uriName(spn.URI()), func(t *testing.T) { +- t.Helper() +- tests.SemanticTokens(t, spn) +- }) +- } +- }) - ---- pos-rename -- --package a +- t.Run("SuggestedFix", func(t *testing.T) { +- t.Helper() +- for spn, actionKinds := range data.SuggestedFixes { +- // Check if we should skip this spn if the -modfile flag is not available. +- if shouldSkip(data, spn.URI()) { +- continue +- } +- t.Run(SpanName(spn), func(t *testing.T) { +- t.Helper() +- tests.SuggestedFix(t, spn, actionKinds, 1) +- }) +- } +- }) - --import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) +- t.Run("MethodExtraction", func(t *testing.T) { +- t.Helper() +- for start, end := range data.MethodExtractions { +- // Check if we should skip this spn if the -modfile flag is not available. +- if shouldSkip(data, start.URI()) { +- continue +- } +- t.Run(SpanName(start), func(t *testing.T) { +- t.Helper() +- tests.MethodExtraction(t, start, end) +- }) +- } +- }) - --func Random() int { -- y := 6 + 7 -- return y --} +- t.Run("InlayHints", func(t *testing.T) { +- t.Helper() +- for _, src := range data.InlayHints { +- t.Run(SpanName(src), func(t *testing.T) { +- t.Helper() +- tests.InlayHints(t, src) +- }) +- } +- }) - --func Random2(y int) int { //@rename("y", "z") -- return y --} +- t.Run("Renames", func(t *testing.T) { +- t.Helper() +- for spn, newText := range data.Renames { +- t.Run(uriName(spn.URI())+"_"+newText, func(t *testing.T) { +- t.Helper() +- tests.Rename(t, spn, newText) +- }) +- } +- }) - --type Pos struct { -- x, y int --} +- t.Run("PrepareRenames", func(t *testing.T) { +- t.Helper() +- for src, want := range data.PrepareRenames { +- t.Run(SpanName(src), func(t *testing.T) { +- t.Helper() +- tests.PrepareRename(t, src, want) +- }) +- } +- }) - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") --} +- t.Run("SignatureHelp", func(t *testing.T) { +- t.Helper() +- for spn, expectedSignature := range data.Signatures { +- t.Run(SpanName(spn), func(t *testing.T) { +- t.Helper() +- tests.SignatureHelp(t, spn, expectedSignature) +- }) +- } +- }) - --func _() { -- var pos Pos //@rename("p", "pos") -- _ = pos.Sum() //@rename("Sum", "GetSum") --} +- t.Run("Link", func(t *testing.T) { +- t.Helper() +- for uri, wantLinks := range data.Links { +- // If we are testing GOPATH, then we do not want links with the versions +- // attached (pkg.go.dev/repoa/moda@v1.1.0/pkg), unless the file is a +- // go.mod, then we can skip it altogether. +- if data.Exported.Exporter == packagestest.GOPATH { +- if strings.HasSuffix(uri.Filename(), ".mod") { +- continue +- } +- re := regexp.MustCompile(`@v\d+\.\d+\.[\w-]+`) +- for i, link := range wantLinks { +- wantLinks[i].Target = re.ReplaceAllString(link.Target, "") +- } +- } +- t.Run(uriName(uri), func(t *testing.T) { +- t.Helper() +- tests.Link(t, uri, wantLinks) +- }) +- } +- }) - --func sw() { -- var x interface{} +- t.Run("AddImport", func(t *testing.T) { +- t.Helper() +- for uri, exp := range data.AddImport { +- t.Run(uriName(uri), func(t *testing.T) { +- tests.AddImport(t, uri, exp) +- }) +- } +- }) - -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- t.Run("SelectionRanges", func(t *testing.T) { +- t.Helper() +- for _, span := range data.SelectionRanges { +- t.Run(SpanName(span), func(t *testing.T) { +- tests.SelectionRanges(t, span) +- }) +- } +- }) +- +- if *UpdateGolden { +- for _, golden := range data.golden { +- if !golden.Modified { +- continue +- } +- sort.Slice(golden.Archive.Files, func(i, j int) bool { +- return golden.Archive.Files[i].Name < golden.Archive.Files[j].Name +- }) +- if err := os.WriteFile(golden.Filename, txtar.Format(golden.Archive), 0666); err != nil { +- t.Fatal(err) +- } +- } - } -} - ---- y0-rename -- --package a +-func checkData(t *testing.T, data *Data) { +- buf := &bytes.Buffer{} +- linksCount := 0 +- for _, want := range data.Links { +- linksCount += len(want) +- } - --import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) +- snippetCount := 0 +- for _, want := range data.CompletionSnippets { +- snippetCount += len(want) +- } - --func Random() int { -- y := 6 + 7 -- return y --} +- countCompletions := func(c map[span.Span][]Completion) (count int) { +- for _, want := range c { +- count += len(want) +- } +- return count +- } - --func Random2(y int) int { //@rename("y", "z") -- return y --} +- fmt.Fprintf(buf, "CallHierarchyCount = %v\n", len(data.CallHierarchy)) +- fmt.Fprintf(buf, "CompletionsCount = %v\n", countCompletions(data.Completions)) +- fmt.Fprintf(buf, "CompletionSnippetCount = %v\n", snippetCount) +- fmt.Fprintf(buf, "DeepCompletionsCount = %v\n", countCompletions(data.DeepCompletions)) +- fmt.Fprintf(buf, "FuzzyCompletionsCount = %v\n", countCompletions(data.FuzzyCompletions)) +- fmt.Fprintf(buf, "RankedCompletionsCount = %v\n", countCompletions(data.RankCompletions)) +- fmt.Fprintf(buf, "CaseSensitiveCompletionsCount = %v\n", countCompletions(data.CaseSensitiveCompletions)) +- fmt.Fprintf(buf, "SemanticTokenCount = %v\n", len(data.SemanticTokens)) +- fmt.Fprintf(buf, "SuggestedFixCount = %v\n", len(data.SuggestedFixes)) +- fmt.Fprintf(buf, "MethodExtractionCount = %v\n", len(data.MethodExtractions)) +- fmt.Fprintf(buf, "InlayHintsCount = %v\n", len(data.InlayHints)) +- fmt.Fprintf(buf, "RenamesCount = %v\n", len(data.Renames)) +- fmt.Fprintf(buf, "PrepareRenamesCount = %v\n", len(data.PrepareRenames)) +- fmt.Fprintf(buf, "SignaturesCount = %v\n", len(data.Signatures)) +- fmt.Fprintf(buf, "LinksCount = %v\n", linksCount) +- fmt.Fprintf(buf, "SelectionRangesCount = %v\n", len(data.SelectionRanges)) - --type Pos struct { -- x, y int +- want := string(data.Golden(t, "summary", summaryFile, func() ([]byte, error) { +- return buf.Bytes(), nil +- })) +- got := buf.String() +- if want != got { +- // These counters change when assertions are added or removed. +- // They act as an independent safety net to ensure that the +- // tests didn't spuriously pass because they did no work. +- t.Errorf("test summary does not match:\n%s\n(Run with -golden to update golden file; also, there may be one per Go version.)", compare.Text(want, got)) +- } -} - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") --} +-func (data *Data) Mapper(uri span.URI) (*protocol.Mapper, error) { +- data.mappersMu.Lock() +- defer data.mappersMu.Unlock() - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") +- if _, ok := data.mappers[uri]; !ok { +- content, err := data.Exported.FileContents(uri.Filename()) +- if err != nil { +- return nil, err +- } +- data.mappers[uri] = protocol.NewMapper(uri, content) +- } +- return data.mappers[uri], nil -} - --func sw() { -- var x interface{} +-func (data *Data) Golden(t *testing.T, tag, target string, update func() ([]byte, error)) []byte { +- t.Helper() +- fragment, found := data.fragments[target] +- if !found { +- if filepath.IsAbs(target) { +- t.Fatalf("invalid golden file fragment %v", target) +- } +- fragment = target +- } +- golden := data.golden[fragment] +- if golden == nil { +- if !*UpdateGolden { +- t.Fatalf("could not find golden file %v: %v", fragment, tag) +- } +- golden = &Golden{ +- Filename: filepath.Join(data.dir, fragment+goldenFileSuffix), +- Archive: &txtar.Archive{}, +- Modified: true, +- } +- data.golden[fragment] = golden +- } +- var file *txtar.File +- for i := range golden.Archive.Files { +- f := &golden.Archive.Files[i] +- if f.Name == tag { +- file = f +- break +- } +- } +- if *UpdateGolden { +- if file == nil { +- golden.Archive.Files = append(golden.Archive.Files, txtar.File{ +- Name: tag, +- }) +- file = &golden.Archive.Files[len(golden.Archive.Files)-1] +- } +- contents, err := update() +- if err != nil { +- t.Fatalf("could not update golden file %v: %v", fragment, err) +- } +- file.Data = append(contents, '\n') // add trailing \n for txtar +- golden.Modified = true - -- switch y0 := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y0) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y0) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y0) //@rename("y", "y3"),rename("f2","fmt2") - } +- if file == nil { +- t.Fatalf("could not find golden contents %v: %v", fragment, tag) +- } +- if len(file.Data) == 0 { +- return file.Data +- } +- return file.Data[:len(file.Data)-1] // drop the trailing \n -} - ---- y1-rename -- --package a -- --import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) -- --func Random() int { -- y := 6 + 7 -- return y +-func (data *Data) collectCompletions(typ CompletionTestType) func(span.Span, []token.Pos) { +- result := func(m map[span.Span][]Completion, src span.Span, expected []token.Pos) { +- m[src] = append(m[src], Completion{ +- CompletionItems: expected, +- }) +- } +- switch typ { +- case CompletionDeep: +- return func(src span.Span, expected []token.Pos) { +- result(data.DeepCompletions, src, expected) +- } +- case CompletionFuzzy: +- return func(src span.Span, expected []token.Pos) { +- result(data.FuzzyCompletions, src, expected) +- } +- case CompletionRank: +- return func(src span.Span, expected []token.Pos) { +- result(data.RankCompletions, src, expected) +- } +- case CompletionCaseSensitive: +- return func(src span.Span, expected []token.Pos) { +- result(data.CaseSensitiveCompletions, src, expected) +- } +- default: +- return func(src span.Span, expected []token.Pos) { +- result(data.Completions, src, expected) +- } +- } -} - --func Random2(y int) int { //@rename("y", "z") -- return y +-func (data *Data) collectCompletionItems(pos token.Pos, label, detail, kind string, args []string) { +- var documentation string +- if len(args) > 3 { +- documentation = args[3] +- } +- data.CompletionItems[pos] = &completion.CompletionItem{ +- Label: label, +- Detail: detail, +- Kind: protocol.ParseCompletionItemKind(kind), +- Documentation: documentation, +- } -} - --type Pos struct { -- x, y int +-func (data *Data) collectAddImports(spn span.Span, imp string) { +- data.AddImport[spn.URI()] = imp -} - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") +-func (data *Data) collectSemanticTokens(spn span.Span) { +- data.SemanticTokens = append(data.SemanticTokens, spn) -} - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") +-func (data *Data) collectSuggestedFixes(spn span.Span, actionKind, fix string) { +- data.SuggestedFixes[spn] = append(data.SuggestedFixes[spn], SuggestedFix{actionKind, fix}) -} - --func sw() { -- var x interface{} -- -- switch y1 := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y1) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y1) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y1) //@rename("y", "y3"),rename("f2","fmt2") +-func (data *Data) collectMethodExtractions(start span.Span, end span.Span) { +- if _, ok := data.MethodExtractions[start]; !ok { +- data.MethodExtractions[start] = end - } -} - ---- y2-rename -- --package a -- --import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) -- --func Random() int { -- y := 6 + 7 -- return y +-func (data *Data) collectSelectionRanges(spn span.Span) { +- data.SelectionRanges = append(data.SelectionRanges, spn) -} - --func Random2(y int) int { //@rename("y", "z") -- return y +-func (data *Data) collectIncomingCalls(src span.Span, calls []span.Span) { +- for _, call := range calls { +- rng := data.mustRange(call) +- // we're only comparing protocol.range +- if data.CallHierarchy[src] != nil { +- data.CallHierarchy[src].IncomingCalls = append(data.CallHierarchy[src].IncomingCalls, +- protocol.CallHierarchyItem{ +- URI: protocol.DocumentURI(call.URI()), +- Range: rng, +- }) +- } else { +- data.CallHierarchy[src] = &CallHierarchyResult{ +- IncomingCalls: []protocol.CallHierarchyItem{ +- {URI: protocol.DocumentURI(call.URI()), Range: rng}, +- }, +- } +- } +- } -} - --type Pos struct { -- x, y int +-func (data *Data) collectOutgoingCalls(src span.Span, calls []span.Span) { +- if data.CallHierarchy[src] == nil { +- data.CallHierarchy[src] = &CallHierarchyResult{} +- } +- for _, call := range calls { +- // we're only comparing protocol.range +- data.CallHierarchy[src].OutgoingCalls = append(data.CallHierarchy[src].OutgoingCalls, +- protocol.CallHierarchyItem{ +- URI: protocol.DocumentURI(call.URI()), +- Range: data.mustRange(call), +- }) +- } -} - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") +-func (data *Data) collectInlayHints(src span.Span) { +- data.InlayHints = append(data.InlayHints, src) -} - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") +-func (data *Data) collectRenames(src span.Span, newText string) { +- data.Renames[src] = newText -} - --func sw() { -- var x interface{} -- -- switch y2 := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y2) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y2) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y2) //@rename("y", "y3"),rename("f2","fmt2") +-func (data *Data) collectPrepareRenames(src, spn span.Span, placeholder string) { +- data.PrepareRenames[src] = &source.PrepareItem{ +- Range: data.mustRange(spn), +- Text: placeholder, - } -} - ---- y3-rename -- --package a -- --import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) -- --func Random() int { -- y := 6 + 7 -- return y +-// mustRange converts spn into a protocol.Range, panicking on any error. +-func (data *Data) mustRange(spn span.Span) protocol.Range { +- m, err := data.Mapper(spn.URI()) +- rng, err := m.SpanRange(spn) +- if err != nil { +- panic(fmt.Sprintf("converting span %s to range: %v", spn, err)) +- } +- return rng -} - --func Random2(y int) int { //@rename("y", "z") -- return y +-func (data *Data) collectSignatures(spn span.Span, signature string, activeParam int64) { +- data.Signatures[spn] = &protocol.SignatureHelp{ +- Signatures: []protocol.SignatureInformation{ +- { +- Label: signature, +- }, +- }, +- ActiveParameter: uint32(activeParam), +- } +- // Hardcode special case to test the lack of a signature. +- if signature == "" && activeParam == 0 { +- data.Signatures[spn] = nil +- } -} - --type Pos struct { -- x, y int +-func (data *Data) collectCompletionSnippets(spn span.Span, item token.Pos, plain, placeholder string) { +- data.CompletionSnippets[spn] = append(data.CompletionSnippets[spn], CompletionSnippet{ +- CompletionItem: item, +- PlainSnippet: plain, +- PlaceholderSnippet: placeholder, +- }) -} - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") +-func (data *Data) collectLinks(spn span.Span, link string, note *expect.Note, fset *token.FileSet) { +- position := safetoken.StartPosition(fset, note.Pos) +- uri := spn.URI() +- data.Links[uri] = append(data.Links[uri], Link{ +- Src: spn, +- Target: link, +- NotePosition: position, +- }) -} - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") +-func uriName(uri span.URI) string { +- return filepath.Base(strings.TrimSuffix(uri.Filename(), ".go")) -} - --func sw() { -- var x interface{} +-// TODO(golang/go#54845): improve the formatting here to match standard +-// line:column position formatting. +-func SpanName(spn span.Span) string { +- return fmt.Sprintf("%v_%v_%v", uriName(spn.URI()), spn.Start().Line(), spn.Start().Column()) +-} - -- switch y3 := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y3) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y3) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y3) //@rename("y", "y3"),rename("f2","fmt2") +-func shouldSkip(data *Data, uri span.URI) bool { +- if data.ModfileFlagAvailable { +- return false +- } +- // If the -modfile flag is not available, then we do not want to run +- // any tests on the go.mod file. +- if strings.HasSuffix(uri.Filename(), ".mod") { +- return true - } +- // If the -modfile flag is not available, then we do not want to test any +- // uri that contains "go mod tidy". +- m, err := data.Mapper(uri) +- return err == nil && strings.Contains(string(m.Content), ", \"go mod tidy\",") -} +diff -urN a/gopls/internal/lsp/tests/util.go b/gopls/internal/lsp/tests/util.go +--- a/gopls/internal/lsp/tests/util.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/tests/util.go 1970-01-01 08:00:00 +@@ -1,311 +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. - ---- z-rename -- --package a +-package tests - -import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") +- "bytes" +- "fmt" +- "go/token" +- "sort" +- "strconv" +- "strings" +- +- "github.com/google/go-cmp/cmp" +- "github.com/google/go-cmp/cmp/cmpopts" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source/completion" +- "golang.org/x/tools/gopls/internal/lsp/tests/compare" +- "golang.org/x/tools/gopls/internal/span" -) - --func Random() int { -- y := 6 + 7 -- return y +-var builtins = map[string]bool{ +- "append": true, +- "cap": true, +- "close": true, +- "complex": true, +- "copy": true, +- "delete": true, +- "error": true, +- "false": true, +- "imag": true, +- "iota": true, +- "len": true, +- "make": true, +- "new": true, +- "nil": true, +- "panic": true, +- "print": true, +- "println": true, +- "real": true, +- "recover": true, +- "true": true, -} - --func Random2(z int) int { //@rename("y", "z") -- return z +-// DiffLinks takes the links we got and checks if they are located within the source or a Note. +-// If the link is within a Note, the link is removed. +-// Returns an diff comment if there are differences and empty string if no diffs. +-func DiffLinks(mapper *protocol.Mapper, wantLinks []Link, gotLinks []protocol.DocumentLink) string { +- var notePositions []token.Position +- links := make(map[span.Span]string, len(wantLinks)) +- for _, link := range wantLinks { +- links[link.Src] = link.Target +- notePositions = append(notePositions, link.NotePosition) +- } +- +- var msg strings.Builder +- for _, link := range gotLinks { +- spn, err := mapper.RangeSpan(link.Range) +- if err != nil { +- return fmt.Sprintf("%v", err) +- } +- linkInNote := false +- for _, notePosition := range notePositions { +- // Drop the links found inside expectation notes arguments as this links are not collected by expect package. +- if notePosition.Line == spn.Start().Line() && +- notePosition.Column <= spn.Start().Column() { +- delete(links, spn) +- linkInNote = true +- } +- } +- if linkInNote { +- continue +- } +- +- if target, ok := links[spn]; ok { +- delete(links, spn) +- if target != *link.Target { +- fmt.Fprintf(&msg, "%s: want link with target %q, got %q\n", spn, target, *link.Target) +- } +- } else { +- fmt.Fprintf(&msg, "%s: got unexpected link with target %q\n", spn, *link.Target) +- } +- } +- for spn, target := range links { +- fmt.Fprintf(&msg, "%s: expected link with target %q is missing\n", spn, target) +- } +- return msg.String() -} - --type Pos struct { -- x, y int +-// inRange reports whether p is contained within [r.Start, r.End), or if p == +-// r.Start == r.End (special handling for the case where the range is a single +-// point). +-func inRange(p protocol.Position, r protocol.Range) bool { +- if protocol.IsPoint(r) { +- return protocol.ComparePosition(r.Start, p) == 0 +- } +- if protocol.ComparePosition(r.Start, p) <= 0 && protocol.ComparePosition(p, r.End) < 0 { +- return true +- } +- return false -} - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") +-func DiffSignatures(spn span.Span, want, got *protocol.SignatureHelp) string { +- decorate := func(f string, args ...interface{}) string { +- return fmt.Sprintf("invalid signature at %s: %s", spn, fmt.Sprintf(f, args...)) +- } +- if len(got.Signatures) != 1 { +- return decorate("wanted 1 signature, got %d", len(got.Signatures)) +- } +- if got.ActiveSignature != 0 { +- return decorate("wanted active signature of 0, got %d", int(got.ActiveSignature)) +- } +- if want.ActiveParameter != got.ActiveParameter { +- return decorate("wanted active parameter of %d, got %d", want.ActiveParameter, int(got.ActiveParameter)) +- } +- g := got.Signatures[0] +- w := want.Signatures[0] +- if diff := compare.Text(NormalizeAny(w.Label), NormalizeAny(g.Label)); diff != "" { +- return decorate("mismatched labels:\n%s", diff) +- } +- var paramParts []string +- for _, p := range g.Parameters { +- paramParts = append(paramParts, p.Label) +- } +- paramsStr := strings.Join(paramParts, ", ") +- if !strings.Contains(g.Label, paramsStr) { +- return decorate("expected signature %q to contain params %q", g.Label, paramsStr) +- } +- return "" -} - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") +-// NormalizeAny replaces occurrences of interface{} in input with any. +-// +-// In Go 1.18, standard library functions were changed to use the 'any' +-// alias in place of interface{}, which affects their type string. +-func NormalizeAny(input string) string { +- return strings.ReplaceAll(input, "interface{}", "any") -} - --func sw() { -- var x interface{} +-// DiffCallHierarchyItems returns the diff between expected and actual call locations for incoming/outgoing call hierarchies +-func DiffCallHierarchyItems(gotCalls []protocol.CallHierarchyItem, expectedCalls []protocol.CallHierarchyItem) string { +- expected := make(map[protocol.Location]bool) +- for _, call := range expectedCalls { +- expected[protocol.Location{URI: call.URI, Range: call.Range}] = true +- } - -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- got := make(map[protocol.Location]bool) +- for _, call := range gotCalls { +- got[protocol.Location{URI: call.URI, Range: call.Range}] = true +- } +- if len(got) != len(expected) { +- return fmt.Sprintf("expected %d calls but got %d", len(expected), len(got)) +- } +- for spn := range got { +- if !expected[spn] { +- return fmt.Sprintf("incorrect calls, expected locations %v but got locations %v", expected, got) +- } - } +- return "" -} - -diff -urN a/gopls/internal/lsp/testdata/rename/a/random.go.in b/gopls/internal/lsp/testdata/rename/a/random.go.in ---- a/gopls/internal/lsp/testdata/rename/a/random.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/a/random.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,42 +0,0 @@ --package a +-func FilterBuiltins(src span.Span, items []protocol.CompletionItem) []protocol.CompletionItem { +- var ( +- got []protocol.CompletionItem +- wantBuiltins = strings.Contains(string(src.URI()), "builtins") +- wantKeywords = strings.Contains(string(src.URI()), "keywords") +- ) +- for _, item := range items { +- if !wantBuiltins && isBuiltin(item.Label, item.Detail, item.Kind) { +- continue +- } - --import ( -- lg "log" -- "fmt" //@rename("fmt", "fmty") -- f2 "fmt" //@rename("f2", "f2name"),rename("fmt","f2y") --) +- if !wantKeywords && token.Lookup(item.Label).IsKeyword() { +- continue +- } - --func Random() int { -- y := 6 + 7 -- return y +- got = append(got, item) +- } +- return got -} - --func Random2(y int) int { //@rename("y", "z") -- return y +-func isBuiltin(label, detail string, kind protocol.CompletionItemKind) bool { +- if detail == "" && kind == protocol.ClassCompletion { +- return true +- } +- // Remaining builtin constants, variables, interfaces, and functions. +- trimmed := label +- if i := strings.Index(trimmed, "("); i >= 0 { +- trimmed = trimmed[:i] +- } +- return builtins[trimmed] -} - --type Pos struct { -- x, y int --} +-func CheckCompletionOrder(want, got []protocol.CompletionItem, strictScores bool) string { +- var ( +- matchedIdxs []int +- lastGotIdx int +- lastGotSort float64 +- inOrder = true +- errorMsg = "completions out of order" +- ) +- for _, w := range want { +- var found bool +- for i, g := range got { +- if w.Label == g.Label && NormalizeAny(w.Detail) == NormalizeAny(g.Detail) && w.Kind == g.Kind { +- matchedIdxs = append(matchedIdxs, i) +- found = true - --func (p *Pos) Sum() int { -- return p.x + p.y //@rename("x", "myX") --} +- if i < lastGotIdx { +- inOrder = false +- } +- lastGotIdx = i - --func _() { -- var p Pos //@rename("p", "pos") -- _ = p.Sum() //@rename("Sum", "GetSum") --} +- sort, _ := strconv.ParseFloat(g.SortText, 64) +- if strictScores && len(matchedIdxs) > 1 && sort <= lastGotSort { +- inOrder = false +- errorMsg = "candidate scores not strictly decreasing" +- } +- lastGotSort = sort - --func sw() { -- var x interface{} +- break +- } +- } +- if !found { +- return summarizeCompletionItems(-1, []protocol.CompletionItem{w}, got, "didn't find expected completion") +- } +- } - -- switch y := x.(type) { //@rename("y", "y0") -- case int: -- fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format") -- case string: -- lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log") -- default: -- f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2") +- sort.Ints(matchedIdxs) +- matched := make([]protocol.CompletionItem, 0, len(matchedIdxs)) +- for _, idx := range matchedIdxs { +- matched = append(matched, got[idx]) - } --} -diff -urN a/gopls/internal/lsp/testdata/rename/b/b.go b/gopls/internal/lsp/testdata/rename/b/b.go ---- a/gopls/internal/lsp/testdata/rename/b/b.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/b/b.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,20 +0,0 @@ --package b - --var c int //@rename("int", "uint") +- if !inOrder { +- return summarizeCompletionItems(-1, want, matched, errorMsg) +- } - --func _() { -- a := 1 //@rename("a", "error") -- a = 2 -- _ = a +- return "" -} - --var ( -- // Hello there. -- // Foo does the thing. -- Foo int //@rename("Foo", "Bob") --) -- --/* --Hello description --*/ --func Hello() {} //@rename("Hello", "Goodbye") -diff -urN a/gopls/internal/lsp/testdata/rename/b/b.go.golden b/gopls/internal/lsp/testdata/rename/b/b.go.golden ---- a/gopls/internal/lsp/testdata/rename/b/b.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/b/b.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,78 +0,0 @@ ---- Bob-rename -- --package b -- --var c int //@rename("int", "uint") +-func DiffSnippets(want string, got *protocol.CompletionItem) string { +- if want == "" { +- if got != nil { +- x := got.TextEdit +- return fmt.Sprintf("expected no snippet but got %s", x.NewText) +- } +- } else { +- if got == nil { +- return fmt.Sprintf("couldn't find completion matching %q", want) +- } +- x := got.TextEdit +- if want != x.NewText { +- return fmt.Sprintf("expected snippet %q, got %q", want, x.NewText) +- } +- } +- return "" +-} - --func _() { -- a := 1 //@rename("a", "error") -- a = 2 -- _ = a +-func FindItem(list []protocol.CompletionItem, want completion.CompletionItem) *protocol.CompletionItem { +- for _, item := range list { +- if item.Label == want.Label { +- return &item +- } +- } +- return nil -} - --var ( -- // Hello there. -- // Bob does the thing. -- Bob int //@rename("Foo", "Bob") --) +-// DiffCompletionItems prints the diff between expected and actual completion +-// test results. +-// +-// The diff will be formatted using '-' and '+' for want and got, respectively. +-func DiffCompletionItems(want, got []protocol.CompletionItem) string { +- // Many fields are not set in the "want" slice. +- irrelevantFields := []string{ +- "AdditionalTextEdits", +- "Documentation", +- "TextEdit", +- "SortText", +- "Preselect", +- "FilterText", +- "InsertText", +- "InsertTextFormat", +- } +- ignore := cmpopts.IgnoreFields(protocol.CompletionItem{}, irrelevantFields...) +- normalizeAny := cmpopts.AcyclicTransformer("NormalizeAny", func(item protocol.CompletionItem) protocol.CompletionItem { +- item.Detail = NormalizeAny(item.Detail) +- return item +- }) +- return cmp.Diff(want, got, ignore, normalizeAny) +-} - --/* --Hello description --*/ --func Hello() {} //@rename("Hello", "Goodbye") +-func summarizeCompletionItems(i int, want, got []protocol.CompletionItem, reason string, args ...interface{}) string { +- msg := &bytes.Buffer{} +- fmt.Fprint(msg, "completion failed") +- if i >= 0 { +- fmt.Fprintf(msg, " at %d", i) +- } +- fmt.Fprint(msg, " because of ") +- fmt.Fprintf(msg, reason, args...) +- fmt.Fprint(msg, ":\nexpected:\n") +- for _, d := range want { +- fmt.Fprintf(msg, " %v\n", d) +- } +- fmt.Fprintf(msg, "got:\n") +- for _, d := range got { +- fmt.Fprintf(msg, " %v\n", d) +- } +- return msg.String() +-} +diff -urN a/gopls/internal/lsp/tests/util_go118.go b/gopls/internal/lsp/tests/util_go118.go +--- a/gopls/internal/lsp/tests/util_go118.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/tests/util_go118.go 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-// Copyright 2023 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. - ---- Goodbye-rename -- --b.go: --package b +-//go:build go1.18 +-// +build go1.18 - --var c int //@rename("int", "uint") +-package tests - --func _() { -- a := 1 //@rename("a", "error") -- a = 2 -- _ = a +-func init() { +- builtins["any"] = true +- builtins["comparable"] = true -} +diff -urN a/gopls/internal/lsp/tests/util_go121.go b/gopls/internal/lsp/tests/util_go121.go +--- a/gopls/internal/lsp/tests/util_go121.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/tests/util_go121.go 1970-01-01 08:00:00 +@@ -1,14 +0,0 @@ +-// Copyright 2023 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. - --var ( -- // Hello there. -- // Foo does the thing. -- Foo int //@rename("Foo", "Bob") --) -- --/* --Goodbye description --*/ --func Goodbye() {} //@rename("Hello", "Goodbye") -- --c.go: --package c +-//go:build go1.21 +-// +build go1.21 - --import "golang.org/lsptests/rename/b" +-package tests - --func _() { -- b.Goodbye() //@rename("Hello", "Goodbye") +-func init() { +- builtins["clear"] = true +- builtins["max"] = true +- builtins["min"] = true -} +diff -urN a/gopls/internal/lsp/tests/util_go122.go b/gopls/internal/lsp/tests/util_go122.go +--- a/gopls/internal/lsp/tests/util_go122.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/tests/util_go122.go 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +-// Copyright 2023 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. - ---- error-rename -- --package b +-//go:build go1.22 +-// +build go1.22 - --var c int //@rename("int", "uint") +-package tests - --func _() { -- error := 1 //@rename("a", "error") -- error = 2 -- _ = error +-func init() { +- builtins["zero"] = true -} +diff -urN a/gopls/internal/lsp/text_synchronization.go b/gopls/internal/lsp/text_synchronization.go +--- a/gopls/internal/lsp/text_synchronization.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/text_synchronization.go 1970-01-01 08:00:00 +@@ -1,367 +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. - --var ( -- // Hello there. -- // Foo does the thing. -- Foo int //@rename("Foo", "Bob") --) +-package lsp - --/* --Hello description --*/ --func Hello() {} //@rename("Hello", "Goodbye") +-import ( +- "bytes" +- "context" +- "errors" +- "fmt" +- "path/filepath" +- "sync" - ---- uint-rename -- --int is built in and cannot be renamed -diff -urN a/gopls/internal/lsp/testdata/rename/bad/bad.go.golden b/gopls/internal/lsp/testdata/rename/bad/bad.go.golden ---- a/gopls/internal/lsp/testdata/rename/bad/bad.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/bad/bad.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,2 +0,0 @@ ---- rFunc-rename -- --renaming "sFunc" to "rFunc" not possible because "golang.org/lsptests/rename/bad" has errors -diff -urN a/gopls/internal/lsp/testdata/rename/bad/bad.go.in b/gopls/internal/lsp/testdata/rename/bad/bad.go.in ---- a/gopls/internal/lsp/testdata/rename/bad/bad.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/bad/bad.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package bad +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/event/tag" +- "golang.org/x/tools/internal/jsonrpc2" +-) - --type myStruct struct { --} +-// ModificationSource identifies the origin of a change. +-type ModificationSource int - --func (s *myStruct) sFunc() bool { //@rename("sFunc", "rFunc") -- return s.Bad --} -diff -urN a/gopls/internal/lsp/testdata/rename/bad/bad_test.go.in b/gopls/internal/lsp/testdata/rename/bad/bad_test.go.in ---- a/gopls/internal/lsp/testdata/rename/bad/bad_test.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/bad/bad_test.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1 +0,0 @@ --package bad -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/rename/c/c2.go b/gopls/internal/lsp/testdata/rename/c/c2.go ---- a/gopls/internal/lsp/testdata/rename/c/c2.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/c/c2.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,4 +0,0 @@ --package c +-const ( +- // FromDidOpen is from a didOpen notification. +- FromDidOpen = ModificationSource(iota) - --//go:embed Static/* --var Static embed.FS //@rename("Static", "static") -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/rename/c/c2.go.golden b/gopls/internal/lsp/testdata/rename/c/c2.go.golden ---- a/gopls/internal/lsp/testdata/rename/c/c2.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/c/c2.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,5 +0,0 @@ ---- static-rename -- --package c +- // FromDidChange is from a didChange notification. +- FromDidChange - --//go:embed Static/* --var static embed.FS //@rename("Static", "static") -diff -urN a/gopls/internal/lsp/testdata/rename/c/c.go b/gopls/internal/lsp/testdata/rename/c/c.go ---- a/gopls/internal/lsp/testdata/rename/c/c.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/c/c.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package c +- // FromDidChangeWatchedFiles is from didChangeWatchedFiles notification. +- FromDidChangeWatchedFiles - --import "golang.org/lsptests/rename/b" +- // FromDidSave is from a didSave notification. +- FromDidSave - --func _() { -- b.Hello() //@rename("Hello", "Goodbye") --} -diff -urN a/gopls/internal/lsp/testdata/rename/c/c.go.golden b/gopls/internal/lsp/testdata/rename/c/c.go.golden ---- a/gopls/internal/lsp/testdata/rename/c/c.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/c/c.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,32 +0,0 @@ ---- Goodbye-rename -- --b.go: --package b +- // FromDidClose is from a didClose notification. +- FromDidClose - --var c int //@rename("int", "uint") +- // FromDidChangeConfiguration is from a didChangeConfiguration notification. +- FromDidChangeConfiguration - --func _() { -- a := 1 //@rename("a", "error") -- a = 2 -- _ = a --} +- // FromRegenerateCgo refers to file modifications caused by regenerating +- // the cgo sources for the workspace. +- FromRegenerateCgo - --var ( -- // Hello there. -- // Foo does the thing. -- Foo int //@rename("Foo", "Bob") +- // FromInitialWorkspaceLoad refers to the loading of all packages in the +- // workspace when the view is first created. +- FromInitialWorkspaceLoad -) - --/* --Goodbye description --*/ --func Goodbye() {} //@rename("Hello", "Goodbye") -- --c.go: --package c -- --import "golang.org/lsptests/rename/b" -- --func _() { -- b.Goodbye() //@rename("Hello", "Goodbye") +-func (m ModificationSource) String() string { +- switch m { +- case FromDidOpen: +- return "opened files" +- case FromDidChange: +- return "changed files" +- case FromDidChangeWatchedFiles: +- return "files changed on disk" +- case FromDidSave: +- return "saved files" +- case FromDidClose: +- return "close files" +- case FromRegenerateCgo: +- return "regenerate cgo" +- case FromInitialWorkspaceLoad: +- return "initial workspace load" +- default: +- return "unknown file modification" +- } -} - -diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go b/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go ---- a/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package another -- --type ( -- I interface{ F() } -- C struct{ I } --) -- --func (C) g() +-func (s *Server) didOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error { +- ctx, done := event.Start(ctx, "lsp.Server.didOpen", tag.URI.Of(params.TextDocument.URI)) +- defer done() - --func _() { -- var x I = C{} -- x.F() //@rename("F", "G") +- uri := params.TextDocument.URI.SpanURI() +- if !uri.IsFile() { +- return nil +- } +- // There may not be any matching view in the current session. If that's +- // the case, try creating a new view based on the opened file path. +- // +- // TODO(rstambler): This seems like it would continuously add new +- // views, but it won't because ViewOf only returns an error when there +- // are no views in the session. I don't know if that logic should go +- // here, or if we can continue to rely on that implementation detail. +- // +- // TODO(golang/go#57979): this will be generalized to a different view calculation. +- if _, err := s.session.ViewOf(uri); err != nil { +- dir := filepath.Dir(uri.Filename()) +- if err := s.addFolders(ctx, []protocol.WorkspaceFolder{{ +- URI: string(protocol.URIFromPath(dir)), +- Name: filepath.Base(dir), +- }}); err != nil { +- return err +- } +- } +- return s.didModifyFiles(ctx, []source.FileModification{{ +- URI: uri, +- Action: source.Open, +- Version: params.TextDocument.Version, +- Text: []byte(params.TextDocument.Text), +- LanguageID: params.TextDocument.LanguageID, +- }}, FromDidOpen) -} -diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go.golden b/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go.golden ---- a/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/crosspkg/another/another.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,15 +0,0 @@ ---- G-rename -- --package another - --type ( -- I interface{ G() } -- C struct{ I } --) +-func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error { +- ctx, done := event.Start(ctx, "lsp.Server.didChange", tag.URI.Of(params.TextDocument.URI)) +- defer done() - --func (C) g() +- uri := params.TextDocument.URI.SpanURI() +- if !uri.IsFile() { +- return nil +- } - --func _() { -- var x I = C{} -- x.G() //@rename("F", "G") +- text, err := s.changedText(ctx, uri, params.ContentChanges) +- if err != nil { +- return err +- } +- c := source.FileModification{ +- URI: uri, +- Action: source.Change, +- Version: params.TextDocument.Version, +- Text: text, +- } +- if err := s.didModifyFiles(ctx, []source.FileModification{c}, FromDidChange); err != nil { +- return err +- } +- return s.warnAboutModifyingGeneratedFiles(ctx, uri) -} - -diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go b/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go ---- a/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package crosspkg +-// warnAboutModifyingGeneratedFiles shows a warning if a user tries to edit a +-// generated file for the first time. +-func (s *Server) warnAboutModifyingGeneratedFiles(ctx context.Context, uri span.URI) error { +- s.changedFilesMu.Lock() +- _, ok := s.changedFiles[uri] +- if !ok { +- s.changedFiles[uri] = struct{}{} +- } +- s.changedFilesMu.Unlock() - --func Foo() { //@rename("Foo", "Dolphin") +- // This file has already been edited before. +- if ok { +- return nil +- } - --} +- // Ideally, we should be able to specify that a generated file should +- // be opened as read-only. Tell the user that they should not be +- // editing a generated file. +- view, err := s.session.ViewOf(uri) +- if err != nil { +- return err +- } +- snapshot, release, err := view.Snapshot() +- if err != nil { +- return err +- } +- isGenerated := source.IsGenerated(ctx, snapshot, uri) +- release() - --var Bar int //@rename("Bar", "Tomato") -diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go.golden b/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go.golden ---- a/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/crosspkg/crosspkg.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,40 +0,0 @@ ---- Dolphin-rename -- --crosspkg.go: --package crosspkg +- if !isGenerated { +- return nil +- } +- return s.client.ShowMessage(ctx, &protocol.ShowMessageParams{ +- Message: fmt.Sprintf("Do not edit this file! %s is a generated file.", uri.Filename()), +- Type: protocol.Warning, +- }) +-} - --func Dolphin() { //@rename("Foo", "Dolphin") +-func (s *Server) didChangeWatchedFiles(ctx context.Context, params *protocol.DidChangeWatchedFilesParams) error { +- ctx, done := event.Start(ctx, "lsp.Server.didChangeWatchedFiles") +- defer done() - +- var modifications []source.FileModification +- for _, change := range params.Changes { +- uri := change.URI.SpanURI() +- if !uri.IsFile() { +- continue +- } +- action := changeTypeToFileAction(change.Type) +- modifications = append(modifications, source.FileModification{ +- URI: uri, +- Action: action, +- OnDisk: true, +- }) +- } +- return s.didModifyFiles(ctx, modifications, FromDidChangeWatchedFiles) -} - --var Bar int //@rename("Bar", "Tomato") +-func (s *Server) didSave(ctx context.Context, params *protocol.DidSaveTextDocumentParams) error { +- ctx, done := event.Start(ctx, "lsp.Server.didSave", tag.URI.Of(params.TextDocument.URI)) +- defer done() - --other.go: --package other +- uri := params.TextDocument.URI.SpanURI() +- if !uri.IsFile() { +- return nil +- } +- c := source.FileModification{ +- URI: uri, +- Action: source.Save, +- } +- if params.Text != nil { +- c.Text = []byte(*params.Text) +- } +- return s.didModifyFiles(ctx, []source.FileModification{c}, FromDidSave) +-} - --import "golang.org/lsptests/rename/crosspkg" +-func (s *Server) didClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error { +- ctx, done := event.Start(ctx, "lsp.Server.didClose", tag.URI.Of(params.TextDocument.URI)) +- defer done() - --func Other() { -- crosspkg.Bar -- crosspkg.Dolphin() //@rename("Foo", "Flamingo") +- uri := params.TextDocument.URI.SpanURI() +- if !uri.IsFile() { +- return nil +- } +- return s.didModifyFiles(ctx, []source.FileModification{ +- { +- URI: uri, +- Action: source.Close, +- Version: -1, +- Text: nil, +- }, +- }, FromDidClose) -} - ---- Tomato-rename -- --crosspkg.go: --package crosspkg +-func (s *Server) didModifyFiles(ctx context.Context, modifications []source.FileModification, cause ModificationSource) error { +- // wg guards two conditions: +- // 1. didModifyFiles is complete +- // 2. the goroutine diagnosing changes on behalf of didModifyFiles is +- // complete, if it was started +- // +- // Both conditions must be satisfied for the purpose of testing: we don't +- // want to observe the completion of change processing until we have received +- // all diagnostics as well as all server->client notifications done on behalf +- // of this function. +- var wg sync.WaitGroup +- wg.Add(1) +- defer wg.Done() - --func Foo() { //@rename("Foo", "Dolphin") +- if s.Options().VerboseWorkDoneProgress { +- work := s.progress.Start(ctx, DiagnosticWorkTitle(cause), "Calculating file diagnostics...", nil, nil) +- go func() { +- wg.Wait() +- work.End(ctx, "Done.") +- }() +- } - --} +- onDisk := cause == FromDidChangeWatchedFiles - --var Tomato int //@rename("Bar", "Tomato") +- s.stateMu.Lock() +- if s.state >= serverShutDown { +- // This state check does not prevent races below, and exists only to +- // produce a better error message. The actual race to the cache should be +- // guarded by Session.viewMu. +- s.stateMu.Unlock() +- return errors.New("server is shut down") +- } +- s.stateMu.Unlock() - --other.go: --package other +- // If the set of changes included directories, expand those directories +- // to their files. +- modifications = s.session.ExpandModificationsToDirectories(ctx, modifications) - --import "golang.org/lsptests/rename/crosspkg" +- // Build a lookup map for file modifications, so that we can later join +- // with the snapshot file associations. +- modMap := make(map[span.URI]source.FileModification) +- for _, mod := range modifications { +- modMap[mod.URI] = mod +- } - --func Other() { -- crosspkg.Tomato -- crosspkg.Foo() //@rename("Foo", "Flamingo") --} +- snapshots, release, err := s.session.DidModifyFiles(ctx, modifications) +- if err != nil { +- return err +- } - -diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go b/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go ---- a/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package other +- // golang/go#50267: diagnostics should be re-sent after an open or close. For +- // some clients, it may be helpful to re-send after each change. +- for snapshot, uris := range snapshots { +- for _, uri := range uris { +- mod := modMap[uri] +- if snapshot.Options().ChattyDiagnostics || mod.Action == source.Open || mod.Action == source.Close { +- s.mustPublishDiagnostics(uri) +- } +- } +- } - --import "golang.org/lsptests/rename/crosspkg" +- wg.Add(1) +- go func() { +- s.diagnoseSnapshots(snapshots, onDisk) +- release() +- wg.Done() +- }() - --func Other() { -- crosspkg.Bar -- crosspkg.Foo() //@rename("Foo", "Flamingo") +- // After any file modifications, we need to update our watched files, +- // in case something changed. Compute the new set of directories to watch, +- // and if it differs from the current set, send updated registrations. +- return s.updateWatchedDirectories(ctx) -} -diff -urN a/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go.golden b/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go.golden ---- a/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/crosspkg/other/other.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,20 +0,0 @@ ---- Flamingo-rename -- --crosspkg.go: --package crosspkg -- --func Flamingo() { //@rename("Foo", "Dolphin") - +-// DiagnosticWorkTitle returns the title of the diagnostic work resulting from a +-// file change originating from the given cause. +-func DiagnosticWorkTitle(cause ModificationSource) string { +- return fmt.Sprintf("diagnosing %v", cause) -} - --var Bar int //@rename("Bar", "Tomato") +-func (s *Server) changedText(ctx context.Context, uri span.URI, changes []protocol.TextDocumentContentChangeEvent) ([]byte, error) { +- if len(changes) == 0 { +- return nil, fmt.Errorf("%w: no content changes provided", jsonrpc2.ErrInternal) +- } - --other.go: --package other +- // Check if the client sent the full content of the file. +- // We accept a full content change even if the server expected incremental changes. +- if len(changes) == 1 && changes[0].Range == nil && changes[0].RangeLength == 0 { +- return []byte(changes[0].Text), nil +- } +- return s.applyIncrementalChanges(ctx, uri, changes) +-} - --import "golang.org/lsptests/rename/crosspkg" +-func (s *Server) applyIncrementalChanges(ctx context.Context, uri span.URI, changes []protocol.TextDocumentContentChangeEvent) ([]byte, error) { +- fh, err := s.session.ReadFile(ctx, uri) +- if err != nil { +- return nil, err +- } +- content, err := fh.Content() +- if err != nil { +- return nil, fmt.Errorf("%w: file not found (%v)", jsonrpc2.ErrInternal, err) +- } +- for _, change := range changes { +- // TODO(adonovan): refactor to use diff.Apply, which is robust w.r.t. +- // out-of-order or overlapping changes---and much more efficient. - --func Other() { -- crosspkg.Bar -- crosspkg.Flamingo() //@rename("Foo", "Flamingo") +- // Make sure to update mapper along with the content. +- m := protocol.NewMapper(uri, content) +- if change.Range == nil { +- return nil, fmt.Errorf("%w: unexpected nil range for change", jsonrpc2.ErrInternal) +- } +- spn, err := m.RangeSpan(*change.Range) +- if err != nil { +- return nil, err +- } +- start, end := spn.Start().Offset(), spn.End().Offset() +- if end < start { +- return nil, fmt.Errorf("%w: invalid range for content change", jsonrpc2.ErrInternal) +- } +- var buf bytes.Buffer +- buf.Write(content[:start]) +- buf.WriteString(change.Text) +- buf.Write(content[end:]) +- content = buf.Bytes() +- } +- return content, nil -} - -diff -urN a/gopls/internal/lsp/testdata/rename/generics/embedded.go b/gopls/internal/lsp/testdata/rename/generics/embedded.go ---- a/gopls/internal/lsp/testdata/rename/generics/embedded.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/generics/embedded.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --//go:build go1.18 --// +build go1.18 +-func changeTypeToFileAction(ct protocol.FileChangeType) source.FileAction { +- switch ct { +- case protocol.Changed: +- return source.Change +- case protocol.Created: +- return source.Create +- case protocol.Deleted: +- return source.Delete +- } +- return source.UnknownFileAction +-} +diff -urN a/gopls/internal/lsp/work/completion.go b/gopls/internal/lsp/work/completion.go +--- a/gopls/internal/lsp/work/completion.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/work/completion.go 1970-01-01 08:00:00 +@@ -1,154 +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 generics +-package work - --type foo[P any] int //@rename("foo","bar") +-import ( +- "context" +- "errors" +- "fmt" +- "os" +- "path/filepath" +- "sort" +- "strings" - --var x struct{ foo[int] } +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/internal/event" +-) - --var _ = x.foo -diff -urN a/gopls/internal/lsp/testdata/rename/generics/embedded.go.golden b/gopls/internal/lsp/testdata/rename/generics/embedded.go.golden ---- a/gopls/internal/lsp/testdata/rename/generics/embedded.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/generics/embedded.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ ---- bar-rename -- --//go:build go1.18 --// +build go1.18 +-func Completion(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, position protocol.Position) (*protocol.CompletionList, error) { +- ctx, done := event.Start(ctx, "work.Completion") +- defer done() - --package generics +- // Get the position of the cursor. +- pw, err := snapshot.ParseWork(ctx, fh) +- if err != nil { +- return nil, fmt.Errorf("getting go.work file handle: %w", err) +- } +- cursor, err := pw.Mapper.PositionOffset(position) +- if err != nil { +- return nil, fmt.Errorf("computing cursor offset: %w", err) +- } - --type bar[P any] int //@rename("foo","bar") +- // Find the use statement the user is in. +- use, pathStart, _ := usePath(pw, cursor) +- if use == nil { +- return &protocol.CompletionList{}, nil +- } +- completingFrom := use.Path[:cursor-pathStart] - --var x struct{ bar[int] } +- // We're going to find the completions of the user input +- // (completingFrom) by doing a walk on the innermost directory +- // of the given path, and comparing the found paths to make sure +- // that they match the component of the path after the +- // innermost directory. +- // +- // We'll maintain two paths when doing this: pathPrefixSlash +- // is essentially the path the user typed in, and pathPrefixAbs +- // is the path made absolute from the go.work directory. - --var _ = x.bar +- pathPrefixSlash := completingFrom +- pathPrefixAbs := filepath.FromSlash(pathPrefixSlash) +- if !filepath.IsAbs(pathPrefixAbs) { +- pathPrefixAbs = filepath.Join(filepath.Dir(pw.URI.Filename()), pathPrefixAbs) +- } - -diff -urN a/gopls/internal/lsp/testdata/rename/generics/generics.go b/gopls/internal/lsp/testdata/rename/generics/generics.go ---- a/gopls/internal/lsp/testdata/rename/generics/generics.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/generics/generics.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,25 +0,0 @@ --//go:build go1.18 --// +build go1.18 +- // pathPrefixDir is the directory that will be walked to find matches. +- // If pathPrefixSlash is not explicitly a directory boundary (is either equivalent to "." or +- // ends in a separator) we need to examine its parent directory to find sibling files that +- // match. +- depthBound := 5 +- pathPrefixDir, pathPrefixBase := pathPrefixAbs, "" +- pathPrefixSlashDir := pathPrefixSlash +- if filepath.Clean(pathPrefixSlash) != "." && !strings.HasSuffix(pathPrefixSlash, "/") { +- depthBound++ +- pathPrefixDir, pathPrefixBase = filepath.Split(pathPrefixAbs) +- pathPrefixSlashDir = dirNonClean(pathPrefixSlash) +- } - --package generics +- var completions []string +- // Stop traversing deeper once we've hit 10k files to try to stay generally under 100ms. +- const numSeenBound = 10000 +- var numSeen int +- stopWalking := errors.New("hit numSeenBound") +- err = filepath.Walk(pathPrefixDir, func(wpath string, info os.FileInfo, err error) error { +- if numSeen > numSeenBound { +- // Stop traversing if we hit bound. +- return stopWalking +- } +- numSeen++ - --type G[P any] struct { -- F int --} +- // rel is the path relative to pathPrefixDir. +- // Make sure that it has pathPrefixBase as a prefix +- // otherwise it won't match the beginning of the +- // base component of the path the user typed in. +- rel := strings.TrimPrefix(wpath[len(pathPrefixDir):], string(filepath.Separator)) +- if info.IsDir() && wpath != pathPrefixDir && !strings.HasPrefix(rel, pathPrefixBase) { +- return filepath.SkipDir +- } - --func (G[_]) M() {} +- // Check for a match (a module directory). +- if filepath.Base(rel) == "go.mod" { +- relDir := strings.TrimSuffix(dirNonClean(rel), string(os.PathSeparator)) +- completionPath := join(pathPrefixSlashDir, filepath.ToSlash(relDir)) - --func F[P any](P) { -- var p P //@rename("P", "Q") -- _ = p --} +- if !strings.HasPrefix(completionPath, completingFrom) { +- return nil +- } +- if strings.HasSuffix(completionPath, "/") { +- // Don't suggest paths that end in "/". This happens +- // when the input is a path that ends in "/" and +- // the completion is empty. +- return nil +- } +- completion := completionPath[len(completingFrom):] +- if completingFrom == "" && !strings.HasPrefix(completion, "./") { +- // Bias towards "./" prefixes. +- completion = join(".", completion) +- } - --func _() { -- var x G[int] //@rename("G", "H") -- _ = x.F //@rename("F", "K") -- x.M() //@rename("M", "N") +- completions = append(completions, completion) +- } - -- var y G[string] -- _ = y.F -- y.M() --} -diff -urN a/gopls/internal/lsp/testdata/rename/generics/generics.go.golden b/gopls/internal/lsp/testdata/rename/generics/generics.go.golden ---- a/gopls/internal/lsp/testdata/rename/generics/generics.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/generics/generics.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,108 +0,0 @@ ---- H-rename -- --//go:build go1.18 --// +build go1.18 +- if depth := strings.Count(rel, string(filepath.Separator)); depth >= depthBound { +- return filepath.SkipDir +- } +- return nil +- }) +- if err != nil && !errors.Is(err, stopWalking) { +- return nil, fmt.Errorf("walking to find completions: %w", err) +- } - --package generics +- sort.Strings(completions) - --type H[P any] struct { -- F int +- items := []protocol.CompletionItem{} // must be a slice +- for _, c := range completions { +- items = append(items, protocol.CompletionItem{ +- Label: c, +- InsertText: c, +- }) +- } +- return &protocol.CompletionList{Items: items}, nil -} - --func (H[_]) M() {} -- --func F[P any](P) { -- var p P //@rename("P", "Q") -- _ = p +-// dirNonClean is filepath.Dir, without the Clean at the end. +-func dirNonClean(path string) string { +- vol := filepath.VolumeName(path) +- i := len(path) - 1 +- for i >= len(vol) && !os.IsPathSeparator(path[i]) { +- i-- +- } +- return path[len(vol) : i+1] -} - --func _() { -- var x H[int] //@rename("G", "H") -- _ = x.F //@rename("F", "K") -- x.M() //@rename("M", "N") -- -- var y H[string] -- _ = y.F -- y.M() +-func join(a, b string) string { +- if a == "" { +- return b +- } +- if b == "" { +- return a +- } +- return strings.TrimSuffix(a, "/") + "/" + b -} +diff -urN a/gopls/internal/lsp/work/diagnostics.go b/gopls/internal/lsp/work/diagnostics.go +--- a/gopls/internal/lsp/work/diagnostics.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/work/diagnostics.go 1970-01-01 08:00:00 +@@ -1,92 +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. - ---- K-rename -- --//go:build go1.18 --// +build go1.18 -- --package generics +-package work - --type G[P any] struct { -- K int --} +-import ( +- "context" +- "fmt" +- "os" +- "path/filepath" - --func (G[_]) M() {} +- "golang.org/x/mod/modfile" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/event" +-) - --func F[P any](P) { -- var p P //@rename("P", "Q") -- _ = p --} +-func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[span.URI][]*source.Diagnostic, error) { +- ctx, done := event.Start(ctx, "work.Diagnostics", source.SnapshotLabels(snapshot)...) +- defer done() - --func _() { -- var x G[int] //@rename("G", "H") -- _ = x.K //@rename("F", "K") -- x.M() //@rename("M", "N") +- reports := map[span.URI][]*source.Diagnostic{} +- uri := snapshot.WorkFile() +- if uri == "" { +- return nil, nil +- } +- fh, err := snapshot.ReadFile(ctx, uri) +- if err != nil { +- return nil, err +- } +- reports[fh.URI()] = []*source.Diagnostic{} +- diagnostics, err := DiagnosticsForWork(ctx, snapshot, fh) +- if err != nil { +- return nil, err +- } +- for _, d := range diagnostics { +- fh, err := snapshot.ReadFile(ctx, d.URI) +- if err != nil { +- return nil, err +- } +- reports[fh.URI()] = append(reports[fh.URI()], d) +- } - -- var y G[string] -- _ = y.K -- y.M() +- return reports, nil -} - ---- N-rename -- --//go:build go1.18 --// +build go1.18 -- --package generics -- --type G[P any] struct { -- F int --} +-func DiagnosticsForWork(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]*source.Diagnostic, error) { +- pw, err := snapshot.ParseWork(ctx, fh) +- if err != nil { +- if pw == nil || len(pw.ParseErrors) == 0 { +- return nil, err +- } +- return pw.ParseErrors, nil +- } - --func (G[_]) N() {} +- // Add diagnostic if a directory does not contain a module. +- var diagnostics []*source.Diagnostic +- for _, use := range pw.File.Use { +- rng, err := pw.Mapper.OffsetRange(use.Syntax.Start.Byte, use.Syntax.End.Byte) +- if err != nil { +- return nil, err +- } - --func F[P any](P) { -- var p P //@rename("P", "Q") -- _ = p +- modfh, err := snapshot.ReadFile(ctx, modFileURI(pw, use)) +- if err != nil { +- return nil, err +- } +- if _, err := modfh.Content(); err != nil && os.IsNotExist(err) { +- diagnostics = append(diagnostics, &source.Diagnostic{ +- URI: fh.URI(), +- Range: rng, +- Severity: protocol.SeverityError, +- Source: source.WorkFileError, +- Message: fmt.Sprintf("directory %v does not contain a module", use.Path), +- }) +- } +- } +- return diagnostics, nil -} - --func _() { -- var x G[int] //@rename("G", "H") -- _ = x.F //@rename("F", "K") -- x.N() //@rename("M", "N") +-func modFileURI(pw *source.ParsedWorkFile, use *modfile.Use) span.URI { +- workdir := filepath.Dir(pw.URI.Filename()) - -- var y G[string] -- _ = y.F -- y.N() +- modroot := filepath.FromSlash(use.Path) +- if !filepath.IsAbs(modroot) { +- modroot = filepath.Join(workdir, modroot) +- } +- +- return span.URIFromPath(filepath.Join(modroot, "go.mod")) -} +diff -urN a/gopls/internal/lsp/work/format.go b/gopls/internal/lsp/work/format.go +--- a/gopls/internal/lsp/work/format.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/work/format.go 1970-01-01 08:00:00 +@@ -1,28 +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. - ---- Q-rename -- --//go:build go1.18 --// +build go1.18 +-package work - --package generics +-import ( +- "context" - --type G[P any] struct { -- F int --} +- "golang.org/x/mod/modfile" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/internal/event" +-) - --func (G[_]) M() {} +-func Format(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.TextEdit, error) { +- ctx, done := event.Start(ctx, "work.Format") +- defer done() - --func F[Q any](Q) { -- var p Q //@rename("P", "Q") -- _ = p +- pw, err := snapshot.ParseWork(ctx, fh) +- if err != nil { +- return nil, err +- } +- formatted := modfile.Format(pw.File.Syntax) +- // Calculate the edits to be made due to the change. +- diffs := snapshot.Options().ComputeEdits(string(pw.Mapper.Content), string(formatted)) +- return source.ToProtocolEdits(pw.Mapper, diffs) -} +diff -urN a/gopls/internal/lsp/work/hover.go b/gopls/internal/lsp/work/hover.go +--- a/gopls/internal/lsp/work/hover.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/work/hover.go 1970-01-01 08:00:00 +@@ -1,92 +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. - --func _() { -- var x G[int] //@rename("G", "H") -- _ = x.F //@rename("F", "K") -- x.M() //@rename("M", "N") +-package work - -- var y G[string] -- _ = y.F -- y.M() --} +-import ( +- "bytes" +- "context" +- "fmt" - -diff -urN a/gopls/internal/lsp/testdata/rename/generics/unions.go b/gopls/internal/lsp/testdata/rename/generics/unions.go ---- a/gopls/internal/lsp/testdata/rename/generics/unions.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/generics/unions.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --//go:build go1.18 --// +build go1.18 +- "golang.org/x/mod/modfile" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/internal/event" +-) - --package generics +-func Hover(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, position protocol.Position) (*protocol.Hover, error) { +- // We only provide hover information for the view's go.work file. +- if fh.URI() != snapshot.WorkFile() { +- return nil, nil +- } - --type T string //@rename("T", "R") +- ctx, done := event.Start(ctx, "work.Hover") +- defer done() - --type C interface { -- T | ~int //@rename("T", "S") --} -diff -urN a/gopls/internal/lsp/testdata/rename/generics/unions.go.golden b/gopls/internal/lsp/testdata/rename/generics/unions.go.golden ---- a/gopls/internal/lsp/testdata/rename/generics/unions.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/generics/unions.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ ---- R-rename -- --//go:build go1.18 --// +build go1.18 +- // Get the position of the cursor. +- pw, err := snapshot.ParseWork(ctx, fh) +- if err != nil { +- return nil, fmt.Errorf("getting go.work file handle: %w", err) +- } +- offset, err := pw.Mapper.PositionOffset(position) +- if err != nil { +- return nil, fmt.Errorf("computing cursor offset: %w", err) +- } - --package generics +- // Confirm that the cursor is inside a use statement, and then find +- // the position of the use statement's directory path. +- use, pathStart, pathEnd := usePath(pw, offset) - --type R string //@rename("T", "R") +- // The cursor position is not on a use statement. +- if use == nil { +- return nil, nil +- } - --type C interface { -- R | ~int //@rename("T", "S") --} +- // Get the mod file denoted by the use. +- modfh, err := snapshot.ReadFile(ctx, modFileURI(pw, use)) +- if err != nil { +- return nil, fmt.Errorf("getting modfile handle: %w", err) +- } +- pm, err := snapshot.ParseMod(ctx, modfh) +- if err != nil { +- return nil, fmt.Errorf("getting modfile handle: %w", err) +- } +- if pm.File.Module == nil { +- return nil, fmt.Errorf("modfile has no module declaration") +- } +- mod := pm.File.Module.Mod - ---- S-rename -- --//go:build go1.18 --// +build go1.18 +- // Get the range to highlight for the hover. +- rng, err := pw.Mapper.OffsetRange(pathStart, pathEnd) +- if err != nil { +- return nil, err +- } +- options := snapshot.Options() +- return &protocol.Hover{ +- Contents: protocol.MarkupContent{ +- Kind: options.PreferredContentFormat, +- Value: mod.Path, +- }, +- Range: rng, +- }, nil +-} - --package generics +-func usePath(pw *source.ParsedWorkFile, offset int) (use *modfile.Use, pathStart, pathEnd int) { +- for _, u := range pw.File.Use { +- path := []byte(u.Path) +- s, e := u.Syntax.Start.Byte, u.Syntax.End.Byte +- i := bytes.Index(pw.Mapper.Content[s:e], path) +- if i == -1 { +- // This should not happen. +- continue +- } +- // Shift the start position to the location of the +- // module directory within the use statement. +- pathStart, pathEnd = s+i, s+i+len(path) +- if pathStart <= offset && offset <= pathEnd { +- return u, pathStart, pathEnd +- } +- } +- return nil, 0, 0 +-} +diff -urN a/gopls/internal/lsp/workspace.go b/gopls/internal/lsp/workspace.go +--- a/gopls/internal/lsp/workspace.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/workspace.go 1970-01-01 08:00:00 +@@ -1,100 +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. - --type S string //@rename("T", "R") +-package lsp - --type C interface { -- S | ~int //@rename("T", "S") --} +-import ( +- "context" +- "fmt" +- "sync" - -diff -urN a/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.golden b/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.golden ---- a/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ ---- bar-rename -- --package issue39614 +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/internal/event" +-) - --func fn() { -- var bar bool //@rename("foo","bar") -- make(map[string]bool -- if true { +-func (s *Server) didChangeWorkspaceFolders(ctx context.Context, params *protocol.DidChangeWorkspaceFoldersParams) error { +- event := params.Event +- for _, folder := range event.Removed { +- view := s.session.ViewByName(folder.Name) +- if view != nil { +- s.session.RemoveView(view) +- } else { +- return fmt.Errorf("view %s for %v not found", folder.Name, folder.URI) +- } - } +- return s.addFolders(ctx, event.Added) -} - -diff -urN a/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.in b/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.in ---- a/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue39614/issue39614.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package issue39614 -- --func fn() { -- var foo bool //@rename("foo","bar") -- make(map[string]bool -- if true { +-// addView returns a Snapshot and a release function that must be +-// called when it is no longer needed. +-func (s *Server) addView(ctx context.Context, name string, uri span.URI) (source.Snapshot, func(), error) { +- s.stateMu.Lock() +- state := s.state +- s.stateMu.Unlock() +- if state < serverInitialized { +- return nil, nil, fmt.Errorf("addView called before server initialized") +- } +- options, err := s.fetchFolderOptions(ctx, uri) +- if err != nil { +- return nil, nil, err - } +- _, snapshot, release, err := s.session.NewView(ctx, name, uri, options) +- return snapshot, release, err -} -diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/1.go b/gopls/internal/lsp/testdata/rename/issue42134/1.go ---- a/gopls/internal/lsp/testdata/rename/issue42134/1.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue42134/1.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package issue42134 - --func _() { -- // foo computes things. -- foo := func() {} +-func (s *Server) didChangeConfiguration(ctx context.Context, _ *protocol.DidChangeConfigurationParams) error { +- ctx, done := event.Start(ctx, "lsp.Server.didChangeConfiguration") +- defer done() - -- foo() //@rename("foo", "bar") --} -diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/1.go.golden b/gopls/internal/lsp/testdata/rename/issue42134/1.go.golden ---- a/gopls/internal/lsp/testdata/rename/issue42134/1.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue42134/1.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ ---- bar-rename -- --package issue42134 +- // Apply any changes to the session-level settings. +- options, err := s.fetchFolderOptions(ctx, "") +- if err != nil { +- return err +- } +- s.SetOptions(options) - --func _() { -- // bar computes things. -- bar := func() {} +- // Collect options for all workspace folders. +- seen := make(map[span.URI]bool) +- for _, view := range s.session.Views() { +- if seen[view.Folder()] { +- continue +- } +- seen[view.Folder()] = true +- options, err := s.fetchFolderOptions(ctx, view.Folder()) +- if err != nil { +- return err +- } +- s.session.SetFolderOptions(ctx, view.Folder(), options) +- } - -- bar() //@rename("foo", "bar") --} +- var wg sync.WaitGroup +- for _, view := range s.session.Views() { +- view := view +- wg.Add(1) +- go func() { +- defer wg.Done() +- snapshot, release, err := view.Snapshot() +- if err != nil { +- return // view is shut down; no need to diagnose +- } +- defer release() +- s.diagnoseSnapshot(snapshot, nil, false, 0) +- }() +- } - -diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/2.go b/gopls/internal/lsp/testdata/rename/issue42134/2.go ---- a/gopls/internal/lsp/testdata/rename/issue42134/2.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue42134/2.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ --package issue42134 +- if s.Options().VerboseWorkDoneProgress { +- work := s.progress.Start(ctx, DiagnosticWorkTitle(FromDidChangeConfiguration), "Calculating diagnostics...", nil, nil) +- go func() { +- wg.Wait() +- work.End(ctx, "Done.") +- }() +- } - --import "fmt" +- // An options change may have affected the detected Go version. +- s.checkViewGoVersions() - --func _() { -- // minNumber is a min number. -- // Second line. -- minNumber := min(1, 2) -- fmt.Println(minNumber) //@rename("minNumber", "res") +- return nil -} +diff -urN a/gopls/internal/lsp/workspace_symbol.go b/gopls/internal/lsp/workspace_symbol.go +--- a/gopls/internal/lsp/workspace_symbol.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/lsp/workspace_symbol.go 1970-01-01 08:00:00 +@@ -1,38 +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. - --func min(a, b int) int { return a } -diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/2.go.golden b/gopls/internal/lsp/testdata/rename/issue42134/2.go.golden ---- a/gopls/internal/lsp/testdata/rename/issue42134/2.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue42134/2.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ ---- res-rename -- --package issue42134 -- --import "fmt" +-package lsp - --func _() { -- // res is a min number. -- // Second line. -- res := min(1, 2) -- fmt.Println(res) //@rename("minNumber", "res") --} +-import ( +- "context" - --func min(a, b int) int { return a } +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/source" +- "golang.org/x/tools/gopls/internal/telemetry" +- "golang.org/x/tools/internal/event" +-) - -diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/3.go b/gopls/internal/lsp/testdata/rename/issue42134/3.go ---- a/gopls/internal/lsp/testdata/rename/issue42134/3.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue42134/3.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package issue42134 +-func (s *Server) symbol(ctx context.Context, params *protocol.WorkspaceSymbolParams) (_ []protocol.SymbolInformation, rerr error) { +- recordLatency := telemetry.StartLatencyTimer("symbol") +- defer func() { +- recordLatency(ctx, rerr) +- }() - --func _() { -- /* -- tests contains test cases -- */ -- tests := []struct { //@rename("tests", "testCases") -- in, out string -- }{} -- _ = tests --} -diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/3.go.golden b/gopls/internal/lsp/testdata/rename/issue42134/3.go.golden ---- a/gopls/internal/lsp/testdata/rename/issue42134/3.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue42134/3.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ ---- testCases-rename -- --package issue42134 +- ctx, done := event.Start(ctx, "lsp.Server.symbol") +- defer done() - --func _() { -- /* -- testCases contains test cases -- */ -- testCases := []struct { //@rename("tests", "testCases") -- in, out string -- }{} -- _ = testCases +- views := s.session.Views() +- matcher := s.Options().SymbolMatcher +- style := s.Options().SymbolStyle +- // TODO(rfindley): it looks wrong that we need to pass views here. +- // +- // Evidence: +- // - this is the only place we convert views to []source.View +- // - workspace symbols is the only place where we call source.View.Snapshot +- var sourceViews []source.View +- for _, v := range views { +- sourceViews = append(sourceViews, v) +- } +- return source.WorkspaceSymbols(ctx, matcher, style, sourceViews, params.Query) -} +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 08:00:00 +@@ -1,351 +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. - -diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/4.go b/gopls/internal/lsp/testdata/rename/issue42134/4.go ---- a/gopls/internal/lsp/testdata/rename/issue42134/4.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue42134/4.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package issue42134 -- --func _() { -- // a is equal to 5. Comment must stay the same +-package bench - -- a := 5 -- _ = a //@rename("a", "b") --} -diff -urN a/gopls/internal/lsp/testdata/rename/issue42134/4.go.golden b/gopls/internal/lsp/testdata/rename/issue42134/4.go.golden ---- a/gopls/internal/lsp/testdata/rename/issue42134/4.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue42134/4.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ ---- b-rename -- --package issue42134 +-import ( +- "bytes" +- "compress/gzip" +- "context" +- "flag" +- "fmt" +- "io" +- "log" +- "os" +- "os/exec" +- "path/filepath" +- "strings" +- "sync" +- "testing" +- "time" - --func _() { -- // a is equal to 5. Comment must stay the same +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/hooks" +- "golang.org/x/tools/gopls/internal/lsp/cmd" +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/fake" +- "golang.org/x/tools/gopls/internal/lsp/regtest" +- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/fakenet" +- "golang.org/x/tools/internal/jsonrpc2" +- "golang.org/x/tools/internal/jsonrpc2/servertest" +- "golang.org/x/tools/internal/pprof" +- "golang.org/x/tools/internal/tool" +-) - -- b := 5 -- _ = b //@rename("a", "b") --} +-var ( +- goplsPath = flag.String("gopls_path", "", "if set, use this gopls for testing; incompatible with -gopls_commit") - -diff -urN a/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.golden b/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.golden ---- a/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ ---- bar-rename -- --package issue43616 +- installGoplsOnce sync.Once // guards installing gopls at -gopls_commit +- goplsCommit = flag.String("gopls_commit", "", "if set, install and use gopls at this commit for testing; incompatible with -gopls_path") - --type bar int //@rename("foo","bar"),prepare("oo","foo","foo") +- cpuProfile = flag.String("gopls_cpuprofile", "", "if set, the cpu profile file suffix; see \"Profiling\" in the package doc") +- memProfile = flag.String("gopls_memprofile", "", "if set, the mem profile file suffix; see \"Profiling\" in the package doc") +- allocProfile = flag.String("gopls_allocprofile", "", "if set, the alloc profile file suffix; see \"Profiling\" in the package doc") +- trace = flag.String("gopls_trace", "", "if set, the trace file suffix; see \"Profiling\" in the package doc") - --var x struct{ bar } //@rename("foo","baz") +- // If non-empty, tempDir is a temporary working dir that was created by this +- // test suite. +- makeTempDirOnce sync.Once // guards creation of the temp dir +- tempDir string +-) - --var _ = x.bar //@rename("foo","quux") +-// if runAsGopls is "true", run the gopls command instead of the testing.M. +-const runAsGopls = "_GOPLS_BENCH_RUN_AS_GOPLS" - ---- baz-rename -- --can't rename embedded fields: rename the type directly or name the field ---- quux-rename -- --can't rename embedded fields: rename the type directly or name the field -diff -urN a/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.in b/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.in ---- a/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/issue43616/issue43616.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package issue43616 +-func TestMain(m *testing.M) { +- bug.PanicOnBugs = true +- if os.Getenv(runAsGopls) == "true" { +- tool.Main(context.Background(), cmd.New("gopls", "", nil, hooks.Options), os.Args[1:]) +- os.Exit(0) +- } +- event.SetExporter(nil) // don't log to stderr +- code := m.Run() +- if err := cleanup(); err != nil { +- fmt.Fprintf(os.Stderr, "cleaning up after benchmarks: %v\n", err) +- if code == 0 { +- code = 1 +- } +- } +- os.Exit(code) +-} - --type foo int //@rename("foo","bar"),prepare("oo","foo","foo") +-// getTempDir returns the temporary directory to use for benchmark files, +-// creating it if necessary. +-func getTempDir() string { +- makeTempDirOnce.Do(func() { +- var err error +- tempDir, err = os.MkdirTemp("", "gopls-bench") +- if err != nil { +- log.Fatal(err) +- } +- }) +- return tempDir +-} - --var x struct{ foo } //@rename("foo","baz") +-// shallowClone performs a shallow clone of repo into dir at the given +-// 'commitish' ref (any commit reference understood by git). +-// +-// The directory dir must not already exist. +-func shallowClone(dir, repo, commitish string) error { +- if err := os.Mkdir(dir, 0750); err != nil { +- return fmt.Errorf("creating dir for %s: %v", repo, err) +- } - --var _ = x.foo //@rename("foo","quux") -diff -urN a/gopls/internal/lsp/testdata/rename/shadow/shadow.go b/gopls/internal/lsp/testdata/rename/shadow/shadow.go ---- a/gopls/internal/lsp/testdata/rename/shadow/shadow.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/shadow/shadow.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,20 +0,0 @@ --package shadow +- // Set a timeout for git fetch. If this proves flaky, it can be removed. +- ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) +- defer cancel() - --func _() { -- a := true -- b, c, _ := A(), B(), D() //@rename("A", "a"),rename("B", "b"),rename("b", "c"),rename("D", "d") -- d := false -- _, _, _, _ = a, b, c, d +- // Use a shallow fetch to download just the relevant commit. +- shInit := fmt.Sprintf("git init && git fetch --depth=1 %q %q && git checkout FETCH_HEAD", repo, commitish) +- initCmd := exec.CommandContext(ctx, "/bin/sh", "-c", shInit) +- initCmd.Dir = dir +- if output, err := initCmd.CombinedOutput(); err != nil { +- return fmt.Errorf("checking out %s: %v\n%s", repo, err, output) +- } +- return nil -} - --func A() int { -- return 0 --} +-// connectEditor connects a fake editor session in the given dir, using the +-// given editor config. +-func connectEditor(dir string, config fake.EditorConfig, ts servertest.Connector) (*fake.Sandbox, *fake.Editor, *regtest.Awaiter, error) { +- s, err := fake.NewSandbox(&fake.SandboxConfig{ +- Workdir: dir, +- GOPROXY: "https://proxy.golang.org", +- }) +- if err != nil { +- return nil, nil, nil, err +- } - --func B() int { -- return 0 --} +- a := regtest.NewAwaiter(s.Workdir) +- const skipApplyEdits = false +- editor, err := fake.NewEditor(s, config).Connect(context.Background(), ts, a.Hooks(), skipApplyEdits) +- if err != nil { +- return nil, nil, nil, err +- } - --func D() int { -- return 0 +- return s, editor, a, nil -} -diff -urN a/gopls/internal/lsp/testdata/rename/shadow/shadow.go.golden b/gopls/internal/lsp/testdata/rename/shadow/shadow.go.golden ---- a/gopls/internal/lsp/testdata/rename/shadow/shadow.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/shadow/shadow.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,51 +0,0 @@ ---- a-rename -- --shadow/shadow.go:10:6: renaming this func "A" to "a" --shadow/shadow.go:5:13: would cause this reference to become shadowed --shadow/shadow.go:4:2: by this intervening var definition ---- b-rename -- --package shadow - --func _() { -- a := true -- b, c, _ := A(), b(), D() //@rename("A", "a"),rename("B", "b"),rename("b", "c"),rename("D", "d") -- d := false -- _, _, _, _ = a, b, c, d +-// newGoplsConnector returns a connector that connects to a new gopls process, +-// executed with the provided arguments. +-func newGoplsConnector(args []string) (servertest.Connector, error) { +- if *goplsPath != "" && *goplsCommit != "" { +- panic("can't set both -gopls_path and -gopls_commit") +- } +- var ( +- goplsPath = *goplsPath +- env []string +- ) +- if *goplsCommit != "" { +- goplsPath = getInstalledGopls() +- } +- if goplsPath == "" { +- var err error +- goplsPath, err = os.Executable() +- if err != nil { +- return nil, err +- } +- env = []string{fmt.Sprintf("%s=true", runAsGopls)} +- } +- return &SidecarServer{ +- goplsPath: goplsPath, +- env: env, +- args: args, +- }, nil -} - --func A() int { -- return 0 +-// profileArgs returns additional command-line arguments to use when invoking +-// gopls, to enable the user-requested profiles. +-// +-// If wantCPU is set, CPU profiling is enabled as well. Some tests may want to +-// instrument profiling around specific critical sections of the benchmark, +-// rather than the entire process. +-// +-// TODO(rfindley): like CPU, all of these would be better served by a custom +-// command. Very rarely do we care about memory usage as the process exits: we +-// care about specific points in time during the benchmark. mem and alloc +-// should be snapshotted, and tracing should be bracketed around critical +-// sections. +-func profileArgs(name string, wantCPU bool) []string { +- var args []string +- if wantCPU && *cpuProfile != "" { +- args = append(args, fmt.Sprintf("-profile.cpu=%s", qualifiedName(name, *cpuProfile))) +- } +- if *memProfile != "" { +- args = append(args, fmt.Sprintf("-profile.mem=%s", qualifiedName(name, *memProfile))) +- } +- if *allocProfile != "" { +- args = append(args, fmt.Sprintf("-profile.alloc=%s", qualifiedName(name, *allocProfile))) +- } +- if *trace != "" { +- args = append(args, fmt.Sprintf("-profile.trace=%s", qualifiedName(name, *trace))) +- } +- return args -} - --func b() int { -- return 0 +-func qualifiedName(args ...string) string { +- return strings.Join(args, ".") -} - --func D() int { -- return 0 --} +-// getInstalledGopls builds gopls at the given -gopls_commit, returning the +-// path to the gopls binary. +-func getInstalledGopls() string { +- if *goplsCommit == "" { +- panic("must provide -gopls_commit") +- } +- toolsDir := filepath.Join(getTempDir(), "gopls_build") +- goplsPath := filepath.Join(toolsDir, "gopls", "gopls") - ---- c-rename -- --shadow/shadow.go:5:2: renaming this var "b" to "c" --shadow/shadow.go:5:5: conflicts with var in same block ---- d-rename -- --package shadow +- installGoplsOnce.Do(func() { +- log.Printf("installing gopls: checking out x/tools@%s into %s\n", *goplsCommit, toolsDir) +- if err := shallowClone(toolsDir, "https://go.googlesource.com/tools", *goplsCommit); err != nil { +- log.Fatal(err) +- } - --func _() { -- a := true -- b, c, _ := A(), B(), d() //@rename("A", "a"),rename("B", "b"),rename("b", "c"),rename("D", "d") -- d := false -- _, _, _, _ = a, b, c, d --} +- log.Println("installing gopls: building...") +- bld := exec.Command("go", "build", ".") +- bld.Dir = filepath.Join(toolsDir, "gopls") +- if output, err := bld.CombinedOutput(); err != nil { +- log.Fatalf("building gopls: %v\n%s", err, output) +- } - --func A() int { -- return 0 +- // Confirm that the resulting path now exists. +- if _, err := os.Stat(goplsPath); err != nil { +- log.Fatalf("os.Stat(%s): %v", goplsPath, err) +- } +- }) +- return goplsPath -} - --func B() int { -- return 0 +-// A SidecarServer starts (and connects to) a separate gopls process at the +-// given path. +-type SidecarServer struct { +- goplsPath string +- env []string // additional environment bindings +- args []string // command-line arguments -} - --func d() int { -- return 0 --} +-// Connect creates new io.Pipes and binds them to the underlying StreamServer. +-// +-// It implements the servertest.Connector interface. +-func (s *SidecarServer) Connect(ctx context.Context) jsonrpc2.Conn { +- // Note: don't use CommandContext here, as we want gopls to exit gracefully +- // in order to write out profile data. +- // +- // We close the connection on context cancelation below. +- cmd := exec.Command(s.goplsPath, s.args...) - -diff -urN a/gopls/internal/lsp/testdata/rename/testy/testy.go b/gopls/internal/lsp/testdata/rename/testy/testy.go ---- a/gopls/internal/lsp/testdata/rename/testy/testy.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/testy/testy.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package testy +- stdin, err := cmd.StdinPipe() +- if err != nil { +- log.Fatal(err) +- } +- stdout, err := cmd.StdoutPipe() +- if err != nil { +- log.Fatal(err) +- } +- cmd.Stderr = os.Stderr +- cmd.Env = append(os.Environ(), s.env...) +- if err := cmd.Start(); err != nil { +- log.Fatalf("starting gopls: %v", err) +- } - --type tt int //@rename("tt", "testyType") +- go func() { +- // If we don't log.Fatal here, benchmarks may hang indefinitely if gopls +- // exits abnormally. +- // +- // TODO(rfindley): ideally we would shut down the connection gracefully, +- // but that doesn't currently work. +- if err := cmd.Wait(); err != nil { +- log.Fatalf("gopls invocation failed with error: %v", err) +- } +- }() - --func a() { -- foo := 42 //@rename("foo", "bar") --} -diff -urN a/gopls/internal/lsp/testdata/rename/testy/testy.go.golden b/gopls/internal/lsp/testdata/rename/testy/testy.go.golden ---- a/gopls/internal/lsp/testdata/rename/testy/testy.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/testy/testy.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ ---- bar-rename -- --package testy +- clientStream := jsonrpc2.NewHeaderStream(fakenet.NewConn("stdio", stdout, stdin)) +- clientConn := jsonrpc2.NewConn(clientStream) - --type tt int //@rename("tt", "testyType") +- go func() { +- select { +- case <-ctx.Done(): +- clientConn.Close() +- clientStream.Close() +- case <-clientConn.Done(): +- } +- }() - --func a() { -- bar := 42 //@rename("foo", "bar") +- return clientConn -} - ---- testyType-rename -- --package testy -- --type testyType int //@rename("tt", "testyType") -- --func a() { -- foo := 42 //@rename("foo", "bar") +-// startProfileIfSupported checks to see if the remote gopls instance supports +-// the start/stop profiling commands. If so, it starts profiling and returns a +-// function that stops profiling and records the total CPU seconds sampled in the +-// cpu_seconds benchmark metric. +-// +-// If the remote gopls instance does not support profiling commands, this +-// function returns nil. +-// +-// If the supplied userSuffix is non-empty, the profile is written to +-// ., and not deleted when the benchmark exits. Otherwise, +-// the profile is written to a temp file that is deleted after the cpu_seconds +-// metric has been computed. +-func startProfileIfSupported(b *testing.B, env *regtest.Env, name string) func() { +- if !env.Editor.HasCommand(command.StartProfile.ID()) { +- return nil +- } +- b.StopTimer() +- stopProfile := env.StartProfile() +- b.StartTimer() +- return func() { +- b.StopTimer() +- profFile := stopProfile() +- totalCPU, err := totalCPUForProfile(profFile) +- if err != nil { +- b.Fatalf("reading profile: %v", err) +- } +- b.ReportMetric(totalCPU.Seconds()/float64(b.N), "cpu_seconds/op") +- if *cpuProfile == "" { +- // The user didn't request profiles, so delete it to clean up. +- if err := os.Remove(profFile); err != nil { +- b.Errorf("removing profile file: %v", err) +- } +- } else { +- // NOTE: if this proves unreliable (due to e.g. EXDEV), we can fall back +- // on Read+Write+Remove. +- name := qualifiedName(name, *cpuProfile) +- if err := os.Rename(profFile, name); err != nil { +- b.Fatalf("renaming profile file: %v", err) +- } +- } +- } -} - -diff -urN a/gopls/internal/lsp/testdata/rename/testy/testy_test.go b/gopls/internal/lsp/testdata/rename/testy/testy_test.go ---- a/gopls/internal/lsp/testdata/rename/testy/testy_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/testy/testy_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,8 +0,0 @@ --package testy -- --import "testing" +-// totalCPUForProfile reads the pprof profile with the given file name, parses, +-// and aggregates the total CPU sampled during the profile. +-func totalCPUForProfile(filename string) (time.Duration, error) { +- protoGz, err := os.ReadFile(filename) +- if err != nil { +- return 0, err +- } +- rd, err := gzip.NewReader(bytes.NewReader(protoGz)) +- if err != nil { +- return 0, fmt.Errorf("creating gzip reader for %s: %v", filename, err) +- } +- data, err := io.ReadAll(rd) +- if err != nil { +- return 0, fmt.Errorf("reading %s: %v", filename, err) +- } +- return pprof.TotalTime(data) +-} - --func TestSomething(t *testing.T) { -- var x int //@rename("x", "testyX") -- a() //@rename("a", "b") +-// closeBuffer stops the benchmark timer and closes the buffer with the given +-// name. +-// +-// It may be used to clean up files opened in the shared environment during +-// benchmarking. +-func closeBuffer(b *testing.B, env *regtest.Env, name string) { +- b.StopTimer() +- env.CloseBuffer(name) +- env.AfterChange() +- b.StartTimer() -} -diff -urN a/gopls/internal/lsp/testdata/rename/testy/testy_test.go.golden b/gopls/internal/lsp/testdata/rename/testy/testy_test.go.golden ---- a/gopls/internal/lsp/testdata/rename/testy/testy_test.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rename/testy/testy_test.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,30 +0,0 @@ ---- b-rename -- --testy.go: --package testy +diff -urN a/gopls/internal/regtest/bench/codeaction_test.go b/gopls/internal/regtest/bench/codeaction_test.go +--- a/gopls/internal/regtest/bench/codeaction_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/codeaction_test.go 1970-01-01 08:00:00 +@@ -1,69 +0,0 @@ +-// Copyright 2023 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. - --type tt int //@rename("tt", "testyType") +-package bench - --func b() { -- foo := 42 //@rename("foo", "bar") --} +-import ( +- "fmt" +- "sync/atomic" +- "testing" - --testy_test.go: --package testy +- "golang.org/x/tools/gopls/internal/lsp/protocol" +-) - --import "testing" +-func BenchmarkCodeAction(b *testing.B) { +- for _, test := range didChangeTests { +- b.Run(test.repo, func(b *testing.B) { +- env := getRepo(b, test.repo).sharedEnv(b) +- env.OpenFile(test.file) +- defer closeBuffer(b, env, test.file) +- env.AfterChange() - --func TestSomething(t *testing.T) { -- var x int //@rename("x", "testyX") -- b() //@rename("a", "b") --} +- env.CodeAction(test.file, nil) // pre-warm - ---- testyX-rename -- --package testy +- b.ResetTimer() - --import "testing" +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "hover")); stopAndRecord != nil { +- defer stopAndRecord() +- } - --func TestSomething(t *testing.T) { -- var testyX int //@rename("x", "testyX") -- a() //@rename("a", "b") +- for i := 0; i < b.N; i++ { +- env.CodeAction(test.file, nil) +- } +- }) +- } -} - -diff -urN a/gopls/internal/lsp/testdata/rundespiteerrors/rundespiteerrors.go b/gopls/internal/lsp/testdata/rundespiteerrors/rundespiteerrors.go ---- a/gopls/internal/lsp/testdata/rundespiteerrors/rundespiteerrors.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/rundespiteerrors/rundespiteerrors.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ --package rundespiteerrors +-func BenchmarkCodeActionFollowingEdit(b *testing.B) { +- for _, test := range didChangeTests { +- b.Run(test.repo, func(b *testing.B) { +- env := getRepo(b, test.repo).sharedEnv(b) +- env.OpenFile(test.file) +- defer closeBuffer(b, env, test.file) +- env.EditBuffer(test.file, protocol.TextEdit{NewText: "// __REGTEST_PLACEHOLDER_0__\n"}) +- env.AfterChange() - --// This test verifies that analyzers without RunDespiteErrors are not --// executed on a package containing type errors (see issue #54762). --func _() { -- // A type error. -- _ = 1 + "" //@diag("1", "compiler", "mismatched types|cannot convert", "error") +- env.CodeAction(test.file, nil) // pre-warm - -- // A violation of an analyzer for which RunDespiteErrors=false: -- // no diagnostic is produced; the diag comment is merely illustrative. -- for _ = range "" { //diag("for _", "simplifyrange", "simplify range expression", "warning") +- b.ResetTimer() +- +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "hover")); stopAndRecord != nil { +- defer stopAndRecord() +- } - +- for i := 0; i < b.N; i++ { +- edits := atomic.AddInt64(&editID, 1) +- env.EditBuffer(test.file, protocol.TextEdit{ +- Range: protocol.Range{ +- Start: protocol.Position{Line: 0, Character: 0}, +- End: protocol.Position{Line: 1, Character: 0}, +- }, +- // Increment the placeholder text, to ensure cache misses. +- NewText: fmt.Sprintf("// __REGTEST_PLACEHOLDER_%d__\n", edits), +- }) +- env.CodeAction(test.file, nil) +- } +- }) - } -} -diff -urN a/gopls/internal/lsp/testdata/selectionrange/foo.go b/gopls/internal/lsp/testdata/selectionrange/foo.go ---- a/gopls/internal/lsp/testdata/selectionrange/foo.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/selectionrange/foo.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package foo +diff -urN a/gopls/internal/regtest/bench/completion_test.go b/gopls/internal/regtest/bench/completion_test.go +--- a/gopls/internal/regtest/bench/completion_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/completion_test.go 1970-01-01 08:00:00 +@@ -1,290 +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. - --import "time" +-package bench - --func Bar(x, y int, t time.Time) int { -- zs := []int{1, 2, 3} //@selectionrange("1") +-import ( +- "flag" +- "fmt" +- "sync/atomic" +- "testing" - -- for _, z := range zs { -- x = x + z + y + zs[1] //@selectionrange("1") -- } +- "golang.org/x/tools/gopls/internal/lsp/fake" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) - -- return x + y //@selectionrange("+") +-// TODO(rfindley): update these completion tests to run on multiple repos. +- +-type completionBenchOptions struct { +- file, locationRegexp string +- +- // Hooks to run edits before initial completion +- setup func(*Env) // run before the benchmark starts +- beforeCompletion func(*Env) // run before each completion -} -diff -urN a/gopls/internal/lsp/testdata/selectionrange/foo.go.golden b/gopls/internal/lsp/testdata/selectionrange/foo.go.golden ---- a/gopls/internal/lsp/testdata/selectionrange/foo.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/selectionrange/foo.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,29 +0,0 @@ ---- selectionrange_foo_12_11 -- --Ranges 0: -- 11:8-11:13 "x + y" -- 11:1-11:13 "return x + y" -- 4:36-12:1 "{\\n\tzs := []int{...ionrange(\"+\")\\n}" -- 4:0-12:1 "func Bar(x, y i...ionrange(\"+\")\\n}" -- 0:0-12:1 "package foo\\n\\nim...ionrange(\"+\")\\n}" - ---- selectionrange_foo_6_14 -- --Ranges 0: -- 5:13-5:14 "1" -- 5:7-5:21 "[]int{1, 2, 3}" -- 5:1-5:21 "zs := []int{1, 2, 3}" -- 4:36-12:1 "{\\n\tzs := []int{...ionrange(\"+\")\\n}" -- 4:0-12:1 "func Bar(x, y i...ionrange(\"+\")\\n}" -- 0:0-12:1 "package foo\\n\\nim...ionrange(\"+\")\\n}" +-func benchmarkCompletion(options completionBenchOptions, b *testing.B) { +- repo := getRepo(b, "tools") +- _ = repo.sharedEnv(b) // ensure cache is warm +- env := repo.newEnv(b, fake.EditorConfig{}, "completion", false) +- defer env.Close() - ---- selectionrange_foo_9_22 -- --Ranges 0: -- 8:21-8:22 "1" -- 8:18-8:23 "zs[1]" -- 8:6-8:23 "x + z + y + zs[1]" -- 8:2-8:23 "x = x + z + y + zs[1]" -- 7:22-9:2 "{\\n\t\tx = x + z +...onrange(\"1\")\\n\t}" -- 7:1-9:2 "for _, z := ran...onrange(\"1\")\\n\t}" -- 4:36-12:1 "{\\n\tzs := []int{...ionrange(\"+\")\\n}" -- 4:0-12:1 "func Bar(x, y i...ionrange(\"+\")\\n}" -- 0:0-12:1 "package foo\\n\\nim...ionrange(\"+\")\\n}" +- // Run edits required for this completion. +- if options.setup != nil { +- options.setup(env) +- } - -diff -urN a/gopls/internal/lsp/testdata/selector/selector.go.in b/gopls/internal/lsp/testdata/selector/selector.go.in ---- a/gopls/internal/lsp/testdata/selector/selector.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/selector/selector.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,66 +0,0 @@ --// +build go1.11 +- // Run a completion to make sure the system is warm. +- loc := env.RegexpSearch(options.file, options.locationRegexp) +- completions := env.Completion(loc) - --package selector +- if testing.Verbose() { +- fmt.Println("Results:") +- for i := 0; i < len(completions.Items); i++ { +- fmt.Printf("\t%d. %v\n", i, completions.Items[i]) +- } +- } - --import ( -- "golang.org/lsptests/bar" --) +- b.Run("tools", func(b *testing.B) { +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName("tools", "completion")); stopAndRecord != nil { +- defer stopAndRecord() +- } - --type S struct { -- B, A, C int //@item(Bf, "B", "int", "field"),item(Af, "A", "int", "field"),item(Cf, "C", "int", "field") +- for i := 0; i < b.N; i++ { +- if options.beforeCompletion != nil { +- options.beforeCompletion(env) +- } +- env.Completion(loc) +- } +- }) -} - --func _() { -- _ = S{}.; //@complete(";", Af, Bf, Cf) +-// endRangeInBuffer returns the position for last character in the buffer for +-// the given file. +-func endRangeInBuffer(env *Env, name string) protocol.Range { +- buffer := env.BufferText(name) +- m := protocol.NewMapper("", []byte(buffer)) +- rng, err := m.OffsetRange(len(buffer), len(buffer)) +- if err != nil { +- env.T.Fatal(err) +- } +- return rng -} - --type bob struct { a int } //@item(a, "a", "int", "field") --type george struct { b int } --type jack struct { c int } //@item(c, "c", "int", "field") --type jill struct { d int } +-// Benchmark struct completion in tools codebase. +-func BenchmarkStructCompletion(b *testing.B) { +- file := "internal/lsp/cache/session.go" - --func (b *bob) george() *george {} //@item(george, "george", "func() *george", "method") --func (g *george) jack() *jack {} --func (j *jack) jill() *jill {} //@item(jill, "jill", "func() *jill", "method") +- setup := func(env *Env) { +- env.OpenFile(file) +- env.EditBuffer(file, protocol.TextEdit{ +- Range: endRangeInBuffer(env, file), +- NewText: "\nvar testVariable map[string]bool = Session{}.\n", +- }) +- } - --func _() { -- b := &bob{} -- y := b.george(). -- jack(); -- y.; //@complete(";", c, jill) +- benchmarkCompletion(completionBenchOptions{ +- file: file, +- locationRegexp: `var testVariable map\[string\]bool = Session{}(\.)`, +- setup: setup, +- }, b) -} - --func _() { -- bar. //@complete(" /", Bar) -- x := 5 -- -- var b *bob -- b. //@complete(" /", a, george) -- y, z := 5, 6 -- -- b. //@complete(" /", a, george) -- y, z, a, b, c := 5, 6 +-// Benchmark import completion in tools codebase. +-func BenchmarkImportCompletion(b *testing.B) { +- const file = "internal/lsp/source/completion/completion.go" +- benchmarkCompletion(completionBenchOptions{ +- file: file, +- locationRegexp: `go\/()`, +- setup: func(env *Env) { env.OpenFile(file) }, +- }, b) -} - --func _() { -- bar. //@complete(" /", Bar) -- bar.Bar() +-// Benchmark slice completion in tools codebase. +-func BenchmarkSliceCompletion(b *testing.B) { +- file := "internal/lsp/cache/session.go" - -- bar. //@complete(" /", Bar) -- go f() --} +- setup := func(env *Env) { +- env.OpenFile(file) +- env.EditBuffer(file, protocol.TextEdit{ +- Range: endRangeInBuffer(env, file), +- NewText: "\nvar testVariable []byte = \n", +- }) +- } - --func _() { -- var b *bob -- if y != b. //@complete(" /", a, george) -- z := 5 +- benchmarkCompletion(completionBenchOptions{ +- file: file, +- locationRegexp: `var testVariable \[\]byte (=)`, +- setup: setup, +- }, b) +-} - -- if z + y + 1 + b. //@complete(" /", a, george) -- r, s, t := 4, 5 +-// Benchmark deep completion in function call in tools codebase. +-func BenchmarkFuncDeepCompletion(b *testing.B) { +- file := "internal/lsp/source/completion/completion.go" +- fileContent := ` +-func (c *completer) _() { +- c.inference.kindMatches(c.) +-} +-` +- setup := func(env *Env) { +- env.OpenFile(file) +- originalBuffer := env.BufferText(file) +- env.EditBuffer(file, protocol.TextEdit{ +- Range: endRangeInBuffer(env, file), +- // TODO(rfindley): this is a bug: it should just be fileContent. +- NewText: originalBuffer + fileContent, +- }) +- } - -- if y != b. //@complete(" /", a, george) -- z = 5 +- benchmarkCompletion(completionBenchOptions{ +- file: file, +- locationRegexp: `func \(c \*completer\) _\(\) {\n\tc\.inference\.kindMatches\((c)`, +- setup: setup, +- }, b) +-} - -- if z + y + 1 + b. //@complete(" /", a, george) -- r = 4 +-type completionFollowingEditTest struct { +- repo string +- name string +- file string // repo-relative file to create +- content string // file content +- locationRegexp string // regexp for completion -} -diff -urN a/gopls/internal/lsp/testdata/semantic/a.go b/gopls/internal/lsp/testdata/semantic/a.go ---- a/gopls/internal/lsp/testdata/semantic/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/semantic/a.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,81 +0,0 @@ --package semantictokens //@ semantic("") - --import ( -- _ "encoding/utf8" -- utf "encoding/utf8" -- "fmt" //@ semantic("fmt") -- . "fmt" -- "unicode/utf8" --) +-var completionFollowingEditTests = []completionFollowingEditTest{ +- { +- "tools", +- "selector", +- "internal/lsp/source/completion/completion2.go", +- ` +-package completion +- +-func (c *completer) _() { +- c.inference.kindMatches(c.) +-} +-`, +- `func \(c \*completer\) _\(\) {\n\tc\.inference\.kindMatches\((c)`, +- }, +- { +- "kubernetes", +- "selector", +- "pkg/kubelet/kubelet2.go", +- ` +-package kubelet - --var ( -- a = fmt.Print -- b []string = []string{"foo"} -- c1 chan int -- c2 <-chan int -- c3 = make([]chan<- int) -- b = A{X: 23} -- m map[bool][3]*float64 --) +-func (kl *Kubelet) _() { +- kl. +-} +-`, +- `kl\.()`, +- }, +- { +- "kubernetes", +- "identifier", +- "pkg/kubelet/kubelet2.go", +- ` +-package kubelet - --const ( -- xx F = iota -- yy = xx + 3 -- zz = "" -- ww = "not " + zz --) +-func (kl *Kubelet) _() { +- k // here +-} +-`, +- `k() // here`, +- }, +- { +- "oracle", +- "selector", +- "dataintegration/pivot2.go", +- ` +-package dataintegration - --type A struct { -- X int `foof` +-func (p *Pivot) _() { +- p. -} --type B interface { -- A -- sad(int) bool +-`, +- `p\.()`, +- }, -} - --type F int +-// Benchmark completion following an arbitrary edit. +-// +-// Edits force type-checked packages to be invalidated, so we want to measure +-// how long it takes before completion results are available. +-func BenchmarkCompletionFollowingEdit(b *testing.B) { +- for _, test := range completionFollowingEditTests { +- b.Run(fmt.Sprintf("%s_%s", test.repo, test.name), func(b *testing.B) { +- for _, completeUnimported := range []bool{true, false} { +- b.Run(fmt.Sprintf("completeUnimported=%v", completeUnimported), func(b *testing.B) { +- for _, budget := range []string{"0s", "100ms"} { +- b.Run(fmt.Sprintf("budget=%s", budget), func(b *testing.B) { +- runCompletionFollowingEdit(b, test, completeUnimported, budget) +- }) +- } +- }) +- } +- }) +- } +-} - --func (a *A) f() bool { -- var z string -- x := "foo" -- a(x) -- y := "bar" + x -- switch z { -- case "xx": -- default: +-var gomodcache = flag.String("gomodcache", "", "optional GOMODCACHE for unimported completion benchmarks") +- +-func runCompletionFollowingEdit(b *testing.B, test completionFollowingEditTest, completeUnimported bool, budget string) { +- repo := getRepo(b, test.repo) +- sharedEnv := repo.sharedEnv(b) // ensure cache is warm +- envvars := map[string]string{ +- "GOPATH": sharedEnv.Sandbox.GOPATH(), // use the warm cache - } -- select { -- case z := <-c3[0]: -- default: +- +- if *gomodcache != "" { +- envvars["GOMODCACHE"] = *gomodcache - } -- for k, v := range m { -- return (!k) && v[0] == nil +- +- env := repo.newEnv(b, fake.EditorConfig{ +- Env: envvars, +- Settings: map[string]interface{}{ +- "completeUnimported": completeUnimported, +- "completionBudget": budget, +- }, +- }, "completionFollowingEdit", false) +- defer env.Close() +- +- env.CreateBuffer(test.file, "// __REGTEST_PLACEHOLDER_0__\n"+test.content) +- editPlaceholder := func() { +- edits := atomic.AddInt64(&editID, 1) +- env.EditBuffer(test.file, protocol.TextEdit{ +- Range: protocol.Range{ +- Start: protocol.Position{Line: 0, Character: 0}, +- End: protocol.Position{Line: 1, Character: 0}, +- }, +- // Increment the placeholder text, to ensure cache misses. +- NewText: fmt.Sprintf("// __REGTEST_PLACEHOLDER_%d__\n", edits), +- }) - } -- c2 <- A.X -- w := b[4:] -- j := len(x) -- j-- -- q := []interface{}{j, 23i, &y} -- g(q...) -- return true --} +- env.AfterChange() - --func g(vv ...interface{}) { -- ff := func() {} -- defer ff() -- go utf.RuneCount("") -- go utf8.RuneCount(vv.(string)) -- if true { -- } else { +- // Run a completion to make sure the system is warm. +- loc := env.RegexpSearch(test.file, test.locationRegexp) +- completions := env.Completion(loc) +- +- if testing.Verbose() { +- fmt.Println("Results:") +- for i, item := range completions.Items { +- fmt.Printf("\t%d. %v\n", i, item) +- } - } --Never: -- for i := 0; i < 10; { -- break Never +- +- b.ResetTimer() +- +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "completionFollowingEdit")); stopAndRecord != nil { +- defer stopAndRecord() - } -- _, ok := vv[0].(A) -- if !ok { -- switch x := vv[0].(type) { -- } -- goto Never +- +- for i := 0; i < b.N; i++ { +- editPlaceholder() +- loc := env.RegexpSearch(test.file, test.locationRegexp) +- env.Completion(loc) - } -} -diff -urN a/gopls/internal/lsp/testdata/semantic/a.go.golden b/gopls/internal/lsp/testdata/semantic/a.go.golden ---- a/gopls/internal/lsp/testdata/semantic/a.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/semantic/a.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,83 +0,0 @@ ---- semantic -- --/*⇒7,keyword,[]*/package /*⇒14,namespace,[]*/semantictokens /*⇒16,comment,[]*///@ semantic("") +diff -urN a/gopls/internal/regtest/bench/definition_test.go b/gopls/internal/regtest/bench/definition_test.go +--- a/gopls/internal/regtest/bench/definition_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/definition_test.go 1970-01-01 08:00:00 +@@ -1,46 +0,0 @@ +-// Copyright 2023 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. - --/*⇒6,keyword,[]*/import ( -- _ "encoding/utf8" -- /*⇒3,namespace,[]*/utf "encoding/utf8" -- "fmt"/*⇐3,namespace,[]*/ /*⇒19,comment,[]*///@ semantic("fmt") -- . "fmt" -- "unicode/utf8"/*⇐4,namespace,[]*/ --) +-package bench - --/*⇒3,keyword,[]*/var ( -- /*⇒1,variable,[definition]*/a = /*⇒3,namespace,[]*/fmt./*⇒5,function,[]*/Print -- /*⇒1,variable,[definition]*/b []/*⇒6,type,[defaultLibrary]*/string = []/*⇒6,type,[defaultLibrary]*/string{/*⇒5,string,[]*/"foo"} -- /*⇒2,variable,[definition]*/c1 /*⇒4,keyword,[]*/chan /*⇒3,type,[defaultLibrary]*/int -- /*⇒2,variable,[definition]*/c2 /*⇒2,operator,[]*/<-/*⇒4,keyword,[]*/chan /*⇒3,type,[defaultLibrary]*/int -- /*⇒2,variable,[definition]*/c3 = /*⇒4,function,[defaultLibrary]*/make([]/*⇒4,keyword,[]*/chan/*⇒2,operator,[]*/<- /*⇒3,type,[defaultLibrary]*/int) -- /*⇒1,variable,[definition]*/b = /*⇒1,type,[]*/A{/*⇒1,variable,[]*/X: /*⇒2,number,[]*/23} -- /*⇒1,variable,[definition]*/m /*⇒3,keyword,[]*/map[/*⇒4,type,[defaultLibrary]*/bool][/*⇒1,number,[]*/3]/*⇒1,operator,[]*/*/*⇒7,type,[defaultLibrary]*/float64 +-import ( +- "testing" -) - --/*⇒5,keyword,[]*/const ( -- /*⇒2,variable,[definition readonly]*/xx /*⇒1,type,[]*/F = /*⇒4,variable,[readonly]*/iota -- /*⇒2,variable,[definition readonly]*/yy = /*⇒2,variable,[readonly]*/xx /*⇒1,operator,[]*/+ /*⇒1,number,[]*/3 -- /*⇒2,variable,[definition readonly]*/zz = /*⇒2,string,[]*/"" -- /*⇒2,variable,[definition readonly]*/ww = /*⇒6,string,[]*/"not " /*⇒1,operator,[]*/+ /*⇒2,variable,[readonly]*/zz --) +-func BenchmarkDefinition(b *testing.B) { +- tests := []struct { +- repo string +- file string +- regexp string +- }{ +- {"istio", "pkg/config/model.go", `gogotypes\.(MarshalAny)`}, +- {"google-cloud-go", "httpreplay/httpreplay.go", `proxy\.(ForRecording)`}, +- {"kubernetes", "pkg/controller/lookup_cache.go", `hashutil\.(DeepHashObject)`}, +- {"kuma", "api/generic/insights.go", `proto\.(Message)`}, +- {"pkgsite", "internal/log/log.go", `derrors\.(Wrap)`}, +- {"starlark", "starlark/eval.go", "prog.compiled.(Encode)"}, +- {"tools", "internal/lsp/cache/check.go", `(snapshot)\) buildKey`}, +- } - --/*⇒4,keyword,[]*/type /*⇒1,type,[definition]*/A /*⇒6,keyword,[]*/struct { -- /*⇒1,variable,[definition]*/X /*⇒3,type,[defaultLibrary]*/int /*⇒6,string,[]*/`foof` --} --/*⇒4,keyword,[]*/type /*⇒1,type,[definition]*/B /*⇒9,keyword,[]*/interface { -- /*⇒1,type,[]*/A -- /*⇒3,method,[definition]*/sad(/*⇒3,type,[defaultLibrary]*/int) /*⇒4,type,[defaultLibrary]*/bool --} +- for _, test := range tests { +- b.Run(test.repo, func(b *testing.B) { +- env := getRepo(b, test.repo).sharedEnv(b) +- env.OpenFile(test.file) +- defer closeBuffer(b, env, test.file) - --/*⇒4,keyword,[]*/type /*⇒1,type,[definition]*/F /*⇒3,type,[defaultLibrary]*/int +- loc := env.RegexpSearch(test.file, test.regexp) +- env.Await(env.DoneWithOpen()) +- env.GoToDefinition(loc) // pre-warm the query, and open the target file +- b.ResetTimer() - --/*⇒4,keyword,[]*/func (/*⇒1,variable,[]*/a /*⇒1,operator,[]*/*/*⇒1,type,[]*/A) /*⇒1,method,[definition]*/f() /*⇒4,type,[defaultLibrary]*/bool { -- /*⇒3,keyword,[]*/var /*⇒1,variable,[definition]*/z /*⇒6,type,[defaultLibrary]*/string -- /*⇒1,variable,[definition]*/x /*⇒2,operator,[]*/:= /*⇒5,string,[]*/"foo" -- /*⇒1,variable,[]*/a(/*⇒1,variable,[]*/x) -- /*⇒1,variable,[definition]*/y /*⇒2,operator,[]*/:= /*⇒5,string,[]*/"bar" /*⇒1,operator,[]*/+ /*⇒1,variable,[]*/x -- /*⇒6,keyword,[]*/switch /*⇒1,variable,[]*/z { -- /*⇒4,keyword,[]*/case /*⇒4,string,[]*/"xx": -- /*⇒7,keyword,[]*/default: -- } -- /*⇒6,keyword,[]*/select { -- /*⇒4,keyword,[]*/case /*⇒1,variable,[definition]*/z /*⇒2,operator,[]*/:= /*⇒2,operator,[]*/<-/*⇒2,variable,[]*/c3[/*⇒1,number,[]*/0]: -- /*⇒7,keyword,[]*/default: -- } -- /*⇒3,keyword,[]*/for /*⇒1,variable,[definition]*/k, /*⇒1,variable,[definition]*/v := /*⇒5,keyword,[]*/range /*⇒1,variable,[]*/m { -- /*⇒6,keyword,[]*/return (/*⇒1,operator,[]*/!/*⇒1,variable,[]*/k) /*⇒2,operator,[]*/&& /*⇒1,variable,[]*/v[/*⇒1,number,[]*/0] /*⇒2,operator,[]*/== /*⇒3,variable,[readonly defaultLibrary]*/nil -- } -- /*⇒2,variable,[]*/c2 /*⇒2,operator,[]*/<- /*⇒1,type,[]*/A./*⇒1,variable,[]*/X -- /*⇒1,variable,[definition]*/w /*⇒2,operator,[]*/:= /*⇒1,variable,[]*/b[/*⇒1,number,[]*/4:] -- /*⇒1,variable,[definition]*/j /*⇒2,operator,[]*/:= /*⇒3,function,[defaultLibrary]*/len(/*⇒1,variable,[]*/x) -- /*⇒1,variable,[]*/j/*⇒2,operator,[]*/-- -- /*⇒1,variable,[definition]*/q /*⇒2,operator,[]*/:= []/*⇒9,keyword,[]*/interface{}{/*⇒1,variable,[]*/j, /*⇒3,number,[]*/23i, /*⇒1,operator,[]*/&/*⇒1,variable,[]*/y} -- /*⇒1,function,[]*/g(/*⇒1,variable,[]*/q/*⇒3,operator,[]*/...) -- /*⇒6,keyword,[]*/return /*⇒4,variable,[readonly]*/true --} +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "definition")); stopAndRecord != nil { +- defer stopAndRecord() +- } - --/*⇒4,keyword,[]*/func /*⇒1,function,[definition]*/g(/*⇒2,parameter,[definition]*/vv /*⇒3,operator,[]*/.../*⇒9,keyword,[]*/interface{}) { -- /*⇒2,variable,[definition]*/ff /*⇒2,operator,[]*/:= /*⇒4,keyword,[]*/func() {} -- /*⇒5,keyword,[]*/defer /*⇒2,function,[]*/ff() -- /*⇒2,keyword,[]*/go /*⇒3,namespace,[]*/utf./*⇒9,function,[]*/RuneCount(/*⇒2,string,[]*/"") -- /*⇒2,keyword,[]*/go /*⇒4,namespace,[]*/utf8./*⇒9,function,[]*/RuneCount(/*⇒2,parameter,[]*/vv.(/*⇒6,type,[]*/string)) -- /*⇒2,keyword,[]*/if /*⇒4,variable,[readonly]*/true { -- } /*⇒4,keyword,[]*/else { -- } --/*⇒5,parameter,[definition]*/Never: -- /*⇒3,keyword,[]*/for /*⇒1,variable,[definition]*/i /*⇒2,operator,[]*/:= /*⇒1,number,[]*/0; /*⇒1,variable,[]*/i /*⇒1,operator,[]*/< /*⇒2,number,[]*/10; { -- /*⇒5,keyword,[]*/break Never -- } -- _, /*⇒2,variable,[definition]*/ok /*⇒2,operator,[]*/:= /*⇒2,parameter,[]*/vv[/*⇒1,number,[]*/0].(/*⇒1,type,[]*/A) -- /*⇒2,keyword,[]*/if /*⇒1,operator,[]*/!/*⇒2,variable,[]*/ok { -- /*⇒6,keyword,[]*/switch /*⇒1,variable,[definition]*/x /*⇒2,operator,[]*/:= /*⇒2,parameter,[]*/vv[/*⇒1,number,[]*/0].(/*⇒4,keyword,[]*/type) { -- } -- /*⇒4,keyword,[]*/goto Never +- for i := 0; i < b.N; i++ { +- env.GoToDefinition(loc) // pre-warm the query +- } +- }) - } -} +diff -urN a/gopls/internal/regtest/bench/didchange_test.go b/gopls/internal/regtest/bench/didchange_test.go +--- a/gopls/internal/regtest/bench/didchange_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/didchange_test.go 1970-01-01 08:00:00 +@@ -1,142 +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. - -diff -urN a/gopls/internal/lsp/testdata/semantic/b.go b/gopls/internal/lsp/testdata/semantic/b.go ---- a/gopls/internal/lsp/testdata/semantic/b.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/semantic/b.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,38 +0,0 @@ --package semantictokens //@ semantic("") +-package bench - --func f(x ...interface{}) { --} +-import ( +- "fmt" +- "sync/atomic" +- "testing" +- "time" - --func weirⰀd() { /*😀*/ // comment -- const ( -- snil = nil -- nil = true -- true = false -- false = snil -- cmd = `foof` -- double = iota -- iota = copy -- four = (len(cmd)/2 < 5) -- five = four -- ) -- f(cmd, nil, double, iota) --} +- "golang.org/x/tools/gopls/internal/lsp/fake" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +-) - --/* +-// Use a global edit counter as bench function may execute multiple times, and +-// we want to avoid cache hits. Use time.Now to also avoid cache hits from the +-// shared file cache. +-var editID int64 = time.Now().UnixNano() - --multiline */ /* --multiline --*/ --type AA int --type BB struct { -- AA +-type changeTest struct { +- repo string +- file string +- canSave bool -} --type CC struct { -- AA int +- +-var didChangeTests = []changeTest{ +- {"google-cloud-go", "internal/annotate.go", true}, +- {"istio", "pkg/fuzz/util.go", true}, +- {"kubernetes", "pkg/controller/lookup_cache.go", true}, +- {"kuma", "api/generic/insights.go", true}, +- {"oracle", "dataintegration/data_type.go", false}, // diagnoseSave fails because this package is generated +- {"pkgsite", "internal/frontend/server.go", true}, +- {"starlark", "starlark/eval.go", true}, +- {"tools", "internal/lsp/cache/snapshot.go", true}, -} --type D func(aa AA) (BB error) --type E func(AA) BB - --var a chan<- chan int --var b chan<- <-chan int --var c <-chan <-chan int -diff -urN a/gopls/internal/lsp/testdata/semantic/b.go.golden b/gopls/internal/lsp/testdata/semantic/b.go.golden ---- a/gopls/internal/lsp/testdata/semantic/b.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/semantic/b.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,40 +0,0 @@ ---- semantic -- --/*⇒7,keyword,[]*/package /*⇒14,namespace,[]*/semantictokens /*⇒16,comment,[]*///@ semantic("") +-// BenchmarkDidChange 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. +-func BenchmarkDidChange(b *testing.B) { +- for _, test := range didChangeTests { +- b.Run(test.repo, func(b *testing.B) { +- env := getRepo(b, test.repo).sharedEnv(b) +- env.OpenFile(test.file) +- defer closeBuffer(b, env, test.file) - --/*⇒4,keyword,[]*/func /*⇒1,function,[definition]*/f(/*⇒1,parameter,[definition]*/x /*⇒3,operator,[]*/.../*⇒9,keyword,[]*/interface{}) { +- // Insert the text we'll be modifying at the top of the file. +- env.EditBuffer(test.file, protocol.TextEdit{NewText: "// __REGTEST_PLACEHOLDER_0__\n"}) +- env.AfterChange() +- b.ResetTimer() +- +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "didchange")); stopAndRecord != nil { +- defer stopAndRecord() +- } +- +- for i := 0; i < b.N; i++ { +- edits := atomic.AddInt64(&editID, 1) +- env.EditBuffer(test.file, protocol.TextEdit{ +- Range: protocol.Range{ +- Start: protocol.Position{Line: 0, Character: 0}, +- End: protocol.Position{Line: 1, Character: 0}, +- }, +- // Increment the placeholder text, to ensure cache misses. +- NewText: fmt.Sprintf("// __REGTEST_PLACEHOLDER_%d__\n", edits), +- }) +- env.Await(env.StartedChange()) +- } +- }) +- } -} - --/*⇒4,keyword,[]*/func /*⇒6,function,[definition]*/weirⰀd() { /*⇒5,comment,[]*//*😀*/ /*⇒10,comment,[]*/// comment -- /*⇒5,keyword,[]*/const ( -- /*⇒4,variable,[definition readonly]*/snil = /*⇒3,variable,[readonly defaultLibrary]*/nil -- /*⇒3,variable,[definition readonly]*/nil = /*⇒4,variable,[readonly]*/true -- /*⇒4,variable,[definition readonly]*/true = /*⇒5,variable,[readonly]*/false -- /*⇒5,variable,[definition readonly]*/false = /*⇒4,variable,[readonly]*/snil -- /*⇒3,variable,[definition readonly]*/cmd = /*⇒6,string,[]*/`foof` -- /*⇒6,variable,[definition readonly]*/double = /*⇒4,variable,[readonly]*/iota -- /*⇒4,variable,[definition readonly]*/iota = /*⇒4,function,[defaultLibrary]*/copy -- /*⇒4,variable,[definition readonly]*/four = (/*⇒3,function,[defaultLibrary]*/len(/*⇒3,variable,[readonly]*/cmd)/*⇒1,operator,[]*// /*⇒1,number,[]*/2 /*⇒1,operator,[]*/< /*⇒1,number,[]*/5) -- /*⇒4,variable,[definition readonly]*/five = /*⇒4,variable,[readonly]*/four -- ) -- /*⇒1,function,[]*/f(/*⇒3,variable,[readonly]*/cmd, /*⇒3,variable,[readonly]*/nil, /*⇒6,variable,[readonly]*/double, /*⇒4,variable,[readonly]*/iota) +-func BenchmarkDiagnoseChange(b *testing.B) { +- for _, test := range didChangeTests { +- runChangeDiagnosticsBenchmark(b, test, false, "diagnoseChange") +- } -} - --/*⇒2,comment,[]*//* --/*⇒0,comment,[]*/ --/*⇒12,comment,[]*/multiline */ /*⇒2,comment,[]*//* --/*⇒9,comment,[]*/multiline --/*⇒2,comment,[]*/*/ --/*⇒4,keyword,[]*/type /*⇒2,type,[definition]*/AA /*⇒3,type,[defaultLibrary]*/int --/*⇒4,keyword,[]*/type /*⇒2,type,[definition]*/BB /*⇒6,keyword,[]*/struct { -- /*⇒2,type,[]*/AA +-// TODO(rfindley): add a benchmark for with a metadata-affecting change, when +-// this matters. +-func BenchmarkDiagnoseSave(b *testing.B) { +- for _, test := range didChangeTests { +- runChangeDiagnosticsBenchmark(b, test, true, "diagnoseSave") +- } -} --/*⇒4,keyword,[]*/type /*⇒2,type,[definition]*/CC /*⇒6,keyword,[]*/struct { -- /*⇒2,variable,[definition]*/AA /*⇒3,type,[defaultLibrary]*/int +- +-// runChangeDiagnosticsBenchmark runs a benchmark to edit the test file and +-// await the resulting diagnostics pass. If save is set, the file is also saved. +-func runChangeDiagnosticsBenchmark(b *testing.B, test changeTest, save bool, operation string) { +- b.Run(test.repo, func(b *testing.B) { +- if !test.canSave { +- b.Skipf("skipping as %s cannot be saved", test.file) +- } +- sharedEnv := getRepo(b, test.repo).sharedEnv(b) +- config := fake.EditorConfig{ +- Env: map[string]string{ +- "GOPATH": sharedEnv.Sandbox.GOPATH(), +- }, +- Settings: map[string]interface{}{ +- "diagnosticsDelay": "0s", +- }, +- } +- // Use a new env to avoid the diagnostic delay: we want to measure how +- // long it takes to produce the diagnostics. +- env := getRepo(b, test.repo).newEnv(b, config, operation, false) +- defer env.Close() +- env.OpenFile(test.file) +- // Insert the text we'll be modifying at the top of the file. +- env.EditBuffer(test.file, protocol.TextEdit{NewText: "// __REGTEST_PLACEHOLDER_0__\n"}) +- if save { +- env.SaveBuffer(test.file) +- } +- env.AfterChange() +- b.ResetTimer() +- +- // We must use an extra subtest layer here, so that we only set up the +- // shared env once (otherwise we pay additional overhead and the profiling +- // flags don't work). +- b.Run("diagnose", func(b *testing.B) { +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, operation)); stopAndRecord != nil { +- defer stopAndRecord() +- } +- for i := 0; i < b.N; i++ { +- edits := atomic.AddInt64(&editID, 1) +- env.EditBuffer(test.file, protocol.TextEdit{ +- Range: protocol.Range{ +- Start: protocol.Position{Line: 0, Character: 0}, +- End: protocol.Position{Line: 1, Character: 0}, +- }, +- // Increment the placeholder text, to ensure cache misses. +- NewText: fmt.Sprintf("// __REGTEST_PLACEHOLDER_%d__\n", edits), +- }) +- if save { +- env.SaveBuffer(test.file) +- } +- env.AfterChange() +- } +- }) +- }) -} --/*⇒4,keyword,[]*/type /*⇒1,type,[definition]*/D /*⇒4,keyword,[]*/func(/*⇒2,parameter,[definition]*/aa /*⇒2,type,[]*/AA) (/*⇒2,parameter,[definition]*/BB /*⇒5,type,[]*/error) --/*⇒4,keyword,[]*/type /*⇒1,type,[definition]*/E /*⇒4,keyword,[]*/func(/*⇒2,type,[]*/AA) /*⇒2,type,[]*/BB +diff -urN a/gopls/internal/regtest/bench/doc.go b/gopls/internal/regtest/bench/doc.go +--- a/gopls/internal/regtest/bench/doc.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/doc.go 1970-01-01 08:00:00 +@@ -1,40 +0,0 @@ +-// Copyright 2023 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. - --/*⇒3,keyword,[]*/var /*⇒1,variable,[definition]*/a /*⇒4,keyword,[]*/chan/*⇒2,operator,[]*/<- /*⇒4,keyword,[]*/chan /*⇒3,type,[defaultLibrary]*/int --/*⇒3,keyword,[]*/var /*⇒1,variable,[definition]*/b /*⇒4,keyword,[]*/chan/*⇒2,operator,[]*/<- /*⇒2,operator,[]*/<-/*⇒4,keyword,[]*/chan /*⇒3,type,[defaultLibrary]*/int --/*⇒3,keyword,[]*/var /*⇒1,variable,[definition]*/c /*⇒2,operator,[]*/<-/*⇒4,keyword,[]*/chan /*⇒2,operator,[]*/<-/*⇒4,keyword,[]*/chan /*⇒3,type,[defaultLibrary]*/int +-// The bench package implements benchmarks for various LSP operations. +-// +-// Benchmarks check out specific commits of popular and/or exemplary +-// repositories, and script an external gopls process via a fake text editor. +-// By default, benchmarks run the test executable as gopls (using a special +-// "gopls mode" environment variable). A different gopls binary may be used by +-// setting the -gopls_path or -gopls_commit flags. +-// +-// This package is a work in progress. +-// +-// # Profiling +-// +-// Benchmark functions run gopls in a separate process, which means the normal +-// test flags for profiling aren't useful. Instead the -gopls_cpuprofile, +-// -gopls_memprofile, -gopls_allocprofile, and -gopls_trace flags may be used +-// to pass through profiling to the gopls subproces. +-// +-// Each of these flags sets a suffix for the respective gopls profile, which is +-// named according to the schema ... For example, +-// setting -gopls_cpuprofile=cpu will result in profiles named tools.iwl.cpu, +-// tools.rename.cpu, etc. In some cases, these profiles are for the entire +-// gopls subprocess (as in the initial workspace load), whereas in others they +-// span only the critical section of the benchmark. It is up to each benchmark +-// to implement profiling as appropriate. +-// +-// # Integration with perf.golang.org +-// +-// Benchmarks that run with -short are automatically tracked by +-// perf.golang.org, at +-// https://perf.golang.org/dashboard/?benchmark=all&repository=tools&branch=release-branch.go1.20 +-// +-// # TODO +-// - add more benchmarks, and more repositories +-// - fix the perf dashboard to not require the branch= parameter +-// - improve this documentation +-package bench +diff -urN a/gopls/internal/regtest/bench/hover_test.go b/gopls/internal/regtest/bench/hover_test.go +--- a/gopls/internal/regtest/bench/hover_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/hover_test.go 1970-01-01 08:00:00 +@@ -1,47 +0,0 @@ +-// Copyright 2023 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. - -diff -urN a/gopls/internal/lsp/testdata/semantic/README.md b/gopls/internal/lsp/testdata/semantic/README.md ---- a/gopls/internal/lsp/testdata/semantic/README.md 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/semantic/README.md 1970-01-01 00:00:00.000000000 +0000 -@@ -1,2 +0,0 @@ --The golden files are the output of `gopls semtok `, with `-- semantic --` --inserted as the first line (the spaces are mandatory) and an extra newline at the end. -diff -urN a/gopls/internal/lsp/testdata/semantic/semantic_test.go b/gopls/internal/lsp/testdata/semantic/semantic_test.go ---- a/gopls/internal/lsp/testdata/semantic/semantic_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/semantic/semantic_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package semantictokens +-package bench - -import ( -- "os" - "testing" -) - --func TestSemanticTokens(t *testing.T) { -- a, _ := os.Getwd() -- // climb up to find internal/lsp -- // find all the .go files +-func BenchmarkHover(b *testing.B) { +- tests := []struct { +- repo string +- file string +- regexp string +- }{ +- {"google-cloud-go", "httpreplay/httpreplay.go", `proxy\.(ForRecording)`}, +- {"istio", "pkg/config/model.go", `gogotypes\.(MarshalAny)`}, +- {"kubernetes", "pkg/apis/core/types.go", "type (Pod)"}, +- {"kuma", "api/generic/insights.go", `proto\.(Message)`}, +- {"pkgsite", "internal/log/log.go", `derrors\.(Wrap)`}, +- {"starlark", "starlark/eval.go", "prog.compiled.(Encode)"}, +- {"tools", "internal/lsp/cache/check.go", `(snapshot)\) buildKey`}, +- } - --} -diff -urN a/gopls/internal/lsp/testdata/signature/signature2.go.golden b/gopls/internal/lsp/testdata/signature/signature2.go.golden ---- a/gopls/internal/lsp/testdata/signature/signature2.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/signature/signature2.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ ---- Foo(a string, b int) (c bool)-signature -- --Foo(a string, b int) (c bool) +- for _, test := range tests { +- b.Run(test.repo, func(b *testing.B) { +- env := getRepo(b, test.repo).sharedEnv(b) +- env.OpenFile(test.file) +- defer closeBuffer(b, env, test.file) - -diff -urN a/gopls/internal/lsp/testdata/signature/signature2.go.in b/gopls/internal/lsp/testdata/signature/signature2.go.in ---- a/gopls/internal/lsp/testdata/signature/signature2.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/signature/signature2.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,5 +0,0 @@ --package signature +- loc := env.RegexpSearch(test.file, test.regexp) +- env.AfterChange() - --func _() { -- Foo(//@signature("//", "Foo(a string, b int) (c bool)", 0) --} -diff -urN a/gopls/internal/lsp/testdata/signature/signature3.go.golden b/gopls/internal/lsp/testdata/signature/signature3.go.golden ---- a/gopls/internal/lsp/testdata/signature/signature3.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/signature/signature3.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ ---- Foo(a string, b int) (c bool)-signature -- --Foo(a string, b int) (c bool) +- env.Hover(loc) // pre-warm the query +- b.ResetTimer() - -diff -urN a/gopls/internal/lsp/testdata/signature/signature3.go.in b/gopls/internal/lsp/testdata/signature/signature3.go.in ---- a/gopls/internal/lsp/testdata/signature/signature3.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/signature/signature3.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,5 +0,0 @@ --package signature +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "hover")); stopAndRecord != nil { +- defer stopAndRecord() +- } - --func _() { -- Foo("hello",//@signature("//", "Foo(a string, b int) (c bool)", 1) +- for i := 0; i < b.N; i++ { +- env.Hover(loc) // pre-warm the query +- } +- }) +- } -} -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/signature/signature.go b/gopls/internal/lsp/testdata/signature/signature.go ---- a/gopls/internal/lsp/testdata/signature/signature.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/signature/signature.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,85 +0,0 @@ --// Package signature has tests for signature help. --package signature +diff -urN a/gopls/internal/regtest/bench/implementations_test.go b/gopls/internal/regtest/bench/implementations_test.go +--- a/gopls/internal/regtest/bench/implementations_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/implementations_test.go 1970-01-01 08:00:00 +@@ -1,44 +0,0 @@ +-// Copyright 2023 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. - --import ( -- "bytes" -- "encoding/json" -- "math/big" --) +-package bench - --func Foo(a string, b int) (c bool) { -- return --} +-import "testing" - --func Bar(float64, ...byte) { --} +-func BenchmarkImplementations(b *testing.B) { +- tests := []struct { +- repo string +- file string +- regexp string +- }{ +- {"google-cloud-go", "httpreplay/httpreplay.go", `type (Recorder)`}, +- {"istio", "pkg/config/mesh/watcher.go", `type (Watcher)`}, +- {"kubernetes", "pkg/controller/lookup_cache.go", `objectWithMeta`}, +- {"kuma", "api/generic/insights.go", `type (Insight)`}, +- {"pkgsite", "internal/datasource.go", `type (DataSource)`}, +- {"starlark", "syntax/syntax.go", `type (Expr)`}, +- {"tools", "internal/lsp/source/view.go", `type (Snapshot)`}, +- } +- +- for _, test := range tests { +- b.Run(test.repo, func(b *testing.B) { +- env := getRepo(b, test.repo).sharedEnv(b) +- env.OpenFile(test.file) +- defer closeBuffer(b, env, test.file) - --type myStruct struct{} +- loc := env.RegexpSearch(test.file, test.regexp) +- env.AfterChange() +- env.Implementations(loc) // pre-warm the query +- b.ResetTimer() - --func (*myStruct) foo(e *json.Decoder) (*big.Int, error) { -- return nil, nil --} +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "implementations")); stopAndRecord != nil { +- defer stopAndRecord() +- } - --type MyType struct{} +- for i := 0; i < b.N; i++ { +- env.Implementations(loc) +- } +- }) +- } +-} +diff -urN a/gopls/internal/regtest/bench/iwl_test.go b/gopls/internal/regtest/bench/iwl_test.go +--- a/gopls/internal/regtest/bench/iwl_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/iwl_test.go 1970-01-01 08:00:00 +@@ -1,76 +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. - --type MyFunc func(foo int) string +-package bench - --type Alias = int --type OtherAlias = int --type StringAlias = string +-import ( +- "testing" - --func AliasSlice(a []*Alias) (b Alias) { return 0 } --func AliasMap(a map[*Alias]StringAlias) (b, c map[*Alias]StringAlias) { return nil, nil } --func OtherAliasMap(a, b map[Alias]OtherAlias) map[Alias]OtherAlias { return nil } +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/fake" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) - --func Qux() { -- Foo("foo", 123) //@signature("(", "Foo(a string, b int) (c bool)", 0) -- Foo("foo", 123) //@signature("123", "Foo(a string, b int) (c bool)", 1) -- Foo("foo", 123) //@signature(",", "Foo(a string, b int) (c bool)", 0) -- Foo("foo", 123) //@signature(" 1", "Foo(a string, b int) (c bool)", 1) -- Foo("foo", 123) //@signature(")", "Foo(a string, b int) (c bool)", 1) +-// BenchmarkInitialWorkspaceLoad benchmarks the initial workspace load time for +-// a new editing session. +-func BenchmarkInitialWorkspaceLoad(b *testing.B) { +- tests := []struct { +- repo string +- file string +- }{ +- {"google-cloud-go", "httpreplay/httpreplay.go"}, +- {"istio", "pkg/fuzz/util.go"}, +- {"kubernetes", "pkg/controller/lookup_cache.go"}, +- {"kuma", "api/generic/insights.go"}, +- {"oracle", "dataintegration/data_type.go"}, +- {"pkgsite", "internal/frontend/server.go"}, +- {"starlark", "starlark/eval.go"}, +- {"tools", "internal/lsp/cache/snapshot.go"}, +- {"hashiform", "internal/provider/provider.go"}, +- } - -- Bar(13.37, 0x13) //@signature("13.37", "Bar(float64, ...byte)", 0) -- Bar(13.37, 0x37) //@signature("0x37", "Bar(float64, ...byte)", 1) -- Bar(13.37, 1, 2, 3, 4) //@signature("4", "Bar(float64, ...byte)", 1) +- for _, test := range tests { +- b.Run(test.repo, func(b *testing.B) { +- repo := getRepo(b, test.repo) +- // get the (initialized) shared env to ensure the cache is warm. +- // Reuse its GOPATH so that we get cache hits for things in the module +- // cache. +- sharedEnv := repo.sharedEnv(b) +- b.ResetTimer() - -- fn := func(hi, there string) func(i int) rune { -- return func(int) rune { return 0 } +- for i := 0; i < b.N; i++ { +- doIWL(b, sharedEnv.Sandbox.GOPATH(), repo, test.file) +- } +- }) - } +-} - -- fn("hi", "there") //@signature("hi", "", 0) -- fn("hi", "there") //@signature(",", "fn(hi string, there string) func(i int) rune", 0) -- fn("hi", "there")(1) //@signature("1", "func(i int) rune", 0) +-func doIWL(b *testing.B, gopath string, repo *repo, file string) { +- // Exclude the time to set up the env from the benchmark time, as this may +- // involve installing gopls and/or checking out the repo dir. +- b.StopTimer() +- config := fake.EditorConfig{Env: map[string]string{"GOPATH": gopath}} +- env := repo.newEnv(b, config, "iwl", true) +- defer env.Close() +- b.StartTimer() - -- fnPtr := &fn -- (*fnPtr)("hi", "there") //@signature(",", "func(hi string, there string) func(i int) rune", 0) +- // Note: in the future, we may need to open a file in order to cause gopls to +- // start loading the workspace. - -- var fnIntf interface{} = Foo -- fnIntf.(func(string, int) bool)("hi", 123) //@signature("123", "func(string, int) bool", 1) +- env.Await(InitialWorkspaceLoad) - -- (&bytes.Buffer{}).Next(2) //@signature("2", "Next(n int) []byte", 0) +- if env.Editor.HasCommand(command.MemStats.ID()) { +- b.StopTimer() +- params := &protocol.ExecuteCommandParams{ +- Command: command.MemStats.ID(), +- } +- var memstats command.MemStatsResult +- env.ExecuteCommand(params, &memstats) +- b.ReportMetric(float64(memstats.HeapAlloc), "alloc_bytes") +- b.ReportMetric(float64(memstats.HeapInUse), "in_use_bytes") +- b.ReportMetric(float64(memstats.TotalAlloc), "total_alloc_bytes") +- b.StartTimer() +- } +-} +diff -urN a/gopls/internal/regtest/bench/references_test.go b/gopls/internal/regtest/bench/references_test.go +--- a/gopls/internal/regtest/bench/references_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/references_test.go 1970-01-01 08:00:00 +@@ -1,44 +0,0 @@ +-// Copyright 2023 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. - -- myFunc := MyFunc(func(n int) string { return "" }) -- myFunc(123) //@signature("123", "myFunc(foo int) string", 0) +-package bench - -- var ms myStruct -- ms.foo(nil) //@signature("nil", "foo(e *json.Decoder) (*big.Int, error)", 0) +-import "testing" - -- _ = make([]int, 1, 2) //@signature("2", "make(t Type, size ...int) Type", 1) +-func BenchmarkReferences(b *testing.B) { +- tests := []struct { +- repo string +- file string +- regexp string +- }{ +- {"google-cloud-go", "httpreplay/httpreplay.go", `func (NewRecorder)`}, +- {"istio", "pkg/config/model.go", "type (Meta)"}, +- {"kubernetes", "pkg/controller/lookup_cache.go", "type (objectWithMeta)"}, // TODO: choose an exported identifier +- {"kuma", "pkg/events/interfaces.go", "type (Event)"}, +- {"pkgsite", "internal/log/log.go", "func (Infof)"}, +- {"starlark", "syntax/syntax.go", "type (Ident)"}, +- {"tools", "internal/lsp/source/view.go", "type (Snapshot)"}, +- } - -- Foo(myFunc(123), 456) //@signature("myFunc", "Foo(a string, b int) (c bool)", 0) -- Foo(myFunc(123), 456) //@signature("123", "myFunc(foo int) string", 0) +- for _, test := range tests { +- b.Run(test.repo, func(b *testing.B) { +- env := getRepo(b, test.repo).sharedEnv(b) +- env.OpenFile(test.file) +- defer closeBuffer(b, env, test.file) - -- panic("oops!") //@signature(")", "panic(v interface{})", 0) -- println("hello", "world") //@signature(",", "println(args ...Type)", 0) +- loc := env.RegexpSearch(test.file, test.regexp) +- env.AfterChange() +- env.References(loc) // pre-warm the query +- b.ResetTimer() - -- Hello(func() { -- //@signature("//", "", 0) -- }) +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "references")); stopAndRecord != nil { +- defer stopAndRecord() +- } - -- AliasSlice() //@signature(")", "AliasSlice(a []*Alias) (b Alias)", 0) -- AliasMap() //@signature(")", "AliasMap(a map[*Alias]StringAlias) (b map[*Alias]StringAlias, c map[*Alias]StringAlias)", 0) -- OtherAliasMap() //@signature(")", "OtherAliasMap(a map[Alias]OtherAlias, b map[Alias]OtherAlias) map[Alias]OtherAlias", 0) +- for i := 0; i < b.N; i++ { +- env.References(loc) +- } +- }) +- } -} +diff -urN a/gopls/internal/regtest/bench/reload_test.go b/gopls/internal/regtest/bench/reload_test.go +--- a/gopls/internal/regtest/bench/reload_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/reload_test.go 1970-01-01 08:00:00 +@@ -1,52 +0,0 @@ +-// Copyright 2023 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 - --func Hello(func()) {} -diff -urN a/gopls/internal/lsp/testdata/signature/signature.go.golden b/gopls/internal/lsp/testdata/signature/signature.go.golden ---- a/gopls/internal/lsp/testdata/signature/signature.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/signature/signature.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,53 +0,0 @@ ---- AliasMap(a map[*Alias]StringAlias) (b map[*Alias]StringAlias, c map[*Alias]StringAlias)-signature -- --AliasMap(a map[*Alias]StringAlias) (b map[*Alias]StringAlias, c map[*Alias]StringAlias) +-import ( +- "testing" - ---- AliasSlice(a []*Alias) (b Alias)-signature -- --AliasSlice(a []*Alias) (b Alias) +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) - ---- Bar(float64, ...byte)-signature -- --Bar(float64, ...byte) +-// BenchmarkReload benchmarks reloading a file metadata after a change to an import. +-// +-// This ensures we are able to diagnose a changed file without reloading all +-// invalidated packages. See also golang/go#61344 +-func BenchmarkReload(b *testing.B) { +- // TODO(rfindley): add more tests, make this test table-driven +- const ( +- repo = "kubernetes" +- // pkg/util/hash is transitively imported by a large number of packages. +- // We should not need to reload those packages to get a diagnostic. +- file = "pkg/util/hash/hash.go" +- ) +- b.Run(repo, func(b *testing.B) { +- env := getRepo(b, repo).sharedEnv(b) - ---- Foo(a string, b int) (c bool)-signature -- --Foo(a string, b int) (c bool) +- env.OpenFile(file) +- defer closeBuffer(b, env, file) - ---- Next(n int) []byte-signature -- --Next(n int) []byte +- env.AfterChange() - --Next returns a slice containing the next n bytes from the buffer, advancing the buffer as if the bytes had been returned by Read. +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(repo, "reload")); stopAndRecord != nil { +- defer stopAndRecord() +- } - ---- OtherAliasMap(a map[Alias]OtherAlias, b map[Alias]OtherAlias) map[Alias]OtherAlias-signature -- --OtherAliasMap(a map[Alias]OtherAlias, b map[Alias]OtherAlias) map[Alias]OtherAlias +- b.ResetTimer() +- for i := 0; i < b.N; i++ { +- // Change the "hash" import. This may result in cache hits, but that's +- // OK: the goal is to ensure that we don't reload more than just the +- // current package. +- env.RegexpReplace(file, `"hash"`, `"hashx"`) +- // Note: don't use env.AfterChange() here: we only want to await the +- // first diagnostic. +- // +- // Awaiting a full diagnosis would await diagnosing everything, which +- // would require reloading everything. +- env.Await(Diagnostics(ForFile(file))) +- env.RegexpReplace(file, `"hashx"`, `"hash"`) +- env.Await(NoDiagnostics(ForFile(file))) +- } +- }) +-} +diff -urN a/gopls/internal/regtest/bench/rename_test.go b/gopls/internal/regtest/bench/rename_test.go +--- a/gopls/internal/regtest/bench/rename_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/rename_test.go 1970-01-01 08:00:00 +@@ -1,49 +0,0 @@ +-// Copyright 2023 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. - ---- fn(hi string, there string) func(i int) rune-signature -- --fn(hi string, there string) func(i int) rune +-package bench - ---- foo(e *json.Decoder) (*big.Int, error)-signature -- --foo(e *json.Decoder) (*big.Int, error) +-import ( +- "fmt" +- "testing" +-) - ---- func(hi string, there string) func(i int) rune-signature -- --func(hi string, there string) func(i int) rune +-func BenchmarkRename(b *testing.B) { +- tests := []struct { +- repo string +- file string +- regexp string +- baseName string +- }{ +- {"google-cloud-go", "httpreplay/httpreplay.go", `func (NewRecorder)`, "NewRecorder"}, +- {"istio", "pkg/config/model.go", `(Namespace) string`, "Namespace"}, +- {"kubernetes", "pkg/controller/lookup_cache.go", `hashutil\.(DeepHashObject)`, "DeepHashObject"}, +- {"kuma", "pkg/events/interfaces.go", `Delete`, "Delete"}, +- {"pkgsite", "internal/log/log.go", `func (Infof)`, "Infof"}, +- {"starlark", "starlark/eval.go", `Program\) (Filename)`, "Filename"}, +- {"tools", "internal/lsp/cache/snapshot.go", `meta \*(metadataGraph)`, "metadataGraph"}, +- } - ---- func(i int) rune-signature -- --func(i int) rune +- for _, test := range tests { +- names := 0 // bench function may execute multiple times +- b.Run(test.repo, func(b *testing.B) { +- env := getRepo(b, test.repo).sharedEnv(b) +- env.OpenFile(test.file) +- loc := env.RegexpSearch(test.file, test.regexp) +- env.Await(env.DoneWithOpen()) +- env.Rename(loc, test.baseName+"X") // pre-warm the query +- b.ResetTimer() - ---- func(string, int) bool-signature -- --func(string, int) bool +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "rename")); stopAndRecord != nil { +- defer stopAndRecord() +- } - ---- make(t Type, size ...int) Type-signature -- --make(t Type, size ...int) Type +- for i := 0; i < b.N; i++ { +- names++ +- newName := fmt.Sprintf("%s%d", test.baseName, names) +- env.Rename(loc, newName) +- } +- }) +- } +-} +diff -urN a/gopls/internal/regtest/bench/repo_test.go b/gopls/internal/regtest/bench/repo_test.go +--- a/gopls/internal/regtest/bench/repo_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/repo_test.go 1970-01-01 08:00:00 +@@ -1,290 +0,0 @@ +-// Copyright 2023 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. - --The make built-in function allocates and initializes an object of type slice, map, or chan (only). +-package bench - ---- myFunc(foo int) string-signature -- --myFunc(foo int) string +-import ( +- "bytes" +- "context" +- "errors" +- "flag" +- "fmt" +- "log" +- "os" +- "path/filepath" +- "sync" +- "testing" +- "time" - ---- panic(v interface{})-signature -- --panic(v any) +- "golang.org/x/tools/gopls/internal/lsp/fake" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) - --The panic built-in function stops normal execution of the current goroutine. +-// repos holds shared repositories for use in benchmarks. +-// +-// These repos were selected to represent a variety of different types of +-// codebases. +-var repos = map[string]*repo{ +- // google-cloud-go has 145 workspace modules (!), and is quite large. +- "google-cloud-go": { +- name: "google-cloud-go", +- url: "https://github.com/googleapis/google-cloud-go.git", +- commit: "07da765765218debf83148cc7ed8a36d6e8921d5", +- inDir: flag.String("cloud_go_dir", "", "if set, reuse this directory as google-cloud-go@07da7657"), +- }, - ---- println(args ...Type)-signature -- --println(args ...Type) +- // Used by x/benchmarks; large. +- "istio": { +- name: "istio", +- url: "https://github.com/istio/istio", +- commit: "1.17.0", +- inDir: flag.String("istio_dir", "", "if set, reuse this directory as istio@v1.17.0"), +- }, - --The println built-in function formats its arguments in an implementation-specific way and writes the result to standard error. +- // Kubernetes is a large repo with many dependencies, and in the past has +- // been about as large a repo as gopls could handle. +- "kubernetes": { +- name: "kubernetes", +- url: "https://github.com/kubernetes/kubernetes", +- commit: "v1.24.0", +- short: true, +- inDir: flag.String("kubernetes_dir", "", "if set, reuse this directory as kubernetes@v1.24.0"), +- }, - -diff -urN a/gopls/internal/lsp/testdata/signature/signature_test.go b/gopls/internal/lsp/testdata/signature/signature_test.go ---- a/gopls/internal/lsp/testdata/signature/signature_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/signature/signature_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package signature_test +- // A large, industrial application. +- "kuma": { +- name: "kuma", +- url: "https://github.com/kumahq/kuma", +- commit: "2.1.1", +- inDir: flag.String("kuma_dir", "", "if set, reuse this directory as kuma@v2.1.1"), +- }, - --import ( -- "testing" +- // A repo containing a very large package (./dataintegration). +- "oracle": { +- name: "oracle", +- url: "https://github.com/oracle/oci-go-sdk.git", +- commit: "v65.43.0", +- short: true, +- inDir: flag.String("oracle_dir", "", "if set, reuse this directory as oracle/oci-go-sdk@v65.43.0"), +- }, - -- sig "golang.org/lsptests/signature" --) +- // x/pkgsite is familiar and represents a common use case (a webserver). It +- // also has a number of static non-go files and template files. +- "pkgsite": { +- name: "pkgsite", +- url: "https://go.googlesource.com/pkgsite", +- commit: "81f6f8d4175ad0bf6feaa03543cc433f8b04b19b", +- short: true, +- inDir: flag.String("pkgsite_dir", "", "if set, reuse this directory as pkgsite@81f6f8d4"), +- }, - --func TestSignature(t *testing.T) { -- sig.AliasSlice() //@signature(")", "AliasSlice(a []*sig.Alias) (b sig.Alias)", 0) -- sig.AliasMap() //@signature(")", "AliasMap(a map[*sig.Alias]sig.StringAlias) (b map[*sig.Alias]sig.StringAlias, c map[*sig.Alias]sig.StringAlias)", 0) -- sig.OtherAliasMap() //@signature(")", "OtherAliasMap(a map[sig.Alias]sig.OtherAlias, b map[sig.Alias]sig.OtherAlias) map[sig.Alias]sig.OtherAlias", 0) --} -diff -urN a/gopls/internal/lsp/testdata/signature/signature_test.go.golden b/gopls/internal/lsp/testdata/signature/signature_test.go.golden ---- a/gopls/internal/lsp/testdata/signature/signature_test.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/signature/signature_test.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ ---- AliasMap(a map[*sig.Alias]sig.StringAlias) (b map[*sig.Alias]sig.StringAlias, c map[*sig.Alias]sig.StringAlias)-signature -- --AliasMap(a map[*sig.Alias]sig.StringAlias) (b map[*sig.Alias]sig.StringAlias, c map[*sig.Alias]sig.StringAlias) +- // A tiny self-contained project. +- "starlark": { +- name: "starlark", +- url: "https://github.com/google/starlark-go", +- commit: "3f75dec8e4039385901a30981e3703470d77e027", +- short: true, +- inDir: flag.String("starlark_dir", "", "if set, reuse this directory as starlark@3f75dec8"), +- }, - ---- AliasSlice(a []*sig.Alias) (b sig.Alias)-signature -- --AliasSlice(a []*sig.Alias) (b sig.Alias) +- // The current repository, which is medium-small and has very few dependencies. +- "tools": { +- name: "tools", +- url: "https://go.googlesource.com/tools", +- commit: "gopls/v0.9.0", +- short: true, +- inDir: flag.String("tools_dir", "", "if set, reuse this directory as x/tools@v0.9.0"), +- }, - ---- OtherAliasMap(a map[sig.Alias]sig.OtherAlias, b map[sig.Alias]sig.OtherAlias) map[sig.Alias]sig.OtherAlias-signature -- --OtherAliasMap(a map[sig.Alias]sig.OtherAlias, b map[sig.Alias]sig.OtherAlias) map[sig.Alias]sig.OtherAlias +- // A repo of similar size to kubernetes, but with substantially more +- // complex types that led to a serious performance regression (issue #60621). +- "hashiform": { +- name: "hashiform", +- url: "https://github.com/hashicorp/terraform-provider-aws", +- commit: "ac55de2b1950972d93feaa250d7505d9ed829c7c", +- inDir: flag.String("hashiform_dir", "", "if set, reuse this directory as hashiform@ac55de2"), +- }, +-} - -diff -urN a/gopls/internal/lsp/testdata/snippets/func_snippets118.go.in b/gopls/internal/lsp/testdata/snippets/func_snippets118.go.in ---- a/gopls/internal/lsp/testdata/snippets/func_snippets118.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/snippets/func_snippets118.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,19 +0,0 @@ --// +build go1.18 --//go:build go1.18 +-// getRepo gets the requested repo, and skips the test if -short is set and +-// repo is not configured as a short repo. +-func getRepo(tb testing.TB, name string) *repo { +- tb.Helper() +- repo := repos[name] +- if repo == nil { +- tb.Fatalf("repo %s does not exist", name) +- } +- if !repo.short && testing.Short() { +- tb.Skipf("large repo %s does not run with -short", repo.name) +- } +- return repo +-} - --package snippets +-// A repo represents a working directory for a repository checked out at a +-// specific commit. +-// +-// Repos are used for sharing state across benchmarks that operate on the same +-// codebase. +-type repo struct { +- // static configuration +- name string // must be unique, used for subdirectory +- url string // repo url +- commit string // full commit hash or tag +- short bool // whether this repo runs with -short +- inDir *string // if set, use this dir as url@commit, and don't delete - --type SyncMap[K comparable, V any] struct{} +- dirOnce sync.Once +- dir string // directory contaning source code checked out to url@commit - --func NewSyncMap[K comparable, V any]() (result *SyncMap[K, V]) { //@item(NewSyncMap, "NewSyncMap", "", "") -- return +- // shared editor state +- editorOnce sync.Once +- editor *fake.Editor +- sandbox *fake.Sandbox +- awaiter *Awaiter -} - --func Identity[P ~int](p P) P { //@item(Identity, "Identity", "", "") -- return p +-// reusableDir return a reusable directory for benchmarking, or "". +-// +-// If the user specifies a directory, the test will create and populate it +-// on the first run an re-use it on subsequent runs. Otherwise it will +-// create, populate, and delete a temporary directory. +-func (r *repo) reusableDir() string { +- if r.inDir == nil { +- return "" +- } +- return *r.inDir -} - --func _() { -- _ = NewSyncM //@snippet(" //", NewSyncMap, "NewSyncMap[${1:}]()", "NewSyncMap[${1:K comparable}, ${2:V any}]()") -- _ = Identi //@snippet(" //", Identity, "Identity[${1:}](${2:})", "Identity[${1:P ~int}](${2:p P})") +-// getDir returns directory containing repo source code, creating it if +-// necessary. It is safe for concurrent use. +-func (r *repo) getDir() string { +- r.dirOnce.Do(func() { +- if r.dir = r.reusableDir(); r.dir == "" { +- r.dir = filepath.Join(getTempDir(), r.name) +- } +- +- _, err := os.Stat(r.dir) +- switch { +- case os.IsNotExist(err): +- log.Printf("cloning %s@%s into %s", r.url, r.commit, r.dir) +- if err := shallowClone(r.dir, r.url, r.commit); err != nil { +- log.Fatal(err) +- } +- case err != nil: +- log.Fatal(err) +- default: +- log.Printf("reusing %s as %s@%s", r.dir, r.url, r.commit) +- } +- }) +- return r.dir -} -diff -urN a/gopls/internal/lsp/testdata/snippets/literal.go b/gopls/internal/lsp/testdata/snippets/literal.go ---- a/gopls/internal/lsp/testdata/snippets/literal.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/snippets/literal.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,22 +0,0 @@ --package snippets - --import ( -- "golang.org/lsptests/signature" -- t "golang.org/lsptests/types" --) +-// sharedEnv returns a shared benchmark environment. It is safe for concurrent +-// use. +-// +-// Every call to sharedEnv uses the same editor and sandbox, as a means to +-// avoid reinitializing the editor for large repos. Calling repo.Close cleans +-// up the shared environment. +-// +-// Repos in the package-local Repos var are closed at the end of the test main +-// function. +-func (r *repo) sharedEnv(tb testing.TB) *Env { +- r.editorOnce.Do(func() { +- dir := r.getDir() - --type structy struct { -- x signature.MyType --} +- start := time.Now() +- log.Printf("starting initial workspace load for %s", r.name) +- ts, err := newGoplsConnector(profileArgs(r.name, false)) +- if err != nil { +- log.Fatal(err) +- } +- r.sandbox, r.editor, r.awaiter, err = connectEditor(dir, fake.EditorConfig{}, ts) +- if err != nil { +- log.Fatalf("connecting editor: %v", err) +- } - --func X(_ map[signature.Alias]t.CoolAlias) (map[signature.Alias]t.CoolAlias) { -- return nil --} +- if err := r.awaiter.Await(context.Background(), InitialWorkspaceLoad); err != nil { +- log.Fatal(err) +- } +- log.Printf("initial workspace load (cold) for %s took %v", r.name, time.Since(start)) +- }) - --func _() { -- X() //@signature(")", "X(_ map[signature.Alias]t.CoolAlias) map[signature.Alias]t.CoolAlias", 0) -- _ = signature.MyType{} //@item(literalMyType, "signature.MyType{}", "", "var") -- s := structy{ -- x: //@snippet(" //", literalMyType, "signature.MyType{\\}", "signature.MyType{\\}") +- return &Env{ +- T: tb, +- Ctx: context.Background(), +- Editor: r.editor, +- Sandbox: r.sandbox, +- Awaiter: r.awaiter, - } -} -\ No newline at end of file -diff -urN a/gopls/internal/lsp/testdata/snippets/literal.go.golden b/gopls/internal/lsp/testdata/snippets/literal.go.golden ---- a/gopls/internal/lsp/testdata/snippets/literal.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/snippets/literal.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ ---- X(_ map[signature.Alias]t.CoolAlias) map[signature.Alias]t.CoolAlias-signature -- --X(_ map[signature.Alias]t.CoolAlias) map[signature.Alias]t.CoolAlias - -diff -urN a/gopls/internal/lsp/testdata/snippets/literal_snippets118.go.in b/gopls/internal/lsp/testdata/snippets/literal_snippets118.go.in ---- a/gopls/internal/lsp/testdata/snippets/literal_snippets118.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/snippets/literal_snippets118.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ --// +build go1.18 --//go:build go1.18 +-// newEnv returns a new Env connected to a new gopls process communicating +-// over stdin/stdout. It is safe for concurrent use. +-// +-// It is the caller's responsibility to call Close on the resulting Env when it +-// is no longer needed. +-func (r *repo) newEnv(tb testing.TB, config fake.EditorConfig, forOperation string, cpuProfile bool) *Env { +- dir := r.getDir() - --package snippets +- args := profileArgs(qualifiedName(r.name, forOperation), cpuProfile) +- ts, err := newGoplsConnector(args) +- if err != nil { +- tb.Fatal(err) +- } +- sandbox, editor, awaiter, err := connectEditor(dir, config, ts) +- if err != nil { +- log.Fatalf("connecting editor: %v", err) +- } - --type Tree[T any] struct{} +- return &Env{ +- T: tb, +- Ctx: context.Background(), +- Editor: editor, +- Sandbox: sandbox, +- Awaiter: awaiter, +- } +-} - --func (tree Tree[T]) Do(f func(s T)) {} +-// Close cleans up shared state referenced by the repo. +-func (r *repo) Close() error { +- var errBuf bytes.Buffer +- if r.editor != nil { +- if err := r.editor.Close(context.Background()); err != nil { +- fmt.Fprintf(&errBuf, "closing editor: %v", err) +- } +- } +- if r.sandbox != nil { +- if err := r.sandbox.Close(); err != nil { +- fmt.Fprintf(&errBuf, "closing sandbox: %v", err) +- } +- } +- if r.dir != "" && r.reusableDir() == "" { +- if err := os.RemoveAll(r.dir); err != nil { +- fmt.Fprintf(&errBuf, "cleaning dir: %v", err) +- } +- } +- if errBuf.Len() > 0 { +- return errors.New(errBuf.String()) +- } +- return nil +-} - --func _() { -- _ = "func(...) {}" //@item(litFunc, "func(...) {}", "", "var") -- var t Tree[string] -- t.Do(fun) //@complete(")", litFunc),snippet(")", litFunc, "func(s string) {$0\\}", "func(s string) {$0\\}") +-// cleanup cleans up state that is shared across benchmark functions. +-func cleanup() error { +- var errBuf bytes.Buffer +- for _, repo := range repos { +- if err := repo.Close(); err != nil { +- fmt.Fprintf(&errBuf, "closing %q: %v", repo.name, err) +- } +- } +- if tempDir != "" { +- if err := os.RemoveAll(tempDir); err != nil { +- fmt.Fprintf(&errBuf, "cleaning tempDir: %v", err) +- } +- } +- if errBuf.Len() > 0 { +- return errors.New(errBuf.String()) +- } +- return nil -} -diff -urN a/gopls/internal/lsp/testdata/snippets/literal_snippets.go.in b/gopls/internal/lsp/testdata/snippets/literal_snippets.go.in ---- a/gopls/internal/lsp/testdata/snippets/literal_snippets.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/snippets/literal_snippets.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,233 +0,0 @@ --package snippets +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 08:00:00 +@@ -1,94 +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 ( -- "bytes" - "context" -- "go/ast" -- "net/http" -- "sort" +- "flag" +- "fmt" +- "testing" +- "time" - -- "golang.org/lsptests/foo" +- "golang.org/x/tools/gopls/internal/hooks" +- "golang.org/x/tools/gopls/internal/lsp/cache" +- "golang.org/x/tools/gopls/internal/lsp/fake" +- "golang.org/x/tools/gopls/internal/lsp/lsprpc" +- "golang.org/x/tools/internal/jsonrpc2" +- "golang.org/x/tools/internal/jsonrpc2/servertest" -) - --func _() { -- []int{} //@item(litIntSlice, "[]int{}", "", "var") -- &[]int{} //@item(litIntSliceAddr, "&[]int{}", "", "var") -- make([]int, 0) //@item(makeIntSlice, "make([]int, 0)", "", "func") -- -- var _ *[]int = in //@snippet(" //", litIntSliceAddr, "&[]int{$0\\}", "&[]int{$0\\}") -- var _ **[]int = in //@complete(" //") -- -- var slice []int -- slice = i //@snippet(" //", litIntSlice, "[]int{$0\\}", "[]int{$0\\}") -- slice = m //@snippet(" //", makeIntSlice, "make([]int, ${1:})", "make([]int, ${1:0})") --} -- --func _() { -- type namedInt []int -- -- namedInt{} //@item(litNamedSlice, "namedInt{}", "", "var") -- make(namedInt, 0) //@item(makeNamedSlice, "make(namedInt, 0)", "", "func") +-// github.com/pilosa/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 namedSlice namedInt -- namedSlice = n //@snippet(" //", litNamedSlice, "namedInt{$0\\}", "namedInt{$0\\}") -- namedSlice = m //@snippet(" //", makeNamedSlice, "make(namedInt, ${1:})", "make(namedInt, ${1:0})") --} +-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 _() { -- make(chan int) //@item(makeChan, "make(chan int)", "", "func") +-func TestPilosaStress(t *testing.T) { +- // TODO(rfindley): revisit this test and make it is hermetic: it should check +- // out pilosa into a directory. +- // +- // Note: This stress test has not been run recently, and may no longer +- // function properly. +- if *pilosaPath == "" { +- t.Skip("-pilosa_path not configured") +- } - -- var ch chan int -- ch = m //@snippet(" //", makeChan, "make(chan int)", "make(chan int)") --} +- sandbox, err := fake.NewSandbox(&fake.SandboxConfig{ +- Workdir: *pilosaPath, +- GOPROXY: "https://proxy.golang.org", +- }) +- if err != nil { +- t.Fatal(err) +- } - --func _() { -- map[string]struct{}{} //@item(litMap, "map[string]struct{}{}", "", "var") -- make(map[string]struct{}) //@item(makeMap, "make(map[string]struct{})", "", "func") +- server := lsprpc.NewStreamServer(cache.New(nil), false, hooks.Options) +- ts := servertest.NewPipeServer(server, jsonrpc2.NewRawStream) +- ctx := context.Background() - -- var m map[string]struct{} -- m = m //@snippet(" //", litMap, "map[string]struct{\\}{$0\\}", "map[string]struct{\\}{$0\\}") -- m = m //@snippet(" //", makeMap, "make(map[string]struct{\\})", "make(map[string]struct{\\})") +- const skipApplyEdits = false +- editor, err := fake.NewEditor(sandbox, fake.EditorConfig{}).Connect(ctx, ts, fake.ClientHooks{}, skipApplyEdits) +- if err != nil { +- t.Fatal(err) +- } - -- struct{}{} //@item(litEmptyStruct, "struct{}{}", "", "var") +- files := []string{ +- "cmd.go", +- "internal/private.pb.go", +- "roaring/roaring.go", +- "roaring/roaring_internal_test.go", +- "server/handler_test.go", +- } +- for _, file := range files { +- if err := editor.OpenFile(ctx, file); err != nil { +- t.Fatal(err) +- } +- } +- ctx, cancel := context.WithTimeout(ctx, 10*time.Minute) +- defer cancel() - -- m["hi"] = s //@snippet(" //", litEmptyStruct, "struct{\\}{\\}", "struct{\\}{\\}") +- i := 1 +- // MagicNumber is an identifier that occurs in roaring.go. Just change it +- // arbitrarily. +- if err := editor.RegexpReplace(ctx, "roaring/roaring.go", "MagicNumber", fmt.Sprintf("MagicNumber%d", 1)); err != nil { +- t.Fatal(err) +- } +- for { +- select { +- case <-ctx.Done(): +- return +- default: +- } +- if err := editor.RegexpReplace(ctx, "roaring/roaring.go", fmt.Sprintf("MagicNumber%d", i), fmt.Sprintf("MagicNumber%d", i+1)); err != nil { +- t.Fatal(err) +- } +- // Simulate (very fast) typing. +- // +- // Typing 80 wpm ~150ms per keystroke. +- time.Sleep(150 * time.Millisecond) +- i++ +- } -} +diff -urN a/gopls/internal/regtest/bench/typing_test.go b/gopls/internal/regtest/bench/typing_test.go +--- a/gopls/internal/regtest/bench/typing_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/typing_test.go 1970-01-01 08:00:00 +@@ -1,63 +0,0 @@ +-// Copyright 2023 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. - --func _() { -- type myStruct struct{ i int } //@item(myStructType, "myStruct", "struct{...}", "struct") +-package bench - -- myStruct{} //@item(litStruct, "myStruct{}", "", "var") -- &myStruct{} //@item(litStructPtr, "&myStruct{}", "", "var") +-import ( +- "fmt" +- "sync/atomic" +- "testing" +- "time" - -- var ms myStruct -- ms = m //@snippet(" //", litStruct, "myStruct{$0\\}", "myStruct{$0\\}") +- "golang.org/x/tools/gopls/internal/lsp/protocol" +-) - -- var msPtr *myStruct -- msPtr = m //@snippet(" //", litStructPtr, "&myStruct{$0\\}", "&myStruct{$0\\}") +-// BenchmarkTyping simulates typing steadily in a single file at different +-// paces. +-// +-// The key metric for this benchmark is not latency, but cpu_seconds per +-// operation. +-func BenchmarkTyping(b *testing.B) { +- for _, test := range didChangeTests { +- b.Run(test.repo, func(b *testing.B) { +- env := getRepo(b, test.repo).sharedEnv(b) +- env.OpenFile(test.file) +- defer closeBuffer(b, env, test.file) - -- msPtr = &m //@snippet(" //", litStruct, "myStruct{$0\\}", "myStruct{$0\\}") +- // Insert the text we'll be modifying at the top of the file. +- env.EditBuffer(test.file, protocol.TextEdit{NewText: "// __REGTEST_PLACEHOLDER_0__\n"}) +- env.AfterChange() - -- type myStructCopy struct { i int } //@item(myStructCopyType, "myStructCopy", "struct{...}", "struct") +- delays := []time.Duration{ +- 10 * time.Millisecond, // automated changes +- 50 * time.Millisecond, // very fast mashing, or fast key sequences +- 150 * time.Millisecond, // avg interval for 80wpm typing. +- } - -- // Don't offer literal completion for convertible structs. -- ms = myStruct //@complete(" //", litStruct, myStructType, myStructCopyType) +- for _, delay := range delays { +- b.Run(delay.String(), func(b *testing.B) { +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(test.repo, "typing")); stopAndRecord != nil { +- defer stopAndRecord() +- } +- ticker := time.NewTicker(delay) +- for i := 0; i < b.N; i++ { +- edits := atomic.AddInt64(&editID, 1) +- env.EditBuffer(test.file, protocol.TextEdit{ +- Range: protocol.Range{ +- Start: protocol.Position{Line: 0, Character: 0}, +- End: protocol.Position{Line: 1, Character: 0}, +- }, +- // Increment the placeholder text, to ensure cache misses. +- NewText: fmt.Sprintf("// __REGTEST_PLACEHOLDER_%d__\n", edits), +- }) +- <-ticker.C +- } +- b.StopTimer() +- ticker.Stop() +- env.AfterChange() // wait for all change processing to complete +- }) +- } +- }) +- } -} +diff -urN a/gopls/internal/regtest/bench/workspace_symbols_test.go b/gopls/internal/regtest/bench/workspace_symbols_test.go +--- a/gopls/internal/regtest/bench/workspace_symbols_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/bench/workspace_symbols_test.go 1970-01-01 08:00:00 +@@ -1,41 +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. - --type myImpl struct{} -- --func (myImpl) foo() {} -- --func (*myImpl) bar() {} -- --type myBasicImpl string +-package bench - --func (myBasicImpl) foo() {} +-import ( +- "flag" +- "fmt" +- "testing" +-) - --func _() { -- type myIntf interface { -- foo() -- } +-var symbolQuery = flag.String("symbol_query", "test", "symbol query to use in benchmark") - -- myImpl{} //@item(litImpl, "myImpl{}", "", "var") +-// BenchmarkWorkspaceSymbols benchmarks the time to execute a workspace symbols +-// request (controlled by the -symbol_query flag). +-func BenchmarkWorkspaceSymbols(b *testing.B) { +- for name := range repos { +- b.Run(name, func(b *testing.B) { +- env := getRepo(b, name).sharedEnv(b) +- symbols := env.Symbol(*symbolQuery) // warm the cache - -- var mi myIntf -- mi = m //@snippet(" //", litImpl, "myImpl{\\}", "myImpl{\\}") +- if testing.Verbose() { +- fmt.Println("Results:") +- for i, symbol := range symbols { +- fmt.Printf("\t%d. %s (%s)\n", i, symbol.Name, symbol.ContainerName) +- } +- } - -- myBasicImpl() //@item(litBasicImpl, "myBasicImpl()", "string", "var") +- b.ResetTimer() - -- mi = m //@snippet(" //", litBasicImpl, "myBasicImpl($0)", "myBasicImpl($0)") +- if stopAndRecord := startProfileIfSupported(b, env, qualifiedName(name, "workspaceSymbols")); stopAndRecord != nil { +- defer stopAndRecord() +- } - -- // only satisfied by pointer to myImpl -- type myPtrIntf interface { -- bar() +- for i := 0; i < b.N; i++ { +- env.Symbol(*symbolQuery) +- } +- }) - } -- -- &myImpl{} //@item(litImplPtr, "&myImpl{}", "", "var") -- -- var mpi myPtrIntf -- mpi = m //@snippet(" //", litImplPtr, "&myImpl{\\}", "&myImpl{\\}") -} +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 08:00:00 +@@ -1,348 +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. - --func _() { -- var s struct{ i []int } //@item(litSliceField, "i", "[]int", "field") -- var foo []int -- // no literal completions after selector -- foo = s.i //@complete(" //", litSliceField) --} +-package codelens - --func _() { -- type myStruct struct{ i int } //@item(litMyStructType, "myStruct", "struct{...}", "struct") -- myStruct{} //@item(litMyStruct, "myStruct{}", "", "var") +-import ( +- "fmt" +- "testing" - -- foo := func(s string, args ...myStruct) {} -- // Don't give literal slice candidate for variadic arg. -- // Do give literal candidates for variadic element. -- foo("", myStruct) //@complete(")", litMyStruct, litMyStructType) --} +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/hooks" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +- "golang.org/x/tools/gopls/internal/lsp/tests/compare" - --func _() { -- Buffer{} //@item(litBuffer, "Buffer{}", "", "var") +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/internal/testenv" +-) - -- var b *bytes.Buffer -- b = bytes.Bu //@snippet(" //", litBuffer, "Buffer{\\}", "Buffer{\\}") +-func TestMain(m *testing.M) { +- bug.PanicOnBugs = true +- Main(m, hooks.Options) -} - --func _() { -- _ = "func(...) {}" //@item(litFunc, "func(...) {}", "", "var") -- -- sort.Slice(nil, fun) //@complete(")", litFunc),snippet(")", litFunc, "func(i, j int) bool {$0\\}", "func(i, j int) bool {$0\\}") -- -- http.HandleFunc("", f) //@snippet(")", litFunc, "func(w http.ResponseWriter, r *http.Request) {$0\\}", "func(${1:w} http.ResponseWriter, ${2:r} *http.Request) {$0\\}") -- -- // no literal "func" completions -- http.Handle("", fun) //@complete(")") -- -- http.HandlerFunc() //@item(handlerFunc, "http.HandlerFunc()", "", "var") -- http.Handle("", h) //@snippet(")", handlerFunc, "http.HandlerFunc($0)", "http.HandlerFunc($0)") -- http.Handle("", http.HandlerFunc()) //@snippet("))", litFunc, "func(w http.ResponseWriter, r *http.Request) {$0\\}", "func(${1:w} http.ResponseWriter, ${2:r} *http.Request) {$0\\}") -- -- var namedReturn func(s string) (b bool) -- namedReturn = f //@snippet(" //", litFunc, "func(s string) (b bool) {$0\\}", "func(s string) (b bool) {$0\\}") -- -- var multiReturn func() (bool, int) -- multiReturn = f //@snippet(" //", litFunc, "func() (bool, int) {$0\\}", "func() (bool, int) {$0\\}") -- -- var multiNamedReturn func() (b bool, i int) -- multiNamedReturn = f //@snippet(" //", litFunc, "func() (b bool, i int) {$0\\}", "func() (b bool, i int) {$0\\}") -- -- var duplicateParams func(myImpl, int, myImpl) -- duplicateParams = f //@snippet(" //", litFunc, "func(mi1 myImpl, i int, mi2 myImpl) {$0\\}", "func(${1:mi1} myImpl, ${2:i} int, ${3:mi2} myImpl) {$0\\}") -- -- type aliasImpl = myImpl -- var aliasParams func(aliasImpl) aliasImpl -- aliasParams = f //@snippet(" //", litFunc, "func(ai aliasImpl) aliasImpl {$0\\}", "func(${1:ai} aliasImpl) aliasImpl {$0\\}") -- -- const two = 2 -- var builtinTypes func([]int, [two]bool, map[string]string, struct{ i int }, interface{ foo() }, <-chan int) -- builtinTypes = f //@snippet(" //", litFunc, "func(i1 []int, b [two]bool, m map[string]string, s struct{ i int \\}, i2 interface{ foo() \\}, c <-chan int) {$0\\}", "func(${1:i1} []int, ${2:b} [two]bool, ${3:m} map[string]string, ${4:s} struct{ i int \\}, ${5:i2} interface{ foo() \\}, ${6:c} <-chan int) {$0\\}") -- -- var _ func(ast.Node) = f //@snippet(" //", litFunc, "func(n ast.Node) {$0\\}", "func(${1:n} ast.Node) {$0\\}") -- var _ func(error) = f //@snippet(" //", litFunc, "func(err error) {$0\\}", "func(${1:err} error) {$0\\}") -- var _ func(context.Context) = f //@snippet(" //", litFunc, "func(ctx context.Context) {$0\\}", "func(${1:ctx} context.Context) {$0\\}") +-func TestDisablingCodeLens(t *testing.T) { +- const workspace = ` +--- go.mod -- +-module codelens.test - -- type context struct {} -- var _ func(context) = f //@snippet(" //", litFunc, "func(ctx context) {$0\\}", "func(${1:ctx} context) {$0\\}") --} +-go 1.12 +--- lib.go -- +-package lib - --func _() { -- StructFoo{} //@item(litStructFoo, "StructFoo{}", "struct{...}", "struct") +-type Number int - -- var sfp *foo.StructFoo -- // Don't insert the "&" before "StructFoo{}". -- sfp = foo.Str //@snippet(" //", litStructFoo, "StructFoo{$0\\}", "StructFoo{$0\\}") +-const ( +- Zero Number = iota +- One +- Two +-) - -- var sf foo.StructFoo -- sf = foo.Str //@snippet(" //", litStructFoo, "StructFoo{$0\\}", "StructFoo{$0\\}") -- sf = foo. //@snippet(" //", litStructFoo, "StructFoo{$0\\}", "StructFoo{$0\\}") +-//` + `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( +- Settings{"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) +- } +- }) +- }) +- } -} - --func _() { -- float64() //@item(litFloat64, "float64()", "float64", "var") -- -- // don't complete to "&float64()" -- var _ *float64 = float64 //@complete(" //") +-// 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. It also checks that these code lenses +-// only affect the diagnostics and contents of the containing go.mod file. +-func TestUpgradeCodelens(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) // uses go.work - -- var f float64 -- f = fl //@complete(" //", litFloat64),snippet(" //", litFloat64, "float64($0)", "float64($0)") +- const proxyWithLatest = ` +--- golang.org/x/hello@v1.3.3/go.mod -- +-module golang.org/x/hello - -- type myInt int -- myInt() //@item(litMyInt, "myInt()", "", "var") +-go 1.12 +--- golang.org/x/hello@v1.3.3/hi/hi.go -- +-package hi - -- var mi myInt -- mi = my //@snippet(" //", litMyInt, "myInt($0)", "myInt($0)") --} +-var Goodbye error +--- golang.org/x/hello@v1.2.3/go.mod -- +-module golang.org/x/hello - --func _() { -- type ptrStruct struct { -- p *ptrStruct -- } +-go 1.12 +--- golang.org/x/hello@v1.2.3/hi/hi.go -- +-package hi - -- ptrStruct{} //@item(litPtrStruct, "ptrStruct{}", "", "var") +-var Goodbye error +-` - -- ptrStruct{ -- p: &ptrSt, //@rank(",", litPtrStruct) -- } +- const shouldUpdateDep = ` +--- go.work -- +-go 1.18 - -- &ptrStruct{} //@item(litPtrStructPtr, "&ptrStruct{}", "", "var") +-use ( +- ./a +- ./b +-) +--- a/go.mod -- +-module mod.com/a - -- &ptrStruct{ -- p: ptrSt, //@rank(",", litPtrStructPtr) -- } --} +-go 1.14 - --func _() { -- f := func(...[]int) {} -- f() //@snippet(")", litIntSlice, "[]int{$0\\}", "[]int{$0\\}") --} +-require golang.org/x/hello v1.2.3 +--- a/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= +--- a/main.go -- +-package main - +-import "golang.org/x/hello/hi" - --func _() { -- // don't complete to "untyped int()" -- []int{}[untyped] //@complete("] //") +-func main() { +- _ = hi.Goodbye -} -diff -urN a/gopls/internal/lsp/testdata/snippets/postfix.go b/gopls/internal/lsp/testdata/snippets/postfix.go ---- a/gopls/internal/lsp/testdata/snippets/postfix.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/snippets/postfix.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,42 +0,0 @@ --package snippets -- --// These tests check that postfix completions do and do not show up in --// certain cases. Tests for the postfix completion contents are under --// regtest. -- --func _() { -- /* append! */ //@item(postfixAppend, "append!", "append and re-assign slice", "snippet") -- var foo []int -- foo.append //@rank(" //", postfixAppend) -- -- []int{}.append //@complete(" //") +--- b/go.mod -- +-module mod.com/b - -- []int{}.last //@complete(" //") +-go 1.14 - -- /* copy! */ //@item(postfixCopy, "copy!", "duplicate slice", "snippet") +-require golang.org/x/hello v1.2.3 +--- b/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= +--- b/main.go -- +-package main - -- foo.copy //@rank(" //", postfixCopy) +-import ( +- "golang.org/x/hello/hi" +-) - -- var s struct{ i []int } -- s.i.copy //@rank(" //", postfixCopy) +-func main() { +- _ = hi.Goodbye +-} +-` - -- var _ []int = s.i.copy //@complete(" //") +- const wantGoModA = `module mod.com/a - -- var blah func() []int -- blah().append //@complete(" //") --} +-go 1.14 - --func _() { -- /* append! */ //@item(postfixAppend, "append!", "append and re-assign slice", "snippet") -- /* last! */ //@item(postfixLast, "last!", "s[len(s)-1]", "snippet") -- /* print! */ //@item(postfixPrint, "print!", "print to stdout", "snippet") -- /* range! */ //@item(postfixRange, "range!", "range over slice", "snippet") -- /* reverse! */ //@item(postfixReverse, "reverse!", "reverse slice", "snippet") -- /* sort! */ //@item(postfixSort, "sort!", "sort.Slice()", "snippet") -- /* var! */ //@item(postfixVar, "var!", "assign to variable", "snippet") +-require golang.org/x/hello v1.3.3 +-` +- // Applying the diagnostics or running the codelenses for a/go.mod +- // should not change the contents of b/go.mod +- const wantGoModB = `module mod.com/b - -- var foo []int -- foo. //@complete(" //", postfixAppend, postfixCopy, postfixLast, postfixPrint, postfixRange, postfixReverse, postfixSort, postfixVar) +-go 1.14 - -- foo = nil --} -diff -urN a/gopls/internal/lsp/testdata/snippets/snippets.go.golden b/gopls/internal/lsp/testdata/snippets/snippets.go.golden ---- a/gopls/internal/lsp/testdata/snippets/snippets.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/snippets/snippets.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ ---- baz(at AliasType, b bool)-signature -- --baz(at AliasType, b bool) +-require golang.org/x/hello v1.2.3 +-` - -diff -urN a/gopls/internal/lsp/testdata/snippets/snippets.go.in b/gopls/internal/lsp/testdata/snippets/snippets.go.in ---- a/gopls/internal/lsp/testdata/snippets/snippets.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/snippets/snippets.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,61 +0,0 @@ --package snippets +- 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("a/go.mod") +- env.OpenFile("b/go.mod") +- var lens protocol.CodeLens +- var found bool +- for _, l := range env.CodeLens("a/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.AfterChange() +- if got := env.BufferText("a/go.mod"); got != wantGoModA { +- t.Fatalf("a/go.mod upgrade failed:\n%s", compare.Text(wantGoModA, got)) +- } +- if got := env.BufferText("b/go.mod"); got != wantGoModB { +- t.Fatalf("b/go.mod changed unexpectedly:\n%s", compare.Text(wantGoModB, 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.RunGoCommandInDirWithEnv("a", []string{"GOWORK=off"}, "mod", "vendor") +- } +- env.AfterChange() +- env.OpenFile("a/go.mod") +- env.OpenFile("b/go.mod") - --type AliasType = int //@item(sigAliasType, "AliasType", "AliasType", "type") +- // Await the diagnostics resulting from opening the modfiles, because +- // otherwise they may cause races when running asynchronously to the +- // explicit re-diagnosing below. +- // +- // TODO(golang/go#58750): there is still a race here, inherent to +- // accessing state on the View; we should create a new snapshot when +- // the view diagnostics change. +- env.AfterChange() - --func foo(i int, b bool) {} //@item(snipFoo, "foo", "func(i int, b bool)", "func") --func bar(fn func()) func() {} //@item(snipBar, "bar", "func(fn func())", "func") --func baz(at AliasType, b bool) {} //@item(snipBaz, "baz", "func(at AliasType, b bool)", "func") +- env.ExecuteCodeLensCommand("a/go.mod", command.CheckUpgrades, nil) +- d := &protocol.PublishDiagnosticsParams{} +- env.OnceMet( +- Diagnostics(env.AtRegexp("a/go.mod", `require`), WithMessage("can be upgraded")), +- ReadDiagnostics("a/go.mod", d), +- // We do not want there to be a diagnostic for b/go.mod, +- // but there may be some subtlety in timing here, where this +- // should always succeed, but may not actually test the correct +- // behavior. +- NoDiagnostics(env.AtRegexp("b/go.mod", `require`)), +- ) +- // Check for upgrades in b/go.mod and then clear them. +- env.ExecuteCodeLensCommand("b/go.mod", command.CheckUpgrades, nil) +- env.Await(Diagnostics(env.AtRegexp("b/go.mod", `require`), WithMessage("can be upgraded"))) +- env.ExecuteCodeLensCommand("b/go.mod", command.ResetGoModDiagnostics, nil) +- env.Await(NoDiagnostics(ForFile("b/go.mod"))) - --type Foo struct { -- Bar int //@item(snipFieldBar, "Bar", "int", "field") -- Func func(at AliasType) error //@item(snipFieldFunc, "Func", "func(at AliasType) error", "field") +- // Apply the diagnostics to a/go.mod. +- env.ApplyQuickFixes("a/go.mod", d.Diagnostics) +- env.AfterChange() +- if got := env.BufferText("a/go.mod"); got != wantGoModA { +- t.Fatalf("a/go.mod upgrade failed:\n%s", compare.Text(wantGoModA, got)) +- } +- if got := env.BufferText("b/go.mod"); got != wantGoModB { +- t.Fatalf("b/go.mod changed unexpectedly:\n%s", compare.Text(wantGoModB, got)) +- } +- }) +- }) +- } -} - --func (Foo) Baz() func() {} //@item(snipMethodBaz, "Baz", "func() func()", "method") --func (Foo) BazBar() func() {} //@item(snipMethodBazBar, "BazBar", "func() func()", "method") --func (Foo) BazBaz(at AliasType) func() {} //@item(snipMethodBazBaz, "BazBaz", "func(at AliasType) func()", "method") -- --func _() { -- f //@snippet(" //", snipFoo, "foo(${1:})", "foo(${1:i int}, ${2:b bool})") -- -- bar //@snippet(" //", snipBar, "bar(${1:})", "bar(${1:fn func()})") +-func TestUnusedDependenciesCodelens(t *testing.T) { +- const proxy = ` +--- golang.org/x/hello@v1.0.0/go.mod -- +-module golang.org/x/hello - -- baz //@snippet(" //", snipBaz, "baz(${1:})", "baz(${1:at AliasType}, ${2:b bool})") -- baz() //@signature("(", "baz(at AliasType, b bool)", 0) +-go 1.14 +--- golang.org/x/hello@v1.0.0/hi/hi.go -- +-package hi - -- bar(nil) //@snippet("(", snipBar, "bar", "bar") -- bar(ba) //@snippet(")", snipBar, "bar(${1:})", "bar(${1:fn func()})") -- var f Foo -- bar(f.Ba) //@snippet(")", snipMethodBaz, "Baz()", "Baz()") -- (bar)(nil) //@snippet(")", snipBar, "bar(${1:})", "bar(${1:fn func()})") -- (f.Ba)() //@snippet(")", snipMethodBaz, "Baz()", "Baz()") +-var Goodbye error +--- golang.org/x/unused@v1.0.0/go.mod -- +-module golang.org/x/unused - -- Foo{ -- B //@snippet(" //", snipFieldBar, "Bar: ${1:},", "Bar: ${1:int},") -- } +-go 1.14 +--- golang.org/x/unused@v1.0.0/nouse/nouse.go -- +-package nouse - -- Foo{ -- F //@snippet(" //", snipFieldFunc, "Func: ${1:},", "Func: ${1:func(at AliasType) error},") -- } +-var NotUsed error +-` - -- Foo{B} //@snippet("}", snipFieldBar, "Bar: ${1:}", "Bar: ${1:int}") -- Foo{} //@snippet("}", snipFieldBar, "Bar: ${1:}", "Bar: ${1:int}") +- const shouldRemoveDep = ` +--- go.mod -- +-module mod.com - -- Foo{Foo{}.B} //@snippet("} ", snipFieldBar, "Bar", "Bar") +-go 1.14 - -- var err error -- err.Error() //@snippet("E", Error, "Error()", "Error()") -- f.Baz() //@snippet("B", snipMethodBaz, "Baz()", "Baz()") +-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 - -- f.Baz() //@snippet("(", snipMethodBazBar, "BazBar", "BazBar") +-import "golang.org/x/hello/hi" - -- f.Baz() //@snippet("B", snipMethodBazBaz, "BazBaz(${1:})", "BazBaz(${1:at AliasType})") +-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, nil) +- env.Await(env.DoneWithChangeWatchedFiles()) +- got := env.BufferText("go.mod") +- const wantGoMod = `module mod.com - --func _() { -- type bar struct { -- a int -- b float64 //@item(snipBarB, "b", "float64", "field") -- } -- bar{b} //@snippet("}", snipBarB, "b: ${1:}", "b: ${1:float64}") --} -diff -urN a/gopls/internal/lsp/testdata/statements/append.go b/gopls/internal/lsp/testdata/statements/append.go ---- a/gopls/internal/lsp/testdata/statements/append.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/statements/append.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,42 +0,0 @@ --package statements +-go 1.14 - --func _() { -- type mySlice []int +-require golang.org/x/hello v1.0.0 +-` +- if got != wantGoMod { +- t.Fatalf("go.mod tidy failed:\n%s", compare.Text(wantGoMod, got)) +- } +- }) +-} - -- var ( -- abc []int //@item(stmtABC, "abc", "[]int", "var") -- abcdef mySlice //@item(stmtABCDEF, "abcdef", "mySlice", "var") -- ) +-func TestRegenerateCgo(t *testing.T) { +- testenv.NeedsTool(t, "cgo") +- const workspace = ` +--- go.mod -- +-module example.com - -- /* abcdef = append(abcdef, ) */ //@item(stmtABCDEFAssignAppend, "abcdef = append(abcdef, )", "", "func") +-go 1.12 +--- cgo.go -- +-package x - -- // don't offer "abc = append(abc, )" because "abc" isn't necessarily -- // better than "abcdef". -- abc //@complete(" //", stmtABC, stmtABCDEF) +-/* +-int fortythree() { return 42; } +-*/ +-import "C" - -- abcdef //@complete(" //", stmtABCDEF, stmtABCDEFAssignAppend) +-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.AfterChange( +- Diagnostics(env.AtRegexp("cgo.go", ``), WithMessage("go list failed to return CompiledGoFiles")), +- ) - -- /* append(abc, ) */ //@item(stmtABCAppend, "append(abc, )", "", "func") +- // 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.AfterChange( +- Diagnostics(env.AtRegexp("cgo.go", ``), WithMessage("go list failed to return CompiledGoFiles")), +- ) - -- abc = app //@snippet(" //", stmtABCAppend, "append(abc, ${1:})", "append(abc, ${1:})") +- // Regenerate cgo, fixing the diagnostic. +- env.ExecuteCodeLensCommand("cgo.go", command.RegenerateCgo, nil) +- env.Await(NoDiagnostics(ForFile("cgo.go"))) +- }) -} +diff -urN a/gopls/internal/regtest/codelens/gcdetails_test.go b/gopls/internal/regtest/codelens/gcdetails_test.go +--- a/gopls/internal/regtest/codelens/gcdetails_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/codelens/gcdetails_test.go 1970-01-01 08:00:00 +@@ -1,127 +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. - --func _() { -- var s struct{ xyz []int } +-package codelens - -- /* xyz = append(s.xyz, ) */ //@item(stmtXYZAppend, "xyz = append(s.xyz, )", "", "func") +-import ( +- "runtime" +- "strings" +- "testing" - -- s.x //@snippet(" //", stmtXYZAppend, "xyz = append(s.xyz, ${1:})", "xyz = append(s.xyz, ${1:})") +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/fake" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) - -- /* s.xyz = append(s.xyz, ) */ //@item(stmtDeepXYZAppend, "s.xyz = append(s.xyz, )", "", "func") +-func TestGCDetails_Toggle(t *testing.T) { +- if runtime.GOOS == "android" { +- t.Skipf("the gc details code lens doesn't work on Android") +- } - -- sx //@snippet(" //", stmtDeepXYZAppend, "s.xyz = append(s.xyz, ${1:})", "s.xyz = append(s.xyz, ${1:})") --} +- const mod = ` +--- go.mod -- +-module mod.com - --func _() { -- var foo [][]int +-go 1.15 +--- main.go -- +-package main - -- /* append(foo[0], ) */ //@item(stmtFooAppend, "append(foo[0], )", "", "func") +-import "fmt" - -- foo[0] = app //@complete(" //"),snippet(" //", stmtFooAppend, "append(foo[0], ${1:})", "append(foo[0], ${1:})") +-func main() { +- fmt.Println(42) -} -diff -urN a/gopls/internal/lsp/testdata/statements/if_err_check_return_2.go b/gopls/internal/lsp/testdata/statements/if_err_check_return_2.go ---- a/gopls/internal/lsp/testdata/statements/if_err_check_return_2.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/statements/if_err_check_return_2.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ --package statements -- --import "os" +-` +- WithOptions( +- Settings{ +- "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, nil) +- d := &protocol.PublishDiagnosticsParams{} +- env.OnceMet( +- Diagnostics(AtPosition("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`) +- } - --func two() error { -- var s struct{ err error } +- // 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.AfterChange(NoDiagnostics(ForFile("main.go"))) - -- /* if s.err != nil { return s.err } */ //@item(stmtTwoIfErrReturn, "if s.err != nil { return s.err }", "", "") +- // Saving a buffer should re-format back to the original state, and +- // re-enable the gc_details diagnostics. +- env.SaveBuffer("main.go") +- env.AfterChange(Diagnostics(AtPosition("main.go", 5, 13))) - -- _, s.err = os.Open("foo") -- //@snippet("", stmtTwoIfErrReturn, "", "if s.err != nil {\n\treturn ${1:s.err}\n\\}") +- // Toggle the GC details code lens again so now it should be off. +- env.ExecuteCodeLensCommand("main.go", command.GCDetails, nil) +- env.Await(NoDiagnostics(ForFile("main.go"))) +- }) -} -diff -urN a/gopls/internal/lsp/testdata/statements/if_err_check_return.go b/gopls/internal/lsp/testdata/statements/if_err_check_return.go ---- a/gopls/internal/lsp/testdata/statements/if_err_check_return.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/statements/if_err_check_return.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,27 +0,0 @@ --package statements -- --import ( -- "bytes" -- "io" -- "os" --) -- --func one() (int, float32, io.Writer, *int, []int, bytes.Buffer, error) { -- /* if err != nil { return err } */ //@item(stmtOneIfErrReturn, "if err != nil { return err }", "", "") -- /* err != nil { return err } */ //@item(stmtOneErrReturn, "err != nil { return err }", "", "") - -- _, err := os.Open("foo") -- //@snippet("", stmtOneIfErrReturn, "", "if err != nil {\n\treturn 0, 0, nil, nil, nil, bytes.Buffer{\\}, ${1:err}\n\\}") +-// Test for the crasher in golang/go#54199 +-func TestGCDetails_NewFile(t *testing.T) { +- bug.PanicOnBugs = false +- const src = ` +--- go.mod -- +-module mod.test - -- _, err = os.Open("foo") -- i //@snippet(" //", stmtOneIfErrReturn, "", "if err != nil {\n\treturn 0, 0, nil, nil, nil, bytes.Buffer{\\}, ${1:err}\n\\}") +-go 1.12 +-` - -- _, err = os.Open("foo") -- if er //@snippet(" //", stmtOneErrReturn, "", "err != nil {\n\treturn 0, 0, nil, nil, nil, bytes.Buffer{\\}, ${1:err}\n\\}") +- WithOptions( +- Settings{ +- "codelenses": map[string]bool{ +- "gc_details": true, +- }, +- }, +- ).Run(t, src, func(t *testing.T, env *Env) { +- env.CreateBuffer("p_test.go", "") - -- _, err = os.Open("foo") -- if //@snippet(" //", stmtOneIfErrReturn, "", "if err != nil {\n\treturn 0, 0, nil, nil, nil, bytes.Buffer{\\}, ${1:err}\n\\}") +- const gcDetailsCommand = "gopls." + string(command.GCDetails) - -- _, err = os.Open("foo") -- if //@snippet("//", stmtOneIfErrReturn, "", "if err != nil {\n\treturn 0, 0, nil, nil, nil, bytes.Buffer{\\}, ${1:err}\n\\}") --} -diff -urN a/gopls/internal/lsp/testdata/statements/if_err_check_test.go b/gopls/internal/lsp/testdata/statements/if_err_check_test.go ---- a/gopls/internal/lsp/testdata/statements/if_err_check_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/statements/if_err_check_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,20 +0,0 @@ --package statements +- hasGCDetails := func() bool { +- lenses := env.CodeLens("p_test.go") // should not crash +- for _, lens := range lenses { +- if lens.Command.Command == gcDetailsCommand { +- return true +- } +- } +- return false +- } - --import ( -- "os" -- "testing" --) +- // With an empty file, we shouldn't get the gc_details codelens because +- // there is nowhere to position it (it needs a package name). +- if hasGCDetails() { +- t.Errorf("got the gc_details codelens for an empty file") +- } - --func TestErr(t *testing.T) { -- /* if err != nil { t.Fatal(err) } */ //@item(stmtOneIfErrTFatal, "if err != nil { t.Fatal(err) }", "", "") +- // Edit to provide a package name. +- env.EditBuffer("p_test.go", fake.NewEdit(0, 0, 0, 0, "package p")) - -- _, err := os.Open("foo") -- //@snippet("", stmtOneIfErrTFatal, "", "if err != nil {\n\tt.Fatal(err)\n\\}") +- // Now we should get the gc_details codelens. +- if !hasGCDetails() { +- t.Errorf("didn't get the gc_details codelens for a valid non-empty Go file") +- } +- }) -} +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 08:00:00 +@@ -1,124 +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. - --func BenchmarkErr(b *testing.B) { -- /* if err != nil { b.Fatal(err) } */ //@item(stmtOneIfErrBFatal, "if err != nil { b.Fatal(err) }", "", "") +-//go:build go1.18 +-// +build go1.18 - -- _, err := os.Open("foo") -- //@snippet("", stmtOneIfErrBFatal, "", "if err != nil {\n\tb.Fatal(err)\n\\}") --} -diff -urN a/gopls/internal/lsp/testdata/stub/other/other.go b/gopls/internal/lsp/testdata/stub/other/other.go ---- a/gopls/internal/lsp/testdata/stub/other/other.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/other/other.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package other +-package completion - -import ( -- "bytes" -- renamed_context "context" --) -- --type Interface interface { -- Get(renamed_context.Context) *bytes.Buffer --} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_add_selector.go b/gopls/internal/lsp/testdata/stub/stub_add_selector.go ---- a/gopls/internal/lsp/testdata/stub/stub_add_selector.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_add_selector.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ --package stub -- --import "io" -- --// This file tests that if an interface --// method references a type from its own package --// then our implementation must add the import/package selector --// in the concrete method if the concrete type is outside of the interface --// package --var _ io.ReaderFrom = &readerFrom{} //@suggestedfix("&readerFrom", "refactor.rewrite", "") -- --type readerFrom struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_add_selector.go.golden b/gopls/internal/lsp/testdata/stub/stub_add_selector.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_add_selector.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_add_selector.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,19 +0,0 @@ ---- suggestedfix_stub_add_selector_10_23 -- --package stub +- "testing" - --import "io" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) - --// This file tests that if an interface --// method references a type from its own package --// then our implementation must add the import/package selector --// in the concrete method if the concrete type is outside of the interface --// package --var _ io.ReaderFrom = &readerFrom{} //@suggestedfix("&readerFrom", "refactor.rewrite", "") +-// test generic receivers +-func TestGenericReceiver(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - --type readerFrom struct{} +-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) {} +-` - --// ReadFrom implements io.ReaderFrom --func (*readerFrom) ReadFrom(r io.Reader) (n int64, err error) { -- panic("unimplemented") +- 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 { +- loc := env.RegexpSearch("main.go", tst.pat) +- loc.Range.Start.Character += uint32(protocol.UTF16Len([]byte(tst.pat))) +- completions := env.Completion(loc) +- result := compareCompletionLabels(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 - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_assign.go b/gopls/internal/lsp/testdata/stub/stub_assign.go ---- a/gopls/internal/lsp/testdata/stub/stub_assign.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_assign.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package stub -- --import "io" -- --func main() { -- var br io.ByteWriter -- br = &byteWriter{} //@suggestedfix("&", "refactor.rewrite", "") +-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 - --type byteWriter struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_assign.go.golden b/gopls/internal/lsp/testdata/stub/stub_assign.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_assign.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_assign.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,17 +0,0 @@ ---- suggestedfix_stub_assign_7_7 -- --package stub -- --import "io" -- --func main() { -- var br io.ByteWriter -- br = &byteWriter{} //@suggestedfix("&", "refactor.rewrite", "") +- tests := []struct { +- file string +- pat string +- offset uint32 // UTF16 length 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()) +- loc := env.RegexpSearch(test.file, test.pat) +- loc.Range.Start.Character += test.offset // character user just typed? will type? +- completions := env.Completion(loc) +- result := compareCompletionLabels(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 08:00:00 +@@ -1,1005 +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. - --type byteWriter struct{} -- --// WriteByte implements io.ByteWriter --func (*byteWriter) WriteByte(c byte) error { -- panic("unimplemented") --} +-package completion - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go b/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go ---- a/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package stub +-import ( +- "fmt" +- "sort" +- "strings" +- "testing" +- "time" - --import "io" +- "github.com/google/go-cmp/cmp" +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/hooks" +- "golang.org/x/tools/gopls/internal/lsp/fake" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +- "golang.org/x/tools/internal/testenv" +-) - --func main() { -- var br io.ByteWriter -- var i int -- i, br = 1, &multiByteWriter{} //@suggestedfix("&", "refactor.rewrite", "") +-func TestMain(m *testing.M) { +- bug.PanicOnBugs = true +- Main(m, hooks.Options) -} - --type multiByteWriter struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go.golden b/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_assign_multivars.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ ---- suggestedfix_stub_assign_multivars_8_13 -- --package stub +-const proxy = ` +--- example.com@v1.2.3/go.mod -- +-module example.com - --import "io" +-go 1.12 +--- example.com@v1.2.3/blah/blah.go -- +-package blah - --func main() { -- var br io.ByteWriter -- var i int -- i, br = 1, &multiByteWriter{} //@suggestedfix("&", "refactor.rewrite", "") --} +-const Name = "Blah" +--- random.org@v1.2.3/go.mod -- +-module random.org - --type multiByteWriter struct{} +-go 1.12 +--- random.org@v1.2.3/blah/blah.go -- +-package hello - --// WriteByte implements io.ByteWriter --func (*multiByteWriter) WriteByte(c byte) error { -- panic("unimplemented") --} +-const Name = "Hello" +-` - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_call_expr.go b/gopls/internal/lsp/testdata/stub/stub_call_expr.go ---- a/gopls/internal/lsp/testdata/stub/stub_call_expr.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_call_expr.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package stub +-func TestPackageCompletion(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - --func main() { -- check(&callExpr{}) //@suggestedfix("&", "refactor.rewrite", "") --} +-go 1.12 +--- fruits/apple.go -- +-package apple - --func check(err error) { -- if err != nil { -- panic(err) -- } +-fun apple() int { +- return 0 -} - --type callExpr struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_call_expr.go.golden b/gopls/internal/lsp/testdata/stub/stub_call_expr.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_call_expr.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_call_expr.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,20 +0,0 @@ ---- suggestedfix_stub_call_expr_4_8 -- --package stub +--- fruits/testfile.go -- +-// this is a comment - --func main() { -- check(&callExpr{}) //@suggestedfix("&", "refactor.rewrite", "") --} +-/* +- this is a multiline comment +-*/ - --func check(err error) { -- if err != nil { -- panic(err) -- } --} +-import "fmt" - --type callExpr struct{} +-func test() {} - --// Error implements error --func (*callExpr) Error() string { -- panic("unimplemented") --} +--- fruits/testfile2.go -- +-package - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_embedded.go b/gopls/internal/lsp/testdata/stub/stub_embedded.go ---- a/gopls/internal/lsp/testdata/stub/stub_embedded.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_embedded.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,15 +0,0 @@ --package stub +--- fruits/testfile3.go -- +-pac +--- 123f_r.u~its-123/testfile.go -- +-package - --import ( -- "io" -- "sort" --) +--- .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(env.RegexpSearch(tc.filename, tc.triggerRegexp)) - --var _ embeddedInterface = (*embeddedConcrete)(nil) //@suggestedfix("(", "refactor.rewrite", "") +- // Check that the completion item suggestions are in the range +- // of the file. {Start,End}.Line are zero-based. +- lineCount := len(strings.Split(env.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 <= %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 <= %d", end, lineCount) +- } +- } - --type embeddedConcrete struct{} +- if tc.want != nil { +- expectedLoc := env.RegexpSearch(tc.filename, tc.editRegexp) +- for _, item := range completions.Items { +- gotRng := item.TextEdit.Range +- if expectedLoc.Range != gotRng { +- t.Errorf("unexpected completion range for completion item %s: got %v, want %v", +- item.Label, gotRng, expectedLoc.Range) +- } +- } +- } - --type embeddedInterface interface { -- sort.Interface -- io.Reader +- diff := compareCompletionLabels(tc.want, completions.Items) +- if diff != "" { +- t.Error(diff) +- } +- }) +- }) +- } -} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_embedded.go.golden b/gopls/internal/lsp/testdata/stub/stub_embedded.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_embedded.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_embedded.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,37 +0,0 @@ ---- suggestedfix_stub_embedded_8_27 -- --package stub -- --import ( -- "io" -- "sort" --) - --var _ embeddedInterface = (*embeddedConcrete)(nil) //@suggestedfix("(", "refactor.rewrite", "") +-func TestPackageNameCompletion(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - --type embeddedConcrete struct{} +-go 1.12 +--- math/add.go -- +-package ma +-` - --// Len implements embeddedInterface --func (*embeddedConcrete) Len() int { -- panic("unimplemented") --} +- 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(env.RegexpSearch("math/add.go", "package ma()")) - --// Less implements embeddedInterface --func (*embeddedConcrete) Less(i int, j int) bool { -- panic("unimplemented") +- diff := compareCompletionLabels(want, completions.Items) +- if diff != "" { +- t.Fatal(diff) +- } +- }) -} - --// Read implements embeddedInterface --func (*embeddedConcrete) Read(p []byte) (n int, err error) { -- panic("unimplemented") --} +-// TODO(rfindley): audit/clean up call sites for this helper, to ensure +-// consistent test errors. +-func compareCompletionLabels(want []string, gotItems []protocol.CompletionItem) string { +- 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) +- } +- } - --// Swap implements embeddedInterface --func (*embeddedConcrete) Swap(i int, j int) { -- panic("unimplemented") --} +- if len(got) == 0 && len(want) == 0 { +- return "" // treat nil and the empty slice as equivalent +- } - --type embeddedInterface interface { -- sort.Interface -- io.Reader +- if diff := cmp.Diff(want, got); diff != "" { +- return fmt.Sprintf("completion item mismatch (-want +got):\n%s", diff) +- } +- return "" -} - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_err.go b/gopls/internal/lsp/testdata/stub/stub_err.go ---- a/gopls/internal/lsp/testdata/stub/stub_err.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_err.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package stub +-func TestUnimportedCompletion(t *testing.T) { +- const mod = ` +--- go.mod -- +-module mod.com - --func main() { -- var br error = &customErr{} //@suggestedfix("&", "refactor.rewrite", "") --} +-go 1.14 - --type customErr struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_err.go.golden b/gopls/internal/lsp/testdata/stub/stub_err.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_err.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_err.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ ---- suggestedfix_stub_err_4_17 -- --package stub +-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() { -- var br error = &customErr{} //@suggestedfix("&", "refactor.rewrite", "") +- _ = blah -} +--- main2.go -- +-package main - --type customErr struct{} +-import "example.com/blah" - --// Error implements error --func (*customErr) Error() string { -- panic("unimplemented") +-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()) - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_function_return.go b/gopls/internal/lsp/testdata/stub/stub_function_return.go ---- a/gopls/internal/lsp/testdata/stub/stub_function_return.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_function_return.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package stub +- // Trigger unimported completions for the example.com/blah package. +- env.OpenFile("main.go") +- env.Await(env.DoneWithOpen()) +- loc := env.RegexpSearch("main.go", "ah") +- completions := env.Completion(loc) +- if len(completions.Items) == 0 { +- t.Fatalf("no completion items") +- } +- env.AcceptCompletion(loc, completions.Items[0]) // adds blah import to main.go +- env.Await(env.DoneWithChange()) - --import ( -- "io" --) +- // Trigger completions once again for the blah.<> selector. +- env.RegexpReplace("main.go", "_ = blah", "_ = blah.") +- env.Await(env.DoneWithChange()) +- loc = env.RegexpSearch("main.go", "\n}") +- completions = env.Completion(loc) +- 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(loc, item) - --func newCloser() io.Closer { -- return closer{} //@suggestedfix("c", "refactor.rewrite", "") +- // Await the diagnostics to add example.com/blah to the go.mod file. +- env.AfterChange( +- Diagnostics(env.AtRegexp("main.go", `"example.com/blah"`)), +- ) +- }) -} - --type closer struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_function_return.go.golden b/gopls/internal/lsp/testdata/stub/stub_function_return.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_function_return.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_function_return.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ ---- suggestedfix_stub_function_return_8_9 -- --package stub +-// 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 - --import ( -- "io" --) +-go 1.14 - --func newCloser() io.Closer { -- return closer{} //@suggestedfix("c", "refactor.rewrite", "") --} +-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 - --type closer struct{} +-package pkg +-import "example.com/blah" +-var _ = blah.Name +--- mainmod/mainmod.go -- +-package mainmod - --// Close implements io.Closer --func (closer) Close() error { -- panic("unimplemented") +-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) +- } +- }) -} - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go b/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go ---- a/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,15 +0,0 @@ --//go:build go1.18 --// +build go1.18 -- --package stub -- --import "io" +-// 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 - --// This file tests that that the stub method generator accounts for concrete --// types that have type parameters defined. --var _ io.ReaderFrom = &genReader[string, int]{} //@suggestedfix("&genReader", "refactor.rewrite", "Implement io.ReaderFrom") +-go 1.12 +--- foo.go -- +-package foo - --type genReader[T, Y any] struct { -- T T -- Y Y +-func _() { +- var s S +- if s. -} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go.golden b/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_generic_receiver.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,22 +0,0 @@ ---- suggestedfix_stub_generic_receiver_10_23 -- --//go:build go1.18 --// +build go1.18 -- --package stub - --import "io" -- --// This file tests that that the stub method generator accounts for concrete --// types that have type parameters defined. --var _ io.ReaderFrom = &genReader[string, int]{} //@suggestedfix("&genReader", "refactor.rewrite", "Implement io.ReaderFrom") -- --type genReader[T, Y any] struct { -- T T -- Y Y +-type S struct { +- i int -} +-` - --// ReadFrom implements io.ReaderFrom --func (*genReader[T, Y]) ReadFrom(r io.Reader) (n int64, err error) { -- panic("unimplemented") +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("foo.go") +- completions := env.Completion(env.RegexpSearch("foo.go", `if s\.()`)) +- diff := compareCompletionLabels([]string{"i"}, completions.Items) +- if diff != "" { +- t.Fatal(diff) +- } +- }) -} - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go b/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go ---- a/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ --package stub -- --import ( -- "compress/zlib" -- . "io" -- _ "io" --) -- --// This file tests that dot-imports and underscore imports --// are properly ignored and that a new import is added to --// reference method types -- --var ( -- _ Reader -- _ zlib.Resetter = (*ignoredResetter)(nil) //@suggestedfix("(", "refactor.rewrite", "") --) +-func TestCompletion_Issue45510(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - --type ignoredResetter struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go.golden b/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_ignored_imports.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,25 +0,0 @@ ---- suggestedfix_stub_ignored_imports_15_20 -- --package stub +-go 1.12 +--- main.go -- +-package main - --import ( -- "compress/zlib" -- . "io" -- _ "io" --) +-func _() { +- type a *a +- var aaaa1, aaaa2 a +- var _ a = aaaa - --// This file tests that dot-imports and underscore imports --// are properly ignored and that a new import is added to --// reference method types +- type b a +- var bbbb1, bbbb2 b +- var _ b = bbbb +-} - --var ( -- _ Reader -- _ zlib.Resetter = (*ignoredResetter)(nil) //@suggestedfix("(", "refactor.rewrite", "") +-type ( +- c *d +- d *e +- e **c -) - --type ignoredResetter struct{} +-func _() { +- var ( +- xxxxc c +- xxxxd d +- xxxxe e +- ) - --// Reset implements zlib.Resetter --func (*ignoredResetter) Reset(r Reader, dict []byte) error { -- panic("unimplemented") +- var _ c = xxxx +- var _ d = xxxx +- var _ e = xxxx -} +-` - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_issue2606.go b/gopls/internal/lsp/testdata/stub/stub_issue2606.go ---- a/gopls/internal/lsp/testdata/stub/stub_issue2606.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_issue2606.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package stub -- --type I interface{ error } +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") - --type C int +- 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(env.RegexpSearch("main.go", tt.re)) +- diff := compareCompletionLabels(tt.want, completions.Items) +- if diff != "" { +- t.Errorf("%s: %s", tt.re, diff) +- } +- } +- }) +-} - --var _ I = C(0) //@suggestedfix("C", "refactor.rewrite", "") -diff -urN a/gopls/internal/lsp/testdata/stub/stub_issue2606.go.golden b/gopls/internal/lsp/testdata/stub/stub_issue2606.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_issue2606.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_issue2606.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ ---- suggestedfix_stub_issue2606_7_11 -- --package stub +-func TestCompletionDeprecation(t *testing.T) { +- const files = ` +--- go.mod -- +-module test.com - --type I interface{ error } +-go 1.16 +--- prog.go -- +-package waste +-// Deprecated, use newFoof +-func fooFunc() bool { +- return false +-} - --type C int +-// Deprecated +-const badPi = 3.14 - --// Error implements I --func (C) Error() string { -- panic("unimplemented") +-func doit() { +- if fooF +- panic() +- x := badP +-} +-` +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("prog.go") +- loc := env.RegexpSearch("prog.go", "if fooF") +- loc.Range.Start.Character += uint32(protocol.UTF16Len([]byte("if fooF"))) +- completions := env.Completion(loc) +- diff := compareCompletionLabels([]string{"fooFunc"}, completions.Items) +- if diff != "" { +- t.Error(diff) +- } +- if completions.Items[0].Tags == nil { +- t.Errorf("expected Tags to show deprecation %#v", completions.Items[0].Tags) +- } +- loc = env.RegexpSearch("prog.go", "= badP") +- loc.Range.Start.Character += uint32(protocol.UTF16Len([]byte("= badP"))) +- completions = env.Completion(loc) +- diff = compareCompletionLabels([]string{"badPi"}, completions.Items) +- if diff != "" { +- t.Error(diff) +- } +- if completions.Items[0].Tags == nil { +- t.Errorf("expected Tags to show deprecation %#v", completions.Items[0].Tags) +- } +- }) -} - --var _ I = C(0) //@suggestedfix("C", "refactor.rewrite", "") +-func TestUnimportedCompletion_VSCodeIssue1489(t *testing.T) { +- const src = ` +--- go.mod -- +-module mod.com - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_multi_var.go b/gopls/internal/lsp/testdata/stub/stub_multi_var.go ---- a/gopls/internal/lsp/testdata/stub/stub_multi_var.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_multi_var.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package stub +-go 1.14 - --import "io" +--- main.go -- +-package main - --// This test ensures that a variable declaration that --// has multiple values on the same line can still be --// analyzed correctly to target the interface implementation --// diagnostic. --var one, two, three io.Reader = nil, &multiVar{}, nil //@suggestedfix("&", "refactor.rewrite", "") +-import "fmt" - --type multiVar struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_multi_var.go.golden b/gopls/internal/lsp/testdata/stub/stub_multi_var.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_multi_var.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_multi_var.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ ---- suggestedfix_stub_multi_var_9_38 -- --package stub +-func main() { +- fmt.Println("a") +- math.Sqr +-} +-` +- WithOptions( +- WindowsLineEndings(), +- Settings{"ui.completion.usePlaceholders": true}, +- ).Run(t, src, func(t *testing.T, env *Env) { +- // Trigger unimported completions for the mod.com package. +- env.OpenFile("main.go") +- env.Await(env.DoneWithOpen()) +- loc := env.RegexpSearch("main.go", "Sqr()") +- completions := env.Completion(loc) +- if len(completions.Items) == 0 { +- t.Fatalf("no completion items") +- } +- env.AcceptCompletion(loc, completions.Items[0]) +- env.Await(env.DoneWithChange()) +- got := env.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:x float64})\r\n}\r\n" +- if diff := cmp.Diff(want, got); diff != "" { +- t.Errorf("unimported completion (-want +got):\n%s", diff) +- } +- }) +-} - --import "io" +-func TestUnimportedCompletionHasPlaceholders60269(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) // uses type params - --// This test ensures that a variable declaration that --// has multiple values on the same line can still be --// analyzed correctly to target the interface implementation --// diagnostic. --var one, two, three io.Reader = nil, &multiVar{}, nil //@suggestedfix("&", "refactor.rewrite", "") +- // We can't express this as a marker test because it doesn't support AcceptCompletion. +- const src = ` +--- go.mod -- +-module example.com +-go 1.12 - --type multiVar struct{} +--- a/a.go -- +-package a - --// Read implements io.Reader --func (*multiVar) Read(p []byte) (n int, err error) { -- panic("unimplemented") --} +-var _ = b.F - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_pointer.go b/gopls/internal/lsp/testdata/stub/stub_pointer.go ---- a/gopls/internal/lsp/testdata/stub/stub_pointer.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_pointer.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package stub +--- b/b.go -- +-package b - --import "io" +-func F0(a, b int, c float64) {} +-func F1(int, chan *string) {} +-func F2[K, V any](map[K]V, chan V) {} // missing type parameters was issue #60959 +-func F3[K comparable, V any](map[K]V, chan V) {} +-` +- WithOptions( +- WindowsLineEndings(), +- Settings{"ui.completion.usePlaceholders": true}, +- ).Run(t, src, func(t *testing.T, env *Env) { +- env.OpenFile("a/a.go") +- env.Await(env.DoneWithOpen()) - --func getReaderFrom() io.ReaderFrom { -- return &pointerImpl{} //@suggestedfix("&", "refactor.rewrite", "") +- // The table lists the expected completions of b.F as they appear in Items. +- const common = "package a\r\n\r\nimport \"example.com/b\"\r\n\r\nvar _ = " +- for i, want := range []string{ +- common + "b.F0(${1:a int}, ${2:b int}, ${3:c float64})\r\n", +- common + "b.F1(${1:_ int}, ${2:_ chan *string})\r\n", +- common + "b.F2[${1:K any}, ${2:V any}](${3:_ map[K]V}, ${4:_ chan V})\r\n", +- common + "b.F3[${1:K comparable}, ${2:V any}](${3:_ map[K]V}, ${4:_ chan V})\r\n", +- } { +- loc := env.RegexpSearch("a/a.go", "b.F()") +- completions := env.Completion(loc) +- if len(completions.Items) == 0 { +- t.Fatalf("no completion items") +- } +- saved := env.BufferText("a/a.go") +- env.AcceptCompletion(loc, completions.Items[i]) +- env.Await(env.DoneWithChange()) +- got := env.BufferText("a/a.go") +- if diff := cmp.Diff(want, got); diff != "" { +- t.Errorf("%d: unimported completion (-want +got):\n%s", i, diff) +- } +- env.SetBufferContent("a/a.go", saved) // restore +- } +- }) -} - --type pointerImpl struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_pointer.go.golden b/gopls/internal/lsp/testdata/stub/stub_pointer.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_pointer.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_pointer.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,16 +0,0 @@ ---- suggestedfix_stub_pointer_6_9 -- --package stub +-func TestPackageMemberCompletionAfterSyntaxError(t *testing.T) { +- // This test documents the current broken behavior due to golang/go#58833. +- const src = ` +--- go.mod -- +-module mod.com - --import "io" +-go 1.14 - --func getReaderFrom() io.ReaderFrom { -- return &pointerImpl{} //@suggestedfix("&", "refactor.rewrite", "") --} +--- main.go -- +-package main - --type pointerImpl struct{} +-import "math" - --// ReadFrom implements io.ReaderFrom --func (*pointerImpl) ReadFrom(r io.Reader) (n int64, err error) { -- panic("unimplemented") +-func main() { +- math.Sqrt(,0) +- math.Ldex +-} +-` +- Run(t, src, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- env.Await(env.DoneWithOpen()) +- loc := env.RegexpSearch("main.go", "Ldex()") +- completions := env.Completion(loc) +- if len(completions.Items) == 0 { +- t.Fatalf("no completion items") +- } +- env.AcceptCompletion(loc, completions.Items[0]) +- env.Await(env.DoneWithChange()) +- got := env.BufferText("main.go") +- // The completion of math.Ldex after the syntax error on the +- // previous line is not "math.Ldexp" but "math.Ldexmath.Abs". +- // (In VSCode, "Abs" wrongly appears in the completion menu.) +- // This is a consequence of poor error recovery in the parser +- // causing "math.Ldex" to become a BadExpr. +- want := "package main\n\nimport \"math\"\n\nfunc main() {\n\tmath.Sqrt(,0)\n\tmath.Ldexmath.Abs(${1:})\n}\n" +- if diff := cmp.Diff(want, got); diff != "" { +- t.Errorf("unimported completion (-want +got):\n%s", diff) +- } +- }) -} - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_renamed_import.go b/gopls/internal/lsp/testdata/stub/stub_renamed_import.go ---- a/gopls/internal/lsp/testdata/stub/stub_renamed_import.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_renamed_import.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package stub +-func TestCompleteAllFields(t *testing.T) { +- // This test verifies that completion results always include all struct fields. +- // See golang/go#53992. - --import ( -- "compress/zlib" -- myio "io" --) +- const src = ` +--- go.mod -- +-module mod.com - --var _ zlib.Resetter = &myIO{} //@suggestedfix("&", "refactor.rewrite", "") --var _ myio.Reader +-go 1.18 - --type myIO struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_renamed_import.go.golden b/gopls/internal/lsp/testdata/stub/stub_renamed_import.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_renamed_import.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_renamed_import.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ ---- suggestedfix_stub_renamed_import_8_23 -- --package stub +--- p/p.go -- +-package p - -import ( -- "compress/zlib" -- myio "io" +- "fmt" +- +- . "net/http" +- . "runtime" +- . "go/types" +- . "go/parser" +- . "go/ast" -) - --var _ zlib.Resetter = &myIO{} //@suggestedfix("&", "refactor.rewrite", "") --var _ myio.Reader +-type S struct { +- a, b, c, d, e, f, g, h, i, j, k, l, m int +- n, o, p, q, r, s, t, u, v, w, x, y, z int +-} - --type myIO struct{} +-func _() { +- var s S +- fmt.Println(s.) +-} +-` - --// Reset implements zlib.Resetter --func (*myIO) Reset(r myio.Reader, dict []byte) error { -- panic("unimplemented") +- WithOptions(Settings{ +- "completionBudget": "1ns", // must be non-zero as 0 => infinity +- }).Run(t, src, func(t *testing.T, env *Env) { +- wantFields := make(map[string]bool) +- for c := 'a'; c <= 'z'; c++ { +- wantFields[string(c)] = true +- } +- +- env.OpenFile("p/p.go") +- // Make an arbitrary edit to ensure we're not hitting the cache. +- env.EditBuffer("p/p.go", fake.NewEdit(0, 0, 0, 0, fmt.Sprintf("// current time: %v\n", time.Now()))) +- loc := env.RegexpSearch("p/p.go", `s\.()`) +- completions := env.Completion(loc) +- gotFields := make(map[string]bool) +- for _, item := range completions.Items { +- if item.Kind == protocol.FieldCompletion { +- gotFields[item.Label] = true +- } +- } +- +- if diff := cmp.Diff(wantFields, gotFields); diff != "" { +- t.Errorf("Completion(...) returned mismatching fields (-want +got):\n%s", diff) +- } +- }) -} - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go b/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go ---- a/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package stub +-func TestDefinition(t *testing.T) { +- testenv.NeedsGo1Point(t, 17) // in go1.16, The FieldList in func x is not empty +- files := ` +--- go.mod -- +-module mod.com - --import ( -- "golang.org/lsptests/stub/other" --) +-go 1.18 +--- a_test.go -- +-package foo +-` +- tests := []struct { +- line string // the sole line in the buffer after the package statement +- pat string // the pattern to search for +- want []string // expected completions +- }{ +- {"func T", "T", []string{"TestXxx(t *testing.T)", "TestMain(m *testing.M)"}}, +- {"func T()", "T", []string{"TestMain", "Test"}}, +- {"func TestM", "TestM", []string{"TestMain(m *testing.M)", "TestM(t *testing.T)"}}, +- {"func TestM()", "TestM", []string{"TestMain"}}, +- {"func TestMi", "TestMi", []string{"TestMi(t *testing.T)"}}, +- {"func TestMi()", "TestMi", nil}, +- {"func TestG", "TestG", []string{"TestG(t *testing.T)"}}, +- {"func TestG(", "TestG", nil}, +- {"func Ben", "B", []string{"BenchmarkXxx(b *testing.B)"}}, +- {"func Ben(", "Ben", []string{"Benchmark"}}, +- {"func BenchmarkFoo", "BenchmarkFoo", []string{"BenchmarkFoo(b *testing.B)"}}, +- {"func BenchmarkFoo(", "BenchmarkFoo", nil}, +- {"func Fuz", "F", []string{"FuzzXxx(f *testing.F)"}}, +- {"func Fuz(", "Fuz", []string{"Fuzz"}}, +- {"func Testx", "Testx", nil}, +- {"func TestMe(t *testing.T)", "TestMe", nil}, +- {"func Te(t *testing.T)", "Te", []string{"TestMain", "Test"}}, +- } +- fname := "a_test.go" +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile(fname) +- env.Await(env.DoneWithOpen()) +- for _, test := range tests { +- env.SetBufferContent(fname, "package foo\n"+test.line) +- loc := env.RegexpSearch(fname, test.pat) +- loc.Range.Start.Character += uint32(protocol.UTF16Len([]byte(test.pat))) +- completions := env.Completion(loc) +- if diff := compareCompletionLabels(test.want, completions.Items); diff != "" { +- t.Error(diff) +- } +- } +- }) +-} - --// This file tests that if an interface --// method references an import from its own package --// that the concrete type does not yet import, and that import happens --// to be renamed, then we prefer the renaming of the interface. --var _ other.Interface = &otherInterfaceImpl{} //@suggestedfix("&otherInterfaceImpl", "refactor.rewrite", "") +-// Test that completing a definition replaces source text when applied, golang/go#56852. +-// Note: With go <= 1.16 the completions does not add parameters and fails these tests. +-func TestDefinitionReplaceRange(t *testing.T) { +- testenv.NeedsGo1Point(t, 17) - --type otherInterfaceImpl struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go.golden b/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_renamed_import_iface.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,22 +0,0 @@ ---- suggestedfix_stub_renamed_import_iface_11_25 -- --package stub +- const mod = ` +--- go.mod -- +-module mod.com - --import ( -- "bytes" -- "context" -- "golang.org/lsptests/stub/other" --) +-go 1.17 +-` - --// This file tests that if an interface --// method references an import from its own package --// that the concrete type does not yet import, and that import happens --// to be renamed, then we prefer the renaming of the interface. --var _ other.Interface = &otherInterfaceImpl{} //@suggestedfix("&otherInterfaceImpl", "refactor.rewrite", "") +- tests := []struct { +- name string +- before, after string +- }{ +- { +- name: "func TestMa", +- before: ` +-package foo_test - --type otherInterfaceImpl struct{} +-func TestMa +-`, +- after: ` +-package foo_test - --// Get implements other.Interface --func (*otherInterfaceImpl) Get(context.Context) *bytes.Buffer { -- panic("unimplemented") --} +-func TestMain(m *testing.M) +-`, +- }, +- { +- name: "func TestSome", +- before: ` +-package foo_test - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_stdlib.go b/gopls/internal/lsp/testdata/stub/stub_stdlib.go ---- a/gopls/internal/lsp/testdata/stub/stub_stdlib.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_stdlib.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package stub +-func TestSome +-`, +- after: ` +-package foo_test - --import ( -- "io" --) +-func TestSome(t *testing.T) +-`, +- }, +- { +- name: "func Bench", +- before: ` +-package foo_test - --var _ io.Writer = writer{} //@suggestedfix("w", "refactor.rewrite", "") +-func Bench +-`, +- // Note: Snippet with escaped }. +- after: ` +-package foo_test - --type writer struct{} -diff -urN a/gopls/internal/lsp/testdata/stub/stub_stdlib.go.golden b/gopls/internal/lsp/testdata/stub/stub_stdlib.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_stdlib.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_stdlib.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,16 +0,0 @@ ---- suggestedfix_stub_stdlib_7_19 -- --package stub +-func Benchmark${1:Xxx}(b *testing.B) { +- $0 +-\} +-`, +- }, +- } - --import ( -- "io" --) +- Run(t, mod, func(t *testing.T, env *Env) { +- env.CreateBuffer("foo_test.go", "") - --var _ io.Writer = writer{} //@suggestedfix("w", "refactor.rewrite", "") +- for _, tst := range tests { +- tst.before = strings.Trim(tst.before, "\n") +- tst.after = strings.Trim(tst.after, "\n") +- env.SetBufferContent("foo_test.go", tst.before) - --type writer struct{} +- loc := env.RegexpSearch("foo_test.go", tst.name) +- loc.Range.Start.Character = uint32(protocol.UTF16Len([]byte(tst.name))) +- completions := env.Completion(loc) +- if len(completions.Items) == 0 { +- t.Fatalf("no completion items") +- } - --// Write implements io.Writer --func (writer) Write(p []byte) (n int, err error) { -- panic("unimplemented") +- env.AcceptCompletion(loc, completions.Items[0]) +- env.Await(env.DoneWithChange()) +- if buf := env.BufferText("foo_test.go"); buf != tst.after { +- t.Errorf("%s:incorrect completion: got %q, want %q", tst.name, buf, tst.after) +- } +- } +- }) -} - -diff -urN a/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go b/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go ---- a/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,27 +0,0 @@ --package stub +-func TestGoWorkCompletion(t *testing.T) { +- const files = ` +--- go.work -- +-go 1.18 - --// Regression test for Issue #56825: file corrupted by insertion of --// methods after TypeSpec in a parenthesized TypeDecl. +-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 -- +-` - --import "io" +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("go.work") - --func newReadCloser() io.ReadCloser { -- return rdcloser{} //@suggestedfix("rd", "refactor.rewrite", "") +- 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(env.RegexpSearch("go.work", tt.re)) +- diff := compareCompletionLabels(tt.want, completions.Items) +- if diff != "" { +- t.Errorf("%s: %s", tt.re, diff) +- } +- } +- }) -} - --type ( -- A int -- rdcloser struct{} -- B int --) +-func TestBuiltinCompletion(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com +- +-go 1.18 +--- a.go -- +-package a - -func _() { -- // Local types can't be stubbed as there's nowhere to put the methods. -- // The suggestedfix assertion can't express this yet. TODO(adonovan): support it. -- type local struct{} -- var _ io.ReadCloser = local{} // want error: `local type "local" cannot be stubbed` +- // here -} +-` - --type ( -- C int --) -diff -urN a/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go.golden b/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go.golden ---- a/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/stub/stub_typedecl_group.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,39 +0,0 @@ ---- suggestedfix_stub_typedecl_group_9_9 -- --package stub -- --// Regression test for Issue #56825: file corrupted by insertion of --// methods after TypeSpec in a parenthesized TypeDecl. +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("a.go") +- result := env.Completion(env.RegexpSearch("a.go", `// here`)) +- builtins := []string{ +- "any", "append", "bool", "byte", "cap", "close", +- "comparable", "complex", "complex128", "complex64", "copy", "delete", +- "error", "false", "float32", "float64", "imag", "int", "int16", "int32", +- "int64", "int8", "len", "make", "new", "panic", "print", "println", "real", +- "recover", "rune", "string", "true", "uint", "uint16", "uint32", "uint64", +- "uint8", "uintptr", "nil", +- } +- if testenv.Go1Point() >= 21 { +- builtins = append(builtins, "clear", "max", "min") +- } +- sort.Strings(builtins) +- var got []string - --import "io" +- for _, item := range result.Items { +- // TODO(rfindley): for flexibility, ignore zero while it is being +- // implemented. Remove this if/when zero lands. +- if item.Label != "zero" { +- got = append(got, item.Label) +- } +- } +- sort.Strings(got) - --func newReadCloser() io.ReadCloser { -- return rdcloser{} //@suggestedfix("rd", "refactor.rewrite", "") +- if diff := cmp.Diff(builtins, got); diff != "" { +- t.Errorf("Completion: unexpected mismatch (-want +got):\n%s", diff) +- } +- }) -} - --type ( -- A int -- rdcloser struct{} -- B int --) -- --// Close implements io.ReadCloser --func (rdcloser) Close() error { -- panic("unimplemented") --} +-func TestOverlayCompletion(t *testing.T) { +- const files = ` +--- go.mod -- +-module foo.test - --// Read implements io.ReadCloser --func (rdcloser) Read(p []byte) (n int, err error) { -- panic("unimplemented") --} +-go 1.18 - --func _() { -- // Local types can't be stubbed as there's nowhere to put the methods. -- // The suggestedfix assertion can't express this yet. TODO(adonovan): support it. -- type local struct{} -- var _ io.ReadCloser = local{} // want error: `local type "local" cannot be stubbed` --} +--- foo/foo.go -- +-package foo - --type ( -- C int --) +-type Foo struct{} +-` - -diff -urN a/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go b/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go ---- a/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,11 +0,0 @@ --package suggestedfix +- Run(t, files, func(t *testing.T, env *Env) { +- env.CreateBuffer("nodisk/nodisk.go", ` +-package nodisk - -import ( -- "log" +- "foo.test/foo" -) - --func goodbye() { -- s := "hiiiiiii" -- s = s //@suggestedfix("s = s", "quickfix", "") -- log.Print(s) +-func _() { +- foo.Foo() -} -diff -urN a/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go.golden b/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go.golden ---- a/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/suggestedfix/has_suggested_fix.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ ---- suggestedfix_has_suggested_fix_9_2 -- --package suggestedfix -- --import ( -- "log" --) -- --func goodbye() { -- s := "hiiiiiii" -- //@suggestedfix("s = s", "quickfix", "") -- log.Print(s) +-`) +- list := env.Completion(env.RegexpSearch("nodisk/nodisk.go", "foo.(Foo)")) +- want := []string{"Foo"} +- var got []string +- for _, item := range list.Items { +- got = append(got, item.Label) +- } +- if diff := cmp.Diff(want, got); diff != "" { +- t.Errorf("Completion: unexpected mismatch (-want +got):\n%s", diff) +- } +- }) -} - -diff -urN a/gopls/internal/lsp/testdata/summary_go1.18.txt.golden b/gopls/internal/lsp/testdata/summary_go1.18.txt.golden ---- a/gopls/internal/lsp/testdata/summary_go1.18.txt.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/summary_go1.18.txt.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,32 +0,0 @@ ---- summary -- --CallHierarchyCount = 2 --CodeLensCount = 5 --CompletionsCount = 264 --CompletionSnippetCount = 115 --UnimportedCompletionsCount = 5 --DeepCompletionsCount = 5 --FuzzyCompletionsCount = 8 --RankedCompletionsCount = 174 --CaseSensitiveCompletionsCount = 4 --DiagnosticsCount = 42 --FoldingRangesCount = 2 --FormatCount = 6 --ImportCount = 8 --SemanticTokenCount = 3 --SuggestedFixCount = 71 --FunctionExtractionCount = 27 --MethodExtractionCount = 6 --DefinitionsCount = 47 --TypeDefinitionsCount = 18 --HighlightsCount = 69 --InlayHintsCount = 5 --ReferencesCount = 30 --RenamesCount = 48 --PrepareRenamesCount = 7 --SymbolsCount = 2 --WorkspaceSymbolsCount = 20 --SignaturesCount = 33 --LinksCount = 7 --ImplementationsCount = 26 --SelectionRangesCount = 3 -- -diff -urN a/gopls/internal/lsp/testdata/summary.txt.golden b/gopls/internal/lsp/testdata/summary.txt.golden ---- a/gopls/internal/lsp/testdata/summary.txt.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/summary.txt.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,32 +0,0 @@ ---- summary -- --CallHierarchyCount = 2 --CodeLensCount = 5 --CompletionsCount = 263 --CompletionSnippetCount = 106 --UnimportedCompletionsCount = 5 --DeepCompletionsCount = 5 --FuzzyCompletionsCount = 8 --RankedCompletionsCount = 164 --CaseSensitiveCompletionsCount = 4 --DiagnosticsCount = 42 --FoldingRangesCount = 2 --FormatCount = 6 --ImportCount = 8 --SemanticTokenCount = 3 --SuggestedFixCount = 65 --FunctionExtractionCount = 27 --MethodExtractionCount = 6 --DefinitionsCount = 47 --TypeDefinitionsCount = 18 --HighlightsCount = 69 --InlayHintsCount = 4 --ReferencesCount = 30 --RenamesCount = 41 --PrepareRenamesCount = 7 --SymbolsCount = 1 --WorkspaceSymbolsCount = 20 --SignaturesCount = 33 --LinksCount = 7 --ImplementationsCount = 16 --SelectionRangesCount = 3 +-// Fix for golang/go#60062: unimported completion included "golang.org/toolchain" results. +-func TestToolchainCompletions(t *testing.T) { +- const files = ` +--- go.mod -- +-module foo.test/foo - -diff -urN a/gopls/internal/lsp/testdata/symbols/go1.18.go b/gopls/internal/lsp/testdata/symbols/go1.18.go ---- a/gopls/internal/lsp/testdata/symbols/go1.18.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/symbols/go1.18.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,16 +0,0 @@ --//go:build go1.18 --// +build go1.18 +-go 1.21 - --package main +--- foo.go -- +-package foo - --type T[P any] struct { //@symbol("T", "T", "Struct", "struct{...}", "T", "") -- F P //@symbol("F", "F", "Field", "P", "", "T") +-func _() { +- os.Open -} - --type Constraint interface { //@symbol("Constraint", "Constraint", "Interface", "interface{...}", "Constraint", "") -- ~int | struct{ int } //@symbol("~int | struct{int}", "~int | struct{ int }", "Field", "", "", "Constraint") -- -- // TODO(rfindley): the selection range below is the entire interface field. -- // Can we reduce it? -- interface{ M() } //@symbol("interface{...}", "interface{ M() }", "Field", "", "iFaceField", "Constraint"), symbol("M", "M", "Method", "func()", "", "iFaceField") +-func _() { +- strings -} -diff -urN a/gopls/internal/lsp/testdata/symbols/go1.18.go.golden b/gopls/internal/lsp/testdata/symbols/go1.18.go.golden ---- a/gopls/internal/lsp/testdata/symbols/go1.18.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/symbols/go1.18.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ ---- symbols -- --T Struct 6:6-6:7 -- F Field 7:2-7:3 --Constraint Interface 10:6-10:16 -- interface{...} Field 15:2-15:18 -- ~int | struct{int} Field 11:2-11:22 -- -diff -urN a/gopls/internal/lsp/testdata/symbols/main.go b/gopls/internal/lsp/testdata/symbols/main.go ---- a/gopls/internal/lsp/testdata/symbols/main.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/symbols/main.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,91 +0,0 @@ --package main +-` - --import ( -- "io" --) +- const proxy = ` +--- golang.org/toolchain@v0.0.1-go1.21.1.linux-amd64/go.mod -- +-module golang.org/toolchain +--- golang.org/toolchain@v0.0.1-go1.21.1.linux-amd64/src/os/os.go -- +-package os - --// Each symbol marker in this file defines the following information: --// symbol(name, selectionSpan, kind, detail, id, parentID) --// - name: DocumentSymbol.Name --// - selectionSpan: DocumentSymbol.SelectionRange --// - kind: DocumentSymbol.Kind --// - detail: DocumentSymbol.Detail --// - id: if non-empty, a unique identifier for this symbol --// - parentID: if non-empty, the id of the parent of this symbol --// --// This data in aggregate defines a set of document symbols and their --// parent-child relationships, which is compared against the DocummentSymbols --// response from gopls for the current file. --// --// TODO(rfindley): the symbol annotations here are complicated and difficult to --// maintain. It would be simpler to just write out the full expected response --// in the golden file, perhaps as raw JSON. +-func Open() {} +--- golang.org/toolchain@v0.0.1-go1.21.1.linux-amd64/src/strings/strings.go -- +-package strings - --var _ = 1 +-func Join() {} +-` - --var x = 42 //@symbol("x", "x", "Variable", "", "", "") +- WithOptions( +- ProxyFiles(proxy), +- ).Run(t, files, func(t *testing.T, env *Env) { +- env.RunGoCommand("mod", "download", "golang.org/toolchain@v0.0.1-go1.21.1.linux-amd64") +- env.OpenFile("foo.go") - --var nested struct { //@symbol("nested", "nested", "Variable", "struct{...}", "nested", "") -- nestedField struct { //@symbol("nestedField", "nestedField", "Field", "struct{...}", "nestedField", "nested") -- f int //@symbol("f", "f", "Field", "int", "", "nestedField") -- } +- for _, pattern := range []string{"os.Open()", "string()"} { +- loc := env.RegexpSearch("foo.go", pattern) +- res := env.Completion(loc) +- for _, item := range res.Items { +- if strings.Contains(item.Detail, "golang.org/toolchain") { +- t.Errorf("Completion(...) returned toolchain item %#v", item) +- } +- } +- } +- }) -} +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 08:00:00 +@@ -1,590 +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. - --const y = 43 //@symbol("y", "y", "Constant", "", "", "") -- --type Number int //@symbol("Number", "Number", "Class", "int", "", "") -- --type Alias = string //@symbol("Alias", "Alias", "Class", "string", "", "") +-package completion - --type NumberAlias = Number //@symbol("NumberAlias", "NumberAlias", "Class", "Number", "", "") +-import ( +- "strings" +- "testing" - --type ( -- Boolean bool //@symbol("Boolean", "Boolean", "Class", "bool", "", "") -- BoolAlias = bool //@symbol("BoolAlias", "BoolAlias", "Class", "bool", "", "") +- . "golang.org/x/tools/gopls/internal/lsp/regtest" -) - --type Foo struct { //@symbol("Foo", "Foo", "Struct", "struct{...}", "Foo", "") -- Quux //@symbol("Quux", "Quux", "Field", "Quux", "", "Foo") -- W io.Writer //@symbol("W", "W", "Field", "io.Writer", "", "Foo") -- Bar int //@symbol("Bar", "Bar", "Field", "int", "", "Foo") -- baz string //@symbol("baz", "baz", "Field", "string", "", "Foo") -- funcField func(int) int //@symbol("funcField", "funcField", "Field", "func(int) int", "", "Foo") --} +-func TestPostfixSnippetCompletion(t *testing.T) { +- const mod = ` +--- go.mod -- +-module mod.com - --type Quux struct { //@symbol("Quux", "Quux", "Struct", "struct{...}", "Quux", "") -- X, Y float64 //@symbol("X", "X", "Field", "float64", "", "Quux"), symbol("Y", "Y", "Field", "float64", "", "Quux") --} +-go 1.12 +-` - --type EmptyStruct struct{} //@symbol("EmptyStruct", "EmptyStruct", "Struct", "struct{}", "", "") +- cases := []struct { +- name string +- before, after string +- }{ +- { +- name: "sort", +- before: ` +-package foo - --func (f Foo) Baz() string { //@symbol("(Foo).Baz", "Baz", "Method", "func() string", "", "") -- return f.baz +-func _() { +- var foo []int +- foo.sort -} +-`, +- after: ` +-package foo - --func _() {} -- --func (q *Quux) Do() {} //@symbol("(*Quux).Do", "Do", "Method", "func()", "", "") +-import "sort" - --func main() { //@symbol("main", "main", "Function", "func()", "", "") +-func _() { +- var foo []int +- sort.Slice(foo, func(i, j int) bool { +- $0 +-}) -} +-`, +- }, +- { +- name: "sort_renamed_sort_package", +- before: ` +-package foo - --type Stringer interface { //@symbol("Stringer", "Stringer", "Interface", "interface{...}", "Stringer", "") -- String() string //@symbol("String", "String", "Method", "func() string", "", "Stringer") --} +-import blahsort "sort" - --type ABer interface { //@symbol("ABer", "ABer", "Interface", "interface{...}", "ABer", "") -- B() //@symbol("B", "B", "Method", "func()", "", "ABer") -- A() string //@symbol("A", "A", "Method", "func() string", "", "ABer") --} +-var j int - --type WithEmbeddeds interface { //@symbol("WithEmbeddeds", "WithEmbeddeds", "Interface", "interface{...}", "WithEmbeddeds", "") -- Do() //@symbol("Do", "Do", "Method", "func()", "", "WithEmbeddeds") -- ABer //@symbol("ABer", "ABer", "Field", "ABer", "", "WithEmbeddeds") -- io.Writer //@symbol("Writer", "Writer", "Field", "io.Writer", "", "WithEmbeddeds") +-func _() { +- var foo []int +- foo.sort -} +-`, +- after: ` +-package foo - --type EmptyInterface interface{} //@symbol("EmptyInterface", "EmptyInterface", "Interface", "interface{}", "", "") -- --func Dunk() int { return 0 } //@symbol("Dunk", "Dunk", "Function", "func() int", "", "") +-import blahsort "sort" - --func dunk() {} //@symbol("dunk", "dunk", "Function", "func()", "", "") -diff -urN a/gopls/internal/lsp/testdata/symbols/main.go.golden b/gopls/internal/lsp/testdata/symbols/main.go.golden ---- a/gopls/internal/lsp/testdata/symbols/main.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/symbols/main.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,36 +0,0 @@ ---- symbols -- --x Variable 26:5-26:6 --nested Variable 28:5-28:11 -- nestedField Field 29:2-29:13 --y Constant 34:7-34:8 --Number Class 36:6-36:12 --Alias Class 38:6-38:11 --NumberAlias Class 40:6-40:17 --Boolean Class 43:2-43:9 --BoolAlias Class 44:2-44:11 --Foo Struct 47:6-47:9 -- Bar Field 50:2-50:5 -- Quux Field 48:2-48:6 -- W Field 49:2-49:3 -- baz Field 51:2-51:5 -- funcField Field 52:2-52:11 --Quux Struct 55:6-55:10 -- X Field 56:2-56:3 -- Y Field 56:5-56:6 --EmptyStruct Struct 59:6-59:17 --(Foo).Baz Method 61:14-61:17 --(*Quux).Do Method 67:16-67:18 --main Function 69:6-69:10 --Stringer Interface 72:6-72:14 -- String Method 73:2-73:8 --ABer Interface 76:6-76:10 -- A Method 78:2-78:3 -- B Method 77:2-77:3 --WithEmbeddeds Interface 81:6-81:19 -- ABer Field 83:2-83:6 -- Do Method 82:2-82:4 -- Writer Field 84:5-84:11 --EmptyInterface Interface 87:6-87:20 --Dunk Function 89:6-89:10 --dunk Function 91:6-91:10 -- -diff -urN a/gopls/internal/lsp/testdata/testy/testy.go b/gopls/internal/lsp/testdata/testy/testy.go ---- a/gopls/internal/lsp/testdata/testy/testy.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/testy/testy.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,5 +0,0 @@ --package testy +-var j int - --func a() { //@mark(identA, "a"),item(funcA, "a", "func()", "func"),refs("a", identA, testyA) -- //@complete("", funcA) +-func _() { +- var foo []int +- blahsort.Slice(foo, func(i, j2 int) bool { +- $0 +-}) -} -diff -urN a/gopls/internal/lsp/testdata/testy/testy_test.go b/gopls/internal/lsp/testdata/testy/testy_test.go ---- a/gopls/internal/lsp/testdata/testy/testy_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/testy/testy_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ --package testy -- --import ( -- "testing" -- -- sig "golang.org/lsptests/signature" -- "golang.org/lsptests/snippets" --) +-`, +- }, +- { +- name: "last", +- before: ` +-package foo - --func TestSomething(t *testing.T) { //@item(TestSomething, "TestSomething(t *testing.T)", "", "func") -- var x int //@mark(testyX, "x"),diag("x", "compiler", "x declared (and|but) not used", "error"),refs("x", testyX) -- a() //@mark(testyA, "a") +-func _() { +- var s struct { i []int } +- s.i.last -} +-`, +- after: ` +-package foo - -func _() { -- _ = snippets.X(nil) //@signature("nil", "X(_ map[sig.Alias]types.CoolAlias) map[sig.Alias]types.CoolAlias", 0) -- var _ sig.Alias +- var s struct { i []int } +- s.i[len(s.i)-1] -} -diff -urN a/gopls/internal/lsp/testdata/testy/testy_test.go.golden b/gopls/internal/lsp/testdata/testy/testy_test.go.golden ---- a/gopls/internal/lsp/testdata/testy/testy_test.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/testy/testy_test.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ ---- X(_ map[sig.Alias]types.CoolAlias) map[sig.Alias]types.CoolAlias-signature -- --X(_ map[sig.Alias]types.CoolAlias) map[sig.Alias]types.CoolAlias -- -diff -urN a/gopls/internal/lsp/testdata/typdef/typdef.go b/gopls/internal/lsp/testdata/typdef/typdef.go ---- a/gopls/internal/lsp/testdata/typdef/typdef.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/typdef/typdef.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,65 +0,0 @@ --package typdef +-`, +- }, +- { +- name: "reverse", +- before: ` +-package foo - --type Struct struct { //@item(Struct, "Struct", "struct{...}", "struct") -- Field string +-func _() { +- var foo []int +- foo.reverse -} -- --type Int int //@item(Int, "Int", "int", "type") +-`, +- after: ` +-package foo - -func _() { -- var ( -- value Struct -- point *Struct -- ) -- _ = value //@typdef("value", Struct) -- _ = point //@typdef("point", Struct) -- -- var ( -- array [3]Struct -- slice []Struct -- ch chan Struct -- complex [3]chan *[5][]Int -- ) -- _ = array //@typdef("array", Struct) -- _ = slice //@typdef("slice", Struct) -- _ = ch //@typdef("ch", Struct) -- _ = complex //@typdef("complex", Int) -- -- var s struct { -- x struct { -- xx struct { -- field1 []Struct -- field2 []Int -- } -- } -- } -- s.x.xx.field1 //@typdef("field1", Struct) -- s.x.xx.field2 //@typdef("field2", Int) +- 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] -} - --func F1() Int { return 0 } --func F2() (Int, float64) { return 0, 0 } --func F3() (Struct, int, bool, error) { return Struct{}, 0, false, nil } --func F4() (**int, Int, bool, *error) { return nil, Struct{}, false, nil } --func F5() (int, float64, error, Struct) { return 0, 0, nil, Struct{} } --func F6() (int, float64, ***Struct, error) { return 0, 0, nil, nil } +-} +-`, +- }, +- { +- name: "slice_range", +- before: ` +-package foo - -func _() { -- F1() //@typdef("F1", Int) -- F2() //@typdef("F2", Int) -- F3() //@typdef("F3", Struct) -- F4() //@typdef("F4", Int) -- F5() //@typdef("F5", Struct) -- F6() //@typdef("F6", Struct) -- -- f := func() Int { return 0 } -- f() //@typdef("f", Int) +- type myThing struct{} +- var foo []myThing +- foo.range -} +-`, +- after: ` +-package foo - --// https://github.com/golang/go/issues/38589#issuecomment-620350922 -func _() { -- type myFunc func(int) Int //@item(myFunc, "myFunc", "func", "type") -- -- var foo myFunc -- bar := foo() //@typdef("foo", myFunc) +- type myThing struct{} +- var foo []myThing +- for i, mt := range foo { +- $0 -} -diff -urN a/gopls/internal/lsp/testdata/typeassert/type_assert.go b/gopls/internal/lsp/testdata/typeassert/type_assert.go ---- a/gopls/internal/lsp/testdata/typeassert/type_assert.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/typeassert/type_assert.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,24 +0,0 @@ --package typeassert -- --type abc interface { //@item(abcIntf, "abc", "interface{...}", "interface") -- abc() -} -- --type abcImpl struct{} //@item(abcImpl, "abcImpl", "struct{...}", "struct") --func (abcImpl) abc() -- --type abcPtrImpl struct{} //@item(abcPtrImpl, "abcPtrImpl", "struct{...}", "struct") --func (*abcPtrImpl) abc() -- --type abcNotImpl struct{} //@item(abcNotImpl, "abcNotImpl", "struct{...}", "struct") +-`, +- }, +- { +- name: "append_stmt", +- before: ` +-package foo - -func _() { -- var a abc -- switch a.(type) { -- case ab: //@complete(":", abcImpl, abcPtrImpl, abcIntf, abcNotImpl) -- case *ab: //@complete(":", abcImpl, abcPtrImpl, abcIntf, abcNotImpl) -- } +- var foo []int +- foo.append +-} +-`, +- after: ` +-package foo - -- a.(ab) //@complete(")", abcImpl, abcPtrImpl, abcIntf, abcNotImpl) -- a.(*ab) //@complete(")", abcImpl, abcPtrImpl, abcIntf, abcNotImpl) +-func _() { +- var foo []int +- foo = append(foo, $0) -} -diff -urN a/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go b/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go ---- a/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,5 +0,0 @@ --package typeerrors +-`, +- }, +- { +- name: "append_expr", +- before: ` +-package foo - --func x() { return nil } //@suggestedfix("nil", "quickfix", "") +-func _() { +- var foo []int +- var _ []int = foo.append +-} +-`, +- after: ` +-package foo - --func y() { return nil, "hello" } //@suggestedfix("nil", "quickfix", "") -diff -urN a/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go.golden b/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go.golden ---- a/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/typeerrors/noresultvalues.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ ---- suggestedfix_noresultvalues_3_19 -- --package typeerrors +-func _() { +- var foo []int +- var _ []int = append(foo, $0) +-} +-`, +- }, +- { +- name: "slice_copy", +- before: ` +-package foo - --func x() { return } //@suggestedfix("nil", "quickfix", "") +-func _() { +- var foo []int +- foo.copy +-} +-`, +- after: ` +-package foo - --func y() { return nil, "hello" } //@suggestedfix("nil", "quickfix", "") +-func _() { +- var foo []int +- fooCopy := make([]int, len(foo)) +-copy(fooCopy, foo) - ---- suggestedfix_noresultvalues_5_19 -- --package typeerrors +-} +-`, +- }, +- { +- name: "map_range", +- before: ` +-package foo - --func x() { return nil } //@suggestedfix("nil", "quickfix", "") +-func _() { +- var foo map[string]int +- foo.range +-} +-`, +- after: ` +-package foo - --func y() { return } //@suggestedfix("nil", "quickfix", "") +-func _() { +- var foo map[string]int +- for k, v := range foo { +- $0 +-} +-} +-`, +- }, +- { +- name: "map_clear", +- before: ` +-package foo - -diff -urN a/gopls/internal/lsp/testdata/typemods/type_mods.go b/gopls/internal/lsp/testdata/typemods/type_mods.go ---- a/gopls/internal/lsp/testdata/typemods/type_mods.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/typemods/type_mods.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,21 +0,0 @@ --package typemods +-func _() { +- var foo map[string]int +- foo.clear +-} +-`, +- after: ` +-package foo - --func fooFunc() func() int { //@item(modFooFunc, "fooFunc", "func() func() int", "func") -- return func() int { -- return 0 -- } +-func _() { +- var foo map[string]int +- for k := range foo { +- delete(foo, k) -} - --func fooPtr() *int { //@item(modFooPtr, "fooPtr", "func() *int", "func") -- return nil -} +-`, +- }, +- { +- name: "map_keys", +- before: ` +-package foo - -func _() { -- var _ int = foo //@snippet(" //", modFooFunc, "fooFunc()()", "fooFunc()()"),snippet(" //", modFooPtr, "*fooPtr()", "*fooPtr()") +- var foo map[string]int +- foo.keys -} +-`, +- after: ` +-package foo - -func _() { -- var m map[int][]chan int //@item(modMapChanPtr, "m", "map[int]chan *int", "var") -- -- var _ int = m //@snippet(" //", modMapChanPtr, "<-m[${1:}][${2:}]", "<-m[${1:}][${2:}]") +- var foo map[string]int +- keys := make([]string, 0, len(foo)) +-for k := range foo { +- keys = append(keys, k) -} -diff -urN a/gopls/internal/lsp/testdata/typeparams/type_params.go b/gopls/internal/lsp/testdata/typeparams/type_params.go ---- a/gopls/internal/lsp/testdata/typeparams/type_params.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/typeparams/type_params.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,61 +0,0 @@ --//go:build go1.18 --// +build go1.18 - --package typeparams +-} +-`, +- }, +- { +- name: "channel_range", +- before: ` +-package foo - --func one[a int | string]() {} --func two[a int | string, b float64 | int]() {} +-func _() { +- foo := make(chan int) +- foo.range +-} +-`, +- after: ` +-package foo - -func _() { -- one[]() //@rank("]", string, float64) -- two[]() //@rank("]", int, float64) -- two[int, f]() //@rank("]", float64, float32) +- foo := make(chan int) +- for e := range foo { +- $0 +-} -} +-`, +- }, +- { +- name: "var", +- before: ` +-package foo - --func slices[a []int | []float64]() {} //@item(tpInts, "[]int", "[]int", "type"),item(tpFloats, "[]float64", "[]float64", "type") +-func foo() (int, error) { return 0, nil } - -func _() { -- slices[]() //@rank("]", tpInts),rank("]", tpFloats) +- foo().var -} +-`, +- after: ` +-package foo - --type s[a int | string] struct{} +-func foo() (int, error) { return 0, nil } - -func _() { -- s[]{} //@rank("]", int, float64) +- i, err := foo() -} +-`, +- }, +- { +- name: "var_single_value", +- before: ` +-package foo - --func takesGeneric[a int | string](s[a]) { -- "s[a]{}" //@item(tpInScopeLit, "s[a]{}", "", "var") -- takesGeneric() //@rank(")", tpInScopeLit),snippet(")", tpInScopeLit, "s[a]{\\}", "s[a]{\\}") --} +-func foo() error { return nil } - -func _() { -- s[int]{} //@item(tpInstLit, "s[int]{}", "", "var") -- takesGeneric[int]() //@rank(")", tpInstLit),snippet(")", tpInstLit, "s[int]{\\}", "s[int]{\\}") -- -- "s[...]{}" //@item(tpUninstLit, "s[...]{}", "", "var") -- takesGeneric() //@rank(")", tpUninstLit),snippet(")", tpUninstLit, "s[${1:}]{\\}", "s[${1:a}]{\\}") +- foo().var -} +-`, +- after: ` +-package foo - --func returnTP[A int | float64](a A) A { //@item(returnTP, "returnTP", "something", "func") -- return a --} +-func foo() error { return nil } - -func _() { -- // disabled - see issue #54822 -- var _ int = returnTP // snippet(" //", returnTP, "returnTP[${1:}](${2:})", "returnTP[${1:A int|float64}](${2:a A})") -- -- var aa int //@item(tpInt, "aa", "int", "var") -- var ab float64 //@item(tpFloat, "ab", "float64", "var") -- returnTP[int](a) //@rank(")", tpInt, tpFloat) +- err := foo() -} +-`, +- }, +- { +- name: "var_same_type", +- before: ` +-package foo - --func takesFunc[T any](func(T) T) { -- var _ func(t T) T = f //@snippet(" //", tpLitFunc, "func(t T) T {$0\\}", "func(t T) T {$0\\}") --} +-func foo() (int, int) { return 0, 0 } - -func _() { -- _ = "func(...) {}" //@item(tpLitFunc, "func(...) {}", "", "var") -- takesFunc() //@snippet(")", tpLitFunc, "func(${1:}) ${2:} {$0\\}", "func(${1:t} ${2:T}) ${3:T} {$0\\}") -- takesFunc[int]() //@snippet(")", tpLitFunc, "func(i int) int {$0\\}", "func(${1:i} int) int {$0\\}") +- foo().var -} -diff -urN a/gopls/internal/lsp/testdata/types/types.go b/gopls/internal/lsp/testdata/types/types.go ---- a/gopls/internal/lsp/testdata/types/types.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/types/types.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ --package types +-`, +- after: ` +-package foo - --type CoolAlias = int //@item(CoolAlias, "CoolAlias", "int", "type") +-func foo() (int, int) { return 0, 0 } - --type X struct { //@item(X_struct, "X", "struct{...}", "struct") -- x int +-func _() { +- i, i2 := foo() -} +-`, +- }, +- { +- name: "print_scalar", +- before: ` +-package foo - --type Y struct { //@item(Y_struct, "Y", "struct{...}", "struct") -- y int +-func _() { +- var foo int +- foo.print -} +-`, +- after: ` +-package foo - --type Bob interface { //@item(Bob_interface, "Bob", "interface{...}", "interface") -- Bobby() +-import "fmt" +- +-func _() { +- var foo int +- fmt.Printf("foo: %v\n", foo) -} +-`, +- }, +- { +- name: "print_multi", +- before: ` +-package foo - --func (*X) Bobby() {} --func (*Y) Bobby() {} -diff -urN a/gopls/internal/lsp/testdata/undeclared/var.go b/gopls/internal/lsp/testdata/undeclared/var.go ---- a/gopls/internal/lsp/testdata/undeclared/var.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/undeclared/var.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,14 +0,0 @@ --package undeclared +-func foo() (int, error) { return 0, nil } - --func m() int { -- z, _ := 1+y, 11 //@diag("y", "compiler", "(undeclared name|undefined): y", "error"),suggestedfix("y", "quickfix", "") -- if 100 < 90 { -- z = 1 -- } else if 100 > n+2 { //@diag("n", "compiler", "(undeclared name|undefined): n", "error"),suggestedfix("n", "quickfix", "") -- z = 4 -- } -- for i < 200 { //@diag("i", "compiler", "(undeclared name|undefined): i", "error"),suggestedfix("i", "quickfix", "") -- } -- r() //@diag("r", "compiler", "(undeclared name|undefined): r", "error") -- return z +-func _() { +- foo().print -} -diff -urN a/gopls/internal/lsp/testdata/undeclared/var.go.golden b/gopls/internal/lsp/testdata/undeclared/var.go.golden ---- a/gopls/internal/lsp/testdata/undeclared/var.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/undeclared/var.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,51 +0,0 @@ ---- suggestedfix_var_10_6 -- --package undeclared +-`, +- after: ` +-package foo - --func m() int { -- z, _ := 1+y, 11 //@diag("y", "compiler", "(undeclared name|undefined): y", "error"),suggestedfix("y", "quickfix", "") -- if 100 < 90 { -- z = 1 -- } else if 100 > n+2 { //@diag("n", "compiler", "(undeclared name|undefined): n", "error"),suggestedfix("n", "quickfix", "") -- z = 4 -- } -- i := -- for i < 200 { //@diag("i", "compiler", "(undeclared name|undefined): i", "error"),suggestedfix("i", "quickfix", "") -- } -- r() //@diag("r", "compiler", "(undeclared name|undefined): r", "error") -- return z --} +-import "fmt" - ---- suggestedfix_var_4_12 -- --package undeclared +-func foo() (int, error) { return 0, nil } - --func m() int { -- y := -- z, _ := 1+y, 11 //@diag("y", "compiler", "(undeclared name|undefined): y", "error"),suggestedfix("y", "quickfix", "") -- if 100 < 90 { -- z = 1 -- } else if 100 > n+2 { //@diag("n", "compiler", "(undeclared name|undefined): n", "error"),suggestedfix("n", "quickfix", "") -- z = 4 -- } -- for i < 200 { //@diag("i", "compiler", "(undeclared name|undefined): i", "error"),suggestedfix("i", "quickfix", "") -- } -- r() //@diag("r", "compiler", "(undeclared name|undefined): r", "error") -- return z +-func _() { +- fmt.Println(foo()) -} +-`, +- }, +- { +- name: "string split", +- before: ` +-package foo - ---- suggestedfix_var_7_18 -- --package undeclared +-func foo() []string { +- x := "test" +- return x.split +-}`, +- after: ` +-package foo - --func m() int { -- z, _ := 1+y, 11 //@diag("y", "compiler", "(undeclared name|undefined): y", "error"),suggestedfix("y", "quickfix", "") -- n := -- if 100 < 90 { -- z = 1 -- } else if 100 > n+2 { //@diag("n", "compiler", "(undeclared name|undefined): n", "error"),suggestedfix("n", "quickfix", "") -- z = 4 -- } -- for i < 200 { //@diag("i", "compiler", "(undeclared name|undefined): i", "error"),suggestedfix("i", "quickfix", "") -- } -- r() //@diag("r", "compiler", "(undeclared name|undefined): r", "error") -- return z --} +-import "strings" - -diff -urN a/gopls/internal/lsp/testdata/unimported/export_test.go b/gopls/internal/lsp/testdata/unimported/export_test.go ---- a/gopls/internal/lsp/testdata/unimported/export_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/unimported/export_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ --package unimported +-func foo() []string { +- x := "test" +- return strings.Split(x, "$0") +-}`, +- }, +- { +- name: "string slice join", +- before: ` +-package foo - --var TestExport int //@item(testexport, "TestExport", "var (from \"golang.org/lsptests/unimported\")", "var") -diff -urN a/gopls/internal/lsp/testdata/unimported/unimported_cand_type.go b/gopls/internal/lsp/testdata/unimported/unimported_cand_type.go ---- a/gopls/internal/lsp/testdata/unimported/unimported_cand_type.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/unimported/unimported_cand_type.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,16 +0,0 @@ --package unimported +-func foo() string { +- x := []string{"a", "test"} +- return x.join +-}`, +- after: ` +-package foo - --import ( -- _ "context" +-import "strings" - -- "golang.org/lsptests/baz" -- _ "golang.org/lsptests/signature" // provide type information for unimported completions in the other file --) +-func foo() string { +- x := []string{"a", "test"} +- return strings.Join(x, "$0") +-}`, +- }, +- { +- name: "if not nil interface", +- before: ` +-package foo - -func _() { -- foo.StructFoo{} //@item(litFooStructFoo, "foo.StructFoo{}", "struct{...}", "struct") -- -- // We get the literal completion for "foo.StructFoo{}" even though we haven't -- // imported "foo" yet. -- baz.FooStruct = f //@snippet(" //", litFooStructFoo, "foo.StructFoo{$0\\}", "foo.StructFoo{$0\\}") +- var foo error +- foo.ifnotnil -} -diff -urN a/gopls/internal/lsp/testdata/unimported/unimported.go.in b/gopls/internal/lsp/testdata/unimported/unimported.go.in ---- a/gopls/internal/lsp/testdata/unimported/unimported.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/unimported/unimported.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,23 +0,0 @@ --package unimported +-`, +- after: ` +-package foo - -func _() { -- http //@unimported("p", nethttp) -- // container/ring is extremely unlikely to be imported by anything, so shouldn't have type information. -- ring.Ring //@unimported("Ring", ringring) -- signature.Foo //@unimported("Foo", signaturefoo) -- -- context.Bac //@unimported(" //", contextBackground) +- var foo error +- if foo != nil { +- $0 -} -- --// Create markers for unimported std lib packages. Only for use by this test. --/* http */ //@item(nethttp, "http", "\"net/http\"", "package") -- --/* ring.Ring */ //@item(ringring, "Ring", "(from \"container/ring\")", "var") -- --/* signature.Foo */ //@item(signaturefoo, "Foo", "func (from \"golang.org/lsptests/signature\")", "func") -- --/* context.Background */ //@item(contextBackground, "Background", "func (from \"context\")", "func") -- --// Now that we no longer type-check imported completions, --// we don't expect the context.Background().Err method (see golang/go#58663). --/* context.Background().Err */ //@item(contextBackgroundErr, "Background().Err", "func (from \"context\")", "method") -diff -urN a/gopls/internal/lsp/testdata/unimported/x_test.go b/gopls/internal/lsp/testdata/unimported/x_test.go ---- a/gopls/internal/lsp/testdata/unimported/x_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/unimported/x_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package unimported_test -- --import ( -- "testing" --) -- --func TestSomething(t *testing.T) { -- _ = unimported.TestExport //@unimported("TestExport", testexport) -} -diff -urN a/gopls/internal/lsp/testdata/unresolved/unresolved.go.in b/gopls/internal/lsp/testdata/unresolved/unresolved.go.in ---- a/gopls/internal/lsp/testdata/unresolved/unresolved.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/unresolved/unresolved.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,6 +0,0 @@ --package unresolved +-`, +- }, +- { +- name: "if not nil pointer", +- before: ` +-package foo - --func foo(interface{}) { -- // don't crash on fake "resolved" type -- foo(func(i, j f //@complete(" //") +-func _() { +- var foo *int +- foo.ifnotnil -} -diff -urN a/gopls/internal/lsp/testdata/unsafe/unsafe.go b/gopls/internal/lsp/testdata/unsafe/unsafe.go ---- a/gopls/internal/lsp/testdata/unsafe/unsafe.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/unsafe/unsafe.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --package unsafe -- --import ( -- "unsafe" --) -- --// Pre-set this marker, as we don't have a "source" for it in this package. --/* unsafe.Sizeof */ //@item(Sizeof, "Sizeof", "invalid type", "text") -- --func _() { -- x := struct{}{} -- _ = unsafe.Sizeof(x) //@complete("z", Sizeof) --} -diff -urN a/gopls/internal/lsp/testdata/variadic/variadic.go.in b/gopls/internal/lsp/testdata/variadic/variadic.go.in ---- a/gopls/internal/lsp/testdata/variadic/variadic.go.in 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/variadic/variadic.go.in 1970-01-01 00:00:00.000000000 +0000 -@@ -1,38 +0,0 @@ --package variadic -- --func foo(i int, strs ...string) {} +-`, +- after: ` +-package foo - --func bar() []string { //@item(vFunc, "bar", "func() []string", "func") -- return nil +-func _() { +- var foo *int +- if foo != nil { +- $0 -} +-} +-`, +- }, +- { +- name: "if not nil slice", +- before: ` +-package foo - -func _() { -- var ( -- i int //@item(vInt, "i", "int", "var") -- s string //@item(vStr, "s", "string", "var") -- ss []string //@item(vStrSlice, "ss", "[]string", "var") -- v interface{} //@item(vIntf, "v", "interface{}", "var") -- ) -- -- foo() //@rank(")", vInt, vStr),rank(")", vInt, vStrSlice) -- foo(123, ) //@rank(")", vStr, vInt),rank(")", vStrSlice, vInt) -- foo(123, "", ) //@rank(")", vStr, vInt),rank(")", vStr, vStrSlice) -- foo(123, s, "") //@rank(", \"", vStr, vStrSlice) -- -- // snippet will add the "..." for you -- foo(123, ) //@snippet(")", vStrSlice, "ss...", "ss..."),snippet(")", vFunc, "bar()...", "bar()..."),snippet(")", vStr, "s", "s") -- -- // don't add "..." for interface{} -- foo(123, ) //@snippet(")", vIntf, "v", "v") +- var foo []int +- foo.ifnotnil -} -- --func qux(...func()) {} --func f() {} //@item(vVarArg, "f", "func()", "func") +-`, +- after: ` +-package foo - -func _() { -- qux(f) //@snippet(")", vVarArg, "f", "f") +- var foo []int +- if foo != nil { +- $0 +-} -} +-`, +- }, +- { +- name: "if not nil map", +- before: ` +-package foo - -func _() { -- foo(0, []string{}...) //@complete(")") +- var foo map[string]any +- foo.ifnotnil -} -diff -urN a/gopls/internal/lsp/testdata/variadic/variadic_intf.go b/gopls/internal/lsp/testdata/variadic/variadic_intf.go ---- a/gopls/internal/lsp/testdata/variadic/variadic_intf.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/variadic/variadic_intf.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,21 +0,0 @@ --package variadic +-`, +- after: ` +-package foo - --type baz interface { -- baz() +-func _() { +- var foo map[string]any +- if foo != nil { +- $0 -} +-} +-`, +- }, +- { +- name: "if not nil channel", +- before: ` +-package foo - --func wantsBaz(...baz) {} -- --type bazImpl int -- --func (bazImpl) baz() {} +-func _() { +- var foo chan int +- foo.ifnotnil +-} +-`, +- after: ` +-package foo - -func _() { -- var ( -- impls []bazImpl //@item(vImplSlice, "impls", "[]bazImpl", "var") -- impl bazImpl //@item(vImpl, "impl", "bazImpl", "var") -- bazes []baz //@item(vIntfSlice, "bazes", "[]baz", "var") -- ) +- var foo chan int +- if foo != nil { +- $0 +-} +-} +-`, +- }, +- { +- name: "if not nil function", +- before: ` +-package foo - -- wantsBaz() //@rank(")", vImpl, vImplSlice),rank(")", vIntfSlice, vImplSlice) +-func _() { +- var foo func() +- foo.ifnotnil -} -diff -urN a/gopls/internal/lsp/testdata/workspacesymbol/a/a.go b/gopls/internal/lsp/testdata/workspacesymbol/a/a.go ---- a/gopls/internal/lsp/testdata/workspacesymbol/a/a.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/workspacesymbol/a/a.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,9 +0,0 @@ --package a +-`, +- after: ` +-package foo - --var RandomGopherVariableA = "a" +-func _() { +- var foo func() +- if foo != nil { +- $0 +-} +-} +-`, +- }, +- } - --const RandomGopherConstantA = "a" +- r := WithOptions( +- Settings{ +- "experimentalPostfixCompletions": true, +- }, +- ) +- r.Run(t, mod, func(t *testing.T, env *Env) { +- env.CreateBuffer("foo.go", "") - --const ( -- randomgopherinvariable = iota --) -diff -urN a/gopls/internal/lsp/testdata/workspacesymbol/a/a_test.go b/gopls/internal/lsp/testdata/workspacesymbol/a/a_test.go ---- a/gopls/internal/lsp/testdata/workspacesymbol/a/a_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/workspacesymbol/a/a_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ --package a +- 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") - --var RandomGopherTestVariableA = "a" -diff -urN a/gopls/internal/lsp/testdata/workspacesymbol/a/a_x_test.go b/gopls/internal/lsp/testdata/workspacesymbol/a/a_x_test.go ---- a/gopls/internal/lsp/testdata/workspacesymbol/a/a_x_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/workspacesymbol/a/a_x_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ --package a_test +- env.SetBufferContent("foo.go", c.before) - --var RandomGopherXTestVariableA = "a" -diff -urN a/gopls/internal/lsp/testdata/workspacesymbol/b/b.go b/gopls/internal/lsp/testdata/workspacesymbol/b/b.go ---- a/gopls/internal/lsp/testdata/workspacesymbol/b/b.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/workspacesymbol/b/b.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,7 +0,0 @@ --package b +- loc := env.RegexpSearch("foo.go", "\n}") +- completions := env.Completion(loc) +- if len(completions.Items) != 1 { +- t.Fatalf("expected one completion, got %v", completions.Items) +- } - --var RandomGopherVariableB = "b" +- env.AcceptCompletion(loc, completions.Items[0]) - --type RandomGopherStructB struct { -- Bar int +- if buf := env.BufferText("foo.go"); buf != c.after { +- t.Errorf("\nGOT:\n%s\nEXPECTED:\n%s", buf, c.after) +- } +- }) +- } +- }) -} -diff -urN a/gopls/internal/lsp/testdata/workspacesymbol/issue44806.go b/gopls/internal/lsp/testdata/workspacesymbol/issue44806.go ---- a/gopls/internal/lsp/testdata/workspacesymbol/issue44806.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/workspacesymbol/issue44806.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,10 +0,0 @@ --package main -- --type T struct{} +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 08:00:00 +@@ -1,101 +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. - --// We should accept all valid receiver syntax when scanning symbols. --func (*(T)) m1() {} --func (*T) m2() {} --func (T) m3() {} --func ((T)) m4() {} --func ((*T)) m5() {} -diff -urN a/gopls/internal/lsp/testdata/workspacesymbol/main.go b/gopls/internal/lsp/testdata/workspacesymbol/main.go ---- a/gopls/internal/lsp/testdata/workspacesymbol/main.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/workspacesymbol/main.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,47 +0,0 @@ --package main +-package debug - -import ( +- "context" - "encoding/json" -- "fmt" --) -- --func main() { // function -- fmt.Println("Hello") --} -- --var myvar int // variable -- --type myType string // basic type -- --type myDecoder json.Decoder // to use the encoding/json import +- "io" +- "net/http" +- "strings" +- "testing" - --func (m *myType) Blahblah() {} // method +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/hooks" +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) - --type myStruct struct { // struct type -- myStructField int // struct field +-func TestMain(m *testing.M) { +- Main(m, hooks.Options) -} - --type myInterface interface { // interface -- DoSomeCoolStuff() string // interface method +-func TestBugNotification(t *testing.T) { +- // Verify that a properly configured session gets notified of a bug on the +- // server. +- WithOptions( +- Modes(Default), // must be in-process to receive the bug report below +- Settings{"showBugReports": true}, +- ).Run(t, "", func(t *testing.T, env *Env) { +- const desc = "got a bug" +- bug.Report(desc) +- env.Await(ShownMessage(desc)) +- }) -} - --type embed struct { -- myStruct +-// TestStartDebugging executes a gopls.start_debugging command to +-// start the internal web server. +-func TestStartDebugging(t *testing.T) { +- WithOptions( +- Modes(Default|Experimental), // doesn't work in Forwarded mode +- ).Run(t, "", func(t *testing.T, env *Env) { +- // Start a debugging server. +- res, err := startDebugging(env.Ctx, env.Editor.Server, &command.DebuggingArgs{ +- Addr: "", // any free port +- }) +- if err != nil { +- t.Fatalf("startDebugging: %v", err) +- } - -- nestedStruct struct { -- nestedField int +- // Assert that the server requested that the +- // client show the debug page in a browser. +- debugURL := res.URLs[0] +- env.Await(ShownDocument(debugURL)) - -- nestedStruct2 struct { -- int +- // Send a request to the debug server and ensure it responds. +- resp, err := http.Get(debugURL) +- if err != nil { +- t.Fatal(err) - } -- } -- -- nestedInterface interface { -- myInterface -- nestedMethod() -- } +- defer resp.Body.Close() +- data, err := io.ReadAll(resp.Body) +- if err != nil { +- t.Fatalf("reading HTTP response body: %v", err) +- } +- const want = "GoPls" +- if !strings.Contains(string(data), want) { +- t.Errorf("GET %s response does not contain %q: <<%s>>", debugURL, want, data) +- } +- }) -} - --func Dunk() int { return 0 } -- --func dunk() {} -diff -urN a/gopls/internal/lsp/testdata/workspacesymbol/p/p.go b/gopls/internal/lsp/testdata/workspacesymbol/p/p.go ---- a/gopls/internal/lsp/testdata/workspacesymbol/p/p.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/workspacesymbol/p/p.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,3 +0,0 @@ --package p -- --const Message = "Hello World." // constant -diff -urN a/gopls/internal/lsp/testdata/workspacesymbol/query.go b/gopls/internal/lsp/testdata/workspacesymbol/query.go ---- a/gopls/internal/lsp/testdata/workspacesymbol/query.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/workspacesymbol/query.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,29 +0,0 @@ --package main -- --// Contains all of the workspace symbol queries. -- --// -- Fuzzy matching -- --//@workspacesymbolfuzzy("rgop") --//@workspacesymbolfuzzy("randoma") --//@workspacesymbolfuzzy("randomb") -- --// -- Case sensitive -- --//@workspacesymbolcasesensitive("main.main") --//@workspacesymbolcasesensitive("p.Message") --//@workspacesymbolcasesensitive("main.myvar") --//@workspacesymbolcasesensitive("main.myType") --//@workspacesymbolcasesensitive("main.myType.Blahblah") --//@workspacesymbolcasesensitive("main.myStruct") --//@workspacesymbolcasesensitive("main.myStruct.myStructField") --//@workspacesymbolcasesensitive("main.myInterface") --//@workspacesymbolcasesensitive("main.myInterface.DoSomeCoolStuff") --//@workspacesymbolcasesensitive("main.embed.myStruct") --//@workspacesymbolcasesensitive("main.embed.nestedStruct.nestedStruct2.int") --//@workspacesymbolcasesensitive("main.embed.nestedInterface.myInterface") --//@workspacesymbolcasesensitive("main.embed.nestedInterface.nestedMethod") --//@workspacesymbolcasesensitive("dunk") --//@workspacesymbolcasesensitive("Dunk") -- --// -- Standard -- --//@workspacesymbol("") --//@workspacesymbol("randomgophervar") -diff -urN a/gopls/internal/lsp/testdata/workspacesymbol/query.go.golden b/gopls/internal/lsp/testdata/workspacesymbol/query.go.golden ---- a/gopls/internal/lsp/testdata/workspacesymbol/query.go.golden 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/testdata/workspacesymbol/query.go.golden 1970-01-01 00:00:00.000000000 +0000 -@@ -1,83 +0,0 @@ ---- workspace_symbol-caseinsensitive- -- -- -- ---- workspace_symbol-caseinsensitive-randomgophervar -- --workspacesymbol/a/a.go:3:5-26 RandomGopherVariableA Variable --workspacesymbol/b/b.go:3:5-26 RandomGopherVariableB Variable -- ---- workspace_symbol-casesensitive-Dunk -- --workspacesymbol/main.go:45:6-10 Dunk Function -- ---- workspace_symbol-casesensitive-dunk -- --workspacesymbol/main.go:47:6-10 dunk Function -- ---- workspace_symbol-casesensitive-main.embed.myStruct -- --workspacesymbol/main.go:29:2-10 main.embed.myStruct Field -- ---- workspace_symbol-casesensitive-main.embed.nestedInterface.myInterface -- --workspacesymbol/main.go:40:3-14 main.embed.nestedInterface.myInterface Interface -- ---- workspace_symbol-casesensitive-main.embed.nestedInterface.nestedMethod -- --workspacesymbol/main.go:41:3-15 main.embed.nestedInterface.nestedMethod Method -- ---- workspace_symbol-casesensitive-main.embed.nestedStruct.nestedStruct2.int -- --workspacesymbol/main.go:35:4-7 main.embed.nestedStruct.nestedStruct2.int Field -- ---- workspace_symbol-casesensitive-main.main -- --workspacesymbol/main.go:8:6-10 main.main Function -- ---- workspace_symbol-casesensitive-main.myInterface -- --workspacesymbol/main.go:24:6-17 main.myInterface Interface --workspacesymbol/main.go:25:2-17 main.myInterface.DoSomeCoolStuff Method -- ---- workspace_symbol-casesensitive-main.myInterface.DoSomeCoolStuff -- --workspacesymbol/main.go:25:2-17 main.myInterface.DoSomeCoolStuff Method -- ---- workspace_symbol-casesensitive-main.myStruct -- --workspacesymbol/main.go:20:6-14 main.myStruct Struct --workspacesymbol/main.go:21:2-15 main.myStruct.myStructField Field -- ---- workspace_symbol-casesensitive-main.myStruct.myStructField -- --workspacesymbol/main.go:21:2-15 main.myStruct.myStructField Field -- ---- workspace_symbol-casesensitive-main.myType -- --workspacesymbol/main.go:14:6-12 main.myType Class --workspacesymbol/main.go:18:18-26 main.myType.Blahblah Method -- ---- workspace_symbol-casesensitive-main.myType.Blahblah -- --workspacesymbol/main.go:18:18-26 main.myType.Blahblah Method -- ---- workspace_symbol-casesensitive-main.myvar -- --workspacesymbol/main.go:12:5-10 main.myvar Variable -- ---- workspace_symbol-casesensitive-p.Message -- --workspacesymbol/p/p.go:3:7-14 p.Message Constant -- ---- workspace_symbol-fuzzy-randoma -- --workspacesymbol/a/a.go:3:5-26 RandomGopherVariableA Variable --workspacesymbol/a/a.go:5:7-28 RandomGopherConstantA Constant --workspacesymbol/a/a.go:8:2-24 randomgopherinvariable Constant --workspacesymbol/a/a_test.go:3:5-30 RandomGopherTestVariableA Variable --workspacesymbol/a/a_x_test.go:3:5-31 RandomGopherXTestVariableA Variable --workspacesymbol/b/b.go:3:5-26 RandomGopherVariableB Variable --workspacesymbol/b/b.go:6:2-5 RandomGopherStructB.Bar Field -- ---- workspace_symbol-fuzzy-randomb -- --workspacesymbol/a/a.go:3:5-26 RandomGopherVariableA Variable --workspacesymbol/a/a.go:8:2-24 randomgopherinvariable Constant --workspacesymbol/a/a_test.go:3:5-30 RandomGopherTestVariableA Variable --workspacesymbol/a/a_x_test.go:3:5-31 RandomGopherXTestVariableA Variable --workspacesymbol/b/b.go:3:5-26 RandomGopherVariableB Variable --workspacesymbol/b/b.go:5:6-25 RandomGopherStructB Struct --workspacesymbol/b/b.go:6:2-5 RandomGopherStructB.Bar Field -- ---- workspace_symbol-fuzzy-rgop -- --workspacesymbol/a/a.go:3:5-26 RandomGopherVariableA Variable --workspacesymbol/a/a.go:5:7-28 RandomGopherConstantA Constant --workspacesymbol/a/a.go:8:2-24 randomgopherinvariable Constant --workspacesymbol/a/a_test.go:3:5-30 RandomGopherTestVariableA Variable --workspacesymbol/a/a_x_test.go:3:5-31 RandomGopherXTestVariableA Variable --workspacesymbol/b/b.go:3:5-26 RandomGopherVariableB Variable --workspacesymbol/b/b.go:5:6-25 RandomGopherStructB Struct --workspacesymbol/b/b.go:6:2-5 RandomGopherStructB.Bar Field -- -diff -urN a/gopls/internal/lsp/tests/compare/text.go b/gopls/internal/lsp/tests/compare/text.go ---- a/gopls/internal/lsp/tests/compare/text.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/tests/compare/text.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,49 +0,0 @@ +-// startDebugging starts a debugging server. +-// TODO(adonovan): move into command package? +-func startDebugging(ctx context.Context, server protocol.Server, args *command.DebuggingArgs) (*command.DebuggingResult, error) { +- rawArgs, err := command.MarshalArgs(args) +- if err != nil { +- return nil, err +- } +- res0, err := server.ExecuteCommand(ctx, &protocol.ExecuteCommandParams{ +- Command: command.StartDebugging.ID(), +- Arguments: rawArgs, +- }) +- if err != nil { +- return nil, err +- } +- // res0 is the result of a schemaless (map[string]any) JSON decoding. +- // Re-encode and decode into the correct Go struct type. +- // TODO(adonovan): fix (*serverDispatcher).ExecuteCommand. +- data, err := json.Marshal(res0) +- if err != nil { +- return nil, err +- } +- var res *command.DebuggingResult +- if err := json.Unmarshal(data, &res); err != nil { +- return nil, err +- } +- return res, nil +-} +diff -urN a/gopls/internal/regtest/diagnostics/analysis_test.go b/gopls/internal/regtest/diagnostics/analysis_test.go +--- a/gopls/internal/regtest/diagnostics/analysis_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/diagnostics/analysis_test.go 1970-01-01 08:00:00 +@@ -1,127 +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 compare +-package diagnostics - -import ( -- "bytes" +- "fmt" +- "testing" - -- "golang.org/x/tools/internal/diff" +- "golang.org/x/tools/gopls/internal/lsp/cache" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" -) - --// Text returns a formatted unified diff of the edits to go from want to --// got, returning "" if and only if want == got. +-// Test for the timeformat analyzer, following golang/vscode-go#2406. -// --// This function is intended for use in testing, and panics if any error occurs --// while computing the diff. It is not sufficiently tested for production use. --func Text(want, got string) string { -- return NamedText("want", "got", want, got) --} +-// This test checks that applying the suggested fix from the analyzer resolves +-// the diagnostic warning. +-func TestTimeFormatAnalyzer(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - --// NamedText is like text, but allows passing custom names of the 'want' and --// 'got' content. --func NamedText(wantName, gotName, want, got string) string { -- if want == got { -- return "" -- } +-go 1.18 +--- main.go -- +-package main - -- // Add newlines to avoid verbose newline messages ("No newline at end of file"). -- unified := diff.Unified(wantName, gotName, want+"\n", got+"\n") +-import ( +- "fmt" +- "time" +-) - -- // Defensively assert that we get an actual diff, so that we guarantee the -- // invariant that we return "" if and only if want == got. -- // -- // This is probably unnecessary, but convenient. -- if unified == "" { -- panic("empty diff for non-identical input") -- } +-func main() { +- now := time.Now() +- fmt.Println(now.Format("2006-02-01")) +-}` - -- return unified --} +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") - --// Bytes is like Text but using byte slices. --func Bytes(want, got []byte) string { -- if bytes.Equal(want, got) { -- return "" // common case -- } -- return Text(string(want), string(got)) +- var d protocol.PublishDiagnosticsParams +- env.AfterChange( +- Diagnostics(env.AtRegexp("main.go", "2006-02-01")), +- ReadDiagnostics("main.go", &d), +- ) +- +- env.ApplyQuickFixes("main.go", d.Diagnostics) +- env.AfterChange(NoDiagnostics(ForFile("main.go"))) +- }) -} -diff -urN a/gopls/internal/lsp/tests/compare/text_test.go b/gopls/internal/lsp/tests/compare/text_test.go ---- a/gopls/internal/lsp/tests/compare/text_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/tests/compare/text_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,28 +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 compare_test +-func TestAnalysisProgressReporting(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - --import ( -- "testing" +-go 1.18 - -- "golang.org/x/tools/gopls/internal/lsp/tests/compare" --) +--- main.go -- +-package main +- +-func main() { +-}` - --func TestText(t *testing.T) { - tests := []struct { -- got, want, wantDiff string +- setting bool +- want Expectation - }{ -- {"", "", ""}, -- {"equal", "equal", ""}, -- {"a", "b", "--- want\n+++ got\n@@ -1 +1 @@\n-b\n+a\n"}, -- {"a\nd\nc\n", "a\nb\nc\n", "--- want\n+++ got\n@@ -1,4 +1,4 @@\n a\n-b\n+d\n c\n \n"}, +- {true, CompletedWork(cache.AnalysisProgressTitle, 1, true)}, +- {false, Not(CompletedWork(cache.AnalysisProgressTitle, 1, true))}, - } - - for _, test := range tests { -- if gotDiff := compare.Text(test.want, test.got); gotDiff != test.wantDiff { -- t.Errorf("compare.Text(%q, %q) =\n%q, want\n%q", test.want, test.got, gotDiff, test.wantDiff) -- } +- t.Run(fmt.Sprint(test.setting), func(t *testing.T) { +- WithOptions( +- Settings{ +- "reportAnalysisProgressAfter": "0s", +- "analysisProgressReporting": test.setting, +- }, +- ).Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- env.AfterChange(test.want) +- }) +- }) - } -} -diff -urN a/gopls/internal/lsp/tests/markdown_go118.go b/gopls/internal/lsp/tests/markdown_go118.go ---- a/gopls/internal/lsp/tests/markdown_go118.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/tests/markdown_go118.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,69 +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.19 --// +build !go1.19 -- --package tests -- --import ( -- "regexp" -- "strings" -- -- "golang.org/x/tools/gopls/internal/lsp/tests/compare" --) -- --// DiffMarkdown compares two markdown strings produced by parsing go doc --// comments. --// --// For go1.19 and later, markdown conversion is done using go/doc/comment. --// Compared to the newer version, the older version has extra escapes, and --// treats code blocks slightly differently. --func DiffMarkdown(want, got string) string { -- want = normalizeMarkdown(want) -- got = normalizeMarkdown(got) -- return compare.Text(want, got) --} - --// normalizeMarkdown normalizes whitespace and escaping of the input string, to --// eliminate differences between the Go 1.18 and Go 1.19 generated markdown for --// doc comments. Note that it does not normalize to either the 1.18 or 1.19 --// formatting: it simplifies both so that they may be compared. +-// Test the embed directive analyzer. -// --// This function may need to be adjusted as we encounter more differences in --// the generated text. --// --// TODO(rfindley): this function doesn't correctly handle the case of --// multi-line docstrings. --func normalizeMarkdown(input string) string { -- input = strings.TrimSpace(input) -- -- // For simplicity, eliminate blank lines. -- input = regexp.MustCompile("\n+").ReplaceAllString(input, "\n") +-// There is a fix for missing imports, but it should not trigger for other +-// kinds of issues reported by the analayzer, here the variable +-// declaration following the embed directive is wrong. +-func TestNoSuggestedFixesForEmbedDirectiveDeclaration(t *testing.T) { +- const generated = ` +--- go.mod -- +-module mod.com - -- // Replace common escaped characters with their unescaped version. -- // -- // This list may not be exhaustive: it was just sufficient to make tests -- // pass. -- input = strings.NewReplacer( -- `\\`, ``, -- `\@`, `@`, -- `\(`, `(`, -- `\)`, `)`, -- `\{`, `{`, -- `\}`, `}`, -- `\"`, `"`, -- `\.`, `.`, -- `\-`, `-`, -- `\'`, `'`, -- `\+`, `+`, -- `\~`, `~`, -- `\=`, `=`, -- `\:`, `:`, -- `\?`, `?`, -- `\n\n\n`, `\n\n`, // Note that these are *escaped* newlines. -- ).Replace(input) +-go 1.20 - -- return input --} -diff -urN a/gopls/internal/lsp/tests/markdown_go119.go b/gopls/internal/lsp/tests/markdown_go119.go ---- a/gopls/internal/lsp/tests/markdown_go119.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/tests/markdown_go119.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,22 +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. +--- foo.txt -- +-FOO - --//go:build go1.19 --// +build go1.19 +--- main.go -- +-package main - --package tests +-import _ "embed" - --import ( -- "golang.org/x/tools/gopls/internal/lsp/tests/compare" --) +-//go:embed foo.txt +-var foo, bar string - --// DiffMarkdown compares two markdown strings produced by parsing go doc --// comments. --// --// For go1.19 and later, markdown conversion is done using go/doc/comment. --// Compared to the newer version, the older version has extra escapes, and --// treats code blocks slightly differently. --func DiffMarkdown(want, got string) string { -- return compare.Text(want, got) +-func main() { +- _ = foo -} -diff -urN a/gopls/internal/lsp/tests/normalizer.go b/gopls/internal/lsp/tests/normalizer.go ---- a/gopls/internal/lsp/tests/normalizer.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/tests/normalizer.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,113 +0,0 @@ --// Copyright 2019 The Go Authors. All rights reserved. +-` +- Run(t, generated, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- var d protocol.PublishDiagnosticsParams +- env.AfterChange( +- Diagnostics(env.AtRegexp("main.go", "//go:embed")), +- ReadDiagnostics("main.go", &d), +- ) +- if fixes := env.GetQuickFixes("main.go", d.Diagnostics); len(fixes) != 0 { +- t.Errorf("got quick fixes %v, wanted none", fixes) +- } +- }) +-} +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 08:00:00 +@@ -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 tests +-package diagnostics - -import ( -- "path/filepath" -- "strconv" - "strings" +- "testing" - -- "golang.org/x/tools/go/packages/packagestest" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" -) - --type Normalizer struct { -- path string -- slashed string -- escaped string -- fragment string --} +-func TestIssue44866(t *testing.T) { +- src := ` +--- go.mod -- +-module mod.com - --func CollectNormalizers(exported *packagestest.Exported) []Normalizer { -- // build the path normalizing patterns -- var normalizers []Normalizer -- for _, m := range exported.Modules { -- for fragment := range m.Files { -- n := Normalizer{ -- path: exported.File(m.Name, fragment), -- fragment: fragment, -- } -- if n.slashed = filepath.ToSlash(n.path); n.slashed == n.path { -- n.slashed = "" -- } -- quoted := strconv.Quote(n.path) -- if n.escaped = quoted[1 : len(quoted)-1]; n.escaped == n.path { -- n.escaped = "" -- } -- normalizers = append(normalizers, n) -- } -- } -- return normalizers --} +-go 1.12 +--- a.go -- +-package a - --// Normalize replaces all paths present in s with just the fragment portion --// this is used to make golden files not depend on the temporary paths of the files --func Normalize(s string, normalizers []Normalizer) string { -- type entry struct { -- path string -- index int -- fragment string -- } -- var match []entry -- // collect the initial state of all the matchers -- for _, n := range normalizers { -- index := strings.Index(s, n.path) -- if index >= 0 { -- match = append(match, entry{n.path, index, n.fragment}) -- } -- if n.slashed != "" { -- index := strings.Index(s, n.slashed) -- if index >= 0 { -- match = append(match, entry{n.slashed, index, n.fragment}) -- } -- } -- if n.escaped != "" { -- index := strings.Index(s, n.escaped) -- if index >= 0 { -- match = append(match, entry{n.escaped, index, n.fragment}) -- } -- } -- } -- // result should be the same or shorter than the input -- var b strings.Builder -- last := 0 -- for { -- // find the nearest path match to the start of the buffer -- next := -1 -- nearest := len(s) -- for i, c := range match { -- if c.index >= 0 && nearest > c.index { -- nearest = c.index -- next = i -- } -- } -- // if there are no matches, we copy the rest of the string and are done -- if next < 0 { -- b.WriteString(s[last:]) -- return b.String() +-const ( +- c = iota +-) +-` +- Run(t, src, func(t *testing.T, env *Env) { +- env.OpenFile("a.go") +- loc := env.GoToDefinition(env.RegexpSearch("a.go", "iota")) +- if !strings.HasSuffix(string(loc.URI), "builtin.go") { +- t.Fatalf("jumped to %q, want builtin.go", loc.URI) - } -- // we have a match -- n := &match[next] -- // copy up to the start of the match -- b.WriteString(s[last:n.index]) -- // skip over the filename -- last = n.index + len(n.path) +- env.AfterChange(NoDiagnostics(ForFile("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 08:00:00 +@@ -1,2114 +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. - -- // Hack: In multi-module mode, we add a "testmodule/" prefix, so trim -- // it from the fragment. -- fragment := n.fragment -- if strings.HasPrefix(fragment, "testmodule") { -- split := strings.Split(filepath.ToSlash(fragment), "/") -- fragment = filepath.FromSlash(strings.Join(split[1:], "/")) -- } +-package diagnostics - -- // add in the fragment instead -- b.WriteString(fragment) -- // see what the next match for this path is -- n.index = strings.Index(s[last:], n.path) -- if n.index >= 0 { -- n.index += last -- } -- } --} -diff -urN a/gopls/internal/lsp/tests/README.md b/gopls/internal/lsp/tests/README.md ---- a/gopls/internal/lsp/tests/README.md 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/tests/README.md 1970-01-01 00:00:00.000000000 +0000 -@@ -1,66 +0,0 @@ --# Testing +-import ( +- "context" +- "fmt" +- "os/exec" +- "testing" - --LSP has "marker tests" defined in `internal/lsp/testdata`, as well as --traditional tests. +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/hooks" +- "golang.org/x/tools/gopls/internal/lsp" +- "golang.org/x/tools/gopls/internal/lsp/fake" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +- "golang.org/x/tools/internal/testenv" +-) - --## Marker tests +-func TestMain(m *testing.M) { +- bug.PanicOnBugs = true +- Main(m, hooks.Options) +-} - --Marker tests have a standard input file, like --`internal/lsp/testdata/foo/bar.go`, and some may have a corresponding golden --file, like `internal/lsp/testdata/foo/bar.go.golden`. The former is the "input" --and the latter is the expected output. +-// Use mod.com for all go.mod files due to golang/go#35230. +-const exampleProgram = ` +--- go.mod -- +-module mod.com - --Each input file contains annotations like --`//@suggestedfix("}", "refactor.rewrite", "Fill anonymous struct")`. These annotations are interpreted by --test runners to perform certain actions. The expected output after those actions --is encoded in the golden file. +-go 1.12 +--- main.go -- +-package main - --When tests are run, each annotation results in a new subtest, which is encoded --in the golden file with a heading like, +-import "fmt" - --```bash ---- suggestedfix_bar_11_21 -- --// expected contents go here ---- suggestedfix_bar_13_20 -- --// expected contents go here --``` +-func main() { +- fmt.Println("Hello World.") +-}` - --The format of these headings vary: they are defined by the --[`Golden`](https://pkg.go.dev/golang.org/x/tools/gopls/internal/lsp/tests#Data.Golden) --function for each annotation. In the case above, the format is: annotation --name, file name, annotation line location, annotation character location. +-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.AfterChange( +- Diagnostics(env.AtRegexp("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(), +- ) +- }) +-} - --So, if `internal/lsp/testdata/foo/bar.go` has three `suggestedfix` annotations, --the golden file should have three headers with `suggestedfix_bar_xx_yy` --headings. +-func TestMissingImportDiagsClearOnFirstFile(t *testing.T) { +- const onlyMod = ` +--- go.mod -- +-module mod.com - --To see a list of all available annotations, see the exported "expectations" in --[tests.go](https://github.com/golang/tools/blob/299f270db45902e93469b1152fafed034bb3f033/internal/lsp/tests/tests.go#L418-L447). +-go 1.12 +-` +- Run(t, onlyMod, func(t *testing.T, env *Env) { +- env.CreateBuffer("main.go", `package main - --To run marker tests, +-func m() { +- log.Println() +-} +-`) +- env.AfterChange(Diagnostics(env.AtRegexp("main.go", "log"))) +- env.SaveBuffer("main.go") +- env.AfterChange(NoDiagnostics(ForFile("main.go"))) +- }) +-} - --```bash --cd /path/to/tools +-func TestDiagnosticErrorInNewFile(t *testing.T) { +- const brokenFile = `package main - --# The marker tests are located in "internal/lsp", "internal/lsp/cmd, and --# "internal/lsp/source". --go test ./internal/lsp/... --``` +-const Foo = "abc +-` +- Run(t, brokenFile, func(t *testing.T, env *Env) { +- env.CreateBuffer("broken.go", brokenFile) +- env.AfterChange(Diagnostics(env.AtRegexp("broken.go", "\"abc"))) +- }) +-} - --There are quite a lot of marker tests, so to run one individually, pass the test --path and heading into a -run argument: +-// badPackage contains a duplicate definition of the 'a' const. +-const badPackage = ` +--- go.mod -- +-module mod.com - --```bash --cd /path/to/tools --go test ./internal/lsp/... -v -run TestLSP/Modules/SuggestedFix/bar_11_21 --``` +-go 1.12 +--- a.go -- +-package consts - --## Resetting marker tests +-const a = 1 +--- b.go -- +-package consts - --Sometimes, a change is made to lsp that requires a change to multiple golden --files. When this happens, you can run, +-const a = 2 +-` - --```bash --cd /path/to/tools --./internal/lsp/reset_golden.sh --``` -diff -urN a/gopls/internal/lsp/tests/tests.go b/gopls/internal/lsp/tests/tests.go ---- a/gopls/internal/lsp/tests/tests.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/tests/tests.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,1446 +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. +-func TestDiagnosticClearingOnEdit(t *testing.T) { +- Run(t, badPackage, func(t *testing.T, env *Env) { +- env.OpenFile("b.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("a.go", "a = 1")), +- Diagnostics(env.AtRegexp("b.go", "a = 2")), +- ) - --// Package tests exports functionality to be used across a variety of gopls tests. --package tests +- // Fix the error by editing the const name in b.go to `b`. +- env.RegexpReplace("b.go", "(a) = 2", "b") +- env.AfterChange( +- NoDiagnostics(ForFile("a.go")), +- NoDiagnostics(ForFile("b.go")), +- ) +- }) +-} - --import ( -- "bytes" -- "context" -- "flag" -- "fmt" -- "go/ast" -- "go/token" -- "io" -- "io/ioutil" -- "os" -- "path/filepath" -- "regexp" -- "sort" -- "strconv" -- "strings" -- "sync" -- "testing" -- "time" +-func TestDiagnosticClearingOnDelete_Issue37049(t *testing.T) { +- Run(t, badPackage, func(t *testing.T, env *Env) { +- env.OpenFile("a.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("a.go", "a = 1")), +- Diagnostics(env.AtRegexp("b.go", "a = 2")), +- ) +- env.RemoveWorkspaceFile("b.go") - -- "golang.org/x/tools/go/expect" -- "golang.org/x/tools/go/packages" -- "golang.org/x/tools/go/packages/packagestest" -- "golang.org/x/tools/gopls/internal/lsp/command" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/safetoken" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/lsp/source/completion" -- "golang.org/x/tools/gopls/internal/lsp/tests/compare" -- "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/testenv" -- "golang.org/x/tools/internal/typeparams" -- "golang.org/x/tools/txtar" --) +- env.AfterChange( +- NoDiagnostics(ForFile("a.go")), +- NoDiagnostics(ForFile("b.go")), +- ) +- }) +-} - --const ( -- overlayFileSuffix = ".overlay" -- goldenFileSuffix = ".golden" -- inFileSuffix = ".in" +-func TestDiagnosticClearingOnClose(t *testing.T) { +- Run(t, badPackage, func(t *testing.T, env *Env) { +- env.CreateBuffer("c.go", `package consts - -- // The module path containing the testdata packages. -- // -- // Warning: the length of this module path matters, as we have bumped up -- // against command-line limitations on windows (golang/go#54800). -- testModule = "golang.org/lsptests" --) +-const a = 3`) +- env.AfterChange( +- Diagnostics(env.AtRegexp("a.go", "a = 1")), +- Diagnostics(env.AtRegexp("b.go", "a = 2")), +- Diagnostics(env.AtRegexp("c.go", "a = 3")), +- ) +- env.CloseBuffer("c.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("a.go", "a = 1")), +- Diagnostics(env.AtRegexp("b.go", "a = 2")), +- NoDiagnostics(ForFile("c.go")), +- ) +- }) +-} - --var summaryFile = "summary.txt" +-// 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", "") - --func init() { -- if typeparams.Enabled { -- summaryFile = "summary_go1.18.txt" -- } +- // Write the file contents with a missing import. +- env.EditBuffer("c/c.go", protocol.TextEdit{ +- NewText: `package c +- +-const a = http.MethodGet +-`, +- }) +- env.AfterChange( +- Diagnostics(env.AtRegexp("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.AfterChange( +- NoDiagnostics(ForFile("c/c.go")), +- ) +- }) -} - --var UpdateGolden = flag.Bool("golden", false, "Update golden files") +-// 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 - --// These type names apparently avoid the need to repeat the --// type in the field name and the make() expression. --type CallHierarchy = map[span.Span]*CallHierarchyResult --type CodeLens = map[span.URI][]protocol.CodeLens --type Diagnostics = map[span.URI][]*source.Diagnostic --type CompletionItems = map[token.Pos]*completion.CompletionItem --type Completions = map[span.Span][]Completion --type CompletionSnippets = map[span.Span][]CompletionSnippet --type UnimportedCompletions = map[span.Span][]Completion --type DeepCompletions = map[span.Span][]Completion --type FuzzyCompletions = map[span.Span][]Completion --type CaseSensitiveCompletions = map[span.Span][]Completion --type RankCompletions = map[span.Span][]Completion --type FoldingRanges = []span.Span --type Formats = []span.Span --type Imports = []span.Span --type SemanticTokens = []span.Span --type SuggestedFixes = map[span.Span][]SuggestedFix --type FunctionExtractions = map[span.Span]span.Span --type MethodExtractions = map[span.Span]span.Span --type Definitions = map[span.Span]Definition --type Implementations = map[span.Span][]span.Span --type Highlights = map[span.Span][]span.Span --type References = map[span.Span][]span.Span --type Renames = map[span.Span]string --type PrepareRenames = map[span.Span]*source.PrepareItem --type Symbols = map[span.URI][]*symbol --type InlayHints = []span.Span --type WorkspaceSymbols = map[WorkspaceSymbolsTestType]map[span.URI][]string --type Signatures = map[span.Span]*protocol.SignatureHelp --type Links = map[span.URI][]Link --type AddImport = map[span.URI]string --type SelectionRanges = []span.Span +-go 1.12 +--- a.go -- +-package x - --type Data struct { -- Config packages.Config -- Exported *packagestest.Exported -- CallHierarchy CallHierarchy -- CodeLens CodeLens -- Diagnostics Diagnostics -- CompletionItems CompletionItems -- Completions Completions -- CompletionSnippets CompletionSnippets -- UnimportedCompletions UnimportedCompletions -- DeepCompletions DeepCompletions -- FuzzyCompletions FuzzyCompletions -- CaseSensitiveCompletions CaseSensitiveCompletions -- RankCompletions RankCompletions -- FoldingRanges FoldingRanges -- Formats Formats -- Imports Imports -- SemanticTokens SemanticTokens -- SuggestedFixes SuggestedFixes -- FunctionExtractions FunctionExtractions -- MethodExtractions MethodExtractions -- Definitions Definitions -- Implementations Implementations -- Highlights Highlights -- References References -- Renames Renames -- InlayHints InlayHints -- PrepareRenames PrepareRenames -- Symbols Symbols -- WorkspaceSymbols WorkspaceSymbols -- Signatures Signatures -- Links Links -- AddImport AddImport -- SelectionRanges SelectionRanges +-// import "fmt" - -- fragments map[string]string -- dir string -- golden map[string]*Golden -- mode string +-func f() {} - -- ModfileFlagAvailable bool +--- a_test.go -- +-package x - -- mappersMu sync.Mutex -- mappers map[span.URI]*protocol.Mapper +-import "testing" +- +-func TestA(t *testing.T) { +- f(3) -} +-` - --// The Tests interface abstracts the LSP-based implementation of the marker --// test operators (such as @codelens) appearing in files beneath ../testdata/. --// --// TODO(adonovan): reduce duplication; see https://github.com/golang/go/issues/54845. --// There is only one implementation (*runner in ../lsp_test.go), so --// we can abolish the interface now. --type Tests interface { -- CallHierarchy(*testing.T, span.Span, *CallHierarchyResult) -- CodeLens(*testing.T, span.URI, []protocol.CodeLens) -- Diagnostics(*testing.T, span.URI, []*source.Diagnostic) -- Completion(*testing.T, span.Span, Completion, CompletionItems) -- CompletionSnippet(*testing.T, span.Span, CompletionSnippet, bool, CompletionItems) -- UnimportedCompletion(*testing.T, span.Span, Completion, CompletionItems) -- DeepCompletion(*testing.T, span.Span, Completion, CompletionItems) -- FuzzyCompletion(*testing.T, span.Span, Completion, CompletionItems) -- CaseSensitiveCompletion(*testing.T, span.Span, Completion, CompletionItems) -- RankCompletion(*testing.T, span.Span, Completion, CompletionItems) -- FoldingRanges(*testing.T, span.Span) -- Format(*testing.T, span.Span) -- Import(*testing.T, span.Span) -- SemanticTokens(*testing.T, span.Span) -- SuggestedFix(*testing.T, span.Span, []SuggestedFix, int) -- FunctionExtraction(*testing.T, span.Span, span.Span) -- MethodExtraction(*testing.T, span.Span, span.Span) -- Definition(*testing.T, span.Span, Definition) -- Implementation(*testing.T, span.Span, []span.Span) -- Highlight(*testing.T, span.Span, []span.Span) -- InlayHints(*testing.T, span.Span) -- References(*testing.T, span.Span, []span.Span) -- Rename(*testing.T, span.Span, string) -- PrepareRename(*testing.T, span.Span, *source.PrepareItem) -- Symbols(*testing.T, span.URI, []protocol.DocumentSymbol) -- WorkspaceSymbols(*testing.T, span.URI, string, WorkspaceSymbolsTestType) -- SignatureHelp(*testing.T, span.Span, *protocol.SignatureHelp) -- Link(*testing.T, span.URI, []Link) -- AddImport(*testing.T, span.URI, string) -- SelectionRanges(*testing.T, span.Span) +-// 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.AfterChange(Diagnostics(env.AtRegexp("a_test.go", `f\((3)\)`))) +- env.RemoveWorkspaceFile("a_test.go") +- env.AfterChange(NoDiagnostics(ForFile("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.AfterChange(Diagnostics(env.AtRegexp("a.go", `"fmt"`))) +- }) -} - --type Definition struct { -- Name string -- IsType bool -- OnlyHover bool -- Src, Def span.Span +-// 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.AfterChange(Diagnostics(AtPosition("a_test.go", 5, 3))) +- env.Sandbox.Workdir.RemoveFile(context.Background(), "a_test.go") +- env.AfterChange(Diagnostics(AtPosition("a_test.go", 5, 3))) +- }) -} - --type CompletionTestType int +-// 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 - --const ( -- // Default runs the standard completion tests. -- CompletionDefault = CompletionTestType(iota) +-import "mod.com/bob" - -- // Unimported tests the autocompletion of unimported packages. -- CompletionUnimported +-func main() { +- bob.Hello() +-} +--- bob/bob.go -- +-package bob - -- // Deep tests deep completion. -- CompletionDeep +-func Hello() { +- var x int +-} +-` - -- // Fuzzy tests deep completion and fuzzy matching. -- CompletionFuzzy +- t.Run("manual", func(t *testing.T) { +- Run(t, noMod, func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics(env.AtRegexp("main.go", `"mod.com/bob"`)), +- ) +- env.CreateBuffer("go.mod", `module mod.com - -- // CaseSensitive tests case sensitive completion. -- CompletionCaseSensitive +- go 1.12 +-`) +- env.SaveBuffer("go.mod") +- var d protocol.PublishDiagnosticsParams +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- Diagnostics(env.AtRegexp("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.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics(env.AtRegexp("main.go", `"mod.com/bob"`)), +- ) +- env.RunGoCommand("mod", "init", "mod.com") +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- Diagnostics(env.AtRegexp("bob/bob.go", "x")), +- ) +- }) +- }) - -- // CompletionRank candidates in test must be valid and in the right relative order. -- CompletionRank --) +- t.Run("without workspace module", func(t *testing.T) { +- WithOptions( +- Modes(Default), +- ).Run(t, noMod, func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics(env.AtRegexp("main.go", `"mod.com/bob"`)), +- ) +- if err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}, nil, true); err != nil { +- t.Fatal(err) +- } +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- Diagnostics(env.AtRegexp("bob/bob.go", "x")), +- ) +- }) +- }) +-} - --type WorkspaceSymbolsTestType int +-// Tests golang/go#38267. +-func TestIssue38267(t *testing.T) { +- const testPackage = ` +--- go.mod -- +-module mod.com - --const ( -- // Default runs the standard workspace symbols tests. -- WorkspaceSymbolsDefault = WorkspaceSymbolsTestType(iota) +-go 1.12 +--- lib.go -- +-package lib - -- // Fuzzy tests workspace symbols with fuzzy matching. -- WorkspaceSymbolsFuzzy +-func Hello(x string) { +- _ = x +-} +--- lib_test.go -- +-package lib - -- // CaseSensitive tests workspace symbols with case sensitive. -- WorkspaceSymbolsCaseSensitive --) +-import "testing" - --type Completion struct { -- CompletionItems []token.Pos +-type testStruct struct{ +- name string -} - --type CompletionSnippet struct { -- CompletionItem token.Pos -- PlainSnippet string -- PlaceholderSnippet string +-func TestHello(t *testing.T) { +- testStructs := []*testStruct{ +- &testStruct{"hello"}, +- &testStruct{"goodbye"}, +- } +- for y := range testStructs { +- _ = y +- } -} +-` - --type CallHierarchyResult struct { -- IncomingCalls, OutgoingCalls []protocol.CallHierarchyItem +- Run(t, testPackage, func(t *testing.T, env *Env) { +- env.OpenFile("lib_test.go") +- env.AfterChange( +- Diagnostics(AtPosition("lib_test.go", 10, 2)), +- Diagnostics(AtPosition("lib_test.go", 11, 2)), +- ) +- env.OpenFile("lib.go") +- env.RegexpReplace("lib.go", "_ = x", "var y int") +- env.AfterChange( +- Diagnostics(env.AtRegexp("lib.go", "y int")), +- NoDiagnostics(ForFile("lib_test.go")), +- ) +- }) -} - --type Link struct { -- Src span.Span -- Target string -- NotePosition token.Position --} +-// Tests golang/go#38328. +-func TestPackageChange_Issue38328(t *testing.T) { +- const packageChange = ` +--- go.mod -- +-module fake - --type SuggestedFix struct { -- ActionKind, Title string +-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.AfterChange( +- NoDiagnostics(ForFile("a.go")), +- ) +- }) -} - --// A symbol holds a DocumentSymbol along with its parent-child edge. --type symbol struct { -- pSymbol protocol.DocumentSymbol -- id, parentID string --} +-const testPackageWithRequire = ` +--- go.mod -- +-module mod.com - --type Golden struct { -- Filename string -- Archive *txtar.Archive -- Modified bool --} +-go 1.12 - --func Context(t testing.TB) context.Context { -- return context.Background() --} +-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 - --func DefaultOptions(o *source.Options) { -- o.SupportedCodeActions = map[source.FileKind]map[protocol.CodeActionKind]bool{ -- source.Go: { -- protocol.SourceOrganizeImports: true, -- protocol.QuickFix: true, -- protocol.RefactorRewrite: true, -- protocol.RefactorExtract: true, -- protocol.SourceFixAll: true, -- }, -- source.Mod: { -- protocol.SourceOrganizeImports: true, -- }, -- source.Sum: {}, -- source.Work: {}, -- source.Tmpl: {}, -- } -- o.UserOptions.Codelenses[string(command.Test)] = true -- o.HoverKind = source.SynopsisDocumentation -- o.InsertTextFormat = protocol.SnippetTextFormat -- o.CompletionBudget = time.Minute -- o.HierarchicalDocumentSymbolSupport = true -- o.SemanticTokens = true -- o.InternalOptions.NewDiff = "both" +-import ( +- "fmt" +- +- "foo.test/bar" +-) +- +-func PrintAnswer() { +- fmt.Printf("answer: %s", bar.Answer) -} +-` - --func RunTests(t *testing.T, dataDir string, includeMultiModule bool, f func(*testing.T, *Data)) { -- t.Helper() -- modes := []string{"Modules", "GOPATH"} -- if includeMultiModule { -- modes = append(modes, "MultiModule") -- } -- for _, mode := range modes { -- t.Run(mode, func(t *testing.T) { -- datum := load(t, mode, dataDir) -- t.Helper() -- f(t, datum) -- }) -- } +-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. +- env.AfterChange( +- Diagnostics( +- env.AtRegexp("print.go", "fmt.Printf"), +- WithMessage("wrong type int"), +- ), +- ) +- }) -} - --func load(t testing.TB, mode string, dir string) *Data { -- datum := &Data{ -- CallHierarchy: make(CallHierarchy), -- CodeLens: make(CodeLens), -- Diagnostics: make(Diagnostics), -- CompletionItems: make(CompletionItems), -- Completions: make(Completions), -- CompletionSnippets: make(CompletionSnippets), -- UnimportedCompletions: make(UnimportedCompletions), -- DeepCompletions: make(DeepCompletions), -- FuzzyCompletions: make(FuzzyCompletions), -- RankCompletions: make(RankCompletions), -- CaseSensitiveCompletions: make(CaseSensitiveCompletions), -- Definitions: make(Definitions), -- Implementations: make(Implementations), -- Highlights: make(Highlights), -- References: make(References), -- Renames: make(Renames), -- PrepareRenames: make(PrepareRenames), -- SuggestedFixes: make(SuggestedFixes), -- FunctionExtractions: make(FunctionExtractions), -- MethodExtractions: make(MethodExtractions), -- Symbols: make(Symbols), -- WorkspaceSymbols: make(WorkspaceSymbols), -- Signatures: make(Signatures), -- Links: make(Links), -- AddImport: make(AddImport), +-func TestMissingDependency(t *testing.T) { +- Run(t, testPackageWithRequire, func(t *testing.T, env *Env) { +- env.OpenFile("print.go") +- env.Await( +- // Log messages are asynchronous to other events on the LSP stream, so we +- // can't use OnceMet or AfterChange here. +- LogMatching(protocol.Error, "initial workspace load failed", 1, false), +- ) +- }) +-} - -- dir: dir, -- fragments: map[string]string{}, -- golden: map[string]*Golden{}, -- mode: mode, -- mappers: map[span.URI]*protocol.Mapper{}, -- } +-// Tests golang/go#36951. +-func TestAdHocPackages_Issue36951(t *testing.T) { +- const adHoc = ` +--- b/b.go -- +-package b - -- if !*UpdateGolden { -- summary := filepath.Join(filepath.FromSlash(dir), summaryFile+goldenFileSuffix) -- if _, err := os.Stat(summary); os.IsNotExist(err) { -- t.Fatalf("could not find golden file summary.txt in %#v", dir) -- } -- archive, err := txtar.ParseFile(summary) -- if err != nil { -- t.Fatalf("could not read golden file %v/%v: %v", dir, summary, err) -- } -- datum.golden[summaryFile] = &Golden{ -- Filename: summary, -- Archive: archive, -- } -- } +-func Hello() { +- var x int +-} +-` +- Run(t, adHoc, func(t *testing.T, env *Env) { +- env.OpenFile("b/b.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("b/b.go", "x")), +- ) +- }) +-} - -- files := packagestest.MustCopyFileTree(dir) -- // Prune test cases that exercise generics. -- if !typeparams.Enabled { -- for name := range files { -- if strings.Contains(name, "_generics") { -- delete(files, name) -- } -- } -- } -- overlays := map[string][]byte{} -- for fragment, operation := range files { -- if trimmed := strings.TrimSuffix(fragment, goldenFileSuffix); trimmed != fragment { -- delete(files, fragment) -- goldFile := filepath.Join(dir, fragment) -- archive, err := txtar.ParseFile(goldFile) -- if err != nil { -- t.Fatalf("could not read golden file %v: %v", fragment, err) -- } -- datum.golden[trimmed] = &Golden{ -- Filename: goldFile, -- Archive: archive, -- } -- } else if trimmed := strings.TrimSuffix(fragment, inFileSuffix); trimmed != fragment { -- delete(files, fragment) -- files[trimmed] = operation -- } else if index := strings.Index(fragment, overlayFileSuffix); index >= 0 { -- delete(files, fragment) -- partial := fragment[:index] + fragment[index+len(overlayFileSuffix):] -- contents, err := ioutil.ReadFile(filepath.Join(dir, fragment)) -- if err != nil { -- t.Fatal(err) -- } -- overlays[partial] = contents -- } -- } +-// Tests golang/go#37984: GOPATH should be read from the go command. +-func TestNoGOPATH_Issue37984(t *testing.T) { +- const files = ` +--- main.go -- +-package main - -- modules := []packagestest.Module{ -- { -- Name: testModule, -- Files: files, -- Overlay: overlays, +-func _() { +- fmt.Println("Hello World") +-} +-` +- WithOptions( +- EnvVars{ +- "GOPATH": "", +- "GO111MODULE": "off", - }, -- } -- switch mode { -- case "Modules": -- datum.Exported = packagestest.Export(t, packagestest.Modules, modules) -- case "GOPATH": -- datum.Exported = packagestest.Export(t, packagestest.GOPATH, modules) -- case "MultiModule": -- files := map[string]interface{}{} -- for k, v := range modules[0].Files { -- files[filepath.Join("testmodule", k)] = v -- } -- modules[0].Files = files -- -- overlays := map[string][]byte{} -- for k, v := range modules[0].Overlay { -- overlays[filepath.Join("testmodule", k)] = v -- } -- modules[0].Overlay = overlays +- ).Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- env.AfterChange(Diagnostics(env.AtRegexp("main.go", "fmt"))) +- env.SaveBuffer("main.go") +- env.AfterChange(NoDiagnostics(ForFile("main.go"))) +- }) +-} - -- golden := map[string]*Golden{} -- for k, v := range datum.golden { -- if k == summaryFile { -- golden[k] = v -- } else { -- golden[filepath.Join("testmodule", k)] = v -- } -- } -- datum.golden = golden +-// Tests golang/go#38669. +-func TestEqualInEnv_Issue38669(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - -- datum.Exported = packagestest.Export(t, packagestest.Modules, modules) -- default: -- panic("unknown mode " + mode) -- } +-go 1.12 +--- main.go -- +-package main - -- for _, m := range modules { -- for fragment := range m.Files { -- filename := datum.Exported.File(m.Name, fragment) -- datum.fragments[filename] = fragment -- } -- } +-var _ = x.X +--- x/x.go -- +-package x - -- // Turn off go/packages debug logging. -- datum.Exported.Config.Logf = nil -- datum.Config.Logf = nil +-var X = 0 +-` +- WithOptions( +- EnvVars{"GOFLAGS": "-tags=foo"}, +- ).Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- env.OrganizeImports("main.go") +- env.AfterChange(NoDiagnostics(ForFile("main.go"))) +- }) +-} - -- // Merge the exported.Config with the view.Config. -- datum.Config = *datum.Exported.Config -- datum.Config.Fset = token.NewFileSet() -- datum.Config.Context = Context(nil) -- datum.Config.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) { -- panic("ParseFile should not be called") -- } +-// Tests golang/go#38467. +-func TestNoSuggestedFixesForGeneratedFiles_Issue38467(t *testing.T) { +- const generated = ` +--- go.mod -- +-module mod.com - -- // Do a first pass to collect special markers for completion and workspace symbols. -- if err := datum.Exported.Expect(map[string]interface{}{ -- "item": func(name string, r packagestest.Range, _ []string) { -- datum.Exported.Mark(name, r) -- }, -- "symbol": func(name string, r packagestest.Range, _ []string) { -- datum.Exported.Mark(name, r) -- }, -- }); err != nil { -- t.Fatal(err) -- } +-go 1.12 +--- main.go -- +-package main - -- // Collect any data that needs to be used by subsequent tests. -- if err := datum.Exported.Expect(map[string]interface{}{ -- "codelens": datum.collectCodeLens, -- "diag": datum.collectDiagnostics, -- "item": datum.collectCompletionItems, -- "complete": datum.collectCompletions(CompletionDefault), -- "unimported": datum.collectCompletions(CompletionUnimported), -- "deep": datum.collectCompletions(CompletionDeep), -- "fuzzy": datum.collectCompletions(CompletionFuzzy), -- "casesensitive": datum.collectCompletions(CompletionCaseSensitive), -- "rank": datum.collectCompletions(CompletionRank), -- "snippet": datum.collectCompletionSnippets, -- "fold": datum.collectFoldingRanges, -- "format": datum.collectFormats, -- "import": datum.collectImports, -- "semantic": datum.collectSemanticTokens, -- "godef": datum.collectDefinitions, -- "implementations": datum.collectImplementations, -- "typdef": datum.collectTypeDefinitions, -- "hoverdef": datum.collectHoverDefinitions, -- "highlight": datum.collectHighlights, -- "inlayHint": datum.collectInlayHints, -- "refs": datum.collectReferences, -- "rename": datum.collectRenames, -- "prepare": datum.collectPrepareRenames, -- "symbol": datum.collectSymbols, -- "signature": datum.collectSignatures, -- "link": datum.collectLinks, -- "suggestedfix": datum.collectSuggestedFixes, -- "extractfunc": datum.collectFunctionExtractions, -- "extractmethod": datum.collectMethodExtractions, -- "incomingcalls": datum.collectIncomingCalls, -- "outgoingcalls": datum.collectOutgoingCalls, -- "addimport": datum.collectAddImports, -- "selectionrange": datum.collectSelectionRanges, -- }); err != nil { -- t.Fatal(err) -- } +-// Code generated by generator.go. DO NOT EDIT. - -- // Collect names for the entries that require golden files. -- if err := datum.Exported.Expect(map[string]interface{}{ -- "godef": datum.collectDefinitionNames, -- "hoverdef": datum.collectDefinitionNames, -- "workspacesymbol": datum.collectWorkspaceSymbols(WorkspaceSymbolsDefault), -- "workspacesymbolfuzzy": datum.collectWorkspaceSymbols(WorkspaceSymbolsFuzzy), -- "workspacesymbolcasesensitive": datum.collectWorkspaceSymbols(WorkspaceSymbolsCaseSensitive), -- }); err != nil { -- t.Fatal(err) +-func _() { +- for i, _ := range []string{} { +- _ = i - } -- if mode == "MultiModule" { -- if err := moveFile(filepath.Join(datum.Config.Dir, "go.mod"), filepath.Join(datum.Config.Dir, "testmodule/go.mod")); err != nil { -- t.Fatal(err) +-} +-` +- Run(t, generated, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- var d protocol.PublishDiagnosticsParams +- env.AfterChange( +- Diagnostics(AtPosition("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) - } -- } -- -- return datum +- }) -} - --// moveFile moves the file at oldpath to newpath, by renaming if possible --// or copying otherwise. --func moveFile(oldpath, newpath string) (err error) { -- renameErr := os.Rename(oldpath, newpath) -- if renameErr == nil { -- return nil -- } -- -- src, err := os.Open(oldpath) -- if err != nil { -- return err -- } -- defer func() { -- src.Close() -- if err == nil { -- err = os.Remove(oldpath) -- } -- }() +-// Expect a module/GOPATH error if there is an error in the file at startup. +-// Tests golang/go#37279. +-func TestBrokenWorkspace_OutsideModule(t *testing.T) { +- const noModule = ` +--- a.go -- +-package foo - -- perm := os.ModePerm -- fi, err := src.Stat() -- if err == nil { -- perm = fi.Mode().Perm() -- } +-import "mod.com/hello" - -- dst, err := os.OpenFile(newpath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm) -- if err != nil { -- return err -- } +-func f() { +- hello.Goodbye() +-} +-` +- Run(t, noModule, func(t *testing.T, env *Env) { +- env.OpenFile("a.go") +- env.AfterChange( +- // Expect the adHocPackagesWarning. +- OutstandingWork(lsp.WorkspaceLoadFailure, "outside of a module"), +- ) +- // Deleting the import dismisses the warning. +- env.RegexpReplace("a.go", `import "mod.com/hello"`, "") +- env.AfterChange( +- NoOutstandingWork(IgnoreTelemetryPromptWork), +- ) +- }) +-} - -- _, err = io.Copy(dst, src) -- if closeErr := dst.Close(); err == nil { -- err = closeErr +-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( +- EnvVars{"GO111MODULE": go111module}, +- ).Run(t, files, func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- NoOutstandingWork(IgnoreTelemetryPromptWork), +- ) +- }) +- }) - } -- return err -} - --func Run(t *testing.T, tests Tests, data *Data) { -- t.Helper() -- checkData(t, data) +-// 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 - -- eachCompletion := func(t *testing.T, cases map[span.Span][]Completion, test func(*testing.T, span.Span, Completion, CompletionItems)) { -- t.Helper() +-import "x/hello" - -- for src, exp := range cases { -- for i, e := range exp { -- t.Run(SpanName(src)+"_"+strconv.Itoa(i), func(t *testing.T) { -- t.Helper() -- if strings.Contains(t.Name(), "cgo") { -- testenv.NeedsTool(t, "cgo") -- } -- test(t, src, e, data.CompletionItems) -- }) -- } +-func Hello() { +- hello.HiThere() +-} +--- x/main.go -- +-package main - -- } -- } +-func main() { +- fmt.Println("") +-} +-` +- WithOptions( +- InGOPATH(), +- EnvVars{"GO111MODULE": "off"}, +- ).Run(t, collision, func(t *testing.T, env *Env) { +- env.OpenFile("x/x.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("x/x.go", `^`), WithMessage("found packages main (main.go) and x (x.go)")), +- Diagnostics(env.AtRegexp("x/main.go", `^`), WithMessage("found packages main (main.go) and x (x.go)")), +- ) - -- t.Run("CallHierarchy", func(t *testing.T) { -- t.Helper() -- for spn, callHierarchyResult := range data.CallHierarchy { -- t.Run(SpanName(spn), func(t *testing.T) { -- t.Helper() -- tests.CallHierarchy(t, spn, callHierarchyResult) -- }) +- // 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.AfterChange( +- Diagnostics(env.AtRegexp("x/main.go", `fmt`)), +- ) - } - }) +-} - -- t.Run("Completion", func(t *testing.T) { -- t.Helper() -- eachCompletion(t, data.Completions, tests.Completion) -- }) +-const ardanLabsProxy = ` +--- github.com/ardanlabs/conf@v1.2.3/go.mod -- +-module github.com/ardanlabs/conf - -- t.Run("CompletionSnippets", func(t *testing.T) { -- t.Helper() -- for _, placeholders := range []bool{true, false} { -- for src, expecteds := range data.CompletionSnippets { -- for i, expected := range expecteds { -- name := SpanName(src) + "_" + strconv.Itoa(i+1) -- if placeholders { -- name += "_placeholders" -- } +-go 1.12 +--- github.com/ardanlabs/conf@v1.2.3/conf.go -- +-package conf - -- t.Run(name, func(t *testing.T) { -- t.Helper() -- tests.CompletionSnippet(t, src, expected, placeholders, data.CompletionItems) -- }) -- } -- } -- } -- }) +-var ErrHelpWanted error +-` - -- t.Run("UnimportedCompletion", func(t *testing.T) { -- t.Helper() -- eachCompletion(t, data.UnimportedCompletions, tests.UnimportedCompletion) -- }) +-// Test for golang/go#38211. +-func Test_Issue38211(t *testing.T) { +- const ardanLabs = ` +--- go.mod -- +-module mod.com - -- t.Run("DeepCompletion", func(t *testing.T) { -- t.Helper() -- eachCompletion(t, data.DeepCompletions, tests.DeepCompletion) -- }) +-go 1.14 +--- main.go -- +-package main - -- t.Run("FuzzyCompletion", func(t *testing.T) { -- t.Helper() -- eachCompletion(t, data.FuzzyCompletions, tests.FuzzyCompletion) -- }) +-import "github.com/ardanlabs/conf" - -- t.Run("CaseSensitiveCompletion", func(t *testing.T) { -- t.Helper() -- eachCompletion(t, data.CaseSensitiveCompletions, tests.CaseSensitiveCompletion) +-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.AfterChange( +- Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`)), +- ReadDiagnostics("main.go", &d), +- ) +- env.ApplyQuickFixes("main.go", d.Diagnostics) +- env.SaveBuffer("go.mod") +- env.AfterChange( +- NoDiagnostics(ForFile("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.AfterChange( +- Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`)), +- ) +- env.SaveBuffer("main.go") +- // Expect a diagnostic and fix to remove the dependency in the go.mod. +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- Diagnostics(env.AtRegexp("go.mod", "require github.com/ardanlabs/conf"), WithMessage("not used in this module")), +- ReadDiagnostics("go.mod", &d), +- ) +- env.ApplyQuickFixes("go.mod", d.Diagnostics) +- env.SaveBuffer("go.mod") +- env.AfterChange( +- NoDiagnostics(ForFile("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.AfterChange( +- Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`)), +- ) - }) +-} - -- t.Run("RankCompletions", func(t *testing.T) { -- t.Helper() -- eachCompletion(t, data.RankCompletions, tests.RankCompletion) -- }) +-// Test for golang/go#38207. +-func TestNewModule_Issue38207(t *testing.T) { +- const emptyFile = ` +--- go.mod -- +-module mod.com - -- t.Run("CodeLens", func(t *testing.T) { -- t.Helper() -- for uri, want := range data.CodeLens { -- // Check if we should skip this URI if the -modfile flag is not available. -- if shouldSkip(data, uri) { -- continue -- } -- t.Run(uriName(uri), func(t *testing.T) { -- t.Helper() -- tests.CodeLens(t, uri, want) -- }) -- } -- }) +-go 1.12 +--- main.go -- +-` +- WithOptions( +- ProxyFiles(ardanLabsProxy), +- ).Run(t, emptyFile, func(t *testing.T, env *Env) { +- env.CreateBuffer("main.go", `package main - -- t.Run("Diagnostics", func(t *testing.T) { -- t.Helper() -- for uri, want := range data.Diagnostics { -- // Check if we should skip this URI if the -modfile flag is not available. -- if shouldSkip(data, uri) { -- continue -- } -- t.Run(uriName(uri), func(t *testing.T) { -- t.Helper() -- tests.Diagnostics(t, uri, want) -- }) -- } -- }) +-import "github.com/ardanlabs/conf" - -- t.Run("FoldingRange", func(t *testing.T) { -- t.Helper() -- for _, spn := range data.FoldingRanges { -- t.Run(uriName(spn.URI()), func(t *testing.T) { -- t.Helper() -- tests.FoldingRanges(t, spn) -- }) -- } +-func main() { +- _ = conf.ErrHelpWanted +-} +-`) +- env.SaveBuffer("main.go") +- var d protocol.PublishDiagnosticsParams +- env.AfterChange( +- Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`), WithMessage("no required module")), +- ReadDiagnostics("main.go", &d), +- ) +- env.ApplyQuickFixes("main.go", d.Diagnostics) +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- ) - }) +-} - -- t.Run("Format", func(t *testing.T) { -- t.Helper() -- for _, spn := range data.Formats { -- t.Run(uriName(spn.URI()), func(t *testing.T) { -- t.Helper() -- tests.Format(t, spn) -- }) -- } -- }) +-// Test for golang/go#36960. +-func TestNewFileBadImports_Issue36960(t *testing.T) { +- const simplePackage = ` +--- go.mod -- +-module mod.com - -- t.Run("Import", func(t *testing.T) { -- t.Helper() -- for _, spn := range data.Imports { -- t.Run(uriName(spn.URI()), func(t *testing.T) { -- t.Helper() -- tests.Import(t, spn) -- }) -- } -- }) +-go 1.14 +--- a/a1.go -- +-package a - -- t.Run("SemanticTokens", func(t *testing.T) { -- t.Helper() -- for _, spn := range data.SemanticTokens { -- t.Run(uriName(spn.URI()), func(t *testing.T) { -- t.Helper() -- tests.SemanticTokens(t, spn) -- }) -- } -- }) +-import "fmt" - -- t.Run("SuggestedFix", func(t *testing.T) { -- t.Helper() -- for spn, actionKinds := range data.SuggestedFixes { -- // Check if we should skip this spn if the -modfile flag is not available. -- if shouldSkip(data, spn.URI()) { -- continue -- } -- t.Run(SpanName(spn), func(t *testing.T) { -- t.Helper() -- tests.SuggestedFix(t, spn, actionKinds, 1) -- }) -- } +-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.AfterChange( +- NoDiagnostics(ForFile("a/a1.go")), +- ) +- env.EditBuffer("a/a2.go", fake.NewEdit(0, 0, 0, 0, `package a`)) +- env.AfterChange( +- NoDiagnostics(ForFile("a/a1.go")), +- ) - }) +-} - -- t.Run("FunctionExtraction", func(t *testing.T) { -- t.Helper() -- for start, end := range data.FunctionExtractions { -- // Check if we should skip this spn if the -modfile flag is not available. -- if shouldSkip(data, start.URI()) { -- continue -- } -- t.Run(SpanName(start), func(t *testing.T) { -- t.Helper() -- tests.FunctionExtraction(t, start, end) -- }) -- } -- }) +-// 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) { +- // Create a package that already has a test variant (in-package test). +- const testVariant = ` +--- go.mod -- +-module mod.com - -- t.Run("MethodExtraction", func(t *testing.T) { -- t.Helper() -- for start, end := range data.MethodExtractions { -- // Check if we should skip this spn if the -modfile flag is not available. -- if shouldSkip(data, start.URI()) { -- continue -- } -- t.Run(SpanName(start), func(t *testing.T) { -- t.Helper() -- tests.MethodExtraction(t, start, end) -- }) -- } +-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.AfterChange( +- Diagnostics(env.AtRegexp("hello/hello.go", "x")), +- Diagnostics(env.AtRegexp("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.AfterChange( +- Diagnostics(env.AtRegexp("hello/hello_x_test.go", "hello.Hello")), +- ) +- env.SaveBuffer("hello/hello_x_test.go") +- env.AfterChange( +- NoDiagnostics(ForFile("hello/hello_x_test.go")), +- ) - }) +-} - -- t.Run("Definition", func(t *testing.T) { -- t.Helper() -- for spn, d := range data.Definitions { -- t.Run(SpanName(spn), func(t *testing.T) { -- t.Helper() -- if strings.Contains(t.Name(), "cgo") { -- testenv.NeedsTool(t, "cgo") -- } -- tests.Definition(t, spn, d) -- }) -- } -- }) +-// Reproduce golang/go#40690. +-func TestCreateOnlyXTest(t *testing.T) { +- const mod = ` +--- go.mod -- +-module mod.com - -- t.Run("Implementation", func(t *testing.T) { -- t.Helper() -- for spn, m := range data.Implementations { -- t.Run(SpanName(spn), func(t *testing.T) { -- t.Helper() -- tests.Implementation(t, spn, m) -- }) -- } -- }) +-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 - -- t.Run("Highlight", func(t *testing.T) { -- t.Helper() -- for pos, locations := range data.Highlights { -- t.Run(SpanName(pos), func(t *testing.T) { -- t.Helper() -- tests.Highlight(t, pos, locations) -- }) -- } -- }) +-import "testing" - -- t.Run("InlayHints", func(t *testing.T) { -- t.Helper() -- for _, src := range data.InlayHints { -- t.Run(SpanName(src), func(t *testing.T) { -- t.Helper() -- tests.InlayHints(t, src) -- }) -- } +-func TestX(t *testing.T) { +- var x int +-} +-`) +- env.AfterChange( +- Diagnostics(env.AtRegexp("foo/bar_test.go", "x")), +- ) - }) +-} - -- t.Run("References", func(t *testing.T) { -- t.Helper() -- for src, itemList := range data.References { -- t.Run(SpanName(src), func(t *testing.T) { -- t.Helper() -- tests.References(t, src, itemList) -- }) -- } -- }) +-func TestChangePackageName(t *testing.T) { +- const mod = ` +--- go.mod -- +-module mod.com - -- t.Run("Renames", func(t *testing.T) { -- t.Helper() -- for spn, newText := range data.Renames { -- t.Run(uriName(spn.URI())+"_"+newText, func(t *testing.T) { -- t.Helper() -- tests.Rename(t, spn, newText) -- }) -- } +-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.AfterChange() +- env.RegexpReplace("foo/bar_test.go", "package foo_", "package foo_test") +- env.AfterChange( +- NoDiagnostics(ForFile("foo/bar_test.go")), +- NoDiagnostics(ForFile("foo/foo.go")), +- ) - }) +-} - -- t.Run("PrepareRenames", func(t *testing.T) { -- t.Helper() -- for src, want := range data.PrepareRenames { -- t.Run(SpanName(src), func(t *testing.T) { -- t.Helper() -- tests.PrepareRename(t, src, want) -- }) -- } -- }) +-func TestIgnoredFiles(t *testing.T) { +- const ws = ` +--- go.mod -- +-module mod.com - -- t.Run("Symbols", func(t *testing.T) { -- t.Helper() -- for uri, allSymbols := range data.Symbols { -- byParent := make(map[string][]*symbol) -- for _, sym := range allSymbols { -- if sym.parentID != "" { -- byParent[sym.parentID] = append(byParent[sym.parentID], sym) -- } -- } +-go 1.12 +--- _foo/x.go -- +-package x - -- // collectChildren does a depth-first traversal of the symbol tree, -- // computing children of child nodes before returning to their parent. -- // This is necessary as the Children field is slice of non-pointer types, -- // and therefore we need to be careful to mutate children first before -- // assigning them to their parent. -- var collectChildren func(id string) []protocol.DocumentSymbol -- collectChildren = func(id string) []protocol.DocumentSymbol { -- children := byParent[id] -- // delete from byParent before recursing, to ensure that -- // collectChildren terminates even in the presence of cycles. -- delete(byParent, id) -- var result []protocol.DocumentSymbol -- for _, child := range children { -- child.pSymbol.Children = collectChildren(child.id) -- result = append(result, child.pSymbol) -- } -- return result -- } +-var _ = foo.Bar +-` +- Run(t, ws, func(t *testing.T, env *Env) { +- env.OpenFile("_foo/x.go") +- env.AfterChange( +- NoDiagnostics(ForFile("_foo/x.go")), +- ) +- }) +-} - -- var topLevel []protocol.DocumentSymbol -- for _, sym := range allSymbols { -- if sym.parentID == "" { -- sym.pSymbol.Children = collectChildren(sym.id) -- topLevel = append(topLevel, sym.pSymbol) -- } -- } +-// 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 - -- t.Run(uriName(uri), func(t *testing.T) { -- t.Helper() -- tests.Symbols(t, uri, topLevel) -- }) -- } -- }) +-go 1.15 +--- a/a.go -- +-package a - -- t.Run("WorkspaceSymbols", func(t *testing.T) { -- t.Helper() +-const A = 1 - -- for _, typ := range []WorkspaceSymbolsTestType{ -- WorkspaceSymbolsDefault, -- WorkspaceSymbolsCaseSensitive, -- WorkspaceSymbolsFuzzy, -- } { -- for uri, cases := range data.WorkspaceSymbols[typ] { -- for _, query := range cases { -- name := query -- if name == "" { -- name = "EmptyQuery" -- } -- t.Run(name, func(t *testing.T) { -- t.Helper() -- tests.WorkspaceSymbols(t, uri, query, typ) -- }) -- } -- } -- } +--- b/b.go -- +-package b - -- }) +-import "mod.com/a" - -- t.Run("SignatureHelp", func(t *testing.T) { -- t.Helper() -- for spn, expectedSignature := range data.Signatures { -- t.Run(SpanName(spn), func(t *testing.T) { -- t.Helper() -- tests.SignatureHelp(t, spn, expectedSignature) -- }) -- } -- }) +-const B = a.A - -- t.Run("Link", func(t *testing.T) { -- t.Helper() -- for uri, wantLinks := range data.Links { -- // If we are testing GOPATH, then we do not want links with the versions -- // attached (pkg.go.dev/repoa/moda@v1.1.0/pkg), unless the file is a -- // go.mod, then we can skip it altogether. -- if data.Exported.Exporter == packagestest.GOPATH { -- if strings.HasSuffix(uri.Filename(), ".mod") { -- continue -- } -- re := regexp.MustCompile(`@v\d+\.\d+\.[\w-]+`) -- for i, link := range wantLinks { -- wantLinks[i].Target = re.ReplaceAllString(link.Target, "") -- } -- } -- t.Run(uriName(uri), func(t *testing.T) { -- t.Helper() -- tests.Link(t, uri, wantLinks) -- }) -- } -- }) +--- c/c.go -- +-package c - -- t.Run("AddImport", func(t *testing.T) { -- t.Helper() -- for uri, exp := range data.AddImport { -- t.Run(uriName(uri), func(t *testing.T) { -- tests.AddImport(t, uri, exp) -- }) -- } -- }) +-import "mod.com/a" - -- t.Run("SelectionRanges", func(t *testing.T) { -- t.Helper() -- for _, span := range data.SelectionRanges { -- t.Run(SpanName(span), func(t *testing.T) { -- tests.SelectionRanges(t, span) -- }) -- } -- }) +-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") - -- if *UpdateGolden { -- for _, golden := range data.golden { -- if !golden.Modified { -- continue -- } -- sort.Slice(golden.Archive.Files, func(i, j int) bool { -- return golden.Archive.Files[i].Name < golden.Archive.Files[j].Name -- }) -- if err := ioutil.WriteFile(golden.Filename, txtar.Format(golden.Archive), 0666); err != nil { -- t.Fatal(err) -- } -- } -- } +- // We should still get diagnostics for files that exist. +- env.RegexpReplace("b/b.go", `a.A`, "a.Nonexistant") +- env.AfterChange( +- Diagnostics(env.AtRegexp("b/b.go", `Nonexistant`)), +- ) +- }) -} - --func checkData(t *testing.T, data *Data) { -- buf := &bytes.Buffer{} -- diagnosticsCount := 0 -- for _, want := range data.Diagnostics { -- diagnosticsCount += len(want) -- } -- linksCount := 0 -- for _, want := range data.Links { -- linksCount += len(want) -- } -- definitionCount := 0 -- typeDefinitionCount := 0 -- for _, d := range data.Definitions { -- if d.IsType { -- typeDefinitionCount++ -- } else { -- definitionCount++ -- } -- } +-// This is a copy of the scenario_default/quickfix_empty_files.txt test from +-// govim. Reproduces golang/go#39646. +-func TestQuickFixEmptyFiles(t *testing.T) { +- const mod = ` +--- go.mod -- +-module mod.com - -- snippetCount := 0 -- for _, want := range data.CompletionSnippets { -- snippetCount += len(want) -- } +-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()) - -- countCompletions := func(c map[span.Span][]Completion) (count int) { -- for _, want := range c { -- count += len(want) -- } -- return count -- } +- env.CreateBuffer(name, "\n") +- env.Await(env.DoneWithOpen()) - -- countCodeLens := func(c map[span.URI][]protocol.CodeLens) (count int) { -- for _, want := range c { -- count += len(want) -- } -- return count -- } +- env.EditBuffer(name, fake.NewEdit(1, 0, 1, 0, content)) +- env.Await(env.DoneWithChange()) - -- countWorkspaceSymbols := func(c map[WorkspaceSymbolsTestType]map[span.URI][]string) (count int) { -- for _, typs := range c { -- for _, queries := range typs { -- count += len(queries) -- } -- } -- return count +- env.EditBuffer(name, fake.NewEdit(0, 0, 1, 0, "")) +- env.Await(env.DoneWithChange()) - } - -- fmt.Fprintf(buf, "CallHierarchyCount = %v\n", len(data.CallHierarchy)) -- fmt.Fprintf(buf, "CodeLensCount = %v\n", countCodeLens(data.CodeLens)) -- fmt.Fprintf(buf, "CompletionsCount = %v\n", countCompletions(data.Completions)) -- fmt.Fprintf(buf, "CompletionSnippetCount = %v\n", snippetCount) -- fmt.Fprintf(buf, "UnimportedCompletionsCount = %v\n", countCompletions(data.UnimportedCompletions)) -- fmt.Fprintf(buf, "DeepCompletionsCount = %v\n", countCompletions(data.DeepCompletions)) -- fmt.Fprintf(buf, "FuzzyCompletionsCount = %v\n", countCompletions(data.FuzzyCompletions)) -- fmt.Fprintf(buf, "RankedCompletionsCount = %v\n", countCompletions(data.RankCompletions)) -- fmt.Fprintf(buf, "CaseSensitiveCompletionsCount = %v\n", countCompletions(data.CaseSensitiveCompletions)) -- fmt.Fprintf(buf, "DiagnosticsCount = %v\n", diagnosticsCount) -- fmt.Fprintf(buf, "FoldingRangesCount = %v\n", len(data.FoldingRanges)) -- fmt.Fprintf(buf, "FormatCount = %v\n", len(data.Formats)) -- fmt.Fprintf(buf, "ImportCount = %v\n", len(data.Imports)) -- fmt.Fprintf(buf, "SemanticTokenCount = %v\n", len(data.SemanticTokens)) -- fmt.Fprintf(buf, "SuggestedFixCount = %v\n", len(data.SuggestedFixes)) -- fmt.Fprintf(buf, "FunctionExtractionCount = %v\n", len(data.FunctionExtractions)) -- fmt.Fprintf(buf, "MethodExtractionCount = %v\n", len(data.MethodExtractions)) -- fmt.Fprintf(buf, "DefinitionsCount = %v\n", definitionCount) -- fmt.Fprintf(buf, "TypeDefinitionsCount = %v\n", typeDefinitionCount) -- fmt.Fprintf(buf, "HighlightsCount = %v\n", len(data.Highlights)) -- fmt.Fprintf(buf, "InlayHintsCount = %v\n", len(data.InlayHints)) -- fmt.Fprintf(buf, "ReferencesCount = %v\n", len(data.References)) -- fmt.Fprintf(buf, "RenamesCount = %v\n", len(data.Renames)) -- fmt.Fprintf(buf, "PrepareRenamesCount = %v\n", len(data.PrepareRenames)) -- fmt.Fprintf(buf, "SymbolsCount = %v\n", len(data.Symbols)) -- fmt.Fprintf(buf, "WorkspaceSymbolsCount = %v\n", countWorkspaceSymbols(data.WorkspaceSymbols)) -- fmt.Fprintf(buf, "SignaturesCount = %v\n", len(data.Signatures)) -- fmt.Fprintf(buf, "LinksCount = %v\n", linksCount) -- fmt.Fprintf(buf, "ImplementationsCount = %v\n", len(data.Implementations)) -- fmt.Fprintf(buf, "SelectionRangesCount = %v\n", len(data.SelectionRanges)) +- const p = `package p; func DoIt(s string) {};` +- const main = `package main - -- want := string(data.Golden(t, "summary", summaryFile, func() ([]byte, error) { -- return buf.Bytes(), nil -- })) -- got := buf.String() -- if want != got { -- // These counters change when assertions are added or removed. -- // They act as an independent safety net to ensure that the -- // tests didn't spuriously pass because they did no work. -- t.Errorf("test summary does not match:\n%s\n(Run with -golden to update golden file; also, there may be one per Go version.)", compare.Text(want, got)) -- } +-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.AfterChange( +- Diagnostics(env.AtRegexp("main.go", "5")), +- ) +- }) +- }) - --func (data *Data) Mapper(uri span.URI) (*protocol.Mapper, error) { -- data.mappersMu.Lock() -- defer data.mappersMu.Unlock() +- // 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 - -- if _, ok := data.mappers[uri]; !ok { -- content, err := data.Exported.FileContents(uri.Filename()) -- if err != nil { -- return nil, err -- } -- data.mappers[uri] = protocol.NewMapper(uri, content) -- } -- return data.mappers[uri], nil +-import "testing" +- +-func TestDoIt(t *testing.T) { +- DoIt(5) -} +-`) +- writeGoVim(env, "p/x_test.go", `package p_test - --func (data *Data) Golden(t *testing.T, tag, target string, update func() ([]byte, error)) []byte { -- t.Helper() -- fragment, found := data.fragments[target] -- if !found { -- if filepath.IsAbs(target) { -- t.Fatalf("invalid golden file fragment %v", target) -- } -- fragment = target -- } -- golden := data.golden[fragment] -- if golden == nil { -- if !*UpdateGolden { -- t.Fatalf("could not find golden file %v: %v", fragment, tag) -- } -- golden = &Golden{ -- Filename: filepath.Join(data.dir, fragment+goldenFileSuffix), -- Archive: &txtar.Archive{}, -- Modified: true, -- } -- data.golden[fragment] = golden -- } -- var file *txtar.File -- for i := range golden.Archive.Files { -- f := &golden.Archive.Files[i] -- if f.Name == tag { -- file = f -- break -- } -- } -- if *UpdateGolden { -- if file == nil { -- golden.Archive.Files = append(golden.Archive.Files, txtar.File{ -- Name: tag, -- }) -- file = &golden.Archive.Files[len(golden.Archive.Files)-1] -- } -- contents, err := update() -- if err != nil { -- t.Fatalf("could not update golden file %v: %v", fragment, err) -- } -- file.Data = append(contents, '\n') // add trailing \n for txtar -- golden.Modified = true +-import ( +- "testing" - -- } -- if file == nil { -- t.Fatalf("could not find golden contents %v: %v", fragment, tag) -- } -- if len(file.Data) == 0 { -- return file.Data -- } -- return file.Data[:len(file.Data)-1] // drop the trailing \n --} +- "mod.com/p" +-) - --func (data *Data) collectCodeLens(spn span.Span, title, cmd string) { -- data.CodeLens[spn.URI()] = append(data.CodeLens[spn.URI()], protocol.CodeLens{ -- Range: data.mustRange(spn), -- Command: &protocol.Command{ -- Title: title, -- Command: cmd, -- }, +-func TestDoIt(t *testing.T) { +- p.DoIt(5) +-} +-`) +- env.AfterChange( +- Diagnostics(env.AtRegexp("main.go", "5")), +- Diagnostics(env.AtRegexp("p/p_test.go", "5")), +- Diagnostics(env.AtRegexp("p/x_test.go", "5")), +- ) +- env.RegexpReplace("p/p.go", "s string", "i int") +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- NoDiagnostics(ForFile("p/p_test.go")), +- NoDiagnostics(ForFile("p/x_test.go")), +- ) +- }) - }) -} - --func (data *Data) collectDiagnostics(spn span.Span, msgSource, msgPattern, msgSeverity string) { -- severity := protocol.SeverityError -- switch msgSeverity { -- case "error": -- severity = protocol.SeverityError -- case "warning": -- severity = protocol.SeverityWarning -- case "hint": -- severity = protocol.SeverityHint -- case "information": -- severity = protocol.SeverityInformation -- } +-func TestSingleFile(t *testing.T) { +- const mod = ` +--- go.mod -- +-module mod.com - -- data.Diagnostics[spn.URI()] = append(data.Diagnostics[spn.URI()], &source.Diagnostic{ -- Range: data.mustRange(spn), -- Severity: severity, -- Source: source.DiagnosticSource(msgSource), -- Message: msgPattern, +-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.AfterChange( +- Diagnostics(env.AtRegexp("a/a.go", "x")), +- ) - }) -} - --func (data *Data) collectCompletions(typ CompletionTestType) func(span.Span, []token.Pos) { -- result := func(m map[span.Span][]Completion, src span.Span, expected []token.Pos) { -- m[src] = append(m[src], Completion{ -- CompletionItems: expected, -- }) -- } -- switch typ { -- case CompletionDeep: -- return func(src span.Span, expected []token.Pos) { -- result(data.DeepCompletions, src, expected) -- } -- case CompletionUnimported: -- return func(src span.Span, expected []token.Pos) { -- result(data.UnimportedCompletions, src, expected) -- } -- case CompletionFuzzy: -- return func(src span.Span, expected []token.Pos) { -- result(data.FuzzyCompletions, src, expected) -- } -- case CompletionRank: -- return func(src span.Span, expected []token.Pos) { -- result(data.RankCompletions, src, expected) -- } -- case CompletionCaseSensitive: -- return func(src span.Span, expected []token.Pos) { -- result(data.CaseSensitiveCompletions, src, expected) -- } -- default: -- return func(src span.Span, expected []token.Pos) { -- result(data.Completions, src, expected) -- } -- } --} +-// 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 - --func (data *Data) collectCompletionItems(pos token.Pos, label, detail, kind string, args []string) { -- var documentation string -- if len(args) > 3 { -- documentation = args[3] -- } -- data.CompletionItems[pos] = &completion.CompletionItem{ -- Label: label, -- Detail: detail, -- Kind: protocol.ParseCompletionItemKind(kind), -- Documentation: documentation, -- } --} +-go 1.15 +--- foo/foo.go -- +-package foo +- +-import "fmt" - --func (data *Data) collectFoldingRanges(spn span.Span) { -- data.FoldingRanges = append(data.FoldingRanges, spn) +-func Foo() { +- fmt.Println("") -} +-` +- Run(t, basic, func(t *testing.T, env *Env) { +- env.WriteWorkspaceFile("foo/foo_test.go", `package main +- +-func main() { - --func (data *Data) collectFormats(spn span.Span) { -- data.Formats = append(data.Formats, spn) +-}`) +- env.OpenFile("foo/foo_test.go") +- env.RegexpReplace("foo/foo_test.go", `package main`, `package foo`) +- env.AfterChange(NoDiagnostics(ForFile("foo/foo.go"))) +- }) -} - --func (data *Data) collectImports(spn span.Span) { -- data.Imports = append(data.Imports, spn) --} +-func TestClosingBuffer(t *testing.T) { +- const basic = ` +--- go.mod -- +-module mod.com +- +-go 1.14 +--- main.go -- +-package main - --func (data *Data) collectAddImports(spn span.Span, imp string) { -- data.AddImport[spn.URI()] = imp +-func main() {} +-` +- Run(t, basic, func(t *testing.T, env *Env) { +- env.Editor.CreateBuffer(env.Ctx, "foo.go", `package main`) +- env.AfterChange() +- env.CloseBuffer("foo.go") +- env.AfterChange(NoLogMatching(protocol.Info, "packages=0")) +- }) -} - --func (data *Data) collectSemanticTokens(spn span.Span) { -- data.SemanticTokens = append(data.SemanticTokens, spn) --} +-// Reproduces golang/go#38424. +-func TestCutAndPaste(t *testing.T) { +- const basic = ` +--- go.mod -- +-module mod.com - --func (data *Data) collectSuggestedFixes(spn span.Span, actionKind, fix string) { -- data.SuggestedFixes[spn] = append(data.SuggestedFixes[spn], SuggestedFix{actionKind, fix}) --} +-go 1.14 +--- main2.go -- +-package main +-` +- Run(t, basic, func(t *testing.T, env *Env) { +- env.CreateBuffer("main.go", "") +- env.Await(env.DoneWithOpen()) - --func (data *Data) collectFunctionExtractions(start span.Span, end span.Span) { -- if _, ok := data.FunctionExtractions[start]; !ok { -- data.FunctionExtractions[start] = end -- } --} +- env.SaveBufferWithoutActions("main.go") +- env.Await(env.DoneWithSave(), env.DoneWithChangeWatchedFiles()) - --func (data *Data) collectMethodExtractions(start span.Span, end span.Span) { -- if _, ok := data.MethodExtractions[start]; !ok { -- data.MethodExtractions[start] = end -- } --} +- env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main - --func (data *Data) collectDefinitions(src, target span.Span) { -- data.Definitions[src] = Definition{ -- Src: src, -- Def: target, -- } +-func main() { -} +-`)) +- env.Await(env.DoneWithChange()) - --func (data *Data) collectSelectionRanges(spn span.Span) { -- data.SelectionRanges = append(data.SelectionRanges, spn) --} +- env.SaveBuffer("main.go") +- env.Await(env.DoneWithSave(), env.DoneWithChangeWatchedFiles()) - --func (data *Data) collectImplementations(src span.Span, targets []span.Span) { -- data.Implementations[src] = targets --} +- env.EditBuffer("main.go", fake.NewEdit(0, 0, 4, 0, "")) +- env.Await(env.DoneWithChange()) - --func (data *Data) collectIncomingCalls(src span.Span, calls []span.Span) { -- for _, call := range calls { -- rng := data.mustRange(call) -- // we're only comparing protocol.range -- if data.CallHierarchy[src] != nil { -- data.CallHierarchy[src].IncomingCalls = append(data.CallHierarchy[src].IncomingCalls, -- protocol.CallHierarchyItem{ -- URI: protocol.DocumentURI(call.URI()), -- Range: rng, -- }) -- } else { -- data.CallHierarchy[src] = &CallHierarchyResult{ -- IncomingCalls: []protocol.CallHierarchyItem{ -- {URI: protocol.DocumentURI(call.URI()), Range: rng}, -- }, -- } -- } -- } --} +- env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main - --func (data *Data) collectOutgoingCalls(src span.Span, calls []span.Span) { -- if data.CallHierarchy[src] == nil { -- data.CallHierarchy[src] = &CallHierarchyResult{} -- } -- for _, call := range calls { -- // we're only comparing protocol.range -- data.CallHierarchy[src].OutgoingCalls = append(data.CallHierarchy[src].OutgoingCalls, -- protocol.CallHierarchyItem{ -- URI: protocol.DocumentURI(call.URI()), -- Range: data.mustRange(call), -- }) -- } +-func main() { +- var x int -} -- --func (data *Data) collectHoverDefinitions(src, target span.Span) { -- data.Definitions[src] = Definition{ -- Src: src, -- Def: target, -- OnlyHover: true, -- } +-`)) +- env.AfterChange( +- Diagnostics(env.AtRegexp("main.go", "x")), +- ) +- }) -} - --func (data *Data) collectTypeDefinitions(src, target span.Span) { -- data.Definitions[src] = Definition{ -- Src: src, -- Def: target, -- IsType: true, -- } --} +-// Reproduces golang/go#39763. +-func TestInvalidPackageName(t *testing.T) { +- const pkgDefault = ` +--- go.mod -- +-module mod.com - --func (data *Data) collectDefinitionNames(src span.Span, name string) { -- d := data.Definitions[src] -- d.Name = name -- data.Definitions[src] = d --} +-go 1.12 +--- main.go -- +-package default - --func (data *Data) collectHighlights(src span.Span, expected []span.Span) { -- // Declaring a highlight in a test file: @highlight(src, expected1, expected2) -- data.Highlights[src] = append(data.Highlights[src], expected...) +-func main() {} +-` +- Run(t, pkgDefault, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- env.AfterChange( +- Diagnostics( +- env.AtRegexp("main.go", "default"), +- WithMessage("expected 'IDENT'"), +- ), +- ) +- }) -} - --func (data *Data) collectInlayHints(src span.Span) { -- data.InlayHints = append(data.InlayHints, src) --} +-// This tests the functionality of the "limitWorkspaceScope" +-func TestLimitWorkspaceScope(t *testing.T) { +- const mod = ` +--- go.mod -- +-module mod.com - --func (data *Data) collectReferences(src span.Span, expected []span.Span) { -- data.References[src] = expected --} +-go 1.12 +--- a/main.go -- +-package main - --func (data *Data) collectRenames(src span.Span, newText string) { -- data.Renames[src] = newText --} +-func main() {} +--- main.go -- +-package main - --func (data *Data) collectPrepareRenames(src, spn span.Span, placeholder string) { -- data.PrepareRenames[src] = &source.PrepareItem{ -- Range: data.mustRange(spn), -- Text: placeholder, -- } +-func main() { +- var x int -} -- --// collectSymbols is responsible for collecting @symbol annotations. --func (data *Data) collectSymbols(name string, selectionRng span.Span, kind, detail, id, parentID string) { -- // We don't set 'Range' here as it is difficult (impossible?) to express -- // multi-line ranges in the packagestest framework. -- uri := selectionRng.URI() -- data.Symbols[uri] = append(data.Symbols[uri], &symbol{ -- pSymbol: protocol.DocumentSymbol{ -- Name: name, -- Kind: protocol.ParseSymbolKind(kind), -- SelectionRange: data.mustRange(selectionRng), -- Detail: detail, -- }, -- id: id, -- parentID: parentID, +-` +- WithOptions( +- WorkspaceFolders("a"), +- ).Run(t, mod, func(t *testing.T, env *Env) { +- env.OpenFile("a/main.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("main.go", "x")), +- ) +- }) +- WithOptions( +- WorkspaceFolders("a"), +- Settings{"expandWorkspaceToModule": false}, +- ).Run(t, mod, func(t *testing.T, env *Env) { +- env.OpenFile("a/main.go") +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- ) - }) -} - --// mustRange converts spn into a protocol.Range, panicking on any error. --func (data *Data) mustRange(spn span.Span) protocol.Range { -- m, err := data.Mapper(spn.URI()) -- rng, err := m.SpanRange(spn) -- if err != nil { -- panic(fmt.Sprintf("converting span %s to range: %v", spn, err)) -- } -- return rng --} +-func TestSimplifyCompositeLitDiagnostic(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - --func (data *Data) collectWorkspaceSymbols(typ WorkspaceSymbolsTestType) func(*expect.Note, string) { -- return func(note *expect.Note, query string) { -- if data.WorkspaceSymbols[typ] == nil { -- data.WorkspaceSymbols[typ] = make(map[span.URI][]string) -- } -- pos := safetoken.StartPosition(data.Exported.ExpectFileSet, note.Pos) -- uri := span.URIFromPath(pos.Filename) -- data.WorkspaceSymbols[typ][uri] = append(data.WorkspaceSymbols[typ][uri], query) -- } --} +-go 1.12 +--- main.go -- +-package main - --func (data *Data) collectSignatures(spn span.Span, signature string, activeParam int64) { -- data.Signatures[spn] = &protocol.SignatureHelp{ -- Signatures: []protocol.SignatureInformation{ -- { -- Label: signature, -- }, -- }, -- ActiveParameter: uint32(activeParam), -- } -- // Hardcode special case to test the lack of a signature. -- if signature == "" && activeParam == 0 { -- data.Signatures[spn] = nil -- } +-import "fmt" +- +-type t struct { +- msg string -} - --func (data *Data) collectCompletionSnippets(spn span.Span, item token.Pos, plain, placeholder string) { -- data.CompletionSnippets[spn] = append(data.CompletionSnippets[spn], CompletionSnippet{ -- CompletionItem: item, -- PlainSnippet: plain, -- PlaceholderSnippet: placeholder, -- }) +-func main() { +- x := []t{t{"msg"}} +- fmt.Println(x) -} +-` - --func (data *Data) collectLinks(spn span.Span, link string, note *expect.Note, fset *token.FileSet) { -- position := safetoken.StartPosition(fset, note.Pos) -- uri := spn.URI() -- data.Links[uri] = append(data.Links[uri], Link{ -- Src: spn, -- Target: link, -- NotePosition: position, +- WithOptions( +- Settings{"staticcheck": true}, +- ).Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- var d protocol.PublishDiagnosticsParams +- env.AfterChange( +- Diagnostics(env.AtRegexp("main.go", `t{"msg"}`), WithMessage("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.AfterChange(NoDiagnostics(ForFile("main.go"))) - }) -} - --func uriName(uri span.URI) string { -- return filepath.Base(strings.TrimSuffix(uri.Filename(), ".go")) --} +-// Test some secondary diagnostics +-func TestSecondaryDiagnostics(t *testing.T) { +- const dir = ` +--- go.mod -- +-module mod.com - --// TODO(golang/go#54845): improve the formatting here to match standard --// line:column position formatting. --func SpanName(spn span.Span) string { -- return fmt.Sprintf("%v_%v_%v", uriName(spn.URI()), spn.Start().Line(), spn.Start().Column()) +-go 1.12 +--- main.go -- +-package main +-func main() { +- panic("not here") -} -- --func CopyFolderToTempDir(folder string) (string, error) { -- if _, err := os.Stat(folder); err != nil { -- return "", err -- } -- dst, err := ioutil.TempDir("", "modfile_test") -- if err != nil { -- return "", err -- } -- fds, err := ioutil.ReadDir(folder) -- if err != nil { -- return "", err -- } -- for _, fd := range fds { -- srcfp := filepath.Join(folder, fd.Name()) -- stat, err := os.Stat(srcfp) -- if err != nil { -- return "", err +--- other.go -- +-package main +-func main() {} +-` +- Run(t, dir, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- env.OpenFile("other.go") +- var mainDiags, otherDiags protocol.PublishDiagnosticsParams +- env.AfterChange( +- ReadDiagnostics("main.go", &mainDiags), +- ReadDiagnostics("other.go", &otherDiags), +- ) +- if len(mainDiags.Diagnostics) != 1 { +- t.Fatalf("main.go, got %d diagnostics, expected 1", len(mainDiags.Diagnostics)) - } -- if !stat.Mode().IsRegular() { -- return "", fmt.Errorf("cannot copy non regular file %s", srcfp) +- keep := mainDiags.Diagnostics[0] +- if len(otherDiags.Diagnostics) != 1 { +- t.Fatalf("other.go: got %d diagnostics, expected 1", len(otherDiags.Diagnostics)) - } -- contents, err := ioutil.ReadFile(srcfp) -- if err != nil { -- return "", err +- if len(otherDiags.Diagnostics[0].RelatedInformation) != 1 { +- t.Fatalf("got %d RelatedInformations, expected 1", len(otherDiags.Diagnostics[0].RelatedInformation)) - } -- if err := ioutil.WriteFile(filepath.Join(dst, fd.Name()), contents, stat.Mode()); err != nil { -- return "", err +- // check that the RelatedInformation matches the error from main.go +- c := otherDiags.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) - } -- } -- return dst, nil --} -- --func shouldSkip(data *Data, uri span.URI) bool { -- if data.ModfileFlagAvailable { -- return false -- } -- // If the -modfile flag is not available, then we do not want to run -- // any tests on the go.mod file. -- if strings.HasSuffix(uri.Filename(), ".mod") { -- return true -- } -- // If the -modfile flag is not available, then we do not want to test any -- // uri that contains "go mod tidy". -- m, err := data.Mapper(uri) -- return err == nil && strings.Contains(string(m.Content), ", \"go mod tidy\",") +- }) -} -diff -urN a/gopls/internal/lsp/tests/util.go b/gopls/internal/lsp/tests/util.go ---- a/gopls/internal/lsp/tests/util.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/tests/util.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,547 +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 tests - --import ( -- "bytes" -- "context" -- "fmt" -- "go/token" -- "path" -- "path/filepath" -- "regexp" -- "sort" -- "strconv" -- "strings" -- "testing" +-func TestOrphanedFiles(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - -- "github.com/google/go-cmp/cmp" -- "github.com/google/go-cmp/cmp/cmpopts" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/lsp/source/completion" -- "golang.org/x/tools/gopls/internal/lsp/tests/compare" -- "golang.org/x/tools/gopls/internal/span" --) +-go 1.12 +--- a/a.go -- +-package a - --var builtins = map[string]bool{ -- "append": true, -- "cap": true, -- "close": true, -- "complex": true, -- "copy": true, -- "delete": true, -- "error": true, -- "false": true, -- "imag": true, -- "iota": true, -- "len": true, -- "make": true, -- "new": true, -- "nil": true, -- "panic": true, -- "print": true, -- "println": true, -- "real": true, -- "recover": true, -- "true": true, +-func main() { +- var x int -} +--- a/a_exclude.go -- +-// +build exclude - --// DiffLinks takes the links we got and checks if they are located within the source or a Note. --// If the link is within a Note, the link is removed. --// Returns an diff comment if there are differences and empty string if no diffs. --func DiffLinks(mapper *protocol.Mapper, wantLinks []Link, gotLinks []protocol.DocumentLink) string { -- var notePositions []token.Position -- links := make(map[span.Span]string, len(wantLinks)) -- for _, link := range wantLinks { -- links[link.Src] = link.Target -- notePositions = append(notePositions, link.NotePosition) -- } -- -- var msg strings.Builder -- for _, link := range gotLinks { -- spn, err := mapper.RangeSpan(link.Range) -- if err != nil { -- return fmt.Sprintf("%v", err) -- } -- linkInNote := false -- for _, notePosition := range notePositions { -- // Drop the links found inside expectation notes arguments as this links are not collected by expect package. -- if notePosition.Line == spn.Start().Line() && -- notePosition.Column <= spn.Start().Column() { -- delete(links, spn) -- linkInNote = true -- } -- } -- if linkInNote { -- continue -- } +-package a - -- if target, ok := links[spn]; ok { -- delete(links, spn) -- if target != link.Target { -- fmt.Fprintf(&msg, "%s: want link with target %q, got %q\n", spn, target, link.Target) -- } -- } else { -- fmt.Fprintf(&msg, "%s: got unexpected link with target %q\n", spn, link.Target) -- } -- } -- for spn, target := range links { -- fmt.Fprintf(&msg, "%s: expected link with target %q is missing\n", spn, target) -- } -- return msg.String() +-func _() { +- var x int -} +-` +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("a/a.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("a/a.go", "x")), +- ) +- env.OpenFile("a/a_exclude.go") - --// CompareDiagnostics reports testing errors to t when the diagnostic set got --// does not match want. If the sole expectation has source "no_diagnostics", --// the test expects that no diagnostics were received for the given document. --func CompareDiagnostics(t *testing.T, uri span.URI, want, got []*source.Diagnostic) { -- t.Helper() -- fileName := path.Base(string(uri)) -- -- // A special case to test that there are no diagnostics for a file. -- if len(want) == 1 && want[0].Source == "no_diagnostics" { -- want = nil -- } -- -- // Build a helper function to match an actual diagnostic to an overlapping -- // expected diagnostic (if any). -- unmatched := make([]*source.Diagnostic, len(want)) -- copy(unmatched, want) -- source.SortDiagnostics(unmatched) -- match := func(g *source.Diagnostic) *source.Diagnostic { -- // Find the last expected diagnostic d for which start(d) < end(g), and -- // check to see if it overlaps. -- i := sort.Search(len(unmatched), func(i int) bool { -- d := unmatched[i] -- // See rangeOverlaps: if a range is a single point, we consider End to be -- // included in the range... -- if g.Range.Start == g.Range.End { -- return protocol.ComparePosition(d.Range.Start, g.Range.End) > 0 -- } -- // ...otherwise the end position of a range is not included. -- return protocol.ComparePosition(d.Range.Start, g.Range.End) >= 0 -- }) -- if i == 0 { -- return nil -- } -- w := unmatched[i-1] -- if rangeOverlaps(w.Range, g.Range) { -- unmatched = append(unmatched[:i-1], unmatched[i:]...) -- return w -- } -- return nil -- } -- -- for _, g := range got { -- w := match(g) -- if w == nil { -- t.Errorf("%s:%s: unexpected diagnostic %q", fileName, g.Range, g.Message) -- continue -- } -- if match, err := regexp.MatchString(w.Message, g.Message); err != nil { -- t.Errorf("%s:%s: invalid regular expression %q: %v", fileName, w.Range.Start, w.Message, err) -- } else if !match { -- t.Errorf("%s:%s: got Message %q, want match for pattern %q", fileName, g.Range.Start, g.Message, w.Message) -- } -- if w.Severity != g.Severity { -- t.Errorf("%s:%s: got Severity %v, want %v", fileName, g.Range.Start, g.Severity, w.Severity) -- } -- if w.Source != g.Source { -- t.Errorf("%s:%s: got Source %v, want %v", fileName, g.Range.Start, g.Source, w.Source) -- } -- } -- -- for _, w := range unmatched { -- t.Errorf("%s:%s: unmatched diagnostic pattern %q", fileName, w.Range, w.Message) -- } --} +- loadOnce := LogMatching(protocol.Info, "query=.*file=.*a_exclude.go", 1, false) - --// rangeOverlaps reports whether r1 and r2 overlap. --func rangeOverlaps(r1, r2 protocol.Range) bool { -- if inRange(r2.Start, r1) || inRange(r1.Start, r2) { -- return true -- } -- return false --} +- // can't use OnceMet or AfterChange as logs are async +- env.Await(loadOnce) +- // ...but ensure that the change has been fully processed before editing. +- // Otherwise, there may be a race where the snapshot is cloned before all +- // state changes resulting from the load have been processed +- // (golang/go#61521). +- env.AfterChange() - --// inRange reports whether p is contained within [r.Start, r.End), or if p == --// r.Start == r.End (special handling for the case where the range is a single --// point). --func inRange(p protocol.Position, r protocol.Range) bool { -- if protocol.IsPoint(r) { -- return protocol.ComparePosition(r.Start, p) == 0 -- } -- if protocol.ComparePosition(r.Start, p) <= 0 && protocol.ComparePosition(p, r.End) < 0 { -- return true -- } -- return false +- // Check that orphaned files are not reloaded, by making a change in +- // a.go file and confirming that the workspace diagnosis did not reload +- // a_exclude.go. +- // +- // This is racy (but fails open) because logs are asynchronous to other LSP +- // operations. There's a chance gopls _did_ log, and we just haven't seen +- // it yet. +- env.RegexpReplace("a/a.go", "package a", "package a // arbitrary comment") +- env.AfterChange(loadOnce) +- }) -} - --func DiffCodeLens(uri span.URI, want, got []protocol.CodeLens) string { -- sortCodeLens(want) -- sortCodeLens(got) +-func TestEnableAllExperiments(t *testing.T) { +- // Before the oldest supported Go version, gopls sends a warning to upgrade +- // Go, which fails the expectation below. +- testenv.NeedsGo1Point(t, lsp.OldestSupportedGoVersion()) - -- if len(got) != len(want) { -- return summarizeCodeLens(-1, uri, want, got, "different lengths got %v want %v", len(got), len(want)) -- } -- for i, w := range want { -- g := got[i] -- if w.Command.Command != g.Command.Command { -- return summarizeCodeLens(i, uri, want, got, "incorrect Command Name got %v want %v", g.Command.Command, w.Command.Command) -- } -- if w.Command.Title != g.Command.Title { -- return summarizeCodeLens(i, uri, want, got, "incorrect Command Title got %v want %v", g.Command.Title, w.Command.Title) -- } -- if protocol.ComparePosition(w.Range.Start, g.Range.Start) != 0 { -- return summarizeCodeLens(i, uri, want, got, "incorrect Start got %v want %v", g.Range.Start, w.Range.Start) -- } -- if !protocol.IsPoint(g.Range) { // Accept any 'want' range if the codelens returns a zero-length range. -- if protocol.ComparePosition(w.Range.End, g.Range.End) != 0 { -- return summarizeCodeLens(i, uri, want, got, "incorrect End got %v want %v", g.Range.End, w.Range.End) -- } -- } -- } -- return "" --} +- const mod = ` +--- go.mod -- +-module mod.com - --func sortCodeLens(c []protocol.CodeLens) { -- sort.Slice(c, func(i int, j int) bool { -- if r := protocol.CompareRange(c[i].Range, c[j].Range); r != 0 { -- return r < 0 -- } -- if c[i].Command.Command < c[j].Command.Command { -- return true -- } else if c[i].Command.Command == c[j].Command.Command { -- return c[i].Command.Title < c[j].Command.Title -- } else { -- return false -- } -- }) --} +-go 1.12 +--- main.go -- +-package main - --func summarizeCodeLens(i int, uri span.URI, want, got []protocol.CodeLens, reason string, args ...interface{}) string { -- msg := &bytes.Buffer{} -- fmt.Fprint(msg, "codelens failed") -- if i >= 0 { -- fmt.Fprintf(msg, " at %d", i) -- } -- fmt.Fprint(msg, " because of ") -- fmt.Fprintf(msg, reason, args...) -- fmt.Fprint(msg, ":\nexpected:\n") -- for _, d := range want { -- fmt.Fprintf(msg, " %s:%v: %s | %s\n", uri, d.Range, d.Command.Command, d.Command.Title) -- } -- fmt.Fprintf(msg, "got:\n") -- for _, d := range got { -- fmt.Fprintf(msg, " %s:%v: %s | %s\n", uri, d.Range, d.Command.Command, d.Command.Title) -- } -- return msg.String() --} +-import "bytes" - --func DiffSignatures(spn span.Span, want, got *protocol.SignatureHelp) string { -- decorate := func(f string, args ...interface{}) string { -- return fmt.Sprintf("invalid signature at %s: %s", spn, fmt.Sprintf(f, args...)) -- } -- if len(got.Signatures) != 1 { -- return decorate("wanted 1 signature, got %d", len(got.Signatures)) -- } -- if got.ActiveSignature != 0 { -- return decorate("wanted active signature of 0, got %d", int(got.ActiveSignature)) -- } -- if want.ActiveParameter != got.ActiveParameter { -- return decorate("wanted active parameter of %d, got %d", want.ActiveParameter, int(got.ActiveParameter)) -- } -- g := got.Signatures[0] -- w := want.Signatures[0] -- if diff := compare.Text(NormalizeAny(w.Label), NormalizeAny(g.Label)); diff != "" { -- return decorate("mismatched labels:\n%s", diff) -- } -- var paramParts []string -- for _, p := range g.Parameters { -- paramParts = append(paramParts, p.Label) -- } -- paramsStr := strings.Join(paramParts, ", ") -- if !strings.Contains(g.Label, paramsStr) { -- return decorate("expected signature %q to contain params %q", g.Label, paramsStr) -- } -- return "" +-func b(c bytes.Buffer) { +- _ = 1 -} -- --// NormalizeAny replaces occurrences of interface{} in input with any. --// --// In Go 1.18, standard library functions were changed to use the 'any' --// alias in place of interface{}, which affects their type string. --func NormalizeAny(input string) string { -- return strings.ReplaceAll(input, "interface{}", "any") +-` +- WithOptions( +- Settings{"allExperiments": true}, +- ).Run(t, mod, func(t *testing.T, env *Env) { +- // Confirm that the setting doesn't cause any warnings. +- env.OnceMet( +- InitialWorkspaceLoad, +- NoShownMessage(""), // empty substring to match any message +- ) +- }) -} - --// DiffCallHierarchyItems returns the diff between expected and actual call locations for incoming/outgoing call hierarchies --func DiffCallHierarchyItems(gotCalls []protocol.CallHierarchyItem, expectedCalls []protocol.CallHierarchyItem) string { -- expected := make(map[protocol.Location]bool) -- for _, call := range expectedCalls { -- expected[protocol.Location{URI: call.URI, Range: call.Range}] = true -- } +-func TestSwig(t *testing.T) { +- // This is fixed in Go 1.17, but not earlier. +- testenv.NeedsGo1Point(t, 17) - -- got := make(map[protocol.Location]bool) -- for _, call := range gotCalls { -- got[protocol.Location{URI: call.URI, Range: call.Range}] = true -- } -- if len(got) != len(expected) { -- return fmt.Sprintf("expected %d calls but got %d", len(expected), len(got)) +- if _, err := exec.LookPath("swig"); err != nil { +- t.Skip("skipping test: swig not available") - } -- for spn := range got { -- if !expected[spn] { -- return fmt.Sprintf("incorrect calls, expected locations %v but got locations %v", expected, got) -- } +- if _, err := exec.LookPath("g++"); err != nil { +- t.Skip("skipping test: g++ not available") - } -- return "" --} - --func FilterBuiltins(src span.Span, items []protocol.CompletionItem) []protocol.CompletionItem { -- var ( -- got []protocol.CompletionItem -- wantBuiltins = strings.Contains(string(src.URI()), "builtins") -- wantKeywords = strings.Contains(string(src.URI()), "keywords") -- ) -- for _, item := range items { -- if !wantBuiltins && isBuiltin(item.Label, item.Detail, item.Kind) { -- continue -- } +- const mod = ` +--- go.mod -- +-module mod.com - -- if !wantKeywords && token.Lookup(item.Label).IsKeyword() { -- continue -- } +-go 1.12 +--- pkg/simple/export_swig.go -- +-package simple - -- got = append(got, item) -- } -- return got +-func ExportSimple(x, y int) int { +- return Gcd(x, y) -} +--- pkg/simple/simple.swigcxx -- +-%module simple - --func isBuiltin(label, detail string, kind protocol.CompletionItemKind) bool { -- if detail == "" && kind == protocol.ClassCompletion { -- return true -- } -- // Remaining builtin constants, variables, interfaces, and functions. -- trimmed := label -- if i := strings.Index(trimmed, "("); i >= 0 { -- trimmed = trimmed[:i] -- } -- return builtins[trimmed] +-%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 CheckCompletionOrder(want, got []protocol.CompletionItem, strictScores bool) string { -- var ( -- matchedIdxs []int -- lastGotIdx int -- lastGotSort float64 -- inOrder = true -- errorMsg = "completions out of order" -- ) -- for _, w := range want { -- var found bool -- for i, g := range got { -- if w.Label == g.Label && NormalizeAny(w.Detail) == NormalizeAny(g.Detail) && w.Kind == g.Kind { -- matchedIdxs = append(matchedIdxs, i) -- found = true +-func main() { +- var x int +-} +-` +- Run(t, mod, func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- NoDiagnostics(WithMessage("illegal character U+0023 '#'")), +- ) +- }) +-} - -- if i < lastGotIdx { -- inOrder = false -- } -- lastGotIdx = i +-// 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) { +- const proxy = ` +--- example.com@v1.2.3/go.mod -- +-module example.com - -- sort, _ := strconv.ParseFloat(g.SortText, 64) -- if strictScores && len(matchedIdxs) > 1 && sort <= lastGotSort { -- inOrder = false -- errorMsg = "candidate scores not strictly decreasing" -- } -- lastGotSort = sort +-go 1.12 +--- example.com@v1.2.3/blah/blah.go -- +-package blah - -- break -- } -- } -- if !found { -- return summarizeCompletionItems(-1, []protocol.CompletionItem{w}, got, "didn't find expected completion") -- } -- } +-const Name = "Blah" +--- random.org@v1.2.3/go.mod -- +-module random.org - -- sort.Ints(matchedIdxs) -- matched := make([]protocol.CompletionItem, 0, len(matchedIdxs)) -- for _, idx := range matchedIdxs { -- matched = append(matched, got[idx]) -- } +-go 1.12 +--- random.org@v1.2.3/blah/blah.go -- +-package hello - -- if !inOrder { -- return summarizeCompletionItems(-1, want, matched, errorMsg) -- } +-const Name = "Hello" +-` - -- return "" --} +- const contents = ` +--- go.mod -- +-module mod.com - --func DiffSnippets(want string, got *protocol.CompletionItem) string { -- if want == "" { -- if got != nil { -- x := got.TextEdit -- return fmt.Sprintf("expected no snippet but got %s", x.NewText) -- } -- } else { -- if got == nil { -- return fmt.Sprintf("couldn't find completion matching %q", want) -- } -- x := got.TextEdit -- if want != x.NewText { -- return fmt.Sprintf("expected snippet %q, got %q", want, x.NewText) -- } -- } -- return "" --} +-go 1.12 +--- main.go -- +-package main - --func FindItem(list []protocol.CompletionItem, want completion.CompletionItem) *protocol.CompletionItem { -- for _, item := range list { -- if item.Label == want.Label { -- return &item -- } -- } -- return nil +-import "example.com/blah" +- +-func main() { +- blah.Hello() -} +--- bob.go -- +-package main +--- foo/foo.go -- +-package foo +--- foo/foo_test.go -- +-package foo_ +-` - --// DiffCompletionItems prints the diff between expected and actual completion --// test results. --// --// The diff will be formatted using '-' and '+' for want and got, respectively. --func DiffCompletionItems(want, got []protocol.CompletionItem) string { -- // Many fields are not set in the "want" slice. -- irrelevantFields := []string{ -- "AdditionalTextEdits", -- "Documentation", -- "TextEdit", -- "SortText", -- "Preselect", -- "FilterText", -- "InsertText", -- "InsertTextFormat", -- } -- ignore := cmpopts.IgnoreFields(protocol.CompletionItem{}, irrelevantFields...) -- normalizeAny := cmpopts.AcyclicTransformer("NormalizeAny", func(item protocol.CompletionItem) protocol.CompletionItem { -- item.Detail = NormalizeAny(item.Detail) -- return item +- WithOptions( +- ProxyFiles(proxy), +- InGOPATH(), +- EnvVars{"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.AfterChange( +- NoDiagnostics(ForFile("foo/foo_test.go")), +- NoOutstandingWork(IgnoreTelemetryPromptWork), +- ) - }) -- return cmp.Diff(want, got, ignore, normalizeAny) -} - --func summarizeCompletionItems(i int, want, got []protocol.CompletionItem, reason string, args ...interface{}) string { -- msg := &bytes.Buffer{} -- fmt.Fprint(msg, "completion failed") -- if i >= 0 { -- fmt.Fprintf(msg, " at %d", i) -- } -- fmt.Fprint(msg, " because of ") -- fmt.Fprintf(msg, reason, args...) -- fmt.Fprint(msg, ":\nexpected:\n") -- for _, d := range want { -- fmt.Fprintf(msg, " %v\n", d) -- } -- fmt.Fprintf(msg, "got:\n") -- for _, d := range got { -- fmt.Fprintf(msg, " %v\n", d) -- } -- return msg.String() --} +-// TestProgressBarErrors confirms that critical workspace load errors are shown +-// and updated via progress reports. +-func TestProgressBarErrors(t *testing.T) { +- const pkg = ` +--- go.mod -- +-modul mod.com - --func EnableAllAnalyzers(opts *source.Options) { -- if opts.Analyses == nil { -- opts.Analyses = make(map[string]bool) -- } -- for _, a := range opts.DefaultAnalyzers { -- if !a.IsEnabled(opts) { -- opts.Analyses[a.Analyzer.Name] = true -- } -- } -- for _, a := range opts.TypeErrorAnalyzers { -- if !a.IsEnabled(opts) { -- opts.Analyses[a.Analyzer.Name] = true -- } -- } -- for _, a := range opts.ConvenienceAnalyzers { -- if !a.IsEnabled(opts) { -- opts.Analyses[a.Analyzer.Name] = true -- } -- } -- for _, a := range opts.StaticcheckAnalyzers { -- if !a.IsEnabled(opts) { -- opts.Analyses[a.Analyzer.Name] = true -- } -- } --} +-go 1.12 +--- main.go -- +-package main +-` +- Run(t, pkg, func(t *testing.T, env *Env) { +- env.OpenFile("go.mod") +- env.AfterChange( +- OutstandingWork(lsp.WorkspaceLoadFailure, "unknown directive"), +- ) +- env.EditBuffer("go.mod", fake.NewEdit(0, 0, 3, 0, `module mod.com - --func EnableAllInlayHints(opts *source.Options) { -- if opts.Hints == nil { -- opts.Hints = make(map[string]bool) -- } -- for name := range source.AllInlayHints { -- opts.Hints[name] = true -- } +-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.AfterChange( +- OutstandingWork(lsp.WorkspaceLoadFailure, "invalid go version"), +- ) +- env.RegexpReplace("go.mod", "go 1.hello", "go 1.12") +- env.SaveBufferWithoutActions("go.mod") +- env.AfterChange( +- NoOutstandingWork(IgnoreTelemetryPromptWork), +- ) +- }) -} - --func WorkspaceSymbolsString(ctx context.Context, data *Data, queryURI span.URI, symbols []protocol.SymbolInformation) (string, error) { -- queryDir := filepath.Dir(queryURI.Filename()) -- var filtered []string -- for _, s := range symbols { -- uri := s.Location.URI.SpanURI() -- dir := filepath.Dir(uri.Filename()) -- if !source.InDir(queryDir, dir) { // assume queries always issue from higher directories -- continue -- } -- m, err := data.Mapper(uri) -- if err != nil { -- return "", err -- } -- spn, err := m.LocationSpan(s.Location) -- if err != nil { -- return "", err -- } -- filtered = append(filtered, fmt.Sprintf("%s %s %s", spn, s.Name, s.Kind)) -- } -- sort.Strings(filtered) -- return strings.Join(filtered, "\n") + "\n", nil --} +-func TestDeleteDirectory(t *testing.T) { +- const mod = ` +--- bob/bob.go -- +-package bob - --func WorkspaceSymbolsTestTypeToMatcher(typ WorkspaceSymbolsTestType) source.SymbolMatcher { -- switch typ { -- case WorkspaceSymbolsFuzzy: -- return source.SymbolFuzzy -- case WorkspaceSymbolsCaseSensitive: -- return source.SymbolCaseSensitive -- default: -- return source.SymbolCaseInsensitive -- } +-func Hello() { +- var x int -} +--- go.mod -- +-module mod.com +--- cmd/main.go -- +-package main - --// LocationsToSpans converts protocol location into span form for testing. --func LocationsToSpans(data *Data, locs []protocol.Location) ([]span.Span, error) { -- spans := make([]span.Span, len(locs)) -- for i, loc := range locs { -- m, err := data.Mapper(loc.URI.SpanURI()) -- if err != nil { -- return nil, err -- } -- spn, err := m.LocationSpan(loc) -- if err != nil { -- return nil, fmt.Errorf("failed for %v: %w", loc, err) -- } -- spans[i] = spn -- } -- return spans, nil --} +-import "mod.com/bob" - --// SortAndFormatSpans sorts and formats a list of spans for use in an assertion. --func SortAndFormatSpans(spans []span.Span) string { -- span.SortSpans(spans) -- var buf strings.Builder -- for _, spn := range spans { -- fmt.Fprintf(&buf, "%v\n", spn) -- } -- return buf.String() +-func main() { +- bob.Hello() +-} +-` +- WithOptions( +- Settings{ +- // Now that we don't watch subdirs by default (except for VS Code), +- // we must explicitly ask gopls to requests subdir watch patterns. +- "subdirWatchPatterns": "on", +- }, +- ).Run(t, mod, func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- FileWatchMatching("bob"), +- ) +- env.RemoveWorkspaceFile("bob") +- env.AfterChange( +- Diagnostics(env.AtRegexp("cmd/main.go", `"mod.com/bob"`)), +- NoDiagnostics(ForFile("bob/bob.go")), +- NoFileWatchMatching("bob"), +- ) +- }) -} -diff -urN a/gopls/internal/lsp/tests/util_go118.go b/gopls/internal/lsp/tests/util_go118.go ---- a/gopls/internal/lsp/tests/util_go118.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/tests/util_go118.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,13 +0,0 @@ --// Copyright 2023 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 +-// Confirms that circular imports are tested and reported. +-func TestCircularImports(t *testing.T) { +- const mod = ` +--- go.mod -- +-module mod.com - --package tests +-go 1.12 +--- self/self.go -- +-package self - --func init() { -- builtins["any"] = true -- builtins["comparable"] = true +-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.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics(env.AtRegexp("self/self.go", `_ "mod.com/self"`), WithMessage("import cycle not allowed")), +- Diagnostics(env.AtRegexp("double/a/a.go", `_ "mod.com/double/b"`), WithMessage("import cycle not allowed")), +- Diagnostics(env.AtRegexp("triple/a/a.go", `_ "mod.com/triple/b"`), WithMessage("import cycle not allowed")), +- ) +- }) -} -diff -urN a/gopls/internal/lsp/tests/util_go121.go b/gopls/internal/lsp/tests/util_go121.go ---- a/gopls/internal/lsp/tests/util_go121.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/tests/util_go121.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,12 +0,0 @@ --// Copyright 2023 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.21 --// +build go1.21 +-// 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 - --package tests +-go 1.16 +--- a/a.go -- +-package a - --func init() { -- builtins["clear"] = true +-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.AfterChange( +- // 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( +- Diagnostics(env.AtRegexp("a/a.go", `"mod.test/b"`), WithMessage("import cycle")), +- Diagnostics(env.AtRegexp("b/b.go", `"mod.test/a"`), WithMessage("import cycle")), +- ), +- ) +- env.RegexpReplace("b/b.go", `const B = a\.B`, "") +- env.SaveBuffer("b/b.go") +- env.AfterChange( +- NoDiagnostics(ForFile("a/a.go")), +- NoDiagnostics(ForFile("b/b.go")), +- ) +- }) -} -diff -urN a/gopls/internal/lsp/text_synchronization.go b/gopls/internal/lsp/text_synchronization.go ---- a/gopls/internal/lsp/text_synchronization.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/text_synchronization.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,349 +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 lsp +-func TestBadImport(t *testing.T) { +- const mod = ` +--- go.mod -- +-module mod.com - --import ( -- "bytes" -- "context" -- "errors" -- "fmt" -- "path/filepath" -- "sync" +-go 1.12 +--- main.go -- +-package main - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/jsonrpc2" +-import ( +- _ "nosuchpkg" -) +-` +- t.Run("module", func(t *testing.T) { +- Run(t, mod, func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics(env.AtRegexp("main.go", `"nosuchpkg"`), WithMessage(`could not import nosuchpkg (no required module provides package "nosuchpkg"`)), +- ) +- }) +- }) +- t.Run("GOPATH", func(t *testing.T) { +- WithOptions( +- InGOPATH(), +- EnvVars{"GO111MODULE": "off"}, +- Modes(Default), +- ).Run(t, mod, func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics(env.AtRegexp("main.go", `"nosuchpkg"`), WithMessage(`cannot find package "nosuchpkg"`)), +- ) +- }) +- }) +-} - --// ModificationSource identifies the originating cause of a file modification. --type ModificationSource int +-func TestNestedModules(t *testing.T) { +- const proxy = ` +--- nested.com@v1.0.0/go.mod -- +-module nested.com - --const ( -- // FromDidOpen is a file modification caused by opening a file. -- FromDidOpen = ModificationSource(iota) +-go 1.12 +--- nested.com@v1.0.0/hello/hello.go -- +-package hello - -- // FromDidChange is a file modification caused by changing a file. -- FromDidChange +-func Hello() {} +-` - -- // FromDidChangeWatchedFiles is a file modification caused by a change to a -- // watched file. -- FromDidChangeWatchedFiles +- const nested = ` +--- go.mod -- +-module mod.com - -- // FromDidSave is a file modification caused by a file save. -- FromDidSave +-go 1.12 - -- // FromDidClose is a file modification caused by closing a file. -- FromDidClose +-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 - -- // TODO: add FromDidChangeConfiguration, once configuration changes cause a -- // new snapshot to be created. +-import "nested.com/hello" - -- // FromRegenerateCgo refers to file modifications caused by regenerating -- // the cgo sources for the workspace. -- FromRegenerateCgo +-func main() { +- hello.Hello() +-} +--- nested/go.mod -- +-module nested.com - -- // FromInitialWorkspaceLoad refers to the loading of all packages in the -- // workspace when the view is first created. -- FromInitialWorkspaceLoad --) +--- nested/hello/hello.go -- +-package hello - --func (m ModificationSource) String() string { -- switch m { -- case FromDidOpen: -- return "opened files" -- case FromDidChange: -- return "changed files" -- case FromDidChangeWatchedFiles: -- return "files changed on disk" -- case FromDidSave: -- return "saved files" -- case FromDidClose: -- return "close files" -- case FromRegenerateCgo: -- return "regenerate cgo" -- case FromInitialWorkspaceLoad: -- return "initial workspace load" -- default: -- return "unknown file modification" -- } +-func Hello() { +- helloHelper() -} +--- nested/hello/hello_helper.go -- +-package hello - --func (s *Server) didOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error { -- uri := params.TextDocument.URI.SpanURI() -- if !uri.IsFile() { -- return nil -- } -- // There may not be any matching view in the current session. If that's -- // the case, try creating a new view based on the opened file path. -- // -- // TODO(rstambler): This seems like it would continuously add new -- // views, but it won't because ViewOf only returns an error when there -- // are no views in the session. I don't know if that logic should go -- // here, or if we can continue to rely on that implementation detail. -- if _, err := s.session.ViewOf(uri); err != nil { -- dir := filepath.Dir(uri.Filename()) -- if err := s.addFolders(ctx, []protocol.WorkspaceFolder{{ -- URI: string(protocol.URIFromPath(dir)), -- Name: filepath.Base(dir), -- }}); err != nil { -- return err -- } -- } -- return s.didModifyFiles(ctx, []source.FileModification{{ -- URI: uri, -- Action: source.Open, -- Version: params.TextDocument.Version, -- Text: []byte(params.TextDocument.Text), -- LanguageID: params.TextDocument.LanguageID, -- }}, FromDidOpen) +-func helloHelper() {} +-` +- WithOptions( +- ProxyFiles(proxy), +- Modes(Default), +- ).Run(t, nested, func(t *testing.T, env *Env) { +- // Expect a diagnostic in a nested module. +- env.OpenFile("nested/hello/hello.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("nested/hello/hello.go", "helloHelper")), +- Diagnostics(env.AtRegexp("nested/hello/hello.go", "package (hello)"), WithMessage("not included in your workspace")), +- ) +- }) -} - --func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error { -- uri := params.TextDocument.URI.SpanURI() -- if !uri.IsFile() { -- return nil -- } +-func TestAdHocPackagesReloading(t *testing.T) { +- const nomod = ` +--- main.go -- +-package main - -- text, err := s.changedText(ctx, uri, params.ContentChanges) -- if err != nil { -- return err -- } -- c := source.FileModification{ -- URI: uri, -- Action: source.Change, -- Version: params.TextDocument.Version, -- Text: text, -- } -- if err := s.didModifyFiles(ctx, []source.FileModification{c}, FromDidChange); err != nil { -- return err -- } -- return s.warnAboutModifyingGeneratedFiles(ctx, uri) +-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.AfterChange(NoLogMatching(protocol.Info, "packages=1")) +- }) -} - --// warnAboutModifyingGeneratedFiles shows a warning if a user tries to edit a --// generated file for the first time. --func (s *Server) warnAboutModifyingGeneratedFiles(ctx context.Context, uri span.URI) error { -- s.changedFilesMu.Lock() -- _, ok := s.changedFiles[uri] -- if !ok { -- s.changedFiles[uri] = struct{}{} -- } -- s.changedFilesMu.Unlock() +-func TestBuildTagChange(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - -- // This file has already been edited before. -- if ok { -- return nil -- } +-go 1.12 +--- foo.go -- +-// decoy comment +-// +build hidden +-// decoy comment - -- // Ideally, we should be able to specify that a generated file should -- // be opened as read-only. Tell the user that they should not be -- // editing a generated file. -- view, err := s.session.ViewOf(uri) -- if err != nil { -- return err -- } -- snapshot, release, err := view.Snapshot() -- if err != nil { -- return err -- } -- isGenerated := source.IsGenerated(ctx, snapshot, uri) -- release() +-package foo +-var Foo = 1 +--- bar.go -- +-package foo +-var Bar = Foo +-` - -- if !isGenerated { -- return nil -- } -- return s.client.ShowMessage(ctx, &protocol.ShowMessageParams{ -- Message: fmt.Sprintf("Do not edit this file! %s is a generated file.", uri.Filename()), -- Type: protocol.Warning, +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("foo.go") +- env.AfterChange(Diagnostics(env.AtRegexp("bar.go", `Foo`))) +- env.RegexpReplace("foo.go", `\+build`, "") +- env.AfterChange(NoDiagnostics(ForFile("bar.go"))) - }) +- -} - --func (s *Server) didChangeWatchedFiles(ctx context.Context, params *protocol.DidChangeWatchedFilesParams) error { -- var modifications []source.FileModification -- for _, change := range params.Changes { -- uri := change.URI.SpanURI() -- if !uri.IsFile() { -- continue -- } -- action := changeTypeToFileAction(change.Type) -- modifications = append(modifications, source.FileModification{ -- URI: uri, -- Action: action, -- OnDisk: true, -- }) -- } -- return s.didModifyFiles(ctx, modifications, FromDidChangeWatchedFiles) +-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 - --func (s *Server) didSave(ctx context.Context, params *protocol.DidSaveTextDocumentParams) error { -- uri := params.TextDocument.URI.SpanURI() -- if !uri.IsFile() { -- return nil -- } -- c := source.FileModification{ -- URI: uri, -- Action: source.Save, -- } -- if params.Text != nil { -- c.Text = []byte(*params.Text) -- } -- return s.didModifyFiles(ctx, []source.FileModification{c}, FromDidSave) +-` +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- env.OpenFile("other.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("main.go", "asdf")), +- Diagnostics(env.AtRegexp("main.go", "fdas")), +- ) +- env.SetBufferContent("other.go", "package main\n\nasdf") +- // The new diagnostic in other.go should not suppress diagnostics in main.go. +- env.AfterChange( +- Diagnostics(env.AtRegexp("other.go", "asdf"), WithMessage("expected declaration")), +- Diagnostics(env.AtRegexp("main.go", "asdf")), +- ) +- }) -} - --func (s *Server) didClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error { -- uri := params.TextDocument.URI.SpanURI() -- if !uri.IsFile() { -- return nil -- } -- return s.didModifyFiles(ctx, []source.FileModification{ -- { -- URI: uri, -- Action: source.Close, -- Version: -1, -- Text: nil, -- }, -- }, FromDidClose) +-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.AfterChange( +- NoLogMatching(protocol.Error, "initial workspace load failed"), +- ) +- }) -} - --func (s *Server) didModifyFiles(ctx context.Context, modifications []source.FileModification, cause ModificationSource) error { -- // wg guards two conditions: -- // 1. didModifyFiles is complete -- // 2. the goroutine diagnosing changes on behalf of didModifyFiles is -- // complete, if it was started -- // -- // Both conditions must be satisfied for the purpose of testing: we don't -- // want to observe the completion of change processing until we have received -- // all diagnostics as well as all server->client notifications done on behalf -- // of this function. -- var wg sync.WaitGroup -- wg.Add(1) -- defer wg.Done() +-// 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 - -- if s.session.Options().VerboseWorkDoneProgress { -- work := s.progress.Start(ctx, DiagnosticWorkTitle(cause), "Calculating file diagnostics...", nil, nil) -- go func() { -- wg.Wait() -- work.End(ctx, "Done.") -- }() -- } +-go 1.12 +--- main.go -- +-package main - -- onDisk := cause == FromDidChangeWatchedFiles +-func main() {} +-` +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("go.mod") +- env.Await( +- // Check that we have only loaded "<dir>/..." once. +- // Log messages are asynchronous to other events on the LSP stream, so we +- // can't use OnceMet or AfterChange here. +- LogMatching(protocol.Info, `.*query=.*\.\.\..*`, 1, false), +- ) +- }) +-} - -- s.stateMu.Lock() -- if s.state >= serverShutDown { -- // This state check does not prevent races below, and exists only to -- // produce a better error message. The actual race to the cache should be -- // guarded by Session.viewMu. -- s.stateMu.Unlock() -- return errors.New("server is shut down") -- } -- s.stateMu.Unlock() +-func TestLangVersion(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) // Requires types.Config.GoVersion, new in 1.18. +- const files = ` +--- go.mod -- +-module mod.com - -- // If the set of changes included directories, expand those directories -- // to their files. -- modifications = s.session.ExpandModificationsToDirectories(ctx, modifications) +-go 1.12 +--- main.go -- +-package main - -- // Build a lookup map for file modifications, so that we can later join -- // with the snapshot file associations. -- modMap := make(map[span.URI]source.FileModification) -- for _, mod := range modifications { -- modMap[mod.URI] = mod -- } +-const C = 0b10 +-` +- Run(t, files, func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics(env.AtRegexp("main.go", `0b10`), WithMessage("go1.13 or later")), +- ) +- env.WriteWorkspaceFile("go.mod", "module mod.com \n\ngo 1.13\n") +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- ) +- }) +-} - -- snapshots, release, err := s.session.DidModifyFiles(ctx, modifications) -- if err != nil { -- return err -- } +-func TestNoQuickFixForUndeclaredConstraint(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) +- const files = ` +--- go.mod -- +-module mod.com - -- // golang/go#50267: diagnostics should be re-sent after an open or close. For -- // some clients, it may be helpful to re-send after each change. -- for snapshot, uris := range snapshots { -- for _, uri := range uris { -- mod := modMap[uri] -- if snapshot.View().Options().ChattyDiagnostics || mod.Action == source.Open || mod.Action == source.Close { -- s.mustPublishDiagnostics(uri) -- } +-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.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics(env.AtRegexp("main.go", `C`)), +- ReadDiagnostics("main.go", &d), +- ) +- if fixes := env.GetQuickFixes("main.go", d.Diagnostics); len(fixes) != 0 { +- t.Errorf("got quick fixes %v, wanted none", fixes) - } -- } +- }) +-} - -- wg.Add(1) -- go func() { -- s.diagnoseSnapshots(snapshots, onDisk) -- release() -- wg.Done() -- }() +-func TestEditGoDirective(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) +- const files = ` +--- go.mod -- +-module mod.com - -- // After any file modifications, we need to update our watched files, -- // in case something changed. Compute the new set of directories to watch, -- // and if it differs from the current set, send updated registrations. -- return s.updateWatchedDirectories(ctx) +-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.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics(env.AtRegexp("main.go", `T any`), WithMessage("type parameter")), +- ReadDiagnostics("main.go", &d), +- ) - --// DiagnosticWorkTitle returns the title of the diagnostic work resulting from a --// file change originating from the given cause. --func DiagnosticWorkTitle(cause ModificationSource) string { -- return fmt.Sprintf("diagnosing %v", cause) +- env.ApplyQuickFixes("main.go", d.Diagnostics) +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- ) +- }) -} - --func (s *Server) changedText(ctx context.Context, uri span.URI, changes []protocol.TextDocumentContentChangeEvent) ([]byte, error) { -- if len(changes) == 0 { -- return nil, fmt.Errorf("%w: no content changes provided", jsonrpc2.ErrInternal) -- } +-func TestEditGoDirectiveWorkspace(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) +- const files = ` +--- go.mod -- +-module mod.com - -- // Check if the client sent the full content of the file. -- // We accept a full content change even if the server expected incremental changes. -- if len(changes) == 1 && changes[0].Range == nil && changes[0].RangeLength == 0 { -- return []byte(changes[0].Text), nil -- } -- return s.applyIncrementalChanges(ctx, uri, changes) +-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 - --func (s *Server) applyIncrementalChanges(ctx context.Context, uri span.URI, changes []protocol.TextDocumentContentChangeEvent) ([]byte, error) { -- fh, err := s.session.GetFile(ctx, uri) -- if err != nil { -- return nil, err -- } -- content, err := fh.Read() -- if err != nil { -- return nil, fmt.Errorf("%w: file not found (%v)", jsonrpc2.ErrInternal, err) -- } -- for _, change := range changes { -- // TODO(adonovan): refactor to use diff.Apply, which is robust w.r.t. -- // out-of-order or overlapping changes---and much more efficient. +- // We should have a diagnostic because generics are not supported at 1.16. +- env.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics(env.AtRegexp("main.go", `T any`), WithMessage("type parameter")), +- ReadDiagnostics("main.go", &d), +- ) - -- // Make sure to update mapper along with the content. -- m := protocol.NewMapper(uri, content) -- if change.Range == nil { -- return nil, fmt.Errorf("%w: unexpected nil range for change", jsonrpc2.ErrInternal) -- } -- spn, err := m.RangeSpan(*change.Range) -- if err != nil { -- return nil, err -- } -- start, end := spn.Start().Offset(), spn.End().Offset() -- if end < start { -- return nil, fmt.Errorf("%w: invalid range for content change", jsonrpc2.ErrInternal) -- } -- var buf bytes.Buffer -- buf.Write(content[:start]) -- buf.WriteString(change.Text) -- buf.Write(content[end:]) -- content = buf.Bytes() -- } -- return content, nil +- // This diagnostic should have a quick fix to edit the go version. +- env.ApplyQuickFixes("main.go", d.Diagnostics) +- +- // Once the edit is applied, the problematic diagnostics should be +- // resolved. +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- ) +- }) -} - --func changeTypeToFileAction(ct protocol.FileChangeType) source.FileAction { -- switch ct { -- case protocol.Changed: -- return source.Change -- case protocol.Created: -- return source.Create -- case protocol.Deleted: -- return source.Delete -- } -- return source.UnknownFileAction +-// This test demonstrates that analysis facts are correctly propagated +-// across packages. +-func TestInterpackageAnalysis(t *testing.T) { +- const src = ` +--- go.mod -- +-module example.com +--- a/a.go -- +-package a +- +-import "example.com/b" +- +-func _() { +- new(b.B).Printf("%d", "s") // printf error -} -diff -urN a/gopls/internal/lsp/work/completion.go b/gopls/internal/lsp/work/completion.go ---- a/gopls/internal/lsp/work/completion.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/work/completion.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,154 +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 work +--- b/b.go -- +-package b - --import ( -- "context" -- "errors" -- "fmt" -- "os" -- "path/filepath" -- "sort" -- "strings" +-import "example.com/c" - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/internal/event" --) +-type B struct{} - --func Completion(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, position protocol.Position) (*protocol.CompletionList, error) { -- ctx, done := event.Start(ctx, "work.Completion") -- defer done() +-func (B) Printf(format string, args ...interface{}) { +- c.MyPrintf(format, args...) +-} - -- // Get the position of the cursor. -- pw, err := snapshot.ParseWork(ctx, fh) -- if err != nil { -- return nil, fmt.Errorf("getting go.work file handle: %w", err) -- } -- cursor, err := pw.Mapper.PositionOffset(position) -- if err != nil { -- return nil, fmt.Errorf("computing cursor offset: %w", err) -- } +--- c/c.go -- +-package c - -- // Find the use statement the user is in. -- use, pathStart, _ := usePath(pw, cursor) -- if use == nil { -- return &protocol.CompletionList{}, nil -- } -- completingFrom := use.Path[:cursor-pathStart] +-import "fmt" - -- // We're going to find the completions of the user input -- // (completingFrom) by doing a walk on the innermost directory -- // of the given path, and comparing the found paths to make sure -- // that they match the component of the path after the -- // innermost directory. -- // -- // We'll maintain two paths when doing this: pathPrefixSlash -- // is essentially the path the user typed in, and pathPrefixAbs -- // is the path made absolute from the go.work directory. +-func MyPrintf(format string, args ...interface{}) { +- fmt.Printf(format, args...) +-} +-` +- Run(t, src, func(t *testing.T, env *Env) { +- env.OpenFile("a/a.go") +- env.AfterChange( +- Diagnostics( +- env.AtRegexp("a/a.go", "new.*Printf"), +- WithMessage("format %d has arg \"s\" of wrong type string"), +- ), +- ) +- }) +-} - -- pathPrefixSlash := completingFrom -- pathPrefixAbs := filepath.FromSlash(pathPrefixSlash) -- if !filepath.IsAbs(pathPrefixAbs) { -- pathPrefixAbs = filepath.Join(filepath.Dir(pw.URI.Filename()), pathPrefixAbs) -- } +-// This test ensures that only Analyzers with RunDespiteErrors=true +-// are invoked on a package that would not compile, even if the errors +-// are distant and localized. +-func TestErrorsThatPreventAnalysis(t *testing.T) { +- const src = ` +--- go.mod -- +-module example.com +--- a/a.go -- +-package a - -- // pathPrefixDir is the directory that will be walked to find matches. -- // If pathPrefixSlash is not explicitly a directory boundary (is either equivalent to "." or -- // ends in a separator) we need to examine its parent directory to find sibling files that -- // match. -- depthBound := 5 -- pathPrefixDir, pathPrefixBase := pathPrefixAbs, "" -- pathPrefixSlashDir := pathPrefixSlash -- if filepath.Clean(pathPrefixSlash) != "." && !strings.HasSuffix(pathPrefixSlash, "/") { -- depthBound++ -- pathPrefixDir, pathPrefixBase = filepath.Split(pathPrefixAbs) -- pathPrefixSlashDir = dirNonClean(pathPrefixSlash) -- } +-import "fmt" +-import "sync" +-import _ "example.com/b" - -- var completions []string -- // Stop traversing deeper once we've hit 10k files to try to stay generally under 100ms. -- const numSeenBound = 10000 -- var numSeen int -- stopWalking := errors.New("hit numSeenBound") -- err = filepath.Walk(pathPrefixDir, func(wpath string, info os.FileInfo, err error) error { -- if numSeen > numSeenBound { -- // Stop traversing if we hit bound. -- return stopWalking -- } -- numSeen++ +-func _() { +- // The copylocks analyzer (RunDespiteErrors, FactTypes={}) does run. +- var mu sync.Mutex +- mu2 := mu // copylocks error, reported +- _ = &mu2 - -- // rel is the path relative to pathPrefixDir. -- // Make sure that it has pathPrefixBase as a prefix -- // otherwise it won't match the beginning of the -- // base component of the path the user typed in. -- rel := strings.TrimPrefix(wpath[len(pathPrefixDir):], string(filepath.Separator)) -- if info.IsDir() && wpath != pathPrefixDir && !strings.HasPrefix(rel, pathPrefixBase) { -- return filepath.SkipDir -- } +- // The printf analyzer (!RunDespiteErrors, FactTypes!={}) does not run: +- // (c, printf) failed because of type error in c +- // (b, printf) and (a, printf) do not run because of failed prerequisites. +- fmt.Printf("%d", "s") // printf error, unreported - -- // Check for a match (a module directory). -- if filepath.Base(rel) == "go.mod" { -- relDir := strings.TrimSuffix(dirNonClean(rel), string(os.PathSeparator)) -- completionPath := join(pathPrefixSlashDir, filepath.ToSlash(relDir)) +- // The bools analyzer (!RunDespiteErrors, FactTypes={}) does not run: +- var cond bool +- _ = cond != true && cond != true // bools error, unreported +-} - -- if !strings.HasPrefix(completionPath, completingFrom) { -- return nil -- } -- if strings.HasSuffix(completionPath, "/") { -- // Don't suggest paths that end in "/". This happens -- // when the input is a path that ends in "/" and -- // the completion is empty. -- return nil -- } -- completion := completionPath[len(completingFrom):] -- if completingFrom == "" && !strings.HasPrefix(completion, "./") { -- // Bias towards "./" prefixes. -- completion = join(".", completion) -- } +--- b/b.go -- +-package b - -- completions = append(completions, completion) -- } +-import _ "example.com/c" - -- if depth := strings.Count(rel, string(filepath.Separator)); depth >= depthBound { -- return filepath.SkipDir +--- c/c.go -- +-package c +- +-var _ = 1 / "" // type error +- +-` +- Run(t, src, func(t *testing.T, env *Env) { +- var diags protocol.PublishDiagnosticsParams +- env.OpenFile("a/a.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("a/a.go", "mu2 := (mu)"), WithMessage("assignment copies lock value")), +- ReadDiagnostics("a/a.go", &diags)) +- +- // Assert that there were no other diagnostics. +- // In particular: +- // - "fmt.Printf" does not trigger a [printf] finding; +- // - "cond != true" does not trigger a [bools] finding. +- // +- // We use this check in preference to NoDiagnosticAtRegexp +- // as it is robust in case of minor mistakes in the position +- // regexp, and because it reports unexpected diagnostics. +- if got, want := len(diags.Diagnostics), 1; got != want { +- t.Errorf("got %d diagnostics in a/a.go, want %d:", got, want) +- for i, diag := range diags.Diagnostics { +- t.Logf("Diagnostics[%d] = %+v", i, diag) +- } - } -- return nil - }) -- if err != nil && !errors.Is(err, stopWalking) { -- return nil, fmt.Errorf("walking to find completions: %w", err) -- } +-} - -- sort.Strings(completions) +-// This test demonstrates the deprecated symbol analyzer +-// produces deprecation notices with expected severity and tags. +-func TestDeprecatedAnalysis(t *testing.T) { +- const src = ` +--- go.mod -- +-module example.com +--- a/a.go -- +-package a - -- var items []protocol.CompletionItem -- for _, c := range completions { -- items = append(items, protocol.CompletionItem{ -- Label: c, -- InsertText: c, -- }) -- } -- return &protocol.CompletionList{Items: items}, nil --} +-import "example.com/b" - --// dirNonClean is filepath.Dir, without the Clean at the end. --func dirNonClean(path string) string { -- vol := filepath.VolumeName(path) -- i := len(path) - 1 -- for i >= len(vol) && !os.IsPathSeparator(path[i]) { -- i-- -- } -- return path[len(vol) : i+1] +-func _() { +- new(b.B).Obsolete() // deprecated -} - --func join(a, b string) string { -- if a == "" { -- return b -- } -- if b == "" { -- return a -- } -- return strings.TrimSuffix(a, "/") + "/" + b +--- b/b.go -- +-package b +- +-type B struct{} +- +-// Deprecated: use New instead. +-func (B) Obsolete() {} +- +-func (B) New() {} +-` +- Run(t, src, func(t *testing.T, env *Env) { +- env.OpenFile("a/a.go") +- env.AfterChange( +- Diagnostics( +- env.AtRegexp("a/a.go", "new.*Obsolete"), +- WithMessage("use New instead."), +- WithSeverityTags("deprecated", protocol.SeverityHint, []protocol.DiagnosticTag{protocol.Deprecated}), +- ), +- ) +- }) -} -diff -urN a/gopls/internal/lsp/work/diagnostics.go b/gopls/internal/lsp/work/diagnostics.go ---- a/gopls/internal/lsp/work/diagnostics.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/work/diagnostics.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,92 +0,0 @@ --// Copyright 2022 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/regtest/diagnostics/golist_test.go b/gopls/internal/regtest/diagnostics/golist_test.go +--- a/gopls/internal/regtest/diagnostics/golist_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/diagnostics/golist_test.go 1970-01-01 08:00:00 +@@ -1,71 +0,0 @@ +-// Copyright 2023 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 work +-package diagnostics - -import ( -- "context" -- "fmt" -- "os" -- "path/filepath" +- "testing" - -- "golang.org/x/mod/modfile" -- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" - "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/span" -- "golang.org/x/tools/internal/event" +- "golang.org/x/tools/internal/testenv" -) - --func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[span.URI][]*source.Diagnostic, error) { -- ctx, done := event.Start(ctx, "work.Diagnostics", source.SnapshotLabels(snapshot)...) -- defer done() -- -- reports := map[span.URI][]*source.Diagnostic{} -- uri := snapshot.WorkFile() -- if uri == "" { -- return nil, nil -- } -- fh, err := snapshot.GetFile(ctx, uri) -- if err != nil { -- return nil, err -- } -- reports[fh.URI()] = []*source.Diagnostic{} -- diagnostics, err := DiagnosticsForWork(ctx, snapshot, fh) -- if err != nil { -- return nil, err -- } -- for _, d := range diagnostics { -- fh, err := snapshot.GetFile(ctx, d.URI) -- if err != nil { -- return nil, err -- } -- reports[fh.URI()] = append(reports[fh.URI()], d) -- } -- -- return reports, nil --} -- --func DiagnosticsForWork(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]*source.Diagnostic, error) { -- pw, err := snapshot.ParseWork(ctx, fh) -- if err != nil { -- if pw == nil || len(pw.ParseErrors) == 0 { -- return nil, err -- } -- return pw.ParseErrors, nil -- } +-func TestGoListErrors(t *testing.T) { +- testenv.NeedsTool(t, "cgo") - -- // Add diagnostic if a directory does not contain a module. -- var diagnostics []*source.Diagnostic -- for _, use := range pw.File.Use { -- rng, err := pw.Mapper.OffsetRange(use.Syntax.Start.Byte, use.Syntax.End.Byte) -- if err != nil { -- return nil, err -- } +- const src = ` +--- go.mod -- +-module a.com - -- modfh, err := snapshot.GetFile(ctx, modFileURI(pw, use)) -- if err != nil { -- return nil, err -- } -- if _, err := modfh.Read(); err != nil && os.IsNotExist(err) { -- diagnostics = append(diagnostics, &source.Diagnostic{ -- URI: fh.URI(), -- Range: rng, -- Severity: protocol.SeverityError, -- Source: source.WorkFileError, -- Message: fmt.Sprintf("directory %v does not contain a module", use.Path), -- }) -- } -- } -- return diagnostics, nil --} +-go 1.18 +--- a/a.go -- +-package a - --func modFileURI(pw *source.ParsedWorkFile, use *modfile.Use) span.URI { -- workdir := filepath.Dir(pw.URI.Filename()) +-import +--- c/c.go -- +-package c - -- modroot := filepath.FromSlash(use.Path) -- if !filepath.IsAbs(modroot) { -- modroot = filepath.Join(workdir, modroot) -- } +-/* +-int fortythree() { return 42; } +-*/ +-import "C" - -- return span.URIFromPath(filepath.Join(modroot, "go.mod")) +-func Foo() { +- print(C.fortytwo()) -} -diff -urN a/gopls/internal/lsp/work/format.go b/gopls/internal/lsp/work/format.go ---- a/gopls/internal/lsp/work/format.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/work/format.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,28 +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. +--- p/p.go -- +-package p - --package work +-import "a.com/q" - --import ( -- "context" +-const P = q.Q + 1 +--- q/q.go -- +-package q - -- "golang.org/x/mod/modfile" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/internal/event" --) +-import "a.com/p" - --func Format(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]protocol.TextEdit, error) { -- ctx, done := event.Start(ctx, "work.Format") -- defer done() +-const Q = p.P + 1 +-` - -- pw, err := snapshot.ParseWork(ctx, fh) -- if err != nil { -- return nil, err -- } -- formatted := modfile.Format(pw.File.Syntax) -- // Calculate the edits to be made due to the change. -- diffs := snapshot.View().Options().ComputeEdits(string(pw.Mapper.Content), string(formatted)) -- return source.ToProtocolEdits(pw.Mapper, diffs) +- Run(t, src, func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- Diagnostics( +- env.AtRegexp("a/a.go", "import\n()"), +- FromSource(string(source.ParseError)), +- ), +- Diagnostics( +- AtPosition("c/c.go", 0, 0), +- FromSource(string(source.ListError)), +- WithMessage("may indicate failure to perform cgo processing"), +- ), +- Diagnostics( +- env.AtRegexp("p/p.go", `"a.com/q"`), +- FromSource(string(source.ListError)), +- WithMessage("import cycle not allowed"), +- ), +- ) +- }) -} -diff -urN a/gopls/internal/lsp/work/hover.go b/gopls/internal/lsp/work/hover.go ---- a/gopls/internal/lsp/work/hover.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/work/hover.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,89 +0,0 @@ +diff -urN a/gopls/internal/regtest/diagnostics/invalidation_test.go b/gopls/internal/regtest/diagnostics/invalidation_test.go +--- a/gopls/internal/regtest/diagnostics/invalidation_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/diagnostics/invalidation_test.go 1970-01-01 08:00:00 +@@ -1,111 +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 work +-package diagnostics - -import ( -- "bytes" -- "context" - "fmt" +- "testing" - -- "golang.org/x/mod/modfile" - "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/internal/event" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" -) - --func Hover(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle, position protocol.Position) (*protocol.Hover, error) { -- // We only provide hover information for the view's go.work file. -- if fh.URI() != snapshot.WorkFile() { -- return nil, nil -- } +-// Test for golang/go#50267: diagnostics should be re-sent after a file is +-// opened. +-func TestDiagnosticsAreResentAfterCloseOrOpen(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - -- ctx, done := event.Start(ctx, "work.Hover") -- defer done() +-go 1.16 +--- main.go -- +-package main - -- // Get the position of the cursor. -- pw, err := snapshot.ParseWork(ctx, fh) -- if err != nil { -- return nil, fmt.Errorf("getting go.work file handle: %w", err) -- } -- offset, err := pw.Mapper.PositionOffset(position) -- if err != nil { -- return nil, fmt.Errorf("computing cursor offset: %w", err) -- } +-func _() { +- x := 2 +-} +-` +- Run(t, files, func(_ *testing.T, env *Env) { // Create a new workspace-level directory and empty file. +- env.OpenFile("main.go") +- var afterOpen protocol.PublishDiagnosticsParams +- env.AfterChange( +- ReadDiagnostics("main.go", &afterOpen), +- ) +- env.CloseBuffer("main.go") +- var afterClose protocol.PublishDiagnosticsParams +- env.AfterChange( +- ReadDiagnostics("main.go", &afterClose), +- ) +- if afterOpen.Version == afterClose.Version { +- t.Errorf("publishDiagnostics: got the same version after closing (%d) as after opening", afterOpen.Version) +- } +- env.OpenFile("main.go") +- var afterReopen protocol.PublishDiagnosticsParams +- env.AfterChange( +- ReadDiagnostics("main.go", &afterReopen), +- ) +- if afterReopen.Version == afterClose.Version { +- t.Errorf("pubslishDiagnostics: got the same version after reopening (%d) as after closing", afterClose.Version) +- } +- }) +-} - -- // Confirm that the cursor is inside a use statement, and then find -- // the position of the use statement's directory path. -- use, pathStart, pathEnd := usePath(pw, offset) +-// Test for the "chattyDiagnostics" setting: we should get re-published +-// diagnostics after every file change, even if diagnostics did not change. +-func TestChattyDiagnostics(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.com - -- // The cursor position is not on a use statement. -- if use == nil { -- return nil, nil -- } +-go 1.16 +--- main.go -- +-package main - -- // Get the mod file denoted by the use. -- modfh, err := snapshot.GetFile(ctx, modFileURI(pw, use)) -- if err != nil { -- return nil, fmt.Errorf("getting modfile handle: %w", err) -- } -- pm, err := snapshot.ParseMod(ctx, modfh) -- if err != nil { -- return nil, fmt.Errorf("getting modfile handle: %w", err) -- } -- mod := pm.File.Module.Mod +-func _() { +- x := 2 +-} - -- // Get the range to highlight for the hover. -- rng, err := pw.Mapper.OffsetRange(pathStart, pathEnd) -- if err != nil { -- return nil, err -- } -- options := snapshot.View().Options() -- return &protocol.Hover{ -- Contents: protocol.MarkupContent{ -- Kind: options.PreferredContentFormat, -- Value: mod.Path, +-// Irrelevant comment #0 +-` +- +- WithOptions( +- Settings{ +- "chattyDiagnostics": true, - }, -- Range: rng, -- }, nil --} +- ).Run(t, files, func(_ *testing.T, env *Env) { // Create a new workspace-level directory and empty file. +- +- env.OpenFile("main.go") +- var d protocol.PublishDiagnosticsParams +- env.AfterChange( +- ReadDiagnostics("main.go", &d), +- ) - --func usePath(pw *source.ParsedWorkFile, offset int) (use *modfile.Use, pathStart, pathEnd int) { -- for _, u := range pw.File.Use { -- path := []byte(u.Path) -- s, e := u.Syntax.Start.Byte, u.Syntax.End.Byte -- i := bytes.Index(pw.Mapper.Content[s:e], path) -- if i == -1 { -- // This should not happen. -- continue +- if len(d.Diagnostics) != 1 { +- t.Fatalf("len(Diagnostics) = %d, want 1", len(d.Diagnostics)) - } -- // Shift the start position to the location of the -- // module directory within the use statement. -- pathStart, pathEnd = s+i, s+i+len(path) -- if pathStart <= offset && offset <= pathEnd { -- return u, pathStart, pathEnd +- msg := d.Diagnostics[0].Message +- +- for i := 0; i < 5; i++ { +- before := d.Version +- env.RegexpReplace("main.go", "Irrelevant comment #.", fmt.Sprintf("Irrelevant comment #%d", i)) +- env.AfterChange( +- ReadDiagnostics("main.go", &d), +- ) +- +- if d.Version == before { +- t.Errorf("after change, got version %d, want new version", d.Version) +- } +- +- // As a sanity check, make sure we have the same diagnostic. +- if len(d.Diagnostics) != 1 { +- t.Fatalf("len(Diagnostics) = %d, want 1", len(d.Diagnostics)) +- } +- newMsg := d.Diagnostics[0].Message +- if newMsg != msg { +- t.Errorf("after change, got message %q, want %q", newMsg, msg) +- } - } -- } -- return nil, 0, 0 +- }) -} -diff -urN a/gopls/internal/lsp/workspace.go b/gopls/internal/lsp/workspace.go ---- a/gopls/internal/lsp/workspace.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/workspace.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,95 +0,0 @@ --// Copyright 2019 The Go Authors. All rights reserved. +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 08:00:00 +@@ -1,73 +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 lsp +-package diagnostics - -import ( -- "context" -- "fmt" +- "testing" - - "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/gopls/internal/span" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" -) - --func (s *Server) didChangeWorkspaceFolders(ctx context.Context, params *protocol.DidChangeWorkspaceFoldersParams) error { -- event := params.Event -- for _, folder := range event.Removed { -- view := s.session.View(folder.Name) -- if view != nil { -- s.session.RemoveView(view) -- } else { -- return fmt.Errorf("view %s for %v not found", folder.Name, folder.URI) -- } -- } -- return s.addFolders(ctx, event.Added) --} +-func TestUndeclaredDiagnostics(t *testing.T) { +- src := ` +--- go.mod -- +-module mod.com - --// addView returns a Snapshot and a release function that must be --// called when it is no longer needed. --func (s *Server) addView(ctx context.Context, name string, uri span.URI) (source.Snapshot, func(), error) { -- s.stateMu.Lock() -- state := s.state -- s.stateMu.Unlock() -- if state < serverInitialized { -- return nil, nil, fmt.Errorf("addView called before server initialized") -- } -- options := s.session.Options().Clone() -- if err := s.fetchConfig(ctx, name, uri, options); err != nil { -- return nil, nil, err -- } -- _, snapshot, release, err := s.session.NewView(ctx, name, uri, options) -- return snapshot, release, err +-go 1.12 +--- a/a.go -- +-package a +- +-func _() int { +- return x -} +--- b/b.go -- +-package b - --func (s *Server) didChangeConfiguration(ctx context.Context, _ *protocol.DidChangeConfigurationParams) error { -- // Apply any changes to the session-level settings. -- options := s.session.Options().Clone() -- if err := s.fetchConfig(ctx, "", "", options); err != nil { -- return err -- } -- s.session.SetOptions(options) +-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 +- } - -- // Go through each view, getting and updating its configuration. -- for _, view := range s.session.Views() { -- options := s.session.Options().Clone() -- if err := s.fetchConfig(ctx, view.Name(), view.Folder(), options); err != nil { -- return err +- // 'x' is undeclared, but still necessary. +- env.OpenFile("a/a.go") +- var adiags protocol.PublishDiagnosticsParams +- env.AfterChange( +- Diagnostics(env.AtRegexp("a/a.go", "x")), +- ReadDiagnostics("a/a.go", &adiags), +- ) +- if got := len(adiags.Diagnostics); got != 1 { +- t.Errorf("len(Diagnostics) = %d, want 1", got) - } -- view, err := s.session.SetViewOptions(ctx, view, options) -- if err != nil { -- return err +- if diag := adiags.Diagnostics[0]; isUnnecessary(diag) { +- t.Errorf("%v tagged unnecessary, want necessary", diag) - } -- go func() { -- snapshot, release, err := view.Snapshot() -- if err != nil { -- return // view is shut down; no need to diagnose -- } -- defer release() -- s.diagnoseDetached(snapshot) -- }() -- } -- -- // An options change may have affected the detected Go version. -- s.checkViewGoVersions() - -- return nil --} -- --func semanticTokenRegistration(tokenTypes, tokenModifiers []string) protocol.Registration { -- return protocol.Registration{ -- ID: "textDocument/semanticTokens", -- Method: "textDocument/semanticTokens", -- RegisterOptions: &protocol.SemanticTokensOptions{ -- Legend: protocol.SemanticTokensLegend{ -- // TODO(pjw): trim these to what we use (and an unused one -- // at position 0 of TokTypes, to catch typos) -- TokenTypes: tokenTypes, -- TokenModifiers: tokenModifiers, -- }, -- Full: &protocol.Or_SemanticTokensOptions_full{Value: true}, -- Range: &protocol.Or_SemanticTokensOptions_range{Value: true}, -- }, -- } +- // 'y = y' is pointless, and should be detected as unnecessary. +- env.OpenFile("b/b.go") +- var bdiags protocol.PublishDiagnosticsParams +- env.AfterChange( +- Diagnostics(env.AtRegexp("b/b.go", "y = y")), +- ReadDiagnostics("b/b.go", &bdiags), +- ) +- if got := len(bdiags.Diagnostics); got != 1 { +- t.Errorf("len(Diagnostics) = %d, want 1", got) +- } +- if diag := bdiags.Diagnostics[0]; !isUnnecessary(diag) { +- t.Errorf("%v tagged necessary, want unnecessary", diag) +- } +- }) -} -diff -urN a/gopls/internal/lsp/workspace_symbol.go b/gopls/internal/lsp/workspace_symbol.go ---- a/gopls/internal/lsp/workspace_symbol.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/lsp/workspace_symbol.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,32 +0,0 @@ --// Copyright 2020 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/regtest/inlayhints/inlayhints_test.go b/gopls/internal/regtest/inlayhints/inlayhints_test.go +--- a/gopls/internal/regtest/inlayhints/inlayhints_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/inlayhints/inlayhints_test.go 1970-01-01 08:00:00 +@@ -1,69 +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 lsp +-package inlayhint - -import ( -- "context" +- "testing" - -- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/hooks" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" - "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/internal/event" -) - --func (s *Server) symbol(ctx context.Context, params *protocol.WorkspaceSymbolParams) ([]protocol.SymbolInformation, error) { -- ctx, done := event.Start(ctx, "lsp.Server.symbol") -- defer done() +-func TestMain(m *testing.M) { +- bug.PanicOnBugs = true +- Main(m, hooks.Options) +-} - -- views := s.session.Views() -- matcher := s.session.Options().SymbolMatcher -- style := s.session.Options().SymbolStyle -- // TODO(rfindley): it looks wrong that we need to pass views here. -- // -- // Evidence: -- // - this is the only place we convert views to []source.View -- // - workspace symbols is the only place where we call source.View.Snapshot -- var sourceViews []source.View -- for _, v := range views { -- sourceViews = append(sourceViews, v) +-func TestEnablingInlayHints(t *testing.T) { +- const workspace = ` +--- go.mod -- +-module inlayHint.test +-go 1.12 +--- lib.go -- +-package lib +-type Number int +-const ( +- Zero Number = iota +- One +- Two +-) +-` +- tests := []struct { +- label string +- enabled map[string]bool +- wantInlayHint bool +- }{ +- { +- label: "default", +- wantInlayHint: false, +- }, +- { +- label: "enable const", +- enabled: map[string]bool{source.ConstantValues: true}, +- wantInlayHint: true, +- }, +- { +- label: "enable parameter names", +- enabled: map[string]bool{source.ParameterNames: true}, +- wantInlayHint: false, +- }, +- } +- for _, test := range tests { +- t.Run(test.label, func(t *testing.T) { +- WithOptions( +- Settings{ +- "hints": test.enabled, +- }, +- ).Run(t, workspace, func(t *testing.T, env *Env) { +- env.OpenFile("lib.go") +- lens := env.InlayHints("lib.go") +- if gotInlayHint := len(lens) > 0; gotInlayHint != test.wantInlayHint { +- t.Errorf("got inlayHint: %t, want %t", gotInlayHint, test.wantInlayHint) +- } +- }) +- }) - } -- return source.WorkspaceSymbols(ctx, matcher, style, sourceViews, params.Query) -} -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 00:00:00.000000000 +0000 -@@ -1,249 +0,0 @@ --// Copyright 2020 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/regtest/marker/marker_test.go b/gopls/internal/regtest/marker/marker_test.go +--- a/gopls/internal/regtest/marker/marker_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/marker_test.go 1970-01-01 08:00:00 +@@ -1,30 +0,0 @@ +-// Copyright 2023 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 +-package marker - -import ( -- "context" -- "flag" -- "fmt" -- "io/ioutil" -- "log" - "os" -- "os/exec" -- "path/filepath" -- "sync" - "testing" -- "time" -- -- "golang.org/x/tools/gopls/internal/hooks" -- "golang.org/x/tools/gopls/internal/lsp/cmd" -- "golang.org/x/tools/gopls/internal/lsp/fake" -- "golang.org/x/tools/internal/bug" -- "golang.org/x/tools/internal/event" -- "golang.org/x/tools/internal/fakenet" -- "golang.org/x/tools/internal/jsonrpc2" -- "golang.org/x/tools/internal/jsonrpc2/servertest" -- "golang.org/x/tools/internal/tool" - +- "golang.org/x/tools/gopls/internal/bug" - . "golang.org/x/tools/gopls/internal/lsp/regtest" +- "golang.org/x/tools/internal/testenv" -) - --var ( -- goplsPath = flag.String("gopls_path", "", "if set, use this gopls for testing; incompatible with -gopls_commit") +-func TestMain(m *testing.M) { +- bug.PanicOnBugs = true +- testenv.ExitIfSmallMachine() +- os.Exit(m.Run()) +-} - -- installGoplsOnce sync.Once // guards installing gopls at -gopls_commit -- goplsCommit = flag.String("gopls_commit", "", "if set, install and use gopls at this commit for testing; incompatible with -gopls_path") +-// Note: we use a separate package for the marker tests so that we can easily +-// compare their performance to the existing marker tests in ./internal/lsp. - -- cpuProfile = flag.String("gopls_cpuprofile", "", "if set, the cpu profile file suffix; see \"Profiling\" in the package doc") -- memProfile = flag.String("gopls_memprofile", "", "if set, the mem profile file suffix; see \"Profiling\" in the package doc") -- trace = flag.String("gopls_trace", "", "if set, the trace file suffix; see \"Profiling\" in the package doc") +-// TestMarkers runs the marker tests from the testdata directory. +-// +-// See RunMarkerTests for details on how marker tests work. +-func TestMarkers(t *testing.T) { +- RunMarkerTests(t, "testdata") +-} +diff -urN a/gopls/internal/regtest/marker/testdata/codeaction/functionextraction.txt b/gopls/internal/regtest/marker/testdata/codeaction/functionextraction.txt +--- a/gopls/internal/regtest/marker/testdata/codeaction/functionextraction.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/codeaction/functionextraction.txt 1970-01-01 08:00:00 +@@ -1,583 +0,0 @@ +-This test verifies various behaviors of function extraction. - -- // If non-empty, tempDir is a temporary working dir that was created by this -- // test suite. -- makeTempDirOnce sync.Once // guards creation of the temp dir -- tempDir string --) +--- go.mod -- +-module mod.test/extract - --// if runAsGopls is "true", run the gopls command instead of the testing.M. --const runAsGopls = "_GOPLS_BENCH_RUN_AS_GOPLS" +-go 1.18 - --func TestMain(m *testing.M) { -- bug.PanicOnBugs = true -- if os.Getenv(runAsGopls) == "true" { -- tool.Main(context.Background(), cmd.New("gopls", "", nil, hooks.Options), os.Args[1:]) -- os.Exit(0) -- } -- event.SetExporter(nil) // don't log to stderr -- code := m.Run() -- if err := cleanup(); err != nil { -- fmt.Fprintf(os.Stderr, "cleaning up after benchmarks: %v\n", err) -- if code == 0 { -- code = 1 -- } -- } -- os.Exit(code) +--- basic.go -- +-package extract +- +-func _() { //@codeaction("refactor.extract", "{", closeBracket, outer) +- a := 1 //@codeaction("refactor.extract", "a", end, inner) +- _ = a + 4 //@loc(end, "4") +-} //@loc(closeBracket, "}") +- +--- @inner/basic.go -- +-package extract +- +-func _() { //@codeaction("refactor.extract", "{", closeBracket, outer) +- //@codeaction("refactor.extract", "a", end, inner) +- newFunction() //@loc(end, "4") -} - --// getTempDir returns the temporary directory to use for benchmark files, --// creating it if necessary. --func getTempDir() string { -- makeTempDirOnce.Do(func() { -- var err error -- tempDir, err = ioutil.TempDir("", "gopls-bench") -- if err != nil { -- log.Fatal(err) -- } -- }) -- return tempDir +-func newFunction() { +- a := 1 +- _ = a + 4 +-} //@loc(closeBracket, "}") +- +--- @outer/basic.go -- +-package extract +- +-func _() { //@codeaction("refactor.extract", "{", closeBracket, outer) +- //@codeaction("refactor.extract", "a", end, inner) +- newFunction() //@loc(end, "4") -} - --// shallowClone performs a shallow clone of repo into dir at the given --// 'commitish' ref (any commit reference understood by git). --// --// The directory dir must not already exist. --func shallowClone(dir, repo, commitish string) error { -- if err := os.Mkdir(dir, 0750); err != nil { -- return fmt.Errorf("creating dir for %s: %v", repo, err) -- } +-func newFunction() { +- a := 1 +- _ = a + 4 +-} //@loc(closeBracket, "}") - -- // Set a timeout for git fetch. If this proves flaky, it can be removed. -- ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) -- defer cancel() +--- return.go -- +-package extract - -- // Use a shallow fetch to download just the relevant commit. -- shInit := fmt.Sprintf("git init && git fetch --depth=1 %q %q && git checkout FETCH_HEAD", repo, commitish) -- initCmd := exec.CommandContext(ctx, "/bin/sh", "-c", shInit) -- initCmd.Dir = dir -- if output, err := initCmd.CombinedOutput(); err != nil { -- return fmt.Errorf("checking out %s: %v\n%s", repo, err, output) -- } -- return nil +-func _() bool { +- x := 1 +- if x == 0 { //@codeaction("refactor.extract", "if", ifend, return) +- return true +- } //@loc(ifend, "}") +- return false -} - --// connectEditor connects a fake editor session in the given dir, using the --// given editor config. --func connectEditor(dir string, config fake.EditorConfig, ts servertest.Connector) (*fake.Sandbox, *fake.Editor, *Awaiter, error) { -- s, err := fake.NewSandbox(&fake.SandboxConfig{ -- Workdir: dir, -- GOPROXY: "https://proxy.golang.org", -- }) -- if err != nil { -- return nil, nil, nil, err +--- @return/return.go -- +-package extract +- +-func _() bool { +- x := 1 +- //@codeaction("refactor.extract", "if", ifend, return) +- shouldReturn, returnValue := newFunction(x) +- if shouldReturn { +- return returnValue +- } //@loc(ifend, "}") +- return false +-} +- +-func newFunction(x int) (bool, bool) { +- if x == 0 { +- return true, true - } +- return false, false +-} - -- a := NewAwaiter(s.Workdir) -- const skipApplyEdits = false -- editor, err := fake.NewEditor(s, config).Connect(context.Background(), ts, a.Hooks(), skipApplyEdits) -- if err != nil { -- return nil, nil, nil, err +--- return_nonnested.go -- +-package extract +- +-func _() bool { +- x := 1 //@codeaction("refactor.extract", "x", rnnEnd, rnn) +- if x == 0 { +- return true - } +- return false //@loc(rnnEnd, "false") +-} - -- return s, editor, a, nil +--- @rnn/return_nonnested.go -- +-package extract +- +-func _() bool { +- //@codeaction("refactor.extract", "x", rnnEnd, rnn) +- return newFunction() //@loc(rnnEnd, "false") -} - --// newGoplsServer returns a connector that connects to a new gopls process. --func newGoplsServer(name string) (servertest.Connector, error) { -- if *goplsPath != "" && *goplsCommit != "" { -- panic("can't set both -gopls_path and -gopls_commit") -- } -- var ( -- goplsPath = *goplsPath -- env []string -- ) -- if *goplsCommit != "" { -- goplsPath = getInstalledGopls() -- } -- if goplsPath == "" { -- var err error -- goplsPath, err = os.Executable() -- if err != nil { -- return nil, err -- } -- env = []string{fmt.Sprintf("%s=true", runAsGopls)} -- } -- var args []string -- if *cpuProfile != "" { -- args = append(args, fmt.Sprintf("-profile.cpu=%s", name+"."+*cpuProfile)) -- } -- if *memProfile != "" { -- args = append(args, fmt.Sprintf("-profile.mem=%s", name+"."+*memProfile)) -- } -- if *trace != "" { -- args = append(args, fmt.Sprintf("-profile.trace=%s", name+"."+*trace)) +-func newFunction() bool { +- x := 1 +- if x == 0 { +- return true - } -- return &SidecarServer{ -- goplsPath: goplsPath, -- env: env, -- args: args, -- }, nil +- return false -} - --// getInstalledGopls builds gopls at the given -gopls_commit, returning the --// path to the gopls binary. --func getInstalledGopls() string { -- if *goplsCommit == "" { -- panic("must provide -gopls_commit") -- } -- toolsDir := filepath.Join(getTempDir(), "gopls_build") -- goplsPath := filepath.Join(toolsDir, "gopls", "gopls") +--- return_complex.go -- +-package extract - -- installGoplsOnce.Do(func() { -- log.Printf("installing gopls: checking out x/tools@%s into %s\n", *goplsCommit, toolsDir) -- if err := shallowClone(toolsDir, "https://go.googlesource.com/tools", *goplsCommit); err != nil { -- log.Fatal(err) -- } +-import "fmt" - -- log.Println("installing gopls: building...") -- bld := exec.Command("go", "build", ".") -- bld.Dir = filepath.Join(toolsDir, "gopls") -- if output, err := bld.CombinedOutput(); err != nil { -- log.Fatalf("building gopls: %v\n%s", err, output) -- } +-func _() (int, string, error) { +- x := 1 +- y := "hello" +- z := "bye" //@codeaction("refactor.extract", "z", rcEnd, rc) +- if y == z { +- return x, y, fmt.Errorf("same") +- } else if false { +- z = "hi" +- return x, z, nil +- } //@loc(rcEnd, "}") +- return x, z, nil +-} - -- // Confirm that the resulting path now exists. -- if _, err := os.Stat(goplsPath); err != nil { -- log.Fatalf("os.Stat(%s): %v", goplsPath, err) -- } -- }) -- return goplsPath +--- @rc/return_complex.go -- +-package extract +- +-import "fmt" +- +-func _() (int, string, error) { +- x := 1 +- y := "hello" +- //@codeaction("refactor.extract", "z", rcEnd, rc) +- z, shouldReturn, returnValue, returnValue1, returnValue2 := newFunction(y, x) +- if shouldReturn { +- return returnValue, returnValue1, returnValue2 +- } //@loc(rcEnd, "}") +- return x, z, nil -} - --// A SidecarServer starts (and connects to) a separate gopls process at the --// given path. --type SidecarServer struct { -- goplsPath string -- env []string // additional environment bindings -- args []string // command-line arguments +-func newFunction(y string, x int) (string, bool, int, string, error) { +- z := "bye" +- if y == z { +- return "", true, x, y, fmt.Errorf("same") +- } else if false { +- z = "hi" +- return "", true, x, z, nil +- } +- return z, false, 0, "", nil -} - --// Connect creates new io.Pipes and binds them to the underlying StreamServer. --// --// It implements the servertest.Connector interface. --func (s *SidecarServer) Connect(ctx context.Context) jsonrpc2.Conn { -- // Note: don't use CommandContext here, as we want gopls to exit gracefully -- // in order to write out profile data. -- // -- // We close the connection on context cancelation below. -- cmd := exec.Command(s.goplsPath, s.args...) +--- return_complex_nonnested.go -- +-package extract - -- stdin, err := cmd.StdinPipe() -- if err != nil { -- log.Fatal(err) -- } -- stdout, err := cmd.StdoutPipe() -- if err != nil { -- log.Fatal(err) -- } -- cmd.Stderr = os.Stderr -- cmd.Env = append(os.Environ(), s.env...) -- if err := cmd.Start(); err != nil { -- log.Fatalf("starting gopls: %v", err) +-import "fmt" +- +-func _() (int, string, error) { +- x := 1 +- y := "hello" +- z := "bye" //@codeaction("refactor.extract", "z", rcnnEnd, rcnn) +- if y == z { +- return x, y, fmt.Errorf("same") +- } else if false { +- z = "hi" +- return x, z, nil - } +- return x, z, nil //@loc(rcnnEnd, "nil") +-} - -- go func() { -- // If we don't log.Fatal here, benchmarks may hang indefinitely if gopls -- // exits abnormally. -- // -- // TODO(rfindley): ideally we would shut down the connection gracefully, -- // but that doesn't currently work. -- if err := cmd.Wait(); err != nil { -- log.Fatalf("gopls invocation failed with error: %v", err) -- } -- }() +--- @rcnn/return_complex_nonnested.go -- +-package extract - -- clientStream := jsonrpc2.NewHeaderStream(fakenet.NewConn("stdio", stdout, stdin)) -- clientConn := jsonrpc2.NewConn(clientStream) +-import "fmt" - -- go func() { -- select { -- case <-ctx.Done(): -- clientConn.Close() -- clientStream.Close() -- case <-clientConn.Done(): -- } -- }() +-func _() (int, string, error) { +- x := 1 +- y := "hello" +- //@codeaction("refactor.extract", "z", rcnnEnd, rcnn) +- return newFunction(y, x) //@loc(rcnnEnd, "nil") +-} - -- return clientConn +-func newFunction(y string, x int) (int, string, error) { +- z := "bye" +- if y == z { +- return x, y, fmt.Errorf("same") +- } else if false { +- z = "hi" +- return x, z, nil +- } +- return x, z, nil -} -diff -urN a/gopls/internal/regtest/bench/completion_test.go b/gopls/internal/regtest/bench/completion_test.go ---- a/gopls/internal/regtest/bench/completion_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/completion_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,173 +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 +--- return_func_lit.go -- +-package extract - --import ( -- "fmt" -- "testing" +-import "go/ast" - -- "golang.org/x/tools/gopls/internal/lsp/fake" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" --) +-func _() { +- ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool { +- if n == nil { //@codeaction("refactor.extract", "if", rflEnd, rfl) +- return true +- } //@loc(rflEnd, "}") +- return false +- }) +-} - --// TODO(rfindley): update these completion tests to run on multiple repos. +--- @rfl/return_func_lit.go -- +-package extract - --type completionBenchOptions struct { -- file, locationRegexp string +-import "go/ast" - -- // Hooks to run edits before initial completion -- setup func(*Env) // run before the benchmark starts -- beforeCompletion func(*Env) // run before each completion +-func _() { +- ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool { +- //@codeaction("refactor.extract", "if", rflEnd, rfl) +- shouldReturn, returnValue := newFunction(n) +- if shouldReturn { +- return returnValue +- } //@loc(rflEnd, "}") +- return false +- }) -} - --func benchmarkCompletion(options completionBenchOptions, b *testing.B) { -- repo := getRepo(b, "tools") -- env := repo.newEnv(b, "completion.tools", fake.EditorConfig{}) -- defer env.Close() -- -- // Run edits required for this completion. -- if options.setup != nil { -- options.setup(env) +-func newFunction(n ast.Node) (bool, bool) { +- if n == nil { +- return true, true - } +- return false, false +-} - -- // Run a completion to make sure the system is warm. -- loc := env.RegexpSearch(options.file, options.locationRegexp) -- completions := env.Completion(loc) +--- return_func_lit_nonnested.go -- +-package extract - -- if testing.Verbose() { -- fmt.Println("Results:") -- for i := 0; i < len(completions.Items); i++ { -- fmt.Printf("\t%d. %v\n", i, completions.Items[i]) -- } -- } +-import "go/ast" - -- b.Run("tools", func(b *testing.B) { -- for i := 0; i < b.N; i++ { -- if options.beforeCompletion != nil { -- options.beforeCompletion(env) -- } -- env.Completion(loc) +-func _() { +- ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool { +- if n == nil { //@codeaction("refactor.extract", "if", rflnnEnd, rflnn) +- return true - } +- return false //@loc(rflnnEnd, "false") - }) -} - --// endRangeInBuffer returns the position for last character in the buffer for --// the given file. --func endRangeInBuffer(env *Env, name string) protocol.Range { -- buffer := env.BufferText(name) -- m := protocol.NewMapper("", []byte(buffer)) -- rng, err := m.OffsetRange(len(buffer), len(buffer)) -- if err != nil { -- env.T.Fatal(err) +--- @rflnn/return_func_lit_nonnested.go -- +-package extract +- +-import "go/ast" +- +-func _() { +- ast.Inspect(ast.NewIdent("a"), func(n ast.Node) bool { +- //@codeaction("refactor.extract", "if", rflnnEnd, rflnn) +- return newFunction(n) //@loc(rflnnEnd, "false") +- }) +-} +- +-func newFunction(n ast.Node) bool { +- if n == nil { +- return true - } -- return rng +- return false -} - --// Benchmark struct completion in tools codebase. --func BenchmarkStructCompletion(b *testing.B) { -- file := "internal/lsp/cache/session.go" +--- return_init.go -- +-package extract - -- setup := func(env *Env) { -- env.OpenFile(file) -- env.EditBuffer(file, protocol.TextEdit{ -- Range: endRangeInBuffer(env, file), -- NewText: "\nvar testVariable map[string]bool = Session{}.\n", -- }) -- } +-func _() string { +- x := 1 +- if x == 0 { //@codeaction("refactor.extract", "if", riEnd, ri) +- x = 3 +- return "a" +- } //@loc(riEnd, "}") +- x = 2 +- return "b" +-} - -- benchmarkCompletion(completionBenchOptions{ -- file: file, -- locationRegexp: `var testVariable map\[string\]bool = Session{}(\.)`, -- setup: setup, -- }, b) +--- @ri/return_init.go -- +-package extract +- +-func _() string { +- x := 1 +- //@codeaction("refactor.extract", "if", riEnd, ri) +- shouldReturn, returnValue := newFunction(x) +- if shouldReturn { +- return returnValue +- } //@loc(riEnd, "}") +- x = 2 +- return "b" -} - --// Benchmark import completion in tools codebase. --func BenchmarkImportCompletion(b *testing.B) { -- const file = "internal/lsp/source/completion/completion.go" -- benchmarkCompletion(completionBenchOptions{ -- file: file, -- locationRegexp: `go\/()`, -- setup: func(env *Env) { env.OpenFile(file) }, -- }, b) +-func newFunction(x int) (bool, string) { +- if x == 0 { +- x = 3 +- return true, "a" +- } +- return false, "" -} - --// Benchmark slice completion in tools codebase. --func BenchmarkSliceCompletion(b *testing.B) { -- file := "internal/lsp/cache/session.go" +--- return_init_nonnested.go -- +-package extract - -- setup := func(env *Env) { -- env.OpenFile(file) -- env.EditBuffer(file, protocol.TextEdit{ -- Range: endRangeInBuffer(env, file), -- NewText: "\nvar testVariable []byte = \n", -- }) +-func _() string { +- x := 1 +- if x == 0 { //@codeaction("refactor.extract", "if", rinnEnd, rinn) +- x = 3 +- return "a" - } -- -- benchmarkCompletion(completionBenchOptions{ -- file: file, -- locationRegexp: `var testVariable \[\]byte (=)`, -- setup: setup, -- }, b) +- x = 2 +- return "b" //@loc(rinnEnd, "\"b\"") -} - --// Benchmark deep completion in function call in tools codebase. --func BenchmarkFuncDeepCompletion(b *testing.B) { -- file := "internal/lsp/source/completion/completion.go" -- fileContent := ` --func (c *completer) _() { -- c.inference.kindMatches(c.) +--- @rinn/return_init_nonnested.go -- +-package extract +- +-func _() string { +- x := 1 +- //@codeaction("refactor.extract", "if", rinnEnd, rinn) +- return newFunction(x) //@loc(rinnEnd, "\"b\"") -} --` -- setup := func(env *Env) { -- env.OpenFile(file) -- originalBuffer := env.BufferText(file) -- env.EditBuffer(file, protocol.TextEdit{ -- Range: endRangeInBuffer(env, file), -- NewText: originalBuffer + fileContent, -- }) -- } - -- benchmarkCompletion(completionBenchOptions{ -- file: file, -- locationRegexp: `func \(c \*completer\) _\(\) {\n\tc\.inference\.kindMatches\((c)`, -- setup: setup, -- }, b) +-func newFunction(x int) string { +- if x == 0 { +- x = 3 +- return "a" +- } +- x = 2 +- return "b" -} - --// Benchmark completion following an arbitrary edit. --// --// Edits force type-checked packages to be invalidated, so we want to measure --// how long it takes before completion results are available. --func BenchmarkCompletionFollowingEdit(b *testing.B) { -- file := "internal/lsp/source/completion/completion2.go" -- fileContent := ` --package completion +--- args_returns.go -- +-package extract - --func (c *completer) _() { -- c.inference.kindMatches(c.) -- // __MAGIC_STRING_1 +-func _() { +- a := 1 +- a = 5 //@codeaction("refactor.extract", "a", araend, ara) +- a = a + 2 //@loc(araend, "2") +- +- b := a * 2 //@codeaction("refactor.extract", "b", arbend, arb) +- _ = b + 4 //@loc(arbend, "4") -} --` -- setup := func(env *Env) { -- env.CreateBuffer(file, fileContent) -- } - -- n := 1 -- beforeCompletion := func(env *Env) { -- old := fmt.Sprintf("__MAGIC_STRING_%d", n) -- new := fmt.Sprintf("__MAGIC_STRING_%d", n+1) -- n++ -- env.RegexpReplace(file, old, new) -- } +--- @ara/args_returns.go -- +-package extract - -- benchmarkCompletion(completionBenchOptions{ -- file: file, -- locationRegexp: `func \(c \*completer\) _\(\) {\n\tc\.inference\.kindMatches\((c)`, -- setup: setup, -- beforeCompletion: beforeCompletion, -- }, b) +-func _() { +- a := 1 +- //@codeaction("refactor.extract", "a", araend, ara) +- a = newFunction(a) //@loc(araend, "2") +- +- b := a * 2 //@codeaction("refactor.extract", "b", arbend, arb) +- _ = b + 4 //@loc(arbend, "4") -} -diff -urN a/gopls/internal/regtest/bench/definition_test.go b/gopls/internal/regtest/bench/definition_test.go ---- a/gopls/internal/regtest/bench/definition_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/definition_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,39 +0,0 @@ --// Copyright 2023 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 +-func newFunction(a int) int { +- a = 5 +- a = a + 2 +- return a +-} - --import ( -- "testing" --) +--- @arb/args_returns.go -- +-package extract - --func BenchmarkDefinition(b *testing.B) { -- tests := []struct { -- repo string -- file string -- regexp string -- }{ -- {"istio", "pkg/config/model.go", `gogotypes\.(MarshalAny)`}, -- {"kubernetes", "pkg/controller/lookup_cache.go", `hashutil\.(DeepHashObject)`}, -- {"kuma", "api/generic/insights.go", `proto\.(Message)`}, -- {"pkgsite", "internal/log/log.go", `derrors\.(Wrap)`}, -- {"starlark", "starlark/eval.go", "prog.compiled.(Encode)"}, -- {"tools", "internal/lsp/cache/check.go", `(snapshot)\) buildKey`}, -- } +-func _() { +- a := 1 +- a = 5 //@codeaction("refactor.extract", "a", araend, ara) +- a = a + 2 //@loc(araend, "2") - -- for _, test := range tests { -- b.Run(test.repo, func(b *testing.B) { -- env := getRepo(b, test.repo).sharedEnv(b) -- env.OpenFile(test.file) -- loc := env.RegexpSearch(test.file, test.regexp) -- env.Await(env.DoneWithOpen()) -- env.GoToDefinition(loc) // pre-warm the query, and open the target file -- b.ResetTimer() +- //@codeaction("refactor.extract", "b", arbend, arb) +- newFunction(a) //@loc(arbend, "4") +-} - -- for i := 0; i < b.N; i++ { -- env.GoToDefinition(loc) // pre-warm the query -- } -- }) -- } +-func newFunction(a int) { +- b := a * 2 +- _ = b + 4 -} -diff -urN a/gopls/internal/regtest/bench/didchange_test.go b/gopls/internal/regtest/bench/didchange_test.go ---- a/gopls/internal/regtest/bench/didchange_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/didchange_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,99 +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 bench +--- scope.go -- +-package extract - --import ( -- "fmt" -- "sync/atomic" -- "testing" -- "time" +-func _() { +- newFunction := 1 +- a := newFunction //@codeaction("refactor.extract", "a", "newFunction", scope) +- _ = a // avoid diagnostic +-} - -- "golang.org/x/tools/gopls/internal/lsp/fake" -- "golang.org/x/tools/gopls/internal/lsp/protocol" --) +-func newFunction1() int { +- return 1 +-} - --// Use a global edit counter as bench function may execute multiple times, and --// we want to avoid cache hits. Use time.Now to also avoid cache hits from the --// shared file cache. --var editID int64 = time.Now().UnixNano() +--- @scope/scope.go -- +-package extract - --var didChangeTests = []struct { -- repo string -- file string --}{ -- {"istio", "pkg/fuzz/util.go"}, -- {"kubernetes", "pkg/controller/lookup_cache.go"}, -- {"kuma", "api/generic/insights.go"}, -- {"pkgsite", "internal/frontend/server.go"}, -- {"starlark", "starlark/eval.go"}, -- {"tools", "internal/lsp/cache/snapshot.go"}, +-func _() { +- newFunction := 1 +- a := newFunction2(newFunction) //@codeaction("refactor.extract", "a", "newFunction", scope) +- _ = a // avoid diagnostic -} - --// BenchmarkDidChange 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. --func BenchmarkDidChange(b *testing.B) { -- for _, test := range didChangeTests { -- b.Run(test.repo, func(b *testing.B) { -- env := getRepo(b, test.repo).sharedEnv(b) -- env.OpenFile(test.file) -- // Insert the text we'll be modifying at the top of the file. -- env.EditBuffer(test.file, protocol.TextEdit{NewText: "// __REGTEST_PLACEHOLDER_0__\n"}) -- env.AfterChange() -- b.ResetTimer() +-func newFunction2(newFunction int) int { +- a := newFunction +- return a +-} - -- for i := 0; i < b.N; i++ { -- edits := atomic.AddInt64(&editID, 1) -- env.EditBuffer(test.file, protocol.TextEdit{ -- Range: protocol.Range{ -- Start: protocol.Position{Line: 0, Character: 0}, -- End: protocol.Position{Line: 1, Character: 0}, -- }, -- // Increment the placeholder text, to ensure cache misses. -- NewText: fmt.Sprintf("// __REGTEST_PLACEHOLDER_%d__\n", edits), -- }) -- env.Await(env.StartedChange()) -- } -- }) -- } +-func newFunction1() int { +- return 1 -} - --func BenchmarkDiagnoseChange(b *testing.B) { -- for _, test := range didChangeTests { -- b.Run(test.repo, func(b *testing.B) { -- // Use a new env to avoid the diagnostic delay: we want to measure how -- // long it takes to produce the diagnostics. -- env := repos[test.repo].newEnv(b, "diagnoseChange", fake.EditorConfig{ -- Settings: map[string]interface{}{ -- "diagnosticsDelay": "0s", -- }, -- }) -- env.OpenFile(test.file) -- // Insert the text we'll be modifying at the top of the file. -- env.EditBuffer(test.file, protocol.TextEdit{NewText: "// __REGTEST_PLACEHOLDER_0__\n"}) -- env.AfterChange() -- b.ResetTimer() +--- smart_initialization.go -- +-package extract - -- // We must use an extra subtest layer here, so that we only set up the -- // shared env once (otherwise we pay additional overhead and the profiling -- // flags don't work). -- b.Run("diagnose", func(b *testing.B) { -- for i := 0; i < b.N; i++ { -- edits := atomic.AddInt64(&editID, 1) -- env.EditBuffer(test.file, protocol.TextEdit{ -- Range: protocol.Range{ -- Start: protocol.Position{Line: 0, Character: 0}, -- End: protocol.Position{Line: 1, Character: 0}, -- }, -- // Increment the placeholder text, to ensure cache misses. -- NewText: fmt.Sprintf("// __REGTEST_PLACEHOLDER_%d__\n", edits), -- }) -- env.AfterChange() -- } -- }) -- }) -- } +-func _() { +- var a []int +- a = append(a, 2) //@codeaction("refactor.extract", "a", siEnd, si) +- b := 4 //@loc(siEnd, "4") +- a = append(a, b) -} -diff -urN a/gopls/internal/regtest/bench/doc.go b/gopls/internal/regtest/bench/doc.go ---- a/gopls/internal/regtest/bench/doc.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/doc.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,33 +0,0 @@ --// Copyright 2023 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. - --// The bench package implements benchmarks for various LSP operations. --// --// Benchmarks check out specific commits of popular and/or exemplary --// repositories, and script an external gopls process via a fake text editor. --// By default, benchmarks run the test executable as gopls (using a special --// "gopls mode" environment variable). A different gopls binary may be used by --// setting the -gopls_path or -gopls_commit flags. --// --// This package is a work in progress. --// --// # Profiling --// --// As benchmark functions run gopls in a separate process, the normal test --// flags for profiling are not useful. Instead the -gopls_cpuprofile, --// -gopls_memprofile, and -gopls_trace flags may be used to pass through --// profiling flags to the gopls process. Each of these flags sets a suffix --// for the respective gopls profiling flag, which is prefixed with a name --// corresponding to the shared repository or (in some cases) benchmark name. --// For example, settings -gopls_cpuprofile=cpu.out will result in profiles --// named tools.cpu.out, BenchmarkInitialWorkspaceLoad.cpu.out, etc. Here, --// tools.cpu.out is the cpu profile for the shared x/tools session, which may --// be used by multiple benchmark functions, and BenchmarkInitialWorkspaceLoad --// is the cpu profile for the last iteration of the initial workspace load --// test, which starts a new editor session for each iteration. --// --// # TODO --// - add more benchmarks, and more repositories --// - improve this documentation --package bench -diff -urN a/gopls/internal/regtest/bench/hover_test.go b/gopls/internal/regtest/bench/hover_test.go ---- a/gopls/internal/regtest/bench/hover_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/hover_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,39 +0,0 @@ --// Copyright 2023 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. +--- @si/smart_initialization.go -- +-package extract - --package bench +-func _() { +- var a []int +- //@codeaction("refactor.extract", "a", siEnd, si) +- a, b := newFunction(a) //@loc(siEnd, "4") +- a = append(a, b) +-} - --import ( -- "testing" --) +-func newFunction(a []int) ([]int, int) { +- a = append(a, 2) +- b := 4 +- return a, b +-} - --func BenchmarkHover(b *testing.B) { -- tests := []struct { -- repo string -- file string -- regexp string -- }{ -- {"istio", "pkg/config/model.go", `gogotypes\.(MarshalAny)`}, -- {"kubernetes", "pkg/apis/core/types.go", "type (Pod)"}, -- {"kuma", "api/generic/insights.go", `proto\.(Message)`}, -- {"pkgsite", "internal/log/log.go", `derrors\.(Wrap)`}, -- {"starlark", "starlark/eval.go", "prog.compiled.(Encode)"}, -- {"tools", "internal/lsp/cache/check.go", `(snapshot)\) buildKey`}, -- } +--- smart_return.go -- +-package extract - -- for _, test := range tests { -- b.Run(test.repo, func(b *testing.B) { -- env := getRepo(b, test.repo).sharedEnv(b) -- env.OpenFile(test.file) -- loc := env.RegexpSearch(test.file, test.regexp) -- env.Await(env.DoneWithOpen()) -- env.Hover(loc) // pre-warm the query -- b.ResetTimer() +-func _() { +- var b []int +- var a int +- a = 2 //@codeaction("refactor.extract", "a", srEnd, sr) +- b = []int{} +- b = append(b, a) //@loc(srEnd, ")") +- b[0] = 1 +-} - -- for i := 0; i < b.N; i++ { -- env.Hover(loc) // pre-warm the query -- } -- }) -- } +--- @sr/smart_return.go -- +-package extract +- +-func _() { +- var b []int +- var a int +- //@codeaction("refactor.extract", "a", srEnd, sr) +- b = newFunction(a, b) //@loc(srEnd, ")") +- b[0] = 1 -} -diff -urN a/gopls/internal/regtest/bench/implementations_test.go b/gopls/internal/regtest/bench/implementations_test.go ---- a/gopls/internal/regtest/bench/implementations_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/implementations_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,37 +0,0 @@ --// Copyright 2023 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 +-func newFunction(a int, b []int) []int { +- a = 2 +- b = []int{} +- b = append(b, a) +- return b +-} - --import "testing" +--- unnecessary_param.go -- +-package extract - --func BenchmarkImplementations(b *testing.B) { -- tests := []struct { -- repo string -- file string -- regexp string -- }{ -- {"istio", "pkg/config/mesh/watcher.go", `type (Watcher)`}, -- {"kubernetes", "pkg/controller/lookup_cache.go", `objectWithMeta`}, -- {"kuma", "api/generic/insights.go", `type (Insight)`}, -- {"pkgsite", "internal/datasource.go", `type (DataSource)`}, -- {"starlark", "syntax/syntax.go", `type (Expr)`}, -- {"tools", "internal/lsp/source/view.go", `type (Snapshot)`}, +-func _() { +- var b []int +- a := 2 //@codeaction("refactor.extract", "a", upEnd, up) +- b = []int{} +- b = append(b, a) //@loc(upEnd, ")") +- b[0] = 1 +- if a == 2 { +- return - } +-} - -- for _, test := range tests { -- b.Run(test.repo, func(b *testing.B) { -- env := getRepo(b, test.repo).sharedEnv(b) -- env.OpenFile(test.file) -- loc := env.RegexpSearch(test.file, test.regexp) -- env.Await(env.DoneWithOpen()) -- env.Implementations(loc) // pre-warm the query -- b.ResetTimer() +--- @up/unnecessary_param.go -- +-package extract - -- for i := 0; i < b.N; i++ { -- env.Implementations(loc) -- } -- }) +-func _() { +- var b []int +- //@codeaction("refactor.extract", "a", upEnd, up) +- a, b := newFunction(b) //@loc(upEnd, ")") +- b[0] = 1 +- if a == 2 { +- return - } -} -diff -urN a/gopls/internal/regtest/bench/iwl_test.go b/gopls/internal/regtest/bench/iwl_test.go ---- a/gopls/internal/regtest/bench/iwl_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/iwl_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,77 +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 bench +-func newFunction(b []int) (int, []int) { +- a := 2 +- b = []int{} +- b = append(b, a) +- return a, b +-} - --import ( -- "testing" +--- comment.go -- +-package extract - -- "golang.org/x/tools/gopls/internal/lsp/command" -- "golang.org/x/tools/gopls/internal/lsp/fake" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" --) +-func _() { +- a := /* comment in the middle of a line */ 1 //@codeaction("refactor.extract", "a", commentEnd, comment1) +- // Comment on its own line //@codeaction("refactor.extract", "Comment", commentEnd, comment2) +- _ = a + 4 //@loc(commentEnd, "4"),codeaction("refactor.extract", "_", lastComment, comment3) +- // Comment right after 3 + 4 - --// BenchmarkInitialWorkspaceLoad benchmarks the initial workspace load time for --// a new editing session. --func BenchmarkInitialWorkspaceLoad(b *testing.B) { -- if testing.Short() { -- // TODO(rfindley): remove this skip once the released gopls version -- // supports the memstats command. -- b.Skip("temporarily skipping as baseline gopls versions do not support the memstats command") -- } -- tests := []struct { -- repo string -- file string -- }{ -- {"tools", "internal/lsp/cache/snapshot.go"}, -- {"kubernetes", "pkg/controller/lookup_cache.go"}, -- {"pkgsite", "internal/frontend/server.go"}, -- {"starlark", "starlark/eval.go"}, -- {"istio", "pkg/fuzz/util.go"}, -- {"kuma", "api/generic/insights.go"}, -- } +- // Comment after with space //@loc(lastComment, "Comment") +-} - -- for _, test := range tests { -- b.Run(test.repo, func(b *testing.B) { -- repo := getRepo(b, test.repo) -- // get the (initialized) shared env to ensure the cache is warm. -- // Reuse its GOPATH so that we get cache hits for things in the module -- // cache. -- sharedEnv := repo.sharedEnv(b) -- b.ResetTimer() +--- @comment1/comment.go -- +-package extract - -- for i := 0; i < b.N; i++ { -- doIWL(b, sharedEnv.Sandbox.GOPATH(), repo, test.file) -- } -- }) -- } +-func _() { +- /* comment in the middle of a line */ +- //@codeaction("refactor.extract", "a", commentEnd, comment1) +- // Comment on its own line //@codeaction("refactor.extract", "Comment", commentEnd, comment2) +- newFunction() //@loc(commentEnd, "4"),codeaction("refactor.extract", "_", lastComment, comment3) +- // Comment right after 3 + 4 +- +- // Comment after with space //@loc(lastComment, "Comment") -} - --func doIWL(b *testing.B, gopath string, repo *repo, file string) { -- // Exclude the time to set up the env from the benchmark time, as this may -- // involve installing gopls and/or checking out the repo dir. -- b.StopTimer() -- config := fake.EditorConfig{Env: map[string]string{"GOPATH": gopath}} -- env := repo.newEnv(b, "iwl."+repo.name, config) -- defer env.Close() -- b.StartTimer() +-func newFunction() { +- a := 1 - -- // Open an arbitrary file to ensure that gopls starts working. -- // -- // In the future, this may matter if gopls doesn't eagerly construct -- // the workspace. -- env.OpenFile(file) +- _ = a + 4 +-} - -- env.Await(InitialWorkspaceLoad) -- b.StopTimer() -- params := &protocol.ExecuteCommandParams{ -- Command: command.MemStats.ID(), -- } -- var memstats command.MemStatsResult -- env.ExecuteCommand(params, &memstats) -- b.ReportMetric(float64(memstats.HeapAlloc), "alloc_bytes") -- b.ReportMetric(float64(memstats.HeapInUse), "in_use_bytes") -- b.StartTimer() +--- @comment2/comment.go -- +-package extract +- +-func _() { +- a := /* comment in the middle of a line */ 1 //@codeaction("refactor.extract", "a", commentEnd, comment1) +- // Comment on its own line //@codeaction("refactor.extract", "Comment", commentEnd, comment2) +- newFunction(a) //@loc(commentEnd, "4"),codeaction("refactor.extract", "_", lastComment, comment3) +- // Comment right after 3 + 4 +- +- // Comment after with space //@loc(lastComment, "Comment") -} -diff -urN a/gopls/internal/regtest/bench/references_test.go b/gopls/internal/regtest/bench/references_test.go ---- a/gopls/internal/regtest/bench/references_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/references_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,37 +0,0 @@ --// Copyright 2023 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 +-func newFunction(a int) { +- _ = a + 4 +-} - --import "testing" +--- @comment3/comment.go -- +-package extract - --func BenchmarkReferences(b *testing.B) { -- tests := []struct { -- repo string -- file string -- regexp string -- }{ -- {"istio", "pkg/config/model.go", "type (Meta)"}, -- {"kubernetes", "pkg/controller/lookup_cache.go", "type (objectWithMeta)"}, -- {"kuma", "pkg/events/interfaces.go", "type (Event)"}, -- {"pkgsite", "internal/log/log.go", "func (Infof)"}, -- {"starlark", "syntax/syntax.go", "type (Ident)"}, -- {"tools", "internal/lsp/source/view.go", "type (Snapshot)"}, -- } +-func _() { +- a := /* comment in the middle of a line */ 1 //@codeaction("refactor.extract", "a", commentEnd, comment1) +- // Comment on its own line //@codeaction("refactor.extract", "Comment", commentEnd, comment2) +- newFunction(a) //@loc(commentEnd, "4"),codeaction("refactor.extract", "_", lastComment, comment3) +- // Comment right after 3 + 4 - -- for _, test := range tests { -- b.Run(test.repo, func(b *testing.B) { -- env := getRepo(b, test.repo).sharedEnv(b) -- env.OpenFile(test.file) -- loc := env.RegexpSearch(test.file, test.regexp) -- env.Await(env.DoneWithOpen()) -- env.References(loc) // pre-warm the query -- b.ResetTimer() +- // Comment after with space //@loc(lastComment, "Comment") +-} - -- for i := 0; i < b.N; i++ { -- env.References(loc) -- } -- }) -- } +-func newFunction(a int) { +- _ = a + 4 -} -diff -urN a/gopls/internal/regtest/bench/rename_test.go b/gopls/internal/regtest/bench/rename_test.go ---- a/gopls/internal/regtest/bench/rename_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/rename_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,44 +0,0 @@ --// Copyright 2023 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 +--- redefine.go -- +-package extract - --import ( -- "fmt" -- "testing" --) +-import "strconv" - --func BenchmarkRename(b *testing.B) { -- tests := []struct { -- repo string -- file string -- regexp string -- baseName string -- }{ -- {"kubernetes", "pkg/controller/lookup_cache.go", `hashutil\.(DeepHashObject)`, "DeepHashObject"}, -- {"kuma", "pkg/events/interfaces.go", `Delete`, "Delete"}, -- {"istio", "pkg/config/model.go", `(Namespace) string`, "Namespace"}, -- {"pkgsite", "internal/log/log.go", `func (Infof)`, "Infof"}, -- {"starlark", "starlark/eval.go", `Program\) (Filename)`, "Filename"}, -- {"tools", "internal/lsp/cache/snapshot.go", `meta \*(metadataGraph)`, "metadataGraph"}, +-func _() { +- i, err := strconv.Atoi("1") +- u, err := strconv.Atoi("2") //@codeaction("refactor.extract", "u", ")", redefine) +- if i == u || err == nil { +- return - } +-} - -- for _, test := range tests { -- names := 0 // bench function may execute multiple times -- b.Run(test.repo, func(b *testing.B) { -- env := getRepo(b, test.repo).sharedEnv(b) -- env.OpenFile(test.file) -- loc := env.RegexpSearch(test.file, test.regexp) -- env.Await(env.DoneWithOpen()) -- env.Rename(loc, test.baseName+"X") // pre-warm the query -- b.ResetTimer() +--- @redefine/redefine.go -- +-package extract - -- for i := 0; i < b.N; i++ { -- names++ -- newName := fmt.Sprintf("%s%d", test.baseName, names) -- env.Rename(loc, newName) -- } -- }) +-import "strconv" +- +-func _() { +- i, err := strconv.Atoi("1") +- u, err := newFunction() //@codeaction("refactor.extract", "u", ")", redefine) +- if i == u || err == nil { +- return - } -} -diff -urN a/gopls/internal/regtest/bench/repo_test.go b/gopls/internal/regtest/bench/repo_test.go ---- a/gopls/internal/regtest/bench/repo_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/repo_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,231 +0,0 @@ --// Copyright 2023 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 +-func newFunction() (int, error) { +- u, err := strconv.Atoi("2") +- return u, err +-} - --import ( -- "bytes" -- "context" -- "errors" -- "fmt" -- "log" -- "os" -- "path/filepath" -- "sync" -- "testing" -- "time" +diff -urN a/gopls/internal/regtest/marker/testdata/codeaction/functionextraction_issue44813.txt b/gopls/internal/regtest/marker/testdata/codeaction/functionextraction_issue44813.txt +--- a/gopls/internal/regtest/marker/testdata/codeaction/functionextraction_issue44813.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/codeaction/functionextraction_issue44813.txt 1970-01-01 08:00:00 +@@ -1,42 +0,0 @@ +-This test verifies the fix for golang/go#44813: extraction failure when there +-are blank identifiers. - -- "golang.org/x/tools/gopls/internal/lsp/fake" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" --) +--- go.mod -- +-module mod.test/extract - --// repos holds shared repositories for use in benchmarks. --// --// These repos were selected to represent a variety of different types of --// codebases. --var repos = map[string]*repo{ -- // Used by x/benchmarks; large. -- "istio": { -- name: "istio", -- url: "https://github.com/istio/istio", -- commit: "1.17.0", -- }, +-go 1.18 - -- // Kubernetes is a large repo with many dependencies, and in the past has -- // been about as large a repo as gopls could handle. -- "kubernetes": { -- name: "kubernetes", -- url: "https://github.com/kubernetes/kubernetes", -- commit: "v1.24.0", -- }, +--- p.go -- +-package extract - -- // A large, industrial application. -- "kuma": { -- name: "kuma", -- url: "https://github.com/kumahq/kuma", -- commit: "2.1.1", -- }, +-import "fmt" - -- // x/pkgsite is familiar and represents a common use case (a webserver). It -- // also has a number of static non-go files and template files. -- "pkgsite": { -- name: "pkgsite", -- url: "https://go.googlesource.com/pkgsite", -- commit: "81f6f8d4175ad0bf6feaa03543cc433f8b04b19b", -- short: true, -- }, +-func main() { +- x := []rune{} //@codeaction("refactor.extract", "x", end, ext) +- s := "HELLO" +- for _, c := range s { +- x = append(x, c) +- } //@loc(end, "}") +- fmt.Printf("%x\n", x) +-} - -- // A tiny self-contained project. -- "starlark": { -- name: "starlark", -- url: "https://github.com/google/starlark-go", -- commit: "3f75dec8e4039385901a30981e3703470d77e027", -- short: true, -- }, +--- @ext/p.go -- +-package extract - -- // The current repository, which is medium-small and has very few dependencies. -- "tools": { -- name: "tools", -- url: "https://go.googlesource.com/tools", -- commit: "gopls/v0.9.0", -- short: true, -- }, +-import "fmt" +- +-func main() { +- //@codeaction("refactor.extract", "x", end, ext) +- x := newFunction() //@loc(end, "}") +- fmt.Printf("%x\n", x) -} - --// getRepo gets the requested repo, and skips the test if -short is set and --// repo is not configured as a short repo. --func getRepo(tb testing.TB, name string) *repo { -- tb.Helper() -- repo := repos[name] -- if repo == nil { -- tb.Fatalf("repo %s does not exist", name) -- } -- if !repo.short && testing.Short() { -- tb.Skipf("large repo %s does not run whith -short", repo.name) +-func newFunction() []rune { +- x := []rune{} +- s := "HELLO" +- for _, c := range s { +- x = append(x, c) - } -- return repo +- return x -} - --// A repo represents a working directory for a repository checked out at a --// specific commit. --// --// Repos are used for sharing state across benchmarks that operate on the same --// codebase. --type repo struct { -- // static configuration -- name string // must be unique, used for subdirectory -- url string // repo url -- commit string // full commit hash or tag -- short bool // whether this repo runs with -short +diff -urN a/gopls/internal/regtest/marker/testdata/codeaction/imports.txt b/gopls/internal/regtest/marker/testdata/codeaction/imports.txt +--- a/gopls/internal/regtest/marker/testdata/codeaction/imports.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/codeaction/imports.txt 1970-01-01 08:00:00 +@@ -1,175 +0,0 @@ +-This test verifies the behavior of the 'source.organizeImports' code action. - -- dirOnce sync.Once -- dir string // directory contaning source code checked out to url@commit +--- go.mod -- +-module mod.test/imports - -- // shared editor state -- editorOnce sync.Once -- editor *fake.Editor -- sandbox *fake.Sandbox -- awaiter *Awaiter --} +-go 1.18 - --// getDir returns directory containing repo source code, creating it if --// necessary. It is safe for concurrent use. --func (r *repo) getDir() string { -- r.dirOnce.Do(func() { -- r.dir = filepath.Join(getTempDir(), r.name) -- log.Printf("cloning %s@%s into %s", r.url, r.commit, r.dir) -- if err := shallowClone(r.dir, r.url, r.commit); err != nil { -- log.Fatal(err) -- } -- }) -- return r.dir --} +--- add.go -- +-package imports //@codeaction("source.organizeImports", "imports", "", add) - --// sharedEnv returns a shared benchmark environment. It is safe for concurrent --// use. --// --// Every call to sharedEnv uses the same editor and sandbox, as a means to --// avoid reinitializing the editor for large repos. Calling repo.Close cleans --// up the shared environment. --// --// Repos in the package-local Repos var are closed at the end of the test main --// function. --func (r *repo) sharedEnv(tb testing.TB) *Env { -- r.editorOnce.Do(func() { -- dir := r.getDir() +-import ( +- "fmt" +-) - -- start := time.Now() -- log.Printf("starting initial workspace load for %s", r.name) -- ts, err := newGoplsServer(r.name) -- if err != nil { -- log.Fatal(err) -- } -- r.sandbox, r.editor, r.awaiter, err = connectEditor(dir, fake.EditorConfig{}, ts) -- if err != nil { -- log.Fatalf("connecting editor: %v", err) -- } +-func _() { +- fmt.Println("") +- bytes.NewBuffer(nil) //@diag("bytes", re"(undeclared|undefined)") +-} - -- if err := r.awaiter.Await(context.Background(), InitialWorkspaceLoad); err != nil { -- log.Fatal(err) -- } -- log.Printf("initial workspace load (cold) for %s took %v", r.name, time.Since(start)) -- }) +--- @add/add.go -- +-package imports //@codeaction("source.organizeImports", "imports", "", add) - -- return &Env{ -- T: tb, -- Ctx: context.Background(), -- Editor: r.editor, -- Sandbox: r.sandbox, -- Awaiter: r.awaiter, -- } +-import ( +- "bytes" +- "fmt" +-) +- +-func _() { +- fmt.Println("") +- bytes.NewBuffer(nil) //@diag("bytes", re"(undeclared|undefined)") -} - --// newEnv returns a new Env connected to a new gopls process communicating --// over stdin/stdout. It is safe for concurrent use. --// --// It is the caller's responsibility to call Close on the resulting Env when it --// is no longer needed. --func (r *repo) newEnv(tb testing.TB, name string, config fake.EditorConfig) *Env { -- dir := r.getDir() +--- good.go -- +-package imports //@codeactionerr("source.organizeImports", "imports", "", re"found 0 CodeActions") - -- ts, err := newGoplsServer(name) -- if err != nil { -- tb.Fatal(err) -- } -- sandbox, editor, awaiter, err := connectEditor(dir, config, ts) -- if err != nil { -- log.Fatalf("connecting editor: %v", err) -- } +-import "fmt" - -- return &Env{ -- T: tb, -- Ctx: context.Background(), -- Editor: editor, -- Sandbox: sandbox, -- Awaiter: awaiter, -- } +-func _() { +-fmt.Println("") -} - --// Close cleans up shared state referenced by the repo. --func (r *repo) Close() error { -- var errBuf bytes.Buffer -- if r.editor != nil { -- if err := r.editor.Close(context.Background()); err != nil { -- fmt.Fprintf(&errBuf, "closing editor: %v", err) -- } -- } -- if r.sandbox != nil { -- if err := r.sandbox.Close(); err != nil { -- fmt.Fprintf(&errBuf, "closing sandbox: %v", err) -- } -- } -- if r.dir != "" { -- if err := os.RemoveAll(r.dir); err != nil { -- fmt.Fprintf(&errBuf, "cleaning dir: %v", err) -- } -- } -- if errBuf.Len() > 0 { -- return errors.New(errBuf.String()) -- } -- return nil --} +--- issue35458.go -- - --// cleanup cleans up state that is shared across benchmark functions. --func cleanup() error { -- var errBuf bytes.Buffer -- for _, repo := range repos { -- if err := repo.Close(); err != nil { -- fmt.Fprintf(&errBuf, "closing %q: %v", repo.name, err) -- } -- } -- if tempDir != "" { -- if err := os.RemoveAll(tempDir); err != nil { -- fmt.Fprintf(&errBuf, "cleaning tempDir: %v", err) -- } -- } -- if errBuf.Len() > 0 { -- return errors.New(errBuf.String()) -- } -- return nil --} -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 00:00:00.000000000 +0000 -@@ -1,94 +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/gopls/internal/hooks" -- "golang.org/x/tools/gopls/internal/lsp/cache" -- "golang.org/x/tools/gopls/internal/lsp/fake" -- "golang.org/x/tools/gopls/internal/lsp/lsprpc" -- "golang.org/x/tools/internal/jsonrpc2" -- "golang.org/x/tools/internal/jsonrpc2/servertest" --) - --// github.com/pilosa/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. +-// package doc +-package imports //@codeaction("source.organizeImports", "imports", "", issue35458) - --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 TestPilosaStress(t *testing.T) { -- // TODO(rfindley): revisit this test and make it is hermetic: it should check -- // out pilosa into a directory. -- // -- // Note: This stress test has not been run recently, and may no longer -- // function properly. -- if *pilosaPath == "" { -- t.Skip("-pilosa_path not configured") -- } - -- sandbox, err := fake.NewSandbox(&fake.SandboxConfig{ -- Workdir: *pilosaPath, -- GOPROXY: "https://proxy.golang.org", -- }) -- if err != nil { -- t.Fatal(err) -- } - -- server := lsprpc.NewStreamServer(cache.New(nil), false, hooks.Options) -- ts := servertest.NewPipeServer(server, jsonrpc2.NewRawStream) -- ctx := context.Background() - -- const skipApplyEdits = false -- editor, err := fake.NewEditor(sandbox, fake.EditorConfig{}).Connect(ctx, ts, fake.ClientHooks{}, skipApplyEdits) -- if err != nil { -- t.Fatal(err) -- } - -- files := []string{ -- "cmd.go", -- "internal/private.pb.go", -- "roaring/roaring.go", -- "roaring/roaring_internal_test.go", -- "server/handler_test.go", -- } -- for _, file := range files { -- if err := editor.OpenFile(ctx, file); err != nil { -- t.Fatal(err) -- } -- } -- ctx, cancel := context.WithTimeout(ctx, 10*time.Minute) -- defer cancel() +-func _() { +- println("Hello, world!") +-} - -- i := 1 -- // MagicNumber is an identifier that occurs in roaring.go. Just change it -- // arbitrarily. -- if err := editor.RegexpReplace(ctx, "roaring/roaring.go", "MagicNumber", fmt.Sprintf("MagicNumber%d", 1)); err != nil { -- t.Fatal(err) -- } -- for { -- select { -- case <-ctx.Done(): -- return -- default: -- } -- if err := editor.RegexpReplace(ctx, "roaring/roaring.go", fmt.Sprintf("MagicNumber%d", i), fmt.Sprintf("MagicNumber%d", i+1)); err != nil { -- t.Fatal(err) -- } -- // Simulate (very fast) typing. -- // -- // Typing 80 wpm ~150ms per keystroke. -- time.Sleep(150 * time.Millisecond) -- i++ -- } +- +- +- +- +- +- +- +--- @issue35458/issue35458.go -- +-// package doc +-package imports //@codeaction("source.organizeImports", "imports", "", issue35458) +- +- +- +- +- +- +-func _() { +- println("Hello, world!") -} -diff -urN a/gopls/internal/regtest/bench/workspace_symbols_test.go b/gopls/internal/regtest/bench/workspace_symbols_test.go ---- a/gopls/internal/regtest/bench/workspace_symbols_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/bench/workspace_symbols_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,37 +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 bench - --import ( -- "flag" -- "fmt" -- "testing" --) - --var symbolQuery = flag.String("symbol_query", "test", "symbol query to use in benchmark") - --// BenchmarkWorkspaceSymbols benchmarks the time to execute a workspace symbols --// request (controlled by the -symbol_query flag). --func BenchmarkWorkspaceSymbols(b *testing.B) { -- for name := range repos { -- b.Run(name, func(b *testing.B) { -- env := getRepo(b, name).sharedEnv(b) -- symbols := env.Symbol(*symbolQuery) // warm the cache - -- if testing.Verbose() { -- fmt.Println("Results:") -- for i, symbol := range symbols { -- fmt.Printf("\t%d. %s (%s)\n", i, symbol.Name, symbol.ContainerName) -- } -- } - -- b.ResetTimer() - -- for i := 0; i < b.N; i++ { -- env.Symbol(*symbolQuery) -- } -- }) -- } +- +--- multi.go -- +-package imports //@codeaction("source.organizeImports", "imports", "", multi) +- +-import "fmt" +- +-import "bytes" //@diag("\"bytes\"", re"not used") +- +-func _() { +- fmt.Println("") -} -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 00:00:00.000000000 +0000 -@@ -1,336 +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 +--- @multi/multi.go -- +-package imports //@codeaction("source.organizeImports", "imports", "", multi) +- +-import "fmt" +- +-//@diag("\"bytes\"", re"not used") +- +-func _() { +- fmt.Println("") +-} +- +--- needs.go -- +-package imports //@codeaction("source.organizeImports", "package", "", needs) +- +-func goodbye() { +- fmt.Printf("HI") //@diag("fmt", re"(undeclared|undefined)") +- log.Printf("byeeeee") //@diag("log", re"(undeclared|undefined)") +-} +- +--- @needs/needs.go -- +-package imports //@codeaction("source.organizeImports", "package", "", needs) - -import ( - "fmt" -- "testing" +- "log" +-) - -- "golang.org/x/tools/gopls/internal/hooks" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/gopls/internal/lsp/tests/compare" -- "golang.org/x/tools/internal/bug" +-func goodbye() { +- fmt.Printf("HI") //@diag("fmt", re"(undeclared|undefined)") +- log.Printf("byeeeee") //@diag("log", re"(undeclared|undefined)") +-} - -- "golang.org/x/tools/gopls/internal/lsp/command" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/internal/testenv" +--- remove.go -- +-package imports //@codeaction("source.organizeImports", "package", "", remove) +- +-import ( +- "bytes" //@diag("\"bytes\"", re"not used") +- "fmt" -) - --func TestMain(m *testing.M) { -- bug.PanicOnBugs = true -- Main(m, hooks.Options) +-func _() { +- fmt.Println("") -} - --func TestDisablingCodeLens(t *testing.T) { -- const workspace = ` ---- go.mod -- --module codelens.test +--- @remove/remove.go -- +-package imports //@codeaction("source.organizeImports", "package", "", remove) - --go 1.12 ---- lib.go -- --package lib +-import ( +- "fmt" +-) - --type Number int +-func _() { +- fmt.Println("") +-} +- +--- removeall.go -- +-package imports //@codeaction("source.organizeImports", "package", "", removeall) +- +-import ( +- "bytes" //@diag("\"bytes\"", re"not used") +- "fmt" //@diag("\"fmt\"", re"not used") - --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( -- Settings{"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) -- } -- }) -- }) -- } +-func _() { -} - --// 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. It also checks that these code lenses --// only affect the diagnostics and contents of the containing go.mod file. --func TestUpgradeCodelens(t *testing.T) { -- testenv.NeedsGo1Point(t, 18) // uses go.work +--- @removeall/removeall.go -- +-package imports //@codeaction("source.organizeImports", "package", "", removeall) - -- const proxyWithLatest = ` ---- golang.org/x/hello@v1.3.3/go.mod -- --module golang.org/x/hello +-//@diag("\"fmt\"", re"not used") - --go 1.12 ---- golang.org/x/hello@v1.3.3/hi/hi.go -- --package hi +-func _() { +-} - --var Goodbye error ---- golang.org/x/hello@v1.2.3/go.mod -- --module golang.org/x/hello +--- twolines.go -- +-package imports +-func main() {} //@codeactionerr("source.organizeImports", "main", "", re"found 0") +diff -urN a/gopls/internal/regtest/marker/testdata/codeaction/infertypeargs.txt b/gopls/internal/regtest/marker/testdata/codeaction/infertypeargs.txt +--- a/gopls/internal/regtest/marker/testdata/codeaction/infertypeargs.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/codeaction/infertypeargs.txt 1970-01-01 08:00:00 +@@ -1,38 +0,0 @@ +-This test verifies the infertypeargs refactoring. - --go 1.12 ---- golang.org/x/hello@v1.2.3/hi/hi.go -- --package hi +--- flags -- +--min_go=go1.18 - --var Goodbye error --` +--- go.mod -- +-module mod.test/infertypeargs - -- const shouldUpdateDep = ` ---- go.work -- -go 1.18 - --use ( -- ./a -- ./b --) ---- a/go.mod -- --module mod.com/a +--- p.go -- +-package infertypeargs - --go 1.14 +-func app[S interface{ ~[]E }, E interface{}](s S, e E) S { +- return append(s, e) +-} - --require golang.org/x/hello v1.2.3 ---- a/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= ---- a/main.go -- --package main +-func _() { +- _ = app[[]int] +- _ = app[[]int, int] +- _ = app[[]int]([]int{}, 0) //@codeaction("refactor.rewrite", "app", ")", infer) +- _ = app([]int{}, 0) +-} - --import "golang.org/x/hello/hi" +--- @infer/p.go -- +-package infertypeargs - --func main() { -- _ = hi.Goodbye +-func app[S interface{ ~[]E }, E interface{}](s S, e E) S { +- return append(s, e) -} ---- b/go.mod -- --module mod.com/b - --go 1.14 +-func _() { +- _ = app[[]int] +- _ = app[[]int, int] +- _ = app([]int{}, 0) //@codeaction("refactor.rewrite", "app", ")", infer) +- _ = app([]int{}, 0) +-} - --require golang.org/x/hello v1.2.3 ---- b/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= ---- b/main.go -- --package main +diff -urN a/gopls/internal/regtest/marker/testdata/codeaction/inline.txt b/gopls/internal/regtest/marker/testdata/codeaction/inline.txt +--- a/gopls/internal/regtest/marker/testdata/codeaction/inline.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/codeaction/inline.txt 1970-01-01 08:00:00 +@@ -1,23 +0,0 @@ +-This is a minimal test of the refactor.inline code action. - --import ( -- "golang.org/x/hello/hi" --) +--- go.mod -- +-module testdata/codeaction +-go 1.18 - --func main() { -- _ = hi.Goodbye +--- a/a.go -- +-package a +- +-func _() { +- println(add(1, 2)) //@codeaction("refactor.inline", "add", ")", inline) -} --` - -- const wantGoModA = `module mod.com/a +-func add(x, y int) int { return x + y } - --go 1.14 +--- @inline/a/a.go -- +-package a - --require golang.org/x/hello v1.3.3 --` -- // Applying the diagnostics or running the codelenses for a/go.mod -- // should not change the contents of b/go.mod -- const wantGoModB = `module mod.com/b +-func _() { +- println(1 + 2) //@codeaction("refactor.inline", "add", ")", inline) +-} - --go 1.14 +-func add(x, y int) int { return x + y } +diff -urN a/gopls/internal/regtest/marker/testdata/codelens/generate.txt b/gopls/internal/regtest/marker/testdata/codelens/generate.txt +--- a/gopls/internal/regtest/marker/testdata/codelens/generate.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/codelens/generate.txt 1970-01-01 08:00:00 +@@ -1,9 +0,0 @@ +-This test exercises the "generate" codelens. - --require golang.org/x/hello v1.2.3 --` +--- generate.go -- +-//@codelenses() - -- 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("a/go.mod") -- env.OpenFile("b/go.mod") -- var lens protocol.CodeLens -- var found bool -- for _, l := range env.CodeLens("a/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.AfterChange() -- if got := env.BufferText("a/go.mod"); got != wantGoModA { -- t.Fatalf("a/go.mod upgrade failed:\n%s", compare.Text(wantGoModA, got)) -- } -- if got := env.BufferText("b/go.mod"); got != wantGoModB { -- t.Fatalf("b/go.mod changed unexpectedly:\n%s", compare.Text(wantGoModB, 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.RunGoCommandInDir("a", "mod", "vendor") -- } -- env.AfterChange() -- env.OpenFile("a/go.mod") -- env.OpenFile("b/go.mod") -- env.ExecuteCodeLensCommand("a/go.mod", command.CheckUpgrades, nil) -- d := &protocol.PublishDiagnosticsParams{} -- env.OnceMet( -- Diagnostics(env.AtRegexp("a/go.mod", `require`), WithMessage("can be upgraded")), -- ReadDiagnostics("a/go.mod", d), -- // We do not want there to be a diagnostic for b/go.mod, -- // but there may be some subtlety in timing here, where this -- // should always succeed, but may not actually test the correct -- // behavior. -- NoDiagnostics(env.AtRegexp("b/go.mod", `require`)), -- ) -- // Check for upgrades in b/go.mod and then clear them. -- env.ExecuteCodeLensCommand("b/go.mod", command.CheckUpgrades, nil) -- env.Await(Diagnostics(env.AtRegexp("b/go.mod", `require`), WithMessage("can be upgraded"))) -- env.ExecuteCodeLensCommand("b/go.mod", command.ResetGoModDiagnostics, nil) -- env.Await(NoDiagnostics(ForFile("b/go.mod"))) +-package generate - -- // Apply the diagnostics to a/go.mod. -- env.ApplyQuickFixes("a/go.mod", d.Diagnostics) -- env.AfterChange() -- if got := env.BufferText("a/go.mod"); got != wantGoModA { -- t.Fatalf("a/go.mod upgrade failed:\n%s", compare.Text(wantGoModA, got)) -- } -- if got := env.BufferText("b/go.mod"); got != wantGoModB { -- t.Fatalf("b/go.mod changed unexpectedly:\n%s", compare.Text(wantGoModB, got)) -- } -- }) -- }) +-//go:generate echo Hi //@ codelens("//go:generate", "run go generate"), codelens("//go:generate", "run go generate ./...") +-//go:generate echo I shall have no CodeLens +diff -urN a/gopls/internal/regtest/marker/testdata/codelens/test.txt b/gopls/internal/regtest/marker/testdata/codelens/test.txt +--- a/gopls/internal/regtest/marker/testdata/codelens/test.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/codelens/test.txt 1970-01-01 08:00:00 +@@ -1,31 +0,0 @@ +-This file tests codelenses for test functions. +- +-TODO: for some reason these code lens have zero width. Does that affect their +-utility/visibility in various LSP clients? +- +--- settings.json -- +-{ +- "codelenses": { +- "test": true - } -} - --func TestUnusedDependenciesCodelens(t *testing.T) { -- const proxy = ` ---- golang.org/x/hello@v1.0.0/go.mod -- --module golang.org/x/hello +--- p_test.go -- +-//@codelenses() - --go 1.14 ---- golang.org/x/hello@v1.0.0/hi/hi.go -- --package hi +-package codelens //@codelens(re"()package codelens", "run file benchmarks") - --var Goodbye error ---- golang.org/x/unused@v1.0.0/go.mod -- --module golang.org/x/unused +-import "testing" - --go 1.14 ---- golang.org/x/unused@v1.0.0/nouse/nouse.go -- --package nouse +-func TestMain(m *testing.M) {} // no code lens for TestMain - --var NotUsed error --` +-func TestFuncWithCodeLens(t *testing.T) { //@codelens(re"()func", "run test") +-} - -- const shouldRemoveDep = ` ---- go.mod -- --module mod.com +-func thisShouldNotHaveACodeLens(t *testing.T) { +-} - --go 1.14 +-func BenchmarkFuncWithCodeLens(b *testing.B) { //@codelens(re"()func", "run benchmark") +-} - --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 +-func helper() {} // expect no code lens +diff -urN a/gopls/internal/regtest/marker/testdata/completion/bad.txt b/gopls/internal/regtest/marker/testdata/completion/bad.txt +--- a/gopls/internal/regtest/marker/testdata/completion/bad.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/completion/bad.txt 1970-01-01 08:00:00 +@@ -1,68 +0,0 @@ +-This test exercises completion in the presence of type errors. - --import "golang.org/x/hello/hi" +-Note: this test was ported from the old marker tests, which did not enable +-unimported completion. Enabling it causes matches in e.g. crypto/rand. - --func main() { -- _ = hi.Goodbye +--- settings.json -- +-{ +- "completeUnimported": false -} --` -- WithOptions(ProxyFiles(proxy)).Run(t, shouldRemoveDep, func(t *testing.T, env *Env) { -- env.OpenFile("go.mod") -- env.ExecuteCodeLensCommand("go.mod", command.Tidy, nil) -- env.Await(env.DoneWithChangeWatchedFiles()) -- got := env.BufferText("go.mod") -- const wantGoMod = `module mod.com - --go 1.14 +--- go.mod -- +-module bad.test - --require golang.org/x/hello v1.0.0 --` -- if got != wantGoMod { -- t.Fatalf("go.mod tidy failed:\n%s", compare.Text(wantGoMod, got)) -- } -- }) +-go 1.18 +- +--- bad/bad0.go -- +-package bad +- +-func stuff() { //@item(stuff, "stuff", "func()", "func") +- x := "heeeeyyyy" +- random2(x) //@diag("x", re"cannot use x \\(variable of type string\\) as int value in argument to random2") +- random2(1) //@complete("dom", random, random2, random3) +- y := 3 //@diag("y", re"y declared (and|but) not used") -} - --func TestRegenerateCgo(t *testing.T) { -- testenv.NeedsTool(t, "cgo") -- const workspace = ` ---- go.mod -- --module example.com +-type bob struct { //@item(bob, "bob", "struct{...}", "struct") +- x int +-} - --go 1.12 ---- cgo.go -- --package x +-func _() { +- var q int +- _ = &bob{ +- f: q, //@diag("f: q", re"unknown field f in struct literal") +- } +-} - --/* --int fortythree() { return 42; } --*/ --import "C" +--- bad/bad1.go -- +-package bad - --func Foo() { -- print(C.fortytwo()) +-// See #36637 +-type stateFunc func() stateFunc //@item(stateFunc, "stateFunc", "func() stateFunc", "type") +- +-var a unknown //@item(global_a, "a", "unknown", "var"),diag("unknown", re"(undeclared name|undefined): unknown") +- +-func random() int { //@item(random, "random", "func() int", "func") +- //@complete("", global_a, bob, random, random2, random3, stateFunc, stuff) +- return 0 -} --` -- 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.AfterChange( -- Diagnostics(env.AtRegexp("cgo.go", ``), WithMessage("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.AfterChange( -- Diagnostics(env.AtRegexp("cgo.go", ``), WithMessage("go list failed to return CompiledGoFiles")), -- ) +-func random2(y int) int { //@item(random2, "random2", "func(y int) int", "func"),item(bad_y_param, "y", "int", "var") +- x := 6 //@item(x, "x", "int", "var"),diag("x", re"x declared (and|but) not used") +- var q blah //@item(q, "q", "blah", "var"),diag("q", re"q declared (and|but) not used"),diag("blah", re"(undeclared name|undefined): blah") +- var t **blob //@item(t, "t", "**blob", "var"),diag("t", re"t declared (and|but) not used"),diag("blob", re"(undeclared name|undefined): blob") +- //@complete("", q, t, x, bad_y_param, global_a, bob, random, random2, random3, stateFunc, stuff) - -- // Regenerate cgo, fixing the diagnostic. -- env.ExecuteCodeLensCommand("cgo.go", command.RegenerateCgo, nil) -- env.Await(NoDiagnostics(ForFile("cgo.go"))) -- }) +- return y -} -diff -urN a/gopls/internal/regtest/codelens/gcdetails_test.go b/gopls/internal/regtest/codelens/gcdetails_test.go ---- a/gopls/internal/regtest/codelens/gcdetails_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/codelens/gcdetails_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,127 +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 +-func random3(y ...int) { //@item(random3, "random3", "func(y ...int)", "func"),item(y_variadic_param, "y", "[]int", "var") +- //@complete("", y_variadic_param, global_a, bob, random, random2, random3, stateFunc, stuff) - --import ( -- "runtime" -- "strings" -- "testing" +- var ch chan (favType1) //@item(ch, "ch", "chan (favType1)", "var"),diag("ch", re"ch declared (and|but) not used"),diag("favType1", re"(undeclared name|undefined): favType1") +- var m map[keyType]int //@item(m, "m", "map[keyType]int", "var"),diag("m", re"m declared (and|but) not used"),diag("keyType", re"(undeclared name|undefined): keyType") +- var arr []favType2 //@item(arr, "arr", "[]favType2", "var"),diag("arr", re"arr declared (and|but) not used"),diag("favType2", re"(undeclared name|undefined): favType2") +- var fn1 func() badResult //@item(fn1, "fn1", "func() badResult", "var"),diag("fn1", re"fn1 declared (and|but) not used"),diag("badResult", re"(undeclared name|undefined): badResult") +- var fn2 func(badParam) //@item(fn2, "fn2", "func(badParam)", "var"),diag("fn2", re"fn2 declared (and|but) not used"),diag("badParam", re"(undeclared name|undefined): badParam") +- //@complete("", arr, ch, fn1, fn2, m, y_variadic_param, global_a, bob, random, random2, random3, stateFunc, stuff) +-} +diff -urN a/gopls/internal/regtest/marker/testdata/completion/foobarbaz.txt b/gopls/internal/regtest/marker/testdata/completion/foobarbaz.txt +--- a/gopls/internal/regtest/marker/testdata/completion/foobarbaz.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/completion/foobarbaz.txt 1970-01-01 08:00:00 +@@ -1,541 +0,0 @@ +-This test ports some arbitrary tests from the old marker framework, that were +-*mostly* about completion. - -- "golang.org/x/tools/gopls/internal/lsp/command" -- "golang.org/x/tools/gopls/internal/lsp/fake" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/internal/bug" --) +--- flags -- +--ignore_extra_diags +--min_go=go1.20 - --func TestGCDetails_Toggle(t *testing.T) { -- if runtime.GOOS == "android" { -- t.Skipf("the gc details code lens doesn't work on Android") -- } +--- settings.json -- +-{ +- "completeUnimported": false, +- "deepCompletion": false, +- "experimentalPostfixCompletions": false +-} - -- const mod = ` --- go.mod -- --module mod.com +-module foobar.test - --go 1.15 ---- main.go -- --package main +-go 1.18 - --import "fmt" +--- foo/foo.go -- +-package foo //@loc(PackageFoo, "foo"),item(PackageFooItem, "foo", "\"foobar.test/foo\"", "package") - --func main() { -- fmt.Println(42) +-type StructFoo struct { //@loc(StructFooLoc, "StructFoo"), item(StructFoo, "StructFoo", "struct{...}", "struct") +- Value int //@item(Value, "Value", "int", "field") -} --` -- WithOptions( -- Settings{ -- "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, nil) -- d := &protocol.PublishDiagnosticsParams{} -- env.OnceMet( -- Diagnostics(AtPosition("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.AfterChange(NoDiagnostics(ForFile("main.go"))) +-// Pre-set this marker, as we don't have a "source" for it in this package. +-/* Error() */ //@item(Error, "Error", "func() string", "method") - -- // Saving a buffer should re-format back to the original state, and -- // re-enable the gc_details diagnostics. -- env.SaveBuffer("main.go") -- env.AfterChange(Diagnostics(AtPosition("main.go", 5, 13))) +-func Foo() { //@item(Foo, "Foo", "func()", "func") +- var err error +- err.Error() //@complete("E", Error) +-} - -- // Toggle the GC details code lens again so now it should be off. -- env.ExecuteCodeLensCommand("main.go", command.GCDetails, nil) -- env.Await(NoDiagnostics(ForFile("main.go"))) -- }) +-func _() { +- var sFoo StructFoo //@complete("t", StructFoo) +- if x := sFoo; x.Value == 1 { //@complete("V", Value), typedef("sFoo", StructFooLoc) +- return +- } +-} +- +-func _() { +- shadowed := 123 +- { +- shadowed := "hi" //@item(shadowed, "shadowed", "string", "var") +- sha //@complete("a", shadowed), diag("sha", re"(undefined|undeclared)") +- _ = shadowed +- } -} - --// Test for the crasher in golang/go#54199 --func TestGCDetails_NewFile(t *testing.T) { -- bug.PanicOnBugs = false -- const src = ` ---- go.mod -- --module mod.test +-type IntFoo int //@loc(IntFooLoc, "IntFoo"), item(IntFoo, "IntFoo", "int", "type") - --go 1.12 --` +--- bar/bar.go -- +-package bar - -- WithOptions( -- Settings{ -- "codelenses": map[string]bool{ -- "gc_details": true, -- }, -- }, -- ).Run(t, src, func(t *testing.T, env *Env) { -- env.CreateBuffer("p_test.go", "") +-import ( +- "foobar.test/foo" //@item(foo, "foo", "\"foobar.test/foo\"", "package") +-) - -- const gcDetailsCommand = "gopls." + string(command.GCDetails) +-func helper(i foo.IntFoo) {} //@item(helper, "helper", "func(i foo.IntFoo)", "func") - -- hasGCDetails := func() bool { -- lenses := env.CodeLens("p_test.go") // should not crash -- for _, lens := range lenses { -- if lens.Command.Command == gcDetailsCommand { -- return true -- } -- } -- return false -- } +-func _() { +- help //@complete("l", helper) +- _ = foo.StructFoo{} //@complete("S", IntFoo, StructFoo) +-} - -- // With an empty file, we shouldn't get the gc_details codelens because -- // there is nowhere to position it (it needs a package name). -- if hasGCDetails() { -- t.Errorf("got the gc_details codelens for an empty file") -- } +-// Bar is a function. +-func Bar() { //@item(Bar, "Bar", "func()", "func", "Bar is a function.") +- foo.Foo() //@complete("F", Foo, IntFoo, StructFoo) +- var _ foo.IntFoo //@complete("I", IntFoo, StructFoo) +- foo.() //@complete("(", Foo, IntFoo, StructFoo), diag(")", re"expected type") +-} - -- // Edit to provide a package name. -- env.EditBuffer("p_test.go", fake.NewEdit(0, 0, 0, 0, "package p")) +-// These items weren't present in the old marker tests (due to settings), but +-// we may as well include them. +-//@item(intConversion, "int()"), item(fooFoo, "foo.Foo") +-//@item(fooIntFoo, "foo.IntFoo"), item(fooStructFoo, "foo.StructFoo") - -- // Now we should get the gc_details codelens. -- if !hasGCDetails() { -- t.Errorf("didn't get the gc_details codelens for a valid non-empty Go file") -- } -- }) --} -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 00:00:00.000000000 +0000 -@@ -1,124 +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. +-func _() { +- var Valentine int //@item(Valentine, "Valentine", "int", "var") - --//go:build go1.18 --// +build go1.18 +- _ = foo.StructFoo{ //@diag("foo", re"unkeyed fields") +- Valu //@complete(" //", Value) +- } +- _ = foo.StructFoo{ //@diag("foo", re"unkeyed fields") +- Va //@complete("a", Value, Valentine) - --package completion +- } +- _ = foo.StructFoo{ +- Value: 5, //@complete("a", Value) +- } +- _ = foo.StructFoo{ +- //@complete("//", Value, Valentine, intConversion, foo, helper, Bar) +- } +- _ = foo.StructFoo{ +- Value: Valen //@complete("le", Valentine) +- } +- _ = foo.StructFoo{ +- Value: //@complete(" //", Valentine, intConversion, foo, helper, Bar) +- } +- _ = foo.StructFoo{ +- Value: //@complete(" ", Valentine, intConversion, foo, helper, Bar) +- } +-} +- +--- baz/baz.go -- +-package baz - -import ( -- "testing" +- "foobar.test/bar" - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" +- f "foobar.test/foo" -) - --// 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) {} --` +-var FooStruct f.StructFoo - -- 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 { -- loc := env.RegexpSearch("main.go", tst.pat) -- loc.Range.Start.Character += uint32(protocol.UTF16Len([]byte(tst.pat))) -- completions := env.Completion(loc) -- result := compareCompletionLabels(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 Baz() { +- defer bar.Bar() //@complete("B", Bar) +- // TODO: Test completion here. +- defer bar.B //@diag(re"bar.B()", re"must be function call") +- var x f.IntFoo //@complete("n", IntFoo), typedef("x", IntFooLoc) +- bar.Bar() //@complete("B", Bar) -} --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) +-func _() { +- bob := f.StructFoo{Value: 5} +- if x := bob. //@complete(" //", Value) +- switch true == false { +- case true: +- if x := bob. //@complete(" //", Value) +- case false: - } -- 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 uint32 // UTF16 length 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"}}, +- if x := bob.Va //@complete("a", Value) +- switch true == true { +- default: - } -- Run(t, data, func(t *testing.T, env *Env) { -- for _, test := range tests { -- env.OpenFile(test.file) -- env.Await(env.DoneWithOpen()) -- loc := env.RegexpSearch(test.file, test.pat) -- loc.Range.Start.Character += test.offset // character user just typed? will type? -- completions := env.Completion(loc) -- result := compareCompletionLabels(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 00:00:00.000000000 +0000 -@@ -1,754 +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 +--- arraytype/arraytype.go -- +-package arraytype - -import ( -- "fmt" -- "strings" -- "testing" +- "foobar.test/foo" +-) - -- "github.com/google/go-cmp/cmp" -- "golang.org/x/tools/gopls/internal/hooks" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/internal/bug" -- "golang.org/x/tools/internal/testenv" +-func _() { +- var ( +- val string //@item(atVal, "val", "string", "var") +- ) - -- "golang.org/x/tools/gopls/internal/lsp/protocol" --) +- [] //@complete(" //", atVal, PackageFooItem) - --func TestMain(m *testing.M) { -- bug.PanicOnBugs = true -- Main(m, hooks.Options) +- []val //@complete(" //") +- +- []foo.StructFoo //@complete(" //", StructFoo) +- +- []foo.StructFoo(nil) //@complete("(", StructFoo) +- +- []*foo.StructFoo //@complete(" //", StructFoo) +- +- [...]foo.StructFoo //@complete(" //", StructFoo) +- +- [2][][4]foo.StructFoo //@complete(" //", StructFoo) +- +- []struct { f []foo.StructFoo } //@complete(" }", StructFoo) -} - --const proxy = ` ---- example.com@v1.2.3/go.mod -- --module example.com +-func _() { +- type myInt int //@item(atMyInt, "myInt", "int", "type") - --go 1.12 ---- example.com@v1.2.3/blah/blah.go -- --package blah +- var mark []myInt //@item(atMark, "mark", "[]myInt", "var") - --const Name = "Blah" ---- random.org@v1.2.3/go.mod -- --module random.org +- var s []myInt //@item(atS, "s", "[]myInt", "var") +- s = []m //@complete(" //", atMyInt) - --go 1.12 ---- random.org@v1.2.3/blah/blah.go -- --package hello +- var a [1]myInt +- a = [1]m //@complete(" //", atMyInt) - --const Name = "Hello" --` +- var ds [][]myInt +- ds = [][]m //@complete(" //", atMyInt) +-} - --func TestPackageCompletion(t *testing.T) { -- const files = ` ---- go.mod -- --module mod.com +-func _() { +- var b [0]byte //@item(atByte, "b", "[0]byte", "var") +- var _ []byte = b //@snippet(" //", atByte, "b[:]") +-} - --go 1.12 ---- fruits/apple.go -- --package apple +--- badstmt/badstmt.go -- +-package badstmt - --fun apple() int { -- return 0 +-import ( +- "foobar.test/foo" +-) +- +-// (The syntax error causes suppression of diagnostics for type errors. +-// See issue #59888.) +- +-func _(x int) { +- defer foo.F //@complete(" //", Foo, IntFoo, StructFoo) +- defer foo.F //@complete(" //", Foo, IntFoo, StructFoo) -} - ---- fruits/testfile.go -- --// this is a comment +-func _() { +- switch true { +- case true: +- go foo.F //@complete(" //", Foo, IntFoo, StructFoo) +- } +-} - --/* -- this is a multiline comment --*/ +-func _() { +- defer func() { +- foo.F //@complete(" //", Foo, IntFoo, StructFoo), snippet(" //", Foo, "Foo()") - --import "fmt" +- foo. //@rank(" //", Foo) +- } +-} - --func test() {} +--- badstmt/badstmt_2.go -- +-package badstmt - ---- fruits/testfile2.go -- --package +-import ( +- "foobar.test/foo" +-) - ---- fruits/testfile3.go -- --pac ---- 123f_r.u~its-123/testfile.go -- --package +-func _() { +- defer func() { foo. } //@rank(" }", Foo) +-} - ---- .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(env.RegexpSearch(tc.filename, tc.triggerRegexp)) +--- badstmt/badstmt_3.go -- +-package badstmt - -- // Check that the completion item suggestions are in the range -- // of the file. {Start,End}.Line are zero-based. -- lineCount := len(strings.Split(env.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 <= %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 <= %d", end, lineCount) -- } -- } +-import ( +- "foobar.test/foo" +-) - -- if tc.want != nil { -- expectedLoc := env.RegexpSearch(tc.filename, tc.editRegexp) -- for _, item := range completions.Items { -- gotRng := item.TextEdit.Range -- if expectedLoc.Range != gotRng { -- t.Errorf("unexpected completion range for completion item %s: got %v, want %v", -- item.Label, gotRng, expectedLoc.Range) -- } -- } -- } +-func _() { +- go foo. //@rank(" //", Foo, IntFoo), snippet(" //", Foo, "Foo()") +-} - -- diff := compareCompletionLabels(tc.want, completions.Items) -- if diff != "" { -- t.Error(diff) -- } -- }) -- }) +--- badstmt/badstmt_4.go -- +-package badstmt +- +-import ( +- "foobar.test/foo" +-) +- +-func _() { +- go func() { +- defer foo. //@rank(" //", Foo, IntFoo) - } -} - --func TestPackageNameCompletion(t *testing.T) { -- const files = ` ---- go.mod -- --module mod.com +--- selector/selector.go -- +-package selector - --go 1.12 ---- math/add.go -- --package ma --` +-import ( +- "foobar.test/bar" +-) - -- 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(env.RegexpSearch("math/add.go", "package ma()")) +-type S struct { +- B, A, C int //@item(Bf, "B", "int", "field"),item(Af, "A", "int", "field"),item(Cf, "C", "int", "field") +-} - -- diff := compareCompletionLabels(want, completions.Items) -- if diff != "" { -- t.Fatal(diff) -- } -- }) +-func _() { +- _ = S{}.; //@complete(";", Af, Bf, Cf) -} - --// TODO(rfindley): audit/clean up call sites for this helper, to ensure --// consistent test errors. --func compareCompletionLabels(want []string, gotItems []protocol.CompletionItem) string { -- 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) -- } -- } +-type bob struct { a int } //@item(a, "a", "int", "field") +-type george struct { b int } +-type jack struct { c int } //@item(c, "c", "int", "field") +-type jill struct { d int } +- +-func (b *bob) george() *george {} //@item(george, "george", "func() *george", "method") +-func (g *george) jack() *jack {} +-func (j *jack) jill() *jill {} //@item(jill, "jill", "func() *jill", "method") +- +-func _() { +- b := &bob{} +- y := b.george(). +- jack(); +- y.; //@complete(";", c, jill) +-} +- +-func _() { +- bar. //@complete(" /", Bar) +- x := 5 +- +- var b *bob +- b. //@complete(" /", a, george) +- y, z := 5, 6 - -- if len(got) == 0 && len(want) == 0 { -- return "" // treat nil and the empty slice as equivalent -- } +- b. //@complete(" /", a, george) +- y, z, a, b, c := 5, 6 +-} - -- if diff := cmp.Diff(want, got); diff != "" { -- return fmt.Sprintf("completion item mismatch (-want +got):\n%s", diff) -- } -- return "" +-func _() { +- bar. //@complete(" /", Bar) +- bar.Bar() +- +- bar. //@complete(" /", Bar) +- go f() -} - --func TestUnimportedCompletion(t *testing.T) { -- const mod = ` ---- go.mod -- --module mod.com +-func _() { +- var b *bob +- if y != b. //@complete(" /", a, george) +- z := 5 - --go 1.14 +- if z + y + 1 + b. //@complete(" /", a, george) +- r, s, t := 4, 5 - --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 +- if y != b. //@complete(" /", a, george) +- z = 5 - --func main() { -- _ = blah +- if z + y + 1 + b. //@complete(" /", a, george) +- r = 4 -} ---- main2.go -- --package main - --import "example.com/blah" +--- literal_snippets/literal_snippets.go -- +-package literal_snippets +- +-import ( +- "bytes" +- "context" +- "go/ast" +- "net/http" +- "sort" +- +- "golang.org/lsptests/foo" +-) - -func _() { -- _ = blah.Hello +- []int{} //@item(litIntSlice, "[]int{}", "", "var") +- &[]int{} //@item(litIntSliceAddr, "&[]int{}", "", "var") +- make([]int, 0) //@item(makeIntSlice, "make([]int, 0)", "", "func") +- +- var _ *[]int = in //@snippet(" //", litIntSliceAddr, "&[]int{$0\\}") +- var _ **[]int = in //@complete(" //") +- +- var slice []int +- slice = i //@snippet(" //", litIntSlice, "[]int{$0\\}") +- slice = m //@snippet(" //", makeIntSlice, "make([]int, ${1:})") -} --` -- 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()) -- loc := env.RegexpSearch("main.go", "ah") -- completions := env.Completion(loc) -- if len(completions.Items) == 0 { -- t.Fatalf("no completion items") -- } -- env.AcceptCompletion(loc, completions.Items[0]) // adds blah import to main.go -- env.Await(env.DoneWithChange()) +-func _() { +- type namedInt []int - -- // Trigger completions once again for the blah.<> selector. -- env.RegexpReplace("main.go", "_ = blah", "_ = blah.") -- env.Await(env.DoneWithChange()) -- loc = env.RegexpSearch("main.go", "\n}") -- completions = env.Completion(loc) -- 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(loc, item) +- namedInt{} //@item(litNamedSlice, "namedInt{}", "", "var") +- make(namedInt, 0) //@item(makeNamedSlice, "make(namedInt, 0)", "", "func") - -- // Await the diagnostics to add example.com/blah to the go.mod file. -- env.AfterChange( -- Diagnostics(env.AtRegexp("main.go", `"example.com/blah"`)), -- ) -- }) +- var namedSlice namedInt +- namedSlice = n //@snippet(" //", litNamedSlice, "namedInt{$0\\}") +- namedSlice = m //@snippet(" //", makeNamedSlice, "make(namedInt, ${1:})") -} - --// 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 +-func _() { +- make(chan int) //@item(makeChan, "make(chan int)", "", "func") - --go 1.14 +- var ch chan int +- ch = m //@snippet(" //", makeChan, "make(chan int)") +-} - --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 +-func _() { +- map[string]struct{}{} //@item(litMap, "map[string]struct{}{}", "", "var") +- make(map[string]struct{}) //@item(makeMap, "make(map[string]struct{})", "", "func") - --package pkg --import "example.com/blah" --var _ = blah.Name ---- mainmod/mainmod.go -- --package mainmod +- var m map[string]struct{} +- m = m //@snippet(" //", litMap, "map[string]struct{\\}{$0\\}") +- m = m //@snippet(" //", makeMap, "make(map[string]struct{\\})") - --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) -- } -- }) +- struct{}{} //@item(litEmptyStruct, "struct{}{}", "", "var") +- +- m["hi"] = s //@snippet(" //", litEmptyStruct, "struct{\\}{\\}") -} - --// 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 +-func _() { +- type myStruct struct{ i int } //@item(myStructType, "myStruct", "struct{...}", "struct") - --go 1.12 ---- foo.go -- --package foo +- myStruct{} //@item(litStruct, "myStruct{}", "", "var") +- &myStruct{} //@item(litStructPtr, "&myStruct{}", "", "var") - --func _() { -- var s S -- if s. --} +- var ms myStruct +- ms = m //@snippet(" //", litStruct, "myStruct{$0\\}") - --type S struct { -- i int --} --` +- var msPtr *myStruct +- msPtr = m //@snippet(" //", litStructPtr, "&myStruct{$0\\}") - -- Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("foo.go") -- completions := env.Completion(env.RegexpSearch("foo.go", `if s\.()`)) -- diff := compareCompletionLabels([]string{"i"}, completions.Items) -- if diff != "" { -- t.Fatal(diff) -- } -- }) +- msPtr = &m //@snippet(" //", litStruct, "myStruct{$0\\}") +- +- type myStructCopy struct { i int } //@item(myStructCopyType, "myStructCopy", "struct{...}", "struct") +- +- // Don't offer literal completion for convertible structs. +- ms = myStruct //@complete(" //", litStruct, myStructType, myStructCopyType) -} - --func TestCompletion_Issue45510(t *testing.T) { -- const files = ` ---- go.mod -- --module mod.com +-type myImpl struct{} - --go 1.12 ---- main.go -- --package main +-func (myImpl) foo() {} - --func _() { -- type a *a -- var aaaa1, aaaa2 a -- var _ a = aaaa +-func (*myImpl) bar() {} - -- type b a -- var bbbb1, bbbb2 b -- var _ b = bbbb --} +-type myBasicImpl string - --type ( -- c *d -- d *e -- e **c --) +-func (myBasicImpl) foo() {} - -func _() { -- var ( -- xxxxc c -- xxxxd d -- xxxxe e -- ) +- type myIntf interface { +- foo() +- } - -- var _ c = xxxx -- var _ d = xxxx -- var _ e = xxxx --} --` +- myImpl{} //@item(litImpl, "myImpl{}", "", "var") - -- Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("main.go") +- var mi myIntf +- mi = m //@snippet(" //", litImpl, "myImpl{\\}") - -- 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(env.RegexpSearch("main.go", tt.re)) -- diff := compareCompletionLabels(tt.want, completions.Items) -- if diff != "" { -- t.Errorf("%s: %s", tt.re, diff) -- } -- } -- }) --} +- myBasicImpl() //@item(litBasicImpl, "myBasicImpl()", "string", "var") - --func TestCompletionDeprecation(t *testing.T) { -- const files = ` ---- go.mod -- --module test.com +- mi = m //@snippet(" //", litBasicImpl, "myBasicImpl($0)") - --go 1.16 ---- prog.go -- --package waste --// Deprecated, use newFoof --func fooFunc() bool { -- return false --} +- // only satisfied by pointer to myImpl +- type myPtrIntf interface { +- bar() +- } - --// Deprecated --const badPi = 3.14 +- &myImpl{} //@item(litImplPtr, "&myImpl{}", "", "var") - --func doit() { -- if fooF -- panic() -- x := badP --} --` -- Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("prog.go") -- loc := env.RegexpSearch("prog.go", "if fooF") -- loc.Range.Start.Character += uint32(protocol.UTF16Len([]byte("if fooF"))) -- completions := env.Completion(loc) -- diff := compareCompletionLabels([]string{"fooFunc"}, completions.Items) -- if diff != "" { -- t.Error(diff) -- } -- if completions.Items[0].Tags == nil { -- t.Errorf("expected Tags to show deprecation %#v", completions.Items[0].Tags) -- } -- loc = env.RegexpSearch("prog.go", "= badP") -- loc.Range.Start.Character += uint32(protocol.UTF16Len([]byte("= badP"))) -- completions = env.Completion(loc) -- diff = compareCompletionLabels([]string{"badPi"}, completions.Items) -- if diff != "" { -- t.Error(diff) -- } -- if completions.Items[0].Tags == nil { -- t.Errorf("expected Tags to show deprecation %#v", completions.Items[0].Tags) -- } -- }) +- var mpi myPtrIntf +- mpi = m //@snippet(" //", litImplPtr, "&myImpl{\\}") -} - --func TestUnimportedCompletion_VSCodeIssue1489(t *testing.T) { -- const src = ` ---- go.mod -- --module mod.com +-func _() { +- var s struct{ i []int } //@item(litSliceField, "i", "[]int", "field") +- var foo []int +- // no literal completions after selector +- foo = s.i //@complete(" //", litSliceField) +-} - --go 1.14 +-func _() { +- type myStruct struct{ i int } //@item(litMyStructType, "myStruct", "struct{...}", "struct") +- myStruct{} //@item(litMyStruct, "myStruct{}", "", "var") - ---- main.go -- --package main +- foo := func(s string, args ...myStruct) {} +- // Don't give literal slice candidate for variadic arg. +- // Do give literal candidates for variadic element. +- foo("", myStruct) //@complete(")", litMyStruct, litMyStructType) +-} - --import "fmt" +-func _() { +- Buffer{} //@item(litBuffer, "Buffer{}", "", "var") - --func main() { -- fmt.Println("a") -- math.Sqr --} --` -- WithOptions( -- WindowsLineEndings(), -- ).Run(t, src, func(t *testing.T, env *Env) { -- // Trigger unimported completions for the mod.com package. -- env.OpenFile("main.go") -- env.Await(env.DoneWithOpen()) -- loc := env.RegexpSearch("main.go", "Sqr()") -- completions := env.Completion(loc) -- if len(completions.Items) == 0 { -- t.Fatalf("no completion items") -- } -- env.AcceptCompletion(loc, completions.Items[0]) -- env.Await(env.DoneWithChange()) -- got := env.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 diff := cmp.Diff(want, got); diff != "" { -- t.Errorf("unimported completion (-want +got):\n%s", diff) -- } -- }) +- var b *bytes.Buffer +- b = bytes.Bu //@snippet(" //", litBuffer, "Buffer{\\}") -} - --func TestPackageMemberCompletionAfterSyntaxError(t *testing.T) { -- // This test documents the current broken behavior due to golang/go#58833. -- const src = ` ---- go.mod -- --module mod.com +-func _() { +- _ = "func(...) {}" //@item(litFunc, "func(...) {}", "", "var") - --go 1.14 +- // no literal "func" completions +- http.Handle("", fun) //@complete(")") - ---- main.go -- --package main +- var namedReturn func(s string) (b bool) +- namedReturn = f //@snippet(" //", litFunc, "func(s string) (b bool) {$0\\}") - --import "math" +- var multiReturn func() (bool, int) +- multiReturn = f //@snippet(" //", litFunc, "func() (bool, int) {$0\\}") - --func main() { -- math.Sqrt(,0) -- math.Ldex --} --` -- Run(t, src, func(t *testing.T, env *Env) { -- env.OpenFile("main.go") -- env.Await(env.DoneWithOpen()) -- loc := env.RegexpSearch("main.go", "Ldex()") -- completions := env.Completion(loc) -- if len(completions.Items) == 0 { -- t.Fatalf("no completion items") -- } -- env.AcceptCompletion(loc, completions.Items[0]) -- env.Await(env.DoneWithChange()) -- got := env.BufferText("main.go") -- // The completion of math.Ldex after the syntax error on the -- // previous line is not "math.Ldexp" but "math.Ldexmath.Abs". -- // (In VSCode, "Abs" wrongly appears in the completion menu.) -- // This is a consequence of poor error recovery in the parser -- // causing "math.Ldex" to become a BadExpr. -- want := "package main\n\nimport \"math\"\n\nfunc main() {\n\tmath.Sqrt(,0)\n\tmath.Ldexmath.Abs(${1:})\n}\n" -- if diff := cmp.Diff(want, got); diff != "" { -- t.Errorf("unimported completion (-want +got):\n%s", diff) -- } -- }) --} +- var multiNamedReturn func() (b bool, i int) +- multiNamedReturn = f //@snippet(" //", litFunc, "func() (b bool, i int) {$0\\}") - --func TestDefinition(t *testing.T) { -- testenv.NeedsGo1Point(t, 17) // in go1.16, The FieldList in func x is not empty -- files := ` ---- go.mod -- --module mod.com +- var duplicateParams func(myImpl, int, myImpl) +- duplicateParams = f //@snippet(" //", litFunc, "func(mi1 myImpl, i int, mi2 myImpl) {$0\\}") - --go 1.18 ---- a_test.go -- --package foo --` -- tests := []struct { -- line string // the sole line in the buffer after the package statement -- pat string // the pattern to search for -- want []string // expected completions -- }{ -- {"func T", "T", []string{"TestXxx(t *testing.T)", "TestMain(m *testing.M)"}}, -- {"func T()", "T", []string{"TestMain", "Test"}}, -- {"func TestM", "TestM", []string{"TestMain(m *testing.M)", "TestM(t *testing.T)"}}, -- {"func TestM()", "TestM", []string{"TestMain"}}, -- {"func TestMi", "TestMi", []string{"TestMi(t *testing.T)"}}, -- {"func TestMi()", "TestMi", nil}, -- {"func TestG", "TestG", []string{"TestG(t *testing.T)"}}, -- {"func TestG(", "TestG", nil}, -- {"func Ben", "B", []string{"BenchmarkXxx(b *testing.B)"}}, -- {"func Ben(", "Ben", []string{"Benchmark"}}, -- {"func BenchmarkFoo", "BenchmarkFoo", []string{"BenchmarkFoo(b *testing.B)"}}, -- {"func BenchmarkFoo(", "BenchmarkFoo", nil}, -- {"func Fuz", "F", []string{"FuzzXxx(f *testing.F)"}}, -- {"func Fuz(", "Fuz", []string{"Fuzz"}}, -- {"func Testx", "Testx", nil}, -- {"func TestMe(t *testing.T)", "TestMe", nil}, -- {"func Te(t *testing.T)", "Te", []string{"TestMain", "Test"}}, -- } -- fname := "a_test.go" -- Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile(fname) -- env.Await(env.DoneWithOpen()) -- for _, test := range tests { -- env.SetBufferContent(fname, "package foo\n"+test.line) -- loc := env.RegexpSearch(fname, test.pat) -- loc.Range.Start.Character += uint32(protocol.UTF16Len([]byte(test.pat))) -- completions := env.Completion(loc) -- if diff := compareCompletionLabels(test.want, completions.Items); diff != "" { -- t.Error(diff) -- } -- } -- }) +- type aliasImpl = myImpl +- var aliasParams func(aliasImpl) aliasImpl +- aliasParams = f //@snippet(" //", litFunc, "func(ai aliasImpl) aliasImpl {$0\\}") +- +- const two = 2 +- var builtinTypes func([]int, [two]bool, map[string]string, struct{ i int }, interface{ foo() }, <-chan int) +- builtinTypes = f //@snippet(" //", litFunc, "func(i1 []int, b [two]bool, m map[string]string, s struct{ i int \\}, i2 interface{ foo() \\}, c <-chan int) {$0\\}") +- +- var _ func(ast.Node) = f //@snippet(" //", litFunc, "func(n ast.Node) {$0\\}") +- var _ func(error) = f //@snippet(" //", litFunc, "func(err error) {$0\\}") +- var _ func(context.Context) = f //@snippet(" //", litFunc, "func(ctx context.Context) {$0\\}") +- +- type context struct {} +- var _ func(context) = f //@snippet(" //", litFunc, "func(ctx context) {$0\\}") -} - --// Test that completing a definition replaces source text when applied, golang/go#56852. --// Note: With go <= 1.16 the completions does not add parameters and fails these tests. --func TestDefinitionReplaceRange(t *testing.T) { -- testenv.NeedsGo1Point(t, 17) +-func _() { +- float64() //@item(litFloat64, "float64()", "float64", "var") - -- const mod = ` ---- go.mod -- --module mod.com +- // don't complete to "&float64()" +- var _ *float64 = float64 //@complete(" //") - --go 1.17 --` +- var f float64 +- f = fl //@complete(" //", litFloat64),snippet(" //", litFloat64, "float64($0)") - -- tests := []struct { -- name string -- before, after string -- }{ -- { -- name: "func TestMa", -- before: ` --package foo_test +- type myInt int +- myInt() //@item(litMyInt, "myInt()", "", "var") - --func TestMa --`, -- after: ` --package foo_test +- var mi myInt +- mi = my //@snippet(" //", litMyInt, "myInt($0)") +-} - --func TestMain(m *testing.M) --`, -- }, -- { -- name: "func TestSome", -- before: ` --package foo_test +-func _() { +- type ptrStruct struct { +- p *ptrStruct +- } - --func TestSome --`, -- after: ` --package foo_test +- ptrStruct{} //@item(litPtrStruct, "ptrStruct{}", "", "var") - --func TestSome(t *testing.T) --`, -- }, -- { -- name: "func Bench", -- before: ` --package foo_test +- ptrStruct{ +- p: &ptrSt, //@rank(",", litPtrStruct) +- } - --func Bench --`, -- // Note: Snippet with escaped }. -- after: ` --package foo_test +- &ptrStruct{} //@item(litPtrStructPtr, "&ptrStruct{}", "", "var") - --func Benchmark${1:Xxx}(b *testing.B) { -- $0 --\} --`, -- }, +- &ptrStruct{ +- p: ptrSt, //@rank(",", litPtrStructPtr) - } +-} +- +-func _() { +- f := func(...[]int) {} +- f() //@snippet(")", litIntSlice, "[]int{$0\\}") +-} +- - -- Run(t, mod, func(t *testing.T, env *Env) { -- env.CreateBuffer("foo_test.go", "") +-func _() { +- // don't complete to "untyped int()" +- []int{}[untyped] //@complete("] //") +-} - -- for _, tst := range tests { -- tst.before = strings.Trim(tst.before, "\n") -- tst.after = strings.Trim(tst.after, "\n") -- env.SetBufferContent("foo_test.go", tst.before) +-type Tree[T any] struct{} - -- loc := env.RegexpSearch("foo_test.go", tst.name) -- loc.Range.Start.Character = uint32(protocol.UTF16Len([]byte(tst.name))) -- completions := env.Completion(loc) -- if len(completions.Items) == 0 { -- t.Fatalf("no completion items") -- } +-func (tree Tree[T]) Do(f func(s T)) {} - -- env.AcceptCompletion(loc, completions.Items[0]) -- env.Await(env.DoneWithChange()) -- if buf := env.BufferText("foo_test.go"); buf != tst.after { -- t.Errorf("%s:incorrect completion: got %q, want %q", tst.name, buf, tst.after) -- } -- } -- }) +-func _() { +- var t Tree[string] +- t.Do(fun) //@complete(")", litFunc), snippet(")", litFunc, "func(s string) {$0\\}") -} +diff -urN a/gopls/internal/regtest/marker/testdata/completion/issue59096.txt b/gopls/internal/regtest/marker/testdata/completion/issue59096.txt +--- a/gopls/internal/regtest/marker/testdata/completion/issue59096.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/completion/issue59096.txt 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ +-This test exercises the panic in golang/go#59096: completing at a syntactic +-type-assert expression was panicking because gopls was translating it into +-a (malformed) selector expr. - --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 -- --` +-module example.com - -- Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("go.work") +--- a/a.go -- +-package a - -- 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(env.RegexpSearch("go.work", tt.re)) -- diff := compareCompletionLabels(tt.want, completions.Items) -- if diff != "" { -- t.Errorf("%s: %s", tt.re, diff) -- } -- } -- }) +-func _() { +- b.(foo) //@complete(re"b.()", B), diag("b", re"(undefined|undeclared name): b") -} -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 00:00:00.000000000 +0000 -@@ -1,464 +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 +-//@item(B, "B", "const (from \"example.com/b\")", "const") - --import ( -- "strings" -- "testing" +--- b/b.go -- +-package b - -- . "golang.org/x/tools/gopls/internal/lsp/regtest" --) +-const B = 0 +diff -urN a/gopls/internal/regtest/marker/testdata/completion/issue60545.txt b/gopls/internal/regtest/marker/testdata/completion/issue60545.txt +--- a/gopls/internal/regtest/marker/testdata/completion/issue60545.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/completion/issue60545.txt 1970-01-01 08:00:00 +@@ -1,28 +0,0 @@ +-This test checks that unimported completion is case-insensitive. - --func TestPostfixSnippetCompletion(t *testing.T) { -- const mod = ` --- go.mod -- --module mod.com +-module mod.test - --go 1.12 --` +-go 1.18 - -- cases := []struct { -- name string -- before, after string -- }{ -- { -- name: "sort", -- before: ` --package foo +--- main.go -- +-package main - --func _() { -- var foo []int -- foo.sort +-//@item(Print, "Print", "func (from \"fmt\")", "func") +-//@item(Printf, "Printf", "func (from \"fmt\")", "func") +-//@item(Println, "Println", "func (from \"fmt\")", "func") +- +-func main() { +- fmt.p //@complete(re"fmt.p()", Print, Printf, Println), diag("fmt", re"(undefined|undeclared)") -} --`, -- after: ` --package foo - --import "sort" +--- other.go -- +-package main +- +-// Including another package that imports "fmt" causes completion to use the +-// existing metadata, which is the codepath leading to golang/go#60545. +-import "fmt" - -func _() { -- var foo []int -- sort.Slice(foo, func(i, j int) bool { -- $0 --}) +- fmt.Println() -} --`, -- }, -- { -- name: "sort_renamed_sort_package", -- before: ` --package foo +diff -urN a/gopls/internal/regtest/marker/testdata/completion/issue62141.txt b/gopls/internal/regtest/marker/testdata/completion/issue62141.txt +--- a/gopls/internal/regtest/marker/testdata/completion/issue62141.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/completion/issue62141.txt 1970-01-01 08:00:00 +@@ -1,39 +0,0 @@ +-This test checks that we don't suggest completion to an untyped conversion such +-as "untyped float(abcdef)". - --import blahsort "sort" +--- main.go -- +-package main - --var j int +-func main() { +- abcdef := 32 //@diag("abcdef", re"not used") +- x := 1.0 / abcd //@acceptcompletion(re"abcd()", "abcdef", int), diag("x", re"not used"), diag("abcd", re"(undefined|undeclared)") - --func _() { -- var foo []int -- foo.sort +- // Verify that we don't suggest converting compatible untyped constants. +- const untypedConst = 42 +- y := 1.1 / untypedC //@acceptcompletion(re"untypedC()", "untypedConst", untyped), diag("y", re"not used"), diag("untypedC", re"(undefined|undeclared)") -} --`, -- after: ` --package foo - --import blahsort "sort" +--- @int/main.go -- +-package main - --var j int +-func main() { +- abcdef := 32 //@diag("abcdef", re"not used") +- x := 1.0 / float64(abcdef) //@acceptcompletion(re"abcd()", "abcdef", int), diag("x", re"not used"), diag("abcd", re"(undefined|undeclared)") - --func _() { -- var foo []int -- blahsort.Slice(foo, func(i, j2 int) bool { -- $0 --}) +- // Verify that we don't suggest converting compatible untyped constants. +- const untypedConst = 42 +- y := 1.1 / untypedC //@acceptcompletion(re"untypedC()", "untypedConst", untyped), diag("y", re"not used"), diag("untypedC", re"(undefined|undeclared)") -} --`, -- }, -- { -- name: "last", -- before: ` --package foo - --func _() { -- var s struct { i []int } -- s.i.last --} --`, -- after: ` --package foo +--- @untyped/main.go -- +-package main - --func _() { -- var s struct { i []int } -- s.i[len(s.i)-1] --} --`, -- }, -- { -- name: "reverse", -- before: ` --package foo +-func main() { +- abcdef := 32 //@diag("abcdef", re"not used") +- x := 1.0 / abcd //@acceptcompletion(re"abcd()", "abcdef", int), diag("x", re"not used"), diag("abcd", re"(undefined|undeclared)") - --func _() { -- var foo []int -- foo.reverse +- // Verify that we don't suggest converting compatible untyped constants. +- const untypedConst = 42 +- y := 1.1 / untypedConst //@acceptcompletion(re"untypedC()", "untypedConst", untyped), diag("y", re"not used"), diag("untypedC", re"(undefined|undeclared)") -} --`, -- after: ` +- +diff -urN a/gopls/internal/regtest/marker/testdata/completion/issue62560.txt b/gopls/internal/regtest/marker/testdata/completion/issue62560.txt +--- a/gopls/internal/regtest/marker/testdata/completion/issue62560.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/completion/issue62560.txt 1970-01-01 08:00:00 +@@ -1,19 +0,0 @@ +-This test verifies that completion of package members in unimported packages +-reflects their fuzzy score, even when those members are present in the +-transitive import graph of the main module. (For technical reasons, this was +-the nature of the regression in golang/go#62560.) +- +--- go.mod -- +-module mod.test +- +--- foo/foo.go -- -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] +- json.U //@rankl(re"U()", "Unmarshal", "InvalidUTF8Error"), diag("json", re"(undefined|undeclared)") -} - +--- bar/bar.go -- +-package bar +- +-import _ "encoding/json" +diff -urN a/gopls/internal/regtest/marker/testdata/completion/issue62676.txt b/gopls/internal/regtest/marker/testdata/completion/issue62676.txt +--- a/gopls/internal/regtest/marker/testdata/completion/issue62676.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/completion/issue62676.txt 1970-01-01 08:00:00 +@@ -1,63 +0,0 @@ +-This test verifies that unimported completion respects the usePlaceholders setting. +- +--- flags -- +--ignore_extra_diags +- +--- settings.json -- +-{ +- "usePlaceholders": false -} --`, -- }, -- { -- name: "slice_range", -- before: ` +- +--- go.mod -- +-module mod.test +- +-go 1.21 +- +--- foo/foo.go -- -package foo - -func _() { -- type myThing struct{} -- var foo []myThing -- foo.range +- // This uses goimports-based completion; TODO: this should insert snippets. +- os.Open //@acceptcompletion(re"Open()", "Open", open) -} --`, -- after: ` --package foo - -func _() { -- type myThing struct{} -- var foo []myThing -- for i, mt := range foo { -- $0 +- // This uses metadata-based completion. +- errors.New //@acceptcompletion(re"New()", "New", new) -} --} --`, -- }, -- { -- name: "append_stmt", -- before: ` +- +--- bar/bar.go -- +-package bar +- +-import _ "errors" // important: doesn't transitively import os. +- +--- @new/foo/foo.go -- -package foo - +-import "errors" +- -func _() { -- var foo []int -- foo.append +- // This uses goimports-based completion; TODO: this should insert snippets. +- os.Open //@acceptcompletion(re"Open()", "Open", open) -} --`, -- after: ` --package foo - -func _() { -- var foo []int -- foo = append(foo, $0) +- // This uses metadata-based completion. +- errors.New(${1:}) //@acceptcompletion(re"New()", "New", new) -} --`, -- }, -- { -- name: "append_expr", -- before: ` +- +--- @open/foo/foo.go -- -package foo - +-import "os" +- -func _() { -- var foo []int -- var _ []int = foo.append +- // This uses goimports-based completion; TODO: this should insert snippets. +- os.Open //@acceptcompletion(re"Open()", "Open", open) -} --`, -- after: ` --package foo - -func _() { -- var foo []int -- var _ []int = append(foo, $0) +- // This uses metadata-based completion. +- errors.New //@acceptcompletion(re"New()", "New", new) -} --`, -- }, -- { -- name: "slice_copy", -- before: ` +- +diff -urN a/gopls/internal/regtest/marker/testdata/completion/lit.txt b/gopls/internal/regtest/marker/testdata/completion/lit.txt +--- a/gopls/internal/regtest/marker/testdata/completion/lit.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/completion/lit.txt 1970-01-01 08:00:00 +@@ -1,49 +0,0 @@ +- +--- flags -- +--ignore_extra_diags +- +--- go.mod -- +-module mod.test +- +-go 1.18 +- +--- foo/foo.go -- -package foo - +-type StructFoo struct{ F int } +- +--- a.go -- +-package a +- +-import "mod.test/foo" +- -func _() { -- var foo []int -- foo.copy +- StructFoo{} //@item(litStructFoo, "StructFoo{}", "struct{...}", "struct") +- +- var sfp *foo.StructFoo +- // Don't insert the "&" before "StructFoo{}". +- sfp = foo.Str //@snippet(" //", litStructFoo, "StructFoo{$0\\}") +- +- var sf foo.StructFoo +- sf = foo.Str //@snippet(" //", litStructFoo, "StructFoo{$0\\}") +- sf = foo. //@snippet(" //", litStructFoo, "StructFoo{$0\\}") -} --`, -- after: ` --package foo +- +--- http.go -- +-package a +- +-import ( +- "net/http" +- "sort" +-) - -func _() { -- var foo []int -- fooCopy := make([]int, len(foo)) --copy(fooCopy, foo) +- sort.Slice(nil, fun) //@snippet(")", litFunc, "func(i, j int) bool {$0\\}") +- +- http.HandleFunc("", f) //@snippet(")", litFunc, "func(w http.ResponseWriter, r *http.Request) {$0\\}") - +- //@item(litFunc, "func(...) {}", "", "var") +- http.HandlerFunc() //@item(handlerFunc, "http.HandlerFunc()", "", "var") +- http.Handle("", http.HandlerFunc()) //@snippet("))", litFunc, "func(w http.ResponseWriter, r *http.Request) {$0\\}") +- http.Handle("", h) //@snippet(")", handlerFunc, "http.HandlerFunc($0)") -} --`, -- }, -- { -- name: "map_range", -- before: ` --package foo +diff -urN a/gopls/internal/regtest/marker/testdata/completion/testy.txt b/gopls/internal/regtest/marker/testdata/completion/testy.txt +--- a/gopls/internal/regtest/marker/testdata/completion/testy.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/completion/testy.txt 1970-01-01 08:00:00 +@@ -1,57 +0,0 @@ - --func _() { -- var foo map[string]int -- foo.range +--- flags -- +--ignore_extra_diags +- +--- go.mod -- +-module testy.test +- +-go 1.18 +- +--- types/types.go -- +-package types +- +- +--- signature/signature.go -- +-package signature +- +-type Alias = int +- +--- snippets/snippets.go -- +-package snippets +- +-import ( +- "testy.test/signature" +- t "testy.test/types" +-) +- +-func X(_ map[signature.Alias]t.CoolAlias) (map[signature.Alias]t.CoolAlias) { +- return nil -} --`, -- after: ` --package foo - --func _() { -- var foo map[string]int -- for k, v := range foo { -- $0 +--- testy/testy.go -- +-package testy +- +-func a() { //@item(funcA, "a", "func()", "func") +- //@complete("", funcA) -} +- +- +--- testy/testy_test.go -- +-package testy +- +-import ( +- "testing" +- +- sig "testy.test/signature" +- "testy.test/snippets" +-) +- +-func TestSomething(t *testing.T) { //@item(TestSomething, "TestSomething(t *testing.T)", "", "func") +- var x int //@loc(testyX, "x"), diag("x", re"x declared (and|but) not used") +- a() //@loc(testyA, "a") -} --`, -- }, -- { -- name: "map_clear", -- before: ` --package foo - -func _() { -- var foo map[string]int -- foo.clear +- _ = snippets.X(nil) //@signature("nil", "X(_ map[sig.Alias]types.CoolAlias) map[sig.Alias]types.CoolAlias") +- var _ sig.Alias -} --`, -- after: ` +diff -urN a/gopls/internal/regtest/marker/testdata/completion/unimported.txt b/gopls/internal/regtest/marker/testdata/completion/unimported.txt +--- a/gopls/internal/regtest/marker/testdata/completion/unimported.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/completion/unimported.txt 1970-01-01 08:00:00 +@@ -1,88 +0,0 @@ +- +--- flags -- +--ignore_extra_diags +- +--- go.mod -- +-module unimported.test +- +-go 1.18 +- +--- unimported/export_test.go -- +-package unimported +- +-var TestExport int //@item(testexport, "TestExport", "var (from \"unimported.test/unimported\")", "var") +- +--- signature/signature.go -- +-package signature +- +-func Foo() {} +- +--- foo/foo.go -- -package foo - +-type StructFoo struct{ F int } +- +--- baz/baz.go -- +-package baz +- +-import ( +- f "unimported.test/foo" +-) +- +-var FooStruct f.StructFoo +- +--- unimported/unimported.go -- +-package unimported +- -func _() { -- var foo map[string]int -- for k := range foo { -- delete(foo, k) --} +- http //@complete("p", http, httptest, httptrace, httputil) +- // container/ring is extremely unlikely to be imported by anything, so shouldn't have type information. +- ring.Ring //@complete(re"R()ing", ringring) +- signature.Foo //@complete("Foo", signaturefoo) - +- context.Bac //@complete(" //", contextBackground) -} --`, -- }, -- { -- name: "map_keys", -- before: ` --package foo +- +-// Create markers for unimported std lib packages. Only for use by this test. +-/* http */ //@item(http, "http", "\"net/http\"", "package") +-/* httptest */ //@item(httptest, "httptest", "\"net/http/httptest\"", "package") +-/* httptrace */ //@item(httptrace, "httptrace", "\"net/http/httptrace\"", "package") +-/* httputil */ //@item(httputil, "httputil", "\"net/http/httputil\"", "package") +- +-/* ring.Ring */ //@item(ringring, "Ring", "(from \"container/ring\")", "var") +- +-/* signature.Foo */ //@item(signaturefoo, "Foo", "func (from \"unimported.test/signature\")", "func") +- +-/* context.Background */ //@item(contextBackground, "Background", "func (from \"context\")", "func") +- +-// Now that we no longer type-check imported completions, +-// we don't expect the context.Background().Err method (see golang/go#58663). +-/* context.Background().Err */ //@item(contextBackgroundErr, "Background().Err", "func (from \"context\")", "method") +- +--- unimported/unimported_cand_type.go -- +-package unimported +- +-import ( +- _ "context" +- +- "unimported.test/baz" +-) - -func _() { -- var foo map[string]int -- foo.keys +- foo.StructFoo{} //@item(litFooStructFoo, "foo.StructFoo{}", "struct{...}", "struct") +- +- // We get the literal completion for "foo.StructFoo{}" even though we haven't +- // imported "foo" yet. +- baz.FooStruct = f //@snippet(" //", litFooStructFoo, "foo.StructFoo{$0\\}") -} --`, -- after: ` --package foo - --func _() { -- var foo map[string]int -- keys := make([]string, 0, len(foo)) --for k := range foo { -- keys = append(keys, k) +--- unimported/x_test.go -- +-package unimported_test +- +-import ( +- "testing" +-) +- +-func TestSomething(t *testing.T) { +- _ = unimported.TestExport //@complete("TestExport", testexport) -} +diff -urN a/gopls/internal/regtest/marker/testdata/definition/cgo.txt b/gopls/internal/regtest/marker/testdata/definition/cgo.txt +--- a/gopls/internal/regtest/marker/testdata/definition/cgo.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/definition/cgo.txt 1970-01-01 08:00:00 +@@ -1,62 +0,0 @@ +-This test is ported from the old marker tests. +-It tests hover and definition for cgo declarations. +- +--- flags -- +--cgo +- +--- go.mod -- +-module cgo.test +- +-go 1.18 +- +--- cgo/cgo.go -- +-package cgo +- +-/* +-#include <stdio.h> +-#include <stdlib.h> - +-void myprint(char* s) { +- printf("%s\n", s); -} --`, -- }, -- { -- name: "channel_range", -- before: ` --package foo +-*/ +-import "C" - --func _() { -- foo := make(chan int) -- foo.range +-import ( +- "fmt" +- "unsafe" +-) +- +-func Example() { //@loc(cgoexample, "Example"), item(cgoexampleItem, "Example", "func()", "func") +- fmt.Println() +- cs := C.CString("Hello from stdio\n") +- C.myprint(cs) +- C.free(unsafe.Pointer(cs)) -} --`, -- after: ` --package foo - -func _() { -- foo := make(chan int) -- for e := range foo { -- $0 --} +- Example() //@hover("ample", "Example", hoverExample), def("ample", cgoexample), complete("ample", cgoexampleItem) -} --`, -- }, -- { -- name: "var", -- before: ` --package foo - --func foo() (int, error) { return 0, nil } +--- @hoverExample/hover.md -- +-```go +-func Example() +-``` - --func _() { -- foo().var --} --`, -- after: ` --package foo +-[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/cgo.test/cgo#Example) +--- usecgo/usecgo.go -- +-package cgoimport - --func foo() (int, error) { return 0, nil } +-import ( +- "cgo.test/cgo" +-) - -func _() { -- i, err := foo() +- cgo.Example() //@hover("ample", "Example", hoverImportedExample), def("ample", cgoexample), complete("ample", cgoexampleItem) -} --`, -- }, -- { -- name: "var_single_value", -- before: ` --package foo +--- @hoverImportedExample/hover.md -- +-```go +-func cgo.Example() +-``` +- +-[`cgo.Example` on pkg.go.dev](https://pkg.go.dev/cgo.test/cgo#Example) +diff -urN a/gopls/internal/regtest/marker/testdata/definition/embed.txt b/gopls/internal/regtest/marker/testdata/definition/embed.txt +--- a/gopls/internal/regtest/marker/testdata/definition/embed.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/definition/embed.txt 1970-01-01 08:00:00 +@@ -1,254 +0,0 @@ +-This test checks definition and hover operations over embedded fields and methods. +- +--- go.mod -- +-module mod.com - --func foo() error { return nil } +-go 1.18 - --func _() { -- foo().var --} --`, -- after: ` --package foo +--- a/a.go -- +-package a - --func foo() error { return nil } +-type A string //@loc(AString, "A") - --func _() { -- err := foo() --} --`, -- }, -- { -- name: "var_same_type", -- before: ` --package foo +-func (_ A) Hi() {} //@loc(AHi, "Hi") - --func foo() (int, int) { return 0, 0 } +-type S struct { +- Field int //@loc(SField, "Field") +- R // embed a struct +- H // embed an interface +-} - --func _() { -- foo().var +-type R struct { +- Field2 int //@loc(RField2, "Field2") -} --`, -- after: ` --package foo - --func foo() (int, int) { return 0, 0 } +-func (_ R) Hey() {} //@loc(RHey, "Hey") - --func _() { -- i, i2 := foo() +-type H interface { //@loc(H, "H") +- Goodbye() //@loc(HGoodbye, "Goodbye") -} --`, -- }, -- { -- name: "print_scalar", -- before: ` --package foo - --func _() { -- var foo int -- foo.print +-type I interface { //@loc(I, "I") +- B() //@loc(IB, "B") +- J -} --`, -- after: ` --package foo -- --import "fmt" - --func _() { -- var foo int -- fmt.Printf("foo: %v\n", foo) +-type J interface { //@loc(J, "J") +- Hello() //@loc(JHello, "Hello") -} --`, -- }, -- { -- name: "print_multi", -- before: ` --package foo - --func foo() (int, error) { return 0, nil } +--- b/b.go -- +-package b - --func _() { -- foo().print +-import "mod.com/a" //@loc(AImport, re"\".*\"") +- +-type embed struct { +- F int //@loc(F, "F") -} --`, -- after: ` --package foo - --import "fmt" +-func (embed) M() //@loc(M, "M") - --func foo() (int, error) { return 0, nil } +-type Embed struct { +- embed +- *a.A +- a.I +- a.S +-} - -func _() { -- fmt.Println(foo()) +- e := Embed{} +- e.Hi() //@def("Hi", AHi),hover("Hi", "Hi", AHi) +- e.B() //@def("B", IB),hover("B", "B", IB) +- _ = e.Field //@def("Field", SField),hover("Field", "Field", SField) +- _ = e.Field2 //@def("Field2", RField2),hover("Field2", "Field2", RField2) +- e.Hello() //@def("Hello", JHello),hover("Hello", "Hello",JHello) +- e.Hey() //@def("Hey", RHey),hover("Hey", "Hey", RHey) +- e.Goodbye() //@def("Goodbye", HGoodbye),hover("Goodbye", "Goodbye", HGoodbye) +- e.M() //@def("M", M),hover("M", "M", M) +- _ = e.F //@def("F", F),hover("F", "F", F) -} --`, -- }, -- { -- 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 +-type aAlias = a.A //@loc(aAlias, "aAlias") - --func foo() string { -- x := []string{"a", "test"} -- return x.join --}`, -- after: ` --package foo +-type S1 struct { //@loc(S1, "S1") +- F1 int //@loc(S1F1, "F1") +- S2 //@loc(S1S2, "S2"),def("S2", S2),hover("S2", "S2", S2) +- a.A //@def("A", AString),hover("A", "A", aA) +- aAlias //@def("a", aAlias),hover("a", "aAlias", aAlias) +-} - --import "strings" +-type S2 struct { //@loc(S2, "S2") +- F1 string //@loc(S2F1, "F1") +- F2 int //@loc(S2F2, "F2") +- *a.A //@def("A", AString),def("a",AImport) +-} - --func foo() string { -- x := []string{"a", "test"} -- return strings.Join(x, "$0") --}`, -- }, +-type S3 struct { +- F1 struct { +- a.A //@def("A", AString) - } +-} - -- r := WithOptions( -- Settings{ -- "experimentalPostfixCompletions": true, -- }, -- ) -- r.Run(t, mod, func(t *testing.T, env *Env) { -- env.CreateBuffer("foo.go", "") +-func Bar() { +- var x S1 //@def("S1", S1),hover("S1", "S1", S1) +- _ = x.S2 //@def("S2", S1S2),hover("S2", "S2", S1S2) +- _ = x.F1 //@def("F1", S1F1),hover("F1", "F1", S1F1) +- _ = x.F2 //@def("F2", S2F2),hover("F2", "F2", S2F2) +- _ = x.S2.F1 //@def("F1", S2F1),hover("F1", "F1", S2F1) +-} - -- 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") +--- b/c.go -- +-package b - -- env.SetBufferContent("foo.go", c.before) +-var _ = S1{ //@def("S1", S1),hover("S1", "S1", S1) +- F1: 99, //@def("F1", S1F1),hover("F1", "F1", S1F1) +-} - -- loc := env.RegexpSearch("foo.go", "\n}") -- completions := env.Completion(loc) -- if len(completions.Items) != 1 { -- t.Fatalf("expected one completion, got %v", completions.Items) -- } +--- @AHi/hover.md -- +-```go +-func (a.A).Hi() +-``` - -- env.AcceptCompletion(loc, completions.Items[0]) +-[`(a.A).Hi` on pkg.go.dev](https://pkg.go.dev/mod.com/a#A.Hi) +--- @F/hover.md -- +-```go +-field F int +-``` - -- if buf := env.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 00:00:00.000000000 +0000 -@@ -1,30 +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. +-@loc(F, "F") - --package debug - --import ( -- "testing" +-[`(b.Embed).F` on pkg.go.dev](https://pkg.go.dev/mod.com/b#Embed.F) +--- @HGoodbye/hover.md -- +-```go +-func (a.H).Goodbye() +-``` - -- "golang.org/x/tools/gopls/internal/hooks" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/internal/bug" --) +-@loc(HGoodbye, "Goodbye") - --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(Default), // must be in-process to receive the bug report below -- Settings{"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/analysis_test.go b/gopls/internal/regtest/diagnostics/analysis_test.go ---- a/gopls/internal/regtest/diagnostics/analysis_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/diagnostics/analysis_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,49 +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. +-[`(a.H).Goodbye` on pkg.go.dev](https://pkg.go.dev/mod.com/a#H.Goodbye) +--- @IB/hover.md -- +-```go +-func (a.I).B() +-``` - --package diagnostics +-@loc(IB, "B") - --import ( -- "testing" - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" --) +-[`(a.I).B` on pkg.go.dev](https://pkg.go.dev/mod.com/a#I.B) +--- @JHello/hover.md -- +-```go +-func (a.J).Hello() +-``` - --// Test for the timeformat analyzer, following golang/vscode-go#2406. --// --// This test checks that applying the suggested fix from the analyzer resolves --// the diagnostic warning. --func TestTimeFormatAnalyzer(t *testing.T) { -- const files = ` ---- go.mod -- --module mod.com +-@loc(JHello, "Hello") - --go 1.18 ---- main.go -- --package main - --import ( -- "fmt" -- "time" --) +-[`(a.J).Hello` on pkg.go.dev](https://pkg.go.dev/mod.com/a#J.Hello) +--- @M/hover.md -- +-```go +-func (embed).M() +-``` - --func main() { -- now := time.Now() -- fmt.Println(now.Format("2006-02-01")) --}` +-[`(b.Embed).M` on pkg.go.dev](https://pkg.go.dev/mod.com/b#Embed.M) +--- @RField2/hover.md -- +-```go +-field Field2 int +-``` - -- Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("main.go") +-@loc(RField2, "Field2") - -- var d protocol.PublishDiagnosticsParams -- env.AfterChange( -- Diagnostics(env.AtRegexp("main.go", "2006-02-01")), -- ReadDiagnostics("main.go", &d), -- ) - -- env.ApplyQuickFixes("main.go", d.Diagnostics) -- env.AfterChange(NoDiagnostics(ForFile("main.go"))) -- }) +-[`(a.R).Field2` on pkg.go.dev](https://pkg.go.dev/mod.com/a#R.Field2) +--- @RHey/hover.md -- +-```go +-func (a.R).Hey() +-``` +- +-[`(a.R).Hey` on pkg.go.dev](https://pkg.go.dev/mod.com/a#R.Hey) +--- @S1/hover.md -- +-```go +-type S1 struct { +- F1 int //@loc(S1F1, "F1") +- S2 //@loc(S1S2, "S2"),def("S2", S2),hover("S2", "S2", S2) +- a.A //@def("A", AString),hover("A", "A", aA) +- aAlias //@def("a", aAlias),hover("a", "aAlias", aAlias) -} -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 00:00:00.000000000 +0000 -@@ -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 diagnostics +-[`b.S1` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S1) +--- @S1F1/hover.md -- +-```go +-field F1 int +-``` - --import ( -- "strings" -- "testing" +-@loc(S1F1, "F1") - -- . "golang.org/x/tools/gopls/internal/lsp/regtest" --) - --func TestIssue44866(t *testing.T) { -- src := ` ---- go.mod -- --module mod.com +-[`(b.S1).F1` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S1.F1) +--- @S1S2/hover.md -- +-```go +-field S2 S2 +-``` - --go 1.12 ---- a.go -- --package a +-@loc(S1S2, "S2"),def("S2", S2),hover("S2", "S2", S2) - --const ( -- c = iota --) --` -- Run(t, src, func(t *testing.T, env *Env) { -- env.OpenFile("a.go") -- loc := env.GoToDefinition(env.RegexpSearch("a.go", "iota")) -- if !strings.HasSuffix(string(loc.URI), "builtin.go") { -- t.Fatalf("jumped to %q, want builtin.go", loc.URI) -- } -- env.AfterChange(NoDiagnostics(ForFile("builtin.go"))) -- }) +- +-[`(b.S1).S2` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S1.S2) +--- @S2/hover.md -- +-```go +-type S2 struct { +- F1 string //@loc(S2F1, "F1") +- F2 int //@loc(S2F2, "F2") +- *a.A //@def("A", AString),def("a",AImport) -} -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 00:00:00.000000000 +0000 -@@ -1,2048 +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 +-[`b.S2` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S2) +--- @S2F1/hover.md -- +-```go +-field F1 string +-``` - --import ( -- "context" -- "fmt" -- "os/exec" -- "testing" +-@loc(S2F1, "F1") - -- "golang.org/x/tools/gopls/internal/hooks" -- "golang.org/x/tools/gopls/internal/lsp" -- "golang.org/x/tools/gopls/internal/lsp/fake" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/internal/bug" -- "golang.org/x/tools/internal/testenv" --) - --func TestMain(m *testing.M) { -- bug.PanicOnBugs = true -- Main(m, hooks.Options) --} +-[`(b.S2).F1` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S2.F1) +--- @S2F2/hover.md -- +-```go +-field F2 int +-``` - --// Use mod.com for all go.mod files due to golang/go#35230. --const exampleProgram = ` ---- go.mod -- --module mod.com +-@loc(S2F2, "F2") - --go 1.12 ---- main.go -- --package main - --import "fmt" +-[`(b.S2).F2` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S2.F2) +--- @SField/hover.md -- +-```go +-field Field int +-``` - --func main() { -- fmt.Println("Hello World.") --}` +-@loc(SField, "Field") - --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.AfterChange( -- Diagnostics(env.AtRegexp("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 +-[`(a.S).Field` on pkg.go.dev](https://pkg.go.dev/mod.com/a#S.Field) +--- @aA/hover.md -- +-```go +-type A string - --go 1.12 --` -- Run(t, onlyMod, func(t *testing.T, env *Env) { -- env.CreateBuffer("main.go", `package main +-func (a.A).Hi() +-``` - --func m() { -- log.Println() --} --`) -- env.AfterChange(Diagnostics(env.AtRegexp("main.go", "log"))) -- env.SaveBuffer("main.go") -- env.AfterChange(NoDiagnostics(ForFile("main.go"))) -- }) --} +-@loc(AString, "A") - --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.AfterChange(Diagnostics(env.AtRegexp("broken.go", "\"abc"))) -- }) --} +-[`a.A` on pkg.go.dev](https://pkg.go.dev/mod.com/a#A) +--- @aAlias/hover.md -- +-```go +-type aAlias = a.A - --// badPackage contains a duplicate definition of the 'a' const. --const badPackage = ` +-func (a.A).Hi() +-``` +- +-@loc(aAlias, "aAlias") +diff -urN a/gopls/internal/regtest/marker/testdata/definition/import.txt b/gopls/internal/regtest/marker/testdata/definition/import.txt +--- a/gopls/internal/regtest/marker/testdata/definition/import.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/definition/import.txt 1970-01-01 08:00:00 +@@ -1,52 +0,0 @@ +-This test checks definition and hover over imports. --- go.mod -- -module mod.com - --go 1.12 ---- a.go -- --package consts -- --const a = 1 ---- b.go -- --package consts -- --const a = 2 --` +-go 1.18 +--- foo/foo.go -- +-package foo - --func TestDiagnosticClearingOnEdit(t *testing.T) { -- Run(t, badPackage, func(t *testing.T, env *Env) { -- env.OpenFile("b.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("a.go", "a = 1")), -- Diagnostics(env.AtRegexp("b.go", "a = 2")), -- ) +-type Foo struct{} - -- // Fix the error by editing the const name in b.go to `b`. -- env.RegexpReplace("b.go", "(a) = 2", "b") -- env.AfterChange( -- NoDiagnostics(ForFile("a.go")), -- NoDiagnostics(ForFile("b.go")), -- ) -- }) --} +-// DoFoo does foo. +-func DoFoo() {} //@loc(DoFoo, "DoFoo") +--- bar/bar.go -- +-package bar - --func TestDiagnosticClearingOnDelete_Issue37049(t *testing.T) { -- Run(t, badPackage, func(t *testing.T, env *Env) { -- env.OpenFile("a.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("a.go", "a = 1")), -- Diagnostics(env.AtRegexp("b.go", "a = 2")), -- ) -- env.RemoveWorkspaceFile("b.go") +-import ( +- myFoo "mod.com/foo" //@loc(myFoo, "myFoo") +-) - -- env.AfterChange( -- NoDiagnostics(ForFile("a.go")), -- NoDiagnostics(ForFile("b.go")), -- ) -- }) --} +-var _ *myFoo.Foo //@def("myFoo", myFoo),hover("myFoo", "myFoo", myFoo) +--- bar/dotimport.go -- +-package bar - --func TestDiagnosticClearingOnClose(t *testing.T) { -- Run(t, badPackage, func(t *testing.T, env *Env) { -- env.CreateBuffer("c.go", `package consts +-import . "mod.com/foo" - --const a = 3`) -- env.AfterChange( -- Diagnostics(env.AtRegexp("a.go", "a = 1")), -- Diagnostics(env.AtRegexp("b.go", "a = 2")), -- Diagnostics(env.AtRegexp("c.go", "a = 3")), -- ) -- env.CloseBuffer("c.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("a.go", "a = 1")), -- Diagnostics(env.AtRegexp("b.go", "a = 2")), -- NoDiagnostics(ForFile("c.go")), -- ) -- }) +-func _() { +- // variable of type foo.Foo +- var _ Foo //@hover("_", "_", FooVar) +- +- DoFoo() //@hover("DoFoo", "DoFoo", DoFoo) -} +--- @DoFoo/hover.md -- +-```go +-func DoFoo() +-``` - --// 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", "") +-DoFoo does foo. - -- // Write the file contents with a missing import. -- env.EditBuffer("c/c.go", protocol.TextEdit{ -- NewText: `package c - --const a = http.MethodGet --`, -- }) -- env.AfterChange( -- Diagnostics(env.AtRegexp("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.AfterChange( -- NoDiagnostics(ForFile("c/c.go")), -- ) -- }) --} +-[`foo.DoFoo` on pkg.go.dev](https://pkg.go.dev/mod.com/foo#DoFoo) +--- @FooVar/hover.md -- +-```go +-var _ Foo +-``` - --// 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 = ` +-variable of type foo.Foo +--- @myFoo/hover.md -- +-```go +-package myFoo ("mod.com/foo") +-``` +- +-[`myFoo` on pkg.go.dev](https://pkg.go.dev/mod.com/foo) +diff -urN a/gopls/internal/regtest/marker/testdata/definition/misc.txt b/gopls/internal/regtest/marker/testdata/definition/misc.txt +--- a/gopls/internal/regtest/marker/testdata/definition/misc.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/definition/misc.txt 1970-01-01 08:00:00 +@@ -1,230 +0,0 @@ +-This test exercises miscellaneous definition and hover requests. --- go.mod -- --module foo +-module mod.com - --go 1.12 +-go 1.16 --- a.go -- --package x +-package a //@loc(aPackage, re"package (a)"),hover(aPackage, aPackage, aPackage) - --// import "fmt" +-var ( +- // x is a variable. +- x string //@loc(x, "x"),hover(x, x, hoverx) +-) - --func f() {} +-// Constant block. When I hover on h, I should see this comment. +-const ( +- // When I hover on g, I should see this comment. +- g = 1 //@hover("g", "g", hoverg) - ---- a_test.go -- --package x +- h = 2 //@hover("h", "h", hoverh) +-) - --import "testing" +-// z is a variable too. +-var z string //@loc(z, "z"),hover(z, z, hoverz) - --func TestA(t *testing.T) { -- f(3) +-func AStuff() { //@loc(AStuff, "AStuff") +- x := 5 +- Random2(x) //@def("dom2", Random2) +- Random() //@def("()", Random) -} --` -- --// 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.AfterChange(Diagnostics(env.AtRegexp("a_test.go", `f\((3)\)`))) -- env.RemoveWorkspaceFile("a_test.go") -- env.AfterChange(NoDiagnostics(ForFile("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.AfterChange(Diagnostics(env.AtRegexp("a.go", `"fmt"`))) -- }) +-type H interface { //@loc(H, "H") +- Goodbye() -} - --// 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.AfterChange(Diagnostics(AtPosition("a_test.go", 5, 3))) -- env.Sandbox.Workdir.RemoveFile(context.Background(), "a_test.go") -- env.AfterChange(Diagnostics(AtPosition("a_test.go", 5, 3))) -- }) +-type I interface { //@loc(I, "I") +- B() +- J -} - --// 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() +-type J interface { //@loc(J, "J") +- Hello() -} ---- bob/bob.go -- --package bob - --func Hello() { -- var x int --} --` +-func _() { +- // 1st type declaration block +- type ( +- a struct { //@hover("a", "a", hoverDeclBlocka) +- x string +- } +- ) - -- t.Run("manual", func(t *testing.T) { -- Run(t, noMod, func(t *testing.T, env *Env) { -- env.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics(env.AtRegexp("main.go", `"mod.com/bob"`)), -- ) -- env.CreateBuffer("go.mod", `module mod.com +- // 2nd type declaration block +- type ( +- // b has a comment +- b struct{} //@hover("b", "b", hoverDeclBlockb) +- ) - -- go 1.12 --`) -- env.SaveBuffer("go.mod") -- var d protocol.PublishDiagnosticsParams -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- Diagnostics(env.AtRegexp("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.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics(env.AtRegexp("main.go", `"mod.com/bob"`)), -- ) -- env.RunGoCommand("mod", "init", "mod.com") -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- Diagnostics(env.AtRegexp("bob/bob.go", "x")), -- ) -- }) -- }) +- // 3rd type declaration block +- type ( +- // c is a struct +- c struct { //@hover("c", "c", hoverDeclBlockc) +- f string +- } - -- t.Run("without workspace module", func(t *testing.T) { -- WithOptions( -- Modes(Default), -- ).Run(t, noMod, func(t *testing.T, env *Env) { -- env.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics(env.AtRegexp("main.go", `"mod.com/bob"`)), -- ) -- if err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}, true); err != nil { -- t.Fatal(err) -- } -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- Diagnostics(env.AtRegexp("bob/bob.go", "x")), -- ) -- }) -- }) +- d string //@hover("d", "d", hoverDeclBlockd) +- ) +- +- type ( +- e struct { //@hover("e", "e", hoverDeclBlocke) +- f float64 +- } // e has a comment +- ) -} - --// Tests golang/go#38267. --func TestIssue38267(t *testing.T) { -- const testPackage = ` ---- go.mod -- --module mod.com +-var ( +- hh H //@hover("H", "H", hoverH) +- ii I //@hover("I", "I", hoverI) +- jj J //@hover("J", "J", hoverJ) +-) +--- a_test.go -- +-package a - --go 1.12 ---- lib.go -- --package lib +-import ( +- "testing" +-) - --func Hello(x string) { -- _ = x +-func TestA(t *testing.T) { //@hover("TestA", "TestA", hoverTestA) -} ---- lib_test.go -- --package lib -- --import "testing" +--- random.go -- +-package a - --type testStruct struct{ -- name string +-func Random() int { //@loc(Random, "Random") +- y := 6 + 7 +- return y -} - --func TestHello(t *testing.T) { -- testStructs := []*testStruct{ -- &testStruct{"hello"}, -- &testStruct{"goodbye"}, -- } -- for y := range testStructs { -- _ = y -- } +-func Random2(y int) int { //@loc(Random2, "Random2"),loc(RandomParamY, "y") +- return y //@def("y", RandomParamY),hover("y", "y", hovery) -} --` - -- Run(t, testPackage, func(t *testing.T, env *Env) { -- env.OpenFile("lib_test.go") -- env.AfterChange( -- Diagnostics(AtPosition("lib_test.go", 10, 2)), -- Diagnostics(AtPosition("lib_test.go", 11, 2)), -- ) -- env.OpenFile("lib.go") -- env.RegexpReplace("lib.go", "_ = x", "var y int") -- env.AfterChange( -- Diagnostics(env.AtRegexp("lib.go", "y int")), -- NoDiagnostics(ForFile("lib_test.go")), -- ) -- }) +-type Pos struct { +- x, y int //@loc(PosX, "x"),loc(PosY, "y") -} - --// Tests golang/go#38328. --func TestPackageChange_Issue38328(t *testing.T) { -- const packageChange = ` ---- go.mod -- --module fake +-// Typ has a comment. Its fields do not. +-type Typ struct{ field string } //@loc(TypField, "field") - --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.AfterChange( -- NoDiagnostics(ForFile("a.go")), -- ) -- }) +-func _() { +- x := &Typ{} +- _ = x.field //@def("field", TypField),hover("field", "field", hoverfield) -} - --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 +-func (p *Pos) Sum() int { //@loc(PosSum, "Sum") +- return p.x + p.y //@hover("x", "x", hoverpx) +-} - --import ( -- "fmt" +-func _() { +- var p Pos +- _ = p.Sum() //@def("()", PosSum),hover("()", `Sum`, hoverSum) +-} +--- @aPackage/hover.md -- +--- @hoverDeclBlocka/hover.md -- +-```go +-type a struct { +- x string +-} +-``` - -- "foo.test/bar" --) +-1st type declaration block +--- @hoverDeclBlockb/hover.md -- +-```go +-type b struct{} +-``` - --func PrintAnswer() { -- fmt.Printf("answer: %s", bar.Answer) +-b has a comment +--- @hoverDeclBlockc/hover.md -- +-```go +-type c struct { +- f string -} --` +-``` - --const testPackageWithRequireProxy = ` ---- foo.test@v1.2.3/go.mod -- --module foo.test +-c is a struct +--- @hoverDeclBlockd/hover.md -- +-```go +-type d string +-``` - --go 1.12 ---- foo.test@v1.2.3/bar/const.go -- --package bar +-3rd type declaration block +--- @hoverDeclBlocke/hover.md -- +-```go +-type e struct { +- f float64 +-} +-``` - --const Answer = 42 --` +-e has a comment +--- @hoverH/hover.md -- +-```go +-type H interface { +- Goodbye() +-} +-``` - --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. -- env.AfterChange( -- Diagnostics( -- env.AtRegexp("print.go", "fmt.Printf"), -- WithMessage("wrong type int"), -- ), -- ) -- }) +-[`a.H` on pkg.go.dev](https://pkg.go.dev/mod.com#H) +--- @hoverI/hover.md -- +-```go +-type I interface { +- B() +- J -} +-``` - --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)) -- }) +-[`a.I` on pkg.go.dev](https://pkg.go.dev/mod.com#I) +--- @hoverJ/hover.md -- +-```go +-type J interface { +- Hello() -} +-``` - --// Tests golang/go#36951. --func TestAdHocPackages_Issue36951(t *testing.T) { -- const adHoc = ` ---- b/b.go -- --package b +-[`a.J` on pkg.go.dev](https://pkg.go.dev/mod.com#J) +--- @hoverSum/hover.md -- +-```go +-func (*Pos).Sum() int +-``` - --func Hello() { -- var x int --} --` -- Run(t, adHoc, func(t *testing.T, env *Env) { -- env.OpenFile("b/b.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("b/b.go", "x")), -- ) -- }) --} +-[`(a.Pos).Sum` on pkg.go.dev](https://pkg.go.dev/mod.com#Pos.Sum) +--- @hoverTestA/hover.md -- +-```go +-func TestA(t *testing.T) +-``` +--- @hoverfield/hover.md -- +-```go +-field field string +-``` +--- @hoverg/hover.md -- +-```go +-const g untyped int = 1 +-``` - --// Tests golang/go#37984: GOPATH should be read from the go command. --func TestNoGOPATH_Issue37984(t *testing.T) { -- const files = ` ---- main.go -- --package main +-When I hover on g, I should see this comment. +--- @hoverh/hover.md -- +-```go +-const h untyped int = 2 +-``` - --func _() { -- fmt.Println("Hello World") --} --` -- WithOptions( -- EnvVars{ -- "GOPATH": "", -- "GO111MODULE": "off", -- }, -- ).Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("main.go") -- env.AfterChange(Diagnostics(env.AtRegexp("main.go", "fmt"))) -- env.SaveBuffer("main.go") -- env.AfterChange(NoDiagnostics(ForFile("main.go"))) -- }) --} +-Constant block. When I hover on h, I should see this comment. +--- @hoverpx/hover.md -- +-```go +-field x int +-``` - --// Tests golang/go#38669. --func TestEqualInEnv_Issue38669(t *testing.T) { -- const files = ` ---- go.mod -- --module mod.com +-@loc(PosX, "x"),loc(PosY, "y") +--- @hoverx/hover.md -- +-```go +-var x string +-``` +- +-x is a variable. +--- @hovery/hover.md -- +-```go +-var y int +-``` +--- @hoverz/hover.md -- +-```go +-var z string +-``` - --go 1.12 ---- main.go -- --package main +-z is a variable too. +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/addgowork.txt b/gopls/internal/regtest/marker/testdata/diagnostics/addgowork.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/addgowork.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/addgowork.txt 1970-01-01 08:00:00 +@@ -1,50 +0,0 @@ +-This test demonstrates diagnostics for adding a go.work file. - --var _ = x.X ---- x/x.go -- --package x +-Quick-fixes change files on disk, so are tested by regtests. - --var X = 0 --` -- WithOptions( -- EnvVars{"GOFLAGS": "-tags=foo"}, -- ).Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("main.go") -- env.OrganizeImports("main.go") -- env.AfterChange(NoDiagnostics(ForFile("main.go"))) -- }) --} +-TODO(rfindley): improve the "cannot find package" import errors. - --// Tests golang/go#38467. --func TestNoSuggestedFixesForGeneratedFiles_Issue38467(t *testing.T) { -- const generated = ` ---- go.mod -- --module mod.com +--- skip -- +-Skipping due to go.dev/issue/60584#issuecomment-1622238115. +-There appears to be a real race in the critical error logic causing this test +-to flake with high frequency. - --go 1.12 ---- main.go -- --package main +--- flags -- +--min_go=go1.18 - --// Code generated by generator.go. DO NOT EDIT. +--- a/go.mod -- +-module mod.com/a - --func _() { -- for i, _ := range []string{} { -- _ = i -- } --} --` -- Run(t, generated, func(t *testing.T, env *Env) { -- env.OpenFile("main.go") -- var d protocol.PublishDiagnosticsParams -- env.AfterChange( -- Diagnostics(AtPosition("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) -- } -- }) --} +-go 1.18 - --// Expect a module/GOPATH error if there is an error in the file at startup. --// Tests golang/go#37279. --func TestBrokenWorkspace_OutsideModule(t *testing.T) { -- const noModule = ` ---- a.go -- --package foo +--- a/main.go -- +-package main //@diag("main", re"add a go.work file") - --import "mod.com/hello" +-import "mod.com/a/lib" //@diag("\"mod.com", re"cannot find package") - --func f() { -- hello.Goodbye() --} --` -- Run(t, noModule, func(t *testing.T, env *Env) { -- env.OpenFile("a.go") -- env.AfterChange( -- // Expect the adHocPackagesWarning. -- OutstandingWork(lsp.WorkspaceLoadFailure, "outside of a module"), -- ) -- // Deleting the import dismisses the warning. -- env.RegexpReplace("a.go", `import "mod.com/hello"`, "") -- env.AfterChange( -- NoOutstandingWork(), -- ) -- }) +-func main() { +- _ = lib.C -} - --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( -- EnvVars{"GO111MODULE": go111module}, -- ).Run(t, files, func(t *testing.T, env *Env) { -- env.OnceMet( -- InitialWorkspaceLoad, -- NoOutstandingWork(), -- ) -- }) -- }) -- } --} +--- a/lib/lib.go -- +-package lib //@diag("lib", re"add a go.work file") - --// 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 +-const C = "b" +--- b/go.mod -- +-module mod.com/b - --import "x/hello" +-go 1.18 - --func Hello() { -- hello.HiThere() --} ---- x/main.go -- --package main +--- b/main.go -- +-package main //@diag("main", re"add a go.work file") +- +-import "mod.com/b/lib" //@diag("\"mod.com", re"cannot find package") - -func main() { -- fmt.Println("") +- _ = lib.C -} --` -- WithOptions( -- InGOPATH(), -- EnvVars{"GO111MODULE": "off"}, -- ).Run(t, collision, func(t *testing.T, env *Env) { -- env.OpenFile("x/x.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("x/x.go", `^`), WithMessage("found packages main (main.go) and x (x.go)")), -- Diagnostics(env.AtRegexp("x/main.go", `^`), WithMessage("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.AfterChange( -- Diagnostics(env.AtRegexp("x/main.go", `fmt`)), -- ) -- } -- }) --} +--- b/lib/lib.go -- +-package lib //@diag("lib", re"add a go.work file") - --const ardanLabsProxy = ` ---- github.com/ardanlabs/conf@v1.2.3/go.mod -- --module github.com/ardanlabs/conf +-const C = "b" +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/analyzers.txt b/gopls/internal/regtest/marker/testdata/diagnostics/analyzers.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/analyzers.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/analyzers.txt 1970-01-01 08:00:00 +@@ -1,51 +0,0 @@ +-Test of warning diagnostics from various analyzers: +-copylocks, printf, slog, tests, and timeformat. - +--- go.mod -- +-module example.com -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) { -- const ardanLabs = ` ---- go.mod -- --module mod.com +--- flags -- +--min_go=go1.21 - --go 1.14 ---- main.go -- --package main +--- bad_test.go -- +-package analyzer - --import "github.com/ardanlabs/conf" +-import ( +- "fmt" +- "log/slog" +- "sync" +- "testing" +- "time" +-) - --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.AfterChange( -- Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`)), -- ReadDiagnostics("main.go", &d), -- ) -- env.ApplyQuickFixes("main.go", d.Diagnostics) -- env.SaveBuffer("go.mod") -- env.AfterChange( -- NoDiagnostics(ForFile("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.AfterChange( -- Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`)), -- ) -- env.SaveBuffer("main.go") -- // Expect a diagnostic and fix to remove the dependency in the go.mod. -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- Diagnostics(env.AtRegexp("go.mod", "require github.com/ardanlabs/conf"), WithMessage("not used in this module")), -- ReadDiagnostics("go.mod", &d), -- ) -- env.ApplyQuickFixes("go.mod", d.Diagnostics) -- env.SaveBuffer("go.mod") -- env.AfterChange( -- NoDiagnostics(ForFile("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.AfterChange( -- Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`)), -- ) -- }) +-// copylocks +-func _() { +- var x sync.Mutex +- _ = x //@diag("x", re"assignment copies lock value to _: sync.Mutex") -} - --// Test for golang/go#38207. --func TestNewModule_Issue38207(t *testing.T) { -- 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 +-// printf +-func _() { +- printfWrapper("%s") //@diag(re`printfWrapper\(.*\)`, re"example.com.printfWrapper format %s reads arg #1, but call has 0 args") +-} - --import "github.com/ardanlabs/conf" +-func printfWrapper(format string, args ...interface{}) { +- fmt.Printf(format, args...) +-} - --func main() { -- _ = conf.ErrHelpWanted +-// slog +-func _() { +- slog.Info("msg", 1) //@diag("1", re`slog.Info arg "1" should be a string or a slog.Attr`) -} --`) -- env.SaveBuffer("main.go") -- var d protocol.PublishDiagnosticsParams -- env.AfterChange( -- Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`), WithMessage("no required module")), -- ReadDiagnostics("main.go", &d), -- ) -- env.ApplyQuickFixes("main.go", d.Diagnostics) -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- ) -- }) +- +-// tests +-func Testbad(t *testing.T) { //@diag("", re"Testbad has malformed name: first letter after 'Test' must not be lowercase") -} - --// Test for golang/go#36960. --func TestNewFileBadImports_Issue36960(t *testing.T) { -- const simplePackage = ` ---- go.mod -- --module mod.com +-// timeformat +-func _() { +- now := time.Now() +- fmt.Println(now.Format("2006-02-01")) //@diag("2006-02-01", re"2006-02-01 should be 2006-01-02") +-} - --go 1.14 ---- a/a1.go -- --package a +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/excludedfile.txt b/gopls/internal/regtest/marker/testdata/diagnostics/excludedfile.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/excludedfile.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/excludedfile.txt 1970-01-01 08:00:00 +@@ -1,38 +0,0 @@ +-This test demonstrates diagnostics for various forms of file exclusion. - --import "fmt" +-Skip on plan9, an arbitrary GOOS, so that we can exercise GOOS exclusions +-resulting from file suffixes. - --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.AfterChange( -- NoDiagnostics(ForFile("a/a1.go")), -- ) -- env.EditBuffer("a/a2.go", fake.NewEdit(0, 0, 0, 0, `package a`)) -- env.AfterChange( -- NoDiagnostics(ForFile("a/a1.go")), -- ) -- }) --} +--- flags -- +--min_go=go1.18 +--skip_goos=plan9 - --// 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) { -- // Create a package that already has a test variant (in-package test). -- const testVariant = ` ---- go.mod -- --module mod.com +--- go.work -- +-go 1.21 - --go 1.15 ---- hello/hello.go -- --package hello +-use ( +- ./a +-) +--- a/go.mod -- +-module mod.com/a - --func Hello() { -- var x int --} ---- hello/hello_test.go -- --package hello +-go 1.18 - --import "testing" +--- a/a.go -- +-package a - --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.AfterChange( -- Diagnostics(env.AtRegexp("hello/hello.go", "x")), -- Diagnostics(env.AtRegexp("hello/hello_test.go", "x")), -- ) +--- a/a_plan9.go -- +-package a //@diag(re"package (a)", re"excluded due to its GOOS/GOARCH") - -- // 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", ``) +--- a/a_ignored.go -- +-//go:build skip +-package a //@diag(re"package (a)", re"excluded due to its build tags") - -- // Save the empty file (no actions since formatting will fail). -- env.SaveBufferWithoutActions("hello/hello_x_test.go") +--- b/go.mod -- +-module mod.com/b - -- // 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 +-go 1.18 - --import ( -- "testing" --) +--- b/b.go -- +-package b //@diag(re"package (b)", re"add this module to your go.work") - --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.AfterChange( -- Diagnostics(env.AtRegexp("hello/hello_x_test.go", "hello.Hello")), -- ) -- env.SaveBuffer("hello/hello_x_test.go") -- env.AfterChange( -- NoDiagnostics(ForFile("hello/hello_x_test.go")), -- ) -- }) --} +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/generated.txt b/gopls/internal/regtest/marker/testdata/diagnostics/generated.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/generated.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/generated.txt 1970-01-01 08:00:00 +@@ -1,21 +0,0 @@ +-Test of "undeclared" diagnostic in generated code. - --// Reproduce golang/go#40690. --func TestCreateOnlyXTest(t *testing.T) { -- const mod = ` --- go.mod -- --module mod.com -- +-module example.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" +--- generated.go -- +-package generated - --func TestX(t *testing.T) { -- var x int --} --`) -- env.AfterChange( -- Diagnostics(env.AtRegexp("foo/bar_test.go", "x")), -- ) -- }) +-// Code generated by generator.go. DO NOT EDIT. +- +-func _() { +- var y int //@diag("y", re"y declared (and|but) not used") -} - --func TestChangePackageName(t *testing.T) { -- const mod = ` ---- go.mod -- --module mod.com +--- generator.go -- +-package generated - --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.AfterChange() -- env.RegexpReplace("foo/bar_test.go", "package foo_", "package foo_test") -- env.AfterChange( -- NoDiagnostics(ForFile("foo/bar_test.go")), -- NoDiagnostics(ForFile("foo/foo.go")), -- ) -- }) +-func _() { +- var x int //@diag("x", re"x declared (and|but) not used") -} +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/issue56943.txt b/gopls/internal/regtest/marker/testdata/diagnostics/issue56943.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/issue56943.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/issue56943.txt 1970-01-01 08:00:00 +@@ -1,22 +0,0 @@ +-This test verifies that we produce diagnostics related to mismatching +-unexported interface methods in non-workspace packages. - --func TestIgnoredFiles(t *testing.T) { -- const ws = ` ---- go.mod -- --module mod.com +-Previously, we would fail to produce a diagnostic because we trimmed the AST. +-See golang/go#56943. +--- main.go -- +-package main - --go 1.12 ---- _foo/x.go -- --package x +-import ( +- "go/ast" +- "go/token" +-) - --var _ = foo.Bar --` -- Run(t, ws, func(t *testing.T, env *Env) { -- env.OpenFile("_foo/x.go") -- env.AfterChange( -- NoDiagnostics(ForFile("_foo/x.go")), -- ) -- }) +-func main() { +- var a int //@diag(re"(a) int", re"a declared.*not used") +- var _ ast.Expr = node{} //@diag("node{}", re"missing.*exprNode") -} - --// 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 +-type node struct{} - --const A = 1 +-func (node) Pos() token.Pos { return 0 } +-func (node) End() token.Pos { return 0 } +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/issue59005.txt b/gopls/internal/regtest/marker/testdata/diagnostics/issue59005.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/issue59005.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/issue59005.txt 1970-01-01 08:00:00 +@@ -1,20 +0,0 @@ +-This test verifies that we don't drop type checking errors on the floor when we +-fail to compute positions for their related errors. - ---- b/b.go -- --package b +--- go.mod -- +-module play.ground - --import "mod.com/a" +--- p.go -- +-package p - --const B = a.A +-import ( +- . "play.ground/foo" +-) - ---- c/c.go -- --package c +-const C = 1 //@diag("C", re"C already declared through dot-import") +-var _ = C - --import "mod.com/a" +--- foo/foo.go -- +-package foo - --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") +-const C = 2 +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/issue60544.txt b/gopls/internal/regtest/marker/testdata/diagnostics/issue60544.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/issue60544.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/issue60544.txt 1970-01-01 08:00:00 +@@ -1,13 +0,0 @@ +-This test exercises a crash due to treatment of "comparable" in methodset +-calculation (golang/go#60544). - -- // We should still get diagnostics for files that exist. -- env.RegexpReplace("b/b.go", `a.A`, "a.Nonexistant") -- env.AfterChange( -- Diagnostics(env.AtRegexp("b/b.go", `Nonexistant`)), -- ) -- }) --} +--min_go is 1.19 as the error message changed at this Go version. +--- flags -- +--min_go=go1.19 - --// This is a copy of the scenario_default/quickfix_empty_files.txt test from --// govim. Reproduces golang/go#39646. --func TestQuickFixEmptyFiles(t *testing.T) { -- const mod = ` ---- go.mod -- --module mod.com +--- main.go -- +-package main - --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()) +-type X struct{} - -- env.CreateBuffer(name, "\n") -- env.Await(env.DoneWithOpen()) +-func (X) test(x comparable) {} //@diag("comparable", re"outside a type constraint") +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/issue60605.txt b/gopls/internal/regtest/marker/testdata/diagnostics/issue60605.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/issue60605.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/issue60605.txt 1970-01-01 08:00:00 +@@ -1,12 +0,0 @@ +-This test verifies that we can export constants with unknown kind. +-Previously, the exporter would panic while attempting to convert such constants +-to their target type (float64, in this case). - -- env.EditBuffer(name, fake.NewEdit(1, 0, 1, 0, content)) -- env.Await(env.DoneWithChange()) +--- go.mod -- +-module mod.txt/p - -- env.EditBuffer(name, fake.NewEdit(0, 0, 1, 0, "")) -- env.Await(env.DoneWithChange()) -- } +-go 1.20 +--- p.go -- +-package p - -- const p = `package p; func DoIt(s string) {};` -- const main = `package main +-const EPSILON float64 = 1e- //@diag(re"1e-()", re"exponent has no digits") +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/parseerr.txt b/gopls/internal/regtest/marker/testdata/diagnostics/parseerr.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/parseerr.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/parseerr.txt 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ - --import "mod.com/p" +-This test exercises diagnostics produced for syntax errors. - --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.AfterChange( -- Diagnostics(env.AtRegexp("main.go", "5")), -- ) -- }) -- }) +-Because parser error recovery can be quite lossy, diagnostics +-for type errors are suppressed in files with syntax errors; +-see issue #59888. But diagnostics are reported for type errors +-in well-formed files of the same package. - -- // 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 +--- go.mod -- +-module example.com +-go 1.12 - --import "testing" +--- bad.go -- +-package p - --func TestDoIt(t *testing.T) { -- DoIt(5) +-func f() { +- append("") // no diagnostic for type error in file containing syntax error -} --`) -- writeGoVim(env, "p/x_test.go", `package p_test - --import ( -- "testing" +-func .() {} //@diag(re"func ().", re"expected 'IDENT', found '.'") - -- "mod.com/p" --) +--- good.go -- +-package p - --func TestDoIt(t *testing.T) { -- p.DoIt(5) --} --`) -- env.AfterChange( -- Diagnostics(env.AtRegexp("main.go", "5")), -- Diagnostics(env.AtRegexp("p/p_test.go", "5")), -- Diagnostics(env.AtRegexp("p/x_test.go", "5")), -- ) -- env.RegexpReplace("p/p.go", "s string", "i int") -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- NoDiagnostics(ForFile("p/p_test.go")), -- NoDiagnostics(ForFile("p/x_test.go")), -- ) -- }) -- }) +-func g() { +- append("") //@diag(re`""`, re"a slice") -} +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/rundespiteerrors.txt b/gopls/internal/regtest/marker/testdata/diagnostics/rundespiteerrors.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/rundespiteerrors.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/rundespiteerrors.txt 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ +-This test verifies that analyzers without RunDespiteErrors are not +-executed on a package containing type errors (see issue #54762). +- +-We require go1.18 because the range of the `1 + ""` go/types error +-changed then, and the new @diag marker is quite particular. - --func TestSingleFile(t *testing.T) { -- const mod = ` --- go.mod -- --module mod.com +-module example.com +-go 1.12 - --go 1.13 ---- a/a.go -- +--- flags -- +--min_go=go1.18 +- +--- 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.AfterChange( -- Diagnostics(env.AtRegexp("a/a.go", "x")), -- ) -- }) +- // A type error. +- _ = 1 + "" //@diag(`1 + ""`, re"mismatched types|cannot convert") +- +- // A violation of an analyzer for which RunDespiteErrors=false: +- // no (simplifyrange, warning) diagnostic is produced; the diag +- // comment is merely illustrative. +- for _ = range "" { //diag("for _", "simplify range expression", ) +- +- } -} +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/typeerr.txt b/gopls/internal/regtest/marker/testdata/diagnostics/typeerr.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/typeerr.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/typeerr.txt 1970-01-01 08:00:00 +@@ -1,33 +0,0 @@ +- +-This test exercises diagnostics produced for type errors +-in the absence of syntax errors. +- +-The type error was chosen to exercise the 'nonewvars' type-error analyzer. +-(The 'undeclaredname' analyzer depends on the text of the go/types +-"undeclared name" error, which changed in go1.20.) +- +-The append() type error was also carefully chosen to have text and +-position that are invariant across all versions of Go run by the builders. - --// 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 +-module example.com +-go 1.12 - --go 1.15 ---- foo/foo.go -- --package foo +--- typeerr.go -- +-package a - --import "fmt" +-func f(x int) { +- append("") //@diag(re`""`, re"a slice") - --func Foo() { -- fmt.Println("") +- x := 123 //@diag(re"x := 123", re"no new variables"), suggestedfix(re"():", re"no new variables", "quickfix", fix) -} --` -- Run(t, basic, func(t *testing.T, env *Env) { -- env.WriteWorkspaceFile("foo/foo_test.go", `package main - --func main() { +--- @fix/typeerr.go -- +-package a - --}`) -- env.OpenFile("foo/foo_test.go") -- env.RegexpReplace("foo/foo_test.go", `package main`, `package foo`) -- env.AfterChange(NoDiagnostics(ForFile("foo/foo.go"))) -- }) +-func f(x int) { +- append("") //@diag(re`""`, re"a slice") +- +- x = 123 //@diag(re"x := 123", re"no new variables"), suggestedfix(re"():", re"no new variables", "quickfix", fix) -} - --func TestClosingBuffer(t *testing.T) { -- const basic = ` ---- go.mod -- --module mod.com +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/useinternal.txt b/gopls/internal/regtest/marker/testdata/diagnostics/useinternal.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/useinternal.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/useinternal.txt 1970-01-01 08:00:00 +@@ -1,21 +0,0 @@ +-This test checks a diagnostic for invalid use of internal packages. - --go 1.14 ---- main.go -- --package main +-This list error changed in Go 1.21. - --func main() {} --` -- Run(t, basic, func(t *testing.T, env *Env) { -- env.Editor.CreateBuffer(env.Ctx, "foo.go", `package main`) -- env.AfterChange() -- env.CloseBuffer("foo.go") -- env.AfterChange(NoLogMatching(protocol.Info, "packages=0")) -- }) --} +--- flags -- +--min_go=go1.21 - --// Reproduces golang/go#38424. --func TestCutAndPaste(t *testing.T) { -- const basic = ` --- go.mod -- --module mod.com +-module bad.test - --go 1.14 ---- main2.go -- --package main --` -- Run(t, basic, func(t *testing.T, env *Env) { -- env.CreateBuffer("main.go", "") -- env.Await(env.DoneWithOpen()) +-go 1.18 - -- env.SaveBufferWithoutActions("main.go") -- env.Await(env.DoneWithSave(), env.DoneWithChangeWatchedFiles()) +--- assign/internal/secret/secret.go -- +-package secret - -- env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main +-func Hello() {} - --func main() { --} --`)) -- env.Await(env.DoneWithChange()) +--- bad/bad.go -- +-package bad - -- env.SaveBuffer("main.go") -- env.Await(env.DoneWithSave(), env.DoneWithChangeWatchedFiles()) +-import _ "bad.test/assign/internal/secret" //@diag("\"bad.test/assign/internal/secret\"", re"could not import bad.test/assign/internal/secret \\(invalid use of internal package \"bad.test/assign/internal/secret\"\\)"),diag("_", re"use of internal package bad.test/assign/internal/secret not allowed") +diff -urN a/gopls/internal/regtest/marker/testdata/diagnostics/usemodule.txt b/gopls/internal/regtest/marker/testdata/diagnostics/usemodule.txt +--- a/gopls/internal/regtest/marker/testdata/diagnostics/usemodule.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/diagnostics/usemodule.txt 1970-01-01 08:00:00 +@@ -1,51 +0,0 @@ +-This test demonstrates diagnostics for a module that is missing from the +-go.work file. - -- env.EditBuffer("main.go", fake.NewEdit(0, 0, 4, 0, "")) -- env.Await(env.DoneWithChange()) +-Quick-fixes change files on disk, so are tested by regtests. - -- env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main +--- flags -- +--min_go=go1.18 +- +--- go.work -- +-go 1.21 +- +-use ( +- ./a +-) +- +--- a/go.mod -- +-module mod.com/a +- +-go 1.18 +- +--- a/main.go -- +-package main +- +-import "mod.com/a/lib" - -func main() { -- var x int --} --`)) -- env.AfterChange( -- Diagnostics(env.AtRegexp("main.go", "x")), -- ) -- }) +- _ = lib.C -} - --// Reproduces golang/go#39763. --func TestInvalidPackageName(t *testing.T) { -- const pkgDefault = ` ---- go.mod -- --module mod.com +--- a/lib/lib.go -- +-package lib - --go 1.12 ---- main.go -- --package default +-const C = "b" +--- b/go.mod -- +-module mod.com/b - --func main() {} --` -- Run(t, pkgDefault, func(t *testing.T, env *Env) { -- env.OpenFile("main.go") -- env.AfterChange( -- Diagnostics( -- env.AtRegexp("main.go", "default"), -- WithMessage("expected 'IDENT'"), -- ), -- ) -- }) +-go 1.18 +- +--- b/main.go -- +-package main //@diag("main", re"add this module to your go.work") +- +-import "mod.com/b/lib" //@diag("\"mod.com", re"not included in a workspace module") +- +-func main() { +- _ = lib.C -} - --// This tests the functionality of the "limitWorkspaceScope" --func TestLimitWorkspaceScope(t *testing.T) { -- const mod = ` ---- go.mod -- --module mod.com +--- b/lib/lib.go -- +-package lib //@diag("lib", re"add this module to your go.work") +- +-const C = "b" +diff -urN a/gopls/internal/regtest/marker/testdata/fixedbugs/issue59318.txt b/gopls/internal/regtest/marker/testdata/fixedbugs/issue59318.txt +--- a/gopls/internal/regtest/marker/testdata/fixedbugs/issue59318.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/fixedbugs/issue59318.txt 1970-01-01 08:00:00 +@@ -1,22 +0,0 @@ +-This test verifies that we can load multiple orphaned files as +-command-line-arguments packages. +- +-Previously, we would load only one because go/packages returns at most one +-command-line-arguments package per query. - --go 1.12 --- a/main.go -- -package main - --func main() {} ---- main.go -- +-func main() { +- var a int //@diag(re"var (a)", re"not used") +-} +--- b/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.AfterChange( -- Diagnostics(env.AtRegexp("main.go", "x")), -- ) -- }) -- WithOptions( -- WorkspaceFolders("a"), -- Settings{"expandWorkspaceToModule": false}, -- ).Run(t, mod, func(t *testing.T, env *Env) { -- env.OpenFile("a/main.go") -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- ) -- }) +- var b int //@diag(re"var (b)", re"not used") -} +--- c/go.mod -- +-module c.com // The existence of this module avoids a workspace error. +- +-go 1.18 +diff -urN a/gopls/internal/regtest/marker/testdata/fixedbugs/issue59944.txt b/gopls/internal/regtest/marker/testdata/fixedbugs/issue59944.txt +--- a/gopls/internal/regtest/marker/testdata/fixedbugs/issue59944.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/fixedbugs/issue59944.txt 1970-01-01 08:00:00 +@@ -1,33 +0,0 @@ +-This test verifies that gopls does not panic when encountering the go/types +-bug described in golang/go#59944: the Bindingf function is not included in +-the methodset of its receiver type. +- +-Adapted from the code in question from the issue. +- +--- flags -- +--cgo - --func TestSimplifyCompositeLitDiagnostic(t *testing.T) { -- const files = ` --- go.mod -- --module mod.com +-module example.com - -go 1.12 ---- main.go -- --package main +- +--- cgo.go -- +-package x - -import "fmt" - --type t struct { -- msg string --} +-/* +-struct layout { +- int field; +-}; +-*/ +-import "C" - --func main() { -- x := []t{t{"msg"}} -- fmt.Println(x) +-type Layout = C.struct_layout +- +-// Bindingf is a printf wrapper. This was necessary to trigger the panic in +-// objectpath while encoding facts. +-func (l *Layout) Bindingf(format string, args ...interface{}) { +- fmt.Printf(format, args...) -} --` +diff -urN a/gopls/internal/regtest/marker/testdata/foldingrange/a.txt b/gopls/internal/regtest/marker/testdata/foldingrange/a.txt +--- a/gopls/internal/regtest/marker/testdata/foldingrange/a.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/foldingrange/a.txt 1970-01-01 08:00:00 +@@ -1,154 +0,0 @@ +-This test checks basic behavior of textDocument/foldingRange. - -- WithOptions( -- Settings{"staticcheck": true}, -- ).Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("main.go") -- var d protocol.PublishDiagnosticsParams -- env.AfterChange( -- Diagnostics(env.AtRegexp("main.go", `t{"msg"}`), WithMessage("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) +--- a.go -- +-package folding //@foldingrange(raw) +- +-import ( +- "fmt" +- _ "log" +-) +- +-import _ "os" +- +-// bar is a function. +-// With a multiline doc comment. +-func bar() string { +- /* This is a single line comment */ +- switch { +- case true: +- if true { +- fmt.Println("true") +- } else { +- fmt.Println("false") - } -- env.ApplyQuickFixes("main.go", d.Diagnostics) -- env.AfterChange(NoDiagnostics(ForFile("main.go"))) -- }) +- case false: +- fmt.Println("false") +- default: +- fmt.Println("default") +- } +- /* This is a multiline +- block +- comment */ +- +- /* This is a multiline +- block +- comment */ +- // Followed by another comment. +- _ = []int{ +- 1, +- 2, +- 3, +- } +- _ = [2]string{"d", +- "e", +- } +- _ = map[string]int{ +- "a": 1, +- "b": 2, +- "c": 3, +- } +- type T struct { +- f string +- g int +- h string +- } +- _ = T{ +- f: "j", +- g: 4, +- h: "i", +- } +- x, y := make(chan bool), make(chan bool) +- select { +- case val := <-x: +- if val { +- fmt.Println("true from x") +- } else { +- fmt.Println("false from x") +- } +- case <-y: +- fmt.Println("y") +- default: +- fmt.Println("default") +- } +- // This is a multiline comment +- // that is not a doc comment. +- return ` +-this string +-is not indented` -} +--- @raw -- +-package folding //@foldingrange(raw) - --// Test some secondary diagnostics --func TestSecondaryDiagnostics(t *testing.T) { -- const dir = ` ---- go.mod -- --module mod.com +-import (<0 kind="imports"> +- "fmt" +- _ "log" +-</0>) - --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") -- var mainDiags, otherDiags protocol.PublishDiagnosticsParams -- env.AfterChange( -- ReadDiagnostics("main.go", &mainDiags), -- ReadDiagnostics("other.go", &otherDiags), -- ) -- if len(mainDiags.Diagnostics) != 1 { -- t.Fatalf("main.go, got %d diagnostics, expected 1", len(mainDiags.Diagnostics)) -- } -- keep := mainDiags.Diagnostics[0] -- if len(otherDiags.Diagnostics) != 1 { -- t.Fatalf("other.go: got %d diagnostics, expected 1", len(otherDiags.Diagnostics)) +-import _ "os" +- +-// bar is a function.<1 kind="comment"> +-// With a multiline doc comment.</1> +-func bar(<2 kind=""></2>) string {<3 kind=""> +- /* This is a single line comment */ +- switch {<4 kind=""> +- case true:<5 kind=""> +- if true {<6 kind=""> +- fmt.Println(<7 kind="">"true"</7>) +- </6>} else {<8 kind=""> +- fmt.Println(<9 kind="">"false"</9>) +- </8>}</5> +- case false:<10 kind=""> +- fmt.Println(<11 kind="">"false"</11>)</10> +- default:<12 kind=""> +- fmt.Println(<13 kind="">"default"</13>)</12> +- </4>} +- /* This is a multiline<14 kind="comment"> +- block +- comment */</14> +- +- /* This is a multiline<15 kind="comment"> +- block +- comment */ +- // Followed by another comment.</15> +- _ = []int{<16 kind=""> +- 1, +- 2, +- 3, +- </16>} +- _ = [2]string{<17 kind="">"d", +- "e", +- </17>} +- _ = map[string]int{<18 kind=""> +- "a": 1, +- "b": 2, +- "c": 3, +- </18>} +- type T struct {<19 kind=""> +- f string +- g int +- h string +- </19>} +- _ = T{<20 kind=""> +- f: "j", +- g: 4, +- h: "i", +- </20>} +- x, y := make(<21 kind="">chan bool</21>), make(<22 kind="">chan bool</22>) +- select {<23 kind=""> +- case val := <-x:<24 kind=""> +- if val {<25 kind=""> +- fmt.Println(<26 kind="">"true from x"</26>) +- </25>} else {<27 kind=""> +- fmt.Println(<28 kind="">"false from x"</28>) +- </27>}</24> +- case <-y:<29 kind=""> +- fmt.Println(<30 kind="">"y"</30>)</29> +- default:<31 kind=""> +- fmt.Println(<32 kind="">"default"</32>)</31> +- </23>} +- // This is a multiline comment<33 kind="comment"> +- // that is not a doc comment.</33> +- return <34 kind="">` +-this string +-is not indented`</34> +-</3>} +diff -urN a/gopls/internal/regtest/marker/testdata/foldingrange/a_lineonly.txt b/gopls/internal/regtest/marker/testdata/foldingrange/a_lineonly.txt +--- a/gopls/internal/regtest/marker/testdata/foldingrange/a_lineonly.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/foldingrange/a_lineonly.txt 1970-01-01 08:00:00 +@@ -1,163 +0,0 @@ +-This test checks basic behavior of the textDocument/foldingRange, when the +-editor only supports line folding. +- +--- capabilities.json -- +-{ +- "textDocument": { +- "foldingRange": { +- "lineFoldingOnly": true - } -- if len(otherDiags.Diagnostics[0].RelatedInformation) != 1 { -- t.Fatalf("got %d RelatedInformations, expected 1", len(otherDiags.Diagnostics[0].RelatedInformation)) +- } +-} +--- a.go -- +-package folding //@foldingrange(raw) +- +-import ( +- "fmt" +- _ "log" +-) +- +-import _ "os" +- +-// bar is a function. +-// With a multiline doc comment. +-func bar() string { +- /* This is a single line comment */ +- switch { +- case true: +- if true { +- fmt.Println("true") +- } else { +- fmt.Println("false") - } -- // check that the RelatedInformation matches the error from main.go -- c := otherDiags.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) +- case false: +- fmt.Println("false") +- default: +- fmt.Println("default") +- } +- /* This is a multiline +- block +- comment */ +- +- /* This is a multiline +- block +- comment */ +- // Followed by another comment. +- _ = []int{ +- 1, +- 2, +- 3, +- } +- _ = [2]string{"d", +- "e", +- } +- _ = map[string]int{ +- "a": 1, +- "b": 2, +- "c": 3, +- } +- type T struct { +- f string +- g int +- h string +- } +- _ = T{ +- f: "j", +- g: 4, +- h: "i", +- } +- x, y := make(chan bool), make(chan bool) +- select { +- case val := <-x: +- if val { +- fmt.Println("true from x") +- } else { +- fmt.Println("false from x") - } -- }) +- case <-y: +- fmt.Println("y") +- default: +- fmt.Println("default") +- } +- // This is a multiline comment +- // that is not a doc comment. +- return ` +-this string +-is not indented` +-} +--- @raw -- +-package folding //@foldingrange(raw) +- +-import (<0 kind="imports"> +- "fmt" +- _ "log"</0> +-) +- +-import _ "os" +- +-// bar is a function.<1 kind="comment"> +-// With a multiline doc comment.</1> +-func bar() string {<2 kind=""> +- /* This is a single line comment */ +- switch {<3 kind=""> +- case true:<4 kind=""> +- if true {<5 kind=""> +- fmt.Println("true")</5> +- } else {<6 kind=""> +- fmt.Println("false")</6> +- }</4> +- case false:<7 kind=""> +- fmt.Println("false")</7> +- default:<8 kind=""> +- fmt.Println("default")</3></8> +- } +- /* This is a multiline<9 kind="comment"> +- block +- comment */</9> +- +- /* This is a multiline<10 kind="comment"> +- block +- comment */ +- // Followed by another comment.</10> +- _ = []int{<11 kind=""> +- 1, +- 2, +- 3</11>, +- } +- _ = [2]string{"d", +- "e", +- } +- _ = map[string]int{<12 kind=""> +- "a": 1, +- "b": 2, +- "c": 3</12>, +- } +- type T struct {<13 kind=""> +- f string +- g int +- h string</13> +- } +- _ = T{<14 kind=""> +- f: "j", +- g: 4, +- h: "i"</14>, +- } +- x, y := make(chan bool), make(chan bool) +- select {<15 kind=""> +- case val := <-x:<16 kind=""> +- if val {<17 kind=""> +- fmt.Println("true from x")</17> +- } else {<18 kind=""> +- fmt.Println("false from x")</18> +- }</16> +- case <-y:<19 kind=""> +- fmt.Println("y")</19> +- default:<20 kind=""> +- fmt.Println("default")</15></20> +- } +- // This is a multiline comment<21 kind="comment"> +- // that is not a doc comment.</21> +- return <22 kind="">` +-this string +-is not indented`</2></22> +-} +diff -urN a/gopls/internal/regtest/marker/testdata/foldingrange/bad.txt b/gopls/internal/regtest/marker/testdata/foldingrange/bad.txt +--- a/gopls/internal/regtest/marker/testdata/foldingrange/bad.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/foldingrange/bad.txt 1970-01-01 08:00:00 +@@ -1,41 +0,0 @@ +-This test verifies behavior of textDocument/foldingRange in the presence of +-unformatted syntax. +- +--- a.go -- +-package folding //@foldingrange(raw) +- +-import ( "fmt" +- _ "log" +-) +- +-import ( +- _ "os" ) +- +-// badBar is a function. +-func badBar() string { x := true +- if x { +- // This is the only foldable thing in this file when lineFoldingOnly +- fmt.Println("true") +- } else { +- fmt.Println("false") } +- return "" -} +--- @raw -- +-package folding //@foldingrange(raw) +- +-import (<0 kind="imports"> "fmt" +- _ "log" +-</0>) +- +-import (<1 kind="imports"> +- _ "os" </1>) +- +-// badBar is a function. +-func badBar(<2 kind=""></2>) string {<3 kind=""> x := true +- if x {<4 kind=""> +- // This is the only foldable thing in this file when lineFoldingOnly +- fmt.Println(<5 kind="">"true"</5>) +- </4>} else {<6 kind=""> +- fmt.Println(<7 kind="">"false"</7>) </6>} +- return "" +-</3>} +diff -urN a/gopls/internal/regtest/marker/testdata/format/format.txt b/gopls/internal/regtest/marker/testdata/format/format.txt +--- a/gopls/internal/regtest/marker/testdata/format/format.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/format/format.txt 1970-01-01 08:00:00 +@@ -1,80 +0,0 @@ +-This test checks basic behavior of textDocument/formatting requests. - --func TestNotifyOrphanedFiles(t *testing.T) { -- const files = ` --- go.mod -- -module mod.com - --go 1.12 ---- a/a.go -- --package a +-go 1.18 +--- good.go -- +-package format //@format(good) - --func main() { -- var x int +-import ( +- "log" +-) +- +-func goodbye() { +- log.Printf("byeeeee") -} ---- a/a_exclude.go -- --// +build exclude - --package a +--- @good -- +-package format //@format(good) - --func _() { -- var x int --} --` -- Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("a/a.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("a/a.go", "x")), -- ) -- env.OpenFile("a/a_exclude.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("a/a_exclude.go", "package (a)")), -- ) -- }) +-import ( +- "log" +-) +- +-func goodbye() { +- log.Printf("byeeeee") -} +--- bad.go -- +-package format //@format(bad) - --func TestEnableAllExperiments(t *testing.T) { -- // Before the oldest supported Go version, gopls sends a warning to upgrade -- // Go, which fails the expectation below. -- testenv.NeedsGo1Point(t, lsp.OldestSupportedGoVersion()) +-import ( +- "runtime" +- "fmt" +- "log" +-) - -- const mod = ` ---- go.mod -- --module mod.com +-func hello() { - --go 1.12 ---- main.go -- --package main - --import "bytes" - --func b(c bytes.Buffer) { -- _ = 1 +- +- var x int //@diag("x", re"x declared (and|but) not used") -} --` -- WithOptions( -- Settings{"allExperiments": true}, -- ).Run(t, mod, func(t *testing.T, env *Env) { -- // Confirm that the setting doesn't cause any warnings. -- env.OnceMet( -- InitialWorkspaceLoad, -- NoShownMessage(""), // empty substring to match any message -- ) -- }) +- +-func hi() { +- runtime.GOROOT() +- fmt.Printf("") +- +- log.Printf("") -} +--- @bad -- +-package format //@format(bad) - --func TestSwig(t *testing.T) { -- // This is fixed in Go 1.17, but not earlier. -- testenv.NeedsGo1Point(t, 17) +-import ( +- "fmt" +- "log" +- "runtime" +-) - -- 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") -- } +-func hello() { - -- const mod = ` ---- go.mod -- --module mod.com +- var x int //@diag("x", re"x declared (and|but) not used") +-} - --go 1.12 ---- pkg/simple/export_swig.go -- --package simple +-func hi() { +- runtime.GOROOT() +- fmt.Printf("") - --func ExportSimple(x, y int) int { -- return Gcd(x, y) +- log.Printf("") -} ---- pkg/simple/simple.swigcxx -- --%module simple +--- newline.go -- +-package format //@format(newline) +-func _() {} +--- @newline -- +-package format //@format(newline) +-func _() {} +--- oneline.go -- +-package format //@format(oneline) +--- @oneline -- +-package format //@format(oneline) +diff -urN a/gopls/internal/regtest/marker/testdata/format/issue59554.txt b/gopls/internal/regtest/marker/testdata/format/issue59554.txt +--- a/gopls/internal/regtest/marker/testdata/format/issue59554.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/format/issue59554.txt 1970-01-01 08:00:00 +@@ -1,33 +0,0 @@ +-Test case for golang/go#59554: data corruption on formatting due to line +-directives. - --%inline %{ --extern int gcd(int x, int y) +-Note that gofumpt is needed for this test case, as it reformats var decls into +-short var decls. +- +-Note that gofumpt requires Go 1.18. +- +--- flags -- +--min_go=go1.18 +- +--- settings.json -- -{ -- int g; -- g = y; -- while (x > 0) { -- g = x; -- x = y % x; -- y = g; -- } -- return g; +- "formatting.gofumpt": true -} --%} --- main.go -- --package a +-package main //@format(main) - --func main() { -- var x int +-func Match(data []byte) int { +-//line :1 +- var idx = ^uint(0) +- _ = idx +- return -1 -} --` -- Run(t, mod, func(t *testing.T, env *Env) { -- env.OnceMet( -- InitialWorkspaceLoad, -- NoDiagnostics(WithMessage("illegal character U+0023 '#'")), -- ) -- }) +--- @main -- +-package main //@format(main) +- +-func Match(data []byte) int { +-//line :1 +- idx := ^uint(0) +- _ = idx +- return -1 -} +diff -urN a/gopls/internal/regtest/marker/testdata/format/noparse.txt b/gopls/internal/regtest/marker/testdata/format/noparse.txt +--- a/gopls/internal/regtest/marker/testdata/format/noparse.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/format/noparse.txt 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ +-This test checks that formatting does not run on code that has parse errors. - --// 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) { -- const proxy = ` ---- example.com@v1.2.3/go.mod -- --module example.com +--- parse.go -- +-package noparse_format //@format(parse) - --go 1.12 ---- example.com@v1.2.3/blah/blah.go -- --package blah +-func _() { +-f() //@diag("f", re"(undefined|undeclared name): f") +-} +--- @parse -- +-package noparse_format //@format(parse) - --const Name = "Blah" ---- random.org@v1.2.3/go.mod -- --module random.org +-func _() { +- f() //@diag("f", re"(undefined|undeclared name): f") +-} +--- noparse.go -- +-package noparse_format //@format(noparse) - --go 1.12 ---- random.org@v1.2.3/blah/blah.go -- --package hello +-// The nonewvars expectation asserts that the go/analysis framework ran. - --const Name = "Hello" --` +-func what() { +- var hi func() +- if { hi() //@diag(re"(){", re".*missing.*") +- } +- hi := nil +-} +--- @noparse -- +-7:5: missing condition in if statement +diff -urN a/gopls/internal/regtest/marker/testdata/highlight/highlight.txt b/gopls/internal/regtest/marker/testdata/highlight/highlight.txt +--- a/gopls/internal/regtest/marker/testdata/highlight/highlight.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/highlight/highlight.txt 1970-01-01 08:00:00 +@@ -1,158 +0,0 @@ +-This test checks basic functionality of the textDocument/highlight request. - -- const contents = ` ---- go.mod -- --module mod.com +--- highlights.go -- +-package highlights - --go 1.12 ---- main.go -- --package main +-import ( +- "fmt" //@loc(fmtImp, "\"fmt\""),highlight(fmtImp, fmtImp, fmt1, fmt2, fmt3, fmt4) +- h2 "net/http" //@loc(hImp, "h2"),highlight(hImp, hImp, hUse) +- "sort" +-) - --import "example.com/blah" +-type F struct{ bar int } //@loc(barDeclaration, "bar"),highlight(barDeclaration, barDeclaration, bar1, bar2, bar3) - --func main() { -- blah.Hello() +-func _() F { +- return F{ +- bar: 123, //@loc(bar1, "bar"),highlight(bar1, barDeclaration, bar1, bar2, bar3) +- } -} ---- bob.go -- --package main ---- foo/foo.go -- --package foo ---- foo/foo_test.go -- --package foo_ --` - -- WithOptions( -- ProxyFiles(proxy), -- InGOPATH(), -- EnvVars{"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.AfterChange( -- NoDiagnostics(ForFile("foo/foo_test.go")), -- NoOutstandingWork(), -- ) -- }) +-var foo = F{bar: 52} //@loc(fooDeclaration, "foo"),loc(bar2, "bar"),highlight(fooDeclaration, fooDeclaration, fooUse),highlight(bar2, barDeclaration, bar1, bar2, bar3) +- +-func Print() { //@loc(printFunc, "Print"),highlight(printFunc, printFunc, printTest) +- _ = h2.Client{} //@loc(hUse, "h2"),highlight(hUse, hImp, hUse) +- +- fmt.Println(foo) //@loc(fooUse, "foo"),highlight(fooUse, fooDeclaration, fooUse),loc(fmt1, "fmt"),highlight(fmt1, fmtImp, fmt1, fmt2, fmt3, fmt4) +- fmt.Print("yo") //@loc(printSep, "Print"),highlight(printSep, printSep, print1, print2),loc(fmt2, "fmt"),highlight(fmt2, fmtImp, fmt1, fmt2, fmt3, fmt4) -} - --// TestProgressBarErrors confirms that critical workspace load errors are shown --// and updated via progress reports. --func TestProgressBarErrors(t *testing.T) { -- const pkg = ` ---- go.mod -- --modul mod.com +-func (x *F) Inc() { //@loc(xRightDecl, "x"),loc(xLeftDecl, " *"),highlight(xRightDecl, xRightDecl, xUse),highlight(xLeftDecl, xRightDecl, xUse) +- x.bar++ //@loc(xUse, "x"),loc(bar3, "bar"),highlight(xUse, xRightDecl, xUse),highlight(bar3, barDeclaration, bar1, bar2, bar3) +-} - --go 1.12 ---- main.go -- --package main --` -- Run(t, pkg, func(t *testing.T, env *Env) { -- env.OpenFile("go.mod") -- env.AfterChange( -- OutstandingWork(lsp.WorkspaceLoadFailure, "unknown directive"), -- ) -- env.EditBuffer("go.mod", fake.NewEdit(0, 0, 3, 0, `module mod.com +-func testFunctions() { +- fmt.Print("main start") //@loc(print1, "Print"),highlight(print1, printSep, print1, print2),loc(fmt3, "fmt"),highlight(fmt3, fmtImp, fmt1, fmt2, fmt3, fmt4) +- fmt.Print("ok") //@loc(print2, "Print"),highlight(print2, printSep, print1, print2),loc(fmt4, "fmt"),highlight(fmt4, fmtImp, fmt1, fmt2, fmt3, fmt4) +- Print() //@loc(printTest, "Print"),highlight(printTest, printFunc, printTest) +-} - --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.AfterChange( -- OutstandingWork(lsp.WorkspaceLoadFailure, "invalid go version"), -- ) -- env.RegexpReplace("go.mod", "go 1.hello", "go 1.12") -- env.SaveBufferWithoutActions("go.mod") -- env.AfterChange( -- NoOutstandingWork(), -- ) -- }) +-// DocumentHighlight is undefined, so its uses below are type errors. +-// Nevertheless, document highlighting should still work. +-//@diag(doc1, re"undefined|undeclared"), diag(doc2, re"undefined|undeclared"), diag(doc3, re"undefined|undeclared") +- +-func toProtocolHighlight(rngs []int) []DocumentHighlight { //@loc(doc1, "DocumentHighlight"),loc(docRet1, "[]DocumentHighlight"),highlight(doc1, docRet1, doc1, doc2, doc3, result) +- result := make([]DocumentHighlight, 0, len(rngs)) //@loc(doc2, "DocumentHighlight"),highlight(doc2, doc1, doc2, doc3) +- for _, rng := range rngs { +- result = append(result, DocumentHighlight{ //@loc(doc3, "DocumentHighlight"),highlight(doc3, doc1, doc2, doc3) +- Range: rng, +- }) +- } +- return result //@loc(result, "result") -} - --func TestDeleteDirectory(t *testing.T) { -- const mod = ` ---- bob/bob.go -- --package bob +-func testForLoops() { +- for i := 0; i < 10; i++ { //@loc(forDecl1, "for"),highlight(forDecl1, forDecl1, brk1, cont1) +- if i > 8 { +- break //@loc(brk1, "break"),highlight(brk1, forDecl1, brk1, cont1) +- } +- if i < 2 { +- for j := 1; j < 10; j++ { //@loc(forDecl2, "for"),highlight(forDecl2, forDecl2, cont2) +- if j < 3 { +- for k := 1; k < 10; k++ { //@loc(forDecl3, "for"),highlight(forDecl3, forDecl3, cont3) +- if k < 3 { +- continue //@loc(cont3, "continue"),highlight(cont3, forDecl3, cont3) +- } +- } +- continue //@loc(cont2, "continue"),highlight(cont2, forDecl2, cont2) +- } +- } +- continue //@loc(cont1, "continue"),highlight(cont1, forDecl1, brk1, cont1) +- } +- } - --func Hello() { -- var x int +- arr := []int{} +- for i := range arr { //@loc(forDecl4, "for"),highlight(forDecl4, forDecl4, brk4, cont4) +- if i > 8 { +- break //@loc(brk4, "break"),highlight(brk4, forDecl4, brk4, cont4) +- } +- if i < 4 { +- continue //@loc(cont4, "continue"),highlight(cont4, forDecl4, brk4, cont4) +- } +- } +- +-Outer: +- for i := 0; i < 10; i++ { //@loc(forDecl5, "for"),highlight(forDecl5, forDecl5, brk5, brk6, brk8) +- break //@loc(brk5, "break"),highlight(brk5, forDecl5, brk5, brk6, brk8) +- for { //@loc(forDecl6, "for"),highlight(forDecl6, forDecl6, cont5), diag("for", re"unreachable") +- if i == 1 { +- break Outer //@loc(brk6, "break Outer"),highlight(brk6, forDecl5, brk5, brk6, brk8) +- } +- switch i { //@loc(switch1, "switch"),highlight(switch1, switch1, brk7) +- case 5: +- break //@loc(brk7, "break"),highlight(brk7, switch1, brk7) +- case 6: +- continue //@loc(cont5, "continue"),highlight(cont5, forDecl6, cont5) +- case 7: +- break Outer //@loc(brk8, "break Outer"),highlight(brk8, forDecl5, brk5, brk6, brk8) +- } +- } +- } -} ---- go.mod -- --module mod.com ---- cmd/main.go -- --package main - --import "mod.com/bob" +-func testSwitch() { +- var i, j int - --func main() { -- bob.Hello() +-L1: +- for { //@loc(forDecl7, "for"),highlight(forDecl7, forDecl7, brk10, cont6) +- L2: +- switch i { //@loc(switch2, "switch"),highlight(switch2, switch2, brk11, brk12, brk13) +- case 1: +- switch j { //@loc(switch3, "switch"),highlight(switch3, switch3, brk9) +- case 1: +- break //@loc(brk9, "break"),highlight(brk9, switch3, brk9) +- case 2: +- break L1 //@loc(brk10, "break L1"),highlight(brk10, forDecl7, brk10, cont6) +- case 3: +- break L2 //@loc(brk11, "break L2"),highlight(brk11, switch2, brk11, brk12, brk13) +- default: +- continue //@loc(cont6, "continue"),highlight(cont6, forDecl7, brk10, cont6) +- } +- case 2: +- break //@loc(brk12, "break"),highlight(brk12, switch2, brk11, brk12, brk13) +- default: +- break L2 //@loc(brk13, "break L2"),highlight(brk13, switch2, brk11, brk12, brk13) +- } +- } -} --` -- Run(t, mod, func(t *testing.T, env *Env) { -- env.OnceMet( -- InitialWorkspaceLoad, -- FileWatchMatching("bob"), -- ) -- env.RemoveWorkspaceFile("bob") -- env.AfterChange( -- Diagnostics(env.AtRegexp("cmd/main.go", `"mod.com/bob"`)), -- NoDiagnostics(ForFile("bob/bob.go")), -- NoFileWatchMatching("bob"), -- ) +- +-func testReturn() bool { //@loc(func1, "func"),loc(bool1, "bool"),highlight(func1, func1, fullRet11, fullRet12),highlight(bool1, bool1, false1, bool2, true1) +- if 1 < 2 { +- return false //@loc(ret11, "return"),loc(fullRet11, "return false"),loc(false1, "false"),highlight(ret11, func1, fullRet11, fullRet12) +- } +- candidates := []int{} +- sort.SliceStable(candidates, func(i, j int) bool { //@loc(func2, "func"),loc(bool2, "bool"),highlight(func2, func2, fullRet2) +- return candidates[i] > candidates[j] //@loc(ret2, "return"),loc(fullRet2, "return candidates[i] > candidates[j]"),highlight(ret2, func2, fullRet2) - }) +- return true //@loc(ret12, "return"),loc(fullRet12, "return true"),loc(true1, "true"),highlight(ret12, func1, fullRet11, fullRet12) -} - --// Confirms that circular imports are tested and reported. --func TestCircularImports(t *testing.T) { -- const mod = ` ---- go.mod -- --module mod.com +-func testReturnFields() float64 { //@loc(retVal1, "float64"),highlight(retVal1, retVal1, retVal11, retVal21) +- if 1 < 2 { +- return 20.1 //@loc(retVal11, "20.1"),highlight(retVal11, retVal1, retVal11, retVal21) +- } +- z := 4.3 //@loc(zDecl, "z") +- return z //@loc(retVal21, "z"),highlight(retVal21, retVal1, retVal11, zDecl, retVal21) +-} - --go 1.12 ---- self/self.go -- --package self +-func testReturnMultipleFields() (float32, string) { //@loc(retVal31, "float32"),loc(retVal32, "string"),highlight(retVal31, retVal31, retVal41, retVal51),highlight(retVal32, retVal32, retVal42, retVal52) +- y := "im a var" //@loc(yDecl, "y"), +- if 1 < 2 { +- return 20.1, y //@loc(retVal41, "20.1"),loc(retVal42, "y"),highlight(retVal41, retVal31, retVal41, retVal51),highlight(retVal42, retVal32, yDecl, retVal42, retVal52) +- } +- return 4.9, "test" //@loc(retVal51, "4.9"),loc(retVal52, "\"test\""),highlight(retVal51, retVal31, retVal41, retVal51),highlight(retVal52, retVal32, retVal42, retVal52) +-} - --import _ "mod.com/self" --func Hello() {} ---- double/a/a.go -- --package a +-func testReturnFunc() int32 { //@loc(retCall, "int32") +- mulch := 1 //@loc(mulchDec, "mulch"),highlight(mulchDec, mulchDec, mulchRet) +- return int32(mulch) //@loc(mulchRet, "mulch"),loc(retFunc, "int32"),loc(retTotal, "int32(mulch)"),highlight(mulchRet, mulchDec, mulchRet),highlight(retFunc, retCall, retFunc, retTotal) +-} +diff -urN a/gopls/internal/regtest/marker/testdata/highlight/issue60435.txt b/gopls/internal/regtest/marker/testdata/highlight/issue60435.txt +--- a/gopls/internal/regtest/marker/testdata/highlight/issue60435.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/highlight/issue60435.txt 1970-01-01 08:00:00 +@@ -1,15 +0,0 @@ +-This is a regression test for issue 60435: +-Highlighting "net/http" shouldn't have any effect +-on an import path that contains it as a substring, +-such as httptest. - --import _ "mod.com/double/b" ---- double/b/b.go -- --package b +--- highlights.go -- +-package highlights - --import _ "mod.com/double/a" ---- triple/a/a.go -- --package a +-import ( +- "net/http" //@loc(httpImp, `"net/http"`) +- "net/http/httptest" //@loc(httptestImp, `"net/http/httptest"`) +-) - --import _ "mod.com/triple/b" ---- triple/b/b.go -- --package b +-var _ = httptest.NewRequest +-var _ = http.NewRequest //@loc(here, "http"), highlight(here, here, httpImp) +diff -urN a/gopls/internal/regtest/marker/testdata/hover/basiclit.txt b/gopls/internal/regtest/marker/testdata/hover/basiclit.txt +--- a/gopls/internal/regtest/marker/testdata/hover/basiclit.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/hover/basiclit.txt 1970-01-01 08:00:00 +@@ -1,81 +0,0 @@ +-This test checks gopls behavior when hovering over basic literals. +--- basiclit.go -- +-package basiclit - --import _ "mod.com/triple/c" ---- triple/c/c.go -- --package c +-func _() { +- _ = 'a' //@hover("'a'", "'a'", latinA) +- _ = 0x61 //@hover("0x61", "0x61", latinAHex) - --import _ "mod.com/triple/a" --` -- Run(t, mod, func(t *testing.T, env *Env) { -- env.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics(env.AtRegexp("self/self.go", `_ "mod.com/self"`), WithMessage("import cycle not allowed")), -- Diagnostics(env.AtRegexp("double/a/a.go", `_ "mod.com/double/b"`), WithMessage("import cycle not allowed")), -- Diagnostics(env.AtRegexp("triple/a/a.go", `_ "mod.com/triple/b"`), WithMessage("import cycle not allowed")), -- ) -- }) --} +- _ = '\u2211' //@hover("'\\u2211'", "'\\u2211'", summation) +- _ = 0x2211 //@hover("0x2211", "0x2211", summationHex) +- _ = "foo \u2211 bar" //@hover("\\u2211", "\\u2211", summation) - --// 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 +- _ = '\a' //@hover("'\\a'", "'\\a'", control) +- _ = "foo \a bar" //@hover("\\a", "\\a", control) - --go 1.16 ---- a/a.go -- --package a +- _ = '\U0001F30A' //@hover("'\\U0001F30A'", "'\\U0001F30A'", waterWave) +- _ = 0x0001F30A //@hover("0x0001F30A", "0x0001F30A", waterWaveHex) +- _ = 0X0001F30A //@hover("0X0001F30A", "0X0001F30A", waterWaveHex) +- _ = "foo \U0001F30A bar" //@hover("\\U0001F30A", "\\U0001F30A", waterWave) - --import "mod.test/b" +- _ = '\x7E' //@hover("'\\x7E'", "'\\x7E'", tilde) +- _ = "foo \x7E bar" //@hover("\\x7E", "\\x7E", tilde) +- _ = "foo \a bar" //@hover("\\a", "\\a", control) - --const A = b.A --const B = 2 ---- b/b.go -- --package b +- _ = '\173' //@hover("'\\173'", "'\\173'", leftCurly) +- _ = "foo \173 bar" //@hover("\\173","\\173", leftCurly) +- _ = "foo \173 bar \u2211 baz" //@hover("\\173","\\173", leftCurly) +- _ = "foo \173 bar \u2211 baz" //@hover("\\u2211","\\u2211", summation) +- _ = "foo\173bar\u2211baz" //@hover("\\173","\\173", leftCurly) +- _ = "foo\173bar\u2211baz" //@hover("\\u2211","\\u2211", summation) - --import "mod.test/a" +- // search for runes in string only if there is an escaped sequence +- _ = "hello" //@hover(`"hello"`, _, _) - --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.AfterChange( -- // 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( -- Diagnostics(env.AtRegexp("a/a.go", `"mod.test/b"`), WithMessage("import cycle")), -- Diagnostics(env.AtRegexp("b/b.go", `"mod.test/a"`), WithMessage("import cycle")), -- ), -- ) -- env.RegexpReplace("b/b.go", `const B = a\.B`, "") -- env.SaveBuffer("b/b.go") -- env.AfterChange( -- NoDiagnostics(ForFile("a/a.go")), -- NoDiagnostics(ForFile("b/b.go")), -- ) -- }) --} +- // incorrect escaped rune sequences +- _ = '\0' //@hover("'\\0'", _, _),diag(re`\\0()'`, re"illegal character") +- _ = '\u22111' //@hover("'\\u22111'", _, _) +- _ = '\U00110000' //@hover("'\\U00110000'", _, _) +- _ = '\u12e45'//@hover("'\\u12e45'", _, _) +- _ = '\xa' //@hover("'\\xa'", _, _) +- _ = 'aa' //@hover("'aa'", _, _) +- +- // other basic lits +- _ = 1 //@hover("1", _, _) +- _ = 1.2 //@hover("1.2", _, _) +- _ = 1.2i //@hover("1.2i", _, _) +- _ = 0123 //@hover("0123", _, _) +- _ = 0b1001 //@hover("0b", "0b1001", binaryNumber) +- _ = 0B1001 //@hover("0B", "0B1001", binaryNumber) +- _ = 0o77 //@hover("0o", "0o77", octalNumber) +- _ = 0O77 //@hover("0O", "0O77", octalNumber) +- _ = 0x1234567890 //@hover("0x1234567890", "0x1234567890", hexNumber) +- _ = 0X1234567890 //@hover("0X1234567890", "0X1234567890", hexNumber) +- _ = 0x1000000000000000000 //@hover("0x1", "0x1000000000000000000", bigHex) +-) +--- @bigHex/hover.md -- +-4722366482869645213696 +--- @binaryNumber/hover.md -- +-9 +--- @control/hover.md -- +-U+0007, control +--- @hexNumber/hover.md -- +-78187493520 +--- @latinA/hover.md -- +-'a', U+0061, LATIN SMALL LETTER A +--- @latinAHex/hover.md -- +-97, 'a', U+0061, LATIN SMALL LETTER A +--- @leftCurly/hover.md -- +-'{', U+007B, LEFT CURLY BRACKET +--- @octalNumber/hover.md -- +-63 +--- @summation/hover.md -- +-'∑', U+2211, N-ARY SUMMATION +--- @summationHex/hover.md -- +-8721, '∑', U+2211, N-ARY SUMMATION +--- @tilde/hover.md -- +-'~', U+007E, TILDE +--- @waterWave/hover.md -- +-'🌊', U+1F30A, WATER WAVE +--- @waterWaveHex/hover.md -- +-127754, '🌊', U+1F30A, WATER WAVE +diff -urN a/gopls/internal/regtest/marker/testdata/hover/const.txt b/gopls/internal/regtest/marker/testdata/hover/const.txt +--- a/gopls/internal/regtest/marker/testdata/hover/const.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/hover/const.txt 1970-01-01 08:00:00 +@@ -1,157 +0,0 @@ +-This test checks hovering over constants. +- +--- flags -- +--min_go=go1.17 - --func TestBadImport(t *testing.T) { -- const mod = ` --- go.mod -- -module mod.com - --go 1.12 ---- main.go -- --package main +-go 1.17 +- +--- c.go -- +-package c - -import ( -- _ "nosuchpkg" +- "math" +- "time" -) --` -- t.Run("module", func(t *testing.T) { -- Run(t, mod, func(t *testing.T, env *Env) { -- env.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics(env.AtRegexp("main.go", `"nosuchpkg"`), WithMessage(`could not import nosuchpkg (no required module provides package "nosuchpkg"`)), -- ) -- }) -- }) -- t.Run("GOPATH", func(t *testing.T) { -- WithOptions( -- InGOPATH(), -- EnvVars{"GO111MODULE": "off"}, -- Modes(Default), -- ).Run(t, mod, func(t *testing.T, env *Env) { -- env.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics(env.AtRegexp("main.go", `"nosuchpkg"`), WithMessage(`cannot find package "nosuchpkg"`)), -- ) -- }) -- }) --} - --func TestNestedModules(t *testing.T) { -- const proxy = ` ---- nested.com@v1.0.0/go.mod -- --module nested.com +-const X = 0 //@hover("X", "X", bX) +- +-// dur is a constant of type time.Duration. +-const dur = 15*time.Minute + 10*time.Second + 350*time.Millisecond //@hover("dur", "dur", dur) - --go 1.12 ---- nested.com@v1.0.0/hello/hello.go -- --package hello +-// MaxFloat32 is used in another package. +-const MaxFloat32 = 0x1p127 * (1 + (1 - 0x1p-23)) - --func Hello() {} --` +-// Numbers. +-func _() { +- const hex, bin = 0xe34e, 0b1001001 - -- const nested = ` ---- go.mod -- --module mod.com +- const ( +- // no inline comment +- decimal = 153 - --go 1.12 +- numberWithUnderscore int64 = 10_000_000_000 +- octal = 0o777 +- expr = 2 << (0b111&0b101 - 2) +- boolean = (55 - 3) == (26 * 2) +- ) - --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 +- _ = decimal //@hover("decimal", "decimal", decimalConst) +- _ = hex //@hover("hex", "hex", hexConst) +- _ = bin //@hover("bin", "bin", binConst) +- _ = numberWithUnderscore //@hover("numberWithUnderscore", "numberWithUnderscore", numberWithUnderscoreConst) +- _ = octal //@hover("octal", "octal", octalConst) +- _ = expr //@hover("expr", "expr", exprConst) +- _ = boolean //@hover("boolean", "boolean", boolConst) - --import "nested.com/hello" +- const ln10 = 2.30258509299404568401799145468436420760110148862877297603332790 - --func main() { -- hello.Hello() +- _ = ln10 //@hover("ln10", "ln10", ln10Const) -} ---- nested/go.mod -- --module nested.com - ---- nested/hello/hello.go -- --package hello +-// Iota. +-func _() { +- const ( +- a = 1 << iota +- b +- ) - --func Hello() { -- helloHelper() +- _ = a //@hover("a", "a", aIota) +- _ = b //@hover("b", "b", bIota) -} ---- nested/hello/hello_helper.go -- --package hello - --func helloHelper() {} --` -- WithOptions( -- ProxyFiles(proxy), -- Modes(Default), -- ).Run(t, nested, func(t *testing.T, env *Env) { -- // Expect a diagnostic in a nested module. -- env.OpenFile("nested/hello/hello.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("nested/hello/hello.go", "helloHelper")), -- Diagnostics(env.AtRegexp("nested/hello/hello.go", "package hello"), WithMessage("nested module")), -- OutstandingWork(lsp.WorkspaceLoadFailure, "nested module"), -- ) -- }) --} +-// Strings. +-func _() { +- const ( +- str = "hello" + " " + "world" +- longStr = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur eget ipsum non nunc +-molestie mattis id quis augue. Mauris dictum tincidunt ipsum, in auctor arcu congue eu. +-Morbi hendrerit fringilla libero commodo varius. Vestibulum in enim rutrum, rutrum tellus +-aliquet, luctus enim. Nunc sem ex, consectetur id porta nec, placerat vel urna.` +- ) - --func TestAdHocPackagesReloading(t *testing.T) { -- const nomod = ` ---- main.go -- --package main +- _ = str //@hover("str", "str", strConst) +- _ = longStr //@hover("longStr", "longStr", longStrConst) +-} - --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.AfterChange(NoLogMatching(protocol.Info, "packages=1")) -- }) +-// Constants from other packages. +-func _() { +- _ = math.Log2E //@hover("Log2E", "Log2E", log2eConst) -} - --func TestBuildTagChange(t *testing.T) { -- const files = ` ---- go.mod -- --module mod.com +--- @bX/hover.md -- +-```go +-const X untyped int = 0 +-``` - --go 1.12 ---- foo.go -- --// decoy comment --// +build hidden --// decoy comment +-@hover("X", "X", bX) - --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.AfterChange(Diagnostics(env.AtRegexp("bar.go", `Foo`))) -- env.RegexpReplace("foo.go", `\+build`, "") -- env.AfterChange(NoDiagnostics(ForFile("bar.go"))) -- }) +-[`c.X` on pkg.go.dev](https://pkg.go.dev/mod.com#X) +--- @dur/hover.md -- +-```go +-const dur time.Duration = 15*time.Minute + 10*time.Second + 350*time.Millisecond // 15m10.35s +-``` - --} +-dur is a constant of type time.Duration. +--- @decimalConst/hover.md -- +-```go +-const decimal untyped int = 153 +-``` - --func TestIssue44736(t *testing.T) { -- const files = ` -- -- go.mod -- --module blah.com +-no inline comment +--- @hexConst/hover.md -- +-```go +-const hex untyped int = 0xe34e // 58190 +-``` +--- @binConst/hover.md -- +-```go +-const bin untyped int = 0b1001001 // 73 +-``` +--- @numberWithUnderscoreConst/hover.md -- +-```go +-const numberWithUnderscore int64 = 10_000_000_000 // 10000000000 +-``` +--- @octalConst/hover.md -- +-```go +-const octal untyped int = 0o777 // 511 +-``` +--- @exprConst/hover.md -- +-```go +-const expr untyped int = 2 << (0b111&0b101 - 2) // 16 +-``` +--- @boolConst/hover.md -- +-```go +-const boolean untyped bool = (55 - 3) == (26 * 2) // true +-``` +--- @ln10Const/hover.md -- +-```go +-const ln10 untyped float = 2.30258509299404568401799145468436420760110148862877297603332790 // 2.30259 +-``` +--- @aIota/hover.md -- +-```go +-const a untyped int = 1 << iota // 1 +-``` +--- @bIota/hover.md -- +-```go +-const b untyped int = 2 +-``` +--- @strConst/hover.md -- +-```go +-const str untyped string = "hello world" +-``` +--- @longStrConst/hover.md -- +-```go +-const longStr untyped string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur e... +-``` +--- @log2eConst/hover.md -- +-```go +-const math.Log2E untyped float = 1 / Ln2 // 1.4427 +-``` - --go 1.16 ---- main.go -- --package main +-Mathematical constants. - --import "fmt" - --func main() { -- asdf -- fmt.Printf("This is a test %v") -- fdas --} ---- other.go -- --package main +-[`math.Log2E` on pkg.go.dev](https://pkg.go.dev/math#Log2E) +diff -urN a/gopls/internal/regtest/marker/testdata/hover/generics.txt b/gopls/internal/regtest/marker/testdata/hover/generics.txt +--- a/gopls/internal/regtest/marker/testdata/hover/generics.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/hover/generics.txt 1970-01-01 08:00:00 +@@ -1,76 +0,0 @@ +-This file contains tests for hovering over generic Go code. - --` -- Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("main.go") -- env.OpenFile("other.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("main.go", "asdf")), -- Diagnostics(env.AtRegexp("main.go", "fdas")), -- ) -- env.SetBufferContent("other.go", "package main\n\nasdf") -- // The new diagnostic in other.go should not suppress diagnostics in main.go. -- env.AfterChange( -- Diagnostics(env.AtRegexp("other.go", "asdf"), WithMessage("expected declaration")), -- Diagnostics(env.AtRegexp("main.go", "asdf")), -- ) -- }) --} +--- flags -- +--min_go=go1.18 - --func TestInitialization(t *testing.T) { -- const files = ` --- go.mod -- +-// A go.mod is require for correct pkgsite links. +-// TODO(rfindley): don't link to ad-hoc or command-line-arguments packages! -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.AfterChange( -- NoLogMatching(protocol.Error, "initial workspace load failed"), -- ) -- }) --} -- --// 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.18 - --go 1.12 ---- main.go -- --package main +--- generics.go -- +-package generics - --func main() {} --` -- Run(t, files, func(t *testing.T, env *Env) { -- env.OpenFile("go.mod") -- env.AfterChange( -- LogMatching(protocol.Info, `.*query=\[builtin mod.com/...\].*`, 1, false), -- ) -- }) +-type value[T any] struct { //hover("lue", "value", value),hover("T", "T", valueT) +- val T //@hover("T", "T", valuevalT) +- Q int //@hover("Q", "Q", valueQ) -} - --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.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics(env.AtRegexp("main.go", `0b10`), WithMessage("go1.13 or later")), -- ) -- env.WriteWorkspaceFile("go.mod", "module mod.com \n\ngo 1.13\n") -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- ) -- }) +-type Value[T any] struct { //@hover("T", "T", ValueT) +- val T //@hover("T", "T", ValuevalT) +- Q int //@hover("Q", "Q", ValueQ) -} - --func TestNoQuickFixForUndeclaredConstraint(t *testing.T) { -- testenv.NeedsGo1Point(t, 18) -- const files = ` ---- go.mod -- --module mod.com +-// disabled - see issue #54822 +-func F[P interface{ ~int | string }]() { // hover("P","P",Ptparam) +- // disabled - see issue #54822 +- var _ P // hover("P","P",Pvar) +-} - --go 1.18 ---- main.go -- --package main +--- inferred.go -- +-package generics - --func F[T C](_ T) { +-func app[S interface{ ~[]E }, E interface{}](s S, e E) S { +- return append(s, e) -} --` - -- Run(t, files, func(t *testing.T, env *Env) { -- var d protocol.PublishDiagnosticsParams -- env.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics(env.AtRegexp("main.go", `C`)), -- 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 _() { +- _ = app[[]int] //@hover("app", "app", appint) +- _ = app[[]int, int] //@hover("app", "app", appint) +- _ = app[[]int]([]int{}, 0) //@hover("app", "app", appint) +- _ = app([]int{}, 0) //@hover("app", "app", appint) -} - --func TestEditGoDirective(t *testing.T) { -- testenv.NeedsGo1Point(t, 18) -- const files = ` ---- go.mod -- --module mod.com +--- @ValueQ/hover.md -- +-```go +-field Q int +-``` - --go 1.16 ---- main.go -- --package main +-@hover("Q", "Q", ValueQ) - --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.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics(env.AtRegexp("main.go", `T any`), WithMessage("type parameter")), -- ReadDiagnostics("main.go", &d), -- ) - -- env.ApplyQuickFixes("main.go", d.Diagnostics) -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- ) -- }) --} +-[`(generics.Value).Q` on pkg.go.dev](https://pkg.go.dev/mod.com#Value.Q) +--- @ValueT/hover.md -- +-```go +-type parameter T any +-``` +--- @ValuevalT/hover.md -- +-```go +-type parameter T any +-``` +--- @appint/hover.md -- +-```go +-func app(s []int, e int) []int // func[S interface{~[]E}, E interface{}](s S, e E) S +-``` +--- @valueQ/hover.md -- +-```go +-field Q int +-``` +- +-@hover("Q", "Q", valueQ) +--- @valuevalT/hover.md -- +-```go +-type parameter T any +-``` +diff -urN a/gopls/internal/regtest/marker/testdata/hover/godef.txt b/gopls/internal/regtest/marker/testdata/hover/godef.txt +--- a/gopls/internal/regtest/marker/testdata/hover/godef.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/hover/godef.txt 1970-01-01 08:00:00 +@@ -1,406 +0,0 @@ +-This test was ported from 'godef' in the old marker tests. +-It tests various hover and definition requests. +- +--- flags -- +--min_go=go1.20 - --func TestEditGoDirectiveWorkspace(t *testing.T) { -- testenv.NeedsGo1Point(t, 18) -- const files = ` --- go.mod -- --module mod.com +-module godef.test - --go 1.16 ---- go.work -- -go 1.18 - --use . ---- main.go -- --package main +--- a/a_x_test.go -- +-package a_test - --func F[T any](_ T) { +-import ( +- "testing" +-) +- +-func TestA2(t *testing.T) { //@hover("TestA2", "TestA2", TestA2) +- Nonexistant() //@diag("Nonexistant", re"(undeclared name|undefined): Nonexistant") -} --` -- Run(t, files, func(_ *testing.T, env *Env) { // Create a new workspace-level directory and empty file. -- var d protocol.PublishDiagnosticsParams - -- // We should have a diagnostic because generics are not supported at 1.16. -- env.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics(env.AtRegexp("main.go", `T any`), WithMessage("type parameter")), -- ReadDiagnostics("main.go", &d), -- ) +--- @TestA2/hover.md -- +-```go +-func TestA2(t *testing.T) +-``` +--- @ember/hover.md -- +-```go +-field Member string +-``` - -- // This diagnostic should have a quick fix to edit the go version. -- env.ApplyQuickFixes("main.go", d.Diagnostics) +-@loc(Member, "Member") - -- // Once the edit is applied, the problematic diagnostics should be -- // resolved. -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- ) -- }) --} - --// This test demonstrates that analysis facts are correctly propagated --// across packages. --func TestInterpackageAnalysis(t *testing.T) { -- const src = ` ---- go.mod -- --module example.com ---- a/a.go -- --package a +-[`(a.Thing).Member` on pkg.go.dev](https://pkg.go.dev/godef.test/a#Thing.Member) +--- a/d.go -- +-package a //@hover("a", _, a) - --import "example.com/b" +-import "fmt" - --func _() { -- new(b.B).Printf("%d", "s") // printf error +-type Thing struct { //@loc(Thing, "Thing") +- Member string //@loc(Member, "Member") -} - ---- b/b.go -- --package b +-var Other Thing //@loc(Other, "Other") - --import "example.com/c" +-func Things(val []string) []Thing { //@loc(Things, "Things") +- return nil +-} - --type B struct{} +-func (t Thing) Method(i int) string { //@loc(Method, "Method") +- return t.Member +-} - --func (B) Printf(format string, args ...interface{}) { -- c.MyPrintf(format, args...) +-func (t Thing) Method3() { -} - ---- c/c.go -- --package c +-func (t *Thing) Method2(i int, j int) (error, string) { +- return nil, t.Member +-} - --import "fmt" +-func (t *Thing) private() { +-} - --func MyPrintf(format string, args ...interface{}) { -- fmt.Printf(format, args...) +-func useThings() { +- t := Thing{ //@hover("ing", "Thing", ing) +- Member: "string", //@hover("ember", "Member", ember), def("ember", Member) +- } +- fmt.Print(t.Member) //@hover("ember", "Member", ember), def("ember", Member) +- fmt.Print(Other) //@hover("ther", "Other", ther), def("ther", Other) +- Things(nil) //@hover("ings", "Things", ings), def("ings", Things) +- t.Method(0) //@hover("eth", "Method", eth), def("eth", Method) -} --` -- Run(t, src, func(t *testing.T, env *Env) { -- env.OpenFile("a/a.go") -- env.AfterChange( -- Diagnostics( -- env.AtRegexp("a/a.go", "new.*Printf"), -- WithMessage("format %d has arg \"s\" of wrong type string"), -- ), -- ) -- }) +- +-type NextThing struct { //@loc(NextThing, "NextThing") +- Thing +- Value int -} - --// This test ensures that only Analyzers with RunDespiteErrors=true --// are invoked on a package that would not compile, even if the errors --// are distant and localized. --func TestErrorsThatPreventAnalysis(t *testing.T) { -- const src = ` ---- go.mod -- --module example.com ---- a/a.go -- --package a +-func (n NextThing) another() string { +- return n.Member +-} - --import "fmt" --import "sync" --import _ "example.com/b" +-// Shadows Thing.Method3 +-func (n *NextThing) Method3() int { +- return n.Value +-} - --func _() { -- // The copylocks analyzer (RunDespiteErrors, FactTypes={}) does run. -- var mu sync.Mutex -- mu2 := mu // copylocks error, reported -- _ = &mu2 +-var nextThing NextThing //@hover("NextThing", "NextThing", NextThing), def("NextThing", NextThing) - -- // The printf analyzer (!RunDespiteErrors, FactTypes!={}) does not run: -- // (c, printf) failed because of type error in c -- // (b, printf) and (a, printf) do not run because of failed prerequisites. -- fmt.Printf("%d", "s") // printf error, unreported +--- @ings/hover.md -- +-```go +-func Things(val []string) []Thing +-``` - -- // The bools analyzer (!RunDespiteErrors, FactTypes={}) does not run: -- var cond bool -- _ = cond != true && cond != true // bools error, unreported +-[`a.Things` on pkg.go.dev](https://pkg.go.dev/godef.test/a#Things) +--- @ther/hover.md -- +-```go +-var Other Thing +-``` +- +-@loc(Other, "Other") +- +- +-[`a.Other` on pkg.go.dev](https://pkg.go.dev/godef.test/a#Other) +--- @a/hover.md -- +--- @ing/hover.md -- +-```go +-type Thing struct { +- Member string //@loc(Member, "Member") -} - ---- b/b.go -- --package b +-func (Thing).Method(i int) string +-func (*Thing).Method2(i int, j int) (error, string) +-func (Thing).Method3() +-func (*Thing).private() +-``` - --import _ "example.com/c" +-[`a.Thing` on pkg.go.dev](https://pkg.go.dev/godef.test/a#Thing) +--- @NextThing/hover.md -- +-```go +-type NextThing struct { +- Thing +- Value int +-} - ---- c/c.go -- --package c +-func (*NextThing).Method3() int +-func (NextThing).another() string +-``` - --var _ = 1 / "" // type error +-[`a.NextThing` on pkg.go.dev](https://pkg.go.dev/godef.test/a#NextThing) +--- @eth/hover.md -- +-```go +-func (Thing).Method(i int) string +-``` - --` -- Run(t, src, func(t *testing.T, env *Env) { -- var diags protocol.PublishDiagnosticsParams -- env.OpenFile("a/a.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("a/a.go", "mu2 := (mu)"), WithMessage("assignment copies lock value")), -- ReadDiagnostics("a/a.go", &diags)) +-[`(a.Thing).Method` on pkg.go.dev](https://pkg.go.dev/godef.test/a#Thing.Method) +--- a/f.go -- +-// Package a is a package for testing go to definition. +-package a - -- // Assert that there were no other diagnostics. -- // In particular: -- // - "fmt.Printf" does not trigger a [printf] finding; -- // - "cond != true" does not trigger a [bools] finding. -- // -- // We use this check in preference to NoDiagnosticAtRegexp -- // as it is robust in case of minor mistakes in the position -- // regexp, and because it reports unexpected diagnostics. -- if got, want := len(diags.Diagnostics), 1; got != want { -- t.Errorf("got %d diagnostics in a/a.go, want %d:", got, want) -- for i, diag := range diags.Diagnostics { -- t.Logf("Diagnostics[%d] = %+v", i, diag) +-import "fmt" +- +-func TypeStuff() { +- var x string +- +- switch y := interface{}(x).(type) { //@loc(y, "y"), hover("y", "y", y) , def("y", y) +- case int: //@loc(intY, "int") +- fmt.Printf("%v", y) //@hover("y", "y", inty), def("y", y) +- case string: //@loc(stringY, "string") +- fmt.Printf("%v", y) //@hover("y", "y", stringy), def("y", y) +- } +- +-} +--- @inty/hover.md -- +-```go +-var y int +-``` +--- @stringy/hover.md -- +-```go +-var y string +-``` +--- @y/hover.md -- +-```go +-var y interface{} +-``` +--- a/h.go -- +-package a +- +-func _() { +- type s struct { +- nested struct { +- // nested number +- number int64 //@loc(nestedNumber, "number") +- } +- nested2 []struct { +- // nested string +- str string //@loc(nestedString, "str") +- } +- x struct { +- x struct { +- x struct { +- x struct { +- x struct { +- // nested map +- m map[string]float64 //@loc(nestedMap, "m") +- } +- } +- } - } - } -- }) +- } +- +- var t s +- _ = t.nested.number //@hover("number", "number", nestedNumber), def("number", nestedNumber) +- _ = t.nested2[0].str //@hover("str", "str", nestedString), def("str", nestedString) +- _ = t.x.x.x.x.x.m //@hover("m", "m", nestedMap), def("m", nestedMap) -} -diff -urN a/gopls/internal/regtest/diagnostics/golist_test.go b/gopls/internal/regtest/diagnostics/golist_test.go ---- a/gopls/internal/regtest/diagnostics/golist_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/diagnostics/golist_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,71 +0,0 @@ --// Copyright 2023 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 +-func _() { +- var s struct { +- // a field +- a int //@loc(structA, "a") +- // b nested struct +- b struct { //@loc(structB, "b") +- // c field of nested struct +- c int //@loc(structC, "c") +- } +- } +- _ = s.a //@def("a", structA) +- _ = s.b //@def("b", structB) +- _ = s.b.c //@def("c", structC) - --import ( -- "testing" +- var arr []struct { +- // d field +- d int //@loc(arrD, "d") +- // e nested struct +- e struct { //@loc(arrE, "e") +- // f field of nested struct +- f int //@loc(arrF, "f") +- } +- } +- _ = arr[0].d //@def("d", arrD) +- _ = arr[0].e //@def("e", arrE) +- _ = arr[0].e.f //@def("f", arrF) - -- . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/internal/testenv" --) +- var complex []struct { +- c <-chan map[string][]struct { +- // h field +- h int //@loc(complexH, "h") +- // i nested struct +- i struct { //@loc(complexI, "i") +- // j field of nested struct +- j int //@loc(complexJ, "j") +- } +- } +- } +- _ = (<-complex[0].c)["0"][0].h //@def("h", complexH) +- _ = (<-complex[0].c)["0"][0].i //@def("i", complexI) +- _ = (<-complex[0].c)["0"][0].i.j //@def("j", complexJ) - --func TestGoListErrors(t *testing.T) { -- testenv.NeedsTool(t, "cgo") +- var mapWithStructKey map[struct { //@diag("struct", re"invalid map key") +- // X key field +- x []string //@loc(mapStructKeyX, "x") +- }]int +- for k := range mapWithStructKey { +- _ = k.x //@def("x", mapStructKeyX) +- } - -- const src = ` ---- go.mod -- --module a.com +- var mapWithStructKeyAndValue map[struct { +- // Y key field +- y string //@loc(mapStructKeyY, "y") +- }]struct { +- // X value field +- x string //@loc(mapStructValueX, "x") +- } +- for k, v := range mapWithStructKeyAndValue { +- // TODO: we don't show docs for y field because both map key and value +- // are structs. And in this case, we parse only map value +- _ = k.y //@hover("y", "y", hoverStructKeyY), def("y", mapStructKeyY) +- _ = v.x //@hover("x", "x", hoverStructKeyX), def("x", mapStructValueX) +- } - --go 1.18 ---- a/a.go -- --package a +- var i []map[string]interface { +- // open method comment +- open() error //@loc(openMethod, "open") +- } +- i[0]["1"].open() //@hover("pen","open", openMethod), def("open", openMethod) +-} - --import ---- c/c.go -- --package c +-func _() { +- test := struct { +- // test description +- desc string //@loc(testDescription, "desc") +- }{} +- _ = test.desc //@def("desc", testDescription) - --/* --int fortythree() { return 42; } --*/ --import "C" +- for _, tt := range []struct { +- // test input +- in map[string][]struct { //@loc(testInput, "in") +- // test key +- key string //@loc(testInputKey, "key") +- // test value +- value interface{} //@loc(testInputValue, "value") +- } +- result struct { +- v <-chan struct { +- // expected test value +- value int //@loc(testResultValue, "value") +- } +- } +- }{} { +- _ = tt.in //@def("in", testInput) +- _ = tt.in["0"][0].key //@def("key", testInputKey) +- _ = tt.in["0"][0].value //@def("value", testInputValue) - --func Foo() { -- print(C.fortytwo()) +- _ = (<-tt.result.v).value //@def("value", testResultValue) +- } -} ---- p/p.go -- --package p - --import "a.com/q" +-func _() { +- getPoints := func() []struct { +- // X coord +- x int //@loc(returnX, "x") +- // Y coord +- y int //@loc(returnY, "y") +- } { +- return nil +- } - --const P = q.Q + 1 ---- q/q.go -- --package q +- r := getPoints() +- _ = r[0].x //@def("x", returnX) +- _ = r[0].y //@def("y", returnY) +-} +--- @hoverStructKeyX/hover.md -- +-```go +-field x string +-``` - --import "a.com/p" +-X value field +--- @hoverStructKeyY/hover.md -- +-```go +-field y string +-``` - --const Q = p.P + 1 --` +-Y key field +--- @nestedNumber/hover.md -- +-```go +-field number int64 +-``` - -- Run(t, src, func(t *testing.T, env *Env) { -- env.OnceMet( -- InitialWorkspaceLoad, -- Diagnostics( -- env.AtRegexp("a/a.go", "import\n()"), -- FromSource(string(source.ParseError)), -- ), -- Diagnostics( -- AtPosition("c/c.go", 0, 0), -- FromSource(string(source.ListError)), -- WithMessage("may indicate failure to perform cgo processing"), -- ), -- Diagnostics( -- env.AtRegexp("p/p.go", `"a.com/q"`), -- FromSource(string(source.ListError)), -- WithMessage("import cycle not allowed"), -- ), -- ) -- }) --} -diff -urN a/gopls/internal/regtest/diagnostics/invalidation_test.go b/gopls/internal/regtest/diagnostics/invalidation_test.go ---- a/gopls/internal/regtest/diagnostics/invalidation_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/diagnostics/invalidation_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,111 +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. +-nested number +--- @nestedString/hover.md -- +-```go +-field str string +-``` - --package diagnostics +-nested string +--- @openMethod/hover.md -- +-```go +-func (interface).open() error +-``` +- +-open method comment +--- @nestedMap/hover.md -- +-```go +-field m map[string]float64 +-``` +- +-nested map +--- b/e.go -- +-package b - -import ( - "fmt" -- "testing" - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" +- "godef.test/a" -) - --// Test for golang/go#50267: diagnostics should be re-sent after a file is --// opened. --func TestDiagnosticsAreResentAfterCloseOrOpen(t *testing.T) { -- const files = ` ---- go.mod -- --module mod.com +-func useThings() { +- t := a.Thing{} //@loc(bStructType, "ing") +- fmt.Print(t.Member) //@loc(bMember, "ember") +- fmt.Print(a.Other) //@loc(bVar, "ther") +- a.Things(nil) //@loc(bFunc, "ings") +-} - --go 1.16 ---- main.go -- --package main +-/*@ +-def(bStructType, Thing) +-def(bMember, Member) +-def(bVar, Other) +-def(bFunc, Things) +-*/ - -func _() { -- x := 2 --} --` -- Run(t, files, func(_ *testing.T, env *Env) { // Create a new workspace-level directory and empty file. -- env.OpenFile("main.go") -- var afterOpen protocol.PublishDiagnosticsParams -- env.AfterChange( -- ReadDiagnostics("main.go", &afterOpen), -- ) -- env.CloseBuffer("main.go") -- var afterClose protocol.PublishDiagnosticsParams -- env.AfterChange( -- ReadDiagnostics("main.go", &afterClose), -- ) -- if afterOpen.Version == afterClose.Version { -- t.Errorf("publishDiagnostics: got the same version after closing (%d) as after opening", afterOpen.Version) -- } -- env.OpenFile("main.go") -- var afterReopen protocol.PublishDiagnosticsParams -- env.AfterChange( -- ReadDiagnostics("main.go", &afterReopen), -- ) -- if afterReopen.Version == afterClose.Version { -- t.Errorf("pubslishDiagnostics: got the same version after reopening (%d) as after closing", afterClose.Version) -- } -- }) +- var x interface{} +- switch x := x.(type) { //@hover("x", "x", xInterface) +- case string: //@loc(eString, "string") +- fmt.Println(x) //@hover("x", "x", xString) +- case int: //@loc(eInt, "int") +- fmt.Println(x) //@hover("x", "x", xInt) +- } -} +--- @xInt/hover.md -- +-```go +-var x int +-``` +--- @xInterface/hover.md -- +-```go +-var x interface{} +-``` +--- @xString/hover.md -- +-```go +-var x string +-``` +--- broken/unclosedIf.go -- +-package broken - --// Test for the "chattyDiagnostics" setting: we should get re-published --// diagnostics after every file change, even if diagnostics did not change. --func TestChattyDiagnostics(t *testing.T) { -- const files = ` ---- go.mod -- --module mod.com -- --go 1.16 ---- main.go -- --package main +-import "fmt" - --func _() { -- x := 2 +-func unclosedIf() { +- if false { +- var myUnclosedIf string //@loc(myUnclosedIf, "myUnclosedIf") +- fmt.Printf("s = %v\n", myUnclosedIf) //@def("my", myUnclosedIf) -} - --// Irrelevant comment #0 --` +-func _() {} //@diag("_", re"expected") +diff -urN a/gopls/internal/regtest/marker/testdata/hover/goprivate.txt b/gopls/internal/regtest/marker/testdata/hover/goprivate.txt +--- a/gopls/internal/regtest/marker/testdata/hover/goprivate.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/hover/goprivate.txt 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ +-This test checks that links in hover obey GOPRIVATE. +--- env -- +-GOPRIVATE=mod.com +--- go.mod -- +-module mod.com +--- p.go -- +-package p - -- WithOptions( -- Settings{ -- "chattyDiagnostics": true, -- }, -- ).Run(t, files, func(_ *testing.T, env *Env) { // Create a new workspace-level directory and empty file. +-// T should not be linked, as it is private. +-type T struct{} //@hover("T", "T", T) +--- lib/lib.go -- +-package lib - -- env.OpenFile("main.go") -- var d protocol.PublishDiagnosticsParams -- env.AfterChange( -- ReadDiagnostics("main.go", &d), -- ) +-// GOPRIVATE should also match nested packages. +-type L struct{} //@hover("L", "L", L) +--- @L/hover.md -- +-```go +-type L struct{} +-``` - -- if len(d.Diagnostics) != 1 { -- t.Fatalf("len(Diagnostics) = %d, want 1", len(d.Diagnostics)) -- } -- msg := d.Diagnostics[0].Message +-GOPRIVATE should also match nested packages. +--- @T/hover.md -- +-```go +-type T struct{} +-``` - -- for i := 0; i < 5; i++ { -- before := d.Version -- env.RegexpReplace("main.go", "Irrelevant comment #.", fmt.Sprintf("Irrelevant comment #%d", i)) -- env.AfterChange( -- ReadDiagnostics("main.go", &d), -- ) +-T should not be linked, as it is private. +diff -urN a/gopls/internal/regtest/marker/testdata/hover/hover.txt b/gopls/internal/regtest/marker/testdata/hover/hover.txt +--- a/gopls/internal/regtest/marker/testdata/hover/hover.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/hover/hover.txt 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +-This test demonstrates some features of the new marker test runner. +--- a.go -- +-package a - -- if d.Version == before { -- t.Errorf("after change, got version %d, want new version", d.Version) -- } +-const abc = 0x2a //@hover("b", "abc", abc),hover(" =", "abc", abc) +--- typeswitch.go -- +-package a - -- // As a sanity check, make sure we have the same diagnostic. -- if len(d.Diagnostics) != 1 { -- t.Fatalf("len(Diagnostics) = %d, want 1", len(d.Diagnostics)) -- } -- newMsg := d.Diagnostics[0].Message -- if newMsg != msg { -- t.Errorf("after change, got message %q, want %q", newMsg, msg) -- } -- } -- }) +-func _() { +- var y interface{} +- switch x := y.(type) { //@hover("x", "x", x) +- case int: +- println(x) //@hover("x", "x", xint),hover(")", "x", xint) +- } -} -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 00:00:00.000000000 +0000 -@@ -1,73 +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" +--- @abc/hover.md -- +-```go +-const abc untyped int = 0x2a // 42 +-``` - -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" --) +-@hover("b", "abc", abc),hover(" =", "abc", abc) +--- @x/hover.md -- +-```go +-var x interface{} +-``` +--- @xint/hover.md -- +-```go +-var x int +-``` +diff -urN a/gopls/internal/regtest/marker/testdata/hover/linkable.txt b/gopls/internal/regtest/marker/testdata/hover/linkable.txt +--- a/gopls/internal/regtest/marker/testdata/hover/linkable.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/hover/linkable.txt 1970-01-01 08:00:00 +@@ -1,120 +0,0 @@ +-This test checks that we correctly determine pkgsite links for various +-identifiers. - --func TestUndeclaredDiagnostics(t *testing.T) { -- src := ` +-We should only produce links that work, meaning the object is reachable via the +-package's public API. --- go.mod -- -module mod.com - --go 1.12 ---- a/a.go -- --package a +-go 1.18 +--- p.go -- +-package p - --func _() int { -- return x +-type E struct { +- Embed int -} ---- 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 -- } +-// T is in the package scope, and so should be linkable. +-type T struct{ //@hover("T", "T", T) +- // Only exported fields should be linkable - -- // 'x' is undeclared, but still necessary. -- env.OpenFile("a/a.go") -- var adiags protocol.PublishDiagnosticsParams -- env.AfterChange( -- Diagnostics(env.AtRegexp("a/a.go", "x")), -- ReadDiagnostics("a/a.go", &adiags), -- ) -- if got := len(adiags.Diagnostics); got != 1 { -- t.Errorf("len(Diagnostics) = %d, want 1", got) -- } -- if diag := adiags.Diagnostics[0]; isUnnecessary(diag) { -- t.Errorf("%v tagged unnecessary, want necessary", diag) -- } +- f int //@hover("f", "f", f) +- F int //@hover("F", "F", F) - -- // 'y = y' is pointless, and should be detected as unnecessary. -- env.OpenFile("b/b.go") -- var bdiags protocol.PublishDiagnosticsParams -- env.AfterChange( -- Diagnostics(env.AtRegexp("b/b.go", "y = y")), -- ReadDiagnostics("b/b.go", &bdiags), -- ) -- if got := len(bdiags.Diagnostics); got != 1 { -- t.Errorf("len(Diagnostics) = %d, want 1", got) -- } -- if diag := bdiags.Diagnostics[0]; !isUnnecessary(diag) { -- t.Errorf("%v tagged necessary, want unnecessary", diag) -- } -- }) --} -diff -urN a/gopls/internal/regtest/inlayhints/inlayhints_test.go b/gopls/internal/regtest/inlayhints/inlayhints_test.go ---- a/gopls/internal/regtest/inlayhints/inlayhints_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/inlayhints/inlayhints_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,69 +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 inlayhint +- E - --import ( -- "testing" +- // TODO(rfindley): is the link here correct? It ignores N. +- N struct { +- // Nested fields should also be linkable. +- Nested int //@hover("Nested", "Nested", Nested) +- } +-} +-// M is an exported method, and so should be linkable. +-func (T) M() {} - -- "golang.org/x/tools/gopls/internal/hooks" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/tools/internal/bug" --) +-// m is not exported, and so should not be linkable. +-func (T) m() {} - --func TestMain(m *testing.M) { -- bug.PanicOnBugs = true -- Main(m, hooks.Options) --} +-func _() { +- var t T - --func TestEnablingInlayHints(t *testing.T) { -- const workspace = ` ---- go.mod -- --module inlayHint.test --go 1.12 ---- lib.go -- --package lib --type Number int --const ( -- Zero Number = iota -- One -- Two --) --` -- tests := []struct { -- label string -- enabled map[string]bool -- wantInlayHint bool -- }{ -- { -- label: "default", -- wantInlayHint: false, -- }, -- { -- label: "enable const", -- enabled: map[string]bool{source.ConstantValues: true}, -- wantInlayHint: true, -- }, -- { -- label: "enable parameter names", -- enabled: map[string]bool{source.ParameterNames: true}, -- wantInlayHint: false, -- }, -- } -- for _, test := range tests { -- t.Run(test.label, func(t *testing.T) { -- WithOptions( -- Settings{ -- "hints": test.enabled, -- }, -- ).Run(t, workspace, func(t *testing.T, env *Env) { -- env.OpenFile("lib.go") -- lens := env.InlayHints("lib.go") -- if gotInlayHint := len(lens) > 0; gotInlayHint != test.wantInlayHint { -- t.Errorf("got inlayHint: %t, want %t", gotInlayHint, test.wantInlayHint) -- } -- }) -- }) -- } --} -diff -urN a/gopls/internal/regtest/marker/marker_test.go b/gopls/internal/regtest/marker/marker_test.go ---- a/gopls/internal/regtest/marker/marker_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/marker_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,21 +0,0 @@ --// Copyright 2023 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. +- // Embedded fields should be linkable. +- _ = t.Embed //@hover("Embed", "Embed", Embed) - --package marker +- // Local variables should not be linkable, even if they are capitalized. +- var X int //@hover("X", "X", X) +- _ = X - --import ( -- "testing" +- // Local types should not be linkable, even if they are capitalized. +- type Local struct { //@hover("Local", "Local", Local) +- E +- } - -- . "golang.org/x/tools/gopls/internal/lsp/regtest" --) +- // But the embedded field should still be linkable. +- var l Local +- _ = l.Embed //@hover("Embed", "Embed", Embed) +-} +--- @Embed/hover.md -- +-```go +-field Embed int +-``` - --// Note: we use a separate package for the marker tests so that we can easily --// compare their performance to the existing marker tests in ./internal/lsp. +-[`(p.E).Embed` on pkg.go.dev](https://pkg.go.dev/mod.com#E.Embed) +--- @F/hover.md -- +-```go +-field F int +-``` - --// TestMarkers runs the marker tests from the testdata directory. --// --// See RunMarkerTests for details on how marker tests work. --func TestMarkers(t *testing.T) { -- RunMarkerTests(t, "testdata") --} -diff -urN a/gopls/internal/regtest/marker/testdata/definition/embed.txt b/gopls/internal/regtest/marker/testdata/definition/embed.txt ---- a/gopls/internal/regtest/marker/testdata/definition/embed.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/definition/embed.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,254 +0,0 @@ --This test checks definition and hover operations over embedded fields and methods. +-@hover("F", "F", F) - ---- go.mod -- --module mod.com - --go 1.18 +-[`(p.T).F` on pkg.go.dev](https://pkg.go.dev/mod.com#T.F) +--- @Local/hover.md -- +-```go +-type Local struct { +- E +-} +-``` - ---- a/a.go -- --package a +-Local types should not be linkable, even if they are capitalized. +--- @Nested/hover.md -- +-```go +-field Nested int +-``` - --type A string //@loc(AString, "A") +-Nested fields should also be linkable. +--- @T/hover.md -- +-```go +-type T struct { +- f int //@hover("f", "f", f) +- F int //@hover("F", "F", F) - --func (_ A) Hi() {} //@loc(AHi, "Hi") +- E - --type S struct { -- Field int //@loc(SField, "Field") -- R // embed a struct -- H // embed an interface +- // TODO(rfindley): is the link here correct? It ignores N. +- N struct { +- // Nested fields should also be linkable. +- Nested int //@hover("Nested", "Nested", Nested) +- } -} - --type R struct { -- Field2 int //@loc(RField2, "Field2") --} +-func (T).M() +-func (T).m() +-``` - --func (_ R) Hey() {} //@loc(RHey, "Hey") +-T is in the package scope, and so should be linkable. - --type H interface { //@loc(H, "H") -- Goodbye() //@loc(HGoodbye, "Goodbye") --} - --type I interface { //@loc(I, "I") -- B() //@loc(IB, "B") -- J --} +-[`p.T` on pkg.go.dev](https://pkg.go.dev/mod.com#T) +--- @X/hover.md -- +-```go +-var X int +-``` - --type J interface { //@loc(J, "J") -- Hello() //@loc(JHello, "Hello") --} +-Local variables should not be linkable, even if they are capitalized. +--- @f/hover.md -- +-```go +-field f int +-``` - ---- b/b.go -- --package b +-@hover("f", "f", f) +diff -urN a/gopls/internal/regtest/marker/testdata/hover/linkable_generics.txt b/gopls/internal/regtest/marker/testdata/hover/linkable_generics.txt +--- a/gopls/internal/regtest/marker/testdata/hover/linkable_generics.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/hover/linkable_generics.txt 1970-01-01 08:00:00 +@@ -1,145 +0,0 @@ +-This file contains tests for documentation links to generic code in hover. - --import "mod.com/a" //@loc(AImport, re"\".*\"") +--- flags -- +--min_go=go1.18 - --type embed struct { -- F int //@loc(F, "F") --} +--- go.mod -- +-module mod.com - --func (embed) M() //@loc(M, "M") +-go 1.19 - --type Embed struct { -- embed -- *a.A -- a.I -- a.S --} +--- a.go -- +-package a +- +-import "mod.com/generic" - -func _() { -- e := Embed{} -- e.Hi() //@def("Hi", AHi),hover("Hi", "Hi", AHi) -- e.B() //@def("B", IB),hover("B", "B", IB) -- _ = e.Field //@def("Field", SField),hover("Field", "Field", SField) -- _ = e.Field2 //@def("Field2", RField2),hover("Field2", "Field2", RField2) -- e.Hello() //@def("Hello", JHello),hover("Hello", "Hello",JHello) -- e.Hey() //@def("Hey", RHey),hover("Hey", "Hey", RHey) -- e.Goodbye() //@def("Goodbye", HGoodbye),hover("Goodbye", "Goodbye", HGoodbye) -- e.M() //@def("M", M),hover("M", "M", M) -- _ = e.F //@def("F", F),hover("F", "F", F) --} +- // Hovering over instantiated object should produce accurate type +- // information, but link to the generic declarations. - --type aAlias = a.A //@loc(aAlias, "aAlias") +- var x generic.GT[int] //@hover("GT", "GT", xGT) +- _ = x.F //@hover("x", "x", x),hover("F", "F", xF) - --type S1 struct { //@loc(S1, "S1") -- F1 int //@loc(S1F1, "F1") -- S2 //@loc(S1S2, "S2"),def("S2", S2),hover("S2", "S2", S2) -- a.A //@def("A", AString),hover("A", "A", aA) -- aAlias //@def("a", aAlias),hover("a", "aAlias", aAlias) +- f := generic.GF[int] //@hover("GF", "GF", fGF) +- _ = f //@hover("f", "f", f) -} - --type S2 struct { //@loc(S2, "S2") -- F1 string //@loc(S2F1, "F1") -- F2 int //@loc(S2F2, "F2") -- *a.A //@def("A", AString),def("a",AImport) --} +--- generic/generic.go -- +-package generic - --type S3 struct { -- F1 struct { -- a.A //@def("A", AString) -- } +-// Hovering over type parameters should link to documentation. +-// +-// TODO(rfindley): should it? We should probably link to the type. +-type GT[P any] struct{ //@hover("GT", "GT", GT),hover("P", "P", GTP) +- F P //@hover("F", "F", F),hover("P", "P", FP) -} - --func Bar() { -- var x S1 //@def("S1", S1),hover("S1", "S1", S1) -- _ = x.S2 //@def("S2", S1S2),hover("S2", "S2", S1S2) -- _ = x.F1 //@def("F1", S1F1),hover("F1", "F1", S1F1) -- _ = x.F2 //@def("F2", S2F2),hover("F2", "F2", S2F2) -- _ = x.S2.F1 //@def("F1", S2F1),hover("F1", "F1", S2F1) +-func (GT[P]) M(p P) { //@hover("GT", "GT", GTrecv),hover("M","M", M),hover(re"p (P)", re"p (P)", pP) -} - ---- b/c.go -- --package b -- --var _ = S1{ //@def("S1", S1),hover("S1", "S1", S1) -- F1: 99, //@def("F1", S1F1),hover("F1", "F1", S1F1) +-func GF[P any] (p P) { //@hover("GF", "GF", GF) -} - ---- @AHi/hover.md -- --```go --func (a.A).Hi() --``` -- --[`(a.A).Hi` on pkg.go.dev](https://pkg.go.dev/mod.com/a#A.Hi) --- @F/hover.md -- -```go --field F int +-field F P -``` - --@loc(F, "F") +-@hover("F", "F", F),hover("P", "P", FP) - - --[`(b.Embed).F` on pkg.go.dev](https://pkg.go.dev/mod.com/b#Embed.F) ---- @HGoodbye/hover.md -- +-[`(generic.GT).F` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT.F) +--- @FP/hover.md -- -```go --func (a.H).Goodbye() +-type parameter P any +-``` +--- @GF/hover.md -- +-```go +-func GF[P any](p P) -``` - --@loc(HGoodbye, "Goodbye") -- -- --[`(a.H).Goodbye` on pkg.go.dev](https://pkg.go.dev/mod.com/a#H.Goodbye) ---- @IB/hover.md -- +-[`generic.GF` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GF) +--- @GT/hover.md -- -```go --func (a.I).B() +-type GT[P any] struct { +- F P //@hover("F", "F", F),hover("P", "P", FP) +-} +- +-func (GT[P]).M(p P) -``` - --@loc(IB, "B") +-Hovering over type parameters should link to documentation. +- +-TODO(rfindley): should it? We should probably link to the type. - - --[`(a.I).B` on pkg.go.dev](https://pkg.go.dev/mod.com/a#I.B) ---- @JHello/hover.md -- +-[`generic.GT` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT) +--- @GTP/hover.md -- -```go --func (a.J).Hello() +-type parameter P any +-``` +--- @GTrecv/hover.md -- +-```go +-type GT[P any] struct { +- F P //@hover("F", "F", F),hover("P", "P", FP) +-} +- +-func (GT[P]).M(p P) -``` - --@loc(JHello, "Hello") +-Hovering over type parameters should link to documentation. +- +-TODO(rfindley): should it? We should probably link to the type. - - --[`(a.J).Hello` on pkg.go.dev](https://pkg.go.dev/mod.com/a#J.Hello) +-[`generic.GT` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT) --- @M/hover.md -- -```go --func (embed).M() +-func (GT[P]).M(p P) -``` - --[`(b.Embed).M` on pkg.go.dev](https://pkg.go.dev/mod.com/b#Embed.M) ---- @RField2/hover.md -- +-[`(generic.GT).M` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT.M) +--- @f/hover.md -- -```go --field Field2 int +-var f func(p int) -``` -- --@loc(RField2, "Field2") -- -- --[`(a.R).Field2` on pkg.go.dev](https://pkg.go.dev/mod.com/a#R.Field2) ---- @RHey/hover.md -- +--- @fGF/hover.md -- -```go --func (a.R).Hey() +-func generic.GF(p int) // func[P any](p P) -``` - --[`(a.R).Hey` on pkg.go.dev](https://pkg.go.dev/mod.com/a#R.Hey) ---- @S1/hover.md -- +-[`generic.GF` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GF) +--- @pP/hover.md -- -```go --type S1 struct { -- F1 int //@loc(S1F1, "F1") -- S2 //@loc(S1S2, "S2"),def("S2", S2),hover("S2", "S2", S2) -- a.A //@def("A", AString),hover("A", "A", aA) -- aAlias //@def("a", aAlias),hover("a", "aAlias", aAlias) --} +-type parameter P any +-``` +--- @x/hover.md -- +-```go +-var x generic.GT[int] -``` - --[`b.S1` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S1) ---- @S1F1/hover.md -- +-@hover("GT", "GT", xGT) +--- @xF/hover.md -- -```go --field F1 int +-field F int -``` - --@loc(S1F1, "F1") +-@hover("F", "F", F),hover("P", "P", FP) - - --[`(b.S1).F1` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S1.F1) ---- @S1S2/hover.md -- +-[`(generic.GT).F` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT.F) +--- @xGT/hover.md -- -```go --field S2 S2 +-type GT[P any] struct { +- F P //@hover("F", "F", F),hover("P", "P", FP) +-} +- +-func (generic.GT[P]).M(p P) -``` - --@loc(S1S2, "S2"),def("S2", S2),hover("S2", "S2", S2) +-Hovering over type parameters should link to documentation. - +-TODO(rfindley): should it? We should probably link to the type. - --[`(b.S1).S2` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S1.S2) ---- @S2/hover.md -- --```go --type S2 struct { -- F1 string //@loc(S2F1, "F1") -- F2 int //@loc(S2F2, "F2") -- *a.A //@def("A", AString),def("a",AImport) +- +-[`generic.GT` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT) +diff -urN a/gopls/internal/regtest/marker/testdata/hover/linkname.txt b/gopls/internal/regtest/marker/testdata/hover/linkname.txt +--- a/gopls/internal/regtest/marker/testdata/hover/linkname.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/hover/linkname.txt 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +-This test check hover on the 2nd argument in go:linkname directives. +--- go.mod -- +-module mod.com +- +--- upper/upper.go -- +-package upper +- +-import ( +- _ "unsafe" +- _ "mod.com/lower" +-) +- +-//go:linkname foo mod.com/lower.bar //@hover("mod.com/lower.bar", "mod.com/lower.bar", bar) +-func foo() string +- +--- lower/lower.go -- +-package lower +- +-// bar does foo. +-func bar() string { +- return "foo by bar" -} --``` - --[`b.S2` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S2) ---- @S2F1/hover.md -- +--- @bar/hover.md -- -```go --field F1 string +-func bar() string -``` - --@loc(S2F1, "F1") +-bar does foo. +diff -urN a/gopls/internal/regtest/marker/testdata/hover/std.txt b/gopls/internal/regtest/marker/testdata/hover/std.txt +--- a/gopls/internal/regtest/marker/testdata/hover/std.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/hover/std.txt 1970-01-01 08:00:00 +@@ -1,80 +0,0 @@ +-This test checks hover results for built-in or standard library symbols. - +-It uses synopsis documentation as full documentation for some of these +-built-ins varies across Go versions, where as it just so happens that the +-synopsis does not. - --[`(b.S2).F1` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S2.F1) ---- @S2F2/hover.md -- +-In the future we may need to limit this test to the latest Go version to avoid +-documentation churn. +--- settings.json -- +-{ +- "hoverKind": "SynopsisDocumentation" +-} +--- go.mod -- +-module mod.com +- +-go 1.18 +--- std.go -- +-package std +- +-import ( +- "fmt" +- "go/types" +- "sync" +-) +- +-func _() { +- var err error //@loc(err, "err") +- fmt.Printf("%v", err) //@def("err", err) +- +- var _ string //@hover("string", "string", hoverstring) +- _ = make([]int, 0) //@hover("make", "make", hovermake) +- +- var mu sync.Mutex +- mu.Lock() //@hover("Lock", "Lock", hoverLock) +- +- var typ *types.Named //@hover("types", "types", hoverTypes) +- typ.Obj().Name() //@hover("Name", "Name", hoverName) +-} +--- @hoverLock/hover.md -- -```go --field F2 int +-func (*sync.Mutex).Lock() -``` - --@loc(S2F2, "F2") +-Lock locks m. - - --[`(b.S2).F2` on pkg.go.dev](https://pkg.go.dev/mod.com/b#S2.F2) ---- @SField/hover.md -- +-[`(sync.Mutex).Lock` on pkg.go.dev](https://pkg.go.dev/sync#Mutex.Lock) +--- @hoverName/hover.md -- -```go --field Field int +-func (*types.object).Name() string -``` - --@loc(SField, "Field") +-Name returns the object's (package-local, unqualified) name. - - --[`(a.S).Field` on pkg.go.dev](https://pkg.go.dev/mod.com/a#S.Field) ---- @aA/hover.md -- +-[`(types.TypeName).Name` on pkg.go.dev](https://pkg.go.dev/go/types#TypeName.Name) +--- @hoverTypes/hover.md -- -```go --type A string +-package types ("go/types") +-``` - --func (a.A).Hi() +-[`types` on pkg.go.dev](https://pkg.go.dev/go/types) +--- @hovermake/hover.md -- +-```go +-func make(t Type, size ...int) Type -``` - --@loc(AString, "A") +-The make built-in function allocates and initializes an object of type slice, map, or chan (only). - - --[`a.A` on pkg.go.dev](https://pkg.go.dev/mod.com/a#A) ---- @aAlias/hover.md -- +-[`make` on pkg.go.dev](https://pkg.go.dev/builtin#make) +--- @hoverstring/hover.md -- -```go --type aAlias = a.A -- --func (a.A).Hi() +-type string string -``` - --@loc(aAlias, "aAlias") -diff -urN a/gopls/internal/regtest/marker/testdata/definition/import.txt b/gopls/internal/regtest/marker/testdata/definition/import.txt ---- a/gopls/internal/regtest/marker/testdata/definition/import.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/definition/import.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,52 +0,0 @@ --This test checks definition and hover over imports. +-string is the set of all strings of 8-bit bytes, conventionally but not necessarily representing UTF-8-encoded text. +- +- +-[`string` on pkg.go.dev](https://pkg.go.dev/builtin#string) +diff -urN a/gopls/internal/regtest/marker/testdata/implementation/basic.txt b/gopls/internal/regtest/marker/testdata/implementation/basic.txt +--- a/gopls/internal/regtest/marker/testdata/implementation/basic.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/implementation/basic.txt 1970-01-01 08:00:00 +@@ -1,73 +0,0 @@ +-Basic test of implementation query. +- --- go.mod -- --module mod.com +-module example.com +-go 1.12 - --go 1.18 ---- foo/foo.go -- --package foo +--- implementation/implementation.go -- +-package implementation - --type Foo struct{} +-import "example.com/other" - --// DoFoo does foo. --func DoFoo() {} //@loc(DoFoo, "DoFoo") ---- bar/bar.go -- --package bar +-type ImpP struct{} //@loc(ImpP, "ImpP"),implementation("ImpP", Laugher, OtherLaugher) - --import ( -- myFoo "mod.com/foo" //@loc(myFoo, "myFoo") --) +-func (*ImpP) Laugh() { //@loc(LaughP, "Laugh"),implementation("Laugh", Laugh, OtherLaugh) +-} - --var _ *myFoo.Foo //@def("myFoo", myFoo),hover("myFoo", "myFoo", myFoo) ---- bar/dotimport.go -- --package bar +-type ImpS struct{} //@loc(ImpS, "ImpS"),implementation("ImpS", Laugher, OtherLaugher) - --import . "mod.com/foo" +-func (ImpS) Laugh() { //@loc(LaughS, "Laugh"),implementation("Laugh", Laugh, OtherLaugh) +-} - --func _() { -- // variable of type foo.Foo -- var _ Foo //@hover("_", "_", FooVar) +-type Laugher interface { //@loc(Laugher, "Laugher"),implementation("Laugher", ImpP, OtherImpP, ImpS, OtherImpS, embedsImpP) +- Laugh() //@loc(Laugh, "Laugh"),implementation("Laugh", LaughP, OtherLaughP, LaughS, OtherLaughS) +-} - -- DoFoo() //@hover("DoFoo", "DoFoo", DoFoo) +-type Foo struct { //@implementation("Foo", Joker) +- other.Foo -} ---- @DoFoo/hover.md -- --```go --func DoFoo() --``` - --DoFoo does foo. +-type Joker interface { //@loc(Joker, "Joker") +- Joke() //@loc(Joke, "Joke"),implementation("Joke", ImpJoker) +-} - +-type cryer int //@implementation("cryer", Cryer) - --[`foo.DoFoo` on pkg.go.dev](https://pkg.go.dev/mod.com/foo#DoFoo) ---- @FooVar/hover.md -- --```go --var _ Foo --``` +-func (cryer) Cry(other.CryType) {} //@loc(CryImpl, "Cry"),implementation("Cry", Cry) - --variable of type foo.Foo ---- @myFoo/hover.md -- --```go --package myFoo ("mod.com/foo") --``` +-type Empty interface{} //@implementation("Empty") - --[`myFoo` on pkg.go.dev](https://pkg.go.dev/mod.com/foo) -diff -urN a/gopls/internal/regtest/marker/testdata/definition/misc.txt b/gopls/internal/regtest/marker/testdata/definition/misc.txt ---- a/gopls/internal/regtest/marker/testdata/definition/misc.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/definition/misc.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,230 +0,0 @@ --This test exercises miscellaneous definition and hover requests. ---- go.mod -- --module mod.com +-var _ interface{ Joke() } //@implementation("Joke", ImpJoker) - --go 1.16 ---- a.go -- --package a //@loc(aPackage, re"package (a)"),hover(aPackage, aPackage, aPackage) +-type embedsImpP struct { //@loc(embedsImpP, "embedsImpP") +- ImpP //@implementation("ImpP", Laugher, OtherLaugher) +-} - --var ( -- // x is a variable. -- x string //@loc(x, "x"),hover(x, x, hoverx) --) +--- other/other.go -- +-package other - --// Constant block. When I hover on h, I should see this comment. --const ( -- // When I hover on g, I should see this comment. -- g = 1 //@hover("g", "g", hoverg) +-type ImpP struct{} //@loc(OtherImpP, "ImpP") - -- h = 2 //@hover("h", "h", hoverh) --) +-func (*ImpP) Laugh() { //@loc(OtherLaughP, "Laugh") +-} - --// z is a variable too. --var z string //@loc(z, "z"),hover(z, z, hoverz) +-type ImpS struct{} //@loc(OtherImpS, "ImpS") - --func AStuff() { //@loc(AStuff, "AStuff") -- x := 5 -- Random2(x) //@def("dom2", Random2) -- Random() //@def("()", Random) +-func (ImpS) Laugh() { //@loc(OtherLaughS, "Laugh") -} - --type H interface { //@loc(H, "H") -- Goodbye() +-type ImpI interface { //@loc(OtherLaugher, "ImpI") +- Laugh() //@loc(OtherLaugh, "Laugh") -} - --type I interface { //@loc(I, "I") -- B() -- J +-type Foo struct { //@implementation("Foo", Joker) -} - --type J interface { //@loc(J, "J") -- Hello() +-func (Foo) Joke() { //@loc(ImpJoker, "Joke"),implementation("Joke", Joke) -} - --func _() { -- // 1st type declaration block -- type ( -- a struct { //@hover("a", "a", hoverDeclBlocka) -- x string -- } -- ) +-type CryType int - -- // 2nd type declaration block -- type ( -- // b has a comment -- b struct{} //@hover("b", "b", hoverDeclBlockb) -- ) +-type Cryer interface { //@loc(Cryer, "Cryer") +- Cry(CryType) //@loc(Cry, "Cry"),implementation("Cry", CryImpl) +-} +diff -urN a/gopls/internal/regtest/marker/testdata/implementation/generics.txt b/gopls/internal/regtest/marker/testdata/implementation/generics.txt +--- a/gopls/internal/regtest/marker/testdata/implementation/generics.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/implementation/generics.txt 1970-01-01 08:00:00 +@@ -1,34 +0,0 @@ +-Test of 'implementation' query on generic types. - -- // 3rd type declaration block -- type ( -- // c is a struct -- c struct { //@hover("c", "c", hoverDeclBlockc) -- f string -- } +--- flags -- +--min_go=go1.18 - -- d string //@hover("d", "d", hoverDeclBlockd) -- ) +--- go.mod -- +-module example.com +-go 1.18 - -- type ( -- e struct { //@hover("e", "e", hoverDeclBlocke) -- f float64 -- } // e has a comment -- ) +--- implementation/implementation.go -- +-package implementation +- +-type GenIface[T any] interface { //@loc(GenIface, "GenIface"),implementation("GenIface", GC) +- F(int, string, T) //@loc(GenIfaceF, "F"),implementation("F", GCF) +-} +- +-type GenConc[U any] int //@loc(GenConc, "GenConc"),implementation("GenConc", GI) +- +-func (GenConc[V]) F(int, string, V) {} //@loc(GenConcF, "F"),implementation("F", GIF) +- +-type GenConcString struct{ GenConc[string] } //@loc(GenConcString, "GenConcString"),implementation(GenConcString, GIString) +- +--- other/other.go -- +-package other +- +-type GI[T any] interface { //@loc(GI, "GI"),implementation("GI", GenConc) +- F(int, string, T) //@loc(GIF, "F"),implementation("F", GenConcF) -} - --var ( -- hh H //@hover("H", "H", hoverH) -- ii I //@hover("I", "I", hoverI) -- jj J //@hover("J", "J", hoverJ) --) ---- a_test.go -- --package a +-type GIString GI[string] //@loc(GIString, "GIString"),implementation("GIString", GenConcString) - --import ( -- "testing" --) +-type GC[U any] int //@loc(GC, "GC"),implementation("GC", GenIface) - --func TestA(t *testing.T) { //@hover("TestA", "TestA", hoverTestA) --} ---- random.go -- --package a +-func (GC[V]) F(int, string, V) {} //@loc(GCF, "F"),implementation("F", GenIfaceF) +diff -urN a/gopls/internal/regtest/marker/testdata/implementation/issue43655.txt b/gopls/internal/regtest/marker/testdata/implementation/issue43655.txt +--- a/gopls/internal/regtest/marker/testdata/implementation/issue43655.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/implementation/issue43655.txt 1970-01-01 08:00:00 +@@ -1,22 +0,0 @@ +-This test verifies that we fine implementations of the built-in error interface. - --func Random() int { //@loc(Random, "Random") -- y := 6 + 7 -- return y --} +--- go.mod -- +-module example.com +-go 1.12 - --func Random2(y int) int { //@loc(Random2, "Random2"),loc(RandomParamY, "y") -- return y //@def("y", RandomParamY),hover("y", "y", hovery) --} +--- p.go -- +-package p - --type Pos struct { -- x, y int //@loc(PosX, "x"),loc(PosY, "y") --} +-type errA struct{ error } //@loc(errA, "errA") - --// Typ has a comment. Its fields do not. --type Typ struct{ field string } //@loc(TypField, "field") +-type errB struct{} //@loc(errB, "errB") +-func (errB) Error() string{ return "" } //@loc(errBError, "Error") +- +-type notAnError struct{} +-func (notAnError) Error() int { return 0 } - -func _() { -- x := &Typ{} -- _ = x.field //@def("field", TypField),hover("field", "field", hoverfield) +- var _ error //@implementation("error", errA, errB) +- var a errA +- _ = a.Error //@implementation("Error", errBError) -} +diff -urN a/gopls/internal/regtest/marker/testdata/quickfix/undeclared.txt b/gopls/internal/regtest/marker/testdata/quickfix/undeclared.txt +--- a/gopls/internal/regtest/marker/testdata/quickfix/undeclared.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/quickfix/undeclared.txt 1970-01-01 08:00:00 +@@ -1,62 +0,0 @@ +-Tests of suggested fixes for "undeclared name" diagnostics, +-which are of ("compiler", "error") type. - --func (p *Pos) Sum() int { //@loc(PosSum, "Sum") -- return p.x + p.y //@hover("x", "x", hoverpx) --} +--- go.mod -- +-module example.com +-go 1.12 - --func _() { -- var p Pos -- _ = p.Sum() //@def("()", PosSum),hover("()", `Sum`, hoverSum) --} ---- @aPackage/hover.md -- ---- @hoverDeclBlocka/hover.md -- --```go --type a struct { -- x string +--- a.go -- +-package p +- +-func a() { +- z, _ := 1+y, 11 //@suggestedfix("y", re"(undeclared name|undefined): y", "quickfix", a) +- _ = z -} --``` - --1st type declaration block ---- @hoverDeclBlockb/hover.md -- --```go --type b struct{} --``` +--- @a/a.go -- +-package p - --b has a comment ---- @hoverDeclBlockc/hover.md -- --```go --type c struct { -- f string +-func a() { +- y := +- z, _ := 1+y, 11 //@suggestedfix("y", re"(undeclared name|undefined): y", "quickfix", a) +- _ = z -} --``` - --c is a struct ---- @hoverDeclBlockd/hover.md -- --```go --type d string --``` +--- b.go -- +-package p - --3rd type declaration block ---- @hoverDeclBlocke/hover.md -- --```go --type e struct { -- f float64 +-func b() { +- if 100 < 90 { +- } else if 100 > n+2 { //@suggestedfix("n", re"(undeclared name|undefined): n", "quickfix", b) +- } -} --``` - --e has a comment ---- @hoverH/hover.md -- --```go --type H interface { -- Goodbye() +--- @b/b.go -- +-package p +- +-func b() { +- n := +- if 100 < 90 { +- } else if 100 > n+2 { //@suggestedfix("n", re"(undeclared name|undefined): n", "quickfix", b) +- } -} --``` - --[`a.H` on pkg.go.dev](https://pkg.go.dev/mod.com#H) ---- @hoverI/hover.md -- --```go --type I interface { -- B() -- J +--- c.go -- +-package p +- +-func c() { +- for i < 200 { //@suggestedfix("i", re"(undeclared name|undefined): i", "quickfix", c) +- } +- r() //@diag("r", re"(undeclared name|undefined): r") -} --``` - --[`a.I` on pkg.go.dev](https://pkg.go.dev/mod.com#I) ---- @hoverJ/hover.md -- --```go --type J interface { -- Hello() +--- @c/c.go -- +-package p +- +-func c() { +- i := +- for i < 200 { //@suggestedfix("i", re"(undeclared name|undefined): i", "quickfix", c) +- } +- r() //@diag("r", re"(undeclared name|undefined): r") -} --``` - --[`a.J` on pkg.go.dev](https://pkg.go.dev/mod.com#J) ---- @hoverSum/hover.md -- --```go --func (*Pos).Sum() int --``` +diff -urN a/gopls/internal/regtest/marker/testdata/quickfix/unusedrequire.txt b/gopls/internal/regtest/marker/testdata/quickfix/unusedrequire.txt +--- a/gopls/internal/regtest/marker/testdata/quickfix/unusedrequire.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/quickfix/unusedrequire.txt 1970-01-01 08:00:00 +@@ -1,24 +0,0 @@ +-This test checks the suggested fix to remove unused require statements from +-go.mod files. - --[`(a.Pos).Sum` on pkg.go.dev](https://pkg.go.dev/mod.com#Pos.Sum) ---- @hoverTestA/hover.md -- --```go --func TestA(t *testing.T) --``` ---- @hoverfield/hover.md -- --```go --field field string --``` ---- @hoverg/hover.md -- --```go --const g untyped int = 1 --``` +--- flags -- +--write_sumfile=a - --When I hover on g, I should see this comment. ---- @hoverh/hover.md -- --```go --const h untyped int = 2 --``` +--- proxy/example.com@v1.0.0/x.go -- +-package pkg +-const X = 1 - --Constant block. When I hover on h, I should see this comment. ---- @hoverpx/hover.md -- --```go --field x int --``` +--- a/go.mod -- +-module mod.com - --@loc(PosX, "x"),loc(PosY, "y") ---- @hoverx/hover.md -- --```go --var x string --``` +-go 1.14 - --x is a variable. ---- @hovery/hover.md -- --```go --var y int --``` ---- @hoverz/hover.md -- --```go --var z string --``` +-require example.com v1.0.0 //@suggestedfix("require", re"not used", "quickfix", a) - --z is a variable too. -diff -urN a/gopls/internal/regtest/marker/testdata/hover/basiclit.txt b/gopls/internal/regtest/marker/testdata/hover/basiclit.txt ---- a/gopls/internal/regtest/marker/testdata/hover/basiclit.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/hover/basiclit.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,60 +0,0 @@ --This test checks gopls behavior when hovering over basic literals. ---- basiclit.go -- --package basiclit +--- @a/a/go.mod -- +-module mod.com - --func _() { -- _ = 'a' //@hover("'a'", "'a'", latinA) -- _ = 0x61 //@hover("0x61", "0x61", latinA) +-go 1.14 +--- a/main.go -- +-package main +-func main() {} +diff -urN a/gopls/internal/regtest/marker/testdata/quickfix/unusedrequire_gowork.txt b/gopls/internal/regtest/marker/testdata/quickfix/unusedrequire_gowork.txt +--- a/gopls/internal/regtest/marker/testdata/quickfix/unusedrequire_gowork.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/quickfix/unusedrequire_gowork.txt 1970-01-01 08:00:00 +@@ -1,49 +0,0 @@ +-This test checks the suggested fix to remove unused require statements from +-go.mod files, when a go.work file is used. - -- _ = '\u2211' //@hover("'\\u2211'", "'\\u2211'", summation) -- _ = 0x2211 //@hover("0x2211", "0x2211", summation) -- _ = "foo \u2211 bar" //@hover("\\u2211", "\\u2211", summation) +-Note that unlike unusedrequire.txt, we need not write go.sum files when +-a go.work file is used. - -- _ = '\a' //@hover("'\\a'", "'\\a'", control) -- _ = "foo \a bar" //@hover("\\a", "\\a", control) +--- flags -- +--min_go=go1.18 - -- _ = '\U0001F30A' //@hover("'\\U0001F30A'", "'\\U0001F30A'", waterWave) -- _ = 0x0001F30A //@hover("0x0001F30A", "0x0001F30A", waterWave) -- _ = "foo \U0001F30A bar" //@hover("\\U0001F30A", "\\U0001F30A", waterWave) +--- proxy/example.com@v1.0.0/x.go -- +-package pkg +-const X = 1 - -- _ = '\x7E' //@hover("'\\x7E'", "'\\x7E'", tilde) -- _ = "foo \x7E bar" //@hover("\\x7E", "\\x7E", tilde) -- _ = "foo \a bar" //@hover("\\a", "\\a", control) +--- go.work -- +-go 1.21 - -- _ = '\173' //@hover("'\\173'", "'\\173'", leftCurly) -- _ = "foo \173 bar" //@hover("\\173","\\173", leftCurly) -- _ = "foo \173 bar \u2211 baz" //@hover("\\173","\\173", leftCurly) -- _ = "foo \173 bar \u2211 baz" //@hover("\\u2211","\\u2211", summation) -- _ = "foo\173bar\u2211baz" //@hover("\\173","\\173", leftCurly) -- _ = "foo\173bar\u2211baz" //@hover("\\u2211","\\u2211", summation) +-use ( +- ./a +- ./b +-) +--- a/go.mod -- +-module mod.com/a - -- // search for runes in string only if there is an escaped sequence -- _ = "hello" //@hover(`"hello"`, _, _) +-go 1.14 - -- // incorrect escaped rune sequences -- _ = '\0' //@hover("'\\0'", _, _),diag(re`\\0()'`, re"illegal character") -- _ = '\u22111' //@hover("'\\u22111'", _, _) -- _ = '\U00110000' //@hover("'\\U00110000'", _, _) -- _ = '\u12e45'//@hover("'\\u12e45'", _, _) -- _ = '\xa' //@hover("'\\xa'", _, _) -- _ = 'aa' //@hover("'aa'", _, _) +-require example.com v1.0.0 //@suggestedfix("require", re"not used", "quickfix", a) - -- // other basic lits -- _ = 1 //@hover("1", _, _) -- _ = 1.2 //@hover("1.2", _, _) -- _ = 1.2i //@hover("1.2i", _, _) -- _ = 0123 //@hover("0123", _, _) -- _ = 0x1234567890 //@hover("0x1234567890", _, _) --) ---- @control/hover.md -- --U+0007, control ---- @latinA/hover.md -- --'a', U+0061, LATIN SMALL LETTER A ---- @leftCurly/hover.md -- --'{', U+007B, LEFT CURLY BRACKET ---- @summation/hover.md -- --'∑', U+2211, N-ARY SUMMATION ---- @tilde/hover.md -- --'~', U+007E, TILDE ---- @waterWave/hover.md -- --'🌊', U+1F30A, WATER WAVE -diff -urN a/gopls/internal/regtest/marker/testdata/hover/const.txt b/gopls/internal/regtest/marker/testdata/hover/const.txt ---- a/gopls/internal/regtest/marker/testdata/hover/const.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/hover/const.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,18 +0,0 @@ --This test checks hovering over constants. ---- go.mod -- --module mod.com +--- @a/a/go.mod -- +-module mod.com/a - --go 1.18 ---- c.go -- --package c +-go 1.14 +--- a/main.go -- +-package main +-func main() {} - --const X = 0 //@hover("X", "X", bX) ---- @bX/hover.md -- --```go --const X untyped int = 0 --``` +--- b/go.mod -- +-module mod.com/b - --@hover("X", "X", bX) +-go 1.14 - +-require example.com v1.0.0 //@suggestedfix("require", re"not used", "quickfix", b) - --[`c.X` on pkg.go.dev](https://pkg.go.dev/mod.com#X) -diff -urN a/gopls/internal/regtest/marker/testdata/hover/generics.txt b/gopls/internal/regtest/marker/testdata/hover/generics.txt ---- a/gopls/internal/regtest/marker/testdata/hover/generics.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/hover/generics.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,77 +0,0 @@ --This file contains tests for hovering over generic Go code. +--- @b/b/go.mod -- +-module mod.com/b - ---- flags -- ---min_go=go1.18 +-go 1.14 +--- b/main.go -- +-package main +-func main() {} +diff -urN a/gopls/internal/regtest/marker/testdata/references/crosspackage.txt b/gopls/internal/regtest/marker/testdata/references/crosspackage.txt +--- a/gopls/internal/regtest/marker/testdata/references/crosspackage.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/crosspackage.txt 1970-01-01 08:00:00 +@@ -1,37 +0,0 @@ +-Test of basic cross-package references. - --- go.mod -- --// A go.mod is require for correct pkgsite links. --// TODO(rfindley): don't link to ad-hoc or command-line-arguments packages! --module mod.com -- --go 1.18 +-module example.com +-go 1.12 - ---- generics.go -- --package generics +--- a/a.go -- +-package a - --type value[T any] struct { //hover("lue", "value", value),hover("T", "T", valueT) -- val T //@hover("T", "T", valuevalT) -- Q int //@hover("Q", "Q", valueQ) +-type X struct { +- Y int //@loc(typeXY, "Y") -} - --type Value[T any] struct { //@hover("T", "T", ValueT) -- val T //@hover("T", "T", ValuevalT) -- Q int //@hover("Q", "Q", ValueQ) --} +--- b/b.go -- +-package b - --// disabled - see issue #54822 --func F[P interface{ ~int | string }]() { // hover("P","P",Ptparam) -- // disabled - see issue #54822 -- var _ P // hover("P","P",Pvar) +-import "example.com/a" +- +-func GetXes() []a.X { +- return []a.X{ +- { +- Y: 1, //@loc(GetXesY, "Y"), refs("Y", typeXY, GetXesY, anotherXY) +- }, +- } -} - ---- inferred.go -- --package generics +--- c/c.go -- +-package c - --func app[S interface{ ~[]E }, E interface{}](s S, e E) S { -- return append(s, e) --} +-import "example.com/b" - -func _() { -- _ = app[[]int] //@hover("app", "app", appint) -- _ = app[[]int, int] //@hover("app", "app", appint) -- // TODO(rfindley): eliminate this diagnostic. -- _ = app[[]int]([]int{}, 0) //@hover("app", "app", appint),diag("[[]int]", re"unnecessary type arguments") -- _ = app([]int{}, 0) //@hover("app", "app", appint) +- xes := b.GetXes() +- for _, x := range xes { //@loc(defX, "x") +- _ = x.Y //@loc(useX, "x"), loc(anotherXY, "Y"), refs("Y", typeXY, anotherXY, GetXesY), refs(".", defX, useX), refs("x", defX, useX) +- } -} +diff -urN a/gopls/internal/regtest/marker/testdata/references/imports.txt b/gopls/internal/regtest/marker/testdata/references/imports.txt +--- a/gopls/internal/regtest/marker/testdata/references/imports.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/imports.txt 1970-01-01 08:00:00 +@@ -1,17 +0,0 @@ +-Test of references to local package names (imports). - ---- @ValueQ/hover.md -- --```go --field Q int --``` +--- go.mod -- +-module example.com +-go 1.12 - --@hover("Q", "Q", ValueQ) +--- a/a.go -- +-package a - +-import "os" //@loc(osDef, `"os"`), refs("os", osDef, osUse) - --[`(generics.Value).Q` on pkg.go.dev](https://pkg.go.dev/mod.com#Value.Q) ---- @ValueT/hover.md -- --```go --type parameter T any --``` ---- @ValuevalT/hover.md -- --```go --type parameter T any --``` ---- @appint/hover.md -- --```go --func app(s []int, e int) []int // func[S interface{~[]E}, E interface{}](s S, e E) S --``` ---- @valueQ/hover.md -- --```go --field Q int --``` +-import fmt2 "fmt" //@loc(fmt2Def, `fmt2`), refs("fmt2", fmt2Def, fmt2Use) +- +-func _() { +- os.Getwd() //@loc(osUse, "os") +- fmt2.Println() //@loc(fmt2Use, "fmt2") +-} +diff -urN a/gopls/internal/regtest/marker/testdata/references/interfaces.txt b/gopls/internal/regtest/marker/testdata/references/interfaces.txt +--- a/gopls/internal/regtest/marker/testdata/references/interfaces.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/interfaces.txt 1970-01-01 08:00:00 +@@ -1,42 +0,0 @@ +-Test of references applied to concrete and interface types that are +-related by assignability. The result includes references to both. - --@hover("Q", "Q", valueQ) ---- @valuevalT/hover.md -- --```go --type parameter T any --``` -diff -urN a/gopls/internal/regtest/marker/testdata/hover/goprivate.txt b/gopls/internal/regtest/marker/testdata/hover/goprivate.txt ---- a/gopls/internal/regtest/marker/testdata/hover/goprivate.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/hover/goprivate.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,27 +0,0 @@ --This test checks that links in hover obey GOPRIVATE. ---- env -- --GOPRIVATE=mod.com --- go.mod -- --module mod.com ---- p.go -- --package p +-module example.com +-go 1.12 - --// T should not be linked, as it is private. --type T struct{} //@hover("T", "T", T) ---- lib/lib.go -- --package lib +--- a/a.go -- +-package a - --// GOPRIVATE should also match nested packages. --type L struct{} //@hover("L", "L", L) ---- @L/hover.md -- --```go --type L struct{} --``` +-type first interface { +- common() //@loc(firCommon, "common"), refs("common", firCommon, xCommon, zCommon) +- firstMethod() //@loc(firMethod, "firstMethod"), refs("firstMethod", firMethod, xfMethod, zfMethod) +-} - --GOPRIVATE should also match nested packages. ---- @T/hover.md -- --```go --type T struct{} --``` +-type second interface { +- common() //@loc(secCommon, "common"), refs("common", secCommon, yCommon, zCommon) +- secondMethod() //@loc(secMethod, "secondMethod"), refs("secondMethod", secMethod, ysMethod, zsMethod) +-} - --T should not be linked, as it is private. -diff -urN a/gopls/internal/regtest/marker/testdata/hover/hover.txt b/gopls/internal/regtest/marker/testdata/hover/hover.txt ---- a/gopls/internal/regtest/marker/testdata/hover/hover.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/hover/hover.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,29 +0,0 @@ --This test demonstrates some features of the new marker test runner. ---- a.go -- --package a +-type s struct {} - --const abc = 0x2a //@hover("b", "abc", abc),hover(" =", "abc", abc) ---- typeswitch.go -- --package a +-func (*s) common() {} //@loc(sCommon, "common"), refs("common", sCommon, xCommon, yCommon, zCommon) - --func _() { -- var y interface{} -- switch x := y.(type) { //@hover("x", "x", x) -- case int: -- println(x) //@hover("x", "x", xint),hover(")", "x", xint) -- } --} ---- @abc/hover.md -- --```go --const abc untyped int = 42 --``` +-func (*s) firstMethod() {} //@loc(sfMethod, "firstMethod"), refs("firstMethod", sfMethod, xfMethod, zfMethod) - --@hover("b", "abc", abc),hover(" =", "abc", abc) ---- @x/hover.md -- --```go --var x interface{} --``` ---- @xint/hover.md -- --```go --var x int --``` -diff -urN a/gopls/internal/regtest/marker/testdata/hover/linkable_generics.txt b/gopls/internal/regtest/marker/testdata/hover/linkable_generics.txt ---- a/gopls/internal/regtest/marker/testdata/hover/linkable_generics.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/hover/linkable_generics.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,145 +0,0 @@ --This file contains tests for documentation links to generic code in hover. +-func (*s) secondMethod() {} //@loc(ssMethod, "secondMethod"), refs("secondMethod", ssMethod, ysMethod, zsMethod) - ---- flags -- ---min_go=go1.18 +-func main() { +- var x first = &s{} +- var y second = &s{} - ---- go.mod -- --module mod.com +- x.common() //@loc(xCommon, "common"), refs("common", firCommon, xCommon, zCommon) +- x.firstMethod() //@loc(xfMethod, "firstMethod"), refs("firstMethod", firMethod, xfMethod, zfMethod) +- y.common() //@loc(yCommon, "common"), refs("common", secCommon, yCommon, zCommon) +- y.secondMethod() //@loc(ysMethod, "secondMethod"), refs("secondMethod", secMethod, ysMethod, zsMethod) - --go 1.19 +- var z *s = &s{} +- z.firstMethod() //@loc(zfMethod, "firstMethod"), refs("firstMethod", sfMethod, xfMethod, zfMethod) +- z.secondMethod() //@loc(zsMethod, "secondMethod"), refs("secondMethod", ssMethod, ysMethod, zsMethod) +- z.common() //@loc(zCommon, "common"), refs("common", sCommon, xCommon, yCommon, zCommon) +-} +diff -urN a/gopls/internal/regtest/marker/testdata/references/intrapackage.txt b/gopls/internal/regtest/marker/testdata/references/intrapackage.txt +--- a/gopls/internal/regtest/marker/testdata/references/intrapackage.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/intrapackage.txt 1970-01-01 08:00:00 +@@ -1,36 +0,0 @@ +-Basic test of references within a single package. - ---- a.go -- --package a +--- go.mod -- +-module example.com +-go 1.12 - --import "mod.com/generic" +--- a/a.go -- +-package a - --func _() { -- // Hovering over instantiated object should produce accurate type -- // information, but link to the generic declarations. +-type i int //@loc(decli, "i"), refs("i", decli, argi, returni, embeddedi) - -- var x generic.GT[int] //@hover("GT", "GT", xGT) -- _ = x.F //@hover("x", "x", x),hover("F", "F", xF) +-func _(_ i) []bool { //@loc(argi, "i") +- return nil +-} - -- f := generic.GF[int] //@hover("GF", "GF", fGF) -- _ = f //@hover("f", "f", f) +-func _(_ []byte) i { //@loc(returni, "i") +- return 0 -} - ---- generic/generic.go -- --package generic +-var q string //@loc(declq, "q"), refs("q", declq, assignq, bobq) - --// Hovering over type parameters should link to documentation. --// --// TODO(rfindley): should it? We should probably link to the type. --type GT[P any] struct{ //@hover("GT", "GT", GT),hover("P", "P", GTP) -- F P //@hover("F", "F", F),hover("P", "P", FP) +-var Q string //@loc(declQ, "Q"), refs("Q", declQ) +- +-func _() { +- q = "hello" //@loc(assignq, "q") +- bob := func(_ string) {} +- bob(q) //@loc(bobq, "q") -} - --func (GT[P]) M(p P) { //@hover("GT", "GT", GTrecv),hover("M","M", M),hover(re"p (P)", re"p (P)", pP) +-type e struct { +- i //@loc(embeddedi, "i"), refs("i", embeddedi, embeddediref) -} - --func GF[P any] (p P) { //@hover("GF", "GF", GF) +-func _() { +- _ = e{}.i //@loc(embeddediref, "i") -} +diff -urN a/gopls/internal/regtest/marker/testdata/references/issue58506.txt b/gopls/internal/regtest/marker/testdata/references/issue58506.txt +--- a/gopls/internal/regtest/marker/testdata/references/issue58506.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/issue58506.txt 1970-01-01 08:00:00 +@@ -1,56 +0,0 @@ +-Regression test for 'references' bug golang/go#58506. - ---- @F/hover.md -- --```go --field F P --``` +-The 'references' query below, applied to method A.F, implicitly uses +-the 'implementation' operation. The correct response includes two +-references to B.F, one from package b and one from package d. +-However, the incremental 'implementation' algorithm had a bug that +-cause it to fail to report the reference from package b. - --@hover("F", "F", F),hover("P", "P", FP) +-The reason was that the incremental implementation uses different +-algorithms for the local and global cases (with disjoint results), and +-that when it discovered that type A satisfies interface B and thus +-that B.F must be included among the global search targets, the +-implementation forgot to also search package b for local references +-to B.F. - +--- go.mod -- +-module example.com +-go 1.12 - --[`(generic.GT).F` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT.F) ---- @FP/hover.md -- --```go --type parameter P any --``` ---- @GF/hover.md -- --```go --func GF[P any](p P) --``` +--- a/a.go -- +-package a - --[`generic.GF` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GF) ---- @GT/hover.md -- --```go --type GT[P any] struct { -- F P //@hover("F", "F", F),hover("P", "P", FP) --} +-type A int - --func (GT[P]).M(p P) --``` +-func (A) F() {} //@loc(refa, "F"), refs("F", refa, refb, refd) +- +--- b/b.go -- +-package b +- +-import ( +- "example.com/a" +- "example.com/c" +-) +- +-type B interface{ F() } +- +-var _ B = a.A(0) +-var _ B = c.C(0) +- +-var _ = B.F //@loc(refb, "F") +- +--- c/c.go -- +-package c +- +-type C int +- +-// Even though C.F is "rename coupled" to A.F by B.F, +-// it should not be among the results. +-func (C) F() {} +- +--- d/d.go -- +-package d - --Hovering over type parameters should link to documentation. +-import "example.com/b" - --TODO(rfindley): should it? We should probably link to the type. +-var _ interface{} = b.B.F //@loc(refd, "F") +diff -urN a/gopls/internal/regtest/marker/testdata/references/issue59851.txt b/gopls/internal/regtest/marker/testdata/references/issue59851.txt +--- a/gopls/internal/regtest/marker/testdata/references/issue59851.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/issue59851.txt 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +-Regression test for 'references' bug golang/go#59851. - +--- go.mod -- +-module example.com +-go 1.12 - --[`generic.GT` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT) ---- @GTP/hover.md -- --```go --type parameter P any --``` ---- @GTrecv/hover.md -- --```go --type GT[P any] struct { -- F P //@hover("F", "F", F),hover("P", "P", FP) --} +--- a/a.go -- +-package a - --func (GT[P]).M(p P) --``` +-type Iface interface { +- Method() +-} - --Hovering over type parameters should link to documentation. +-type implOne struct{} - --TODO(rfindley): should it? We should probably link to the type. +-func (implOne) Method() {} //@loc(def1, "Method"), refs(def1, def1, ref1, iref, ireftest) - +-var _ = implOne.Method //@loc(ref1, "Method") +-var _ = Iface(nil).Method //@loc(iref, "Method") - --[`generic.GT` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT) ---- @M/hover.md -- --```go --func (GT[P]).M(p P) --``` +--- a/a_test.go -- +-package a - --[`(generic.GT).M` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT.M) ---- @f/hover.md -- --```go --var f func(p int) --``` ---- @fGF/hover.md -- --```go --func generic.GF(p int) // func[P any](p P) --``` +-type implTwo struct{} - --[`generic.GF` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GF) ---- @pP/hover.md -- --```go --type parameter P any --``` ---- @x/hover.md -- --```go --var x generic.GT[int] --``` +-func (implTwo) Method() {} //@loc(def2, "Method"), refs(def2, def2, iref, ref2, ireftest) - --@hover("GT", "GT", xGT) ---- @xF/hover.md -- --```go --field F int --``` +-var _ = implTwo.Method //@loc(ref2, "Method") +-var _ = Iface(nil).Method //@loc(ireftest, "Method") +diff -urN a/gopls/internal/regtest/marker/testdata/references/issue60369.txt b/gopls/internal/regtest/marker/testdata/references/issue60369.txt +--- a/gopls/internal/regtest/marker/testdata/references/issue60369.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/issue60369.txt 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +-Regression test for 'references' bug golang/go#60369: a references +-query on the embedded type name T in struct{p.T} instead reports all +-references to the package name p. - --@hover("F", "F", F),hover("P", "P", FP) +-The bug was fixed in release go1.21 of go/types. - +--- flags -- +--min_go=go1.21 - --[`(generic.GT).F` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT.F) ---- @xGT/hover.md -- --```go --type GT[P any] struct { -- F P //@hover("F", "F", F),hover("P", "P", FP) --} +--- go.mod -- +-module example.com +-go 1.12 - --func (generic.GT[P]).M(p P) --``` +--- a/a.go -- +-package a - --Hovering over type parameters should link to documentation. +-type A struct{} +-const C = 0 - --TODO(rfindley): should it? We should probably link to the type. +--- b/b.go -- +-package b - +-import a "example.com/a" //@loc(adef, "a") +-type s struct { +- a.A //@loc(Aref1, "A"), loc(aref1, "a"), refs(Aref1, Aref1, Aref3), refs(aref1, adef, aref1, aref2, aref3) +-} +-var _ a.A //@loc(aref2, re" (a)"), loc(Aref2, "A") +-var _ = s{}.A //@loc(Aref3, "A") +-const c = a.C //@loc(aref3, "a") +diff -urN a/gopls/internal/regtest/marker/testdata/references/issue60622.txt b/gopls/internal/regtest/marker/testdata/references/issue60622.txt +--- a/gopls/internal/regtest/marker/testdata/references/issue60622.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/issue60622.txt 1970-01-01 08:00:00 +@@ -1,25 +0,0 @@ +-Regression test for 'references' bug golang/go#60622: +-references to methods of generics were missing. - --[`generic.GT` on pkg.go.dev](https://pkg.go.dev/mod.com/generic#GT) -diff -urN a/gopls/internal/regtest/marker/testdata/hover/linkable.txt b/gopls/internal/regtest/marker/testdata/hover/linkable.txt ---- a/gopls/internal/regtest/marker/testdata/hover/linkable.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/hover/linkable.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,120 +0,0 @@ --This test checks that we correctly determine pkgsite links for various --identifiers. +--- flags -- +--min_go=go1.18 - --We should only produce links that work, meaning the object is reachable via the --package's public API. --- go.mod -- --module mod.com -- +-module example.com -go 1.18 ---- p.go -- --package p - --type E struct { -- Embed int --} +--- a/a.go -- +-package a - --// T is in the package scope, and so should be linkable. --type T struct{ //@hover("T", "T", T) -- // Only exported fields should be linkable +-type G[T any] struct{} - -- f int //@hover("f", "f", f) -- F int //@hover("F", "F", F) +-func (G[T]) M() {} //@loc(Mdef, "M"), refs(Mdef, Mdef, Mref) - -- E +--- b/b.go -- +-package b - -- // TODO(rfindley): is the link here correct? It ignores N. -- N struct { -- // Nested fields should also be linkable. -- Nested int //@hover("Nested", "Nested", Nested) -- } +-import "example.com/a" +- +-func _() { +- new(a.G[int]).M() //@loc(Mref, "M") -} --// M is an exported method, and so should be linkable. --func (T) M() {} +diff -urN a/gopls/internal/regtest/marker/testdata/references/issue60676.txt b/gopls/internal/regtest/marker/testdata/references/issue60676.txt +--- a/gopls/internal/regtest/marker/testdata/references/issue60676.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/issue60676.txt 1970-01-01 08:00:00 +@@ -1,69 +0,0 @@ +-This test verifies that even after importing from export data, the references +-algorithm is able to find all references to struct fields or methods that are +-shared by types from multiple packages. See golang/go#60676. - --// m is not exported, and so should not be linkable. --func (T) m() {} +-Note that the marker test runner awaits the initial workspace load, so export +-data should be populated at the time references are requested. - --func _() { -- var t T +--- flags -- +--min_go=go1.18 - -- // Embedded fields should be linkable. -- _ = t.Embed //@hover("Embed", "Embed", Embed) +--- go.mod -- +-module mod.test - -- // Local variables should not be linkable, even if they are capitalized. -- var X int //@hover("X", "X", X) -- _ = X +-go 1.18 - -- // Local types should not be linkable, even if they are capitalized. -- type Local struct { //@hover("Local", "Local", Local) -- E -- } +--- a/a.go -- +-package a - -- // But the embedded field should still be linkable. -- var l Local -- _ = l.Embed //@hover("Embed", "Embed", Embed) +-type A struct { +- F int //@loc(FDef, "F") +- E //@loc(EDef, "E") -} ---- @Embed/hover.md -- --```go --field Embed int --``` - --[`(p.E).Embed` on pkg.go.dev](https://pkg.go.dev/mod.com#E.Embed) ---- @F/hover.md -- --```go --field F int --``` -- --@hover("F", "F", F) +-type E struct { +- G string //@loc(GDef, "G") +-} - +-type AI interface { +- M() //@loc(MDef, "M") +- EI +- error +-} - --[`(p.T).F` on pkg.go.dev](https://pkg.go.dev/mod.com#T.F) ---- @Local/hover.md -- --```go --type Local struct { -- E +-type EI interface { +- N() //@loc(NDef, "N") -} --``` - --Local types should not be linkable, even if they are capitalized. ---- @Nested/hover.md -- --```go --field Nested int --``` +-type T[P any] struct{ f P } - --Nested fields should also be linkable. ---- @T/hover.md -- --```go --type T struct { -- f int //@hover("f", "f", f) -- F int //@hover("F", "F", F) +-type Error error - -- E - -- // TODO(rfindley): is the link here correct? It ignores N. -- N struct { -- // Nested fields should also be linkable. -- Nested int //@hover("Nested", "Nested", Nested) -- } --} +--- b/b.go -- +-package b - --func (T).M() --func (T).m() --``` +-import "mod.test/a" - --T is in the package scope, and so should be linkable. +-type B a.A - +-type BI a.AI - --[`p.T` on pkg.go.dev](https://pkg.go.dev/mod.com#T) ---- @X/hover.md -- --```go --var X int --``` +-type T a.T[int] // must not panic - --Local variables should not be linkable, even if they are capitalized. ---- @f/hover.md -- --```go --field f int --``` +--- c/c.go -- +-package c - --@hover("f", "f", f) -diff -urN a/gopls/internal/regtest/marker/testdata/hover/std.txt b/gopls/internal/regtest/marker/testdata/hover/std.txt ---- a/gopls/internal/regtest/marker/testdata/hover/std.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/hover/std.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,80 +0,0 @@ --This test checks hover results for built-in or standard library symbols. +-import "mod.test/b" - --It uses synopsis documentation as full documentation for some of these --built-ins varies across Go versions, where as it just so happens that the --synopsis does not. +-func _() { +- x := b.B{ +- F: 42, //@refs("F", FDef, "F") +- } +- x.G = "hi" //@refs("G", GDef, "G") +- _ = x.E //@refs("E", EDef, "E") - --In the future we may need to limit this test to the latest Go version to avoid --documentation churn. ---- settings.json -- --{ -- "hoverKind": "SynopsisDocumentation" +- var y b.BI +- _ = y.M //@refs("M", MDef, "M") +- _ = y.N //@refs("N", NDef, "N") -} ---- go.mod -- --module mod.com +diff -urN a/gopls/internal/regtest/marker/testdata/references/issue61618.txt b/gopls/internal/regtest/marker/testdata/references/issue61618.txt +--- a/gopls/internal/regtest/marker/testdata/references/issue61618.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/issue61618.txt 1970-01-01 08:00:00 +@@ -1,39 +0,0 @@ +-Regression test for 'references' bug golang/go#61618: +-references to instantiated fields were missing. +- +--- flags -- +--min_go=go1.18 - +--- go.mod -- +-module example.com -go 1.18 ---- std.go -- --package std - --import ( -- "fmt" -- "go/types" -- "sync" --) +--- a.go -- +-package a - --func _() { -- var err error //@loc(err, "err") -- fmt.Printf("%v", err) //@def("err", err) +-// This file is adapted from the example in the issue. - -- var _ string //@hover("string", "string", hoverstring) -- _ = make([]int, 0) //@hover("make", "make", hovermake) +-type builder[S ~[]F, F ~string] struct { +- name string +- elements S //@loc(def, "elements"), refs(def, def, assign, use) +- elemData map[F][]ElemData[F] +-} - -- var mu sync.Mutex -- mu.Lock() //@hover("Lock", "Lock", hoverLock) +-type ElemData[F ~string] struct { +- Name F +-} - -- var typ *types.Named //@hover("types", "types", hoverTypes) -- typ.Obj().Name() //@hover("Name", "Name", hoverName) +-type BuilderImpl[S ~[]F, F ~string] struct{ builder[S, F] } +- +-func NewBuilderImpl[S ~[]F, F ~string](name string) *BuilderImpl[S, F] { +- impl := &BuilderImpl[S,F]{ +- builder[S, F]{ +- name: name, +- elements: S{}, //@loc(assign, "elements"), refs(assign, def, assign, use) +- elemData: map[F][]ElemData[F]{}, +- }, +- } +- +- _ = impl.elements //@loc(use, "elements"), refs(use, def, assign, use) +- return impl -} ---- @hoverLock/hover.md -- --```go --func (*sync.Mutex).Lock() --``` +diff -urN a/gopls/internal/regtest/marker/testdata/references/shadow.txt b/gopls/internal/regtest/marker/testdata/references/shadow.txt +--- a/gopls/internal/regtest/marker/testdata/references/shadow.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/shadow.txt 1970-01-01 08:00:00 +@@ -1,17 +0,0 @@ +-Test of references in the presence of shadowing. - --Lock locks m. +--- go.mod -- +-module example.com +-go 1.12 - +--- a/a.go -- +-package a - --[`(sync.Mutex).Lock` on pkg.go.dev](https://pkg.go.dev/sync#Mutex.Lock) ---- @hoverName/hover.md -- --```go --func (*types.object).Name() string --``` +-func _() { +- x := 123 //@loc(x1, "x"), refs("x", x1, x1ref) +- _ = x //@loc(x1ref, "x") +- { +- x := "hi" //@loc(x2, "x"), refs("x", x2, x2ref) +- _ = x //@loc(x2ref, "x") +- } +-} +diff -urN a/gopls/internal/regtest/marker/testdata/references/test.txt b/gopls/internal/regtest/marker/testdata/references/test.txt +--- a/gopls/internal/regtest/marker/testdata/references/test.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/test.txt 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +-Test of references between the extra files of a test variant +-and the regular package. - --Name returns the object's (package-local, unqualified) name. +--- go.mod -- +-module example.com +-go 1.12 - +--- a/a.go -- +-package a - --[`(types.TypeName).Name` on pkg.go.dev](https://pkg.go.dev/go/types#TypeName.Name) ---- @hoverTypes/hover.md -- --```go --package types ("go/types") --``` +-func fn() {} //@loc(def, "fn"), refs("fn", def, use) - --[`types` on pkg.go.dev](https://pkg.go.dev/go/types) ---- @hovermake/hover.md -- --```go --func make(t Type, size ...int) Type --``` +-type t struct { g int } //@loc(gdef, "g") +-type u struct { t } - --The make built-in function allocates and initializes an object of type slice, map, or chan (only). +-var _ = new(u).g //@loc(gref, "g"), refs("g", gdef, gref) +-// TODO(adonovan): fix: gref2 and gdef2 are missing. - +--- a/a_test.go -- +-package a - --[`make` on pkg.go.dev](https://pkg.go.dev/builtin#make) ---- @hoverstring/hover.md -- --```go --type string string --``` +-func _() { +- fn() //@loc(use, "fn") - --string is the set of all strings of 8-bit bytes, conventionally but not necessarily representing UTF-8-encoded text. +- _ = new(u).g //@loc(gref2, "g"), refs("g", gdef2, gref, gref2) +-} +- +-// This declaration changes the meaning of u.t in the test. +-func (u) g() {} //@loc(gdef2, "g") +diff -urN a/gopls/internal/regtest/marker/testdata/references/typeswitch.txt b/gopls/internal/regtest/marker/testdata/references/typeswitch.txt +--- a/gopls/internal/regtest/marker/testdata/references/typeswitch.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/references/typeswitch.txt 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +-Tests of reference to implicit type switch vars, which are +-a special case in go/types.Info{Def,Use,Implicits}. +- +--- go.mod -- +-module example.com +-go 1.12 - +--- a/a.go -- +-package a - --[`string` on pkg.go.dev](https://pkg.go.dev/builtin#string) +-func _(x interface{}) { +- switch y := x.(type) { //@loc(yDecl, "y"), refs("y", yDecl, yInt, yDefault) +- case int: +- println(y) //@loc(yInt, "y"), refs("y", yDecl, yInt, yDefault) +- default: +- println(y) //@loc(yDefault, "y") +- } +-} diff -urN a/gopls/internal/regtest/marker/testdata/rename/basic.txt b/gopls/internal/regtest/marker/testdata/rename/basic.txt --- a/gopls/internal/regtest/marker/testdata/rename/basic.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/rename/basic.txt 1970-01-01 00:00:00.000000000 +0000 -@@ -1,22 +0,0 @@ ++++ b/gopls/internal/regtest/marker/testdata/rename/basic.txt 1970-01-01 08:00:00 +@@ -1,38 +0,0 @@ -This test performs basic coverage of 'rename' within a single package. - --- basic.go -- -package p - --func f(x int) { println(x) } //@rename("x", y, param_x) +-func f(x int) { println(x) } //@rename("x", "y", xToy) +- +--- @xToy/basic.go -- +-package p +- +-func f(y int) { println(y) } //@rename("x", "y", xToy) - ---- @param_x/basic.go -- +--- alias.go -- -package p - --func f(y int) { println(y) } //@rename("x", y, param_x) +-// from golang/go#61625 +-type LongNameHere struct{} +-type A = LongNameHere //@rename("A", "B", AToB) +-func Foo() A - --- errors.go -- -package p - --func _(x []int) { //@renameerr("_", blank, `can't rename "_"`) -- x = append(x, 1) //@renameerr("append", blank, "built in and cannot be renamed") -- x = nil //@renameerr("nil", blank, "built in and cannot be renamed") -- x = nil //@renameerr("x", x, "old and new names are the same: x") -- _ = 1 //@renameerr("1", x, "no identifier found") +-func _(x []int) { //@renameerr("_", "blank", `can't rename "_"`) +- x = append(x, 1) //@renameerr("append", "blank", "built in and cannot be renamed") +- x = nil //@renameerr("nil", "blank", "built in and cannot be renamed") +- x = nil //@renameerr("x", "x", "old and new names are the same: x") +- _ = 1 //@renameerr("1", "x", "no identifier found") -} - +--- @AToB/alias.go -- +-package p +- +-// from golang/go#61625 +-type LongNameHere struct{} +-type B = LongNameHere //@rename("A", "B", AToB) +-func Foo() B +- diff -urN a/gopls/internal/regtest/marker/testdata/rename/conflict.txt b/gopls/internal/regtest/marker/testdata/rename/conflict.txt --- a/gopls/internal/regtest/marker/testdata/rename/conflict.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/rename/conflict.txt 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/marker/testdata/rename/conflict.txt 1970-01-01 08:00:00 @@ -1,59 +0,0 @@ -This test exercises some renaming conflict scenarios -and ensures that the errors are informative. @@ -114028,7 +125891,7 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/conflict.txt b/gopls/i - -func f(y int) { - println(x) -- println(y) //@renameerr("y", x, errSuperBlockConflict) +- println(y) //@renameerr("y", "x", errSuperBlockConflict) -} - --- @errSuperBlockConflict -- @@ -114041,7 +125904,7 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/conflict.txt b/gopls/i -var a int - -func f2(b int) { -- println(a) //@renameerr("a", b, errSubBlockConflict) +- println(a) //@renameerr("a", "b", errSubBlockConflict) - println(b) -} - @@ -114052,7 +125915,7 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/conflict.txt b/gopls/i --- pkgname/p.go -- -package pkgname - --import e1 "errors" //@renameerr("e1", errors, errImportConflict) +-import e1 "errors" //@renameerr("e1", "errors", errImportConflict) -import "errors" - -var _ = errors.New @@ -114067,7 +125930,7 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/conflict.txt b/gopls/i - --- pkgname2/p2.go -- -package pkgname2 --import "errors" //@renameerr("errors", x, errImportConflict2) +-import "errors" //@renameerr("errors", "x", errImportConflict2) -var _ = errors.New - --- @errImportConflict2 -- @@ -114075,7 +125938,7 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/conflict.txt b/gopls/i -pkgname2/p1.go:2:5: with this package member var diff -urN a/gopls/internal/regtest/marker/testdata/rename/embed.txt b/gopls/internal/regtest/marker/testdata/rename/embed.txt --- a/gopls/internal/regtest/marker/testdata/rename/embed.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/rename/embed.txt 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/marker/testdata/rename/embed.txt 1970-01-01 08:00:00 @@ -1,36 +0,0 @@ -This test exercises renaming of types used as embedded fields. - @@ -114086,36 +125949,426 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/embed.txt b/gopls/inte --- a/a.go -- -package a - --type A int //@rename("A", A2, type) +-type A int //@rename("A", "A2", type) - --- b/b.go -- -package b - -import "example.com/a" - --type B struct { a.A } //@renameerr("A", A3, errAnonField) +-type B struct { a.A } //@renameerr("A", "A3", errAnonField) - --var _ = new(B).A //@renameerr("A", A4, errAnonField) +-var _ = new(B).A //@renameerr("A", "A4", errAnonField) - --- @errAnonField -- -can't rename embedded fields: rename the type directly or name the field --- @type/a/a.go -- -package a - --type A2 int //@rename("A", A2, type) +-type A2 int //@rename("A", "A2", type) - --- @type/b/b.go -- -package b - -import "example.com/a" - --type B struct { a.A2 } //@renameerr("A", A3, errAnonField) +-type B struct { a.A2 } //@renameerr("A", "A3", errAnonField) +- +-var _ = new(B).A2 //@renameerr("A", "A4", errAnonField) +- +diff -urN a/gopls/internal/regtest/marker/testdata/rename/generics.txt b/gopls/internal/regtest/marker/testdata/rename/generics.txt +--- a/gopls/internal/regtest/marker/testdata/rename/generics.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/rename/generics.txt 1970-01-01 08:00:00 +@@ -1,240 +0,0 @@ +-This test exercises various renaming features on generic code. +- +-Fixed bugs: +- +-- golang/go#61614: renaming a method of a type in a package that uses type +- parameter composite lits used to panic, because previous iterations of the +- satisfy analysis did not account for this language feature. +- +-- golang/go#61635: renaming type parameters did not work when they were +- capitalized and the package was imported by another package. +- +--- flags -- +--min_go=go1.18 +- +--- go.mod -- +-module example.com +-go 1.20 +- +--- a.go -- +-package a +- +-type I int +- +-func (I) m() {} //@rename("m", "M", mToM) +- +-func _[P ~[]int]() { +- _ = P{} +-} +- +--- @mToM/a.go -- +-package a +- +-type I int +- +-func (I) M() {} //@rename("m", "M", mToM) +- +-func _[P ~[]int]() { +- _ = P{} +-} +- +--- g.go -- +-package a +- +-type S[P any] struct { //@rename("P", "Q", PToQ) +- P P +- F func(P) P +-} +- +-func F[R any](r R) { +- var _ R //@rename("R", "S", RToS) +-} +- +--- @PToQ/g.go -- +-package a +- +-type S[Q any] struct { //@rename("P", "Q", PToQ) +- P Q +- F func(Q) Q +-} +- +-func F[R any](r R) { +- var _ R //@rename("R", "S", RToS) +-} +- +--- @RToS/g.go -- +-package a +- +-type S[P any] struct { //@rename("P", "Q", PToQ) +- P P +- F func(P) P +-} +- +-func F[S any](r S) { +- var _ S //@rename("R", "S", RToS) +-} +- +--- issue61635/p.go -- +-package issue61635 +- +-type builder[S ~[]F, F ~string] struct { //@rename("S", "T", SToT) +- name string +- elements S +- elemData map[F][]ElemData[F] +- // other fields... +-} +- +-type ElemData[F ~string] struct { +- Name F +- // other fields... +-} +- +-type BuilderImpl[S ~[]F, F ~string] struct{ builder[S, F] } +- +--- importer/i.go -- +-package importer +- +-import "example.com/issue61635" // importing is necessary to repro golang/go#61635 +- +-var _ issue61635.ElemData[string] +- +--- @SToT/issue61635/p.go -- +-package issue61635 +- +-type builder[T ~[]F, F ~string] struct { //@rename("S", "T", SToT) +- name string +- elements T +- elemData map[F][]ElemData[F] +- // other fields... +-} +- +-type ElemData[F ~string] struct { +- Name F +- // other fields... +-} +- +-type BuilderImpl[S ~[]F, F ~string] struct{ builder[S, F] } +- +--- instances/type.go -- +-package instances +- +-type R[P any] struct { //@rename("R", "u", Rtou) +- Next *R[P] //@rename("R", "s", RTos) +-} +- +-func (rv R[P]) Do(R[P]) R[P] { //@rename("Do", "Do1", DoToDo1) +- var x R[P] +- return rv.Do(x) //@rename("Do", "Do2", DoToDo2) +-} +- +-func _() { +- var x R[int] //@rename("R", "r", RTor) +- x = x.Do(x) +-} +- +--- @RTos/instances/type.go -- +-package instances +- +-type s[P any] struct { //@rename("R", "u", Rtou) +- Next *s[P] //@rename("R", "s", RTos) +-} +- +-func (rv s[P]) Do(s[P]) s[P] { //@rename("Do", "Do1", DoToDo1) +- var x s[P] +- return rv.Do(x) //@rename("Do", "Do2", DoToDo2) +-} +- +-func _() { +- var x s[int] //@rename("R", "r", RTor) +- x = x.Do(x) +-} +- +--- @Rtou/instances/type.go -- +-package instances +- +-type u[P any] struct { //@rename("R", "u", Rtou) +- Next *u[P] //@rename("R", "s", RTos) +-} +- +-func (rv u[P]) Do(u[P]) u[P] { //@rename("Do", "Do1", DoToDo1) +- var x u[P] +- return rv.Do(x) //@rename("Do", "Do2", DoToDo2) +-} +- +-func _() { +- var x u[int] //@rename("R", "r", RTor) +- x = x.Do(x) +-} +- +--- @DoToDo1/instances/type.go -- +-package instances +- +-type R[P any] struct { //@rename("R", "u", Rtou) +- Next *R[P] //@rename("R", "s", RTos) +-} +- +-func (rv R[P]) Do1(R[P]) R[P] { //@rename("Do", "Do1", DoToDo1) +- var x R[P] +- return rv.Do1(x) //@rename("Do", "Do2", DoToDo2) +-} +- +-func _() { +- var x R[int] //@rename("R", "r", RTor) +- x = x.Do1(x) +-} +- +--- @DoToDo2/instances/type.go -- +-package instances +- +-type R[P any] struct { //@rename("R", "u", Rtou) +- Next *R[P] //@rename("R", "s", RTos) +-} +- +-func (rv R[P]) Do2(R[P]) R[P] { //@rename("Do", "Do1", DoToDo1) +- var x R[P] +- return rv.Do2(x) //@rename("Do", "Do2", DoToDo2) +-} +- +-func _() { +- var x R[int] //@rename("R", "r", RTor) +- x = x.Do2(x) +-} +- +--- instances/func.go -- +-package instances +- +-func Foo[P any](p P) { //@rename("Foo", "Bar", FooToBar) +- Foo(p) //@rename("Foo", "Baz", FooToBaz) +-} +- +--- @FooToBar/instances/func.go -- +-package instances +- +-func Bar[P any](p P) { //@rename("Foo", "Bar", FooToBar) +- Bar(p) //@rename("Foo", "Baz", FooToBaz) +-} +- +--- @FooToBaz/instances/func.go -- +-package instances +- +-func Baz[P any](p P) { //@rename("Foo", "Bar", FooToBar) +- Baz(p) //@rename("Foo", "Baz", FooToBaz) +-} +- +--- @RTor/instances/type.go -- +-package instances +- +-type r[P any] struct { //@rename("R", "u", Rtou) +- Next *r[P] //@rename("R", "s", RTos) +-} +- +-func (rv r[P]) Do(r[P]) r[P] { //@rename("Do", "Do1", DoToDo1) +- var x r[P] +- return rv.Do(x) //@rename("Do", "Do2", DoToDo2) +-} +- +-func _() { +- var x r[int] //@rename("R", "r", RTor) +- x = x.Do(x) +-} +- +diff -urN a/gopls/internal/regtest/marker/testdata/rename/issue60789.txt b/gopls/internal/regtest/marker/testdata/rename/issue60789.txt +--- a/gopls/internal/regtest/marker/testdata/rename/issue60789.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/rename/issue60789.txt 1970-01-01 08:00:00 +@@ -1,36 +0,0 @@ +- +-This test renames an exported method of an unexported type, +-which is an edge case for objectpath, since it computes a path +-from a syntax package that is no good when applied to an +-export data package. +- +-See issue #60789. +- +--- go.mod -- +-module example.com +-go 1.12 +- +--- a/a.go -- +-package a +- +-type unexported int +-func (unexported) F() {} //@rename("F", "G", fToG) +- +-var _ = unexported(0).F +- +--- b/b.go -- +-package b +- +-// The existence of this package is sufficient to exercise +-// the bug even though it cannot reference a.unexported. +- +-import _ "example.com/a" +- +--- @fToG/a/a.go -- +-package a +- +-type unexported int +-func (unexported) G() {} //@rename("F", "G", fToG) +- +-var _ = unexported(0).G +- +diff -urN a/gopls/internal/regtest/marker/testdata/rename/issue61294.txt b/gopls/internal/regtest/marker/testdata/rename/issue61294.txt +--- a/gopls/internal/regtest/marker/testdata/rename/issue61294.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/rename/issue61294.txt 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +- +-This test renames a parameter var whose name is the same as a +-package-level var, which revealed a bug in isLocal. +- +-This is a regression test for issue #61294. +- +--- go.mod -- +-module example.com +-go 1.18 +- +--- a/a.go -- +-package a +- +-func One() +- +-func Two(One int) //@rename("One", "Three", OneToThree) +- +--- b/b.go -- +-package b +- +-import _ "example.com/a" +- +--- @OneToThree/a/a.go -- +-package a +- +-func One() +- +-func Two(Three int) //@rename("One", "Three", OneToThree) +- +diff -urN a/gopls/internal/regtest/marker/testdata/rename/issue61640.txt b/gopls/internal/regtest/marker/testdata/rename/issue61640.txt +--- a/gopls/internal/regtest/marker/testdata/rename/issue61640.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/rename/issue61640.txt 1970-01-01 08:00:00 +@@ -1,47 +0,0 @@ +-This test verifies that gopls can rename instantiated fields. +- +--- flags -- +--min_go=go1.18 +- +--- a.go -- +-package a +- +-// This file is adapted from the example in the issue. +- +-type builder[S ~[]int] struct { +- elements S //@rename("elements", "elements2", OneToTwo) +-} +- +-type BuilderImpl[S ~[]int] struct{ builder[S] } +- +-func NewBuilderImpl[S ~[]int](name string) *BuilderImpl[S] { +- impl := &BuilderImpl[S]{ +- builder[S]{ +- elements: S{}, +- }, +- } +- +- _ = impl.elements +- return impl +-} +--- @OneToTwo/a.go -- +-package a +- +-// This file is adapted from the example in the issue. +- +-type builder[S ~[]int] struct { +- elements2 S //@rename("elements", "elements2", OneToTwo) +-} +- +-type BuilderImpl[S ~[]int] struct{ builder[S] } +- +-func NewBuilderImpl[S ~[]int](name string) *BuilderImpl[S] { +- impl := &BuilderImpl[S]{ +- builder[S]{ +- elements2: S{}, +- }, +- } +- +- _ = impl.elements2 +- return impl +-} +diff -urN a/gopls/internal/regtest/marker/testdata/rename/issue61813.txt b/gopls/internal/regtest/marker/testdata/rename/issue61813.txt +--- a/gopls/internal/regtest/marker/testdata/rename/issue61813.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/rename/issue61813.txt 1970-01-01 08:00:00 +@@ -1,18 +0,0 @@ +-This test exercises the panic reported in golang/go#61813. +- +--- p.go -- +-package p +- +-type P struct{} +- +-func (P) M() {} //@rename("M", "N", MToN) +- +-var x = []*P{{}} +--- @MToN/p.go -- +-package p +- +-type P struct{} - --var _ = new(B).A2 //@renameerr("A", A4, errAnonField) +-func (P) N() {} //@rename("M", "N", MToN) - +-var x = []*P{{}} diff -urN a/gopls/internal/regtest/marker/testdata/rename/methods.txt b/gopls/internal/regtest/marker/testdata/rename/methods.txt --- a/gopls/internal/regtest/marker/testdata/rename/methods.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/rename/methods.txt 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/marker/testdata/rename/methods.txt 1970-01-01 08:00:00 @@ -1,67 +0,0 @@ -This test exercises renaming of interface methods. - @@ -114131,7 +126384,7 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/methods.txt b/gopls/in - -type A int - --func (A) F() {} //@renameerr("F", G, errAfToG) +-func (A) F() {} //@renameerr("F", "G", errAfToG) - --- b/b.go -- -package b @@ -114139,7 +126392,7 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/methods.txt b/gopls/in -import "example.com/a" -import "example.com/c" - --type B interface { F() } //@rename("F", G, BfToG) +-type B interface { F() } //@rename("F", "G", BfToG) - -var _ B = a.A(0) -var _ B = c.C(0) @@ -114149,7 +126402,7 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/methods.txt b/gopls/in - -type C int - --func (C) F() {} //@renameerr("F", G, errCfToG) +-func (C) F() {} //@renameerr("F", "G", errCfToG) - --- d/d.go -- -package d @@ -114168,7 +126421,7 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/methods.txt b/gopls/in -import "example.com/a" -import "example.com/c" - --type B interface { G() } //@rename("F", G, BfToG) +-type B interface { G() } //@rename("F", "G", BfToG) - -var _ B = a.A(0) -var _ B = c.C(0) @@ -114186,7 +126439,7 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/methods.txt b/gopls/in -b/b.go:6:20: (rename example.com/b.B.F if you intend to change both types) diff -urN a/gopls/internal/regtest/marker/testdata/rename/typeswitch.txt b/gopls/internal/regtest/marker/testdata/rename/typeswitch.txt --- a/gopls/internal/regtest/marker/testdata/rename/typeswitch.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/rename/typeswitch.txt 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/marker/testdata/rename/typeswitch.txt 1970-01-01 08:00:00 @@ -1,26 +0,0 @@ -This test covers the special case of renaming a type switch var. - @@ -114194,11 +126447,11 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/typeswitch.txt b/gopls -package p - -func _(x interface{}) { -- switch y := x.(type) { //@rename("y", z, yToZ) +- switch y := x.(type) { //@rename("y", "z", yToZ) - case string: -- print(y) //@rename("y", z, yToZ) +- print(y) //@rename("y", "z", yToZ) - default: -- print(y) //@rename("y", z, yToZ) +- print(y) //@rename("y", "z", yToZ) - } -} - @@ -114206,17 +126459,46 @@ diff -urN a/gopls/internal/regtest/marker/testdata/rename/typeswitch.txt b/gopls -package p - -func _(x interface{}) { -- switch z := x.(type) { //@rename("y", z, yToZ) +- switch z := x.(type) { //@rename("y", "z", yToZ) - case string: -- print(z) //@rename("y", z, yToZ) +- print(z) //@rename("y", "z", yToZ) - default: -- print(z) //@rename("y", z, yToZ) +- print(z) //@rename("y", "z", yToZ) - } -} - +diff -urN a/gopls/internal/regtest/marker/testdata/rename/unexported.txt b/gopls/internal/regtest/marker/testdata/rename/unexported.txt +--- a/gopls/internal/regtest/marker/testdata/rename/unexported.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/rename/unexported.txt 1970-01-01 08:00:00 +@@ -1,25 +0,0 @@ +- +-This test attempts to rename a.S.X to x, which would make it +-inaccessible from its external test package. The rename tool +-should report an error rather than wrecking the program. +-See issue #59403. +- +--- go.mod -- +-module example.com +-go 1.12 +- +--- a/a.go -- +-package a +- +-var S struct{ X int } //@renameerr("X", "x", oops) +- +--- a/a_test.go -- +-package a_test +- +-import "example.com/a" +- +-var Y = a.S.X +- +--- @oops -- +-a/a.go:3:15: renaming "X" to "x" would make it unexported +-a/a_test.go:5:13: breaking references from packages such as "example.com/a_test" diff -urN a/gopls/internal/regtest/marker/testdata/stubmethods/basic.txt b/gopls/internal/regtest/marker/testdata/stubmethods/basic.txt --- a/gopls/internal/regtest/marker/testdata/stubmethods/basic.txt 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/marker/testdata/stubmethods/basic.txt 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/marker/testdata/stubmethods/basic.txt 1970-01-01 08:00:00 @@ -1,24 +0,0 @@ -This test exercises basic 'stub methods' functionality. - @@ -114229,22 +126511,648 @@ diff -urN a/gopls/internal/regtest/marker/testdata/stubmethods/basic.txt b/gopls - -type C int - --var _ error = C(0) //@suggestedfix(re"C.0.", re"missing method Error", "refactor.rewrite", stub) +-var _ error = C(0) //@suggestedfix(re"C.0.", re"missing method Error", "quickfix", stub) - --- @stub/a/a.go -- -package a - -type C int - --// Error implements error +-// Error implements error. +-func (C) Error() string { +- panic("unimplemented") +-} +- +-var _ error = C(0) //@suggestedfix(re"C.0.", re"missing method Error", "quickfix", stub) +diff -urN a/gopls/internal/regtest/marker/testdata/stubmethods/issue61693.txt b/gopls/internal/regtest/marker/testdata/stubmethods/issue61693.txt +--- a/gopls/internal/regtest/marker/testdata/stubmethods/issue61693.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/stubmethods/issue61693.txt 1970-01-01 08:00:00 +@@ -1,35 +0,0 @@ +-This test exercises stub methods functionality with variadic parameters. +- +-In golang/go#61693 stubmethods was panicking in this case. +- +--- go.mod -- +-module mod.com +- +-go 1.18 +--- main.go -- +-package main +- +-type C int +- +-func F(err ...error) {} +- +-func _() { +- var x error +- F(x, C(0)) //@suggestedfix(re"C.0.", re"missing method Error", "quickfix", stub) +-} +--- @stub/main.go -- +-package main +- +-type C int +- +-// Error implements error. -func (C) Error() string { - panic("unimplemented") -} - --var _ error = C(0) //@suggestedfix(re"C.0.", re"missing method Error", "refactor.rewrite", stub) +-func F(err ...error) {} +- +-func _() { +- var x error +- F(x, C(0)) //@suggestedfix(re"C.0.", re"missing method Error", "quickfix", stub) +-} +diff -urN a/gopls/internal/regtest/marker/testdata/stubmethods/issue61830.txt b/gopls/internal/regtest/marker/testdata/stubmethods/issue61830.txt +--- a/gopls/internal/regtest/marker/testdata/stubmethods/issue61830.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/stubmethods/issue61830.txt 1970-01-01 08:00:00 +@@ -1,36 +0,0 @@ +-This test verifies that method stubbing qualifies types relative to the current +-package. +- +--- p.go -- +-package p +- +-import "io" +- +-type B struct{} +- +-type I interface { +- M(io.Reader, B) +-} +- +-type A struct{} +- +-var _ I = &A{} //@suggestedfix(re"&A..", re"missing method M", "quickfix", stub) +--- @stub/p.go -- +-package p +- +-import "io" +- +-type B struct{} +- +-type I interface { +- M(io.Reader, B) +-} +- +-type A struct{} +- +-// M implements I. +-func (*A) M(io.Reader, B) { +- panic("unimplemented") +-} +- +-var _ I = &A{} //@suggestedfix(re"&A..", re"missing method M", "quickfix", stub) +diff -urN a/gopls/internal/regtest/marker/testdata/symbol/basic.txt b/gopls/internal/regtest/marker/testdata/symbol/basic.txt +--- a/gopls/internal/regtest/marker/testdata/symbol/basic.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/symbol/basic.txt 1970-01-01 08:00:00 +@@ -1,114 +0,0 @@ +-Basic tests of textDocument/documentSymbols. +- +--- symbol.go -- +-package main +- +-//@symbol(want) +- +-import "io" +- +-var _ = 1 +- +-var x = 42 +- +-var nested struct { +- nestedField struct { +- f int +- } +-} +- +-const y = 43 +- +-type Number int +- +-type Alias = string +- +-type NumberAlias = Number +- +-type ( +- Boolean bool +- BoolAlias = bool +-) +- +-type Foo struct { +- Quux +- W io.Writer +- Bar int +- baz string +- funcField func(int) int +-} +- +-type Quux struct { +- X, Y float64 +-} +- +-type EmptyStruct struct{} +- +-func (f Foo) Baz() string { +- return f.baz +-} +- +-func _() {} +- +-func (q *Quux) Do() {} +- +-func main() { +-} +- +-type Stringer interface { +- String() string +-} +- +-type ABer interface { +- B() +- A() string +-} +- +-type WithEmbeddeds interface { +- Do() +- ABer +- io.Writer +-} +- +-type EmptyInterface interface{} +- +-func Dunk() int { return 0 } +- +-func dunk() {} +- +--- @want -- +-(*Quux).Do "func()" +-(Foo).Baz "func() string" +2 lines +-ABer "interface{...}" +3 lines +-ABer.A "func() string" +-ABer.B "func()" +-Alias "string" +-BoolAlias "bool" +-Boolean "bool" +-Dunk "func() int" +-EmptyInterface "interface{}" +-EmptyStruct "struct{}" +-Foo "struct{...}" +6 lines +-Foo.Bar "int" +-Foo.Quux "Quux" +-Foo.W "io.Writer" +-Foo.baz "string" +-Foo.funcField "func(int) int" +-Number "int" +-NumberAlias "Number" +-Quux "struct{...}" +2 lines +-Quux.X "float64" +-Quux.Y "float64" +-Stringer "interface{...}" +2 lines +-Stringer.String "func() string" +-WithEmbeddeds "interface{...}" +4 lines +-WithEmbeddeds.ABer "ABer" +-WithEmbeddeds.Do "func()" +-WithEmbeddeds.Writer "io.Writer" +-dunk "func()" +-main "func()" +1 lines +-nested "struct{...}" +4 lines +-nested.nestedField "struct{...}" +2 lines +-nested.nestedField.f "int" +-x "" +-y "" +diff -urN a/gopls/internal/regtest/marker/testdata/symbol/generic.txt b/gopls/internal/regtest/marker/testdata/symbol/generic.txt +--- a/gopls/internal/regtest/marker/testdata/symbol/generic.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/symbol/generic.txt 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +-Basic tests of textDocument/documentSymbols with generics. +- +--- flags -- +--min_go=go1.18 +- +--- symbol.go -- +-//@symbol(want) +- +-//go:build go1.18 +-// +build go1.18 +- +-package main +- +-type T[P any] struct { +- F P +-} +- +-type Constraint interface { +- ~int | struct{ int } +- interface{ M() } +-} +- +--- @want -- +-Constraint "interface{...}" +3 lines +-Constraint.interface{...} "" +-Constraint.interface{...}.M "func()" +-Constraint.~int | struct{int} "" +-T "struct{...}" +2 lines +-T.F "P" +diff -urN a/gopls/internal/regtest/marker/testdata/typedef/typedef.txt b/gopls/internal/regtest/marker/testdata/typedef/typedef.txt +--- a/gopls/internal/regtest/marker/testdata/typedef/typedef.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/typedef/typedef.txt 1970-01-01 08:00:00 +@@ -1,68 +0,0 @@ +-This test exercises the textDocument/typeDefinition action. +- +--- typedef.go -- +-package typedef +- +-type Struct struct { //@loc(Struct, "Struct"), +- Field string +-} +- +-type Int int //@loc(Int, "Int") +- +-func _() { +- var ( +- value Struct +- point *Struct +- ) +- _ = value //@typedef("value", Struct) +- _ = point //@typedef("point", Struct) +- +- var ( +- array [3]Struct +- slice []Struct +- ch chan Struct +- complex [3]chan *[5][]Int +- ) +- _ = array //@typedef("array", Struct) +- _ = slice //@typedef("slice", Struct) +- _ = ch //@typedef("ch", Struct) +- _ = complex //@typedef("complex", Int) +- +- var s struct { +- x struct { +- xx struct { +- field1 []Struct +- field2 []Int +- } +- } +- } +- _ = s.x.xx.field1 //@typedef("field1", Struct) +- _ = s.x.xx.field2 //@typedef("field2", Int) +-} +- +-func F1() Int { return 0 } +-func F2() (Int, float64) { return 0, 0 } +-func F3() (Struct, int, bool, error) { return Struct{}, 0, false, nil } +-func F4() (**int, Int, bool, *error) { return nil, 0, false, nil } +-func F5() (int, float64, error, Struct) { return 0, 0, nil, Struct{} } +-func F6() (int, float64, ***Struct, error) { return 0, 0, nil, nil } +- +-func _() { +- F1() //@typedef("F1", Int) +- F2() //@typedef("F2", Int) +- F3() //@typedef("F3", Struct) +- F4() //@typedef("F4", Int) +- F5() //@typedef("F5", Struct) +- F6() //@typedef("F6", Struct) +- +- f := func() Int { return 0 } +- f() //@typedef("f", Int) +-} +- +-// https://github.com/golang/go/issues/38589#issuecomment-620350922 +-func _() { +- type myFunc func(int) Int //@loc(myFunc, "myFunc") +- +- var foo myFunc +- _ = foo() //@typedef("foo", myFunc), diag(")", re"not enough arguments") +-} +diff -urN a/gopls/internal/regtest/marker/testdata/workspacesymbol/allscope.txt b/gopls/internal/regtest/marker/testdata/workspacesymbol/allscope.txt +--- a/gopls/internal/regtest/marker/testdata/workspacesymbol/allscope.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/workspacesymbol/allscope.txt 1970-01-01 08:00:00 +@@ -1,30 +0,0 @@ +-This test verifies behavior when "symbolScope" is set to "all". +- +--- settings.json -- +-{ +- "symbolStyle": "full", +- "symbolMatcher": "casesensitive", +- "symbolScope": "all" +-} +- +--- go.mod -- +-module mod.test/symbols +- +-go 1.18 +- +--- query.go -- +-package symbols +- +-//@workspacesymbol("fmt.Println", println) +- +--- fmt/fmt.go -- +-package fmt +- +-import "fmt" +- +-func Println(s string) { +- fmt.Println(s) +-} +--- @println -- +-fmt/fmt.go:5:6-13 mod.test/symbols/fmt.Println Function +-<unknown> fmt.Println Function +diff -urN a/gopls/internal/regtest/marker/testdata/workspacesymbol/caseinsensitive.txt b/gopls/internal/regtest/marker/testdata/workspacesymbol/caseinsensitive.txt +--- a/gopls/internal/regtest/marker/testdata/workspacesymbol/caseinsensitive.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/workspacesymbol/caseinsensitive.txt 1970-01-01 08:00:00 +@@ -1,26 +0,0 @@ +-This file contains test for symbol matches using the caseinsensitive matcher. +- +--- settings.json -- +-{ +- "symbolMatcher": "caseinsensitive" +-} +- +--- go.mod -- +-module mod.test/caseinsensitive +- +-go 1.18 +- +--- p.go -- +-package caseinsensitive +- +-//@workspacesymbol("", blank) +-//@workspacesymbol("randomgophervar", randomgophervar) +- +-var RandomGopherVariableA int +-var randomgopherVariableB int +-var RandomGopherOtherVariable int +- +--- @blank -- +--- @randomgophervar -- +-p.go:6:5-26 RandomGopherVariableA Variable +-p.go:7:5-26 randomgopherVariableB Variable +diff -urN a/gopls/internal/regtest/marker/testdata/workspacesymbol/casesensitive.txt b/gopls/internal/regtest/marker/testdata/workspacesymbol/casesensitive.txt +--- a/gopls/internal/regtest/marker/testdata/workspacesymbol/casesensitive.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/workspacesymbol/casesensitive.txt 1970-01-01 08:00:00 +@@ -1,116 +0,0 @@ +-This file contains tests for symbol matches using the casesensitive matcher. +- +-For historical reasons, it also verifies general behavior of the symbol search. +- +--- settings.json -- +-{ +- "symbolMatcher": "casesensitive" +-} +- +--- go.mod -- +-module mod.test/casesensitive +- +-go 1.18 +- +--- main.go -- +-package main +- +-//@workspacesymbol("main.main", main) +-//@workspacesymbol("p.Message", Message) +-//@workspacesymbol("main.myvar", myvar) +-//@workspacesymbol("main.myType", myType) +-//@workspacesymbol("main.myType.Blahblah", blahblah) +-//@workspacesymbol("main.myStruct", myStruct) +-//@workspacesymbol("main.myStruct.myStructField", myStructField) +-//@workspacesymbol("main.myInterface", myInterface) +-//@workspacesymbol("main.myInterface.DoSomeCoolStuff", DoSomeCoolStuff) +-//@workspacesymbol("main.embed.myStruct", embeddedStruct) +-//@workspacesymbol("main.embed.nestedStruct.nestedStruct2.int", int) +-//@workspacesymbol("main.embed.nestedInterface.myInterface", nestedInterface) +-//@workspacesymbol("main.embed.nestedInterface.nestedMethod", nestedMethod) +-//@workspacesymbol("dunk", dunk) +-//@workspacesymbol("Dunk", Dunk) +- +-import ( +- "encoding/json" +- "fmt" +-) +- +-func main() { // function +- fmt.Println("Hello") +-} +- +-var myvar int // variable +- +-type myType string // basic type +- +-type myDecoder json.Decoder // to use the encoding/json import +- +-func (m *myType) Blahblah() {} // method +- +-type myStruct struct { // struct type +- myStructField int // struct field +-} +- +-type myInterface interface { // interface +- DoSomeCoolStuff() string // interface method +-} +- +-type embed struct { +- myStruct +- +- nestedStruct struct { +- nestedField int +- +- nestedStruct2 struct { +- int +- } +- } +- +- nestedInterface interface { +- myInterface +- nestedMethod() +- } +-} +- +-func Dunk() int { return 0 } +- +-func dunk() {} +- +--- p/p.go -- +-package p +- +-const Message = "Hello World." // constant +--- @DoSomeCoolStuff -- +-main.go:41:2-17 main.myInterface.DoSomeCoolStuff Method +--- @Dunk -- +-main.go:61:6-10 Dunk Function +--- @Message -- +-p/p.go:3:7-14 p.Message Constant +--- @blahblah -- +-main.go:34:18-26 main.myType.Blahblah Method +--- @dunk -- +-main.go:63:6-10 dunk Function +--- @int -- +-main.go:51:4-7 main.embed.nestedStruct.nestedStruct2.int Field +--- @main -- +-main.go:24:6-10 main.main Function +--- @myInterface -- +-main.go:40:6-17 main.myInterface Interface +-main.go:41:2-17 main.myInterface.DoSomeCoolStuff Method +--- @myStruct -- +-main.go:36:6-14 main.myStruct Struct +-main.go:37:2-15 main.myStruct.myStructField Field +--- @myStructField -- +-main.go:37:2-15 main.myStruct.myStructField Field +--- @myType -- +-main.go:30:6-12 main.myType Class +-main.go:34:18-26 main.myType.Blahblah Method +--- @myvar -- +-main.go:28:5-10 main.myvar Variable +--- @nestedInterface -- +-main.go:56:3-14 main.embed.nestedInterface.myInterface Interface +--- @nestedMethod -- +-main.go:57:3-15 main.embed.nestedInterface.nestedMethod Method +--- @embeddedStruct -- +-main.go:45:2-10 main.embed.myStruct Field +diff -urN a/gopls/internal/regtest/marker/testdata/workspacesymbol/issue44806.txt b/gopls/internal/regtest/marker/testdata/workspacesymbol/issue44806.txt +--- a/gopls/internal/regtest/marker/testdata/workspacesymbol/issue44806.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/workspacesymbol/issue44806.txt 1970-01-01 08:00:00 +@@ -1,27 +0,0 @@ +-This test verifies the fix for the crash encountered in golang/go#44806. +- +--- go.mod -- +-module mod.test/symbol +- +-go 1.18 +--- symbol.go -- +-package symbol +- +-//@workspacesymbol("m", m) +- +-type T struct{} +- +-// We should accept all valid receiver syntax when scanning symbols. +-func (*(T)) m1() {} +-func (*T) m2() {} +-func (T) m3() {} +-func ((T)) m4() {} +-func ((*T)) m5() {} +- +--- @m -- +-symbol.go:8:13-15 T.m1 Method +-symbol.go:9:11-13 T.m2 Method +-symbol.go:10:10-12 T.m3 Method +-symbol.go:11:12-14 T.m4 Method +-symbol.go:12:13-15 T.m5 Method +-symbol.go:5:6-7 symbol.T Struct +diff -urN a/gopls/internal/regtest/marker/testdata/workspacesymbol/workspacesymbol.txt b/gopls/internal/regtest/marker/testdata/workspacesymbol/workspacesymbol.txt +--- a/gopls/internal/regtest/marker/testdata/workspacesymbol/workspacesymbol.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/workspacesymbol/workspacesymbol.txt 1970-01-01 08:00:00 +@@ -1,72 +0,0 @@ +-This test contains tests for basic functionality of the workspace/symbol +-request. +- +-TODO(rfindley): add a test for the legacy 'fuzzy' symbol matcher using setting ("symbolMatcher": "fuzzy"). This test uses the default matcher ("fastFuzzy"). +- +--- go.mod -- +-module mod.test/symbols +- +-go 1.18 +- +--- query.go -- +-package symbols +- +-//@workspacesymbol("rgop", rgop) +-//@workspacesymbol("randoma", randoma) +-//@workspacesymbol("randomb", randomb) +- +--- a/a.go -- +-package a +- +-var RandomGopherVariableA = "a" +- +-const RandomGopherConstantA = "a" +- +-const ( +- randomgopherinvariable = iota +-) +- +--- a/a_test.go -- +-package a +- +-var RandomGopherTestVariableA = "a" +- +--- a/a_x_test.go -- +-package a_test +- +-var RandomGopherXTestVariableA = "a" +- +--- b/b.go -- +-package b +- +-var RandomGopherVariableB = "b" +- +-type RandomGopherStructB struct { +- Bar int +-} +- +--- @rgop -- +-b/b.go:5:6-25 RandomGopherStructB Struct +-a/a.go:5:7-28 RandomGopherConstantA Constant +-a/a.go:3:5-26 RandomGopherVariableA Variable +-b/b.go:3:5-26 RandomGopherVariableB Variable +-a/a_test.go:3:5-30 RandomGopherTestVariableA Variable +-a/a_x_test.go:3:5-31 RandomGopherXTestVariableA Variable +-a/a.go:8:2-24 randomgopherinvariable Constant +-b/b.go:6:2-5 RandomGopherStructB.Bar Field +--- @randoma -- +-a/a.go:5:7-28 RandomGopherConstantA Constant +-a/a.go:3:5-26 RandomGopherVariableA Variable +-b/b.go:3:5-26 RandomGopherVariableB Variable +-a/a.go:8:2-24 randomgopherinvariable Constant +-a/a_test.go:3:5-30 RandomGopherTestVariableA Variable +-a/a_x_test.go:3:5-31 RandomGopherXTestVariableA Variable +-b/b.go:6:2-5 RandomGopherStructB.Bar Field +--- @randomb -- +-b/b.go:5:6-25 RandomGopherStructB Struct +-a/a.go:3:5-26 RandomGopherVariableA Variable +-b/b.go:3:5-26 RandomGopherVariableB Variable +-a/a.go:8:2-24 randomgopherinvariable Constant +-a/a_test.go:3:5-30 RandomGopherTestVariableA Variable +-a/a_x_test.go:3:5-31 RandomGopherXTestVariableA Variable +-b/b.go:6:2-5 RandomGopherStructB.Bar Field +diff -urN a/gopls/internal/regtest/marker/testdata/workspacesymbol/wsscope.txt b/gopls/internal/regtest/marker/testdata/workspacesymbol/wsscope.txt +--- a/gopls/internal/regtest/marker/testdata/workspacesymbol/wsscope.txt 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/marker/testdata/workspacesymbol/wsscope.txt 1970-01-01 08:00:00 +@@ -1,29 +0,0 @@ +-This test verifies behavior when "symbolScope" is set to "workspace". +- +--- settings.json -- +-{ +- "symbolStyle": "full", +- "symbolMatcher": "casesensitive", +- "symbolScope": "workspace" +-} +- +--- go.mod -- +-module mod.test/symbols +- +-go 1.18 +- +--- query.go -- +-package symbols +- +-//@workspacesymbol("fmt.Println", println) +- +--- fmt/fmt.go -- +-package fmt +- +-import "fmt" +- +-func Println(s string) { +- fmt.Println(s) +-} +--- @println -- +-fmt/fmt.go:5:6-13 mod.test/symbols/fmt.Println Function 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/call_hierarchy_test.go 1970-01-01 08:00:00 @@ -1,35 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -114283,8 +127191,8 @@ diff -urN a/gopls/internal/regtest/misc/call_hierarchy_test.go b/gopls/internal/ -} 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 00:00:00.000000000 +0000 -@@ -1,159 +0,0 @@ ++++ b/gopls/internal/regtest/misc/configuration_test.go 1970-01-01 08:00:00 +@@ -1,153 +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. @@ -114329,10 +127237,8 @@ diff -urN a/gopls/internal/regtest/misc/configuration_test.go b/gopls/internal/r - cfg.Settings = map[string]interface{}{ - "staticcheck": true, - } -- // TODO(rfindley): support waiting on diagnostics following a configuration -- // change. - env.ChangeConfiguration(cfg) -- env.Await( +- env.AfterChange( - Diagnostics(env.AtRegexp("a/a.go", "var (FooErr)")), - ) - }) @@ -114344,9 +127250,7 @@ diff -urN a/gopls/internal/regtest/misc/configuration_test.go b/gopls/internal/r -// -// Gopls should not get confused about buffer content when recreating the view. -func TestMajorOptionsChange(t *testing.T) { -- t.Skip("broken due to golang/go#57934") -- -- testenv.NeedsGo1Point(t, 17) +- testenv.NeedsGo1Point(t, 19) // needs staticcheck - - const files = ` --- go.mod -- @@ -114378,10 +127282,8 @@ diff -urN a/gopls/internal/regtest/misc/configuration_test.go b/gopls/internal/r - cfg.Settings = map[string]interface{}{ - "staticcheck": true, - } -- // TODO(rfindley): support waiting on diagnostics following a configuration -- // change. - env.ChangeConfiguration(cfg) -- env.Await( +- env.AfterChange( - Diagnostics(env.AtRegexp("a/a.go", "var (FooErr)")), - ) - }) @@ -114446,7 +127348,7 @@ diff -urN a/gopls/internal/regtest/misc/configuration_test.go b/gopls/internal/r -} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/debugserver_test.go 1970-01-01 08:00:00 @@ -1,46 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -114496,8 +127398,8 @@ diff -urN a/gopls/internal/regtest/misc/debugserver_test.go b/gopls/internal/reg -} 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 00:00:00.000000000 +0000 -@@ -1,512 +0,0 @@ ++++ b/gopls/internal/regtest/misc/definition_test.go 1970-01-01 08:00:00 +@@ -1,571 +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. @@ -114851,7 +127753,7 @@ diff -urN a/gopls/internal/regtest/misc/definition_test.go b/gopls/internal/regt - Run(t, mod, func(t *testing.T, env *Env) { - env.OpenFile("main.go") - -- loc, err := env.Editor.GoToTypeDefinition(env.Ctx, env.RegexpSearch("main.go", tt.re)) +- loc, err := env.Editor.TypeDefinition(env.Ctx, env.RegexpSearch("main.go", tt.re)) - if tt.wantError { - if err == nil { - t.Fatal("expected error, got nil") @@ -114871,6 +127773,25 @@ diff -urN a/gopls/internal/regtest/misc/definition_test.go b/gopls/internal/regt - } -} - +-func TestGoToTypeDefinition_Issue60544(t *testing.T) { +- const mod = ` +--- go.mod -- +-module mod.com +- +-go 1.19 +--- main.go -- +-package main +- +-func F[T comparable]() {} +-` +- +- Run(t, mod, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- +- _ = env.TypeDefinition(env.RegexpSearch("main.go", "comparable")) // must not panic +- }) +-} +- -// Test for golang/go#47825. -func TestImportTestVariant(t *testing.T) { - const mod = ` @@ -114976,7 +127897,7 @@ diff -urN a/gopls/internal/regtest/misc/definition_test.go b/gopls/internal/regt - } - - // Run 'go mod vendor' outside the editor. -- if err := env.Sandbox.RunGoCommand(env.Ctx, ".", "mod", []string{"vendor"}, true); err != nil { +- if err := env.Sandbox.RunGoCommand(env.Ctx, ".", "mod", []string{"vendor"}, nil, true); err != nil { - t.Fatalf("go mod vendor: %v", err) - } - @@ -115010,9 +127931,49 @@ diff -urN a/gopls/internal/regtest/misc/definition_test.go b/gopls/internal/regt - } - }) -} +- +-const embedDefinition = ` +--- go.mod -- +-module mod.com +- +--- main.go -- +-package main +- +-import ( +- "embed" +-) +- +-//go:embed *.txt +-var foo embed.FS +- +-func main() {} +- +--- skip.sql -- +-SKIP +- +--- foo.txt -- +-FOO +- +--- skip.bat -- +-SKIP +-` +- +-func TestGoToEmbedDefinition(t *testing.T) { +- Run(t, embedDefinition, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- +- start := env.RegexpSearch("main.go", `\*.txt`) +- loc := env.GoToDefinition(start) +- +- name := env.Sandbox.Workdir.URIToPath(loc.URI) +- if want := "foo.txt"; name != want { +- t.Errorf("GoToDefinition: got file %q, want %q", name, want) +- } +- }) +-} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/embed_test.go 1970-01-01 08:00:00 @@ -1,40 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -115056,7 +128017,7 @@ diff -urN a/gopls/internal/regtest/misc/embed_test.go b/gopls/internal/regtest/m -} diff -urN a/gopls/internal/regtest/misc/extract_test.go b/gopls/internal/regtest/misc/extract_test.go --- a/gopls/internal/regtest/misc/extract_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/misc/extract_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/extract_test.go 1970-01-01 08:00:00 @@ -1,65 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -115125,8 +128086,8 @@ diff -urN a/gopls/internal/regtest/misc/extract_test.go b/gopls/internal/regtest -} 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 00:00:00.000000000 +0000 -@@ -1,84 +0,0 @@ ++++ b/gopls/internal/regtest/misc/failures_test.go 1970-01-01 08:00:00 +@@ -1,82 +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. @@ -115144,7 +128105,6 @@ diff -urN a/gopls/internal/regtest/misc/failures_test.go b/gopls/internal/regtes -// that includes a line directive, which makes no difference since -// gopls ignores line directives. -func TestHoverFailure(t *testing.T) { -- t.Skip("line directives //line ") - const mod = ` --- go.mod -- -module mod.com @@ -115177,7 +128137,6 @@ diff -urN a/gopls/internal/regtest/misc/failures_test.go b/gopls/internal/regtes -// This test demonstrates a case where gopls is not at all confused by -// line directives, because it completely ignores them. -func TestFailingDiagnosticClearingOnEdit(t *testing.T) { -- t.Skip("line directives //line ") - // badPackageDup contains a duplicate definition of the 'a' const. - // This is a minor variant of TestDiagnosticClearingOnEdit from - // diagnostics_test.go, with a line directive, which makes no difference. @@ -115213,7 +128172,7 @@ diff -urN a/gopls/internal/regtest/misc/failures_test.go b/gopls/internal/regtes -} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/fix_test.go 1970-01-01 08:00:00 @@ -1,103 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -115320,8 +128279,8 @@ diff -urN a/gopls/internal/regtest/misc/fix_test.go b/gopls/internal/regtest/mis -} 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 00:00:00.000000000 +0000 -@@ -1,368 +0,0 @@ ++++ b/gopls/internal/regtest/misc/formatting_test.go 1970-01-01 08:00:00 +@@ -1,395 +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. @@ -115690,10 +128649,37 @@ diff -urN a/gopls/internal/regtest/misc/formatting_test.go b/gopls/internal/regt - } - }) -} +- +-func TestGofumpt_Issue61692(t *testing.T) { +- testenv.NeedsGo1Point(t, 21) +- +- const input = ` +--- go.mod -- +-module foo +- +-go 1.21rc3 +--- foo.go -- +-package foo +- +-func _() { +- foo := +- "bar" +-} +-` +- +- WithOptions( +- Settings{ +- "gofumpt": true, +- }, +- ).Run(t, input, func(t *testing.T, env *Env) { +- env.OpenFile("foo.go") +- env.FormatBuffer("foo.go") // golang/go#61692: must not panic +- }) +-} 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 00:00:00.000000000 +0000 -@@ -1,72 +0,0 @@ ++++ b/gopls/internal/regtest/misc/generate_test.go 1970-01-01 08:00:00 +@@ -1,71 +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. @@ -115723,12 +128709,11 @@ diff -urN a/gopls/internal/regtest/misc/generate_test.go b/gopls/internal/regtes -package main - -import ( -- "io/ioutil" - "os" -) - -func main() { -- ioutil.WriteFile("generated.go", []byte("package " + os.Args[1] + "\n\nconst Answer = 21"), 0644) +- os.WriteFile("generated.go", []byte("package " + os.Args[1] + "\n\nconst Answer = 21"), 0644) -} - --- lib1/lib.go -- @@ -115768,7 +128753,7 @@ diff -urN a/gopls/internal/regtest/misc/generate_test.go b/gopls/internal/regtes -} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/highlight_test.go 1970-01-01 08:00:00 @@ -1,153 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -115925,8 +128910,8 @@ diff -urN a/gopls/internal/regtest/misc/highlight_test.go b/gopls/internal/regte -} 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 00:00:00.000000000 +0000 -@@ -1,384 +0,0 @@ ++++ b/gopls/internal/regtest/misc/hover_test.go 1970-01-01 08:00:00 +@@ -1,493 +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. @@ -116013,12 +128998,6 @@ diff -urN a/gopls/internal/regtest/misc/hover_test.go b/gopls/internal/regtest/m -} - -func TestHoverIntLiteral(t *testing.T) { -- // TODO(rfindley): this behavior doesn't actually make sense for vars. It is -- // misleading to format their value when it is (of course) variable. -- // -- // Instead, we should allow hovering on numeric literals. -- t.Skip("golang/go#58220: broken due to new hover logic") -- - const source = ` --- main.go -- -package main @@ -116035,13 +129014,13 @@ diff -urN a/gopls/internal/regtest/misc/hover_test.go b/gopls/internal/regtest/m - Run(t, source, func(t *testing.T, env *Env) { - env.OpenFile("main.go") - hexExpected := "58190" -- got, _ := env.Hover(env.RegexpSearch("main.go", "hex")) +- got, _ := env.Hover(env.RegexpSearch("main.go", "0xe")) - 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(env.RegexpSearch("main.go", "bigBin")) +- got, _ = env.Hover(env.RegexpSearch("main.go", "0b1")) - if got != nil && !strings.Contains(got.Value, binExpected) { - t.Errorf("Hover: missing expected field '%s'. Got:\n%q", binExpected, got.Value) - } @@ -116311,10 +129290,262 @@ diff -urN a/gopls/internal/regtest/misc/hover_test.go b/gopls/internal/regtest/m - }) - } -} +- +-const linknameHover = ` +--- go.mod -- +-module mod.com +- +--- upper/upper.go -- +-package upper +- +-import ( +- _ "unsafe" +- _ "mod.com/lower" +-) +- +-//go:linkname foo mod.com/lower.bar +-func foo() string +- +--- lower/lower.go -- +-package lower +- +-// bar does foo. +-func bar() string { +- return "foo by bar" +-}` +- +-func TestHoverLinknameDirective(t *testing.T) { +- Run(t, linknameHover, func(t *testing.T, env *Env) { +- // Jump from directives 2nd arg. +- env.OpenFile("upper/upper.go") +- from := env.RegexpSearch("upper/upper.go", `lower.bar`) +- +- hover, _ := env.Hover(from) +- content := hover.Value +- +- expect := "bar does foo" +- if !strings.Contains(content, expect) { +- t.Errorf("hover: %q does not contain: %q", content, expect) +- } +- }) +-} +- +-func TestHoverGoWork_Issue60821(t *testing.T) { +- const files = ` +--- go.work -- +-go 1.19 +- +-use ( +- moda +- modb +-) +--- moda/go.mod -- +- +-` +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("go.work") +- // Neither of the requests below should crash gopls. +- _, _, _ = env.Editor.Hover(env.Ctx, env.RegexpSearch("go.work", "moda")) +- _, _, _ = env.Editor.Hover(env.Ctx, env.RegexpSearch("go.work", "modb")) +- }) +-} +- +-const embedHover = ` +--- go.mod -- +-module mod.com +-go 1.19 +--- main.go -- +-package main +- +-import "embed" +- +-//go:embed *.txt +-var foo embed.FS +- +-func main() { +-} +--- foo.txt -- +-FOO +--- bar.txt -- +-BAR +--- baz.txt -- +-BAZ +--- other.sql -- +-SKIPPED +--- dir.txt/skip.txt -- +-SKIPPED +-` +- +-func TestHoverEmbedDirective(t *testing.T) { +- testenv.NeedsGo1Point(t, 19) +- Run(t, embedHover, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- from := env.RegexpSearch("main.go", `\*.txt`) +- +- got, _ := env.Hover(from) +- if got == nil { +- t.Fatalf("hover over //go:embed arg not found") +- } +- content := got.Value +- +- wants := []string{"foo.txt", "bar.txt", "baz.txt"} +- for _, want := range wants { +- if !strings.Contains(content, want) { +- t.Errorf("hover: %q does not contain: %q", content, want) +- } +- } +- +- // A directory should never be matched, even if it happens to have a matching name. +- // Content in subdirectories should not match on only one asterisk. +- skips := []string{"other.sql", "dir.txt", "skip.txt"} +- for _, skip := range skips { +- if strings.Contains(content, skip) { +- t.Errorf("hover: %q should not contain: %q", content, skip) +- } +- } +- }) +-} +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 08:00:00 +@@ -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/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +- "golang.org/x/tools/gopls/internal/lsp/tests/compare" +-) +- +-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.BufferText("main.go") +- if got != want { +- t.Fatalf("gopls.add_import failed\n%s", compare.Text(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 00:00:00.000000000 +0000 -@@ -1,258 +0,0 @@ ++++ b/gopls/internal/regtest/misc/imports_test.go 1970-01-01 08:00:00 +@@ -1,286 +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. @@ -116322,13 +129553,13 @@ diff -urN a/gopls/internal/regtest/misc/imports_test.go b/gopls/internal/regtest -package misc - -import ( -- "io/ioutil" - "os" - "path/filepath" - "strings" - "testing" - - . "golang.org/x/tools/gopls/internal/lsp/regtest" +- "golang.org/x/tools/gopls/internal/lsp/tests/compare" - - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/internal/testenv" @@ -116371,6 +129602,34 @@ diff -urN a/gopls/internal/regtest/misc/imports_test.go b/gopls/internal/regtest - }) -} - +-func TestIssue59124(t *testing.T) { +- const stuff = ` +--- go.mod -- +-module foo +-go 1.19 +--- a.go -- +-//line foo.y:102 +-package main +- +-import "fmt" +- +-//this comment is necessary for failure +-func a() { +- fmt.Println("hello") +-} +-` +- Run(t, stuff, func(t *testing.T, env *Env) { +- env.OpenFile("a.go") +- was := env.BufferText("a.go") +- env.Await(NoDiagnostics()) +- env.OrganizeImports("a.go") +- is := env.BufferText("a.go") +- if diff := compare.Text(was, is); diff != "" { +- t.Errorf("unexpected diff after organizeImports:\n%s", diff) +- } +- }) +-} +- -func TestVim1(t *testing.T) { - const vim1 = `package main - @@ -116463,7 +129722,7 @@ diff -urN a/gopls/internal/regtest/misc/imports_test.go b/gopls/internal/regtest - -var _, _ = x.X, y.Y -` -- modcache, err := ioutil.TempDir("", "TestGOMODCACHE-modcache") +- modcache, err := os.MkdirTemp("", "TestGOMODCACHE-modcache") - if err != nil { - t.Fatal(err) - } @@ -116573,239 +129832,9 @@ diff -urN a/gopls/internal/regtest/misc/imports_test.go b/gopls/internal/regtest - env.AfterChange(NoDiagnostics(ForFile("caller/caller.go"))) - }) -} -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 00:00:00.000000000 +0000 -@@ -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/gopls/internal/lsp/command" -- "golang.org/x/tools/gopls/internal/lsp/protocol" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/gopls/internal/lsp/tests/compare" --) -- --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.BufferText("main.go") -- if got != want { -- t.Fatalf("gopls.add_import failed\n%s", compare.Text(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/leak_test.go b/gopls/internal/regtest/misc/leak_test.go ---- a/gopls/internal/regtest/misc/leak_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/misc/leak_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,89 +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 ( -- "context" -- "testing" -- -- "github.com/google/go-cmp/cmp" -- "golang.org/x/tools/gopls/internal/hooks" -- "golang.org/x/tools/gopls/internal/lsp/cache" -- "golang.org/x/tools/gopls/internal/lsp/debug" -- "golang.org/x/tools/gopls/internal/lsp/fake" -- "golang.org/x/tools/gopls/internal/lsp/lsprpc" -- . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/internal/jsonrpc2" -- "golang.org/x/tools/internal/jsonrpc2/servertest" --) -- --// Test for golang/go#57222. --func TestCacheLeak(t *testing.T) { -- // TODO(rfindley): either fix this test with additional instrumentation, or -- // delete it. -- t.Skip("This test races with cache eviction.") -- const files = `-- a.go -- --package a -- --func _() { -- println("1") --} --` -- c := cache.New(nil) -- env := setupEnv(t, files, c) -- env.Await(InitialWorkspaceLoad) -- env.OpenFile("a.go") -- -- // Make a couple edits to stabilize cache state. -- // -- // For some reason, after only one edit we're left with two parsed files -- // (perhaps because something had to ParseHeader). If this test proves flaky, -- // we'll need to investigate exactly what is causing various parse modes to -- // be present (or rewrite the test to be more tolerant, for example make ~100 -- // modifications and assert that we're within a few of where we're started). -- env.RegexpReplace("a.go", "1", "2") -- env.RegexpReplace("a.go", "2", "3") -- env.AfterChange() -- -- // Capture cache state, make an arbitrary change, and wait for gopls to do -- // its work. Afterward, we should have the exact same number of parsed -- before := c.MemStats() -- env.RegexpReplace("a.go", "3", "4") -- env.AfterChange() -- after := c.MemStats() -- -- if diff := cmp.Diff(before, after); diff != "" { -- t.Errorf("store objects differ after change (-before +after)\n%s", diff) -- } --} -- --// setupEnv creates a new sandbox environment for editing the txtar encoded --// content of files. It uses a new gopls instance backed by the Cache c. --func setupEnv(t *testing.T, files string, c *cache.Cache) *Env { -- ctx := debug.WithInstance(context.Background(), "", "off") -- server := lsprpc.NewStreamServer(c, false, hooks.Options) -- ts := servertest.NewPipeServer(server, jsonrpc2.NewRawStream) -- s, err := fake.NewSandbox(&fake.SandboxConfig{ -- Files: fake.UnpackTxt(files), -- }) -- if err != nil { -- t.Fatal(err) -- } -- -- a := NewAwaiter(s.Workdir) -- const skipApplyEdits = false -- editor, err := fake.NewEditor(s, fake.EditorConfig{}).Connect(ctx, ts, a.Hooks(), skipApplyEdits) -- if err != nil { -- t.Fatal(err) -- } -- -- return &Env{ -- T: t, -- Ctx: ctx, -- Editor: editor, -- Sandbox: s, -- Awaiter: a, -- } --} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/link_test.go 1970-01-01 08:00:00 @@ -1,96 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -116871,11 +129900,11 @@ diff -urN a/gopls/internal/regtest/misc/link_test.go b/gopls/internal/regtest/mi - 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 { +- if len(links) != 1 || *links[0].Target != pkgLink { - t.Errorf("documentLink: got links %+v for main.go, want one link with target %q", links, pkgLink) - } - links = env.DocumentLink("go.mod") -- if len(links) != 1 || links[0].Target != modLink { +- if len(links) != 1 || *links[0].Target != modLink { - t.Errorf("documentLink: got links %+v for go.mod, want one link with target %q", links, modLink) - } - @@ -116905,7 +129934,7 @@ diff -urN a/gopls/internal/regtest/misc/link_test.go b/gopls/internal/regtest/mi -} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/misc_test.go 1970-01-01 08:00:00 @@ -1,18 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -116916,9 +129945,9 @@ diff -urN a/gopls/internal/regtest/misc/misc_test.go b/gopls/internal/regtest/mi -import ( - "testing" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/hooks" - "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/internal/bug" -) - -func TestMain(m *testing.M) { @@ -116927,7 +129956,7 @@ diff -urN a/gopls/internal/regtest/misc/misc_test.go b/gopls/internal/regtest/mi -} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/multiple_adhoc_test.go 1970-01-01 08:00:00 @@ -1,44 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -116973,10 +130002,245 @@ diff -urN a/gopls/internal/regtest/misc/multiple_adhoc_test.go b/gopls/internal/ - } - }) -} +diff -urN a/gopls/internal/regtest/misc/prompt_test.go b/gopls/internal/regtest/misc/prompt_test.go +--- a/gopls/internal/regtest/misc/prompt_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/misc/prompt_test.go 1970-01-01 08:00:00 +@@ -1,231 +0,0 @@ +-// Copyright 2023 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" +- "os" +- "path/filepath" +- "regexp" +- "testing" +- +- "golang.org/x/tools/gopls/internal/lsp" +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) +- +-// Test that gopls prompts for telemetry only when it is supposed to. +-func TestTelemetryPrompt_Conditions(t *testing.T) { +- const src = ` +--- go.mod -- +-module mod.com +- +-go 1.12 +--- main.go -- +-package main +- +-func main() { +-} +-` +- +- for _, enabled := range []bool{true, false} { +- t.Run(fmt.Sprintf("telemetryPrompt=%v", enabled), func(t *testing.T) { +- for _, initialMode := range []string{"", "off", "on"} { +- t.Run(fmt.Sprintf("initial_mode=%s", initialMode), func(t *testing.T) { +- modeFile := filepath.Join(t.TempDir(), "mode") +- if initialMode != "" { +- if err := os.WriteFile(modeFile, []byte(initialMode), 0666); err != nil { +- t.Fatal(err) +- } +- } +- WithOptions( +- Modes(Default), // no need to run this in all modes +- EnvVars{ +- lsp.GoplsConfigDirEnvvar: t.TempDir(), +- lsp.FakeTelemetryModefileEnvvar: modeFile, +- }, +- Settings{ +- "telemetryPrompt": enabled, +- }, +- ).Run(t, src, func(t *testing.T, env *Env) { +- wantPrompt := enabled && (initialMode == "" || initialMode == "off") +- expectation := ShownMessageRequest(".*Would you like to enable Go telemetry?") +- if !wantPrompt { +- expectation = Not(expectation) +- } +- env.OnceMet( +- CompletedWork(lsp.TelemetryPromptWorkTitle, 1, true), +- expectation, +- ) +- }) +- }) +- } +- }) +- } +-} +- +-// Test that responding to the telemetry prompt results in the expected state. +-func TestTelemetryPrompt_Response(t *testing.T) { +- const src = ` +--- go.mod -- +-module mod.com +- +-go 1.12 +--- main.go -- +-package main +- +-func main() { +-} +-` +- +- tests := []struct { +- response string // response to choose for the telemetry dialog +- wantMode string // resulting telemetry mode +- wantMsg string // substring contained in the follow-up popup (if empty, no popup is expected) +- }{ +- {lsp.TelemetryYes, "on", "uploading is now enabled"}, +- {lsp.TelemetryNo, "", ""}, +- {"", "", ""}, +- } +- for _, test := range tests { +- t.Run(fmt.Sprintf("response=%s", test.response), func(t *testing.T) { +- modeFile := filepath.Join(t.TempDir(), "mode") +- msgRE := regexp.MustCompile(".*Would you like to enable Go telemetry?") +- respond := func(m *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) { +- if msgRE.MatchString(m.Message) { +- for _, item := range m.Actions { +- if item.Title == test.response { +- return &item, nil +- } +- } +- if test.response != "" { +- t.Errorf("action item %q not found", test.response) +- } +- } +- return nil, nil +- } +- WithOptions( +- Modes(Default), // no need to run this in all modes +- EnvVars{ +- lsp.GoplsConfigDirEnvvar: t.TempDir(), +- lsp.FakeTelemetryModefileEnvvar: modeFile, +- }, +- Settings{ +- "telemetryPrompt": true, +- }, +- MessageResponder(respond), +- ).Run(t, src, func(t *testing.T, env *Env) { +- var postConditions []Expectation +- if test.wantMsg != "" { +- postConditions = append(postConditions, ShownMessage(test.wantMsg)) +- } +- env.OnceMet( +- CompletedWork(lsp.TelemetryPromptWorkTitle, 1, true), +- postConditions..., +- ) +- gotMode := "" +- if contents, err := os.ReadFile(modeFile); err == nil { +- gotMode = string(contents) +- } else if !os.IsNotExist(err) { +- t.Fatal(err) +- } +- if gotMode != test.wantMode { +- t.Errorf("after prompt, mode=%s, want %s", gotMode, test.wantMode) +- } +- }) +- }) +- } +-} +- +-// Test that we stop asking about telemetry after the user ignores the question +-// 5 times. +-func TestTelemetryPrompt_GivingUp(t *testing.T) { +- const src = ` +--- go.mod -- +-module mod.com +- +-go 1.12 +--- main.go -- +-package main +- +-func main() { +-} +-` +- +- // For this test, we want to share state across gopls sessions. +- modeFile := filepath.Join(t.TempDir(), "mode") +- configDir := t.TempDir() +- +- const maxPrompts = 5 // internal prompt limit defined by gopls +- +- for i := 0; i < maxPrompts+1; i++ { +- WithOptions( +- Modes(Default), // no need to run this in all modes +- EnvVars{ +- lsp.GoplsConfigDirEnvvar: configDir, +- lsp.FakeTelemetryModefileEnvvar: modeFile, +- }, +- Settings{ +- "telemetryPrompt": true, +- }, +- ).Run(t, src, func(t *testing.T, env *Env) { +- wantPrompt := i < maxPrompts +- expectation := ShownMessageRequest(".*Would you like to enable Go telemetry?") +- if !wantPrompt { +- expectation = Not(expectation) +- } +- env.OnceMet( +- CompletedWork(lsp.TelemetryPromptWorkTitle, 1, true), +- expectation, +- ) +- }) +- } +-} +- +-// Test that gopls prompts for telemetry only when it is supposed to. +-func TestTelemetryPrompt_Conditions2(t *testing.T) { +- const src = ` +--- go.mod -- +-module mod.com +- +-go 1.12 +--- main.go -- +-package main +- +-func main() { +-} +-` +- modeFile := filepath.Join(t.TempDir(), "mode") +- WithOptions( +- Modes(Default), // no need to run this in all modes +- EnvVars{ +- lsp.GoplsConfigDirEnvvar: t.TempDir(), +- lsp.FakeTelemetryModefileEnvvar: modeFile, +- }, +- Settings{ +- // off because we are testing +- // if we can trigger the prompt with command. +- "telemetryPrompt": false, +- }, +- ).Run(t, src, func(t *testing.T, env *Env) { +- cmd, err := command.NewMaybePromptForTelemetryCommand("prompt") +- if err != nil { +- t.Fatal(err) +- } +- var result error +- env.ExecuteCommand(&protocol.ExecuteCommandParams{ +- Command: cmd.Command, +- }, &result) +- if result != nil { +- t.Fatal(err) +- } +- expectation := ShownMessageRequest(".*Would you like to enable Go telemetry?") +- env.OnceMet( +- CompletedWork(lsp.TelemetryPromptWorkTitle, 2, true), +- expectation, +- ) +- }) +-} 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 00:00:00.000000000 +0000 -@@ -1,399 +0,0 @@ ++++ b/gopls/internal/regtest/misc/references_test.go 1970-01-01 08:00:00 +@@ -1,581 +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. @@ -116986,13 +130250,17 @@ diff -urN a/gopls/internal/regtest/misc/references_test.go b/gopls/internal/regt -import ( - "fmt" - "os" +- "path/filepath" +- "reflect" - "sort" - "strings" - "testing" - - "github.com/google/go-cmp/cmp" - "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/regtest" - . "golang.org/x/tools/gopls/internal/lsp/regtest" +- "golang.org/x/tools/internal/testenv" -) - -func TestStdlibReferences(t *testing.T) { @@ -117077,6 +130345,58 @@ diff -urN a/gopls/internal/regtest/misc/references_test.go b/gopls/internal/regt - }) -} - +-func TestDefsRefsBuiltins(t *testing.T) { +- testenv.NeedsGo1Point(t, 17) // for unsafe.{Add,Slice} +- // TODO(adonovan): add unsafe.{SliceData,String,StringData} in later go versions. +- const files = ` +--- go.mod -- +-module example.com +-go 1.16 +- +--- a.go -- +-package a +- +-import "unsafe" +- +-const _ = iota +-var _ error +-var _ int +-var _ = append() +-var _ = unsafe.Pointer(nil) +-var _ = unsafe.Add(nil, nil) +-var _ = unsafe.Sizeof(0) +-var _ = unsafe.Alignof(0) +-var _ = unsafe.Slice(nil, 0) +-` +- +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("a.go") +- for _, name := range strings.Fields( +- "iota error int nil append iota Pointer Sizeof Alignof Add Slice") { +- loc := env.RegexpSearch("a.go", `\b`+name+`\b`) +- +- // definition -> {builtin,unsafe}.go +- def := env.GoToDefinition(loc) +- if (!strings.HasSuffix(string(def.URI), "builtin.go") && +- !strings.HasSuffix(string(def.URI), "unsafe.go")) || +- def.Range.Start.Line == 0 { +- t.Errorf("definition(%q) = %v, want {builtin,unsafe}.go", +- name, def) +- } +- +- // "references to (builtin "Foo"|unsafe.Foo) are not supported" +- _, err := env.Editor.References(env.Ctx, loc) +- gotErr := fmt.Sprint(err) +- if !strings.Contains(gotErr, "references to") || +- !strings.Contains(gotErr, "not supported") || +- !strings.Contains(gotErr, name) { +- t.Errorf("references(%q) error: got %q, want %q", +- name, gotErr, "references to ... are not supported") +- } +- } +- }) +-} +- -func TestPackageReferences(t *testing.T) { - tests := []struct { - packageName string @@ -117229,16 +130549,6 @@ diff -urN a/gopls/internal/regtest/misc/references_test.go b/gopls/internal/regt - Run(t, files, func(t *testing.T, env *Env) { - env.OpenFile("foo/foo.go") - -- // Helper to map locations relative file paths. -- fileLocations := func(locs []protocol.Location) []string { -- var got []string -- for _, loc := range locs { -- got = append(got, env.Sandbox.Workdir.URIToPath(loc.URI)) -- } -- sort.Strings(got) -- return got -- } -- - refTests := []struct { - re string - wantRefs []string @@ -117248,18 +130558,18 @@ diff -urN a/gopls/internal/regtest/misc/references_test.go b/gopls/internal/regt - // - inside the foo.mod/bar [foo.mod/bar.test] test variant package - // - from the foo.mod/bar_test [foo.mod/bar.test] x_test package - // - from the foo.mod/foo package -- {"Blah", []string{"bar/bar.go", "bar/bar_test.go", "bar/bar_x_test.go", "foo/foo.go"}}, +- {"Blah", []string{"bar/bar.go:3", "bar/bar_test.go:7", "bar/bar_x_test.go:12", "foo/foo.go:12"}}, - - // Foo is referenced in bar_x_test.go via the intermediate test variant - // foo.mod/foo [foo.mod/bar.test]. -- {"Foo", []string{"bar/bar_x_test.go", "foo/foo.go"}}, +- {"Foo", []string{"bar/bar_x_test.go:13", "foo/foo.go:5"}}, - } - - for _, test := range refTests { - loc := env.RegexpSearch("foo/foo.go", test.re) - refs := env.References(loc) - -- got := fileLocations(refs) +- got := fileLocations(env, refs) - if diff := cmp.Diff(test.wantRefs, got); diff != "" { - t.Errorf("References(%q) returned unexpected diff (-want +got):\n%s", test.re, diff) - } @@ -117272,18 +130582,18 @@ diff -urN a/gopls/internal/regtest/misc/references_test.go b/gopls/internal/regt - // InterfaceM is implemented both in foo.mod/bar [foo.mod/bar.test] (which - // doesn't import foo), and in foo.mod/bar_test [foo.mod/bar.test], which - // imports the test variant of foo. -- {"InterfaceM", []string{"bar/bar_test.go", "bar/bar_x_test.go"}}, +- {"InterfaceM", []string{"bar/bar_test.go:3", "bar/bar_x_test.go:8"}}, - - // A search within the ordinary package to should find implementations - // (Fer) within the augmented test package. -- {"InterfaceF", []string{"foo/foo_test.go"}}, +- {"InterfaceF", []string{"foo/foo_test.go:3"}}, - } - - for _, test := range implTests { - loc := env.RegexpSearch("foo/foo.go", test.re) - impls := env.Implementations(loc) - -- got := fileLocations(impls) +- got := fileLocations(env, impls) - if diff := cmp.Diff(test.wantImpls, got); diff != "" { - t.Errorf("Implementations(%q) returned unexpected diff (-want +got):\n%s", test.re, diff) - } @@ -117350,7 +130660,7 @@ diff -urN a/gopls/internal/regtest/misc/references_test.go b/gopls/internal/regt - checkVendor(env.Implementations(refLoc), false) - - // Run 'go mod vendor' outside the editor. -- if err := env.Sandbox.RunGoCommand(env.Ctx, ".", "mod", []string{"vendor"}, true); err != nil { +- if err := env.Sandbox.RunGoCommand(env.Ctx, ".", "mod", []string{"vendor"}, nil, true); err != nil { - t.Fatalf("go mod vendor: %v", err) - } - @@ -117376,9 +130686,145 @@ diff -urN a/gopls/internal/regtest/misc/references_test.go b/gopls/internal/regt - checkVendor(env.Implementations(refLoc), false) - }) -} +- +-// This test can't be expressed as a marker test because the marker +-// test framework opens all files (which is a bit of a hack), creating +-// a <command-line-arguments> package for packages that otherwise +-// wouldn't be found from the go.work file. +-func TestReferencesFromWorkspacePackages59674(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) // for go.work support +- const src = ` +--- a/go.mod -- +-module example.com/a +-go 1.12 +- +--- b/go.mod -- +-module example.com/b +-go 1.12 +- +--- c/go.mod -- +-module example.com/c +-go 1.12 +- +--- lib/go.mod -- +-module example.com/lib +-go 1.12 +- +--- go.work -- +-use ./a +-use ./b +-// don't use ./c +-use ./lib +- +--- a/a.go -- +-package a +- +-import "example.com/lib" +- +-var _ = lib.F // query here +- +--- b/b.go -- +-package b +- +-import "example.com/lib" +- +-var _ = lib.F // also found by references +- +--- c/c.go -- +-package c +- +-import "example.com/lib" +- +-var _ = lib.F // this reference should not be reported +- +--- lib/lib.go -- +-package lib +- +-func F() {} // declaration +-` +- Run(t, src, func(t *testing.T, env *Env) { +- env.OpenFile("a/a.go") +- refLoc := env.RegexpSearch("a/a.go", "F") +- got := fileLocations(env, env.References(refLoc)) +- want := []string{"a/a.go:5", "b/b.go:5", "lib/lib.go:3"} +- if diff := cmp.Diff(want, got); diff != "" { +- t.Errorf("incorrect References (-want +got):\n%s", diff) +- } +- }) +-} +- +-// Test an 'implementation' query on a type that implements 'error'. +-// (Unfortunately builtin locations cannot be expressed using @loc +-// in the marker test framework.) +-func TestImplementationsOfError(t *testing.T) { +- const src = ` +--- go.mod -- +-module example.com +-go 1.12 +- +--- a.go -- +-package a +- +-type Error2 interface { +- Error() string +-} +- +-type MyError int +-func (MyError) Error() string { return "" } +- +-type MyErrorPtr int +-func (*MyErrorPtr) Error() string { return "" } +-` +- Run(t, src, func(t *testing.T, env *Env) { +- env.OpenFile("a.go") +- +- for _, test := range []struct { +- re string +- want []string +- }{ +- // error type +- {"Error2", []string{"a.go:10", "a.go:7", "std:builtin/builtin.go"}}, +- {"MyError", []string{"a.go:3", "std:builtin/builtin.go"}}, +- {"MyErrorPtr", []string{"a.go:3", "std:builtin/builtin.go"}}, +- // error.Error method +- {"(Error).. string", []string{"a.go:11", "a.go:8", "std:builtin/builtin.go"}}, +- {"MyError. (Error)", []string{"a.go:4", "std:builtin/builtin.go"}}, +- {"MyErrorPtr. (Error)", []string{"a.go:4", "std:builtin/builtin.go"}}, +- } { +- matchLoc := env.RegexpSearch("a.go", test.re) +- impls := env.Implementations(matchLoc) +- got := fileLocations(env, impls) +- if !reflect.DeepEqual(got, test.want) { +- t.Errorf("Implementations(%q) = %q, want %q", +- test.re, got, test.want) +- } +- } +- }) +-} +- +-// fileLocations returns a new sorted array of the +-// relative file name and line number of each location. +-// Duplicates are not removed. +-// Standard library filenames are abstracted for robustness. +-func fileLocations(env *regtest.Env, locs []protocol.Location) []string { +- got := make([]string, 0, len(locs)) +- for _, loc := range locs { +- path := env.Sandbox.Workdir.URIToPath(loc.URI) // (slashified) +- if i := strings.LastIndex(path, "/src/"); i >= 0 && filepath.IsAbs(path) { +- // Absolute path with "src" segment: assume it's in GOROOT. +- // Strip directory and don't add line/column since they are fragile. +- path = "std:" + path[i+len("/src/"):] +- } else { +- path = fmt.Sprintf("%s:%d", path, loc.Range.Start.Line+1) +- } +- got = append(got, path) +- } +- sort.Strings(got) +- return got +-} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/rename_test.go 1970-01-01 08:00:00 @@ -1,935 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -118317,7 +131763,7 @@ diff -urN a/gopls/internal/regtest/misc/rename_test.go b/gopls/internal/regtest/ -} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/semantictokens_test.go 1970-01-01 08:00:00 @@ -1,204 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -118525,7 +131971,7 @@ diff -urN a/gopls/internal/regtest/misc/semantictokens_test.go b/gopls/internal/ -) 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/settings_test.go 1970-01-01 08:00:00 @@ -1,32 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -118561,7 +132007,7 @@ diff -urN a/gopls/internal/regtest/misc/settings_test.go b/gopls/internal/regtes -} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/shared_test.go 1970-01-01 08:00:00 @@ -1,72 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -118637,7 +132083,7 @@ diff -urN a/gopls/internal/regtest/misc/shared_test.go b/gopls/internal/regtest/ -} diff -urN a/gopls/internal/regtest/misc/signature_help_test.go b/gopls/internal/regtest/misc/signature_help_test.go --- a/gopls/internal/regtest/misc/signature_help_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/misc/signature_help_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/signature_help_test.go 1970-01-01 08:00:00 @@ -1,69 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -118710,7 +132156,7 @@ diff -urN a/gopls/internal/regtest/misc/signature_help_test.go b/gopls/internal/ -} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/misc/staticcheck_test.go 1970-01-01 08:00:00 @@ -1,110 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -118824,8 +132270,8 @@ diff -urN a/gopls/internal/regtest/misc/staticcheck_test.go b/gopls/internal/reg -} 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 00:00:00.000000000 +0000 -@@ -1,65 +0,0 @@ ++++ b/gopls/internal/regtest/misc/vendor_test.go 1970-01-01 08:00:00 +@@ -1,103 +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. @@ -118891,10 +132337,48 @@ diff -urN a/gopls/internal/regtest/misc/vendor_test.go b/gopls/internal/regtest/ - ) - }) -} +- +-func TestWindowsVendoring_Issue56291(t *testing.T) { +- const src = ` +--- 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= +--- main.go -- +-package main +- +-import "golang.org/x/hello/hi" +- +-func main() { +- _ = hi.Goodbye +-} +-` +- WithOptions( +- Modes(Default), +- ProxyFiles(basicProxy), +- ).Run(t, src, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- env.AfterChange(NoDiagnostics()) +- env.RunGoCommand("mod", "tidy") +- env.RunGoCommand("mod", "vendor") +- env.AfterChange(NoDiagnostics()) +- env.RegexpReplace("main.go", `import "golang.org/x/hello/hi"`, "") +- env.AfterChange( +- Diagnostics(env.AtRegexp("main.go", "hi.Goodbye")), +- ) +- env.SaveBuffer("main.go") +- env.AfterChange(NoDiagnostics()) +- }) +-} 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 00:00:00.000000000 +0000 -@@ -1,977 +0,0 @@ ++++ b/gopls/internal/regtest/misc/vuln_test.go 1970-01-01 08:00:00 +@@ -1,952 +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. @@ -118907,19 +132391,19 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi -import ( - "context" - "encoding/json" -- "path/filepath" - "sort" - "strings" - "testing" - - "github.com/google/go-cmp/cmp" -- "golang.org/x/tools/gopls/internal/govulncheck" +- - "golang.org/x/tools/gopls/internal/lsp/command" - "golang.org/x/tools/gopls/internal/lsp/protocol" - . "golang.org/x/tools/gopls/internal/lsp/regtest" - "golang.org/x/tools/gopls/internal/lsp/source" - "golang.org/x/tools/gopls/internal/lsp/tests/compare" - "golang.org/x/tools/gopls/internal/vulncheck" +- "golang.org/x/tools/gopls/internal/vulncheck/scan" - "golang.org/x/tools/gopls/internal/vulncheck/vulntest" - "golang.org/x/tools/internal/testenv" -) @@ -118982,7 +132466,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - env.Await( - CompletedProgress(result.Token, &ws), - ) -- wantEndMsg, wantMsgPart := "failed", "failed to load packages due to errors" +- wantEndMsg, wantMsgPart := "failed", "There are errors with the provided package patterns:" - if ws.EndMsg != "failed" || !strings.Contains(ws.Msg, wantMsgPart) { - t.Errorf("work status = %+v, want {EndMessage: %q, Message: %q}", ws, wantEndMsg, wantMsgPart) - } @@ -118996,14 +132480,14 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - versions: - - introduced: 1.0.0 - - fixed: 1.0.4 -- - introduced: 1.1.2 - packages: - - package: golang.org/amod/avuln - symbols: - - VulnData.Vuln1 - - VulnData.Vuln2 -description: > -- vuln in amod +- vuln in amod is found +-summary: vuln in amod -references: - - href: pkg.go.dev/vuln/GO-2022-01 --- GO-2022-03.yaml -- @@ -119017,7 +132501,8 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - symbols: - - nonExisting -description: > -- unaffecting vulnerability +- unaffecting vulnerability is found +-summary: unaffecting vulnerability --- GO-2022-02.yaml -- -modules: - - module: golang.org/bmod @@ -119026,10 +132511,11 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - symbols: - - Vuln -description: | -- vuln in bmod +- vuln in bmod is found. - - This is a long description - of this vulnerability. +-summary: vuln in bmod (no fix) -references: - - href: pkg.go.dev/vuln/GO-2022-03 --- GO-2022-04.yaml -- @@ -119040,7 +132526,8 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - symbols: - - Vuln -description: | -- vuln in bmod/somtrhingelse +- vuln in bmod/somethingelse is found +-summary: vuln in bmod/somethingelse -references: - - href: pkg.go.dev/vuln/GO-2022-04 --- GOSTDLIB.yaml -- @@ -119052,6 +132539,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - - package: archive/zip - symbols: - - OpenReader +-summary: vuln in GOSTDLIB -references: - - href: pkg.go.dev/vuln/GOSTDLIB -` @@ -119089,7 +132577,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - // When fetchinging stdlib package vulnerability info, - // behave as if our go version is go1.18 for this testing. - // The default behavior is to run `go env GOVERSION` (which isn't mutable env var). -- vulncheck.GoVersionForVulnTest: "go1.18", +- scan.GoVersionForVulnTest: "go1.18", - "_GOPLS_TEST_BINARY_RUN_AS_GOPLS": "true", // needed to run `gopls vulncheck`. - }, - Settings{ @@ -119129,7 +132617,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - NoDiagnostics(ForFile("go.mod")), - ) - testFetchVulncheckResult(t, env, map[string]fetchVulncheckResult{ -- "go.mod": {IDs: []string{"GOSTDLIB"}, Mode: govulncheck.ModeGovulncheck}}) +- "go.mod": {IDs: []string{"GOSTDLIB"}, Mode: vulncheck.ModeGovulncheck}}) - }) -} - @@ -119165,7 +132653,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - "GOVULNDB": db.URI(), - // When fetchinging stdlib package vulnerability info, - // behave as if our go version is go1.18 for this testing. -- vulncheck.GoVersionForVulnTest: "go1.18", +- scan.GoVersionForVulnTest: "go1.18", - "_GOPLS_TEST_BINARY_RUN_AS_GOPLS": "true", // needed to run `gopls vulncheck`. - }, - Settings{"ui.diagnostic.vulncheck": "Imports"}, @@ -119178,7 +132666,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - testFetchVulncheckResult(t, env, map[string]fetchVulncheckResult{ - "go.mod": { - IDs: []string{"GOSTDLIB"}, -- Mode: govulncheck.ModeImports, +- Mode: vulncheck.ModeImports, - }, - }) - }) @@ -119186,13 +132674,13 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - -type fetchVulncheckResult struct { - IDs []string -- Mode govulncheck.AnalysisMode +- Mode vulncheck.AnalysisMode -} - -func testFetchVulncheckResult(t *testing.T, env *Env, want map[string]fetchVulncheckResult) { - t.Helper() - -- var result map[protocol.DocumentURI]*govulncheck.Result +- var result map[protocol.DocumentURI]*vulncheck.Result - fetchCmd, err := command.NewFetchVulncheckResultCommand("fetch", command.URIArg{ - URI: env.Sandbox.Workdir.URI("go.mod"), - }) @@ -119209,14 +132697,18 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - } - got := map[string]fetchVulncheckResult{} - for k, r := range result { -- var osv []string -- for _, v := range r.Vulns { -- osv = append(osv, v.OSV.ID) +- osv := map[string]bool{} +- for _, v := range r.Findings { +- osv[v.OSV] = true +- } +- ids := make([]string, 0, len(osv)) +- for id := range osv { +- ids = append(ids, id) - } -- sort.Strings(osv) +- sort.Strings(ids) - modfile := env.Sandbox.Workdir.RelPath(k.SpanURI().Filename()) - got[modfile] = fetchVulncheckResult{ -- IDs: osv, +- IDs: ids, - Mode: r.Mode, - } - } @@ -119362,7 +132854,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - // When fetching stdlib package vulnerability info, - // behave as if our go version is go1.18 for this testing. - // The default behavior is to run `go env GOVERSION` (which isn't mutable env var). -- vulncheck.GoVersionForVulnTest: "go1.18", +- scan.GoVersionForVulnTest: "go1.18", - "_GOPLS_TEST_BINARY_RUN_AS_GOPLS": "true", // needed to run `gopls vulncheck`. - "GOSUMDB": "off", - } @@ -119390,7 +132882,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - testFetchVulncheckResult(t, env, map[string]fetchVulncheckResult{ - "go.mod": { - IDs: []string{"GO-2022-01", "GO-2022-02", "GO-2022-03"}, -- Mode: govulncheck.ModeImports, +- Mode: vulncheck.ModeImports, - }, - }) - @@ -119429,7 +132921,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - codeActions: []string{ - "Run govulncheck to verify", - }, -- hover: []string{"GO-2022-02", "This is a long description of this vulnerability.", "No fix is available."}, +- hover: []string{"GO-2022-02", "vuln in bmod (no fix)", "No fix is available."}, - }, - } - @@ -119541,12 +133033,10 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - ) - - testFetchVulncheckResult(t, env, map[string]fetchVulncheckResult{ -- "go.mod": {IDs: []string{"GO-2022-01", "GO-2022-02", "GO-2022-03"}, Mode: govulncheck.ModeGovulncheck}, +- "go.mod": {IDs: []string{"GO-2022-01", "GO-2022-02", "GO-2022-03"}, Mode: vulncheck.ModeGovulncheck}, - }) - env.OpenFile("x/x.go") -- lineX := env.RegexpSearch("x/x.go", `c\.C1\(\)\.Vuln1\(\)`).Range.Start - env.OpenFile("y/y.go") -- lineY := env.RegexpSearch("y/y.go", `c\.C2\(\)\(\)`).Range.Start - wantDiagnostics := map[string]vulnDiagExpectation{ - "golang.org/amod": { - applyAction: "Upgrade to v1.0.6", @@ -119560,10 +133050,6 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - "Upgrade to latest", - "Reset govulncheck result", - }, -- relatedInfo: []vulnRelatedInfo{ -- {"x.go", uint32(lineX.Line), "[GO-2022-01]"}, // avuln.VulnData.Vuln1 -- {"x.go", uint32(lineX.Line), "[GO-2022-01]"}, // avuln.VulnData.Vuln2 -- }, - }, - { - msg: "golang.org/amod has a vulnerability GO-2022-03 that is not used in the code.", @@ -119574,10 +133060,6 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - "Upgrade to latest", - "Reset govulncheck result", - }, -- relatedInfo: []vulnRelatedInfo{ -- {"x.go", uint32(lineX.Line), "[GO-2022-01]"}, // avuln.VulnData.Vuln1 -- {"x.go", uint32(lineX.Line), "[GO-2022-01]"}, // avuln.VulnData.Vuln2 -- }, - }, - }, - codeActions: []string{ @@ -119596,15 +133078,12 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - codeActions: []string{ - "Reset govulncheck result", // no fix, but we should give an option to reset. - }, -- relatedInfo: []vulnRelatedInfo{ -- {"y.go", uint32(lineY.Line), "[GO-2022-02]"}, // bvuln.Vuln -- }, - }, - }, - codeActions: []string{ - "Reset govulncheck result", // no fix, but we should give an option to reset. - }, -- hover: []string{"GO-2022-02", "This is a long description of this vulnerability.", "No fix is available."}, +- hover: []string{"GO-2022-02", "vuln in bmod (no fix)", "No fix is available."}, - }, - } - @@ -119710,7 +133189,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - ReadDiagnostics("go.mod", gotDiagnostics), - ) - -- testFetchVulncheckResult(t, env, map[string]fetchVulncheckResult{"go.mod": {IDs: []string{"GO-2022-02"}, Mode: govulncheck.ModeGovulncheck}}) +- testFetchVulncheckResult(t, env, map[string]fetchVulncheckResult{"go.mod": {IDs: []string{"GO-2022-02"}, Mode: vulncheck.ModeGovulncheck}}) - // wantDiagnostics maps a module path in the require - // section of a go.mod to diagnostics that will be returned - // when running vulncheck. @@ -119729,7 +133208,7 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - codeActions: []string{ - "Reset govulncheck result", - }, -- hover: []string{"GO-2022-02", "This is a long description of this vulnerability.", "No fix is available."}, +- hover: []string{"GO-2022-02", "vuln in bmod (no fix)", "No fix is available."}, - }, - } - @@ -119786,10 +133265,6 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - if diag.Severity != w.severity || diag.Source != w.source { - t.Errorf("incorrect (severity, source) for %q, want (%s, %s) got (%s, %s)\n", w.msg, w.severity, w.source, diag.Severity, diag.Source) - } -- sort.Slice(w.relatedInfo, func(i, j int) bool { return w.relatedInfo[i].less(w.relatedInfo[j]) }) -- if got, want := summarizeRelatedInfo(diag.RelatedInformation), w.relatedInfo; !cmp.Equal(got, want) { -- t.Errorf("related info for %q do not match, want %v, got %v\n", w.msg, want, got) -- } - // Check expected code actions appear. - gotActions := env.CodeAction("go.mod", []protocol.Diagnostic{*diag}) - if diff := diffCodeActions(gotActions, w.codeActions); diff != "" { @@ -119810,22 +133285,6 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi - return modPathDiagnostics -} - --// summarizeRelatedInfo converts protocol.DiagnosticRelatedInformation to vulnRelatedInfo --// that captures only the part that we want to test. --func summarizeRelatedInfo(rinfo []protocol.DiagnosticRelatedInformation) []vulnRelatedInfo { -- var res []vulnRelatedInfo -- for _, r := range rinfo { -- filename := filepath.Base(r.Location.URI.SpanURI().Filename()) -- message, _, _ := strings.Cut(r.Message, " ") -- line := r.Location.Range.Start.Line -- res = append(res, vulnRelatedInfo{filename, line, message}) -- } -- sort.Slice(res, func(i, j int) bool { -- return res[i].less(res[j]) -- }) -- return res --} -- -type vulnRelatedInfo struct { - Filename string - Line uint32 @@ -119874,8 +133333,8 @@ diff -urN a/gopls/internal/regtest/misc/vuln_test.go b/gopls/internal/regtest/mi -} 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 00:00:00.000000000 +0000 -@@ -1,124 +0,0 @@ ++++ b/gopls/internal/regtest/misc/workspace_symbol_test.go 1970-01-01 08:00:00 +@@ -1,114 +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. @@ -119885,7 +133344,7 @@ diff -urN a/gopls/internal/regtest/misc/workspace_symbol_test.go b/gopls/interna -import ( - "testing" - -- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "github.com/google/go-cmp/cmp" - . "golang.org/x/tools/gopls/internal/lsp/regtest" - "golang.org/x/tools/gopls/internal/lsp/source" -) @@ -119899,7 +133358,7 @@ diff -urN a/gopls/internal/regtest/misc/workspace_symbol_test.go b/gopls/interna --- a.go -- -package p - --const C1 = "a.go" +-const K1 = "a.go" --- exclude.go -- - -//go:build exclude @@ -119907,23 +133366,17 @@ diff -urN a/gopls/internal/regtest/misc/workspace_symbol_test.go b/gopls/interna - -package exclude - --const C2 = "exclude.go" +-const K2 = "exclude.go" -` - - Run(t, files, func(t *testing.T, env *Env) { - env.OpenFile("a.go") -- syms := env.Symbol("C") -- if got, want := len(syms), 1; got != want { -- t.Errorf("got %d symbols, want %d", got, want) -- } +- checkSymbols(env, "K", "K1") - - // Opening up an ignored file will result in an overlay with missing - // metadata, but this shouldn't break workspace symbols requests. - env.OpenFile("exclude.go") -- syms = env.Symbol("C") -- if got, want := len(syms), 1; got != want { -- t.Errorf("got %d symbols, want %d", got, want) -- } +- checkSymbols(env, "K", "K1") - }) -} - @@ -119949,15 +133402,13 @@ diff -urN a/gopls/internal/regtest/misc/workspace_symbol_test.go b/gopls/interna - WithOptions( - Settings{"symbolMatcher": symbolMatcher}, - ).Run(t, files, func(t *testing.T, env *Env) { -- want := []string{ +- checkSymbols(env, "Foo", - "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.Symbol("Foo") -- compareSymbols(t, got, want...) +- ) - }) -} - @@ -119980,30 +133431,28 @@ diff -urN a/gopls/internal/regtest/misc/workspace_symbol_test.go b/gopls/interna - WithOptions( - Settings{"symbolMatcher": symbolMatcher}, - ).Run(t, files, func(t *testing.T, env *Env) { -- compareSymbols(t, env.Symbol("ABC"), "ABC", "AxxBxxCxx") -- compareSymbols(t, env.Symbol("'ABC"), "ABC") -- compareSymbols(t, env.Symbol("^mod.com"), "mod.com/a.ABC", "mod.com/a.AxxBxxCxx") -- compareSymbols(t, env.Symbol("^mod.com Axx"), "mod.com/a.AxxBxxCxx") -- compareSymbols(t, env.Symbol("C$"), "ABC") +- checkSymbols(env, "ABC", "ABC", "AxxBxxCxx") +- checkSymbols(env, "'ABC", "ABC") +- checkSymbols(env, "^mod.com", "mod.com/a.ABC", "mod.com/a.AxxBxxCxx") +- checkSymbols(env, "^mod.com Axx", "mod.com/a.AxxBxxCxx") +- checkSymbols(env, "C$", "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)) +-func checkSymbols(env *Env, query string, want ...string) { +- env.T.Helper() +- var got []string +- for _, info := range env.Symbol(query) { +- got = append(got, info.Name) - } -- -- for i := range got { -- if got[i].Name != want[i] { -- t.Errorf("got[%d] = %q, want %q", i, got[i].Name, want[i]) -- } +- if diff := cmp.Diff(got, want); diff != "" { +- env.T.Errorf("unexpected Symbol(%q) result (+want -got):\n%s", query, diff) - } -} 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 00:00:00.000000000 +0000 -@@ -1,1188 +0,0 @@ ++++ b/gopls/internal/regtest/modfile/modfile_test.go 1970-01-01 08:00:00 +@@ -1,1222 +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. @@ -120016,10 +133465,10 @@ diff -urN a/gopls/internal/regtest/modfile/modfile_test.go b/gopls/internal/regt - "strings" - "testing" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/hooks" - . "golang.org/x/tools/gopls/internal/lsp/regtest" - "golang.org/x/tools/gopls/internal/lsp/tests/compare" -- "golang.org/x/tools/internal/bug" - - "golang.org/x/tools/gopls/internal/lsp/protocol" - "golang.org/x/tools/internal/testenv" @@ -120342,48 +133791,6 @@ diff -urN a/gopls/internal/regtest/modfile/modfile_test.go b/gopls/internal/regt - }) -} - --func TestUnusedDiag(t *testing.T) { -- -- 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.AfterChange( -- Diagnostics(env.AtRegexp("a/go.mod", `require example.com`)), -- ReadDiagnostics("a/go.mod", &d), -- ) -- env.ApplyQuickFixes("a/go.mod", d.Diagnostics) -- if got := env.BufferText("a/go.mod"); got != want { -- t.Fatalf("unexpected go.mod content:\n%s", compare.Text(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) { @@ -120545,6 +133952,7 @@ diff -urN a/gopls/internal/regtest/modfile/modfile_test.go b/gopls/internal/regt - Diagnostics(env.AtRegexp("a/go.mod", `require example.com/blah/v2`), WithMessage("cannot find module providing")), - ReadDiagnostics("a/go.mod", &modDiags), - ) +- - env.ApplyQuickFixes("a/go.mod", modDiags.Diagnostics) - const want = `module mod.com - @@ -120925,7 +134333,7 @@ diff -urN a/gopls/internal/regtest/modfile/modfile_test.go b/gopls/internal/regt - } - // Confirm that we no longer have metadata when the file is saved. - env.SaveBufferWithoutActions("go.mod") -- _, err := env.Editor.GoToDefinition(env.Ctx, env.RegexpSearch("main.go", "hello")) +- _, err := env.Editor.Definition(env.Ctx, env.RegexpSearch("main.go", "hello")) - if err == nil { - t.Fatalf("expected error, got none") - } @@ -121192,9 +134600,129 @@ diff -urN a/gopls/internal/regtest/modfile/modfile_test.go b/gopls/internal/regt - env.SaveBuffer("go.work") // doesn't fail - }) -} +- +-func TestInconsistentMod(t *testing.T) { +- const proxy = ` +--- golang.org/x/mod@v0.7.0/go.mod -- +-go 1.20 +-module golang.org/x/mod +--- golang.org/x/mod@v0.7.0/a.go -- +-package mod +-func AutoQuote(string) string { return ""} +--- golang.org/x/mod@v0.9.0/go.mod -- +-go 1.20 +-module golang.org/x/mod +--- golang.org/x/mod@v0.9.0/a.go -- +-package mod +-func AutoQuote(string) string { return ""} +-` +- const files = ` +--- go.work -- +-go 1.20 +-use ( +- ./a +- ./b +-) +- +--- a/go.mod -- +-module a.mod.com +-go 1.20 +-require golang.org/x/mod v0.6.0 // yyy +-replace golang.org/x/mod v0.6.0 => golang.org/x/mod v0.7.0 +--- a/main.go -- +-package main +-import "golang.org/x/mod" +-import "fmt" +-func main() {fmt.Println(mod.AutoQuote(""))} +- +--- b/go.mod -- +-module b.mod.com +-go 1.20 +-require golang.org/x/mod v0.9.0 // xxx +--- b/main.go -- +-package aaa +-import "golang.org/x/mod" +-import "fmt" +-func main() {fmt.Println(mod.AutoQuote(""))} +-var A int +- +--- b/c/go.mod -- +-module c.b.mod.com +-go 1.20 +-require b.mod.com v0.4.2 +-replace b.mod.com => ../ +--- b/c/main.go -- +-package main +-import "b.mod.com/aaa" +-import "fmt" +-func main() {fmt.Println(aaa.A)} +-` +- testenv.NeedsGo1Point(t, 18) +- WithOptions( +- ProxyFiles(proxy), +- Modes(Default), +- ).Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("a/go.mod") +- ahints := env.InlayHints("a/go.mod") +- if len(ahints) != 1 { +- t.Errorf("expected exactly one hint, got %d: %#v", len(ahints), ahints) +- } +- env.OpenFile("b/c/go.mod") +- bhints := env.InlayHints("b/c/go.mod") +- if len(bhints) != 0 { +- t.Errorf("expected no hints, got %d: %#v", len(bhints), bhints) +- } +- }) +- +-} +diff -urN a/gopls/internal/regtest/modfile/tempmodfile_test.go b/gopls/internal/regtest/modfile/tempmodfile_test.go +--- a/gopls/internal/regtest/modfile/tempmodfile_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/modfile/tempmodfile_test.go 1970-01-01 08:00:00 +@@ -1,41 +0,0 @@ +-// Copyright 2023 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 ( +- "testing" +- +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) +- +-// This test replaces an older, problematic test (golang/go#57784). But it has +-// been a long time since the go command would mutate go.mod files. +-// +-// TODO(golang/go#61970): the tempModfile setting should be removed entirely. +-func TestTempModfileUnchanged(t *testing.T) { +- // badMod has a go.mod file that is missing a go directive. +- const badMod = ` +--- go.mod -- +-module badmod.test/p +--- p.go -- +-package p +-` +- +- WithOptions( +- Modes(Default), // no reason to test this with a remote gopls +- ProxyFiles(workspaceProxy), +- Settings{ +- "tempModfile": true, +- }, +- ).Run(t, badMod, func(t *testing.T, env *Env) { +- env.OpenFile("p.go") +- env.AfterChange() +- want := "module badmod.test/p\n" +- got := env.ReadWorkspaceFile("go.mod") +- if got != want { +- t.Errorf("go.mod content:\n%s\nwant:\n%s", got, want) +- } +- }) +-} 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 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/template/template_test.go 1970-01-01 08:00:00 @@ -1,231 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -121206,10 +134734,10 @@ diff -urN a/gopls/internal/regtest/template/template_test.go b/gopls/internal/re - "strings" - "testing" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/hooks" - "golang.org/x/tools/gopls/internal/lsp/protocol" - . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/internal/bug" -) - -func TestMain(m *testing.M) { @@ -121427,10 +134955,99 @@ diff -urN a/gopls/internal/regtest/template/template_test.go b/gopls/internal/re -} - -// Hover needs tests +diff -urN a/gopls/internal/regtest/watch/setting_test.go b/gopls/internal/regtest/watch/setting_test.go +--- a/gopls/internal/regtest/watch/setting_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/watch/setting_test.go 1970-01-01 08:00:00 +@@ -1,85 +0,0 @@ +-// Copyright 2023 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 ( +- "fmt" +- "testing" +- +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) +- +-func TestSubdirWatchPatterns(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.test +- +-go 1.18 +--- subdir/subdir.go -- +-package subdir +-` +- +- tests := []struct { +- clientName string +- subdirWatchPatterns string +- wantWatched bool +- }{ +- {"other client", "on", true}, +- {"other client", "off", false}, +- {"other client", "auto", false}, +- {"Visual Studio Code", "auto", true}, +- } +- +- for _, test := range tests { +- t.Run(fmt.Sprintf("%s_%s", test.clientName, test.subdirWatchPatterns), func(t *testing.T) { +- WithOptions( +- ClientName(test.clientName), +- Settings{ +- "subdirWatchPatterns": test.subdirWatchPatterns, +- }, +- ).Run(t, files, func(t *testing.T, env *Env) { +- var expectation Expectation +- if test.wantWatched { +- expectation = FileWatchMatching("subdir") +- } else { +- expectation = NoFileWatchMatching("subdir") +- } +- env.OnceMet( +- InitialWorkspaceLoad, +- expectation, +- ) +- }) +- }) +- } +-} +- +-// This test checks that we surface errors for invalid subdir watch patterns, +-// as the triple of ("off"|"on"|"auto") may be confusing to users inclined to +-// use (true|false) or some other truthy value. +-func TestSubdirWatchPatterns_BadValues(t *testing.T) { +- tests := []struct { +- badValue interface{} +- wantMessage string +- }{ +- {true, "invalid type bool, expect string"}, +- {false, "invalid type bool, expect string"}, +- {"yes", `invalid option "yes"`}, +- } +- +- for _, test := range tests { +- t.Run(fmt.Sprint(test.badValue), func(t *testing.T) { +- WithOptions( +- Settings{ +- "subdirWatchPatterns": test.badValue, +- }, +- ).Run(t, "", func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- ShownMessage(test.wantMessage), +- ) +- }) +- }) +- } +-} 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 00:00:00.000000000 +0000 -@@ -1,702 +0,0 @@ ++++ b/gopls/internal/regtest/watch/watch_test.go 1970-01-01 08:00:00 +@@ -1,704 +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. @@ -121440,9 +135057,9 @@ diff -urN a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/ -import ( - "testing" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/hooks" - . "golang.org/x/tools/gopls/internal/lsp/regtest" -- "golang.org/x/tools/internal/bug" - - "golang.org/x/tools/gopls/internal/lsp/fake" - "golang.org/x/tools/gopls/internal/lsp/protocol" @@ -121816,7 +135433,9 @@ diff -urN a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/ - ).Run(t, pkg, func(t *testing.T, env *Env) { - env.OpenFile("a/a.go") - env.OpenFile("a/a_unneeded.go") -- env.AfterChange( +- env.Await( +- // Log messages are asynchronous to other events on the LSP stream, so we +- // can't use OnceMet or AfterChange here. - LogMatching(protocol.Info, "a_unneeded.go", 1, false), - ) - @@ -121828,7 +135447,7 @@ diff -urN a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/ - Diagnostics(env.AtRegexp("a/a.go", "fmt")), - ) - env.SaveBuffer("a/a.go") -- env.AfterChange( +- env.Await( - // 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. @@ -121844,7 +135463,7 @@ diff -urN a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/ - ).Run(t, pkg, func(t *testing.T, env *Env) { - env.OpenFile("a/a.go") - env.OpenFile("a/a_unneeded.go") -- env.AfterChange( +- env.Await( - LogMatching(protocol.Info, "a_unneeded.go", 1, false), - ) - @@ -121856,7 +135475,7 @@ diff -urN a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/ - Diagnostics(env.AtRegexp("a/a.go", "fmt")), - ) - env.SaveBuffer("a/a.go") -- env.AfterChange( +- env.Await( - // 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. @@ -122010,7 +135629,7 @@ diff -urN a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/ - env.AfterChange( - NoDiagnostics(ForFile("main.go")), - ) -- if err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}, true); err != nil { +- if err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}, nil, true); err != nil { - t.Fatal(err) - } - @@ -122133,10 +135752,56 @@ diff -urN a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/ - ) - }) -} +diff -urN a/gopls/internal/regtest/workspace/adhoc_test.go b/gopls/internal/regtest/workspace/adhoc_test.go +--- a/gopls/internal/regtest/workspace/adhoc_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/workspace/adhoc_test.go 1970-01-01 08:00:00 +@@ -1,42 +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/gopls/internal/lsp/regtest" +- "golang.org/x/tools/internal/testenv" +-) +- +-// Test for golang/go#57209: editing a file in an ad-hoc package should not +-// trigger conflicting diagnostics. +-func TestAdhoc_Edits(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) +- +- const files = ` +--- a.go -- +-package foo +- +-const X = 1 +- +--- b.go -- +-package foo +- +-// import "errors" +- +-const Y = X +-` +- +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("b.go") +- +- for i := 0; i < 10; i++ { +- env.RegexpReplace("b.go", `// import "errors"`, `import "errors"`) +- env.RegexpReplace("b.go", `import "errors"`, `// import "errors"`) +- env.AfterChange(NoDiagnostics()) +- } +- }) +-} diff -urN a/gopls/internal/regtest/workspace/broken_test.go b/gopls/internal/regtest/workspace/broken_test.go --- a/gopls/internal/regtest/workspace/broken_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/workspace/broken_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,264 +0,0 @@ ++++ b/gopls/internal/regtest/workspace/broken_test.go 1970-01-01 08:00:00 +@@ -1,263 +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. @@ -122162,10 +135827,9 @@ diff -urN a/gopls/internal/regtest/workspace/broken_test.go b/gopls/internal/reg - -// Test for golang/go#53933 -func TestBrokenWorkspace_DuplicateModules(t *testing.T) { -- testenv.NeedsGo1Point(t, 18) -- -- // TODO(golang/go#57650): fix this feature. -- t.Skip("we no longer detect duplicate modules") +- // The go command error message was improved in Go 1.20 to mention multiple +- // modules. +- testenv.NeedsGo1Point(t, 20) - - // This proxy module content is replaced by the workspace, but is still - // required for module resolution to function in the Go command. @@ -122237,8 +135901,8 @@ diff -urN a/gopls/internal/regtest/workspace/broken_test.go b/gopls/internal/reg - ProxyFiles(proxy), - ).Run(t, src, func(t *testing.T, env *Env) { - env.OpenFile("package1/main.go") -- env.Await( -- OutstandingWork(lsp.WorkspaceLoadFailure, `found module "example.com/foo" multiple times in the workspace`), +- env.AfterChange( +- OutstandingWork(lsp.WorkspaceLoadFailure, `module example.com/foo appears multiple times in workspace`), - ) - - // Remove the redundant vendored copy of example.com. @@ -122249,10 +135913,10 @@ diff -urN a/gopls/internal/regtest/workspace/broken_test.go b/gopls/internal/reg - ./package2/vendor/example.com/foo - ) - `) -- env.Await(NoOutstandingWork()) +- env.AfterChange(NoOutstandingWork(IgnoreTelemetryPromptWork)) - - // Check that definitions in package1 go to the copy vendored in package2. -- location := env.GoToDefinition(env.RegexpSearch("package1/main.go", "CompleteMe")).URI.SpanURI().Filename() +- location := string(env.GoToDefinition(env.RegexpSearch("package1/main.go", "CompleteMe")).URI) - const wantLocation = "package2/vendor/example.com/foo/foo.go" - if !strings.HasSuffix(location, wantLocation) { - t.Errorf("got definition of CompleteMe at %q, want %q", location, wantLocation) @@ -122360,7 +136024,7 @@ diff -urN a/gopls/internal/regtest/workspace/broken_test.go b/gopls/internal/reg - env.Await( - NoDiagnostics(ForFile("a/a.go")), - NoDiagnostics(ForFile("b/go.mod")), -- NoOutstandingWork(), +- NoOutstandingWork(IgnoreTelemetryPromptWork), - ) - - env.ChangeWorkspaceFolders(".") @@ -122396,14 +136060,14 @@ diff -urN a/gopls/internal/regtest/workspace/broken_test.go b/gopls/internal/reg - env.OpenFile("a/a.go") - env.AfterChange( - NoDiagnostics(ForFile("a/a.go")), -- NoOutstandingWork(), +- NoOutstandingWork(IgnoreTelemetryPromptWork), - ) - }) - }) -} diff -urN a/gopls/internal/regtest/workspace/directoryfilters_test.go b/gopls/internal/regtest/workspace/directoryfilters_test.go --- a/gopls/internal/regtest/workspace/directoryfilters_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/workspace/directoryfilters_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/workspace/directoryfilters_test.go 1970-01-01 08:00:00 @@ -1,259 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -122666,8 +136330,8 @@ diff -urN a/gopls/internal/regtest/workspace/directoryfilters_test.go b/gopls/in -} diff -urN a/gopls/internal/regtest/workspace/fromenv_test.go b/gopls/internal/regtest/workspace/fromenv_test.go --- a/gopls/internal/regtest/workspace/fromenv_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/workspace/fromenv_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,68 +0,0 @@ ++++ b/gopls/internal/regtest/workspace/fromenv_test.go 1970-01-01 08:00:00 +@@ -1,79 +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. @@ -122675,6 +136339,8 @@ diff -urN a/gopls/internal/regtest/workspace/fromenv_test.go b/gopls/internal/re -package workspace - -import ( +- "fmt" +- "path/filepath" - "testing" - - . "golang.org/x/tools/gopls/internal/lsp/regtest" @@ -122684,7 +136350,12 @@ diff -urN a/gopls/internal/regtest/workspace/fromenv_test.go b/gopls/internal/re -// Test that setting go.work via environment variables or settings works. -func TestUseGoWorkOutsideTheWorkspace(t *testing.T) { - testenv.NeedsGo1Point(t, 18) -- const files = ` +- +- // As discussed in +- // https://github.com/golang/go/issues/59458#issuecomment-1513794691, we must +- // use \-separated paths in go.work use directives for this test to work +- // correctly on windows. +- var files = fmt.Sprintf(` --- work/a/go.mod -- -module a.com - @@ -122711,15 +136382,19 @@ diff -urN a/gopls/internal/regtest/workspace/fromenv_test.go b/gopls/internal/re -go 1.18 - -use ( -- $SANDBOX_WORKDIR/work/a -- $SANDBOX_WORKDIR/work/b -- $SANDBOX_WORKDIR/other/c +- %s +- %s +- %s -) --` +-`, +- filepath.Join("$SANDBOX_WORKDIR", "work", "a"), +- filepath.Join("$SANDBOX_WORKDIR", "work", "b"), +- filepath.Join("$SANDBOX_WORKDIR", "other", "c"), +- ) - - WithOptions( - WorkspaceFolders("work"), // use a nested workspace dir, so that GOWORK is outside the workspace -- EnvVars{"GOWORK": "$SANDBOX_WORKDIR/config/go.work"}, +- EnvVars{"GOWORK": filepath.Join("$SANDBOX_WORKDIR", "config", "go.work")}, - ).Run(t, files, func(t *testing.T, env *Env) { - // When we have an explicit GOWORK set, we should get a file watch request. - env.OnceMet( @@ -122738,8 +136413,8 @@ diff -urN a/gopls/internal/regtest/workspace/fromenv_test.go b/gopls/internal/re -} 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 00:00:00.000000000 +0000 -@@ -1,181 +0,0 @@ ++++ b/gopls/internal/regtest/workspace/metadata_test.go 1970-01-01 08:00:00 +@@ -1,245 +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. @@ -122801,16 +136476,7 @@ diff -urN a/gopls/internal/regtest/workspace/metadata_test.go b/gopls/internal/r -func main() {} - ` - -- WithOptions( -- // TODO(golang/go#54180): we don't run in 'experimental' mode here, because -- // with "experimentalUseInvalidMetadata", this test fails because the -- // orphaned bar.go is diagnosed using stale metadata, and then not -- // re-diagnosed when new metadata arrives. -- // -- // We could fix this by re-running diagnostics after a load, but should -- // consider whether that is worthwhile. -- Modes(Default), -- ).Run(t, src, func(t *testing.T, env *Env) { +- Run(t, src, func(t *testing.T, env *Env) { - env.OpenFile("foo.go") - env.OpenFile("bar.go") - env.OnceMet( @@ -122839,7 +136505,7 @@ diff -urN a/gopls/internal/regtest/workspace/metadata_test.go b/gopls/internal/r - // packages for bar.go - env.RegexpReplace("bar.go", "ignore", "excluded") - env.AfterChange( -- Diagnostics(env.AtRegexp("bar.go", "package (main)"), WithMessage("No packages")), +- Diagnostics(env.AtRegexp("bar.go", "package (main)"), WithMessage("not included in your workspace")), - ) - }) -} @@ -122921,9 +136587,82 @@ diff -urN a/gopls/internal/regtest/workspace/metadata_test.go b/gopls/internal/r - } - }) -} +- +-// Test for golang/go#59458. With lazy module loading, we may not need +-// transitively required modules. +-func TestNestedModuleLoading_Issue59458(t *testing.T) { +- testenv.NeedsGo1Point(t, 17) // needs lazy module loading +- +- // In this test, module b.com/nested requires b.com/other, which in turn +- // requires b.com, but b.com/nested does not reach b.com through the package +- // graph. Therefore, b.com/nested does not need b.com on 1.17 and later, +- // thanks to graph pruning. +- // +- // We verify that we can load b.com/nested successfully. Previously, we +- // couldn't, because loading the pattern b.com/nested/... matched the module +- // b.com, which exists in the module graph but does not have a go.sum entry. +- +- const proxy = ` +--- b.com@v1.2.3/go.mod -- +-module b.com +- +-go 1.18 +--- b.com@v1.2.3/b/b.go -- +-package b +- +-func Hello() {} +- +--- b.com/other@v1.4.6/go.mod -- +-module b.com/other +- +-go 1.18 +- +-require b.com v1.2.3 +--- b.com/other@v1.4.6/go.sun -- +-b.com v1.2.3 h1:AGjCxWRJLUuJiZ21IUTByr9buoa6+B6Qh5LFhVLKpn4= +--- b.com/other@v1.4.6/bar/bar.go -- +-package bar +- +-import "b.com/b" +- +-func _() { +- b.Hello() +-} +--- b.com/other@v1.4.6/foo/foo.go -- +-package foo +- +-const Foo = 0 +-` +- +- const files = ` +--- go.mod -- +-module b.com/nested +- +-go 1.18 +- +-require b.com/other v1.4.6 +--- go.sum -- +-b.com/other v1.4.6 h1:pHXSzGsk6DamYXp9uRdDB9A/ZQqAN9it+JudU0sBf94= +-b.com/other v1.4.6/go.mod h1:T0TYuGdAHw4p/l0+1P/yhhYHfZRia7PaadNVDu58OWM= +--- nested.go -- +-package nested +- +-import "b.com/other/foo" +- +-const C = foo.Foo +-` +- WithOptions( +- ProxyFiles(proxy), +- ).Run(t, files, func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- NoDiagnostics(), +- ) +- }) +-} diff -urN a/gopls/internal/regtest/workspace/misspelling_test.go b/gopls/internal/regtest/workspace/misspelling_test.go --- a/gopls/internal/regtest/workspace/misspelling_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/workspace/misspelling_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/workspace/misspelling_test.go 1970-01-01 08:00:00 @@ -1,80 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -123005,9 +136744,355 @@ diff -urN a/gopls/internal/regtest/workspace/misspelling_test.go b/gopls/interna - env.AfterChange(NoDiagnostics()) - }) -} +diff -urN a/gopls/internal/regtest/workspace/quickfix_test.go b/gopls/internal/regtest/workspace/quickfix_test.go +--- a/gopls/internal/regtest/workspace/quickfix_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/regtest/workspace/quickfix_test.go 1970-01-01 08:00:00 +@@ -1,342 +0,0 @@ +-// Copyright 2023 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 ( +- "strings" +- "testing" +- +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- "golang.org/x/tools/gopls/internal/lsp/tests/compare" +- "golang.org/x/tools/internal/testenv" +- +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +-) +- +-func TestQuickFix_UseModule(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) // needs go.work +- +- const files = ` +--- go.work -- +-go 1.20 +- +-use ( +- ./a +-) +--- a/go.mod -- +-module mod.com/a +- +-go 1.18 +- +--- a/main.go -- +-package main +- +-import "mod.com/a/lib" +- +-func main() { +- _ = lib.C +-} +- +--- a/lib/lib.go -- +-package lib +- +-const C = "b" +--- b/go.mod -- +-module mod.com/b +- +-go 1.18 +- +--- b/main.go -- +-package main +- +-import "mod.com/b/lib" +- +-func main() { +- _ = lib.C +-} +- +--- b/lib/lib.go -- +-package lib +- +-const C = "b" +-` +- +- for _, title := range []string{ +- "Use this module", +- "Use all modules", +- } { +- t.Run(title, func(t *testing.T) { +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("b/main.go") +- var d protocol.PublishDiagnosticsParams +- env.AfterChange(ReadDiagnostics("b/main.go", &d)) +- fixes := env.GetQuickFixes("b/main.go", d.Diagnostics) +- var toApply []protocol.CodeAction +- for _, fix := range fixes { +- if strings.Contains(fix.Title, title) { +- toApply = append(toApply, fix) +- } +- } +- if len(toApply) != 1 { +- t.Fatalf("codeAction: got %d quick fixes matching %q, want 1; got: %v", len(toApply), title, toApply) +- } +- env.ApplyCodeAction(toApply[0]) +- env.AfterChange(NoDiagnostics()) +- want := `go 1.20 +- +-use ( +- ./a +- ./b +-) +-` +- got := env.ReadWorkspaceFile("go.work") +- if diff := compare.Text(want, got); diff != "" { +- t.Errorf("unexpeced go.work content:\n%s", diff) +- } +- }) +- }) +- } +-} +- +-func TestQuickFix_AddGoWork(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) // needs go.work +- +- const files = ` +--- a/go.mod -- +-module mod.com/a +- +-go 1.18 +- +--- a/main.go -- +-package main +- +-import "mod.com/a/lib" +- +-func main() { +- _ = lib.C +-} +- +--- a/lib/lib.go -- +-package lib +- +-const C = "b" +--- b/go.mod -- +-module mod.com/b +- +-go 1.18 +- +--- b/main.go -- +-package main +- +-import "mod.com/b/lib" +- +-func main() { +- _ = lib.C +-} +- +--- b/lib/lib.go -- +-package lib +- +-const C = "b" +-` +- +- tests := []struct { +- name string +- file string +- title string +- want string // expected go.work content, excluding go directive line +- }{ +- { +- "use b", +- "b/main.go", +- "Add a go.work file using this module", +- ` +-use ./b +-`, +- }, +- { +- "use a", +- "a/main.go", +- "Add a go.work file using this module", +- ` +-use ./a +-`, +- }, +- { +- "use all", +- "a/main.go", +- "Add a go.work file using all modules", +- ` +-use ( +- ./a +- ./b +-) +-`, +- }, +- } +- +- for _, test := range tests { +- t.Run(test.name, func(t *testing.T) { +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile(test.file) +- var d protocol.PublishDiagnosticsParams +- env.AfterChange(ReadDiagnostics(test.file, &d)) +- fixes := env.GetQuickFixes(test.file, d.Diagnostics) +- var toApply []protocol.CodeAction +- for _, fix := range fixes { +- if strings.Contains(fix.Title, test.title) { +- toApply = append(toApply, fix) +- } +- } +- if len(toApply) != 1 { +- t.Fatalf("codeAction: got %d quick fixes matching %q, want 1; got: %v", len(toApply), test.title, toApply) +- } +- env.ApplyCodeAction(toApply[0]) +- env.AfterChange( +- NoDiagnostics(ForFile(test.file)), +- ) +- +- got := env.ReadWorkspaceFile("go.work") +- // Ignore the `go` directive, which we assume is on the first line of +- // the go.work file. This allows the test to be independent of go version. +- got = strings.Join(strings.Split(got, "\n")[1:], "\n") +- if diff := compare.Text(test.want, got); diff != "" { +- t.Errorf("unexpected go.work content:\n%s", diff) +- } +- }) +- }) +- } +-} +- +-func TestQuickFix_UnsavedGoWork(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) // needs go.work +- +- const files = ` +--- go.work -- +-go 1.21 +- +-use ( +- ./a +-) +--- a/go.mod -- +-module mod.com/a +- +-go 1.18 +- +--- a/main.go -- +-package main +- +-func main() {} +--- b/go.mod -- +-module mod.com/b +- +-go 1.18 +- +--- b/main.go -- +-package main +- +-func main() {} +-` +- +- for _, title := range []string{ +- "Use this module", +- "Use all modules", +- } { +- t.Run(title, func(t *testing.T) { +- Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("go.work") +- env.OpenFile("b/main.go") +- env.RegexpReplace("go.work", "go 1.21", "go 1.21 // arbitrary comment") +- var d protocol.PublishDiagnosticsParams +- env.AfterChange(ReadDiagnostics("b/main.go", &d)) +- fixes := env.GetQuickFixes("b/main.go", d.Diagnostics) +- var toApply []protocol.CodeAction +- for _, fix := range fixes { +- if strings.Contains(fix.Title, title) { +- toApply = append(toApply, fix) +- } +- } +- if len(toApply) != 1 { +- t.Fatalf("codeAction: got %d quick fixes matching %q, want 1; got: %v", len(toApply), title, toApply) +- } +- fix := toApply[0] +- err := env.Editor.ApplyCodeAction(env.Ctx, fix) +- if err == nil { +- t.Fatalf("codeAction(%q) succeeded unexpectedly", fix.Title) +- } +- +- if got := err.Error(); !strings.Contains(got, "must save") { +- t.Errorf("codeAction(%q) returned error %q, want containing \"must save\"", fix.Title, err) +- } +- }) +- }) +- } +-} +- +-func TestQuickFix_GOWORKOff(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) // needs go.work +- +- const files = ` +--- go.work -- +-go 1.21 +- +-use ( +- ./a +-) +--- a/go.mod -- +-module mod.com/a +- +-go 1.18 +- +--- a/main.go -- +-package main +- +-func main() {} +--- b/go.mod -- +-module mod.com/b +- +-go 1.18 +- +--- b/main.go -- +-package main +- +-func main() {} +-` +- +- for _, title := range []string{ +- "Use this module", +- "Use all modules", +- } { +- t.Run(title, func(t *testing.T) { +- WithOptions( +- EnvVars{"GOWORK": "off"}, +- ).Run(t, files, func(t *testing.T, env *Env) { +- env.OpenFile("go.work") +- env.OpenFile("b/main.go") +- var d protocol.PublishDiagnosticsParams +- env.AfterChange(ReadDiagnostics("b/main.go", &d)) +- fixes := env.GetQuickFixes("b/main.go", d.Diagnostics) +- var toApply []protocol.CodeAction +- for _, fix := range fixes { +- if strings.Contains(fix.Title, title) { +- toApply = append(toApply, fix) +- } +- } +- if len(toApply) != 1 { +- t.Fatalf("codeAction: got %d quick fixes matching %q, want 1; got: %v", len(toApply), title, toApply) +- } +- fix := toApply[0] +- err := env.Editor.ApplyCodeAction(env.Ctx, fix) +- if err == nil { +- t.Fatalf("codeAction(%q) succeeded unexpectedly", fix.Title) +- } +- +- if got := err.Error(); !strings.Contains(got, "GOWORK=off") { +- t.Errorf("codeAction(%q) returned error %q, want containing \"GOWORK=off\"", fix.Title, err) +- } +- }) +- }) +- } +-} diff -urN a/gopls/internal/regtest/workspace/standalone_test.go b/gopls/internal/regtest/workspace/standalone_test.go --- a/gopls/internal/regtest/workspace/standalone_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/regtest/workspace/standalone_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/regtest/workspace/standalone_test.go 1970-01-01 08:00:00 @@ -1,206 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -123033,7 +137118,7 @@ diff -urN a/gopls/internal/regtest/workspace/standalone_test.go b/gopls/internal --- lib/lib.go -- -package lib - --const C = 0 +-const K = 0 - -type I interface { - M() @@ -123048,13 +137133,13 @@ diff -urN a/gopls/internal/regtest/workspace/standalone_test.go b/gopls/internal - "mod.test/lib" -) - --const C = 1 +-const K = 1 - -type Mer struct{} -func (Mer) M() - -func main() { -- println(lib.C + C) +- println(lib.K + K) -} -` - WithOptions( @@ -123064,13 +137149,18 @@ diff -urN a/gopls/internal/regtest/workspace/standalone_test.go b/gopls/internal - Modes(Default), - ).Run(t, files, func(t *testing.T, env *Env) { - // Initially, gopls should not know about the standalone file as it hasn't -- // been opened. Therefore, we should only find one symbol 'C'. -- syms := env.Symbol("C") +- // been opened. Therefore, we should only find one symbol 'K'. +- // +- // (The choice of "K" is a little sleazy: it was originally "C" until +- // we started adding "unsafe" to the workspace unconditionally, which +- // caused a spurious match of "unsafe.Slice". But in practice every +- // workspace depends on unsafe.) +- syms := env.Symbol("K") - if got, want := len(syms), 1; got != want { -- t.Errorf("got %d symbols, want %d", got, want) +- t.Errorf("got %d symbols, want %d (%+v)", got, want, syms) - } - -- // Similarly, we should only find one reference to "C", and no +- // Similarly, we should only find one reference to "K", and no - // implementations of I. - checkLocations := func(method string, gotLocations []protocol.Location, wantFiles ...string) { - var gotFiles []string @@ -123087,14 +137177,14 @@ diff -urN a/gopls/internal/regtest/workspace/standalone_test.go b/gopls/internal - env.OpenFile("lib/lib.go") - env.AfterChange(NoDiagnostics()) - -- // Replacing C with D should not cause any workspace diagnostics, since we +- // Replacing K with D should not cause any workspace diagnostics, since we - // haven't yet opened the standalone file. -- env.RegexpReplace("lib/lib.go", "C", "D") +- env.RegexpReplace("lib/lib.go", "K", "D") - env.AfterChange(NoDiagnostics()) -- env.RegexpReplace("lib/lib.go", "D", "C") +- env.RegexpReplace("lib/lib.go", "D", "K") - env.AfterChange(NoDiagnostics()) - -- refs := env.References(env.RegexpSearch("lib/lib.go", "C")) +- refs := env.References(env.RegexpSearch("lib/lib.go", "K")) - checkLocations("References", refs, "lib/lib.go") - - impls := env.Implementations(env.RegexpSearch("lib/lib.go", "I")) @@ -123106,56 +137196,56 @@ diff -urN a/gopls/internal/regtest/workspace/standalone_test.go b/gopls/internal - - // Having opened the standalone file, we should find its symbols in the - // workspace. -- syms = env.Symbol("C") +- syms = env.Symbol("K") - if got, want := len(syms), 2; got != want { - t.Fatalf("got %d symbols, want %d", got, want) - } - -- foundMainC := false +- foundMainK := false - var symNames []string - for _, sym := range syms { - symNames = append(symNames, sym.Name) -- if sym.Name == "main.C" { -- foundMainC = true +- if sym.Name == "main.K" { +- foundMainK = true - } - } -- if !foundMainC { -- t.Errorf("WorkspaceSymbol(\"C\") = %v, want containing main.C", symNames) +- if !foundMainK { +- t.Errorf("WorkspaceSymbol(\"K\") = %v, want containing main.K", symNames) - } - - // We should resolve workspace definitions in the standalone file. -- fileLoc := env.GoToDefinition(env.RegexpSearch("lib/ignore.go", "lib.(C)")) +- fileLoc := env.GoToDefinition(env.RegexpSearch("lib/ignore.go", "lib.(K)")) - file := env.Sandbox.Workdir.URIToPath(fileLoc.URI) - if got, want := file, "lib/lib.go"; got != want { -- t.Errorf("GoToDefinition(lib.C) = %v, want %v", got, want) +- t.Errorf("GoToDefinition(lib.K) = %v, want %v", got, want) - } - - // ...as well as intra-file definitions -- loc := env.GoToDefinition(env.RegexpSearch("lib/ignore.go", "\\+ (C)")) -- wantLoc := env.RegexpSearch("lib/ignore.go", "const (C)") +- loc := env.GoToDefinition(env.RegexpSearch("lib/ignore.go", "\\+ (K)")) +- wantLoc := env.RegexpSearch("lib/ignore.go", "const (K)") - if loc != wantLoc { -- t.Errorf("GoToDefinition(C) = %v, want %v", loc, wantLoc) +- t.Errorf("GoToDefinition(K) = %v, want %v", loc, wantLoc) - } - -- // Renaming "lib.C" to "lib.D" should cause a diagnostic in the standalone +- // Renaming "lib.K" to "lib.D" should cause a diagnostic in the standalone - // file. -- env.RegexpReplace("lib/lib.go", "C", "D") -- env.AfterChange(Diagnostics(env.AtRegexp("lib/ignore.go", "lib.(C)"))) +- env.RegexpReplace("lib/lib.go", "K", "D") +- env.AfterChange(Diagnostics(env.AtRegexp("lib/ignore.go", "lib.(K)"))) - - // Undoing the replacement should fix diagnostics -- env.RegexpReplace("lib/lib.go", "D", "C") +- env.RegexpReplace("lib/lib.go", "D", "K") - env.AfterChange(NoDiagnostics()) - - // Now that our workspace has no errors, we should be able to find - // references and rename. -- refs = env.References(env.RegexpSearch("lib/lib.go", "C")) +- refs = env.References(env.RegexpSearch("lib/lib.go", "K")) - checkLocations("References", refs, "lib/lib.go", "lib/ignore.go") - - impls = env.Implementations(env.RegexpSearch("lib/lib.go", "I")) - checkLocations("Implementations", impls, "lib/ignore.go") - - // Renaming should rename in the standalone package. -- env.Rename(env.RegexpSearch("lib/lib.go", "C"), "D") +- env.Rename(env.RegexpSearch("lib/lib.go", "K"), "D") - env.RegexpSearch("lib/ignore.go", "lib.D") - }) -} @@ -123204,11 +137294,6 @@ diff -urN a/gopls/internal/regtest/workspace/standalone_test.go b/gopls/internal - "standaloneTags": []string{"ignore"}, - } - env.ChangeConfiguration(cfg) -- -- // TODO(golang/go#56158): gopls does not purge previously published -- // diagnostice when configuration changes. -- env.RegexpReplace("ignore.go", "arbitrary", "meaningless") -- - env.AfterChange( - NoDiagnostics(ForFile("ignore.go")), - Diagnostics(env.AtRegexp("standalone.go", "package (main)")), @@ -123217,8 +137302,8 @@ diff -urN a/gopls/internal/regtest/workspace/standalone_test.go b/gopls/internal -} 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 00:00:00.000000000 +0000 -@@ -1,1263 +0,0 @@ ++++ b/gopls/internal/regtest/workspace/workspace_test.go 1970-01-01 08:00:00 +@@ -1,1258 +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. @@ -123232,11 +137317,11 @@ diff -urN a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/ - "strings" - "testing" - +- "golang.org/x/tools/gopls/internal/bug" - "golang.org/x/tools/gopls/internal/hooks" - "golang.org/x/tools/gopls/internal/lsp" - "golang.org/x/tools/gopls/internal/lsp/fake" - "golang.org/x/tools/gopls/internal/lsp/protocol" -- "golang.org/x/tools/internal/bug" - "golang.org/x/tools/internal/gocommand" - "golang.org/x/tools/internal/testenv" - @@ -123398,35 +137483,12 @@ diff -urN a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/ -replace random.org => %s -`, env.ReadWorkspaceFile("pkg/go.mod"), dir) - env.WriteWorkspaceFile("pkg/go.mod", goModWithReplace) -- env.AfterChange( +- env.Await( - LogMatching(protocol.Info, `packages\.Load #\d+\n`, 2, false), - ) - }) -} - --// 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.AfterChange( -- UnregistrationMatching("didChangeWatchedFiles"), -- RegistrationMatching("didChangeWatchedFiles"), -- ) -- }) --} -- -const workspaceModuleProxy = ` --- example.com@v1.2.3/go.mod -- -module example.com @@ -123796,10 +137858,18 @@ diff -urN a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/ -` - WithOptions( - ProxyFiles(workspaceModuleProxy), +- Settings{ +- "subdirWatchPatterns": "on", +- }, - ).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. +- // Initially, the go.work should cause only the a.com module to be loaded, +- // so we shouldn't get any file watches for modb. Further validate this by +- // jumping to a definition in b.com and ensuring that we go to the module +- // cache. +- env.OnceMet( +- InitialWorkspaceLoad, +- NoFileWatchMatching("modb"), +- ) - env.OpenFile("moda/a/a.go") - env.Await(env.DoneWithOpen()) - @@ -123831,9 +137901,13 @@ diff -urN a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/ -`) - - // As of golang/go#54069, writing go.work to the workspace triggers a -- // workspace reload. +- // workspace reload, and new file watches. - env.AfterChange( - Diagnostics(env.AtRegexp("modb/b/b.go", "x")), +- // TODO(golang/go#60340): we don't get a file watch yet, because +- // updateWatchedDirectories runs before snapshot.load. Instead, we get it +- // after the next change (the didOpen below). +- // FileWatchMatching("modb"), - ) - - // Jumping to definition should now go to b.com in the workspace. @@ -123844,7 +137918,13 @@ diff -urN a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/ - // 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.AfterChange() +- env.AfterChange( +- // TODO(golang/go#60340): delete this expectation in favor of +- // the commented-out expectation above, once we fix the evaluation order +- // of file watches. We should not have to wait for a second change to get +- // the correct watches. +- FileWatchMatching("modb"), +- ) - env.SetBufferContent("go.work", `go 1.17 - -use ( @@ -123868,7 +137948,7 @@ diff -urN a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/ - } - - // This fails if guarded with a OnceMet(DoneWithSave(), ...), because it is -- // debounced (and therefore not synchronous with the change). +- // delayed (and therefore not synchronous with the change). - env.Await(NoDiagnostics(ForFile("modb/go.mod"))) - - // Test Formatting. @@ -124205,1149 +138285,2262 @@ diff -urN a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/ - } - env.ApplyQuickFixes("b/go.mod", []protocol.Diagnostic{d}) - } -- env.AfterChange( -- NoDiagnostics(ForFile("b/go.mod")), -- ) -- }) +- env.AfterChange( +- NoDiagnostics(ForFile("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( +- EnvVars{"GOPATH": filepath.FromSlash("$SANDBOX_WORKDIR/gopath")}, +- Modes(Default), +- ).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 TestAddAndRemoveGoWork(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) +- // Use a workspace with a module in the root directory to exercise the case +- // where a go.work is added to the existing root directory. This verifies +- // that we're detecting changes to the module source, not just the root +- // directory. +- const nomod = ` +--- go.mod -- +-module a.com +- +-go 1.16 +--- main.go -- +-package main +- +-func main() {} +--- b/go.mod -- +-module b.com +- +-go 1.16 +--- b/main.go -- +-package main +- +-func main() {} +-` +- WithOptions( +- Modes(Default), +- ).Run(t, nomod, func(t *testing.T, env *Env) { +- env.OpenFile("main.go") +- env.OpenFile("b/main.go") +- // Since b/main.go is not in the workspace, it should have a warning on its +- // package declaration. +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- Diagnostics(env.AtRegexp("b/main.go", "package (main)")), +- ) +- env.WriteWorkspaceFile("go.work", `go 1.16 +- +-use ( +- . +- b +-) +-`) +- env.AfterChange(NoDiagnostics()) +- // Removing the go.work file should put us back where we started. +- env.RemoveWorkspaceFile("go.work") +- +- // TODO(golang/go#57558, golang/go#57508): file watching is asynchronous, +- // and we must wait for the view to be reconstructed before touching +- // b/main.go, so that the new view "knows" about b/main.go. This is simply +- // a bug, but awaiting the change here avoids it. +- env.Await(env.DoneWithChangeWatchedFiles()) +- +- // TODO(rfindley): fix this bug: reopening b/main.go is necessary here +- // because we no longer "see" the file in any view. +- env.CloseBuffer("b/main.go") +- env.OpenFile("b/main.go") +- +- env.AfterChange( +- NoDiagnostics(ForFile("main.go")), +- Diagnostics(env.AtRegexp("b/main.go", "package (main)")), +- ) +- }) +-} +- +-// Tests the fix for golang/go#52500. +-func TestChangeTestVariant_Issue52500(t *testing.T) { +- 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( +- Diagnostics(env.AtRegexp("other_test.go", "Server")), +- Diagnostics(env.AtRegexp("main_test.go", "otherConst")), +- ) +- env.RegexpReplace("other_test.go", "main", "main_test") +- env.AfterChange( +- NoDiagnostics(ForFile("other_test.go")), +- NoDiagnostics(ForFile("main_test.go")), +- ) +- +- // This will cause a test failure if other_test.go is not in any package. +- _ = env.GoToDefinition(env.RegexpSearch("other_test.go", "Server")) +- }) +-} +- +-// Test for golang/go#48929. +-func TestClearNonWorkspaceDiagnostics(t *testing.T) { +- testenv.NeedsGo1Point(t, 18) // uses go.work +- +- const ws = ` +--- go.work -- +-go 1.18 +- +-use ( +- ./b +-) +--- a/go.mod -- +-module a +- +-go 1.17 +--- a/main.go -- +-package main +- +-func main() { +- var V string +-} +--- b/go.mod -- +-module b +- +-go 1.17 +--- b/main.go -- +-package b +- +-import ( +- _ "fmt" +-) +-` +- Run(t, ws, func(t *testing.T, env *Env) { +- env.OpenFile("b/main.go") +- env.AfterChange( +- NoDiagnostics(ForFile("a/main.go")), +- ) +- env.OpenFile("a/main.go") +- env.AfterChange( +- Diagnostics(env.AtRegexp("a/main.go", "V"), WithMessage("not used")), +- ) +- env.CloseBuffer("a/main.go") +- +- // Make an arbitrary edit because gopls explicitly diagnoses a/main.go +- // whenever it is "changed". +- // +- // TODO(rfindley): it should not be necessary to make another edit here. +- // Gopls should be smart enough to avoid diagnosing a. +- env.RegexpReplace("b/main.go", "package b", "package b // a package") +- env.AfterChange( +- NoDiagnostics(ForFile("a/main.go")), +- ) +- }) +-} +- +-// Test that we don't get a version warning when the Go version in PATH is +-// supported. +-func TestOldGoNotification_SupportedVersion(t *testing.T) { +- v := goVersion(t) +- if v < lsp.OldestSupportedGoVersion() { +- t.Skipf("go version 1.%d is unsupported", v) +- } +- +- Run(t, "", func(t *testing.T, env *Env) { +- env.OnceMet( +- InitialWorkspaceLoad, +- NoShownMessage("upgrade"), +- ) +- }) +-} +- +-// Test that we do get a version warning when the Go version in PATH is +-// unsupported, though this test may never execute if we stop running CI at +-// legacy Go versions (see also TestOldGoNotification_Fake) +-func TestOldGoNotification_UnsupportedVersion(t *testing.T) { +- v := goVersion(t) +- if v >= lsp.OldestSupportedGoVersion() { +- t.Skipf("go version 1.%d is supported", v) +- } +- +- Run(t, "", func(t *testing.T, env *Env) { +- env.Await( +- // Note: cannot use OnceMet(InitialWorkspaceLoad, ...) here, as the +- // upgrade message may race with the IWL. +- ShownMessage("Please upgrade"), +- ) +- }) +-} +- +-func TestOldGoNotification_Fake(t *testing.T) { +- // Get the Go version from path, and make sure it's unsupported. +- // +- // In the future we'll stop running CI on legacy Go versions. By mutating the +- // oldest supported Go version here, we can at least ensure that the +- // ShowMessage pop-up works. +- ctx := context.Background() +- goversion, err := gocommand.GoVersion(ctx, gocommand.Invocation{}, &gocommand.Runner{}) +- if err != nil { +- t.Fatal(err) +- } +- defer func(t []lsp.GoVersionSupport) { +- lsp.GoVersionTable = t +- }(lsp.GoVersionTable) +- lsp.GoVersionTable = []lsp.GoVersionSupport{ +- {GoVersion: goversion, InstallGoplsVersion: "v1.0.0"}, +- } +- +- Run(t, "", func(t *testing.T, env *Env) { +- env.Await( +- // Note: cannot use OnceMet(InitialWorkspaceLoad, ...) here, as the +- // upgrade message may race with the IWL. +- ShownMessage("Please upgrade"), +- ) +- }) +-} +- +-// goVersion returns the version of the Go command in PATH. +-func goVersion(t *testing.T) int { +- t.Helper() +- ctx := context.Background() +- goversion, err := gocommand.GoVersion(ctx, gocommand.Invocation{}, &gocommand.Runner{}) +- if err != nil { +- t.Fatal(err) +- } +- return goversion +-} +diff -urN a/gopls/internal/span/parse.go b/gopls/internal/span/parse.go +--- a/gopls/internal/span/parse.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/span/parse.go 1970-01-01 08:00:00 +@@ -1,114 +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 span +- +-import ( +- "path/filepath" +- "strconv" +- "strings" +- "unicode/utf8" +-) +- +-// Parse returns the location represented by the input. +-// Only file paths are accepted, not URIs. +-// The returned span will be normalized, and thus if printed may produce a +-// different string. +-func Parse(input string) Span { +- return ParseInDir(input, ".") +-} +- +-// ParseInDir is like Parse, but interprets paths relative to wd. +-func ParseInDir(input, wd string) Span { +- uri := func(path string) URI { +- if !filepath.IsAbs(path) { +- path = filepath.Join(wd, path) +- } +- return URIFromPath(path) +- } +- // :0:0#0-0:0#0 +- valid := input +- var hold, offset int +- hadCol := false +- suf := rstripSuffix(input) +- if suf.sep == "#" { +- offset = suf.num +- suf = rstripSuffix(suf.remains) +- } +- if suf.sep == ":" { +- valid = suf.remains +- hold = suf.num +- hadCol = true +- suf = rstripSuffix(suf.remains) +- } +- switch { +- case suf.sep == ":": +- return New(uri(suf.remains), NewPoint(suf.num, hold, offset), Point{}) +- case suf.sep == "-": +- // we have a span, fall out of the case to continue +- default: +- // separator not valid, rewind to either the : or the start +- return New(uri(valid), NewPoint(hold, 0, offset), Point{}) +- } +- // only the span form can get here +- // at this point we still don't know what the numbers we have mean +- // if have not yet seen a : then we might have either a line or a column depending +- // on whether start has a column or not +- // we build an end point and will fix it later if needed +- end := NewPoint(suf.num, hold, offset) +- hold, offset = 0, 0 +- suf = rstripSuffix(suf.remains) +- if suf.sep == "#" { +- offset = suf.num +- suf = rstripSuffix(suf.remains) +- } +- if suf.sep != ":" { +- // turns out we don't have a span after all, rewind +- return New(uri(valid), end, Point{}) +- } +- valid = suf.remains +- hold = suf.num +- suf = rstripSuffix(suf.remains) +- if suf.sep != ":" { +- // line#offset only +- return New(uri(valid), NewPoint(hold, 0, offset), end) +- } +- // we have a column, so if end only had one number, it is also the column +- if !hadCol { +- end = NewPoint(suf.num, end.v.Line, end.v.Offset) +- } +- return New(uri(suf.remains), NewPoint(suf.num, hold, offset), end) +-} +- +-type suffix struct { +- remains string +- sep string +- num int +-} +- +-func rstripSuffix(input string) suffix { +- if len(input) == 0 { +- return suffix{"", "", -1} +- } +- remains := input +- +- // Remove optional trailing decimal number. +- num := -1 +- last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) +- if last >= 0 && last < len(remains)-1 { +- number, err := strconv.ParseInt(remains[last+1:], 10, 64) +- if err == nil { +- num = int(number) +- remains = remains[:last+1] +- } +- } +- // now see if we have a trailing separator +- r, w := utf8.DecodeLastRuneInString(remains) +- // TODO(adonovan): this condition is clearly wrong. Should the third byte be '-'? +- if r != ':' && r != '#' && r == '#' { +- return suffix{input, "", -1} +- } +- remains = remains[:len(remains)-w] +- return suffix{remains, string(r), num} -} +diff -urN a/gopls/internal/span/span.go b/gopls/internal/span/span.go +--- a/gopls/internal/span/span.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/span/span.go 1970-01-01 08:00:00 +@@ -1,249 +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. - --// 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 +-// Package span contains support for representing with positions and ranges in +-// text files. +-package span - --go 1.12 ---- a/a.go -- --package a +-import ( +- "encoding/json" +- "fmt" +- "go/token" +- "path" +- "sort" +- "strings" - --func _() {} ---- a/c/c.go -- --package c ---- gopath/src/b/b.go -- --package b ---- gopath/pkg/mod/example.com/go.mod -- --module example.com +- "golang.org/x/tools/gopls/internal/lsp/safetoken" +-) - --go 1.12 ---- gopath/pkg/mod/example.com/main.go -- --package main --` -- WithOptions( -- EnvVars{"GOPATH": filepath.FromSlash("$SANDBOX_WORKDIR/gopath")}, -- Modes(Default), -- ).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), -- ) -- }) +-// A Span represents a range of text within a source file. The start +-// and end points of a valid span may be hold either its byte offset, +-// or its (line, column) pair, or both. Columns are measured in bytes. +-// +-// Spans are appropriate in user interfaces (e.g. command-line tools) +-// and tests where a position is notated without access to the content +-// of the file. +-// +-// Use protocol.Mapper to convert between Span and other +-// representations, such as go/token (also UTF-8) or the LSP protocol +-// (UTF-16). The latter requires access to file contents. +-// +-// See overview comments at ../lsp/protocol/mapper.go. +-type Span struct { +- v span -} - --func TestAddAndRemoveGoWork(t *testing.T) { -- testenv.NeedsGo1Point(t, 18) -- // Use a workspace with a module in the root directory to exercise the case -- // where a go.work is added to the existing root directory. This verifies -- // that we're detecting changes to the module source, not just the root -- // directory. -- const nomod = ` ---- go.mod -- --module a.com +-// Point represents a single point within a file. +-// In general this should only be used as part of a Span, as on its own it +-// does not carry enough information. +-type Point struct { +- v point +-} - --go 1.16 ---- main.go -- --package main +-// The private span/point types have public fields to support JSON +-// encoding, but the public Span/Point types hide these fields by +-// defining methods that shadow them. (This is used by a few of the +-// command-line tool subcommands, which emit spans and have a -json +-// flag.) - --func main() {} ---- b/go.mod -- --module b.com +-type span struct { +- URI URI `json:"uri"` +- Start point `json:"start"` +- End point `json:"end"` +-} - --go 1.16 ---- b/main.go -- --package main +-type point struct { +- Line int `json:"line"` // 1-based line number +- Column int `json:"column"` // 1-based, UTF-8 codes (bytes) +- Offset int `json:"offset"` // 0-based byte offset +-} - --func main() {} --` -- WithOptions( -- Modes(Default), -- ).Run(t, nomod, func(t *testing.T, env *Env) { -- env.OpenFile("main.go") -- env.OpenFile("b/main.go") -- // Since b/main.go is not in the workspace, it should have a warning on its -- // package declaration. -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- Diagnostics(AtPosition("b/main.go", 0, 0)), -- ) -- env.WriteWorkspaceFile("go.work", `go 1.16 +-// Invalid is a span that reports false from IsValid +-var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}} - --use ( -- . -- b --) --`) -- env.AfterChange(NoDiagnostics()) -- // Removing the go.work file should put us back where we started. -- env.RemoveWorkspaceFile("go.work") +-var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}} - -- // TODO(golang/go#57558, golang/go#57508): file watching is asynchronous, -- // and we must wait for the view to be reconstructed before touching -- // b/main.go, so that the new view "knows" about b/main.go. This is simply -- // a bug, but awaiting the change here avoids it. -- env.Await(env.DoneWithChangeWatchedFiles()) +-func New(uri URI, start, end Point) Span { +- s := Span{v: span{URI: uri, Start: start.v, End: end.v}} +- s.v.clean() +- return s +-} - -- // TODO(rfindley): fix this bug: reopening b/main.go is necessary here -- // because we no longer "see" the file in any view. -- env.CloseBuffer("b/main.go") -- env.OpenFile("b/main.go") +-func NewPoint(line, col, offset int) Point { +- p := Point{v: point{Line: line, Column: col, Offset: offset}} +- p.v.clean() +- return p +-} - -- env.AfterChange( -- NoDiagnostics(ForFile("main.go")), -- Diagnostics(AtPosition("b/main.go", 0, 0)), -- ) +-// SortSpans sorts spans into a stable but unspecified order. +-func SortSpans(spans []Span) { +- sort.SliceStable(spans, func(i, j int) bool { +- return compare(spans[i], spans[j]) < 0 - }) -} - --// Tests the fix for golang/go#52500. --func TestChangeTestVariant_Issue52500(t *testing.T) { -- const src = ` ---- go.mod -- --module mod.test +-// compare implements a three-valued ordered comparison of Spans. +-func compare(a, b Span) int { +- // This is a textual comparison. It does not perform path +- // cleaning, case folding, resolution of symbolic links, +- // testing for existence, or any I/O. +- if cmp := strings.Compare(string(a.URI()), string(b.URI())); cmp != 0 { +- return cmp +- } +- if cmp := comparePoint(a.v.Start, b.v.Start); cmp != 0 { +- return cmp +- } +- return comparePoint(a.v.End, b.v.End) +-} - --go 1.12 ---- main_test.go -- --package main_test +-func comparePoint(a, b point) int { +- if !a.hasPosition() { +- if a.Offset < b.Offset { +- return -1 +- } +- if a.Offset > b.Offset { +- return 1 +- } +- return 0 +- } +- if a.Line < b.Line { +- return -1 +- } +- if a.Line > b.Line { +- return 1 +- } +- if a.Column < b.Column { +- return -1 +- } +- if a.Column > b.Column { +- return 1 +- } +- return 0 +-} - --type Server struct{} +-func (s Span) HasPosition() bool { return s.v.Start.hasPosition() } +-func (s Span) HasOffset() bool { return s.v.Start.hasOffset() } +-func (s Span) IsValid() bool { return s.v.Start.isValid() } +-func (s Span) IsPoint() bool { return s.v.Start == s.v.End } +-func (s Span) URI() URI { return s.v.URI } +-func (s Span) Start() Point { return Point{s.v.Start} } +-func (s Span) End() Point { return Point{s.v.End} } +-func (s *Span) MarshalJSON() ([]byte, error) { return json.Marshal(&s.v) } +-func (s *Span) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &s.v) } - --const mainConst = otherConst ---- other_test.go -- --package main_test +-func (p Point) HasPosition() bool { return p.v.hasPosition() } +-func (p Point) HasOffset() bool { return p.v.hasOffset() } +-func (p Point) IsValid() bool { return p.v.isValid() } +-func (p *Point) MarshalJSON() ([]byte, error) { return json.Marshal(&p.v) } +-func (p *Point) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &p.v) } +-func (p Point) Line() int { +- if !p.v.hasPosition() { +- panic(fmt.Errorf("position not set in %v", p.v)) +- } +- return p.v.Line +-} +-func (p Point) Column() int { +- if !p.v.hasPosition() { +- panic(fmt.Errorf("position not set in %v", p.v)) +- } +- return p.v.Column +-} +-func (p Point) Offset() int { +- if !p.v.hasOffset() { +- panic(fmt.Errorf("offset not set in %v", p.v)) +- } +- return p.v.Offset +-} - --const otherConst = 0 +-func (p point) hasPosition() bool { return p.Line > 0 } +-func (p point) hasOffset() bool { return p.Offset >= 0 } +-func (p point) isValid() bool { return p.hasPosition() || p.hasOffset() } +-func (p point) isZero() bool { +- return (p.Line == 1 && p.Column == 1) || (!p.hasPosition() && p.Offset == 0) +-} - --func (Server) Foo() {} --` +-func (s *span) clean() { +- //this presumes the points are already clean +- if !s.End.isValid() || (s.End == point{}) { +- s.End = s.Start +- } +-} - -- Run(t, src, func(t *testing.T, env *Env) { -- env.OpenFile("other_test.go") -- env.RegexpReplace("other_test.go", "main_test", "main") +-func (p *point) clean() { +- if p.Line < 0 { +- p.Line = 0 +- } +- if p.Column <= 0 { +- if p.Line > 0 { +- p.Column = 1 +- } else { +- p.Column = 0 +- } +- } +- if p.Offset == 0 && (p.Line > 1 || p.Column > 1) { +- p.Offset = -1 +- } +-} - -- // 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( -- Diagnostics(env.AtRegexp("other_test.go", "Server")), -- Diagnostics(env.AtRegexp("main_test.go", "otherConst")), -- ) -- env.RegexpReplace("other_test.go", "main", "main_test") -- env.AfterChange( -- NoDiagnostics(ForFile("other_test.go")), -- NoDiagnostics(ForFile("main_test.go")), -- ) +-// Format implements fmt.Formatter to print the Location in a standard form. +-// The format produced is one that can be read back in using Parse. +-func (s Span) Format(f fmt.State, c rune) { +- fullForm := f.Flag('+') +- preferOffset := f.Flag('#') +- // we should always have a uri, simplify if it is file format +- //TODO: make sure the end of the uri is unambiguous +- uri := string(s.v.URI) +- if c == 'f' { +- uri = path.Base(uri) +- } else if !fullForm { +- uri = s.v.URI.Filename() +- } +- fmt.Fprint(f, uri) +- if !s.IsValid() || (!fullForm && s.v.Start.isZero() && s.v.End.isZero()) { +- return +- } +- // see which bits of start to write +- printOffset := s.HasOffset() && (fullForm || preferOffset || !s.HasPosition()) +- printLine := s.HasPosition() && (fullForm || !printOffset) +- printColumn := printLine && (fullForm || (s.v.Start.Column > 1 || s.v.End.Column > 1)) +- fmt.Fprint(f, ":") +- if printLine { +- fmt.Fprintf(f, "%d", s.v.Start.Line) +- } +- if printColumn { +- fmt.Fprintf(f, ":%d", s.v.Start.Column) +- } +- if printOffset { +- fmt.Fprintf(f, "#%d", s.v.Start.Offset) +- } +- // start is written, do we need end? +- if s.IsPoint() { +- return +- } +- // we don't print the line if it did not change +- printLine = fullForm || (printLine && s.v.End.Line > s.v.Start.Line) +- fmt.Fprint(f, "-") +- if printLine { +- fmt.Fprintf(f, "%d", s.v.End.Line) +- } +- if printColumn { +- if printLine { +- fmt.Fprint(f, ":") +- } +- fmt.Fprintf(f, "%d", s.v.End.Column) +- } +- if printOffset { +- fmt.Fprintf(f, "#%d", s.v.End.Offset) +- } +-} - -- // This will cause a test failure if other_test.go is not in any package. -- _ = env.GoToDefinition(env.RegexpSearch("other_test.go", "Server")) -- }) +-// SetRange implements packagestest.rangeSetter, allowing +-// gopls' test suites to use Spans instead of Range in parameters. +-func (span *Span) SetRange(file *token.File, start, end token.Pos) { +- point := func(pos token.Pos) Point { +- posn := safetoken.Position(file, pos) +- return NewPoint(posn.Line, posn.Column, posn.Offset) +- } +- *span = New(URIFromPath(file.Name()), point(start), point(end)) -} +diff -urN a/gopls/internal/span/span_test.go b/gopls/internal/span/span_test.go +--- a/gopls/internal/span/span_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/span/span_test.go 1970-01-01 08:00:00 +@@ -1,57 +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. - --// Test for golang/go#48929. --func TestClearNonWorkspaceDiagnostics(t *testing.T) { -- testenv.NeedsGo1Point(t, 18) // uses go.work +-package span_test - -- const ws = ` ---- go.work -- --go 1.18 +-import ( +- "fmt" +- "path/filepath" +- "strings" +- "testing" - --use ( -- ./b +- "golang.org/x/tools/gopls/internal/span" -) ---- a/go.mod -- --module a - --go 1.17 ---- a/main.go -- --package main +-func TestFormat(t *testing.T) { +- formats := []string{"%v", "%#v", "%+v"} - --func main() { -- var V string +- // Element 0 is the input, and the elements 0-2 are the expected +- // output in [%v %#v %+v] formats. Thus the first must be in +- // canonical form (invariant under span.Parse + fmt.Sprint). +- // The '#' form displays offsets; the '+' form outputs a URI. +- // If len=4, element 0 is a noncanonical input and 1-3 are expected outputs. +- for _, test := range [][]string{ +- {"C:/file_a", "C:/file_a", "file:///C:/file_a:#0"}, +- {"C:/file_b:1:2", "C:/file_b:1:2", "file:///C:/file_b:1:2"}, +- {"C:/file_c:1000", "C:/file_c:1000", "file:///C:/file_c:1000:1"}, +- {"C:/file_d:14:9", "C:/file_d:14:9", "file:///C:/file_d:14:9"}, +- {"C:/file_e:1:2-7", "C:/file_e:1:2-7", "file:///C:/file_e:1:2-1:7"}, +- {"C:/file_f:500-502", "C:/file_f:500-502", "file:///C:/file_f:500:1-502:1"}, +- {"C:/file_g:3:7-8", "C:/file_g:3:7-8", "file:///C:/file_g:3:7-3:8"}, +- {"C:/file_h:3:7-4:8", "C:/file_h:3:7-4:8", "file:///C:/file_h:3:7-4:8"}, +- {"C:/file_i:#100", "C:/file_i:#100", "file:///C:/file_i:#100"}, +- {"C:/file_j:#26-#28", "C:/file_j:#26-#28", "file:///C:/file_j:#26-0#28"}, // 0#28? +- {"C:/file_h:3:7#26-4:8#37", // not canonical +- "C:/file_h:3:7-4:8", "C:/file_h:#26-#37", "file:///C:/file_h:3:7#26-4:8#37"}} { +- input := test[0] +- spn := span.Parse(input) +- wants := test[0:3] +- if len(test) == 4 { +- wants = test[1:4] +- } +- for i, format := range formats { +- want := toPath(wants[i]) +- if got := fmt.Sprintf(format, spn); got != want { +- t.Errorf("Sprintf(%q, %q) = %q, want %q", format, input, got, want) +- } +- } +- } -} ---- b/go.mod -- --module b - --go 1.17 ---- b/main.go -- --package b +-func toPath(value string) string { +- if strings.HasPrefix(value, "file://") { +- return value +- } +- return filepath.FromSlash(value) +-} +diff -urN a/gopls/internal/span/uri.go b/gopls/internal/span/uri.go +--- a/gopls/internal/span/uri.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/span/uri.go 1970-01-01 08:00:00 +@@ -1,177 +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 span - -import ( -- _ "fmt" +- "fmt" +- "net/url" +- "os" +- "path/filepath" +- "runtime" +- "strings" +- "unicode" -) --` -- Run(t, ws, func(t *testing.T, env *Env) { -- env.OpenFile("b/main.go") -- env.AfterChange( -- NoDiagnostics(ForFile("a/main.go")), -- ) -- env.OpenFile("a/main.go") -- env.AfterChange( -- Diagnostics(env.AtRegexp("a/main.go", "V"), WithMessage("not used")), -- ) -- env.CloseBuffer("a/main.go") - -- // Make an arbitrary edit because gopls explicitly diagnoses a/main.go -- // whenever it is "changed". -- // -- // TODO(rfindley): it should not be necessary to make another edit here. -- // Gopls should be smart enough to avoid diagnosing a. -- env.RegexpReplace("b/main.go", "package b", "package b // a package") -- env.AfterChange( -- NoDiagnostics(ForFile("a/main.go")), -- ) -- }) +-const fileScheme = "file" +- +-// URI represents the full URI for a file. +-type URI string +- +-func (uri URI) IsFile() bool { +- return strings.HasPrefix(string(uri), "file://") +-} +- +-// Filename returns the file path for the given URI. +-// It is an error to call this on a URI that is not a valid filename. +-func (uri URI) Filename() string { +- filename, err := filename(uri) +- if err != nil { +- panic(err) +- } +- return filepath.FromSlash(filename) -} - --// Test that we don't get a version warning when the Go version in PATH is --// supported. --func TestOldGoNotification_SupportedVersion(t *testing.T) { -- v := goVersion(t) -- if v < lsp.OldestSupportedGoVersion() { -- t.Skipf("go version 1.%d is unsupported", v) +-func filename(uri URI) (string, error) { +- if uri == "" { +- return "", nil - } - -- Run(t, "", func(t *testing.T, env *Env) { -- env.OnceMet( -- InitialWorkspaceLoad, -- NoShownMessage("upgrade"), -- ) -- }) +- // This conservative check for the common case +- // of a simple non-empty absolute POSIX filename +- // avoids the allocation of a net.URL. +- if strings.HasPrefix(string(uri), "file:///") { +- rest := string(uri)[len("file://"):] // leave one slash +- for i := 0; i < len(rest); i++ { +- b := rest[i] +- // Reject these cases: +- if b < ' ' || b == 0x7f || // control character +- b == '%' || b == '+' || // URI escape +- b == ':' || // Windows drive letter +- b == '@' || b == '&' || b == '?' { // authority or query +- goto slow +- } +- } +- return rest, nil +- } +-slow: +- +- u, err := url.ParseRequestURI(string(uri)) +- if err != nil { +- return "", err +- } +- if u.Scheme != fileScheme { +- return "", fmt.Errorf("only file URIs are supported, got %q from %q", u.Scheme, uri) +- } +- // If the URI is a Windows URI, we trim the leading "/" and uppercase +- // the drive letter, which will never be case sensitive. +- if isWindowsDriveURIPath(u.Path) { +- u.Path = strings.ToUpper(string(u.Path[1])) + u.Path[2:] +- } +- +- return u.Path, nil -} - --// Test that we do get a version warning when the Go version in PATH is --// unsupported, though this test may never execute if we stop running CI at --// legacy Go versions (see also TestOldGoNotification_Fake) --func TestOldGoNotification_UnsupportedVersion(t *testing.T) { -- v := goVersion(t) -- if v >= lsp.OldestSupportedGoVersion() { -- t.Skipf("go version 1.%d is supported", v) +-// TODO(adonovan): document this function, and any invariants of +-// span.URI that it is supposed to establish. +-func URIFromURI(s string) URI { +- if !strings.HasPrefix(s, "file://") { +- return URI(s) - } - -- Run(t, "", func(t *testing.T, env *Env) { -- env.Await( -- // Note: cannot use OnceMet(InitialWorkspaceLoad, ...) here, as the -- // upgrade message may race with the IWL. -- ShownMessage("Please upgrade"), -- ) -- }) +- if !strings.HasPrefix(s, "file:///") { +- // VS Code sends URLs with only two slashes, which are invalid. golang/go#39789. +- s = "file:///" + s[len("file://"):] +- } +- // Even though the input is a URI, it may not be in canonical form. VS Code +- // in particular over-escapes :, @, etc. Unescape and re-encode to canonicalize. +- path, err := url.PathUnescape(s[len("file://"):]) +- if err != nil { +- panic(err) +- } +- +- // File URIs from Windows may have lowercase drive letters. +- // Since drive letters are guaranteed to be case insensitive, +- // we change them to uppercase to remain consistent. +- // For example, file:///c:/x/y/z becomes file:///C:/x/y/z. +- if isWindowsDriveURIPath(path) { +- path = path[:1] + strings.ToUpper(string(path[1])) + path[2:] +- } +- u := url.URL{Scheme: fileScheme, Path: path} +- return URI(u.String()) -} - --func TestOldGoNotification_Fake(t *testing.T) { -- // Get the Go version from path, and make sure it's unsupported. -- // -- // In the future we'll stop running CI on legacy Go versions. By mutating the -- // oldest supported Go version here, we can at least ensure that the -- // ShowMessage pop-up works. -- ctx := context.Background() -- goversion, err := gocommand.GoVersion(ctx, gocommand.Invocation{}, &gocommand.Runner{}) +-// SameExistingFile reports whether two spans denote the +-// same existing file by querying the file system. +-func SameExistingFile(a, b URI) bool { +- fa, err := filename(a) - if err != nil { -- t.Fatal(err) +- return false - } -- defer func(t []lsp.GoVersionSupport) { -- lsp.GoVersionTable = t -- }(lsp.GoVersionTable) -- lsp.GoVersionTable = []lsp.GoVersionSupport{ -- {GoVersion: goversion, InstallGoplsVersion: "v1.0.0"}, +- fb, err := filename(b) +- if err != nil { +- return false +- } +- infoa, err := os.Stat(filepath.FromSlash(fa)) +- if err != nil { +- return false +- } +- infob, err := os.Stat(filepath.FromSlash(fb)) +- if err != nil { +- return false - } +- return os.SameFile(infoa, infob) +-} - -- Run(t, "", func(t *testing.T, env *Env) { -- env.Await( -- // Note: cannot use OnceMet(InitialWorkspaceLoad, ...) here, as the -- // upgrade message may race with the IWL. -- ShownMessage("Please upgrade"), -- ) -- }) +-// URIFromPath returns a span URI for the supplied file path. +-// +-// For empty paths, URIFromPath returns the empty URI "". +-// For non-empty paths, URIFromPath returns a uri with the file:// scheme. +-func URIFromPath(path string) URI { +- if path == "" { +- return "" +- } +- // Handle standard library paths that contain the literal "$GOROOT". +- // TODO(rstambler): The go/packages API should allow one to determine a user's $GOROOT. +- const prefix = "$GOROOT" +- if len(path) >= len(prefix) && strings.EqualFold(prefix, path[:len(prefix)]) { +- suffix := path[len(prefix):] +- path = runtime.GOROOT() + suffix +- } +- if !isWindowsDrivePath(path) { +- if abs, err := filepath.Abs(path); err == nil { +- path = abs +- } +- } +- // Check the file path again, in case it became absolute. +- if isWindowsDrivePath(path) { +- path = "/" + strings.ToUpper(string(path[0])) + path[1:] +- } +- path = filepath.ToSlash(path) +- u := url.URL{ +- Scheme: fileScheme, +- Path: path, +- } +- return URI(u.String()) -} - --// goVersion returns the version of the Go command in PATH. --func goVersion(t *testing.T) int { -- t.Helper() -- ctx := context.Background() -- goversion, err := gocommand.GoVersion(ctx, gocommand.Invocation{}, &gocommand.Runner{}) -- if err != nil { -- t.Fatal(err) +-// isWindowsDrivePath returns true if the file path is of the form used by +-// Windows. We check if the path begins with a drive letter, followed by a ":". +-// For example: C:/x/y/z. +-func isWindowsDrivePath(path string) bool { +- if len(path) < 3 { +- return false - } -- return goversion +- return unicode.IsLetter(rune(path[0])) && path[1] == ':' -} -diff -urN a/gopls/internal/span/parse.go b/gopls/internal/span/parse.go ---- a/gopls/internal/span/parse.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/span/parse.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,114 +0,0 @@ +- +-// isWindowsDriveURIPath returns true if the file URI is of the format used by +-// Windows URIs. The url.Parse package does not specially handle Windows paths +-// (see golang/go#6027), so we check if the URI path has a drive prefix (e.g. "/C:"). +-func isWindowsDriveURIPath(uri string) bool { +- if len(uri) < 4 { +- return false +- } +- return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':' +-} +diff -urN a/gopls/internal/span/uri_test.go b/gopls/internal/span/uri_test.go +--- a/gopls/internal/span/uri_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/span/uri_test.go 1970-01-01 08:00:00 +@@ -1,117 +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 span +-//go:build !windows +-// +build !windows +- +-package span_test - -import ( -- "path/filepath" -- "strconv" -- "strings" -- "unicode/utf8" +- "testing" +- +- "golang.org/x/tools/gopls/internal/span" -) - --// Parse returns the location represented by the input. --// Only file paths are accepted, not URIs. --// The returned span will be normalized, and thus if printed may produce a --// different string. --func Parse(input string) Span { -- return ParseInDir(input, ".") +-// TestURI tests the conversion between URIs and filenames. The test cases +-// include Windows-style URIs and filepaths, but we avoid having OS-specific +-// tests by using only forward slashes, assuming that the standard library +-// functions filepath.ToSlash and filepath.FromSlash do not need testing. +-func TestURIFromPath(t *testing.T) { +- for _, test := range []struct { +- path, wantFile string +- wantURI span.URI +- }{ +- { +- path: ``, +- wantFile: ``, +- wantURI: span.URI(""), +- }, +- { +- path: `C:/Windows/System32`, +- wantFile: `C:/Windows/System32`, +- wantURI: span.URI("file:///C:/Windows/System32"), +- }, +- { +- path: `C:/Go/src/bob.go`, +- wantFile: `C:/Go/src/bob.go`, +- wantURI: span.URI("file:///C:/Go/src/bob.go"), +- }, +- { +- path: `c:/Go/src/bob.go`, +- wantFile: `C:/Go/src/bob.go`, +- wantURI: span.URI("file:///C:/Go/src/bob.go"), +- }, +- { +- path: `/path/to/dir`, +- wantFile: `/path/to/dir`, +- wantURI: span.URI("file:///path/to/dir"), +- }, +- { +- path: `/a/b/c/src/bob.go`, +- wantFile: `/a/b/c/src/bob.go`, +- wantURI: span.URI("file:///a/b/c/src/bob.go"), +- }, +- { +- path: `c:/Go/src/bob george/george/george.go`, +- wantFile: `C:/Go/src/bob george/george/george.go`, +- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), +- }, +- } { +- got := span.URIFromPath(test.path) +- if got != test.wantURI { +- t.Errorf("URIFromPath(%q): got %q, expected %q", test.path, got, test.wantURI) +- } +- gotFilename := got.Filename() +- if gotFilename != test.wantFile { +- t.Errorf("Filename(%q): got %q, expected %q", got, gotFilename, test.wantFile) +- } +- } -} - --// ParseInDir is like Parse, but interprets paths relative to wd. --func ParseInDir(input, wd string) Span { -- uri := func(path string) URI { -- if !filepath.IsAbs(path) { -- path = filepath.Join(wd, path) +-func TestURIFromURI(t *testing.T) { +- for _, test := range []struct { +- inputURI, wantFile string +- wantURI span.URI +- }{ +- { +- inputURI: `file:///c:/Go/src/bob%20george/george/george.go`, +- wantFile: `C:/Go/src/bob george/george/george.go`, +- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), +- }, +- { +- inputURI: `file:///C%3A/Go/src/bob%20george/george/george.go`, +- wantFile: `C:/Go/src/bob george/george/george.go`, +- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), +- }, +- { +- inputURI: `file:///path/to/%25p%25ercent%25/per%25cent.go`, +- wantFile: `/path/to/%p%ercent%/per%cent.go`, +- wantURI: span.URI(`file:///path/to/%25p%25ercent%25/per%25cent.go`), +- }, +- { +- inputURI: `file:///C%3A/`, +- wantFile: `C:/`, +- wantURI: span.URI(`file:///C:/`), +- }, +- { +- inputURI: `file:///`, +- wantFile: `/`, +- wantURI: span.URI(`file:///`), +- }, +- { +- inputURI: `file://wsl%24/Ubuntu/home/wdcui/repo/VMEnclaves/cvm-runtime`, +- wantFile: `/wsl$/Ubuntu/home/wdcui/repo/VMEnclaves/cvm-runtime`, +- wantURI: span.URI(`file:///wsl$/Ubuntu/home/wdcui/repo/VMEnclaves/cvm-runtime`), +- }, +- } { +- got := span.URIFromURI(test.inputURI) +- if got != test.wantURI { +- t.Errorf("NewURI(%q): got %q, expected %q", test.inputURI, got, test.wantURI) +- } +- gotFilename := got.Filename() +- if gotFilename != test.wantFile { +- t.Errorf("Filename(%q): got %q, expected %q", got, gotFilename, test.wantFile) - } -- return URIFromPath(path) -- } -- // :0:0#0-0:0#0 -- valid := input -- var hold, offset int -- hadCol := false -- suf := rstripSuffix(input) -- if suf.sep == "#" { -- offset = suf.num -- suf = rstripSuffix(suf.remains) -- } -- if suf.sep == ":" { -- valid = suf.remains -- hold = suf.num -- hadCol = true -- suf = rstripSuffix(suf.remains) -- } -- switch { -- case suf.sep == ":": -- return New(uri(suf.remains), NewPoint(suf.num, hold, offset), Point{}) -- case suf.sep == "-": -- // we have a span, fall out of the case to continue -- default: -- // separator not valid, rewind to either the : or the start -- return New(uri(valid), NewPoint(hold, 0, offset), Point{}) -- } -- // only the span form can get here -- // at this point we still don't know what the numbers we have mean -- // if have not yet seen a : then we might have either a line or a column depending -- // on whether start has a column or not -- // we build an end point and will fix it later if needed -- end := NewPoint(suf.num, hold, offset) -- hold, offset = 0, 0 -- suf = rstripSuffix(suf.remains) -- if suf.sep == "#" { -- offset = suf.num -- suf = rstripSuffix(suf.remains) -- } -- if suf.sep != ":" { -- // turns out we don't have a span after all, rewind -- return New(uri(valid), end, Point{}) - } -- valid = suf.remains -- hold = suf.num -- suf = rstripSuffix(suf.remains) -- if suf.sep != ":" { -- // line#offset only -- return New(uri(valid), NewPoint(hold, 0, offset), end) +-} +diff -urN a/gopls/internal/span/uri_windows_test.go b/gopls/internal/span/uri_windows_test.go +--- a/gopls/internal/span/uri_windows_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/span/uri_windows_test.go 1970-01-01 08:00:00 +@@ -1,112 +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 windows +-// +build windows +- +-package span_test +- +-import ( +- "testing" +- +- "golang.org/x/tools/gopls/internal/span" +-) +- +-// TestURI tests the conversion between URIs and filenames. The test cases +-// include Windows-style URIs and filepaths, but we avoid having OS-specific +-// tests by using only forward slashes, assuming that the standard library +-// functions filepath.ToSlash and filepath.FromSlash do not need testing. +-func TestURIFromPath(t *testing.T) { +- for _, test := range []struct { +- path, wantFile string +- wantURI span.URI +- }{ +- { +- path: ``, +- wantFile: ``, +- wantURI: span.URI(""), +- }, +- { +- path: `C:\Windows\System32`, +- wantFile: `C:\Windows\System32`, +- wantURI: span.URI("file:///C:/Windows/System32"), +- }, +- { +- path: `C:\Go\src\bob.go`, +- wantFile: `C:\Go\src\bob.go`, +- wantURI: span.URI("file:///C:/Go/src/bob.go"), +- }, +- { +- path: `c:\Go\src\bob.go`, +- wantFile: `C:\Go\src\bob.go`, +- wantURI: span.URI("file:///C:/Go/src/bob.go"), +- }, +- { +- path: `\path\to\dir`, +- wantFile: `C:\path\to\dir`, +- wantURI: span.URI("file:///C:/path/to/dir"), +- }, +- { +- path: `\a\b\c\src\bob.go`, +- wantFile: `C:\a\b\c\src\bob.go`, +- wantURI: span.URI("file:///C:/a/b/c/src/bob.go"), +- }, +- { +- path: `c:\Go\src\bob george\george\george.go`, +- wantFile: `C:\Go\src\bob george\george\george.go`, +- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), +- }, +- } { +- got := span.URIFromPath(test.path) +- if got != test.wantURI { +- t.Errorf("URIFromPath(%q): got %q, expected %q", test.path, got, test.wantURI) +- } +- gotFilename := got.Filename() +- if gotFilename != test.wantFile { +- t.Errorf("Filename(%q): got %q, expected %q", got, gotFilename, test.wantFile) +- } - } -- // we have a column, so if end only had one number, it is also the column -- if !hadCol { -- end = NewPoint(suf.num, end.v.Line, end.v.Offset) +-} +- +-func TestURIFromURI(t *testing.T) { +- for _, test := range []struct { +- inputURI, wantFile string +- wantURI span.URI +- }{ +- { +- inputURI: `file:///c:/Go/src/bob%20george/george/george.go`, +- wantFile: `C:\Go\src\bob george\george\george.go`, +- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), +- }, +- { +- inputURI: `file:///C%3A/Go/src/bob%20george/george/george.go`, +- wantFile: `C:\Go\src\bob george\george\george.go`, +- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), +- }, +- { +- inputURI: `file:///c:/path/to/%25p%25ercent%25/per%25cent.go`, +- wantFile: `C:\path\to\%p%ercent%\per%cent.go`, +- wantURI: span.URI(`file:///C:/path/to/%25p%25ercent%25/per%25cent.go`), +- }, +- { +- inputURI: `file:///C%3A/`, +- wantFile: `C:\`, +- wantURI: span.URI(`file:///C:/`), +- }, +- { +- inputURI: `file:///`, +- wantFile: `\`, +- wantURI: span.URI(`file:///`), +- }, +- } { +- got := span.URIFromURI(test.inputURI) +- if got != test.wantURI { +- t.Errorf("NewURI(%q): got %q, expected %q", test.inputURI, got, test.wantURI) +- } +- gotFilename := got.Filename() +- if gotFilename != test.wantFile { +- t.Errorf("Filename(%q): got %q, expected %q", got, gotFilename, test.wantFile) +- } - } -- return New(uri(suf.remains), NewPoint(suf.num, hold, offset), end) -} +diff -urN a/gopls/internal/telemetry/latency.go b/gopls/internal/telemetry/latency.go +--- a/gopls/internal/telemetry/latency.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/telemetry/latency.go 1970-01-01 08:00:00 +@@ -1,102 +0,0 @@ +-// Copyright 2023 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. - --type suffix struct { -- remains string -- sep string -- num int +-package telemetry +- +-import ( +- "context" +- "errors" +- "fmt" +- "sort" +- "sync" +- "time" +- +- "golang.org/x/telemetry/counter" +-) +- +-// latencyKey is used for looking up latency counters. +-type latencyKey struct { +- operation, bucket string +- isError bool -} - --func rstripSuffix(input string) suffix { -- if len(input) == 0 { -- return suffix{"", "", -1} +-var ( +- latencyBuckets = []struct { +- end time.Duration +- name string +- }{ +- {10 * time.Millisecond, "<10ms"}, +- {50 * time.Millisecond, "<50ms"}, +- {100 * time.Millisecond, "<100ms"}, +- {200 * time.Millisecond, "<200ms"}, +- {500 * time.Millisecond, "<500ms"}, +- {1 * time.Second, "<1s"}, +- {5 * time.Second, "<5s"}, +- {24 * time.Hour, "<24h"}, - } -- remains := input - -- // Remove optional trailing decimal number. -- num := -1 -- last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) -- if last >= 0 && last < len(remains)-1 { -- number, err := strconv.ParseInt(remains[last+1:], 10, 64) -- if err == nil { -- num = int(number) -- remains = remains[:last+1] +- latencyCounterMu sync.Mutex +- latencyCounters = make(map[latencyKey]*counter.Counter) // lazily populated +-) +- +-// ForEachLatencyCounter runs the provided function for each current latency +-// counter measuring the given operation. +-// +-// Exported for testing. +-func ForEachLatencyCounter(operation string, isError bool, f func(*counter.Counter)) { +- latencyCounterMu.Lock() +- defer latencyCounterMu.Unlock() +- +- for k, v := range latencyCounters { +- if k.operation == operation && k.isError == isError { +- f(v) +- } +- } +-} +- +-// getLatencyCounter returns the counter used to record latency of the given +-// operation in the given bucket. +-func getLatencyCounter(operation, bucket string, isError bool) *counter.Counter { +- latencyCounterMu.Lock() +- defer latencyCounterMu.Unlock() +- +- key := latencyKey{operation, bucket, isError} +- c, ok := latencyCounters[key] +- if !ok { +- var name string +- if isError { +- name = fmt.Sprintf("gopls/%s/error-latency:%s", operation, bucket) +- } else { +- name = fmt.Sprintf("gopls/%s/latency:%s", operation, bucket) - } +- c = counter.New(name) +- latencyCounters[key] = c - } -- // now see if we have a trailing separator -- r, w := utf8.DecodeLastRuneInString(remains) -- // TODO(adonovan): this condition is clearly wrong. Should the third byte be '-'? -- if r != ':' && r != '#' && r == '#' { -- return suffix{input, "", -1} +- return c +-} +- +-// StartLatencyTimer starts a timer for the gopls operation with the given +-// name, and returns a func to stop the timer and record the latency sample. +-// +-// If the context provided to the the resulting func is done, no observation is +-// recorded. +-func StartLatencyTimer(operation string) func(context.Context, error) { +- start := time.Now() +- return func(ctx context.Context, err error) { +- if errors.Is(ctx.Err(), context.Canceled) { +- // Ignore timing where the operation is cancelled, it may be influenced +- // by client behavior. +- return +- } +- latency := time.Since(start) +- bucketIdx := sort.Search(len(latencyBuckets), func(i int) bool { +- bucket := latencyBuckets[i] +- return latency < bucket.end +- }) +- if bucketIdx < len(latencyBuckets) { // ignore latency longer than a day :) +- bucketName := latencyBuckets[bucketIdx].name +- getLatencyCounter(operation, bucketName, err != nil).Inc() +- } - } -- remains = remains[:len(remains)-w] -- return suffix{remains, string(r), num} -} -diff -urN a/gopls/internal/span/span.go b/gopls/internal/span/span.go ---- a/gopls/internal/span/span.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/span/span.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,253 +0,0 @@ --// Copyright 2019 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/telemetry/telemetry.go b/gopls/internal/telemetry/telemetry.go +--- a/gopls/internal/telemetry/telemetry.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/telemetry/telemetry.go 1970-01-01 08:00:00 +@@ -1,93 +0,0 @@ +-// Copyright 2023 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 span contains support for representing with positions and ranges in --// text files. --package span +-//go:build go1.19 +-// +build go1.19 +- +-package telemetry - -import ( -- "encoding/json" - "fmt" -- "go/token" -- "path" -- "sort" -- "strings" - -- "golang.org/x/tools/gopls/internal/lsp/safetoken" +- "golang.org/x/telemetry" +- "golang.org/x/telemetry/counter" +- "golang.org/x/telemetry/upload" +- "golang.org/x/tools/gopls/internal/lsp/protocol" -) - --// A Span represents a range of text within a source file. The start --// and end points of a valid span may be hold either its byte offset, --// or its (line, column) pair, or both. Columns are measured in bytes. --// --// Spans are appropriate in user interfaces (e.g. command-line tools) --// and tests where a position is notated without access to the content --// of the file. --// --// Use protocol.Mapper to convert between Span and other --// representations, such as go/token (also UTF-8) or the LSP protocol --// (UTF-16). The latter requires access to file contents. --// --// See overview comments at ../lsp/protocol/mapper.go. --type Span struct { -- v span +-// Mode calls x/telemetry.Mode. +-func Mode() string { +- return telemetry.Mode() +-} +- +-// SetMode calls x/telemetry.SetMode. +-func SetMode(mode string) error { +- return telemetry.SetMode(mode) +-} +- +-// Start starts telemetry instrumentation. +-func Start() { +- counter.Open() +- // upload only once at startup, hoping that users restart gopls often. +- go upload.Run(nil) +-} +- +-// RecordClientInfo records gopls client info. +-func RecordClientInfo(params *protocol.ParamInitialize) { +- client := "gopls/client:other" +- if params != nil && params.ClientInfo != nil { +- switch params.ClientInfo.Name { +- case "Visual Studio Code": +- client = "gopls/client:vscode" +- case "Visual Studio Code - Insiders": +- client = "gopls/client:vscode-insiders" +- case "VSCodium": +- client = "gopls/client:vscodium" +- case "code-server": +- // https://github.com/coder/code-server/blob/3cb92edc76ecc2cfa5809205897d93d4379b16a6/ci/build/build-vscode.sh#L19 +- client = "gopls/client:code-server" +- case "Eglot": +- // https://lists.gnu.org/archive/html/bug-gnu-emacs/2023-03/msg00954.html +- client = "gopls/client:eglot" +- case "govim": +- // https://github.com/govim/govim/pull/1189 +- client = "gopls/client:govim" +- case "Neovim": +- // https://github.com/neovim/neovim/blob/42333ea98dfcd2994ee128a3467dfe68205154cd/runtime/lua/vim/lsp.lua#L1361 +- client = "gopls/client:neovim" +- case "coc.nvim": +- // https://github.com/neoclide/coc.nvim/blob/3dc6153a85ed0f185abec1deb972a66af3fbbfb4/src/language-client/client.ts#L994 +- client = "gopls/client:coc.nvim" +- case "Sublime Text LSP": +- // https://github.com/sublimelsp/LSP/blob/e608f878e7e9dd34aabe4ff0462540fadcd88fcc/plugin/core/sessions.py#L493 +- client = "gopls/client:sublimetext" +- default: +- // at least accumulate the client name locally +- counter.New(fmt.Sprintf("gopls/client-other:%s", params.ClientInfo.Name)).Inc() +- // but also record client:other +- } +- } +- counter.Inc(client) -} - --// Point represents a single point within a file. --// In general this should only be used as part of a Span, as on its own it --// does not carry enough information. --type Point struct { -- v point +-// RecordViewGoVersion records the Go minor version number (1.x) used for a view. +-func RecordViewGoVersion(x int) { +- if x < 0 { +- return +- } +- name := fmt.Sprintf("gopls/goversion:1.%d", x) +- counter.Inc(name) -} - --// The private span/point types have public fields to support JSON --// encoding, but the public Span/Point types hide these fields by --// defining methods that shadow them. (This is used by a few of the --// command-line tool subcommands, which emit spans and have a -json --// flag.) -- --type span struct { -- URI URI `json:"uri"` -- Start point `json:"start"` -- End point `json:"end"` +-// AddForwardedCounters adds the given counters on behalf of clients. +-// Names and values must have the same length. +-func AddForwardedCounters(names []string, values []int64) { +- for i, n := range names { +- v := values[i] +- if n == "" || v < 0 { +- continue // Should we report an error? Who is the audience? +- } +- counter.Add("fwd/"+n, v) +- } -} +diff -urN a/gopls/internal/telemetry/telemetry_go118.go b/gopls/internal/telemetry/telemetry_go118.go +--- a/gopls/internal/telemetry/telemetry_go118.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/telemetry/telemetry_go118.go 1970-01-01 08:00:00 +@@ -1,30 +0,0 @@ +-// Copyright 2023 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. - --type point struct { -- Line int `json:"line"` // 1-based line number -- Column int `json:"column"` // 1-based, UTF-8 codes (bytes) -- Offset int `json:"offset"` // 0-based byte offset --} +-//go:build !go1.19 +-// +build !go1.19 - --// Invalid is a span that reports false from IsValid --var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}} +-package telemetry - --var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}} +-import "golang.org/x/tools/gopls/internal/lsp/protocol" - --func New(uri URI, start, end Point) Span { -- s := Span{v: span{URI: uri, Start: start.v, End: end.v}} -- s.v.clean() -- return s +-func Mode() string { +- return "local" -} - --func NewPoint(line, col, offset int) Point { -- p := Point{v: point{Line: line, Column: col, Offset: offset}} -- p.v.clean() -- return p +-func SetMode(mode string) error { +- return nil -} - --// SortSpans sorts spans into a stable but unspecified order. --func SortSpans(spans []Span) { -- sort.SliceStable(spans, func(i, j int) bool { -- return compare(spans[i], spans[j]) < 0 -- }) +-func Start() { -} - --// compare implements a three-valued ordered comparison of Spans. --func compare(a, b Span) int { -- // This is a textual comparison. It does not perform path -- // cleaning, case folding, resolution of symbolic links, -- // testing for existence, or any I/O. -- if cmp := strings.Compare(string(a.URI()), string(b.URI())); cmp != 0 { -- return cmp -- } -- if cmp := comparePoint(a.v.Start, b.v.Start); cmp != 0 { -- return cmp -- } -- return comparePoint(a.v.End, b.v.End) +-func RecordClientInfo(params *protocol.ParamInitialize) { -} - --func ComparePoint(a, b Point) int { -- return comparePoint(a.v, b.v) +-func RecordViewGoVersion(x int) { -} - --func comparePoint(a, b point) int { -- if !a.hasPosition() { -- if a.Offset < b.Offset { -- return -1 -- } -- if a.Offset > b.Offset { -- return 1 -- } -- return 0 -- } -- if a.Line < b.Line { -- return -1 -- } -- if a.Line > b.Line { -- return 1 -- } -- if a.Column < b.Column { -- return -1 -- } -- if a.Column > b.Column { -- return 1 -- } -- return 0 +-func AddForwardedCounters(names []string, values []int64) { -} +diff -urN a/gopls/internal/telemetry/telemetry_test.go b/gopls/internal/telemetry/telemetry_test.go +--- a/gopls/internal/telemetry/telemetry_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/telemetry/telemetry_test.go 1970-01-01 08:00:00 +@@ -1,215 +0,0 @@ +-// Copyright 2023 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. - --func (s Span) HasPosition() bool { return s.v.Start.hasPosition() } --func (s Span) HasOffset() bool { return s.v.Start.hasOffset() } --func (s Span) IsValid() bool { return s.v.Start.isValid() } --func (s Span) IsPoint() bool { return s.v.Start == s.v.End } --func (s Span) URI() URI { return s.v.URI } --func (s Span) Start() Point { return Point{s.v.Start} } --func (s Span) End() Point { return Point{s.v.End} } --func (s *Span) MarshalJSON() ([]byte, error) { return json.Marshal(&s.v) } --func (s *Span) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &s.v) } +-//go:build go1.21 && !openbsd && !js && !wasip1 && !solaris && !android && !386 +-// +build go1.21,!openbsd,!js,!wasip1,!solaris,!android,!386 - --func (p Point) HasPosition() bool { return p.v.hasPosition() } --func (p Point) HasOffset() bool { return p.v.hasOffset() } --func (p Point) IsValid() bool { return p.v.isValid() } --func (p *Point) MarshalJSON() ([]byte, error) { return json.Marshal(&p.v) } --func (p *Point) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &p.v) } --func (p Point) Line() int { -- if !p.v.hasPosition() { -- panic(fmt.Errorf("position not set in %v", p.v)) -- } -- return p.v.Line --} --func (p Point) Column() int { -- if !p.v.hasPosition() { -- panic(fmt.Errorf("position not set in %v", p.v)) -- } -- return p.v.Column --} --func (p Point) Offset() int { -- if !p.v.hasOffset() { -- panic(fmt.Errorf("offset not set in %v", p.v)) -- } -- return p.v.Offset --} +-package telemetry_test - --func (p point) hasPosition() bool { return p.Line > 0 } --func (p point) hasOffset() bool { return p.Offset >= 0 } --func (p point) isValid() bool { return p.hasPosition() || p.hasOffset() } --func (p point) isZero() bool { -- return (p.Line == 1 && p.Column == 1) || (!p.hasPosition() && p.Offset == 0) --} +-import ( +- "context" +- "errors" +- "os" +- "strconv" +- "strings" +- "testing" +- "time" - --func (s *span) clean() { -- //this presumes the points are already clean -- if !s.End.isValid() || (s.End == point{}) { -- s.End = s.Start +- "golang.org/x/telemetry/counter" +- "golang.org/x/telemetry/counter/countertest" // requires go1.21+ +- "golang.org/x/tools/gopls/internal/bug" +- "golang.org/x/tools/gopls/internal/hooks" +- "golang.org/x/tools/gopls/internal/lsp/command" +- "golang.org/x/tools/gopls/internal/lsp/protocol" +- . "golang.org/x/tools/gopls/internal/lsp/regtest" +- "golang.org/x/tools/gopls/internal/telemetry" +-) +- +-func TestMain(m *testing.M) { +- tmp, err := os.MkdirTemp("", "gopls-telemetry-test") +- if err != nil { +- panic(err) - } +- countertest.Open(tmp) +- defer os.RemoveAll(tmp) +- Main(m, hooks.Options) -} - --func (p *point) clean() { -- if p.Line < 0 { -- p.Line = 0 +-func TestTelemetry(t *testing.T) { +- var ( +- goversion = "" +- editor = "vscode" // We set ClientName("Visual Studio Code") below. +- ) +- +- // Run gopls once to determine the Go version. +- WithOptions( +- Modes(Default), +- ).Run(t, "", func(_ *testing.T, env *Env) { +- goversion = strconv.Itoa(env.GoVersion()) +- }) +- +- // counters that should be incremented once per session +- sessionCounters := []*counter.Counter{ +- counter.New("gopls/client:" + editor), +- counter.New("gopls/goversion:1." + goversion), +- counter.New("fwd/vscode/linter:a"), - } -- if p.Column <= 0 { -- if p.Line > 0 { -- p.Column = 1 -- } else { -- p.Column = 0 +- initialCounts := make([]uint64, len(sessionCounters)) +- for i, c := range sessionCounters { +- count, err := countertest.ReadCounter(c) +- if err != nil { +- t.Fatalf("ReadCounter(%s): %v", c.Name(), err) - } +- initialCounts[i] = count - } -- if p.Offset == 0 && (p.Line > 1 || p.Column > 1) { -- p.Offset = -1 -- } --} - --// Format implements fmt.Formatter to print the Location in a standard form. --// The format produced is one that can be read back in using Parse. --func (s Span) Format(f fmt.State, c rune) { -- fullForm := f.Flag('+') -- preferOffset := f.Flag('#') -- // we should always have a uri, simplify if it is file format -- //TODO: make sure the end of the uri is unambiguous -- uri := string(s.v.URI) -- if c == 'f' { -- uri = path.Base(uri) -- } else if !fullForm { -- uri = s.v.URI.Filename() -- } -- fmt.Fprint(f, uri) -- if !s.IsValid() || (!fullForm && s.v.Start.isZero() && s.v.End.isZero()) { -- return -- } -- // see which bits of start to write -- printOffset := s.HasOffset() && (fullForm || preferOffset || !s.HasPosition()) -- printLine := s.HasPosition() && (fullForm || !printOffset) -- printColumn := printLine && (fullForm || (s.v.Start.Column > 1 || s.v.End.Column > 1)) -- fmt.Fprint(f, ":") -- if printLine { -- fmt.Fprintf(f, "%d", s.v.Start.Line) +- // Verify that a properly configured session gets notified of a bug on the +- // server. +- WithOptions( +- Modes(Default), // must be in-process to receive the bug report below +- Settings{"showBugReports": true}, +- ClientName("Visual Studio Code"), +- ).Run(t, "", func(_ *testing.T, env *Env) { +- goversion = strconv.Itoa(env.GoVersion()) +- addForwardedCounters(env, []string{"vscode/linter:a"}, []int64{1}) +- const desc = "got a bug" +- bug.Report(desc) // want a stack counter with the trace starting from here. +- env.Await(ShownMessage(desc)) +- }) +- +- // gopls/editor:client +- // gopls/goversion:1.x +- // fwd/vscode/linter:a +- for i, c := range sessionCounters { +- want := initialCounts[i] + 1 +- got, err := countertest.ReadCounter(c) +- if err != nil || got != want { +- t.Errorf("ReadCounter(%q) = (%v, %v), want (%v, nil)", c.Name(), got, err, want) +- t.Logf("Current timestamp = %v", time.Now().UTC()) +- } - } -- if printColumn { -- fmt.Fprintf(f, ":%d", s.v.Start.Column) +- +- // gopls/bug +- bugcount := bug.BugReportCount +- counts, err := countertest.ReadStackCounter(bugcount) +- if err != nil { +- t.Fatalf("ReadStackCounter(bugreportcount) failed - %v", err) - } -- if printOffset { -- fmt.Fprintf(f, "#%d", s.v.Start.Offset) +- if len(counts) != 1 || !hasEntry(counts, t.Name(), 1) { +- t.Errorf("read stackcounter(%q) = (%#v, %v), want one entry", "gopls/bug", counts, err) +- t.Logf("Current timestamp = %v", time.Now().UTC()) - } -- // start is written, do we need end? -- if s.IsPoint() { -- return +-} +- +-func addForwardedCounters(env *Env, names []string, values []int64) { +- args, err := command.MarshalArgs(command.AddTelemetryCountersArgs{ +- Names: names, Values: values, +- }) +- if err != nil { +- env.T.Fatal(err) - } -- // we don't print the line if it did not change -- printLine = fullForm || (printLine && s.v.End.Line > s.v.Start.Line) -- fmt.Fprint(f, "-") -- if printLine { -- fmt.Fprintf(f, "%d", s.v.End.Line) +- var res error +- env.ExecuteCommand(&protocol.ExecuteCommandParams{ +- Command: command.AddTelemetryCounters.ID(), +- Arguments: args, +- }, res) +- if res != nil { +- env.T.Errorf("%v failed - %v", command.AddTelemetryCounters.ID(), res) - } -- if printColumn { -- if printLine { -- fmt.Fprint(f, ":") +-} +- +-func hasEntry(counts map[string]uint64, pattern string, want uint64) bool { +- for k, v := range counts { +- if strings.Contains(k, pattern) && v == want { +- return true - } -- fmt.Fprintf(f, "%d", s.v.End.Column) -- } -- if printOffset { -- fmt.Fprintf(f, "#%d", s.v.End.Offset) - } +- return false -} - --// SetRange implements packagestest.rangeSetter, allowing --// gopls' test suites to use Spans instead of Range in parameters. --func (span *Span) SetRange(file *token.File, start, end token.Pos) { -- point := func(pos token.Pos) Point { -- posn := safetoken.Position(file, pos) -- return NewPoint(posn.Line, posn.Column, posn.Offset) +-func TestLatencyCounter(t *testing.T) { +- const operation = "TestLatencyCounter" // a unique operation name +- +- stop := telemetry.StartLatencyTimer(operation) +- stop(context.Background(), nil) +- +- for isError, want := range map[bool]uint64{false: 1, true: 0} { +- if got := totalLatencySamples(t, operation, isError); got != want { +- t.Errorf("totalLatencySamples(operation=%v, isError=%v) = %d, want %d", operation, isError, got, want) +- } - } -- *span = New(URIFromPath(file.Name()), point(start), point(end)) -} -diff -urN a/gopls/internal/span/span_test.go b/gopls/internal/span/span_test.go ---- a/gopls/internal/span/span_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/span/span_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,57 +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 span_test +-func TestLatencyCounter_Error(t *testing.T) { +- const operation = "TestLatencyCounter_Error" // a unique operation name - --import ( -- "fmt" -- "path/filepath" -- "strings" -- "testing" +- stop := telemetry.StartLatencyTimer(operation) +- stop(context.Background(), errors.New("bad")) - -- "golang.org/x/tools/gopls/internal/span" --) +- for isError, want := range map[bool]uint64{false: 0, true: 1} { +- if got := totalLatencySamples(t, operation, isError); got != want { +- t.Errorf("totalLatencySamples(operation=%v, isError=%v) = %d, want %d", operation, isError, got, want) +- } +- } +-} - --func TestFormat(t *testing.T) { -- formats := []string{"%v", "%#v", "%+v"} +-func TestLatencyCounter_Cancellation(t *testing.T) { +- const operation = "TestLatencyCounter_Cancellation" - -- // Element 0 is the input, and the elements 0-2 are the expected -- // output in [%v %#v %+v] formats. Thus the first must be in -- // canonical form (invariant under span.Parse + fmt.Sprint). -- // The '#' form displays offsets; the '+' form outputs a URI. -- // If len=4, element 0 is a noncanonical input and 1-3 are expected outputs. -- for _, test := range [][]string{ -- {"C:/file_a", "C:/file_a", "file:///C:/file_a:#0"}, -- {"C:/file_b:1:2", "C:/file_b:1:2", "file:///C:/file_b:1:2"}, -- {"C:/file_c:1000", "C:/file_c:1000", "file:///C:/file_c:1000:1"}, -- {"C:/file_d:14:9", "C:/file_d:14:9", "file:///C:/file_d:14:9"}, -- {"C:/file_e:1:2-7", "C:/file_e:1:2-7", "file:///C:/file_e:1:2-1:7"}, -- {"C:/file_f:500-502", "C:/file_f:500-502", "file:///C:/file_f:500:1-502:1"}, -- {"C:/file_g:3:7-8", "C:/file_g:3:7-8", "file:///C:/file_g:3:7-3:8"}, -- {"C:/file_h:3:7-4:8", "C:/file_h:3:7-4:8", "file:///C:/file_h:3:7-4:8"}, -- {"C:/file_i:#100", "C:/file_i:#100", "file:///C:/file_i:#100"}, -- {"C:/file_j:#26-#28", "C:/file_j:#26-#28", "file:///C:/file_j:#26-0#28"}, // 0#28? -- {"C:/file_h:3:7#26-4:8#37", // not canonical -- "C:/file_h:3:7-4:8", "C:/file_h:#26-#37", "file:///C:/file_h:3:7#26-4:8#37"}} { -- input := test[0] -- spn := span.Parse(input) -- wants := test[0:3] -- if len(test) == 4 { -- wants = test[1:4] -- } -- for i, format := range formats { -- want := toPath(wants[i]) -- if got := fmt.Sprintf(format, spn); got != want { -- t.Errorf("Sprintf(%q, %q) = %q, want %q", format, input, got, want) -- } +- stop := telemetry.StartLatencyTimer(operation) +- ctx, cancel := context.WithCancel(context.Background()) +- cancel() +- stop(ctx, nil) +- +- for isError, want := range map[bool]uint64{false: 0, true: 0} { +- if got := totalLatencySamples(t, operation, isError); got != want { +- t.Errorf("totalLatencySamples(operation=%v, isError=%v) = %d, want %d", operation, isError, got, want) - } - } -} - --func toPath(value string) string { -- if strings.HasPrefix(value, "file://") { -- return value -- } -- return filepath.FromSlash(value) +-func totalLatencySamples(t *testing.T, operation string, isError bool) uint64 { +- var total uint64 +- telemetry.ForEachLatencyCounter(operation, isError, func(c *counter.Counter) { +- count, err := countertest.ReadCounter(c) +- if err != nil { +- t.Errorf("ReadCounter(%s) failed: %v", c.Name(), err) +- } else { +- total += count +- } +- }) +- return total -} -diff -urN a/gopls/internal/span/uri.go b/gopls/internal/span/uri.go ---- a/gopls/internal/span/uri.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/span/uri.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,185 +0,0 @@ --// Copyright 2019 The Go Authors. All rights reserved. +- +-func TestLatencyInstrumentation(t *testing.T) { +- const files = ` +--- go.mod -- +-module mod.test/a +-go 1.18 +--- a.go -- +-package a +- +-func _() { +- x := 0 +- _ = x +-} +-` +- +- // Verify that a properly configured session gets notified of a bug on the +- // server. +- WithOptions( +- Modes(Default), // must be in-process to receive the bug report below +- ).Run(t, files, func(_ *testing.T, env *Env) { +- env.OpenFile("a.go") +- before := totalLatencySamples(t, "completion", false) +- loc := env.RegexpSearch("a.go", "x") +- for i := 0; i < 10; i++ { +- env.Completion(loc) +- } +- after := totalLatencySamples(t, "completion", false) +- if after-before < 10 { +- t.Errorf("after 10 completions, completion counter went from %d to %d", before, after) +- } +- }) +-} +diff -urN a/gopls/internal/vulncheck/copier.go b/gopls/internal/vulncheck/copier.go +--- a/gopls/internal/vulncheck/copier.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/copier.go 1970-01-01 08:00:00 +@@ -1,142 +0,0 @@ +-// Copyright 2023 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 span +-//go:build ignore +-// +build ignore +- +-//go:generate go run ./copier.go +- +-// Copier is a tool to automate copy of govulncheck's internal files. +-// +-// - copy golang.org/x/vuln/internal/osv/ to osv +-// - copy golang.org/x/vuln/internal/govulncheck/ to govulncheck +-package main - -import ( +- "bytes" +- "encoding/json" - "fmt" -- "net/url" +- "go/parser" +- "go/token" +- "log" - "os" +- "os/exec" - "path/filepath" -- "runtime" +- "strconv" - "strings" -- "unicode" --) - --const fileScheme = "file" +- "golang.org/x/tools/internal/edit" +-) - --// URI represents the full URI for a file. --type URI string +-func main() { +- log.SetPrefix("copier: ") +- log.SetFlags(log.Lshortfile) - --func (uri URI) IsFile() bool { -- return strings.HasPrefix(string(uri), "file://") --} +- srcMod := "golang.org/x/vuln" +- srcModVers := "@latest" +- srcDir, srcVer := downloadModule(srcMod + srcModVers) - --// Filename returns the file path for the given URI. --// It is an error to call this on a URI that is not a valid filename. --func (uri URI) Filename() string { -- filename, err := filename(uri) -- if err != nil { -- panic(err) +- cfg := rewrite{ +- banner: fmt.Sprintf("// Code generated by copying from %v@%v (go run copier.go); DO NOT EDIT.", srcMod, srcVer), +- srcImportPath: "golang.org/x/vuln/internal", +- dstImportPath: currentPackagePath(), - } -- return filepath.FromSlash(filename) --} - --func filename(uri URI) (string, error) { -- if uri == "" { -- return "", nil -- } +- copyFiles("osv", filepath.Join(srcDir, "internal", "osv"), cfg) +- copyFiles("govulncheck", filepath.Join(srcDir, "internal", "govulncheck"), cfg) +-} - -- // This conservative check for the common case -- // of a simple non-empty absolute POSIX filename -- // avoids the allocation of a net.URL. -- if strings.HasPrefix(string(uri), "file:///") { -- rest := string(uri)[len("file://"):] // leave one slash -- for i := 0; i < len(rest); i++ { -- b := rest[i] -- // Reject these cases: -- if b < ' ' || b == 0x7f || // control character -- b == '%' || b == '+' || // URI escape -- b == ':' || // Windows drive letter -- b == '@' || b == '&' || b == '?' { // authority or query -- goto slow -- } -- } -- return rest, nil -- } --slow: +-type rewrite struct { +- // DO NOT EDIT marker to add at the beginning +- banner string +- // rewrite srcImportPath with dstImportPath +- srcImportPath string +- dstImportPath string +-} - -- u, err := url.ParseRequestURI(string(uri)) +-func copyFiles(dst, src string, cfg rewrite) { +- entries, err := os.ReadDir(src) - if err != nil { -- return "", err -- } -- if u.Scheme != fileScheme { -- return "", fmt.Errorf("only file URIs are supported, got %q from %q", u.Scheme, uri) +- log.Fatalf("failed to read dir: %v", err) - } -- // If the URI is a Windows URI, we trim the leading "/" and uppercase -- // the drive letter, which will never be case sensitive. -- if isWindowsDriveURIPath(u.Path) { -- u.Path = strings.ToUpper(string(u.Path[1])) + u.Path[2:] +- if err := os.MkdirAll(dst, 0777); err != nil { +- log.Fatalf("failed to create dir: %v", err) - } - -- return u.Path, nil --} -- --// TODO(adonovan): document this function, and any invariants of --// span.URI that it is supposed to establish. --func URIFromURI(s string) URI { -- if !strings.HasPrefix(s, "file://") { -- return URI(s) -- } +- for _, e := range entries { +- fname := e.Name() +- // we need only non-test go files. +- if e.IsDir() || !strings.HasSuffix(fname, ".go") || strings.HasSuffix(fname, "_test.go") { +- continue +- } +- data, err := os.ReadFile(filepath.Join(src, fname)) +- if err != nil { +- log.Fatal(err) +- } +- fset := token.NewFileSet() +- f, err := parser.ParseFile(fset, fname, data, parser.ParseComments|parser.ImportsOnly) +- if err != nil { +- log.Fatalf("parsing source module:\n%s", err) +- } - -- if !strings.HasPrefix(s, "file:///") { -- // VS Code sends URLs with only two slashes, which are invalid. golang/go#39789. -- s = "file:///" + s[len("file://"):] -- } -- // Even though the input is a URI, it may not be in canonical form. VS Code -- // in particular over-escapes :, @, etc. Unescape and re-encode to canonicalize. -- path, err := url.PathUnescape(s[len("file://"):]) -- if err != nil { -- panic(err) -- } +- buf := edit.NewBuffer(data) +- at := func(p token.Pos) int { +- return fset.File(p).Offset(p) +- } - -- // File URIs from Windows may have lowercase drive letters. -- // Since drive letters are guaranteed to be case insensitive, -- // we change them to uppercase to remain consistent. -- // For example, file:///c:/x/y/z becomes file:///C:/x/y/z. -- if isWindowsDriveURIPath(path) { -- path = path[:1] + strings.ToUpper(string(path[1])) + path[2:] -- } -- u := url.URL{Scheme: fileScheme, Path: path} -- return URI(u.String()) --} +- // Add banner right after the copyright statement (the first comment) +- bannerInsert, banner := f.FileStart, cfg.banner +- if len(f.Comments) > 0 && strings.HasPrefix(f.Comments[0].Text(), "Copyright ") { +- bannerInsert = f.Comments[0].End() +- banner = "\n\n" + banner +- } +- buf.Replace(at(bannerInsert), at(bannerInsert), banner) - --// SameExistingFile reports whether two spans denote the --// same existing file by querying the file system. --func SameExistingFile(a, b URI) bool { -- fa, err := filename(a) -- if err != nil { -- return false -- } -- fb, err := filename(b) -- if err != nil { -- return false -- } -- infoa, err := os.Stat(filepath.FromSlash(fa)) -- if err != nil { -- return false -- } -- infob, err := os.Stat(filepath.FromSlash(fb)) -- if err != nil { -- return false +- // Adjust imports +- for _, spec := range f.Imports { +- path, err := strconv.Unquote(spec.Path.Value) +- if err != nil { +- log.Fatal(err) +- } +- if strings.HasPrefix(path, cfg.srcImportPath) { +- newPath := strings.Replace(path, cfg.srcImportPath, cfg.dstImportPath, 1) +- buf.Replace(at(spec.Path.Pos()), at(spec.Path.End()), strconv.Quote(newPath)) +- } +- } +- data = buf.Bytes() +- +- if err := os.WriteFile(filepath.Join(dst, fname), data, 0666); err != nil { +- log.Fatal(err) +- } - } -- return os.SameFile(infoa, infob) -} - --// URIFromPath returns a span URI for the supplied file path. --// --// For empty paths, URIFromPath returns the empty URI "". --// For non-empty paths, URIFromPath returns a uri with the file:// scheme. --func URIFromPath(path string) URI { -- if path == "" { -- return "" -- } -- // Handle standard library paths that contain the literal "$GOROOT". -- // TODO(rstambler): The go/packages API should allow one to determine a user's $GOROOT. -- const prefix = "$GOROOT" -- if len(path) >= len(prefix) && strings.EqualFold(prefix, path[:len(prefix)]) { -- suffix := path[len(prefix):] -- path = runtime.GOROOT() + suffix -- } -- if !isWindowsDrivePath(path) { -- if abs, err := filepath.Abs(path); err == nil { -- path = abs -- } +-func downloadModule(srcModVers string) (dir, ver string) { +- var stdout, stderr bytes.Buffer +- cmd := exec.Command("go", "mod", "download", "-json", srcModVers) +- cmd.Stdout = &stdout +- cmd.Stderr = &stderr +- if err := cmd.Run(); err != nil { +- log.Fatalf("go mod download -json %s: %v\n%s%s", srcModVers, err, stderr.Bytes(), stdout.Bytes()) - } -- // Check the file path again, in case it became absolute. -- if isWindowsDrivePath(path) { -- path = "/" + strings.ToUpper(string(path[0])) + path[1:] +- var info struct { +- Dir string +- Version string - } -- path = filepath.ToSlash(path) -- u := url.URL{ -- Scheme: fileScheme, -- Path: path, +- if err := json.Unmarshal(stdout.Bytes(), &info); err != nil { +- log.Fatalf("go mod download -json %s: invalid JSON output: %v\n%s%s", srcModVers, err, stderr.Bytes(), stdout.Bytes()) - } -- return URI(u.String()) +- return info.Dir, info.Version -} - --// isWindowsDrivePath returns true if the file path is of the form used by --// Windows. We check if the path begins with a drive letter, followed by a ":". --// For example: C:/x/y/z. --func isWindowsDrivePath(path string) bool { -- if len(path) < 3 { -- return false +-func currentPackagePath() string { +- var stdout, stderr bytes.Buffer +- cmd := exec.Command("go", "list", ".") +- cmd.Stdout = &stdout +- cmd.Stderr = &stderr +- if err := cmd.Run(); err != nil { +- log.Fatalf("go list: %v\n%s%s", err, stderr.Bytes(), stdout.Bytes()) - } -- return unicode.IsLetter(rune(path[0])) && path[1] == ':' +- return strings.TrimSpace(stdout.String()) -} +diff -urN a/gopls/internal/vulncheck/govulncheck/govulncheck.go b/gopls/internal/vulncheck/govulncheck/govulncheck.go +--- a/gopls/internal/vulncheck/govulncheck/govulncheck.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/govulncheck/govulncheck.go 1970-01-01 08:00:00 +@@ -1,160 +0,0 @@ +-// Copyright 2023 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. - --// isWindowsDriveURIPath returns true if the file URI is of the format used by --// Windows URIs. The url.Parse package does not specially handle Windows paths --// (see golang/go#6027), so we check if the URI path has a drive prefix (e.g. "/C:"). --func isWindowsDriveURIPath(uri string) bool { -- if len(uri) < 4 { -- return false -- } -- return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':' +-// Code generated by copying from golang.org/x/vuln@v1.0.1 (go run copier.go); DO NOT EDIT. +- +-// Package govulncheck contains the JSON output structs for govulncheck. +-package govulncheck +- +-import ( +- "time" +- +- "golang.org/x/tools/gopls/internal/vulncheck/osv" +-) +- +-const ( +- // ProtocolVersion is the current protocol version this file implements +- ProtocolVersion = "v1.0.0" +-) +- +-// Message is an entry in the output stream. It will always have exactly one +-// field filled in. +-type Message struct { +- Config *Config `json:"config,omitempty"` +- Progress *Progress `json:"progress,omitempty"` +- OSV *osv.Entry `json:"osv,omitempty"` +- Finding *Finding `json:"finding,omitempty"` -} - --// Dir returns the URI for the directory containing uri. Dir panics if uri is --// not a file uri. --// --// TODO(rfindley): add a unit test for various edge cases. --func Dir(uri URI) URI { -- return URIFromPath(filepath.Dir(uri.Filename())) +-// Config must occur as the first message of a stream and informs the client +-// about the information used to generate the findings. +-// The only required field is the protocol version. +-type Config struct { +- // ProtocolVersion specifies the version of the JSON protocol. +- ProtocolVersion string `json:"protocol_version"` +- +- // ScannerName is the name of the tool, for example, govulncheck. +- // +- // We expect this JSON format to be used by other tools that wrap +- // govulncheck, which will have a different name. +- ScannerName string `json:"scanner_name,omitempty"` +- +- // ScannerVersion is the version of the tool. +- ScannerVersion string `json:"scanner_version,omitempty"` +- +- // DB is the database used by the tool, for example, +- // vuln.go.dev. +- DB string `json:"db,omitempty"` +- +- // LastModified is the last modified time of the data source. +- DBLastModified *time.Time `json:"db_last_modified,omitempty"` +- +- // GoVersion is the version of Go used for analyzing standard library +- // vulnerabilities. +- GoVersion string `json:"go_version,omitempty"` +- +- // ScanLevel instructs govulncheck to analyze at a specific level of detail. +- // Valid values include module, package and symbol. +- ScanLevel ScanLevel `json:"scan_level,omitempty"` -} -diff -urN a/gopls/internal/span/uri_test.go b/gopls/internal/span/uri_test.go ---- a/gopls/internal/span/uri_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/span/uri_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,117 +0,0 @@ --// Copyright 2019 The Go Authors. All rights reserved. +- +-// Progress messages are informational only, intended to allow users to monitor +-// the progress of a long running scan. +-// A stream must remain fully valid and able to be interpreted with all progress +-// messages removed. +-type Progress struct { +- // A time stamp for the message. +- Timestamp *time.Time `json:"time,omitempty"` +- +- // Message is the progress message. +- Message string `json:"message,omitempty"` +-} +- +-// Vuln represents a single OSV entry. +-type Finding struct { +- // OSV is the id of the detected vulnerability. +- OSV string `json:"osv,omitempty"` +- +- // FixedVersion is the module version where the vulnerability was +- // fixed. This is empty if a fix is not available. +- // +- // If there are multiple fixed versions in the OSV report, this will +- // be the fixed version in the latest range event for the OSV report. +- // +- // For example, if the range events are +- // {introduced: 0, fixed: 1.0.0} and {introduced: 1.1.0}, the fixed version +- // will be empty. +- // +- // For the stdlib, we will show the fixed version closest to the +- // Go version that is used. For example, if a fix is available in 1.17.5 and +- // 1.18.5, and the GOVERSION is 1.17.3, 1.17.5 will be returned as the +- // fixed version. +- FixedVersion string `json:"fixed_version,omitempty"` +- +- // Trace contains an entry for each frame in the trace. +- // +- // Frames are sorted starting from the imported vulnerable symbol +- // until the entry point. The first frame in Frames should match +- // Symbol. +- // +- // In binary mode, trace will contain a single-frame with no position +- // information. +- // +- // When a package is imported but no vulnerable symbol is called, the trace +- // will contain a single-frame with no symbol or position information. +- Trace []*Frame `json:"trace,omitempty"` +-} +- +-// Frame represents an entry in a finding trace. +-type Frame struct { +- // Module is the module path of the module containing this symbol. +- // +- // Importable packages in the standard library will have the path "stdlib". +- Module string `json:"module"` +- +- // Version is the module version from the build graph. +- Version string `json:"version,omitempty"` +- +- // Package is the import path. +- Package string `json:"package,omitempty"` +- +- // Function is the function name. +- Function string `json:"function,omitempty"` +- +- // Receiver is the receiver type if the called symbol is a method. +- // +- // The client can create the final symbol name by +- // prepending Receiver to FuncName. +- Receiver string `json:"receiver,omitempty"` +- +- // Position describes an arbitrary source position +- // including the file, line, and column location. +- // A Position is valid if the line number is > 0. +- Position *Position `json:"position,omitempty"` +-} +- +-// Position represents arbitrary source position. +-type Position struct { +- Filename string `json:"filename,omitempty"` // filename, if any +- Offset int `json:"offset"` // byte offset, starting at 0 +- Line int `json:"line"` // line number, starting at 1 +- Column int `json:"column"` // column number, starting at 1 (byte count) +-} +- +-// ScanLevel represents the detail level at which a scan occurred. +-// This can be necessary to correctly interpret the findings, for instance if +-// a scan is at symbol level and a finding does not have a symbol it means the +-// vulnerability was imported but not called. If the scan however was at +-// "package" level, that determination cannot be made. +-type ScanLevel string +- +-const ( +- scanLevelModule = "module" +- scanLevelPackage = "package" +- scanLevelSymbol = "symbol" +-) +- +-// WantSymbols can be used to check whether the scan level is one that is able +-// to generate symbols called findings. +-func (l ScanLevel) WantSymbols() bool { return l == scanLevelSymbol } +diff -urN a/gopls/internal/vulncheck/govulncheck/handler.go b/gopls/internal/vulncheck/govulncheck/handler.go +--- a/gopls/internal/vulncheck/govulncheck/handler.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/govulncheck/handler.go 1970-01-01 08:00:00 +@@ -1,61 +0,0 @@ +-// Copyright 2023 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 !windows --// +build !windows +-// Code generated by copying from golang.org/x/vuln@v1.0.1 (go run copier.go); DO NOT EDIT. - --package span_test +-package govulncheck - -import ( -- "testing" +- "encoding/json" +- "io" - -- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/gopls/internal/vulncheck/osv" -) - --// TestURI tests the conversion between URIs and filenames. The test cases --// include Windows-style URIs and filepaths, but we avoid having OS-specific --// tests by using only forward slashes, assuming that the standard library --// functions filepath.ToSlash and filepath.FromSlash do not need testing. --func TestURIFromPath(t *testing.T) { -- for _, test := range []struct { -- path, wantFile string -- wantURI span.URI -- }{ -- { -- path: ``, -- wantFile: ``, -- wantURI: span.URI(""), -- }, -- { -- path: `C:/Windows/System32`, -- wantFile: `C:/Windows/System32`, -- wantURI: span.URI("file:///C:/Windows/System32"), -- }, -- { -- path: `C:/Go/src/bob.go`, -- wantFile: `C:/Go/src/bob.go`, -- wantURI: span.URI("file:///C:/Go/src/bob.go"), -- }, -- { -- path: `c:/Go/src/bob.go`, -- wantFile: `C:/Go/src/bob.go`, -- wantURI: span.URI("file:///C:/Go/src/bob.go"), -- }, -- { -- path: `/path/to/dir`, -- wantFile: `/path/to/dir`, -- wantURI: span.URI("file:///path/to/dir"), -- }, -- { -- path: `/a/b/c/src/bob.go`, -- wantFile: `/a/b/c/src/bob.go`, -- wantURI: span.URI("file:///a/b/c/src/bob.go"), -- }, -- { -- path: `c:/Go/src/bob george/george/george.go`, -- wantFile: `C:/Go/src/bob george/george/george.go`, -- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), -- }, -- } { -- got := span.URIFromPath(test.path) -- if got != test.wantURI { -- t.Errorf("URIFromPath(%q): got %q, expected %q", test.path, got, test.wantURI) -- } -- gotFilename := got.Filename() -- if gotFilename != test.wantFile { -- t.Errorf("Filename(%q): got %q, expected %q", got, gotFilename, test.wantFile) -- } -- } +-// Handler handles messages to be presented in a vulnerability scan output +-// stream. +-type Handler interface { +- // Config communicates introductory message to the user. +- Config(config *Config) error +- +- // Progress is called to display a progress message. +- Progress(progress *Progress) error +- +- // OSV is invoked for each osv Entry in the stream. +- OSV(entry *osv.Entry) error +- +- // Finding is called for each vulnerability finding in the stream. +- Finding(finding *Finding) error -} - --func TestURIFromURI(t *testing.T) { -- for _, test := range []struct { -- inputURI, wantFile string -- wantURI span.URI -- }{ -- { -- inputURI: `file:///c:/Go/src/bob%20george/george/george.go`, -- wantFile: `C:/Go/src/bob george/george/george.go`, -- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), -- }, -- { -- inputURI: `file:///C%3A/Go/src/bob%20george/george/george.go`, -- wantFile: `C:/Go/src/bob george/george/george.go`, -- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), -- }, -- { -- inputURI: `file:///path/to/%25p%25ercent%25/per%25cent.go`, -- wantFile: `/path/to/%p%ercent%/per%cent.go`, -- wantURI: span.URI(`file:///path/to/%25p%25ercent%25/per%25cent.go`), -- }, -- { -- inputURI: `file:///C%3A/`, -- wantFile: `C:/`, -- wantURI: span.URI(`file:///C:/`), -- }, -- { -- inputURI: `file:///`, -- wantFile: `/`, -- wantURI: span.URI(`file:///`), -- }, -- { -- inputURI: `file://wsl%24/Ubuntu/home/wdcui/repo/VMEnclaves/cvm-runtime`, -- wantFile: `/wsl$/Ubuntu/home/wdcui/repo/VMEnclaves/cvm-runtime`, -- wantURI: span.URI(`file:///wsl$/Ubuntu/home/wdcui/repo/VMEnclaves/cvm-runtime`), -- }, -- } { -- got := span.URIFromURI(test.inputURI) -- if got != test.wantURI { -- t.Errorf("NewURI(%q): got %q, expected %q", test.inputURI, got, test.wantURI) +-// HandleJSON reads the json from the supplied stream and hands the decoded +-// output to the handler. +-func HandleJSON(from io.Reader, to Handler) error { +- dec := json.NewDecoder(from) +- for dec.More() { +- msg := Message{} +- // decode the next message in the stream +- if err := dec.Decode(&msg); err != nil { +- return err - } -- gotFilename := got.Filename() -- if gotFilename != test.wantFile { -- t.Errorf("Filename(%q): got %q, expected %q", got, gotFilename, test.wantFile) +- // dispatch the message +- var err error +- if msg.Config != nil { +- err = to.Config(msg.Config) +- } +- if msg.Progress != nil { +- err = to.Progress(msg.Progress) +- } +- if msg.OSV != nil { +- err = to.OSV(msg.OSV) +- } +- if msg.Finding != nil { +- err = to.Finding(msg.Finding) +- } +- if err != nil { +- return err - } - } +- return nil -} -diff -urN a/gopls/internal/span/uri_windows_test.go b/gopls/internal/span/uri_windows_test.go ---- a/gopls/internal/span/uri_windows_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/span/uri_windows_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,112 +0,0 @@ --// Copyright 2020 The Go Authors. All rights reserved. +diff -urN a/gopls/internal/vulncheck/govulncheck/jsonhandler.go b/gopls/internal/vulncheck/govulncheck/jsonhandler.go +--- a/gopls/internal/vulncheck/govulncheck/jsonhandler.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/govulncheck/jsonhandler.go 1970-01-01 08:00:00 +@@ -1,46 +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 windows --// +build windows +-// Code generated by copying from golang.org/x/vuln@v1.0.1 (go run copier.go); DO NOT EDIT. - --package span_test +-package govulncheck - -import ( -- "testing" +- "encoding/json" - -- "golang.org/x/tools/gopls/internal/span" +- "io" +- +- "golang.org/x/tools/gopls/internal/vulncheck/osv" -) - --// TestURI tests the conversion between URIs and filenames. The test cases --// include Windows-style URIs and filepaths, but we avoid having OS-specific --// tests by using only forward slashes, assuming that the standard library --// functions filepath.ToSlash and filepath.FromSlash do not need testing. --func TestURIFromPath(t *testing.T) { -- for _, test := range []struct { -- path, wantFile string -- wantURI span.URI -- }{ -- { -- path: ``, -- wantFile: ``, -- wantURI: span.URI(""), -- }, -- { -- path: `C:\Windows\System32`, -- wantFile: `C:\Windows\System32`, -- wantURI: span.URI("file:///C:/Windows/System32"), -- }, -- { -- path: `C:\Go\src\bob.go`, -- wantFile: `C:\Go\src\bob.go`, -- wantURI: span.URI("file:///C:/Go/src/bob.go"), -- }, -- { -- path: `c:\Go\src\bob.go`, -- wantFile: `C:\Go\src\bob.go`, -- wantURI: span.URI("file:///C:/Go/src/bob.go"), -- }, -- { -- path: `\path\to\dir`, -- wantFile: `C:\path\to\dir`, -- wantURI: span.URI("file:///C:/path/to/dir"), -- }, -- { -- path: `\a\b\c\src\bob.go`, -- wantFile: `C:\a\b\c\src\bob.go`, -- wantURI: span.URI("file:///C:/a/b/c/src/bob.go"), -- }, -- { -- path: `c:\Go\src\bob george\george\george.go`, -- wantFile: `C:\Go\src\bob george\george\george.go`, -- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), -- }, -- } { -- got := span.URIFromPath(test.path) -- if got != test.wantURI { -- t.Errorf("URIFromPath(%q): got %q, expected %q", test.path, got, test.wantURI) -- } -- gotFilename := got.Filename() -- if gotFilename != test.wantFile { -- t.Errorf("Filename(%q): got %q, expected %q", got, gotFilename, test.wantFile) -- } -- } +-type jsonHandler struct { +- enc *json.Encoder -} - --func TestURIFromURI(t *testing.T) { -- for _, test := range []struct { -- inputURI, wantFile string -- wantURI span.URI -- }{ -- { -- inputURI: `file:///c:/Go/src/bob%20george/george/george.go`, -- wantFile: `C:\Go\src\bob george\george\george.go`, -- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), -- }, -- { -- inputURI: `file:///C%3A/Go/src/bob%20george/george/george.go`, -- wantFile: `C:\Go\src\bob george\george\george.go`, -- wantURI: span.URI("file:///C:/Go/src/bob%20george/george/george.go"), -- }, -- { -- inputURI: `file:///c:/path/to/%25p%25ercent%25/per%25cent.go`, -- wantFile: `C:\path\to\%p%ercent%\per%cent.go`, -- wantURI: span.URI(`file:///C:/path/to/%25p%25ercent%25/per%25cent.go`), -- }, -- { -- inputURI: `file:///C%3A/`, -- wantFile: `C:\`, -- wantURI: span.URI(`file:///C:/`), -- }, -- { -- inputURI: `file:///`, -- wantFile: `\`, -- wantURI: span.URI(`file:///`), -- }, -- } { -- got := span.URIFromURI(test.inputURI) -- if got != test.wantURI { -- t.Errorf("NewURI(%q): got %q, expected %q", test.inputURI, got, test.wantURI) -- } -- gotFilename := got.Filename() -- if gotFilename != test.wantFile { -- t.Errorf("Filename(%q): got %q, expected %q", got, gotFilename, test.wantFile) -- } -- } +-// NewJSONHandler returns a handler that writes govulncheck output as json. +-func NewJSONHandler(w io.Writer) Handler { +- enc := json.NewEncoder(w) +- enc.SetIndent("", " ") +- return &jsonHandler{enc: enc} +-} +- +-// Config writes config block in JSON to the underlying writer. +-func (h *jsonHandler) Config(config *Config) error { +- return h.enc.Encode(Message{Config: config}) +-} +- +-// Progress writes a progress message in JSON to the underlying writer. +-func (h *jsonHandler) Progress(progress *Progress) error { +- return h.enc.Encode(Message{Progress: progress}) +-} +- +-// OSV writes an osv entry in JSON to the underlying writer. +-func (h *jsonHandler) OSV(entry *osv.Entry) error { +- return h.enc.Encode(Message{OSV: entry}) +-} +- +-// Finding writes a finding in JSON to the underlying writer. +-func (h *jsonHandler) Finding(finding *Finding) error { +- return h.enc.Encode(Message{Finding: finding}) +-} +diff -urN a/gopls/internal/vulncheck/osv/osv.go b/gopls/internal/vulncheck/osv/osv.go +--- a/gopls/internal/vulncheck/osv/osv.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/osv/osv.go 1970-01-01 08:00:00 +@@ -1,240 +0,0 @@ +-// Copyright 2023 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. +- +-// Code generated by copying from golang.org/x/vuln@v1.0.1 (go run copier.go); DO NOT EDIT. +- +-// Package osv implements the Go OSV vulnerability format +-// (https://go.dev/security/vuln/database#schema), which is a subset of +-// the OSV shared vulnerability format +-// (https://ossf.github.io/osv-schema), with database and +-// ecosystem-specific meanings and fields. +-// +-// As this package is intended for use with the Go vulnerability +-// database, only the subset of features which are used by that +-// database are implemented (for instance, only the SEMVER affected +-// range type is implemented). +-package osv +- +-import "time" +- +-// RangeType specifies the type of version range being recorded and +-// defines the interpretation of the RangeEvent object's Introduced +-// and Fixed fields. +-// +-// In this implementation, only the "SEMVER" type is supported. +-// +-// See https://ossf.github.io/osv-schema/#affectedrangestype-field. +-type RangeType string +- +-// RangeTypeSemver indicates a semantic version as defined by +-// SemVer 2.0.0, with no leading "v" prefix. +-const RangeTypeSemver RangeType = "SEMVER" +- +-// Ecosystem identifies the overall library ecosystem. +-// In this implementation, only the "Go" ecosystem is supported. +-type Ecosystem string +- +-// GoEcosystem indicates the Go ecosystem. +-const GoEcosystem Ecosystem = "Go" +- +-// Pseudo-module paths used to describe vulnerabilities +-// in the Go standard library and toolchain. +-const ( +- // GoStdModulePath is the pseudo-module path string used +- // to describe vulnerabilities in the Go standard library. +- GoStdModulePath = "stdlib" +- // GoCmdModulePath is the pseudo-module path string used +- // to describe vulnerabilities in the go command. +- GoCmdModulePath = "toolchain" +-) +- +-// Module identifies the Go module containing the vulnerability. +-// Note that this field is called "package" in the OSV specification. +-// +-// See https://ossf.github.io/osv-schema/#affectedpackage-field. +-type Module struct { +- // The Go module path. Required. +- // For the Go standard library, this is "stdlib". +- // For the Go toolchain, this is "toolchain." +- Path string `json:"name"` +- // The ecosystem containing the module. Required. +- // This should always be "Go". +- Ecosystem Ecosystem `json:"ecosystem"` +-} +- +-// RangeEvent describes a single module version that either +-// introduces or fixes a vulnerability. +-// +-// Exactly one of Introduced and Fixed must be present. Other range +-// event types (e.g, "last_affected" and "limit") are not supported in +-// this implementation. +-// +-// See https://ossf.github.io/osv-schema/#affectedrangesevents-fields. +-type RangeEvent struct { +- // Introduced is a version that introduces the vulnerability. +- // A special value, "0", represents a version that sorts before +- // any other version, and should be used to indicate that the +- // vulnerability exists from the "beginning of time". +- Introduced string `json:"introduced,omitempty"` +- // Fixed is a version that fixes the vulnerability. +- Fixed string `json:"fixed,omitempty"` +-} +- +-// Range describes the affected versions of the vulnerable module. +-// +-// See https://ossf.github.io/osv-schema/#affectedranges-field. +-type Range struct { +- // Type is the version type that should be used to interpret the +- // versions in Events. Required. +- // In this implementation, only the "SEMVER" type is supported. +- Type RangeType `json:"type"` +- // Events is a list of versions representing the ranges in which +- // the module is vulnerable. Required. +- // The events should be sorted, and MUST represent non-overlapping +- // ranges. +- // There must be at least one RangeEvent containing a value for +- // Introduced. +- // See https://ossf.github.io/osv-schema/#examples for examples. +- Events []RangeEvent `json:"events"` +-} +- +-// Reference type is a reference (link) type. +-type ReferenceType string +- +-const ( +- // ReferenceTypeAdvisory is a published security advisory for +- // the vulnerability. +- ReferenceTypeAdvisory = ReferenceType("ADVISORY") +- // ReferenceTypeArticle is an article or blog post describing the vulnerability. +- ReferenceTypeArticle = ReferenceType("ARTICLE") +- // ReferenceTypeReport is a report, typically on a bug or issue tracker, of +- // the vulnerability. +- ReferenceTypeReport = ReferenceType("REPORT") +- // ReferenceTypeFix is a source code browser link to the fix (e.g., a GitHub commit). +- ReferenceTypeFix = ReferenceType("FIX") +- // ReferenceTypePackage is a home web page for the package. +- ReferenceTypePackage = ReferenceType("PACKAGE") +- // ReferenceTypeEvidence is a demonstration of the validity of a vulnerability claim. +- ReferenceTypeEvidence = ReferenceType("EVIDENCE") +- // ReferenceTypeWeb is a web page of some unspecified kind. +- ReferenceTypeWeb = ReferenceType("WEB") +-) +- +-// Reference is a reference URL containing additional information, +-// advisories, issue tracker entries, etc., about the vulnerability. +-// +-// See https://ossf.github.io/osv-schema/#references-field. +-type Reference struct { +- // The type of reference. Required. +- Type ReferenceType `json:"type"` +- // The fully-qualified URL of the reference. Required. +- URL string `json:"url"` +-} +- +-// Affected gives details about a module affected by the vulnerability. +-// +-// See https://ossf.github.io/osv-schema/#affected-fields. +-type Affected struct { +- // The affected Go module. Required. +- // Note that this field is called "package" in the OSV specification. +- Module Module `json:"package"` +- // The module version ranges affected by the vulnerability. +- Ranges []Range `json:"ranges,omitempty"` +- // Details on the affected packages and symbols within the module. +- EcosystemSpecific EcosystemSpecific `json:"ecosystem_specific"` +-} +- +-// Package contains additional information about an affected package. +-// This is an ecosystem-specific field for the Go ecosystem. +-type Package struct { +- // Path is the package import path. Required. +- Path string `json:"path,omitempty"` +- // GOOS is the execution operating system where the symbols appear, if +- // known. +- GOOS []string `json:"goos,omitempty"` +- // GOARCH specifies the execution architecture where the symbols appear, if +- // known. +- GOARCH []string `json:"goarch,omitempty"` +- // Symbols is a list of function and method names affected by +- // this vulnerability. Methods are listed as <recv>.<method>. +- // +- // If included, only programs which use these symbols will be marked as +- // vulnerable by `govulncheck`. If omitted, any program which imports this +- // package will be marked vulnerable. +- Symbols []string `json:"symbols,omitempty"` +-} +- +-// EcosystemSpecific contains additional information about the vulnerable +-// module for the Go ecosystem. +-// +-// See https://go.dev/security/vuln/database#schema. +-type EcosystemSpecific struct { +- // Packages is the list of affected packages within the module. +- Packages []Package `json:"imports,omitempty"` +-} +- +-// Entry represents a vulnerability in the Go OSV format, documented +-// in https://go.dev/security/vuln/database#schema. +-// It is a subset of the OSV schema (https://ossf.github.io/osv-schema). +-// Only fields that are published in the Go Vulnerability Database +-// are supported. +-type Entry struct { +- // SchemaVersion is the OSV schema version used to encode this +- // vulnerability. +- SchemaVersion string `json:"schema_version,omitempty"` +- // ID is a unique identifier for the vulnerability. Required. +- // The Go vulnerability database issues IDs of the form +- // GO-<YEAR>-<ENTRYID>. +- ID string `json:"id"` +- // Modified is the time the entry was last modified. Required. +- Modified time.Time `json:"modified,omitempty"` +- // Published is the time the entry should be considered to have +- // been published. +- Published time.Time `json:"published,omitempty"` +- // Withdrawn is the time the entry should be considered to have +- // been withdrawn. If the field is missing, then the entry has +- // not been withdrawn. +- Withdrawn *time.Time `json:"withdrawn,omitempty"` +- // Aliases is a list of IDs for the same vulnerability in other +- // databases. +- Aliases []string `json:"aliases,omitempty"` +- // Summary gives a one-line, English textual summary of the vulnerability. +- // It is recommended that this field be kept short, on the order of no more +- // than 120 characters. +- Summary string `json:"summary,omitempty"` +- // Details contains additional English textual details about the vulnerability. +- Details string `json:"details"` +- // Affected contains information on the modules and versions +- // affected by the vulnerability. +- Affected []Affected `json:"affected"` +- // References contains links to more information about the +- // vulnerability. +- References []Reference `json:"references,omitempty"` +- // Credits contains credits to entities that helped find or fix the +- // vulnerability. +- Credits []Credit `json:"credits,omitempty"` +- // DatabaseSpecific contains additional information about the +- // vulnerability, specific to the Go vulnerability database. +- DatabaseSpecific *DatabaseSpecific `json:"database_specific,omitempty"` +-} +- +-// Credit represents a credit for the discovery, confirmation, patch, or +-// other event in the life cycle of a vulnerability. +-// +-// See https://ossf.github.io/osv-schema/#credits-fields. +-type Credit struct { +- // Name is the name, label, or other identifier of the individual or +- // entity being credited. Required. +- Name string `json:"name"` +-} +- +-// DatabaseSpecific contains additional information about the +-// vulnerability, specific to the Go vulnerability database. +-// +-// See https://go.dev/security/vuln/database#schema. +-type DatabaseSpecific struct { +- // The URL of the Go advisory for this vulnerability, of the form +- // "https://pkg.go.dev/GO-YYYY-XXXX". +- URL string `json:"url,omitempty"` -} -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 00:00:00.000000000 +0000 -@@ -1,381 +0,0 @@ +diff -urN a/gopls/internal/vulncheck/scan/command.go b/gopls/internal/vulncheck/scan/command.go +--- a/gopls/internal/vulncheck/scan/command.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/scan/command.go 1970-01-01 08:00:00 +@@ -1,476 +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. @@ -125355,143 +140548,182 @@ diff -urN a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/comma -//go:build go1.18 -// +build go1.18 - --package vulncheck +-package scan - -import ( +- "bytes" - "context" -- "encoding/json" -- "errors" - "fmt" -- "log" +- "io" - "os" -- "regexp" +- "os/exec" - "sort" - "strings" - "sync" +- "time" - - "golang.org/x/mod/semver" - "golang.org/x/sync/errgroup" - "golang.org/x/tools/go/packages" -- "golang.org/x/tools/gopls/internal/govulncheck" - "golang.org/x/tools/gopls/internal/lsp/source" -- "golang.org/x/vuln/client" -- gvcapi "golang.org/x/vuln/exp/govulncheck" -- "golang.org/x/vuln/osv" -- "golang.org/x/vuln/vulncheck" +- "golang.org/x/tools/gopls/internal/vulncheck" +- "golang.org/x/tools/gopls/internal/vulncheck/govulncheck" +- "golang.org/x/tools/gopls/internal/vulncheck/osv" +- isem "golang.org/x/tools/gopls/internal/vulncheck/semver" +- "golang.org/x/vuln/scan" -) - --func init() { -- VulnerablePackages = vulnerablePackages +-// GoVersionForVulnTest is an internal environment variable used in gopls +-// testing to examine govulncheck behavior with a go version different +-// than what `go version` returns in the system. +-const GoVersionForVulnTest = "_GOPLS_TEST_VULNCHECK_GOVERSION" +- +-// Main implements gopls vulncheck. +-func Main(ctx context.Context, args ...string) error { +- // wrapping govulncheck. +- cmd := scan.Command(ctx, args...) +- if err := cmd.Start(); err != nil { +- return err +- } +- return cmd.Wait() -} - --func findGOVULNDB(env []string) []string { -- for _, kv := range env { -- if strings.HasPrefix(kv, "GOVULNDB=") { -- return strings.Split(kv[len("GOVULNDB="):], ",") -- } +-// RunGovulncheck implements the codelens "Run Govulncheck" +-// that runs 'gopls vulncheck' and converts the output to gopls's internal data +-// used for diagnostics and hover message construction. +-func RunGovulncheck(ctx context.Context, pattern string, snapshot source.Snapshot, dir string, log io.Writer) (*vulncheck.Result, error) { +- vulncheckargs := []string{ +- "vulncheck", "--", +- "-json", +- "-mode", "source", +- "-scan", "symbol", +- } +- if dir != "" { +- vulncheckargs = append(vulncheckargs, "-C", dir) - } -- if GOVULNDB := os.Getenv("GOVULNDB"); GOVULNDB != "" { -- return strings.Split(GOVULNDB, ",") +- if db := getEnv(snapshot, "GOVULNDB"); db != "" { +- vulncheckargs = append(vulncheckargs, "-db", db) - } -- return []string{"https://vuln.go.dev"} --} +- vulncheckargs = append(vulncheckargs, pattern) +- // TODO: support -tags. need to compute tags args from opts.BuildFlags. +- // TODO: support -test. - --// GoVersionForVulnTest is an internal environment variable used in gopls --// testing to examine govulncheck behavior with a go version different --// than what `go version` returns in the system. --const GoVersionForVulnTest = "_GOPLS_TEST_VULNCHECK_GOVERSION" +- ir, iw := io.Pipe() +- handler := &govulncheckHandler{logger: log, osvs: map[string]*osv.Entry{}} - --func init() { -- Main = func(cfg packages.Config, patterns ...string) error { -- // Set the mode that Source needs. -- cfg.Mode = packages.NeedName | packages.NeedImports | packages.NeedTypes | -- packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps | -- packages.NeedModule -- logf := log.New(os.Stderr, "", log.Ltime).Printf -- logf("Loading packages...") -- pkgs, err := packages.Load(&cfg, patterns...) -- if err != nil { -- logf("Failed to load packages: %v", err) -- return err -- } -- if n := packages.PrintErrors(pkgs); n > 0 { -- err := errors.New("failed to load packages due to errors") -- logf("%v", err) -- return err -- } -- logf("Loaded %d packages and their dependencies", len(pkgs)) -- cache, err := govulncheck.DefaultCache() -- if err != nil { -- return err -- } -- cli, err := client.NewClient(findGOVULNDB(cfg.Env), client.Options{ -- HTTPCache: cache, -- }) -- if err != nil { -- return err +- stderr := new(bytes.Buffer) +- var g errgroup.Group +- // We run the govulncheck's analysis in a separate process as it can +- // consume a lot of CPUs and memory, and terminates: a separate process +- // is a perfect garbage collector and affords us ways to limit its resource usage. +- g.Go(func() error { +- defer iw.Close() +- +- cmd := exec.CommandContext(ctx, os.Args[0], vulncheckargs...) +- cmd.Env = getEnvSlices(snapshot) +- if goversion := getEnv(snapshot, GoVersionForVulnTest); goversion != "" { +- // Let govulncheck API use a different Go version using the (undocumented) hook +- // in https://go.googlesource.com/vuln/+/v1.0.1/internal/scan/run.go#76 +- cmd.Env = append(cmd.Env, "GOVERSION="+goversion) +- } +- cmd.Stderr = stderr // stream vulncheck's STDERR as progress reports +- cmd.Stdout = iw // let the other goroutine parses the result. +- +- if err := cmd.Start(); err != nil { +- return fmt.Errorf("failed to start govulncheck: %v", err) - } -- res, err := gvcapi.Source(context.Background(), &gvcapi.Config{ -- Client: cli, -- GoVersion: os.Getenv(GoVersionForVulnTest), -- }, vulncheck.Convert(pkgs)) -- if err != nil { -- return err +- if err := cmd.Wait(); err != nil { +- return fmt.Errorf("failed to run govulncheck: %v", err) - } -- affecting := 0 -- for _, v := range res.Vulns { -- if v.IsCalled() { -- affecting++ -- } +- return nil +- }) +- g.Go(func() error { +- return govulncheck.HandleJSON(ir, handler) +- }) +- if err := g.Wait(); err != nil { +- if stderr.Len() > 0 { +- log.Write(stderr.Bytes()) - } -- logf("Found %d affecting vulns and %d unaffecting vulns in imported packages", affecting, len(res.Vulns)-affecting) -- if err := json.NewEncoder(os.Stdout).Encode(res); err != nil { -- return err +- return nil, fmt.Errorf("failed to read govulncheck output: %v", err) +- } +- +- findings := handler.findings // sort so the findings in the result is deterministic. +- sort.Slice(findings, func(i, j int) bool { +- x, y := findings[i], findings[j] +- if x.OSV != y.OSV { +- return x.OSV < y.OSV - } -- return nil +- return x.Trace[0].Package < y.Trace[0].Package +- }) +- result := &vulncheck.Result{ +- Mode: vulncheck.ModeGovulncheck, +- AsOf: time.Now(), +- Entries: handler.osvs, +- Findings: findings, - } +- return result, nil -} - --var ( -- // Regexp for matching go tags. The groups are: -- // 1 the major.minor version -- // 2 the patch version, or empty if none -- // 3 the entire prerelease, if present -- // 4 the prerelease type ("beta" or "rc") -- // 5 the prerelease number -- tagRegexp = regexp.MustCompile(`^go(\d+\.\d+)(\.\d+|)((beta|rc|-pre)(\d+))?$`) --) +-type govulncheckHandler struct { +- logger io.Writer // forward progress reports to logger. +- err error - --// This is a modified copy of pkgsite/internal/stdlib:VersionForTag. --func GoTagToSemver(tag string) string { -- if tag == "" { -- return "" -- } +- osvs map[string]*osv.Entry +- findings []*govulncheck.Finding +-} - -- tag = strings.Fields(tag)[0] -- // Special cases for go1. -- if tag == "go1" { -- return "v1.0.0" +-// Config implements vulncheck.Handler. +-func (h *govulncheckHandler) Config(config *govulncheck.Config) error { +- if config.GoVersion != "" { +- fmt.Fprintf(h.logger, "Go: %v\n", config.GoVersion) - } -- if tag == "go1.0" { -- return "" +- if config.ScannerName != "" { +- scannerName := fmt.Sprintf("Scanner: %v", config.ScannerName) +- if config.ScannerVersion != "" { +- scannerName += "@" + config.ScannerVersion +- } +- fmt.Fprintln(h.logger, scannerName) - } -- m := tagRegexp.FindStringSubmatch(tag) -- if m == nil { -- return "" +- if config.DB != "" { +- dbInfo := fmt.Sprintf("DB: %v", config.DB) +- if config.DBLastModified != nil { +- dbInfo += fmt.Sprintf(" (DB updated: %v)", config.DBLastModified.String()) +- } +- fmt.Fprintln(h.logger, dbInfo) - } -- version := "v" + m[1] -- if m[2] != "" { -- version += m[2] -- } else { -- version += ".0" +- return nil +-} +- +-// Finding implements vulncheck.Handler. +-func (h *govulncheckHandler) Finding(finding *govulncheck.Finding) error { +- h.findings = append(h.findings, finding) +- return nil +-} +- +-// OSV implements vulncheck.Handler. +-func (h *govulncheckHandler) OSV(entry *osv.Entry) error { +- h.osvs[entry.ID] = entry +- return nil +-} +- +-// Progress implements vulncheck.Handler. +-func (h *govulncheckHandler) Progress(progress *govulncheck.Progress) error { +- if progress.Message != "" { +- fmt.Fprintf(h.logger, "%v\n", progress.Message) - } -- if m[3] != "" { -- if !strings.HasPrefix(m[4], "-") { -- version += "-" -- } -- version += m[4] + "." + m[5] +- return nil +-} +- +-func getEnv(snapshot source.Snapshot, key string) string { +- val, ok := snapshot.Options().Env[key] +- if ok { +- return val - } -- return version +- return os.Getenv(key) +-} +- +-func getEnvSlices(snapshot source.Snapshot) []string { +- return append(os.Environ(), snapshot.Options().EnvSlice()...) -} - -// semverToGoTag returns the Go standard library repository tag corresponding @@ -125550,10 +140782,14 @@ diff -urN a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/comma - return i + 1 -} - --// vulnerablePackages queries the vulndb and reports which vulnerabilities +-// VulnerablePackages queries the vulndb and reports which vulnerabilities -// apply to this snapshot. The result contains a set of packages, --// grouped by vuln ID and by module. --func vulnerablePackages(ctx context.Context, snapshot source.Snapshot, modfile source.FileHandle) (*govulncheck.Result, error) { +-// grouped by vuln ID and by module. This implements the "import-based" +-// vulnerability report on go.mod files. +-func VulnerablePackages(ctx context.Context, snapshot source.Snapshot) (*vulncheck.Result, error) { +- // TODO(hyangah): can we let 'govulncheck' take a package list +- // used in the workspace and implement this function? +- - // We want to report the intersection of vulnerable packages in the vulndb - // and packages transitively imported by this module ('go list -deps all'). - // We use snapshot.AllMetadata to retrieve the list of packages @@ -125577,43 +140813,37 @@ diff -urN a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/comma - // Group packages by modules since vuln db is keyed by module. - metadataByModule := map[source.PackagePath][]*source.Metadata{} - for _, md := range metadata { -- mi := md.Module -- modulePath := source.PackagePath("stdlib") -- if mi != nil { +- modulePath := source.PackagePath(osv.GoStdModulePath) +- if mi := md.Module; mi != nil { - modulePath = source.PackagePath(mi.Path) - } - metadataByModule[modulePath] = append(metadataByModule[modulePath], md) - } - -- // Request vuln entries from remote service. -- fsCache, err := govulncheck.DefaultCache() -- if err != nil { -- return nil, err -- } -- cli, err := client.NewClient( -- findGOVULNDB(snapshot.View().Options().EnvSlice()), -- client.Options{HTTPCache: govulncheck.NewInMemoryCache(fsCache)}) -- if err != nil { -- return nil, err -- } -- // Keys are osv.Entry.IDs -- vulnsResult := map[string]*govulncheck.Vuln{} - var ( -- group errgroup.Group -- mu sync.Mutex +- mu sync.Mutex +- // Keys are osv.Entry.ID +- osvs = map[string]*osv.Entry{} +- findings []*govulncheck.Finding - ) - -- goVersion := snapshot.View().Options().Env[GoVersionForVulnTest] +- goVersion := snapshot.Options().Env[GoVersionForVulnTest] - if goVersion == "" { - goVersion = snapshot.View().GoVersionString() - } -- group.SetLimit(10) +- - stdlibModule := &packages.Module{ -- Path: "stdlib", +- Path: osv.GoStdModulePath, - Version: goVersion, - } -- for path, mds := range metadataByModule { -- path, mds := path, mds +- +- // GOVULNDB may point the test db URI. +- db := getEnv(snapshot, "GOVULNDB") +- +- var group errgroup.Group +- group.SetLimit(10) // limit govulncheck api runs +- for _, mds := range metadataByModule { +- mds := mds - group.Go(func() error { - effectiveModule := stdlibModule - if m := mds[0].Module; m != nil { @@ -125623,9 +140853,13 @@ diff -urN a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/comma - effectiveModule = effectiveModule.Replace - } - ver := effectiveModule.Version +- if ver == "" || !isem.Valid(ver) { +- // skip invalid version strings. the underlying scan api is strict. +- return nil +- } - -- // TODO(go.dev/issues/56312): batch these requests for efficiency. -- vulns, err := cli.GetByModule(ctx, effectiveModule.Path) +- // TODO(hyangah): batch these requests and add in-memory cache for efficiency. +- vulns, err := osvsByModule(ctx, db, effectiveModule.Path+"@"+ver) - if err != nil { - return err - } @@ -125639,22 +140873,28 @@ diff -urN a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/comma - - // Report vulnerabilities that affect packages of this module. - for _, entry := range vulns { -- var vulnerablePkgs []*govulncheck.Package +- var vulnerablePkgs []*govulncheck.Finding +- fixed := fixedVersion(effectiveModule.Path, entry.Affected) - - for _, a := range entry.Affected { -- if a.Package.Ecosystem != osv.GoEcosystem || a.Package.Name != effectiveModule.Path { +- if a.Module.Ecosystem != osv.GoEcosystem || a.Module.Path != effectiveModule.Path { - continue - } -- if !a.Ranges.AffectsSemver(ver) { -- continue -- } -- for _, imp := range a.EcosystemSpecific.Imports { +- for _, imp := range a.EcosystemSpecific.Packages { - if knownPkgs == nil { - knownPkgs = toPackagePathSet(mds) - } - if knownPkgs[source.PackagePath(imp.Path)] { -- vulnerablePkgs = append(vulnerablePkgs, &govulncheck.Package{ -- Path: imp.Path, +- vulnerablePkgs = append(vulnerablePkgs, &govulncheck.Finding{ +- OSV: entry.ID, +- FixedVersion: fixed, +- Trace: []*govulncheck.Frame{ +- { +- Module: effectiveModule.Path, +- Version: effectiveModule.Version, +- Package: imp.Path, +- }, +- }, - }) - } - } @@ -125663,17 +140903,8 @@ diff -urN a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/comma - continue - } - mu.Lock() -- vuln, ok := vulnsResult[entry.ID] -- if !ok { -- vuln = &govulncheck.Vuln{OSV: entry} -- vulnsResult[entry.ID] = vuln -- } -- vuln.Modules = append(vuln.Modules, &govulncheck.Module{ -- Path: string(path), -- FoundVersion: ver, -- FixedVersion: fixedVersion(effectiveModule.Path, entry.Affected), -- Packages: vulnerablePkgs, -- }) +- osvs[entry.ID] = entry +- findings = append(findings, vulnerablePkgs...) - mu.Unlock() - } - return nil @@ -125683,17 +140914,18 @@ diff -urN a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/comma - return nil, err - } - -- vulns := make([]*govulncheck.Vuln, 0, len(vulnsResult)) -- for _, v := range vulnsResult { -- vulns = append(vulns, v) -- } - // Sort so the results are deterministic. -- sort.Slice(vulns, func(i, j int) bool { -- return vulns[i].OSV.ID < vulns[j].OSV.ID +- sort.Slice(findings, func(i, j int) bool { +- x, y := findings[i], findings[j] +- if x.OSV != y.OSV { +- return x.OSV < y.OSV +- } +- return x.Trace[0].Package < y.Trace[0].Package - }) -- ret := &govulncheck.Result{ -- Vulns: vulns, -- Mode: govulncheck.ModeImports, +- ret := &vulncheck.Result{ +- Entries: osvs, +- Findings: findings, +- Mode: vulncheck.ModeImports, - } - return ret, nil -} @@ -125708,7 +140940,7 @@ diff -urN a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/comma -} - -func fixedVersion(modulePath string, affected []osv.Affected) string { -- fixed := govulncheck.LatestFixed(modulePath, affected) +- fixed := LatestFixed(modulePath, affected) - if fixed != "" { - fixed = versionString(modulePath, fixed) - } @@ -125729,39 +140961,252 @@ diff -urN a/gopls/internal/vulncheck/command.go b/gopls/internal/vulncheck/comma - } - return v -} -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 00:00:00.000000000 +0000 -@@ -1,25 +0,0 @@ +- +-// osvsByModule runs a govulncheck database query. +-func osvsByModule(ctx context.Context, db, moduleVersion string) ([]*osv.Entry, error) { +- var args []string +- args = append(args, "-mode=query", "-json") +- if db != "" { +- args = append(args, "-db="+db) +- } +- args = append(args, moduleVersion) +- +- ir, iw := io.Pipe() +- handler := &osvReader{} +- +- var g errgroup.Group +- g.Go(func() error { +- defer iw.Close() // scan API doesn't close cmd.Stderr/cmd.Stdout. +- cmd := scan.Command(ctx, args...) +- cmd.Stdout = iw +- // TODO(hakim): Do we need to set cmd.Env = getEnvSlices(), +- // or is the process environment good enough? +- if err := cmd.Start(); err != nil { +- return err +- } +- return cmd.Wait() +- }) +- g.Go(func() error { +- return govulncheck.HandleJSON(ir, handler) +- }) +- +- if err := g.Wait(); err != nil { +- return nil, err +- } +- return handler.entry, nil +-} +- +-// osvReader implements govulncheck.Handler. +-type osvReader struct { +- entry []*osv.Entry +-} +- +-func (h *osvReader) OSV(entry *osv.Entry) error { +- h.entry = append(h.entry, entry) +- return nil +-} +- +-func (h *osvReader) Config(config *govulncheck.Config) error { +- return nil +-} +- +-func (h *osvReader) Finding(finding *govulncheck.Finding) error { +- return nil +-} +- +-func (h *osvReader) Progress(progress *govulncheck.Progress) error { +- return nil +-} +diff -urN a/gopls/internal/vulncheck/scan/util.go b/gopls/internal/vulncheck/scan/util.go +--- a/gopls/internal/vulncheck/scan/util.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/scan/util.go 1970-01-01 08:00:00 +@@ -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 vulncheck provides an analysis command --// that runs vulnerability analysis using data from --// golang.org/x/vuln/vulncheck. --// This package requires go1.18 or newer. +-//go:build go1.18 +-// +build go1.18 +- +-package scan +- +-import ( +- "golang.org/x/mod/semver" +- "golang.org/x/tools/gopls/internal/vulncheck/osv" +- isem "golang.org/x/tools/gopls/internal/vulncheck/semver" +-) +- +-// LatestFixed returns the latest fixed version in the list of affected ranges, +-// or the empty string if there are no fixed versions. +-func LatestFixed(modulePath string, as []osv.Affected) string { +- v := "" +- for _, a := range as { +- if a.Module.Path != modulePath { +- continue +- } +- for _, r := range a.Ranges { +- if r.Type == osv.RangeTypeSemver { +- for _, e := range r.Events { +- if e.Fixed != "" && (v == "" || +- semver.Compare(isem.CanonicalizeSemverPrefix(e.Fixed), isem.CanonicalizeSemverPrefix(v)) > 0) { +- v = e.Fixed +- } +- } +- } +- } +- } +- return v +-} +diff -urN a/gopls/internal/vulncheck/semver/semver.go b/gopls/internal/vulncheck/semver/semver.go +--- a/gopls/internal/vulncheck/semver/semver.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/semver/semver.go 1970-01-01 08:00:00 +@@ -1,59 +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 semver provides shared utilities for manipulating +-// Go semantic versions. +-package semver +- +-import ( +- "regexp" +- "strings" +- +- "golang.org/x/mod/semver" +-) +- +-// addSemverPrefix adds a 'v' prefix to s if it isn't already prefixed +-// with 'v' or 'go'. This allows us to easily test go-style SEMVER +-// strings against normal SEMVER strings. +-func addSemverPrefix(s string) string { +- if !strings.HasPrefix(s, "v") && !strings.HasPrefix(s, "go") { +- return "v" + s +- } +- return s +-} +- +-// removeSemverPrefix removes the 'v' or 'go' prefixes from go-style +-// SEMVER strings, for usage in the public vulnerability format. +-func removeSemverPrefix(s string) string { +- s = strings.TrimPrefix(s, "v") +- s = strings.TrimPrefix(s, "go") +- return s +-} +- +-// CanonicalizeSemverPrefix turns a SEMVER string into the canonical +-// representation using the 'v' prefix, as used by the OSV format. +-// Input may be a bare SEMVER ("1.2.3"), Go prefixed SEMVER ("go1.2.3"), +-// or already canonical SEMVER ("v1.2.3"). +-func CanonicalizeSemverPrefix(s string) string { +- return addSemverPrefix(removeSemverPrefix(s)) +-} +- +-// Valid returns whether v is valid semver, allowing +-// either a "v", "go" or no prefix. +-func Valid(v string) bool { +- return semver.IsValid(CanonicalizeSemverPrefix(v)) +-} +- +-var ( +- // Regexp for matching go tags. The groups are: +- // 1 the major.minor version +- // 2 the patch version, or empty if none +- // 3 the entire prerelease, if present +- // 4 the prerelease type ("beta" or "rc") +- // 5 the prerelease number +- tagRegexp = regexp.MustCompile(`^go(\d+\.\d+)(\.\d+|)((beta|rc|-pre)(\d+))?$`) +-) +diff -urN a/gopls/internal/vulncheck/semver/semver_test.go b/gopls/internal/vulncheck/semver/semver_test.go +--- a/gopls/internal/vulncheck/semver/semver_test.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/semver/semver_test.go 1970-01-01 08:00:00 +@@ -1,28 +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 semver +- +-import ( +- "testing" +-) +- +-func TestCanonicalize(t *testing.T) { +- for _, test := range []struct { +- v string +- want string +- }{ +- {"v1.2.3", "v1.2.3"}, +- {"1.2.3", "v1.2.3"}, +- {"go1.2.3", "v1.2.3"}, +- } { +- got := CanonicalizeSemverPrefix(test.v) +- if got != test.want { +- t.Errorf("want %s; got %s", test.want, got) +- } +- } +-} +diff -urN a/gopls/internal/vulncheck/types.go b/gopls/internal/vulncheck/types.go +--- a/gopls/internal/vulncheck/types.go 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/types.go 1970-01-01 08:00:00 +@@ -1,47 +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:generate go run copier.go +- -package vulncheck - -import ( -- "context" +- "time" - -- "golang.org/x/tools/go/packages" -- "golang.org/x/tools/gopls/internal/govulncheck" -- "golang.org/x/tools/gopls/internal/lsp/source" +- gvc "golang.org/x/tools/gopls/internal/vulncheck/govulncheck" +- "golang.org/x/tools/gopls/internal/vulncheck/osv" -) - --// With go1.18+, this is swapped with the real implementation. --var Main func(cfg packages.Config, patterns ...string) error = nil +-// Result is the result of vulnerability scanning. +-type Result struct { +- // Entries contains all vulnerabilities that are called or imported by +- // the analyzed module. Keys are Entry.IDs. +- Entries map[string]*osv.Entry +- // Findings are vulnerabilities found by vulncheck or import-based analysis. +- // Ordered by the OSV IDs and the package names. +- Findings []*gvc.Finding - --// VulnerablePackages queries the vulndb and reports which vulnerabilities --// apply to this snapshot. The result contains a set of packages, --// grouped by vuln ID and by module. --var VulnerablePackages func(ctx context.Context, snapshot source.Snapshot, modfile source.FileHandle) (*govulncheck.Result, error) = nil +- // Mode contains the source of the vulnerability info. +- // Clients of the gopls.fetch_vulncheck_result command may need +- // to interpret the vulnerabilities differently based on the +- // analysis mode. For example, Vuln without callstack traces +- // indicate a vulnerability that is not used if the result was +- // from 'govulncheck' analysis mode. On the other hand, Vuln +- // without callstack traces just implies the package with the +- // vulnerability is known to the workspace and we do not know +- // whether the vulnerable symbols are actually used or not. +- Mode AnalysisMode `json:",omitempty"` +- +- // AsOf describes when this Result was computed using govulncheck. +- // It is valid only with the govulncheck analysis mode. +- AsOf time.Time `json:",omitempty"` +-} +- +-type AnalysisMode string +- +-const ( +- ModeInvalid AnalysisMode = "" // zero value +- ModeGovulncheck AnalysisMode = "govulncheck" +- ModeImports AnalysisMode = "imports" +-) diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/vulntest/db.go --- a/gopls/internal/vulncheck/vulntest/db.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/vulncheck/vulntest/db.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,303 +0,0 @@ ++++ b/gopls/internal/vulncheck/vulntest/db.go 1970-01-01 08:00:00 +@@ -1,243 +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. @@ -125777,7 +141222,6 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v - "context" - "encoding/json" - "fmt" -- "io/ioutil" - "os" - "path/filepath" - "sort" @@ -125785,9 +141229,8 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v - "time" - - "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/gopls/internal/vulncheck/osv" - "golang.org/x/tools/txtar" -- "golang.org/x/vuln/client" -- "golang.org/x/vuln/osv" -) - -// NewDatabase returns a read-only DB containing the provided @@ -125806,7 +141249,7 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v -// The returned DB's Clean method must be called to clean up the -// generated database. -func NewDatabase(ctx context.Context, txtarReports []byte) (*DB, error) { -- disk, err := ioutil.TempDir("", "vulndb-test") +- disk, err := os.MkdirTemp("", "vulndb-test") - if err != nil { - return nil, err - } @@ -125828,7 +141271,7 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v -// URI returns the file URI that can be used for VULNDB environment -// variable. -func (db *DB) URI() string { -- u := span.URIFromPath(db.disk) +- u := span.URIFromPath(filepath.Join(db.disk, "ID")) - return string(u) -} - @@ -125837,11 +141280,6 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v - return os.RemoveAll(db.disk) -} - --// NewClient returns a vuln DB client that works with the given DB. --func NewClient(db *DB) (client.Client, error) { -- return client.NewClient([]string{db.URI()}, client.Options{}) --} -- -// -// The following was selectively copied from golang.org/x/vulndb/internal/database -// @@ -125853,14 +141291,6 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v - // listed by their IDs. - idDirectory = "ID" - -- // stdFileName is the name of the .json file in the vulndb repo -- // that will contain info on standard library vulnerabilities. -- stdFileName = "stdlib" -- -- // toolchainFileName is the name of the .json file in the vulndb repo -- // that will contain info on toolchain (cmd/...) vulnerabilities. -- toolchainFileName = "toolchain" -- - // cmdModule is the name of the module containing Go toolchain - // binaries. - cmdModule = "cmd" @@ -125873,38 +141303,15 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v -func generateDB(ctx context.Context, txtarData []byte, jsonDir string, indent bool) error { - archive := txtar.Parse(txtarData) - -- jsonVulns, entries, err := generateEntries(ctx, archive) +- entries, err := generateEntries(ctx, archive) - if err != nil { - return err - } -- -- index := make(client.DBIndex, len(jsonVulns)) -- for modulePath, vulns := range jsonVulns { -- epath, err := client.EscapeModulePath(modulePath) -- if err != nil { -- return err -- } -- if err := writeVulns(filepath.Join(jsonDir, epath), vulns, indent); err != nil { -- return err -- } -- for _, v := range vulns { -- if v.Modified.After(index[modulePath]) { -- index[modulePath] = v.Modified -- } -- } -- } -- if err := writeJSON(filepath.Join(jsonDir, "index.json"), index, indent); err != nil { -- return err -- } -- if err := writeAliasIndex(jsonDir, entries, indent); err != nil { -- return err -- } - return writeEntriesByID(filepath.Join(jsonDir, idDirectory), entries, indent) -} - --func generateEntries(_ context.Context, archive *txtar.Archive) (map[string][]osv.Entry, []osv.Entry, error) { +-func generateEntries(_ context.Context, archive *txtar.Archive) ([]osv.Entry, error) { - now := time.Now() -- jsonVulns := map[string][]osv.Entry{} - var entries []osv.Entry - for _, f := range archive.Files { - if !strings.HasSuffix(f.Name, ".yaml") { @@ -125912,17 +141319,14 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v - } - r, err := readReport(bytes.NewReader(f.Data)) - if err != nil { -- return nil, nil, err +- return nil, err - } - name := strings.TrimSuffix(filepath.Base(f.Name), filepath.Ext(f.Name)) - linkName := fmt.Sprintf("%s%s", dbURL, name) -- entry, modulePaths := generateOSVEntry(name, linkName, now, *r) -- for _, modulePath := range modulePaths { -- jsonVulns[modulePath] = append(jsonVulns[modulePath], entry) -- } +- entry := generateOSVEntry(name, linkName, now, *r) - entries = append(entries, entry) - } -- return jsonVulns, entries, nil +- return entries, nil -} - -func writeVulns(outPath string, vulns []osv.Entry, indent bool) error { @@ -125937,27 +141341,13 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v - if err := os.MkdirAll(idDir, 0755); err != nil { - return fmt.Errorf("failed to create directory %q: %v", idDir, err) - } -- var idIndex []string - for _, e := range entries { - outPath := filepath.Join(idDir, e.ID+".json") - if err := writeJSON(outPath, e, indent); err != nil { - return err - } -- idIndex = append(idIndex, e.ID) - } -- // Write an index.json in the ID directory with a list of all the IDs. -- return writeJSON(filepath.Join(idDir, "index.json"), idIndex, indent) --} -- --// Write a JSON file containing a map from alias to GO IDs. --func writeAliasIndex(dir string, entries []osv.Entry, indent bool) error { -- aliasToGoIDs := map[string][]string{} -- for _, e := range entries { -- for _, a := range e.Aliases { -- aliasToGoIDs[a] = append(aliasToGoIDs[a], e.ID) -- } -- } -- return writeJSON(filepath.Join(dir, "aliases.json"), aliasToGoIDs, indent) +- return nil -} - -func writeJSON(filename string, value any, indent bool) (err error) { @@ -125978,45 +141368,40 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v -// generateOSVEntry create an osv.Entry for a report. In addition to the report, it -// takes the ID for the vuln and a URL that will point to the entry in the vuln DB. -// It returns the osv.Entry and a list of module paths that the vuln affects. --func generateOSVEntry(id, url string, lastModified time.Time, r Report) (osv.Entry, []string) { +-func generateOSVEntry(id, url string, lastModified time.Time, r Report) osv.Entry { - entry := osv.Entry{ -- ID: id, -- Published: r.Published, -- Modified: lastModified, -- Withdrawn: r.Withdrawn, -- Details: r.Description, +- ID: id, +- Published: r.Published, +- Modified: lastModified, +- Withdrawn: r.Withdrawn, +- Summary: r.Summary, +- Details: r.Description, +- DatabaseSpecific: &osv.DatabaseSpecific{URL: url}, - } - - moduleMap := make(map[string]bool) - for _, m := range r.Modules { - switch m.Module { - case stdModule: -- moduleMap[stdFileName] = true +- moduleMap[osv.GoStdModulePath] = true - case cmdModule: -- moduleMap[toolchainFileName] = true +- moduleMap[osv.GoCmdModulePath] = true - default: - moduleMap[m.Module] = true - } -- entry.Affected = append(entry.Affected, generateAffected(m, url)) +- entry.Affected = append(entry.Affected, toAffected(m)) - } - for _, ref := range r.References { - entry.References = append(entry.References, osv.Reference{ -- Type: string(ref.Type), +- Type: ref.Type, - URL: ref.URL, - }) - } -- -- var modulePaths []string -- for module := range moduleMap { -- modulePaths = append(modulePaths, module) -- } -- // TODO: handle missing fields - Aliases -- -- return entry, modulePaths +- return entry -} - --func generateAffectedRanges(versions []VersionRange) osv.Affects { -- a := osv.AffectsRange{Type: osv.TypeSemver} +-func AffectedRanges(versions []VersionRange) []osv.Range { +- a := osv.Range{Type: osv.RangeTypeSemver} - if len(versions) == 0 || versions[0].Introduced == "" { - a.Events = append(a.Events, osv.RangeEvent{Introduced: "0"}) - } @@ -126028,15 +141413,15 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v - a.Events = append(a.Events, osv.RangeEvent{Fixed: v.Fixed.Canonical()}) - } - } -- return osv.Affects{a} +- return []osv.Range{a} -} - --func generateImports(m *Module) (imps []osv.EcosystemSpecificImport) { -- for _, p := range m.Packages { +-func toOSVPackages(pkgs []*Package) (imps []osv.Package) { +- for _, p := range pkgs { - syms := append([]string{}, p.Symbols...) - syms = append(syms, p.DerivedSymbols...) - sort.Strings(syms) -- imps = append(imps, osv.EcosystemSpecificImport{ +- imps = append(imps, osv.Package{ - Path: p.Package, - GOOS: p.GOOS, - GOARCH: p.GOARCH, @@ -126045,30 +141430,30 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db.go b/gopls/internal/vulncheck/v - } - return imps -} --func generateAffected(m *Module, url string) osv.Affected { +- +-func toAffected(m *Module) osv.Affected { - name := m.Module - switch name { - case stdModule: -- name = "stdlib" +- name = osv.GoStdModulePath - case cmdModule: -- name = "toolchain" +- name = osv.GoCmdModulePath - } - return osv.Affected{ -- Package: osv.Package{ -- Name: name, +- Module: osv.Module{ +- Path: name, - Ecosystem: osv.GoEcosystem, - }, -- Ranges: generateAffectedRanges(m.Versions), -- DatabaseSpecific: osv.DatabaseSpecific{URL: url}, +- Ranges: AffectedRanges(m.Versions), - EcosystemSpecific: osv.EcosystemSpecific{ -- Imports: generateImports(m), +- Packages: toOSVPackages(m.Packages), - }, - } -} diff -urN a/gopls/internal/vulncheck/vulntest/db_test.go b/gopls/internal/vulncheck/vulntest/db_test.go --- a/gopls/internal/vulncheck/vulntest/db_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/vulncheck/vulntest/db_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,61 +0,0 @@ ++++ b/gopls/internal/vulncheck/vulntest/db_test.go 1970-01-01 08:00:00 +@@ -1,79 +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. @@ -126081,59 +141466,77 @@ diff -urN a/gopls/internal/vulncheck/vulntest/db_test.go b/gopls/internal/vulnch -import ( - "context" - "encoding/json" +- "flag" +- "os" +- "path/filepath" - "testing" +- "time" +- +- "github.com/google/go-cmp/cmp" +- "golang.org/x/tools/gopls/internal/span" +- "golang.org/x/tools/gopls/internal/vulncheck/osv" -) - +-var update = flag.Bool("update", false, "update golden files in testdata/") +- -func TestNewDatabase(t *testing.T) { - ctx := context.Background() -- in := []byte(` ---- GO-2020-0001.yaml -- --modules: -- - module: github.com/gin-gonic/gin -- versions: -- - fixed: 1.6.0 -- packages: -- - package: github.com/gin-gonic/gin -- symbols: -- - defaultLogFormatter --description: | -- Something. --published: 2021-04-14T20:04:52Z --references: -- - fix: https://github.com/gin-gonic/gin/pull/2237 --`) - -- db, err := NewDatabase(ctx, in) +- in, err := os.ReadFile("testdata/report.yaml") - if err != nil { - t.Fatal(err) - } -- defer db.Clean() +- in = append([]byte("-- GO-2020-0001.yaml --\n"), in...) - -- cli, err := NewClient(db) +- db, err := NewDatabase(ctx, in) - if err != nil { - t.Fatal(err) - } -- got, err := cli.GetByID(ctx, "GO-2020-0001") +- defer db.Clean() +- dbpath := span.URIFromURI(db.URI()).Filename() +- +- // The generated JSON file will be in DB/GO-2022-0001.json. +- got := readOSVEntry(t, filepath.Join(dbpath, "GO-2020-0001.json")) +- got.Modified = time.Time{} +- +- if *update { +- updateTestData(t, got, "testdata/GO-2020-0001.json") +- } +- +- want := readOSVEntry(t, "testdata/GO-2020-0001.json") +- want.Modified = time.Time{} +- if diff := cmp.Diff(want, got); diff != "" { +- t.Errorf("mismatch (-want +got):\n%s", diff) +- } +-} +- +-func updateTestData(t *testing.T, got *osv.Entry, fname string) { +- content, err := json.MarshalIndent(got, "", "\t") - if err != nil { - t.Fatal(err) - } -- if got.ID != "GO-2020-0001" { -- m, _ := json.Marshal(got) -- t.Errorf("got %s\nwant GO-2020-0001 entry", m) +- if err := os.WriteFile(fname, content, 0666); err != nil { +- t.Fatal(err) - } -- gotAll, err := cli.GetByModule(ctx, "github.com/gin-gonic/gin") +- t.Logf("updated %v", fname) +-} +- +-func readOSVEntry(t *testing.T, filename string) *osv.Entry { +- t.Helper() +- content, err := os.ReadFile(filename) - if err != nil { - t.Fatal(err) - } -- if len(gotAll) != 1 || gotAll[0].ID != "GO-2020-0001" { -- m, _ := json.Marshal(got) -- t.Errorf("got %s\nwant GO-2020-0001 entry", m) +- var entry osv.Entry +- if err := json.Unmarshal(content, &entry); err != nil { +- t.Fatal(err) - } +- return &entry -} diff -urN a/gopls/internal/vulncheck/vulntest/report.go b/gopls/internal/vulncheck/vulntest/report.go --- a/gopls/internal/vulncheck/vulntest/report.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/vulncheck/vulntest/report.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,176 +0,0 @@ ++++ b/gopls/internal/vulncheck/vulntest/report.go 1970-01-01 08:00:00 +@@ -1,179 +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. @@ -126151,6 +141554,7 @@ diff -urN a/gopls/internal/vulncheck/vulntest/report.go b/gopls/internal/vulnche - "time" - - "golang.org/x/mod/semver" +- "golang.org/x/tools/gopls/internal/vulncheck/osv" - "gopkg.in/yaml.v3" -) - @@ -126172,10 +141576,15 @@ diff -urN a/gopls/internal/vulncheck/vulntest/report.go b/gopls/internal/vulnche -} - -// Report represents a vulnerability report in the vulndb. --// Remember to update doc/format.md when this structure changes. +-// See https://go.googlesource.com/vulndb/+/refs/heads/master/doc/format.md -type Report struct { +- ID string `yaml:",omitempty"` +- - Modules []*Module `yaml:",omitempty"` - +- // Summary is a short phrase describing the vulnerability. +- Summary string `yaml:",omitempty"` +- - // Description is the CVE description from an existing CVE. If we are - // assigning a CVE ID ourselves, use CVEMetadata.Description instead. - Description string `yaml:",omitempty"` @@ -126289,10 +141698,7 @@ diff -urN a/gopls/internal/vulncheck/vulntest/report.go b/gopls/internal/vulnche -// -// For ease of typing, References are represented in the YAML as a -// single-element mapping of type to URL. --type Reference struct { -- Type ReferenceType `json:"type,omitempty"` -- URL string `json:"url,omitempty"` --} +-type Reference osv.Reference - -func (r *Reference) MarshalYAML() (interface{}, error) { - return map[string]string{ @@ -126306,14 +141712,14 @@ diff -urN a/gopls/internal/vulncheck/vulntest/report.go b/gopls/internal/vulnche - fmt.Sprintf("line %d: report.Reference must contain a mapping with one value", n.Line), - }} - } -- r.Type = ReferenceType(strings.ToUpper(n.Content[0].Value)) +- r.Type = osv.ReferenceType(strings.ToUpper(n.Content[0].Value)) - r.URL = n.Content[1].Value - return nil -} diff -urN a/gopls/internal/vulncheck/vulntest/report_test.go b/gopls/internal/vulncheck/vulntest/report_test.go --- a/gopls/internal/vulncheck/vulntest/report_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/vulncheck/vulntest/report_test.go 1970-01-01 00:00:00.000000000 +0000 -@@ -1,52 +0,0 @@ ++++ b/gopls/internal/vulncheck/vulntest/report_test.go 1970-01-01 08:00:00 +@@ -1,51 +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. @@ -126326,7 +141732,6 @@ diff -urN a/gopls/internal/vulncheck/vulntest/report_test.go b/gopls/internal/vu -import ( - "bytes" - "io" -- "io/ioutil" - "os" - "path/filepath" - "testing" @@ -126335,7 +141740,7 @@ diff -urN a/gopls/internal/vulncheck/vulntest/report_test.go b/gopls/internal/vu -) - -func readAll(t *testing.T, filename string) io.Reader { -- d, err := ioutil.ReadFile(filename) +- d, err := os.ReadFile(filename) - if err != nil { - t.Fatal(err) - } @@ -126368,7 +141773,7 @@ diff -urN a/gopls/internal/vulncheck/vulntest/report_test.go b/gopls/internal/vu -} diff -urN a/gopls/internal/vulncheck/vulntest/stdlib.go b/gopls/internal/vulncheck/vulntest/stdlib.go --- a/gopls/internal/vulncheck/vulntest/stdlib.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/vulncheck/vulntest/stdlib.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/vulncheck/vulntest/stdlib.go 1970-01-01 08:00:00 @@ -1,26 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -126398,7 +141803,7 @@ diff -urN a/gopls/internal/vulncheck/vulntest/stdlib.go b/gopls/internal/vulnche -} diff -urN a/gopls/internal/vulncheck/vulntest/stdlib_test.go b/gopls/internal/vulncheck/vulntest/stdlib_test.go --- a/gopls/internal/vulncheck/vulntest/stdlib_test.go 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/vulncheck/vulntest/stdlib_test.go 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/vulncheck/vulntest/stdlib_test.go 1970-01-01 08:00:00 @@ -1,27 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style @@ -126427,9 +141832,64 @@ diff -urN a/gopls/internal/vulncheck/vulntest/stdlib_test.go b/gopls/internal/vu - } - } -} +diff -urN a/gopls/internal/vulncheck/vulntest/testdata/GO-2020-0001.json b/gopls/internal/vulncheck/vulntest/testdata/GO-2020-0001.json +--- a/gopls/internal/vulncheck/vulntest/testdata/GO-2020-0001.json 2000-01-01 00:00:00.000000000 -0000 ++++ b/gopls/internal/vulncheck/vulntest/testdata/GO-2020-0001.json 1970-01-01 08:00:00 +@@ -1,50 +0,0 @@ +-{ +- "id": "GO-2020-0001", +- "modified": "0001-01-01T00:00:00Z", +- "published": "0001-01-01T00:00:00Z", +- "details": "The default Formatter for the Logger middleware (LoggerConfig.Formatter),\nwhich is included in the Default engine, allows attackers to inject arbitrary\nlog entries by manipulating the request path.\n", +- "affected": [ +- { +- "package": { +- "name": "github.com/gin-gonic/gin", +- "ecosystem": "Go" +- }, +- "ranges": [ +- { +- "type": "SEMVER", +- "events": [ +- { +- "introduced": "0" +- }, +- { +- "fixed": "1.6.0" +- } +- ] +- } +- ], +- "ecosystem_specific": { +- "imports": [ +- { +- "path": "github.com/gin-gonic/gin", +- "symbols": [ +- "defaultLogFormatter" +- ] +- } +- ] +- } +- } +- ], +- "references": [ +- { +- "type": "FIX", +- "url": "https://github.com/gin-gonic/gin/pull/1234" +- }, +- { +- "type": "FIX", +- "url": "https://github.com/gin-gonic/gin/commit/abcdefg" +- } +- ], +- "database_specific": { +- "url": "https://pkg.go.dev/vuln/GO-2020-0001" +- } +-} +\ No newline at end of file diff -urN a/gopls/internal/vulncheck/vulntest/testdata/report.yaml b/gopls/internal/vulncheck/vulntest/testdata/report.yaml --- a/gopls/internal/vulncheck/vulntest/testdata/report.yaml 2000-01-01 00:00:00.000000000 -0000 -+++ b/gopls/internal/vulncheck/vulntest/testdata/report.yaml 1970-01-01 00:00:00.000000000 +0000 ++++ b/gopls/internal/vulncheck/vulntest/testdata/report.yaml 1970-01-01 08:00:00 @@ -1,15 +0,0 @@ -modules: - - module: github.com/gin-gonic/gin @@ -126448,8 +141908,8 @@ diff -urN a/gopls/internal/vulncheck/vulntest/testdata/report.yaml b/gopls/inter - - fix: https://github.com/gin-gonic/gin/commit/abcdefg 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 00:00:00.000000000 +0000 -@@ -1,33 +0,0 @@ ++++ b/gopls/main.go 1970-01-01 08:00:00 +@@ -1,30 +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. @@ -126467,161 +141927,23 @@ diff -urN a/gopls/main.go b/gopls/main.go - -import ( - "context" -- "golang.org/x/tools/internal/analysisinternal" - "os" - - "golang.org/x/tools/gopls/internal/hooks" - "golang.org/x/tools/gopls/internal/lsp/cmd" +- "golang.org/x/tools/gopls/internal/telemetry" - "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 +- telemetry.Start() - ctx := context.Background() - tool.Main(ctx, cmd.New("gopls", "", nil, hooks.Options), os.Args[1:]) -} -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 00:00:00.000000000 +0000 -@@ -1,131 +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(rfindley): 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. -- --* [VS Code](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) --* [Lapce](https://github.com/lapce-community/lapce-go) -- --If you use `gopls` with an editor that is not on this list, please send us a CL --[updating 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`, run the following --command: -- --```sh --go install golang.org/x/tools/gopls@latest --``` -- --Learn more in the --[advanced installation instructions](doc/advanced.md#installing-unreleased-versions). -- --Learn more about gopls releases in the [release policy](doc/releases.md). -- --## Setting up your workspace -- --`gopls` supports both Go module, multi-module and GOPATH modes. See the --[workspace documentation](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. -- --## Support Policy -- --Gopls is maintained by engineers on the --[Go tools team](https://github.com/orgs/golang/teams/tools-team/members), --who actively monitor the --[Go](https://github.com/golang/go/issues?q=is%3Aissue+is%3Aopen+label%3Agopls) --and --[VS Code Go](https://github.com/golang/vscode-go/issues) issue trackers. -- --### Supported Go versions -- --`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](https://go.dev/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. -- --In the context of this discussion, gopls "supports" a Go version if it supports --being built with that Go version as well as integrating with the `go` command --of that Go version. -- --The following table shows the final gopls version that supports a given Go --version. Go releases more recent than any in the table can be used with any --version of gopls. -- --| Go Version | Final gopls version with support (without warnings) | --| ----------- | --------------------------------------------------- | --| Go 1.12 | [gopls@v0.7.5](https://github.com/golang/tools/releases/tag/gopls%2Fv0.7.5) | --| Go 1.15 | [gopls@v0.9.5](https://github.com/golang/tools/releases/tag/gopls%2Fv0.9.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. -- --### Supported build systems -- --`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 may be made to work with an appropriately configured --`go/packages` driver. See --[bazelbuild/rules_go#512](https://github.com/bazelbuild/rules_go/issues/512) --for more information. --You can follow [these instructions](https://github.com/bazelbuild/rules_go/wiki/Editor-setup) --to configure your `gopls` to work with Bazel. -- --### Troubleshooting -- --If you are having issues with `gopls`, please follow the steps described in the --[troubleshooting guide](doc/troubleshooting.md). -- --## 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/ 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 00:00:00.000000000 +0000 -@@ -1,156 +0,0 @@ ++++ b/gopls/release/release.go 1970-01-01 08:00:00 +@@ -1,155 +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. @@ -126639,7 +141961,6 @@ diff -urN a/gopls/release/release.go b/gopls/release/release.go - "flag" - "fmt" - "go/types" -- "io/ioutil" - "log" - "os" - "path/filepath" @@ -126733,7 +142054,7 @@ diff -urN a/gopls/release/release.go b/gopls/release/release.go - -func validateGoModFile(goplsDir string) error { - filename := filepath.Join(goplsDir, "go.mod") -- data, err := ioutil.ReadFile(filename) +- data, err := os.ReadFile(filename) - if err != nil { - return err - } @@ -126780,15 +142101,15 @@ diff -urN a/gopls/release/release.go b/gopls/release/release.go -} 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 00:00:00.000000000 +0000 -@@ -1,141 +0,0 @@ ++++ b/gopls/test/debug/debug_test.go 1970-01-01 08:00:00 +@@ -1,153 +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 +-// Provide 'static type checking' of the templates. This guards against changes in various -// gopls datastructures causing template execution to fail. The checking is done by -// the github.com/jba/templatecheck package. Before that is run, the test checks that -// its list of templates and their arguments corresponds to the arguments in @@ -126797,6 +142118,7 @@ diff -urN a/gopls/test/debug/debug_test.go b/gopls/test/debug/debug_test.go -import ( - "go/ast" - "html/template" +- "os" - "runtime" - "sort" - "strings" @@ -126806,33 +142128,42 @@ diff -urN a/gopls/test/debug/debug_test.go b/gopls/test/debug/debug_test.go - "golang.org/x/tools/go/packages" - "golang.org/x/tools/gopls/internal/lsp/cache" - "golang.org/x/tools/gopls/internal/lsp/debug" +- "golang.org/x/tools/internal/testenv" -) - -var templates = map[string]struct { - tmpl *template.Template - data interface{} // a value of the needed type -}{ -- "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": {debug.FileTmpl, &cache.Overlay{}}, -- "InfoTmpl": {debug.InfoTmpl, "something"}, -- "MemoryTmpl": {debug.MemoryTmpl, runtime.MemStats{}}, +- "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": {debug.FileTmpl, &cache.Overlay{}}, +- "InfoTmpl": {debug.InfoTmpl, "something"}, +- "MemoryTmpl": {debug.MemoryTmpl, runtime.MemStats{}}, +- "AnalysisTmpl": {debug.AnalysisTmpl, new(debug.State).Analysis()}, -} - -func TestTemplates(t *testing.T) { -- if runtime.GOOS == "android" { -- t.Skip("this test is not supported for Android") -- } +- testenv.NeedsGoPackages(t) +- testenv.NeedsLocalXTools(t) +- - cfg := &packages.Config{ -- Mode: packages.NeedTypesInfo | packages.LoadAllSyntax, // figure out what's necessary PJW +- Mode: packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo, - } +- cfg.Env = os.Environ() +- cfg.Env = append(cfg.Env, +- "GOPACKAGESDRIVER=off", +- "GOWORK=off", // necessary for -mod=mod below +- "GOFLAGS=-mod=mod", +- ) +- - pkgs, err := packages.Load(cfg, "golang.org/x/tools/gopls/internal/lsp/debug") - if err != nil { - t.Fatal(err) @@ -126891,7 +142222,9 @@ diff -urN a/gopls/test/debug/debug_test.go b/gopls/test/debug/debug_test.go - // 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) +- continue - } +- t.Logf("%s ok", k) - } -} - @@ -126925,7 +142258,7 @@ diff -urN a/gopls/test/debug/debug_test.go b/gopls/test/debug/debug_test.go -} 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 00:00:00.000000000 +0000 ++++ b/gopls/test/json_test.go 1970-01-01 08:00:00 @@ -1,140 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style diff --git a/third_party/org_golang_x_tools-gazelle.patch b/third_party/org_golang_x_tools-gazelle.patch index f3a1f2379e..33d7c10a5b 100644 --- a/third_party/org_golang_x_tools-gazelle.patch +++ b/third_party/org_golang_x_tools-gazelle.patch @@ -1,5 +1,5 @@ diff -urN b/benchmark/parse/BUILD.bazel c/benchmark/parse/BUILD.bazel ---- b/benchmark/parse/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/benchmark/parse/BUILD.bazel 1970-01-01 08:00:00 +++ c/benchmark/parse/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -22,26 +22,8 @@ diff -urN b/benchmark/parse/BUILD.bazel c/benchmark/parse/BUILD.bazel + srcs = ["parse_test.go"], + embed = [":parse"], +) -diff -urN b/blog/atom/BUILD.bazel c/blog/atom/BUILD.bazel ---- b/blog/atom/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/blog/atom/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_library") -+ -+go_library( -+ name = "atom", -+ srcs = ["atom.go"], -+ importpath = "golang.org/x/tools/blog/atom", -+ visibility = ["//visibility:public"], -+) -+ -+alias( -+ name = "go_default_library", -+ actual = ":atom", -+ visibility = ["//visibility:public"], -+) diff -urN b/blog/BUILD.bazel c/blog/BUILD.bazel ---- b/blog/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/blog/BUILD.bazel 1970-01-01 08:00:00 +++ c/blog/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -68,8 +50,26 @@ diff -urN b/blog/BUILD.bazel c/blog/BUILD.bazel + srcs = ["blog_test.go"], + embed = [":blog"], +) +diff -urN b/blog/atom/BUILD.bazel c/blog/atom/BUILD.bazel +--- b/blog/atom/BUILD.bazel 1970-01-01 08:00:00 ++++ c/blog/atom/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,14 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "atom", ++ srcs = ["atom.go"], ++ importpath = "golang.org/x/tools/blog/atom", ++ visibility = ["//visibility:public"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":atom", ++ visibility = ["//visibility:public"], ++) diff -urN b/cmd/auth/authtest/BUILD.bazel c/cmd/auth/authtest/BUILD.bazel ---- b/cmd/auth/authtest/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/auth/authtest/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/auth/authtest/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -88,7 +88,7 @@ diff -urN b/cmd/auth/authtest/BUILD.bazel c/cmd/auth/authtest/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/auth/cookieauth/BUILD.bazel c/cmd/auth/cookieauth/BUILD.bazel ---- b/cmd/auth/cookieauth/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/auth/cookieauth/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/auth/cookieauth/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -106,7 +106,7 @@ diff -urN b/cmd/auth/cookieauth/BUILD.bazel c/cmd/auth/cookieauth/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/auth/gitauth/BUILD.bazel c/cmd/auth/gitauth/BUILD.bazel ---- b/cmd/auth/gitauth/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/auth/gitauth/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/auth/gitauth/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -125,7 +125,7 @@ diff -urN b/cmd/auth/gitauth/BUILD.bazel c/cmd/auth/gitauth/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/auth/netrcauth/BUILD.bazel c/cmd/auth/netrcauth/BUILD.bazel ---- b/cmd/auth/netrcauth/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/auth/netrcauth/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/auth/netrcauth/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -143,7 +143,7 @@ diff -urN b/cmd/auth/netrcauth/BUILD.bazel c/cmd/auth/netrcauth/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/benchcmp/BUILD.bazel c/cmd/benchcmp/BUILD.bazel ---- b/cmd/benchcmp/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/benchcmp/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/benchcmp/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -175,10 +175,47 @@ diff -urN b/cmd/benchcmp/BUILD.bazel c/cmd/benchcmp/BUILD.bazel + embed = [":benchcmp_lib"], + deps = ["//benchmark/parse"], +) +diff -urN b/cmd/bisect/BUILD.bazel c/cmd/bisect/BUILD.bazel +--- b/cmd/bisect/BUILD.bazel 1970-01-01 08:00:00 ++++ c/cmd/bisect/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,33 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") ++ ++go_library( ++ name = "bisect_lib", ++ srcs = [ ++ "go119.go", ++ "go120.go", ++ "main.go", ++ "rand.go", ++ ], ++ importpath = "golang.org/x/tools/cmd/bisect", ++ visibility = ["//visibility:private"], ++ deps = ["//internal/bisect"], ++) ++ ++go_binary( ++ name = "bisect", ++ embed = [":bisect_lib"], ++ visibility = ["//visibility:public"], ++) ++ ++go_test( ++ name = "bisect_test", ++ srcs = ["main_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), ++ embed = [":bisect_lib"], ++ deps = [ ++ "//internal/bisect", ++ "//internal/compat", ++ "//internal/diffp", ++ "//txtar", ++ ], ++) diff -urN b/cmd/bundle/BUILD.bazel c/cmd/bundle/BUILD.bazel ---- b/cmd/bundle/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/bundle/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/bundle/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,22 @@ +@@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +go_library( @@ -198,11 +235,12 @@ diff -urN b/cmd/bundle/BUILD.bazel c/cmd/bundle/BUILD.bazel +go_test( + name = "bundle_test", + srcs = ["main_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + embed = [":bundle_lib"], + deps = ["//go/packages/packagestest"], +) diff -urN b/cmd/bundle/testdata/src/domain.name/importdecl/BUILD.bazel c/cmd/bundle/testdata/src/domain.name/importdecl/BUILD.bazel ---- b/cmd/bundle/testdata/src/domain.name/importdecl/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/bundle/testdata/src/domain.name/importdecl/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/bundle/testdata/src/domain.name/importdecl/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -220,7 +258,7 @@ diff -urN b/cmd/bundle/testdata/src/domain.name/importdecl/BUILD.bazel c/cmd/bun + visibility = ["//visibility:public"], +) diff -urN b/cmd/bundle/testdata/src/initial/BUILD.bazel c/cmd/bundle/testdata/src/initial/BUILD.bazel ---- b/cmd/bundle/testdata/src/initial/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/bundle/testdata/src/initial/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/bundle/testdata/src/initial/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -242,7 +280,7 @@ diff -urN b/cmd/bundle/testdata/src/initial/BUILD.bazel c/cmd/bundle/testdata/sr + visibility = ["//visibility:public"], +) diff -urN b/cmd/callgraph/BUILD.bazel c/cmd/callgraph/BUILD.bazel ---- b/cmd/callgraph/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/callgraph/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/callgraph/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,74 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -260,7 +298,6 @@ diff -urN b/cmd/callgraph/BUILD.bazel c/cmd/callgraph/BUILD.bazel + "//go/callgraph/static", + "//go/callgraph/vta", + "//go/packages", -+ "//go/pointer", + "//go/ssa", + "//go/ssa/ssautil", + ], @@ -275,6 +312,7 @@ diff -urN b/cmd/callgraph/BUILD.bazel c/cmd/callgraph/BUILD.bazel +go_test( + name = "callgraph_test", + srcs = ["main_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + embed = [":callgraph_lib"], + deps = select({ + "@io_bazel_rules_go//go/platform:aix": [ @@ -320,7 +358,7 @@ diff -urN b/cmd/callgraph/BUILD.bazel c/cmd/callgraph/BUILD.bazel + }), +) diff -urN b/cmd/callgraph/testdata/src/pkg/BUILD.bazel c/cmd/callgraph/testdata/src/pkg/BUILD.bazel ---- b/cmd/callgraph/testdata/src/pkg/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/callgraph/testdata/src/pkg/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/callgraph/testdata/src/pkg/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -344,7 +382,7 @@ diff -urN b/cmd/callgraph/testdata/src/pkg/BUILD.bazel c/cmd/callgraph/testdata/ + embed = [":pkg_lib"], +) diff -urN b/cmd/compilebench/BUILD.bazel c/cmd/compilebench/BUILD.bazel ---- b/cmd/compilebench/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/compilebench/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/compilebench/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -363,14 +401,18 @@ diff -urN b/cmd/compilebench/BUILD.bazel c/cmd/compilebench/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/digraph/BUILD.bazel c/cmd/digraph/BUILD.bazel ---- b/cmd/digraph/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/digraph/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/digraph/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,20 @@ +@@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +go_library( + name = "digraph_lib", -+ srcs = ["digraph.go"], ++ srcs = [ ++ "digraph.go", ++ "doc.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/cmd/digraph", + visibility = ["//visibility:private"], +) @@ -387,7 +429,7 @@ diff -urN b/cmd/digraph/BUILD.bazel c/cmd/digraph/BUILD.bazel + embed = [":digraph_lib"], +) diff -urN b/cmd/eg/BUILD.bazel c/cmd/eg/BUILD.bazel ---- b/cmd/eg/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/eg/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/eg/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -410,9 +452,9 @@ diff -urN b/cmd/eg/BUILD.bazel c/cmd/eg/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/file2fuzz/BUILD.bazel c/cmd/file2fuzz/BUILD.bazel ---- b/cmd/file2fuzz/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/file2fuzz/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/file2fuzz/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,20 @@ +@@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +go_library( @@ -432,11 +474,12 @@ diff -urN b/cmd/file2fuzz/BUILD.bazel c/cmd/file2fuzz/BUILD.bazel + name = "file2fuzz_test", + srcs = ["main_test.go"], + embed = [":file2fuzz_lib"], ++ deps = ["//internal/testenv"], +) diff -urN b/cmd/fiximports/BUILD.bazel c/cmd/fiximports/BUILD.bazel ---- b/cmd/fiximports/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/fiximports/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/fiximports/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,63 @@ +@@ -0,0 +1,64 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +go_library( @@ -456,6 +499,7 @@ diff -urN b/cmd/fiximports/BUILD.bazel c/cmd/fiximports/BUILD.bazel +go_test( + name = "fiximports_test", + srcs = ["main_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + embed = [":fiximports_lib"], + deps = select({ + "@io_bazel_rules_go//go/platform:aix": [ @@ -501,7 +545,7 @@ diff -urN b/cmd/fiximports/BUILD.bazel c/cmd/fiximports/BUILD.bazel + }), +) diff -urN b/cmd/fiximports/testdata/src/fruit.io/banana/BUILD.bazel c/cmd/fiximports/testdata/src/fruit.io/banana/BUILD.bazel ---- b/cmd/fiximports/testdata/src/fruit.io/banana/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/fiximports/testdata/src/fruit.io/banana/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/fiximports/testdata/src/fruit.io/banana/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -519,7 +563,7 @@ diff -urN b/cmd/fiximports/testdata/src/fruit.io/banana/BUILD.bazel c/cmd/fiximp + visibility = ["//visibility:public"], +) diff -urN b/cmd/fiximports/testdata/src/fruit.io/orange/BUILD.bazel c/cmd/fiximports/testdata/src/fruit.io/orange/BUILD.bazel ---- b/cmd/fiximports/testdata/src/fruit.io/orange/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/fiximports/testdata/src/fruit.io/orange/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/fiximports/testdata/src/fruit.io/orange/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -537,7 +581,7 @@ diff -urN b/cmd/fiximports/testdata/src/fruit.io/orange/BUILD.bazel c/cmd/fiximp + visibility = ["//visibility:public"], +) diff -urN b/cmd/fiximports/testdata/src/fruit.io/pear/BUILD.bazel c/cmd/fiximports/testdata/src/fruit.io/pear/BUILD.bazel ---- b/cmd/fiximports/testdata/src/fruit.io/pear/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/fiximports/testdata/src/fruit.io/pear/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/fiximports/testdata/src/fruit.io/pear/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -555,7 +599,7 @@ diff -urN b/cmd/fiximports/testdata/src/fruit.io/pear/BUILD.bazel c/cmd/fiximpor + visibility = ["//visibility:public"], +) diff -urN b/cmd/fiximports/testdata/src/new.com/one/BUILD.bazel c/cmd/fiximports/testdata/src/new.com/one/BUILD.bazel ---- b/cmd/fiximports/testdata/src/new.com/one/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/fiximports/testdata/src/new.com/one/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/fiximports/testdata/src/new.com/one/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -573,7 +617,7 @@ diff -urN b/cmd/fiximports/testdata/src/new.com/one/BUILD.bazel c/cmd/fiximports + visibility = ["//visibility:public"], +) diff -urN b/cmd/fiximports/testdata/src/old.com/bad/BUILD.bazel c/cmd/fiximports/testdata/src/old.com/bad/BUILD.bazel ---- b/cmd/fiximports/testdata/src/old.com/bad/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/fiximports/testdata/src/old.com/bad/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/fiximports/testdata/src/old.com/bad/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -591,7 +635,7 @@ diff -urN b/cmd/fiximports/testdata/src/old.com/bad/BUILD.bazel c/cmd/fiximports + visibility = ["//visibility:public"], +) diff -urN b/cmd/fiximports/testdata/src/old.com/one/BUILD.bazel c/cmd/fiximports/testdata/src/old.com/one/BUILD.bazel ---- b/cmd/fiximports/testdata/src/old.com/one/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/fiximports/testdata/src/old.com/one/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/fiximports/testdata/src/old.com/one/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -609,7 +653,7 @@ diff -urN b/cmd/fiximports/testdata/src/old.com/one/BUILD.bazel c/cmd/fiximports + visibility = ["//visibility:public"], +) diff -urN b/cmd/fiximports/testdata/src/titanic.biz/bar/BUILD.bazel c/cmd/fiximports/testdata/src/titanic.biz/bar/BUILD.bazel ---- b/cmd/fiximports/testdata/src/titanic.biz/bar/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/fiximports/testdata/src/titanic.biz/bar/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/fiximports/testdata/src/titanic.biz/bar/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -627,7 +671,7 @@ diff -urN b/cmd/fiximports/testdata/src/titanic.biz/bar/BUILD.bazel c/cmd/fiximp + visibility = ["//visibility:public"], +) diff -urN b/cmd/fiximports/testdata/src/titanic.biz/foo/BUILD.bazel c/cmd/fiximports/testdata/src/titanic.biz/foo/BUILD.bazel ---- b/cmd/fiximports/testdata/src/titanic.biz/foo/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/fiximports/testdata/src/titanic.biz/foo/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/fiximports/testdata/src/titanic.biz/foo/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -645,7 +689,7 @@ diff -urN b/cmd/fiximports/testdata/src/titanic.biz/foo/BUILD.bazel c/cmd/fiximp + visibility = ["//visibility:public"], +) diff -urN b/cmd/getgo/BUILD.bazel c/cmd/getgo/BUILD.bazel ---- b/cmd/getgo/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/getgo/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/getgo/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,74 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -723,7 +767,7 @@ diff -urN b/cmd/getgo/BUILD.bazel c/cmd/getgo/BUILD.bazel + embed = [":getgo_lib"], +) diff -urN b/cmd/getgo/server/BUILD.bazel c/cmd/getgo/server/BUILD.bazel ---- b/cmd/getgo/server/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/getgo/server/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/getgo/server/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -741,7 +785,7 @@ diff -urN b/cmd/getgo/server/BUILD.bazel c/cmd/getgo/server/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/go-contrib-init/BUILD.bazel c/cmd/go-contrib-init/BUILD.bazel ---- b/cmd/go-contrib-init/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/go-contrib-init/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/go-contrib-init/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -766,7 +810,7 @@ diff -urN b/cmd/go-contrib-init/BUILD.bazel c/cmd/go-contrib-init/BUILD.bazel + embed = [":go-contrib-init_lib"], +) diff -urN b/cmd/godex/BUILD.bazel c/cmd/godex/BUILD.bazel ---- b/cmd/godex/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/godex/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/godex/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -794,7 +838,7 @@ diff -urN b/cmd/godex/BUILD.bazel c/cmd/godex/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/godoc/BUILD.bazel c/cmd/godoc/BUILD.bazel ---- b/cmd/godoc/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/godoc/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/godoc/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -839,7 +883,7 @@ diff -urN b/cmd/godoc/BUILD.bazel c/cmd/godoc/BUILD.bazel + ], +) diff -urN b/cmd/goimports/BUILD.bazel c/cmd/goimports/BUILD.bazel ---- b/cmd/goimports/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/goimports/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/goimports/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -866,7 +910,7 @@ diff -urN b/cmd/goimports/BUILD.bazel c/cmd/goimports/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/gomvpkg/BUILD.bazel c/cmd/gomvpkg/BUILD.bazel ---- b/cmd/gomvpkg/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/gomvpkg/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/gomvpkg/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -887,8 +931,42 @@ diff -urN b/cmd/gomvpkg/BUILD.bazel c/cmd/gomvpkg/BUILD.bazel + embed = [":gomvpkg_lib"], + visibility = ["//visibility:public"], +) +diff -urN b/cmd/gonew/BUILD.bazel c/cmd/gonew/BUILD.bazel +--- b/cmd/gonew/BUILD.bazel 1970-01-01 08:00:00 ++++ c/cmd/gonew/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,30 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") ++ ++go_library( ++ name = "gonew_lib", ++ srcs = ["main.go"], ++ importpath = "golang.org/x/tools/cmd/gonew", ++ visibility = ["//visibility:private"], ++ deps = [ ++ "//internal/edit", ++ "@org_golang_x_mod//modfile:go_default_library", ++ "@org_golang_x_mod//module:go_default_library", ++ ], ++) ++ ++go_binary( ++ name = "gonew", ++ embed = [":gonew_lib"], ++ visibility = ["//visibility:public"], ++) ++ ++go_test( ++ name = "gonew_test", ++ srcs = ["main_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), ++ embed = [":gonew_lib"], ++ deps = [ ++ "//internal/diffp", ++ "//txtar", ++ ], ++) diff -urN b/cmd/gorename/BUILD.bazel c/cmd/gorename/BUILD.bazel ---- b/cmd/gorename/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/gorename/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/gorename/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -916,7 +994,7 @@ diff -urN b/cmd/gorename/BUILD.bazel c/cmd/gorename/BUILD.bazel + deps = ["//internal/testenv"], +) diff -urN b/cmd/gotype/BUILD.bazel c/cmd/gotype/BUILD.bazel ---- b/cmd/gotype/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/gotype/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/gotype/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -938,7 +1016,7 @@ diff -urN b/cmd/gotype/BUILD.bazel c/cmd/gotype/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/goyacc/BUILD.bazel c/cmd/goyacc/BUILD.bazel ---- b/cmd/goyacc/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/goyacc/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/goyacc/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -959,7 +1037,7 @@ diff -urN b/cmd/goyacc/BUILD.bazel c/cmd/goyacc/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/goyacc/testdata/expr/BUILD.bazel c/cmd/goyacc/testdata/expr/BUILD.bazel ---- b/cmd/goyacc/testdata/expr/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/goyacc/testdata/expr/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/goyacc/testdata/expr/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -977,17 +1055,14 @@ diff -urN b/cmd/goyacc/testdata/expr/BUILD.bazel c/cmd/goyacc/testdata/expr/BUIL + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/BUILD.bazel c/cmd/guru/BUILD.bazel ---- b/cmd/guru/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,56 @@ +@@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +go_library( + name = "guru_lib", + srcs = [ -+ "callees.go", -+ "callers.go", -+ "callstack.go", + "definition.go", + "describe.go", + "freevars.go", @@ -996,12 +1071,9 @@ diff -urN b/cmd/guru/BUILD.bazel c/cmd/guru/BUILD.bazel + "isAlias18.go", + "isAlias19.go", + "main.go", -+ "peers.go", -+ "pointsto.go", + "pos.go", + "referrers.go", + "what.go", -+ "whicherrs.go", + ], + importpath = "golang.org/x/tools/cmd/guru", + visibility = ["//visibility:private"], @@ -1009,12 +1081,7 @@ diff -urN b/cmd/guru/BUILD.bazel c/cmd/guru/BUILD.bazel + "//cmd/guru/serial", + "//go/ast/astutil", + "//go/buildutil", -+ "//go/callgraph", -+ "//go/callgraph/static", + "//go/loader", -+ "//go/pointer", -+ "//go/ssa", -+ "//go/ssa/ssautil", + "//go/types/typeutil", + "//imports", + "//refactor/importgraph", @@ -1033,11 +1100,12 @@ diff -urN b/cmd/guru/BUILD.bazel c/cmd/guru/BUILD.bazel + "guru_test.go", + "unit_test.go", + ], ++ data = glob(["testdata/**"], allow_empty = True), + embed = [":guru_lib"], + deps = ["//internal/testenv"], +) diff -urN b/cmd/guru/serial/BUILD.bazel c/cmd/guru/serial/BUILD.bazel ---- b/cmd/guru/serial/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/serial/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/serial/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -1055,7 +1123,7 @@ diff -urN b/cmd/guru/serial/BUILD.bazel c/cmd/guru/serial/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/alias/BUILD.bazel c/cmd/guru/testdata/src/alias/BUILD.bazel ---- b/cmd/guru/testdata/src/alias/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/alias/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/alias/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -1072,44 +1140,8 @@ diff -urN b/cmd/guru/testdata/src/alias/BUILD.bazel c/cmd/guru/testdata/src/alia + actual = ":alias", + visibility = ["//visibility:public"], +) -diff -urN b/cmd/guru/testdata/src/calls/BUILD.bazel c/cmd/guru/testdata/src/calls/BUILD.bazel ---- b/cmd/guru/testdata/src/calls/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/cmd/guru/testdata/src/calls/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -+ -+go_library( -+ name = "calls_lib", -+ srcs = ["main.go"], -+ importpath = "golang.org/x/tools/cmd/guru/testdata/src/calls", -+ visibility = ["//visibility:private"], -+) -+ -+go_binary( -+ name = "calls", -+ embed = [":calls_lib"], -+ visibility = ["//visibility:public"], -+) -diff -urN b/cmd/guru/testdata/src/calls-json/BUILD.bazel c/cmd/guru/testdata/src/calls-json/BUILD.bazel ---- b/cmd/guru/testdata/src/calls-json/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/cmd/guru/testdata/src/calls-json/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -+ -+go_library( -+ name = "calls-json_lib", -+ srcs = ["main.go"], -+ importpath = "golang.org/x/tools/cmd/guru/testdata/src/calls-json", -+ visibility = ["//visibility:private"], -+) -+ -+go_binary( -+ name = "calls-json", -+ embed = [":calls-json_lib"], -+ visibility = ["//visibility:public"], -+) diff -urN b/cmd/guru/testdata/src/definition-json/BUILD.bazel c/cmd/guru/testdata/src/definition-json/BUILD.bazel ---- b/cmd/guru/testdata/src/definition-json/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/definition-json/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/definition-json/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -1130,7 +1162,7 @@ diff -urN b/cmd/guru/testdata/src/definition-json/BUILD.bazel c/cmd/guru/testdat + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/describe/BUILD.bazel c/cmd/guru/testdata/src/describe/BUILD.bazel ---- b/cmd/guru/testdata/src/describe/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/describe/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/describe/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -1148,7 +1180,7 @@ diff -urN b/cmd/guru/testdata/src/describe/BUILD.bazel c/cmd/guru/testdata/src/d + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/describe-json/BUILD.bazel c/cmd/guru/testdata/src/describe-json/BUILD.bazel ---- b/cmd/guru/testdata/src/describe-json/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/describe-json/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/describe-json/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -1166,7 +1198,7 @@ diff -urN b/cmd/guru/testdata/src/describe-json/BUILD.bazel c/cmd/guru/testdata/ + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/freevars/BUILD.bazel c/cmd/guru/testdata/src/freevars/BUILD.bazel ---- b/cmd/guru/testdata/src/freevars/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/freevars/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/freevars/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1184,7 +1216,7 @@ diff -urN b/cmd/guru/testdata/src/freevars/BUILD.bazel c/cmd/guru/testdata/src/f + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/implements/BUILD.bazel c/cmd/guru/testdata/src/implements/BUILD.bazel ---- b/cmd/guru/testdata/src/implements/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/implements/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/implements/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1202,7 +1234,7 @@ diff -urN b/cmd/guru/testdata/src/implements/BUILD.bazel c/cmd/guru/testdata/src + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/implements-json/BUILD.bazel c/cmd/guru/testdata/src/implements-json/BUILD.bazel ---- b/cmd/guru/testdata/src/implements-json/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/implements-json/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/implements-json/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1220,7 +1252,7 @@ diff -urN b/cmd/guru/testdata/src/implements-json/BUILD.bazel c/cmd/guru/testdat + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/implements-methods/BUILD.bazel c/cmd/guru/testdata/src/implements-methods/BUILD.bazel ---- b/cmd/guru/testdata/src/implements-methods/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/implements-methods/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/implements-methods/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1238,7 +1270,7 @@ diff -urN b/cmd/guru/testdata/src/implements-methods/BUILD.bazel c/cmd/guru/test + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/implements-methods-json/BUILD.bazel c/cmd/guru/testdata/src/implements-methods-json/BUILD.bazel ---- b/cmd/guru/testdata/src/implements-methods-json/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/implements-methods-json/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/implements-methods-json/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1256,7 +1288,7 @@ diff -urN b/cmd/guru/testdata/src/implements-methods-json/BUILD.bazel c/cmd/guru + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/imports/BUILD.bazel c/cmd/guru/testdata/src/imports/BUILD.bazel ---- b/cmd/guru/testdata/src/imports/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/imports/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/imports/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1274,7 +1306,7 @@ diff -urN b/cmd/guru/testdata/src/imports/BUILD.bazel c/cmd/guru/testdata/src/im + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/lib/BUILD.bazel c/cmd/guru/testdata/src/lib/BUILD.bazel ---- b/cmd/guru/testdata/src/lib/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/lib/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/lib/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -1292,7 +1324,7 @@ diff -urN b/cmd/guru/testdata/src/lib/BUILD.bazel c/cmd/guru/testdata/src/lib/BU + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/lib/sublib/BUILD.bazel c/cmd/guru/testdata/src/lib/sublib/BUILD.bazel ---- b/cmd/guru/testdata/src/lib/sublib/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/lib/sublib/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/lib/sublib/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -1310,7 +1342,7 @@ diff -urN b/cmd/guru/testdata/src/lib/sublib/BUILD.bazel c/cmd/guru/testdata/src + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/main/BUILD.bazel c/cmd/guru/testdata/src/main/BUILD.bazel ---- b/cmd/guru/testdata/src/main/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/main/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/main/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1327,80 +1359,8 @@ diff -urN b/cmd/guru/testdata/src/main/BUILD.bazel c/cmd/guru/testdata/src/main/ + embed = [":main_lib"], + visibility = ["//visibility:public"], +) -diff -urN b/cmd/guru/testdata/src/peers/BUILD.bazel c/cmd/guru/testdata/src/peers/BUILD.bazel ---- b/cmd/guru/testdata/src/peers/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/cmd/guru/testdata/src/peers/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -+ -+go_library( -+ name = "peers_lib", -+ srcs = ["main.go"], -+ importpath = "golang.org/x/tools/cmd/guru/testdata/src/peers", -+ visibility = ["//visibility:private"], -+) -+ -+go_binary( -+ name = "peers", -+ embed = [":peers_lib"], -+ visibility = ["//visibility:public"], -+) -diff -urN b/cmd/guru/testdata/src/peers-json/BUILD.bazel c/cmd/guru/testdata/src/peers-json/BUILD.bazel ---- b/cmd/guru/testdata/src/peers-json/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/cmd/guru/testdata/src/peers-json/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -+ -+go_library( -+ name = "peers-json_lib", -+ srcs = ["main.go"], -+ importpath = "golang.org/x/tools/cmd/guru/testdata/src/peers-json", -+ visibility = ["//visibility:private"], -+) -+ -+go_binary( -+ name = "peers-json", -+ embed = [":peers-json_lib"], -+ visibility = ["//visibility:public"], -+) -diff -urN b/cmd/guru/testdata/src/pointsto/BUILD.bazel c/cmd/guru/testdata/src/pointsto/BUILD.bazel ---- b/cmd/guru/testdata/src/pointsto/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/cmd/guru/testdata/src/pointsto/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -+ -+go_library( -+ name = "pointsto_lib", -+ srcs = ["main.go"], -+ importpath = "golang.org/x/tools/cmd/guru/testdata/src/pointsto", -+ visibility = ["//visibility:private"], -+) -+ -+go_binary( -+ name = "pointsto", -+ embed = [":pointsto_lib"], -+ visibility = ["//visibility:public"], -+) -diff -urN b/cmd/guru/testdata/src/pointsto-json/BUILD.bazel c/cmd/guru/testdata/src/pointsto-json/BUILD.bazel ---- b/cmd/guru/testdata/src/pointsto-json/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/cmd/guru/testdata/src/pointsto-json/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -+ -+go_library( -+ name = "pointsto-json_lib", -+ srcs = ["main.go"], -+ importpath = "golang.org/x/tools/cmd/guru/testdata/src/pointsto-json", -+ visibility = ["//visibility:private"], -+) -+ -+go_binary( -+ name = "pointsto-json", -+ embed = [":pointsto-json_lib"], -+ visibility = ["//visibility:public"], -+) diff -urN b/cmd/guru/testdata/src/referrers/BUILD.bazel c/cmd/guru/testdata/src/referrers/BUILD.bazel ---- b/cmd/guru/testdata/src/referrers/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/referrers/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/referrers/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -1427,7 +1387,7 @@ diff -urN b/cmd/guru/testdata/src/referrers/BUILD.bazel c/cmd/guru/testdata/src/ + embed = [":referrers_lib"], +) diff -urN b/cmd/guru/testdata/src/referrers-json/BUILD.bazel c/cmd/guru/testdata/src/referrers-json/BUILD.bazel ---- b/cmd/guru/testdata/src/referrers-json/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/referrers-json/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/referrers-json/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1444,44 +1404,8 @@ diff -urN b/cmd/guru/testdata/src/referrers-json/BUILD.bazel c/cmd/guru/testdata + embed = [":referrers-json_lib"], + visibility = ["//visibility:public"], +) -diff -urN b/cmd/guru/testdata/src/reflection/BUILD.bazel c/cmd/guru/testdata/src/reflection/BUILD.bazel ---- b/cmd/guru/testdata/src/reflection/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/cmd/guru/testdata/src/reflection/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -+ -+go_library( -+ name = "reflection_lib", -+ srcs = ["main.go"], -+ importpath = "golang.org/x/tools/cmd/guru/testdata/src/reflection", -+ visibility = ["//visibility:private"], -+) -+ -+go_binary( -+ name = "reflection", -+ embed = [":reflection_lib"], -+ visibility = ["//visibility:public"], -+) -diff -urN b/cmd/guru/testdata/src/softerrs/BUILD.bazel c/cmd/guru/testdata/src/softerrs/BUILD.bazel ---- b/cmd/guru/testdata/src/softerrs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/cmd/guru/testdata/src/softerrs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -+ -+go_library( -+ name = "softerrs_lib", -+ srcs = ["main.go"], -+ importpath = "golang.org/x/tools/cmd/guru/testdata/src/softerrs", -+ visibility = ["//visibility:private"], -+) -+ -+go_binary( -+ name = "softerrs", -+ embed = [":softerrs_lib"], -+ visibility = ["//visibility:public"], -+) diff -urN b/cmd/guru/testdata/src/what/BUILD.bazel c/cmd/guru/testdata/src/what/BUILD.bazel ---- b/cmd/guru/testdata/src/what/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/what/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/what/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1499,7 +1423,7 @@ diff -urN b/cmd/guru/testdata/src/what/BUILD.bazel c/cmd/guru/testdata/src/what/ + visibility = ["//visibility:public"], +) diff -urN b/cmd/guru/testdata/src/what-json/BUILD.bazel c/cmd/guru/testdata/src/what-json/BUILD.bazel ---- b/cmd/guru/testdata/src/what-json/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/guru/testdata/src/what-json/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/guru/testdata/src/what-json/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1516,26 +1440,8 @@ diff -urN b/cmd/guru/testdata/src/what-json/BUILD.bazel c/cmd/guru/testdata/src/ + embed = [":what-json_lib"], + visibility = ["//visibility:public"], +) -diff -urN b/cmd/guru/testdata/src/whicherrs/BUILD.bazel c/cmd/guru/testdata/src/whicherrs/BUILD.bazel ---- b/cmd/guru/testdata/src/whicherrs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/cmd/guru/testdata/src/whicherrs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -+ -+go_library( -+ name = "whicherrs_lib", -+ srcs = ["main.go"], -+ importpath = "golang.org/x/tools/cmd/guru/testdata/src/whicherrs", -+ visibility = ["//visibility:private"], -+) -+ -+go_binary( -+ name = "whicherrs", -+ embed = [":whicherrs_lib"], -+ visibility = ["//visibility:public"], -+) diff -urN b/cmd/html2article/BUILD.bazel c/cmd/html2article/BUILD.bazel ---- b/cmd/html2article/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/html2article/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/html2article/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1557,9 +1463,9 @@ diff -urN b/cmd/html2article/BUILD.bazel c/cmd/html2article/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/present/BUILD.bazel c/cmd/present/BUILD.bazel ---- b/cmd/present/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/present/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/present/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,40 @@ +@@ -0,0 +1,42 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( @@ -1576,8 +1482,11 @@ diff -urN b/cmd/present/BUILD.bazel c/cmd/present/BUILD.bazel + "static/dir.js", + "static/favicon.ico", + "static/jquery-ui.js", ++ "static/jquery.js", + "static/notes.css", + "static/notes.js", ++ "static/play.js", ++ "static/playground.js", + "static/slides.js", + "static/styles.css", + "templates/action.tmpl", @@ -1588,7 +1497,6 @@ diff -urN b/cmd/present/BUILD.bazel c/cmd/present/BUILD.bazel + importpath = "golang.org/x/tools/cmd/present", + visibility = ["//visibility:private"], + deps = [ -+ "//godoc/static", + "//playground", + "//playground/socket", + "//present", @@ -1601,7 +1509,7 @@ diff -urN b/cmd/present/BUILD.bazel c/cmd/present/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/present2md/BUILD.bazel c/cmd/present2md/BUILD.bazel ---- b/cmd/present2md/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/present2md/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/present2md/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1620,7 +1528,7 @@ diff -urN b/cmd/present2md/BUILD.bazel c/cmd/present2md/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/signature-fuzzer/fuzz-driver/BUILD.bazel c/cmd/signature-fuzzer/fuzz-driver/BUILD.bazel ---- b/cmd/signature-fuzzer/fuzz-driver/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/signature-fuzzer/fuzz-driver/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/signature-fuzzer/fuzz-driver/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -1646,7 +1554,7 @@ diff -urN b/cmd/signature-fuzzer/fuzz-driver/BUILD.bazel c/cmd/signature-fuzzer/ + deps = ["//internal/testenv"], +) diff -urN b/cmd/signature-fuzzer/fuzz-runner/BUILD.bazel c/cmd/signature-fuzzer/fuzz-runner/BUILD.bazel ---- b/cmd/signature-fuzzer/fuzz-runner/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/signature-fuzzer/fuzz-runner/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/signature-fuzzer/fuzz-runner/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -1672,7 +1580,7 @@ diff -urN b/cmd/signature-fuzzer/fuzz-runner/BUILD.bazel c/cmd/signature-fuzzer/ + deps = ["//internal/testenv"], +) diff -urN b/cmd/signature-fuzzer/fuzz-runner/testdata/BUILD.bazel c/cmd/signature-fuzzer/fuzz-runner/testdata/BUILD.bazel ---- b/cmd/signature-fuzzer/fuzz-runner/testdata/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/signature-fuzzer/fuzz-runner/testdata/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/signature-fuzzer/fuzz-runner/testdata/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1690,7 +1598,7 @@ diff -urN b/cmd/signature-fuzzer/fuzz-runner/testdata/BUILD.bazel c/cmd/signatur + visibility = ["//visibility:public"], +) diff -urN b/cmd/signature-fuzzer/internal/fuzz-generator/BUILD.bazel c/cmd/signature-fuzzer/internal/fuzz-generator/BUILD.bazel ---- b/cmd/signature-fuzzer/internal/fuzz-generator/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/signature-fuzzer/internal/fuzz-generator/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/signature-fuzzer/internal/fuzz-generator/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -1726,9 +1634,9 @@ diff -urN b/cmd/signature-fuzzer/internal/fuzz-generator/BUILD.bazel c/cmd/signa + deps = ["//internal/testenv"], +) diff -urN b/cmd/splitdwarf/BUILD.bazel c/cmd/splitdwarf/BUILD.bazel ---- b/cmd/splitdwarf/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/splitdwarf/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/splitdwarf/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,47 @@ +@@ -0,0 +1,44 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( @@ -1752,9 +1660,6 @@ diff -urN b/cmd/splitdwarf/BUILD.bazel c/cmd/splitdwarf/BUILD.bazel + "@io_bazel_rules_go//go/platform:freebsd": [ + "//cmd/splitdwarf/internal/macho", + ], -+ "@io_bazel_rules_go//go/platform:illumos": [ -+ "//cmd/splitdwarf/internal/macho", -+ ], + "@io_bazel_rules_go//go/platform:ios": [ + "//cmd/splitdwarf/internal/macho", + ], @@ -1777,7 +1682,7 @@ diff -urN b/cmd/splitdwarf/BUILD.bazel c/cmd/splitdwarf/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/splitdwarf/internal/macho/BUILD.bazel c/cmd/splitdwarf/internal/macho/BUILD.bazel ---- b/cmd/splitdwarf/internal/macho/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/splitdwarf/internal/macho/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/splitdwarf/internal/macho/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -1804,11 +1709,11 @@ diff -urN b/cmd/splitdwarf/internal/macho/BUILD.bazel c/cmd/splitdwarf/internal/ +go_test( + name = "macho_test", + srcs = ["file_test.go"], -+ data = glob(["testdata/**"]), ++ data = glob(["testdata/**"], allow_empty = True), + embed = [":macho"], +) diff -urN b/cmd/ssadump/BUILD.bazel c/cmd/ssadump/BUILD.bazel ---- b/cmd/ssadump/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/ssadump/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/ssadump/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1833,9 +1738,9 @@ diff -urN b/cmd/ssadump/BUILD.bazel c/cmd/ssadump/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/stress/BUILD.bazel c/cmd/stress/BUILD.bazel ---- b/cmd/stress/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/stress/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/stress/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,56 @@ +@@ -0,0 +1,50 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( @@ -1859,15 +1764,9 @@ diff -urN b/cmd/stress/BUILD.bazel c/cmd/stress/BUILD.bazel + "@io_bazel_rules_go//go/platform:freebsd": [ + "@org_golang_x_sys//execabs:go_default_library", + ], -+ "@io_bazel_rules_go//go/platform:illumos": [ -+ "@org_golang_x_sys//execabs:go_default_library", -+ ], + "@io_bazel_rules_go//go/platform:ios": [ + "@org_golang_x_sys//execabs:go_default_library", + ], -+ "@io_bazel_rules_go//go/platform:js": [ -+ "@org_golang_x_sys//execabs:go_default_library", -+ ], + "@io_bazel_rules_go//go/platform:linux": [ + "@org_golang_x_sys//execabs:go_default_library", + ], @@ -1893,7 +1792,7 @@ diff -urN b/cmd/stress/BUILD.bazel c/cmd/stress/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/cmd/stringer/BUILD.bazel c/cmd/stringer/BUILD.bazel ---- b/cmd/stringer/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/stringer/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/stringer/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,69 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") @@ -1966,7 +1865,7 @@ diff -urN b/cmd/stringer/BUILD.bazel c/cmd/stringer/BUILD.bazel + }), +) diff -urN b/cmd/stringer/testdata/BUILD.bazel c/cmd/stringer/testdata/BUILD.bazel ---- b/cmd/stringer/testdata/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/stringer/testdata/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/stringer/testdata/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -1997,7 +1896,7 @@ diff -urN b/cmd/stringer/testdata/BUILD.bazel c/cmd/stringer/testdata/BUILD.baze + visibility = ["//visibility:public"], +) diff -urN b/cmd/stringer/testdata/typeparams/BUILD.bazel c/cmd/stringer/testdata/typeparams/BUILD.bazel ---- b/cmd/stringer/testdata/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/stringer/testdata/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/stringer/testdata/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -2018,7 +1917,7 @@ diff -urN b/cmd/stringer/testdata/typeparams/BUILD.bazel c/cmd/stringer/testdata + visibility = ["//visibility:public"], +) diff -urN b/cmd/toolstash/BUILD.bazel c/cmd/toolstash/BUILD.bazel ---- b/cmd/toolstash/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cmd/toolstash/BUILD.bazel 1970-01-01 08:00:00 +++ c/cmd/toolstash/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -2040,7 +1939,7 @@ diff -urN b/cmd/toolstash/BUILD.bazel c/cmd/toolstash/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/container/intsets/BUILD.bazel c/container/intsets/BUILD.bazel ---- b/container/intsets/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/container/intsets/BUILD.bazel 1970-01-01 08:00:00 +++ c/container/intsets/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -2067,7 +1966,7 @@ diff -urN b/container/intsets/BUILD.bazel c/container/intsets/BUILD.bazel + embed = [":intsets"], +) diff -urN b/copyright/BUILD.bazel c/copyright/BUILD.bazel ---- b/copyright/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/copyright/BUILD.bazel 1970-01-01 08:00:00 +++ c/copyright/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -2091,7 +1990,7 @@ diff -urN b/copyright/BUILD.bazel c/copyright/BUILD.bazel + embed = [":copyright"], +) diff -urN b/cover/BUILD.bazel c/cover/BUILD.bazel ---- b/cover/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/cover/BUILD.bazel 1970-01-01 08:00:00 +++ c/cover/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -2114,8 +2013,37 @@ diff -urN b/cover/BUILD.bazel c/cover/BUILD.bazel + srcs = ["profile_test.go"], + embed = [":cover"], +) +diff -urN b/go/analysis/BUILD.bazel c/go/analysis/BUILD.bazel +--- b/go/analysis/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,25 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") ++ ++go_library( ++ name = "analysis", ++ srcs = [ ++ "analysis.go", ++ "diagnostic.go", ++ "doc.go", ++ "validate.go", ++ ], ++ importpath = "golang.org/x/tools/go/analysis", ++ visibility = ["//visibility:public"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":analysis", ++ visibility = ["//visibility:public"], ++) ++ ++go_test( ++ name = "analysis_test", ++ srcs = ["validate_test.go"], ++ embed = [":analysis"], ++) diff -urN b/go/analysis/analysistest/BUILD.bazel c/go/analysis/analysistest/BUILD.bazel ---- b/go/analysis/analysistest/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/analysistest/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/analysistest/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -2150,68 +2078,43 @@ diff -urN b/go/analysis/analysistest/BUILD.bazel c/go/analysis/analysistest/BUIL + "//internal/testenv", + ], +) -diff -urN b/go/analysis/BUILD.bazel c/go/analysis/BUILD.bazel ---- b/go/analysis/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/analysis/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,25 @@ +diff -urN b/go/analysis/internal/analysisflags/BUILD.bazel c/go/analysis/internal/analysisflags/BUILD.bazel +--- b/go/analysis/internal/analysisflags/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/internal/analysisflags/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( -+ name = "analysis", ++ name = "analysisflags", + srcs = [ -+ "analysis.go", -+ "diagnostic.go", -+ "doc.go", -+ "validate.go", ++ "flags.go", ++ "help.go", ++ "url.go", + ], -+ importpath = "golang.org/x/tools/go/analysis", -+ visibility = ["//visibility:public"], ++ importpath = "golang.org/x/tools/go/analysis/internal/analysisflags", ++ visibility = ["//go/analysis:__subpackages__"], ++ deps = ["//go/analysis"], +) + +alias( + name = "go_default_library", -+ actual = ":analysis", -+ visibility = ["//visibility:public"], ++ actual = ":analysisflags", ++ visibility = ["//go/analysis:__subpackages__"], +) + +go_test( -+ name = "analysis_test", -+ srcs = ["validate_test.go"], -+ embed = [":analysis"], -+) -diff -urN b/go/analysis/internal/analysisflags/BUILD.bazel c/go/analysis/internal/analysisflags/BUILD.bazel ---- b/go/analysis/internal/analysisflags/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/analysis/internal/analysisflags/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,27 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -+ -+go_library( -+ name = "analysisflags", ++ name = "analysisflags_test", + srcs = [ -+ "flags.go", -+ "help.go", ++ "flags_test.go", ++ "url_test.go", + ], -+ importpath = "golang.org/x/tools/go/analysis/internal/analysisflags", -+ visibility = ["//go/analysis:__subpackages__"], -+ deps = ["//go/analysis"], -+) -+ -+alias( -+ name = "go_default_library", -+ actual = ":analysisflags", -+ visibility = ["//go/analysis:__subpackages__"], -+) -+ -+go_test( -+ name = "analysisflags_test", -+ srcs = ["flags_test.go"], + deps = [ + ":analysisflags", + "//go/analysis", + ], +) diff -urN b/go/analysis/internal/checker/BUILD.bazel c/go/analysis/internal/checker/BUILD.bazel ---- b/go/analysis/internal/checker/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/internal/checker/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/internal/checker/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -2252,8 +2155,25 @@ diff -urN b/go/analysis/internal/checker/BUILD.bazel c/go/analysis/internal/chec + "//internal/testenv", + ], +) +diff -urN b/go/analysis/internal/versiontest/BUILD.bazel c/go/analysis/internal/versiontest/BUILD.bazel +--- b/go/analysis/internal/versiontest/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/internal/versiontest/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,13 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_test") ++ ++go_test( ++ name = "versiontest_test", ++ srcs = ["version_test.go"], ++ deps = [ ++ "//go/analysis", ++ "//go/analysis/analysistest", ++ "//go/analysis/multichecker", ++ "//go/analysis/singlechecker", ++ "//internal/testenv", ++ ], ++) diff -urN b/go/analysis/multichecker/BUILD.bazel c/go/analysis/multichecker/BUILD.bazel ---- b/go/analysis/multichecker/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/multichecker/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/multichecker/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -2287,10 +2207,84 @@ diff -urN b/go/analysis/multichecker/BUILD.bazel c/go/analysis/multichecker/BUIL + "//internal/testenv", + ], +) +diff -urN b/go/analysis/passes/appends/BUILD.bazel c/go/analysis/passes/appends/BUILD.bazel +--- b/go/analysis/passes/appends/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/appends/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,34 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") ++ ++go_library( ++ name = "appends", ++ srcs = [ ++ "appends.go", ++ "doc.go", ++ ], ++ embedsrcs = ["doc.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/appends", ++ visibility = ["//visibility:public"], ++ deps = [ ++ "//go/analysis", ++ "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", ++ "//go/ast/inspector", ++ ], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":appends", ++ visibility = ["//visibility:public"], ++) ++ ++go_test( ++ name = "appends_test", ++ srcs = ["appends_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), ++ deps = [ ++ ":appends", ++ "//go/analysis/analysistest", ++ ], ++) +diff -urN b/go/analysis/passes/appends/testdata/src/a/BUILD.bazel c/go/analysis/passes/appends/testdata/src/a/BUILD.bazel +--- b/go/analysis/passes/appends/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/appends/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,14 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "a", ++ srcs = ["a.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/appends/testdata/src/a", ++ visibility = ["//visibility:public"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":a", ++ visibility = ["//visibility:public"], ++) +diff -urN b/go/analysis/passes/appends/testdata/src/b/BUILD.bazel c/go/analysis/passes/appends/testdata/src/b/BUILD.bazel +--- b/go/analysis/passes/appends/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/appends/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,14 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "b", ++ srcs = ["b.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/appends/testdata/src/b", ++ visibility = ["//visibility:public"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":b", ++ visibility = ["//visibility:public"], ++) diff -urN b/go/analysis/passes/asmdecl/BUILD.bazel c/go/analysis/passes/asmdecl/BUILD.bazel ---- b/go/analysis/passes/asmdecl/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/asmdecl/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/asmdecl/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,31 @@ +@@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -2317,13 +2311,14 @@ diff -urN b/go/analysis/passes/asmdecl/BUILD.bazel c/go/analysis/passes/asmdecl/ +go_test( + name = "asmdecl_test", + srcs = ["asmdecl_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":asmdecl", + "//go/analysis/analysistest", + ], +) diff -urN b/go/analysis/passes/asmdecl/testdata/src/a/BUILD.bazel c/go/analysis/passes/asmdecl/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/asmdecl/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/asmdecl/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/asmdecl/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2353,14 +2348,18 @@ diff -urN b/go/analysis/passes/asmdecl/testdata/src/a/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/assign/BUILD.bazel c/go/analysis/passes/assign/BUILD.bazel ---- b/go/analysis/passes/assign/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/assign/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/assign/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "assign", -+ srcs = ["assign.go"], ++ srcs = [ ++ "assign.go", ++ "doc.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/assign", + visibility = ["//visibility:public"], + deps = [ @@ -2387,7 +2386,7 @@ diff -urN b/go/analysis/passes/assign/BUILD.bazel c/go/analysis/passes/assign/BU + ], +) diff -urN b/go/analysis/passes/assign/testdata/src/a/BUILD.bazel c/go/analysis/passes/assign/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/assign/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/assign/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/assign/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2405,7 +2404,7 @@ diff -urN b/go/analysis/passes/assign/testdata/src/a/BUILD.bazel c/go/analysis/p + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/assign/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/assign/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/assign/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/assign/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/assign/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2423,14 +2422,18 @@ diff -urN b/go/analysis/passes/assign/testdata/src/typeparams/BUILD.bazel c/go/a + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/atomic/BUILD.bazel c/go/analysis/passes/atomic/BUILD.bazel ---- b/go/analysis/passes/atomic/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/atomic/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/atomic/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "atomic", -+ srcs = ["atomic.go"], ++ srcs = [ ++ "atomic.go", ++ "doc.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/atomic", + visibility = ["//visibility:public"], + deps = [ @@ -2450,6 +2453,7 @@ diff -urN b/go/analysis/passes/atomic/BUILD.bazel c/go/analysis/passes/atomic/BU +go_test( + name = "atomic_test", + srcs = ["atomic_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":atomic", + "//go/analysis/analysistest", @@ -2457,7 +2461,7 @@ diff -urN b/go/analysis/passes/atomic/BUILD.bazel c/go/analysis/passes/atomic/BU + ], +) diff -urN b/go/analysis/passes/atomic/testdata/src/a/BUILD.bazel c/go/analysis/passes/atomic/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/atomic/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/atomic/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/atomic/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2475,7 +2479,7 @@ diff -urN b/go/analysis/passes/atomic/testdata/src/a/BUILD.bazel c/go/analysis/p + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/atomic/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/atomic/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/atomic/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/atomic/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/atomic/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2493,9 +2497,9 @@ diff -urN b/go/analysis/passes/atomic/testdata/src/typeparams/BUILD.bazel c/go/a + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/atomicalign/BUILD.bazel c/go/analysis/passes/atomicalign/BUILD.bazel ---- b/go/analysis/passes/atomicalign/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/atomicalign/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/atomicalign/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,29 @@ +@@ -0,0 +1,30 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -2520,13 +2524,14 @@ diff -urN b/go/analysis/passes/atomicalign/BUILD.bazel c/go/analysis/passes/atom +go_test( + name = "atomicalign_test", + srcs = ["atomicalign_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":atomicalign", + "//go/analysis/analysistest", + ], +) diff -urN b/go/analysis/passes/atomicalign/testdata/src/a/BUILD.bazel c/go/analysis/passes/atomicalign/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/atomicalign/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/atomicalign/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/atomicalign/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2547,7 +2552,7 @@ diff -urN b/go/analysis/passes/atomicalign/testdata/src/a/BUILD.bazel c/go/analy + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/atomicalign/testdata/src/b/BUILD.bazel c/go/analysis/passes/atomicalign/testdata/src/b/BUILD.bazel ---- b/go/analysis/passes/atomicalign/testdata/src/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/atomicalign/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/atomicalign/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2568,9 +2573,9 @@ diff -urN b/go/analysis/passes/atomicalign/testdata/src/b/BUILD.bazel c/go/analy + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/bools/BUILD.bazel c/go/analysis/passes/bools/BUILD.bazel ---- b/go/analysis/passes/bools/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/bools/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/bools/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -2595,6 +2600,7 @@ diff -urN b/go/analysis/passes/bools/BUILD.bazel c/go/analysis/passes/bools/BUIL +go_test( + name = "bools_test", + srcs = ["bools_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":bools", + "//go/analysis/analysistest", @@ -2602,7 +2608,7 @@ diff -urN b/go/analysis/passes/bools/BUILD.bazel c/go/analysis/passes/bools/BUIL + ], +) diff -urN b/go/analysis/passes/bools/testdata/src/a/BUILD.bazel c/go/analysis/passes/bools/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/bools/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/bools/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/bools/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2620,7 +2626,7 @@ diff -urN b/go/analysis/passes/bools/testdata/src/a/BUILD.bazel c/go/analysis/pa + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/bools/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/bools/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/bools/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/bools/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/bools/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2638,9 +2644,9 @@ diff -urN b/go/analysis/passes/bools/testdata/src/typeparams/BUILD.bazel c/go/an + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/buildssa/BUILD.bazel c/go/analysis/passes/buildssa/BUILD.bazel ---- b/go/analysis/passes/buildssa/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/buildssa/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/buildssa/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -2663,6 +2669,7 @@ diff -urN b/go/analysis/passes/buildssa/BUILD.bazel c/go/analysis/passes/buildss +go_test( + name = "buildssa_test", + srcs = ["buildssa_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":buildssa", + "//go/analysis/analysistest", @@ -2670,7 +2677,7 @@ diff -urN b/go/analysis/passes/buildssa/BUILD.bazel c/go/analysis/passes/buildss + ], +) diff -urN b/go/analysis/passes/buildssa/testdata/src/a/BUILD.bazel c/go/analysis/passes/buildssa/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/buildssa/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/buildssa/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/buildssa/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2688,7 +2695,7 @@ diff -urN b/go/analysis/passes/buildssa/testdata/src/a/BUILD.bazel c/go/analysis + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/buildssa/testdata/src/b/BUILD.bazel c/go/analysis/passes/buildssa/testdata/src/b/BUILD.bazel ---- b/go/analysis/passes/buildssa/testdata/src/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/buildssa/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/buildssa/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2706,7 +2713,7 @@ diff -urN b/go/analysis/passes/buildssa/testdata/src/b/BUILD.bazel c/go/analysis + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/buildssa/testdata/src/c/BUILD.bazel c/go/analysis/passes/buildssa/testdata/src/c/BUILD.bazel ---- b/go/analysis/passes/buildssa/testdata/src/c/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/buildssa/testdata/src/c/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/buildssa/testdata/src/c/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2724,9 +2731,9 @@ diff -urN b/go/analysis/passes/buildssa/testdata/src/c/BUILD.bazel c/go/analysis + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/buildtag/BUILD.bazel c/go/analysis/passes/buildtag/BUILD.bazel ---- b/go/analysis/passes/buildtag/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/buildtag/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/buildtag/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,31 @@ +@@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -2752,6 +2759,7 @@ diff -urN b/go/analysis/passes/buildtag/BUILD.bazel c/go/analysis/passes/buildta +go_test( + name = "buildtag_test", + srcs = ["buildtag_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":buildtag", + "//go/analysis", @@ -2759,7 +2767,7 @@ diff -urN b/go/analysis/passes/buildtag/BUILD.bazel c/go/analysis/passes/buildta + ], +) diff -urN b/go/analysis/passes/buildtag/testdata/src/a/BUILD.bazel c/go/analysis/passes/buildtag/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/buildtag/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/buildtag/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/buildtag/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2781,14 +2789,18 @@ diff -urN b/go/analysis/passes/buildtag/testdata/src/a/BUILD.bazel c/go/analysis + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/cgocall/BUILD.bazel c/go/analysis/passes/cgocall/BUILD.bazel ---- b/go/analysis/passes/cgocall/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/cgocall/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/cgocall/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cgocall", -+ srcs = ["cgocall.go"], ++ srcs = [ ++ "cgocall.go", ++ "cgocall_go120.go", ++ "cgocall_go121.go", ++ ], + importpath = "golang.org/x/tools/go/analysis/passes/cgocall", + visibility = ["//visibility:public"], + deps = [ @@ -2806,6 +2818,7 @@ diff -urN b/go/analysis/passes/cgocall/BUILD.bazel c/go/analysis/passes/cgocall/ +go_test( + name = "cgocall_test", + srcs = ["cgocall_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":cgocall", + "//go/analysis/analysistest", @@ -2813,7 +2826,7 @@ diff -urN b/go/analysis/passes/cgocall/BUILD.bazel c/go/analysis/passes/cgocall/ + ], +) diff -urN b/go/analysis/passes/cgocall/testdata/src/a/BUILD.bazel c/go/analysis/passes/cgocall/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/cgocall/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/cgocall/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/cgocall/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2835,7 +2848,7 @@ diff -urN b/go/analysis/passes/cgocall/testdata/src/a/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/cgocall/testdata/src/b/BUILD.bazel c/go/analysis/passes/cgocall/testdata/src/b/BUILD.bazel ---- b/go/analysis/passes/cgocall/testdata/src/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/cgocall/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/cgocall/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2853,7 +2866,7 @@ diff -urN b/go/analysis/passes/cgocall/testdata/src/b/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/cgocall/testdata/src/c/BUILD.bazel c/go/analysis/passes/cgocall/testdata/src/c/BUILD.bazel ---- b/go/analysis/passes/cgocall/testdata/src/c/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/cgocall/testdata/src/c/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/cgocall/testdata/src/c/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2871,7 +2884,7 @@ diff -urN b/go/analysis/passes/cgocall/testdata/src/c/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/cgocall/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/cgocall/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/cgocall/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/cgocall/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/cgocall/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2890,9 +2903,9 @@ diff -urN b/go/analysis/passes/cgocall/testdata/src/typeparams/BUILD.bazel c/go/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/composite/BUILD.bazel c/go/analysis/passes/composite/BUILD.bazel ---- b/go/analysis/passes/composite/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/composite/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/composite/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,33 @@ +@@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -2920,6 +2933,7 @@ diff -urN b/go/analysis/passes/composite/BUILD.bazel c/go/analysis/passes/compos +go_test( + name = "composite_test", + srcs = ["composite_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":composite", + "//go/analysis/analysistest", @@ -2927,7 +2941,7 @@ diff -urN b/go/analysis/passes/composite/BUILD.bazel c/go/analysis/passes/compos + ], +) diff -urN b/go/analysis/passes/composite/testdata/src/a/BUILD.bazel c/go/analysis/passes/composite/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/composite/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/composite/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/composite/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -2951,7 +2965,7 @@ diff -urN b/go/analysis/passes/composite/testdata/src/a/BUILD.bazel c/go/analysi + embed = [":a"], +) diff -urN b/go/analysis/passes/composite/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/composite/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/composite/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/composite/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/composite/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2969,7 +2983,7 @@ diff -urN b/go/analysis/passes/composite/testdata/src/typeparams/BUILD.bazel c/g + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/composite/testdata/src/typeparams/lib/BUILD.bazel c/go/analysis/passes/composite/testdata/src/typeparams/lib/BUILD.bazel ---- b/go/analysis/passes/composite/testdata/src/typeparams/lib/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/composite/testdata/src/typeparams/lib/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/composite/testdata/src/typeparams/lib/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -2987,9 +3001,9 @@ diff -urN b/go/analysis/passes/composite/testdata/src/typeparams/lib/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/copylock/BUILD.bazel c/go/analysis/passes/copylock/BUILD.bazel ---- b/go/analysis/passes/copylock/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/copylock/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/copylock/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,31 @@ +@@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -3015,6 +3029,7 @@ diff -urN b/go/analysis/passes/copylock/BUILD.bazel c/go/analysis/passes/copyloc +go_test( + name = "copylock_test", + srcs = ["copylock_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":copylock", + "//go/analysis/analysistest", @@ -3022,9 +3037,9 @@ diff -urN b/go/analysis/passes/copylock/BUILD.bazel c/go/analysis/passes/copyloc + ], +) diff -urN b/go/analysis/passes/copylock/testdata/src/a/BUILD.bazel c/go/analysis/passes/copylock/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/copylock/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/copylock/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/copylock/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,18 @@ +@@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( @@ -3033,6 +3048,7 @@ diff -urN b/go/analysis/passes/copylock/testdata/src/a/BUILD.bazel c/go/analysis + "copylock.go", + "copylock_func.go", + "copylock_range.go", ++ "issue61678.go", + ], + importpath = "golang.org/x/tools/go/analysis/passes/copylock/testdata/src/a", + visibility = ["//visibility:public"], @@ -3044,7 +3060,7 @@ diff -urN b/go/analysis/passes/copylock/testdata/src/a/BUILD.bazel c/go/analysis + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/copylock/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/copylock/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/copylock/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/copylock/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/copylock/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3062,9 +3078,9 @@ diff -urN b/go/analysis/passes/copylock/testdata/src/typeparams/BUILD.bazel c/go + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/ctrlflow/BUILD.bazel c/go/analysis/passes/ctrlflow/BUILD.bazel ---- b/go/analysis/passes/ctrlflow/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/ctrlflow/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/ctrlflow/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,31 @@ +@@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -3090,6 +3106,7 @@ diff -urN b/go/analysis/passes/ctrlflow/BUILD.bazel c/go/analysis/passes/ctrlflo +go_test( + name = "ctrlflow_test", + srcs = ["ctrlflow_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":ctrlflow", + "//go/analysis/analysistest", @@ -3097,7 +3114,7 @@ diff -urN b/go/analysis/passes/ctrlflow/BUILD.bazel c/go/analysis/passes/ctrlflo + ], +) diff -urN b/go/analysis/passes/ctrlflow/testdata/src/a/BUILD.bazel c/go/analysis/passes/ctrlflow/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/ctrlflow/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/ctrlflow/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/ctrlflow/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3115,7 +3132,7 @@ diff -urN b/go/analysis/passes/ctrlflow/testdata/src/a/BUILD.bazel c/go/analysis + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/ctrlflow/testdata/src/lib/BUILD.bazel c/go/analysis/passes/ctrlflow/testdata/src/lib/BUILD.bazel ---- b/go/analysis/passes/ctrlflow/testdata/src/lib/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/ctrlflow/testdata/src/lib/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/ctrlflow/testdata/src/lib/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3133,7 +3150,7 @@ diff -urN b/go/analysis/passes/ctrlflow/testdata/src/lib/BUILD.bazel c/go/analys + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/ctrlflow/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/ctrlflow/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/ctrlflow/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/ctrlflow/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/ctrlflow/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3151,9 +3168,9 @@ diff -urN b/go/analysis/passes/ctrlflow/testdata/src/typeparams/BUILD.bazel c/go + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/deepequalerrors/BUILD.bazel c/go/analysis/passes/deepequalerrors/BUILD.bazel ---- b/go/analysis/passes/deepequalerrors/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/deepequalerrors/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/deepequalerrors/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -3164,6 +3181,7 @@ diff -urN b/go/analysis/passes/deepequalerrors/BUILD.bazel c/go/analysis/passes/ + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//go/types/typeutil", + ], @@ -3178,6 +3196,7 @@ diff -urN b/go/analysis/passes/deepequalerrors/BUILD.bazel c/go/analysis/passes/ +go_test( + name = "deepequalerrors_test", + srcs = ["deepequalerrors_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":deepequalerrors", + "//go/analysis/analysistest", @@ -3185,7 +3204,7 @@ diff -urN b/go/analysis/passes/deepequalerrors/BUILD.bazel c/go/analysis/passes/ + ], +) diff -urN b/go/analysis/passes/deepequalerrors/testdata/src/a/BUILD.bazel c/go/analysis/passes/deepequalerrors/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/deepequalerrors/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/deepequalerrors/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/deepequalerrors/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3203,7 +3222,7 @@ diff -urN b/go/analysis/passes/deepequalerrors/testdata/src/a/BUILD.bazel c/go/a + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/deepequalerrors/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/deepequalerrors/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/deepequalerrors/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/deepequalerrors/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/deepequalerrors/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3220,10 +3239,89 @@ diff -urN b/go/analysis/passes/deepequalerrors/testdata/src/typeparams/BUILD.baz + actual = ":typeparams", + visibility = ["//visibility:public"], +) +diff -urN b/go/analysis/passes/defers/BUILD.bazel c/go/analysis/passes/defers/BUILD.bazel +--- b/go/analysis/passes/defers/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/defers/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,35 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") ++ ++go_library( ++ name = "defers", ++ srcs = [ ++ "defers.go", ++ "doc.go", ++ ], ++ embedsrcs = ["doc.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/defers", ++ visibility = ["//visibility:public"], ++ deps = [ ++ "//go/analysis", ++ "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", ++ "//go/ast/inspector", ++ "//go/types/typeutil", ++ ], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":defers", ++ visibility = ["//visibility:public"], ++) ++ ++go_test( ++ name = "defers_test", ++ srcs = ["defers_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), ++ deps = [ ++ ":defers", ++ "//go/analysis/analysistest", ++ ], ++) +diff -urN b/go/analysis/passes/defers/cmd/defers/BUILD.bazel c/go/analysis/passes/defers/cmd/defers/BUILD.bazel +--- b/go/analysis/passes/defers/cmd/defers/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/defers/cmd/defers/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,18 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") ++ ++go_library( ++ name = "defers_lib", ++ srcs = ["main.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/defers/cmd/defers", ++ visibility = ["//visibility:private"], ++ deps = [ ++ "//go/analysis/passes/defers", ++ "//go/analysis/singlechecker", ++ ], ++) ++ ++go_binary( ++ name = "defers", ++ embed = [":defers_lib"], ++ visibility = ["//visibility:public"], ++) +diff -urN b/go/analysis/passes/defers/testdata/src/a/BUILD.bazel c/go/analysis/passes/defers/testdata/src/a/BUILD.bazel +--- b/go/analysis/passes/defers/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/defers/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,14 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "a", ++ srcs = ["a.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/defers/testdata/src/a", ++ visibility = ["//visibility:public"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":a", ++ visibility = ["//visibility:public"], ++) diff -urN b/go/analysis/passes/directive/BUILD.bazel c/go/analysis/passes/directive/BUILD.bazel ---- b/go/analysis/passes/directive/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/directive/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/directive/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -3246,6 +3344,7 @@ diff -urN b/go/analysis/passes/directive/BUILD.bazel c/go/analysis/passes/direct +go_test( + name = "directive_test", + srcs = ["directive_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":directive", + "//go/analysis", @@ -3253,7 +3352,7 @@ diff -urN b/go/analysis/passes/directive/BUILD.bazel c/go/analysis/passes/direct + ], +) diff -urN b/go/analysis/passes/directive/testdata/src/a/BUILD.bazel c/go/analysis/passes/directive/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/directive/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/directive/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/directive/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -3279,9 +3378,9 @@ diff -urN b/go/analysis/passes/directive/testdata/src/a/BUILD.bazel c/go/analysi + srcs = ["misplaced_test.go"], +) diff -urN b/go/analysis/passes/errorsas/BUILD.bazel c/go/analysis/passes/errorsas/BUILD.bazel ---- b/go/analysis/passes/errorsas/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/errorsas/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/errorsas/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -3292,6 +3391,7 @@ diff -urN b/go/analysis/passes/errorsas/BUILD.bazel c/go/analysis/passes/errorsa + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//go/types/typeutil", + ], @@ -3313,7 +3413,7 @@ diff -urN b/go/analysis/passes/errorsas/BUILD.bazel c/go/analysis/passes/errorsa + ], +) diff -urN b/go/analysis/passes/errorsas/testdata/src/a/BUILD.bazel c/go/analysis/passes/errorsas/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/errorsas/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/errorsas/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/errorsas/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3331,7 +3431,7 @@ diff -urN b/go/analysis/passes/errorsas/testdata/src/a/BUILD.bazel c/go/analysis + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/errorsas/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/errorsas/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/errorsas/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/errorsas/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/errorsas/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3349,9 +3449,9 @@ diff -urN b/go/analysis/passes/errorsas/testdata/src/typeparams/BUILD.bazel c/go + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/fieldalignment/BUILD.bazel c/go/analysis/passes/fieldalignment/BUILD.bazel ---- b/go/analysis/passes/fieldalignment/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/fieldalignment/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/fieldalignment/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -3375,13 +3475,14 @@ diff -urN b/go/analysis/passes/fieldalignment/BUILD.bazel c/go/analysis/passes/f +go_test( + name = "fieldalignment_test", + srcs = ["fieldalignment_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":fieldalignment", + "//go/analysis/analysistest", + ], +) diff -urN b/go/analysis/passes/fieldalignment/cmd/fieldalignment/BUILD.bazel c/go/analysis/passes/fieldalignment/cmd/fieldalignment/BUILD.bazel ---- b/go/analysis/passes/fieldalignment/cmd/fieldalignment/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/fieldalignment/cmd/fieldalignment/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/fieldalignment/cmd/fieldalignment/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -3403,7 +3504,7 @@ diff -urN b/go/analysis/passes/fieldalignment/cmd/fieldalignment/BUILD.bazel c/g + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/fieldalignment/testdata/src/a/BUILD.bazel c/go/analysis/passes/fieldalignment/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/fieldalignment/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/fieldalignment/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/fieldalignment/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3425,9 +3526,9 @@ diff -urN b/go/analysis/passes/fieldalignment/testdata/src/a/BUILD.bazel c/go/an + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/findcall/BUILD.bazel c/go/analysis/passes/findcall/BUILD.bazel ---- b/go/analysis/passes/findcall/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/findcall/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/findcall/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,24 @@ +@@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -3447,13 +3548,14 @@ diff -urN b/go/analysis/passes/findcall/BUILD.bazel c/go/analysis/passes/findcal +go_test( + name = "findcall_test", + srcs = ["findcall_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":findcall", + "//go/analysis/analysistest", + ], +) diff -urN b/go/analysis/passes/findcall/cmd/findcall/BUILD.bazel c/go/analysis/passes/findcall/cmd/findcall/BUILD.bazel ---- b/go/analysis/passes/findcall/cmd/findcall/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/findcall/cmd/findcall/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/findcall/cmd/findcall/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -3475,7 +3577,7 @@ diff -urN b/go/analysis/passes/findcall/cmd/findcall/BUILD.bazel c/go/analysis/p + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/findcall/testdata/src/a/BUILD.bazel c/go/analysis/passes/findcall/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/findcall/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/findcall/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/findcall/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -3493,9 +3595,9 @@ diff -urN b/go/analysis/passes/findcall/testdata/src/a/BUILD.bazel c/go/analysis + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/framepointer/BUILD.bazel c/go/analysis/passes/framepointer/BUILD.bazel ---- b/go/analysis/passes/framepointer/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/framepointer/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/framepointer/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,27 @@ +@@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -3518,13 +3620,14 @@ diff -urN b/go/analysis/passes/framepointer/BUILD.bazel c/go/analysis/passes/fra +go_test( + name = "framepointer_test", + srcs = ["framepointer_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":framepointer", + "//go/analysis/analysistest", + ], +) diff -urN b/go/analysis/passes/framepointer/testdata/src/a/BUILD.bazel c/go/analysis/passes/framepointer/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/framepointer/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/framepointer/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/framepointer/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3548,9 +3651,9 @@ diff -urN b/go/analysis/passes/framepointer/testdata/src/a/BUILD.bazel c/go/anal + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/httpresponse/BUILD.bazel c/go/analysis/passes/httpresponse/BUILD.bazel ---- b/go/analysis/passes/httpresponse/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/httpresponse/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/httpresponse/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -3575,6 +3678,7 @@ diff -urN b/go/analysis/passes/httpresponse/BUILD.bazel c/go/analysis/passes/htt +go_test( + name = "httpresponse_test", + srcs = ["httpresponse_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":httpresponse", + "//go/analysis/analysistest", @@ -3582,7 +3686,7 @@ diff -urN b/go/analysis/passes/httpresponse/BUILD.bazel c/go/analysis/passes/htt + ], +) diff -urN b/go/analysis/passes/httpresponse/testdata/src/a/BUILD.bazel c/go/analysis/passes/httpresponse/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/httpresponse/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/httpresponse/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/httpresponse/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3600,7 +3704,7 @@ diff -urN b/go/analysis/passes/httpresponse/testdata/src/a/BUILD.bazel c/go/anal + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/httpresponse/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/httpresponse/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/httpresponse/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/httpresponse/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/httpresponse/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3618,22 +3722,25 @@ diff -urN b/go/analysis/passes/httpresponse/testdata/src/typeparams/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/ifaceassert/BUILD.bazel c/go/analysis/passes/ifaceassert/BUILD.bazel ---- b/go/analysis/passes/ifaceassert/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/ifaceassert/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/ifaceassert/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,33 @@ +@@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "ifaceassert", + srcs = [ ++ "doc.go", + "ifaceassert.go", + "parameterized.go", + ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/ifaceassert", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//internal/typeparams", + ], @@ -3648,6 +3755,7 @@ diff -urN b/go/analysis/passes/ifaceassert/BUILD.bazel c/go/analysis/passes/ifac +go_test( + name = "ifaceassert_test", + srcs = ["ifaceassert_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":ifaceassert", + "//go/analysis/analysistest", @@ -3655,7 +3763,7 @@ diff -urN b/go/analysis/passes/ifaceassert/BUILD.bazel c/go/analysis/passes/ifac + ], +) diff -urN b/go/analysis/passes/ifaceassert/cmd/ifaceassert/BUILD.bazel c/go/analysis/passes/ifaceassert/cmd/ifaceassert/BUILD.bazel ---- b/go/analysis/passes/ifaceassert/cmd/ifaceassert/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/ifaceassert/cmd/ifaceassert/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/ifaceassert/cmd/ifaceassert/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -3677,7 +3785,7 @@ diff -urN b/go/analysis/passes/ifaceassert/cmd/ifaceassert/BUILD.bazel c/go/anal + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/ifaceassert/testdata/src/a/BUILD.bazel c/go/analysis/passes/ifaceassert/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/ifaceassert/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/ifaceassert/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/ifaceassert/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3695,7 +3803,7 @@ diff -urN b/go/analysis/passes/ifaceassert/testdata/src/a/BUILD.bazel c/go/analy + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/ifaceassert/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/ifaceassert/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/ifaceassert/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/ifaceassert/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/ifaceassert/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3713,7 +3821,7 @@ diff -urN b/go/analysis/passes/ifaceassert/testdata/src/typeparams/BUILD.bazel c + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/inspect/BUILD.bazel c/go/analysis/passes/inspect/BUILD.bazel ---- b/go/analysis/passes/inspect/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/inspect/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/inspect/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3735,14 +3843,17 @@ diff -urN b/go/analysis/passes/inspect/BUILD.bazel c/go/analysis/passes/inspect/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/internal/analysisutil/BUILD.bazel c/go/analysis/passes/internal/analysisutil/BUILD.bazel ---- b/go/analysis/passes/internal/analysisutil/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/internal/analysisutil/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/internal/analysisutil/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,23 @@ +@@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "analysisutil", -+ srcs = ["util.go"], ++ srcs = [ ++ "extractdoc.go", ++ "util.go", ++ ], + importpath = "golang.org/x/tools/go/analysis/passes/internal/analysisutil", + visibility = ["//go/analysis/passes:__subpackages__"], +) @@ -3755,26 +3866,34 @@ diff -urN b/go/analysis/passes/internal/analysisutil/BUILD.bazel c/go/analysis/p + +go_test( + name = "analysisutil_test", -+ srcs = ["util_test.go"], ++ srcs = [ ++ "extractdoc_test.go", ++ "util_test.go", ++ ], + deps = [ + ":analysisutil", + "//internal/typeparams", + ], +) diff -urN b/go/analysis/passes/loopclosure/BUILD.bazel c/go/analysis/passes/loopclosure/BUILD.bazel ---- b/go/analysis/passes/loopclosure/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/loopclosure/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/loopclosure/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "loopclosure", -+ srcs = ["loopclosure.go"], ++ srcs = [ ++ "doc.go", ++ "loopclosure.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/loopclosure", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//go/types/typeutil", + ], @@ -3789,6 +3908,7 @@ diff -urN b/go/analysis/passes/loopclosure/BUILD.bazel c/go/analysis/passes/loop +go_test( + name = "loopclosure_test", + srcs = ["loopclosure_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":loopclosure", + "//go/analysis/analysistest", @@ -3796,7 +3916,7 @@ diff -urN b/go/analysis/passes/loopclosure/BUILD.bazel c/go/analysis/passes/loop + ], +) diff -urN b/go/analysis/passes/loopclosure/testdata/src/a/BUILD.bazel c/go/analysis/passes/loopclosure/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/loopclosure/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/loopclosure/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/loopclosure/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3818,7 +3938,7 @@ diff -urN b/go/analysis/passes/loopclosure/testdata/src/a/BUILD.bazel c/go/analy + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/loopclosure/testdata/src/golang.org/x/sync/errgroup/BUILD.bazel c/go/analysis/passes/loopclosure/testdata/src/golang.org/x/sync/errgroup/BUILD.bazel ---- b/go/analysis/passes/loopclosure/testdata/src/golang.org/x/sync/errgroup/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/loopclosure/testdata/src/golang.org/x/sync/errgroup/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/loopclosure/testdata/src/golang.org/x/sync/errgroup/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3836,7 +3956,7 @@ diff -urN b/go/analysis/passes/loopclosure/testdata/src/golang.org/x/sync/errgro + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/loopclosure/testdata/src/subtests/BUILD.bazel c/go/analysis/passes/loopclosure/testdata/src/subtests/BUILD.bazel ---- b/go/analysis/passes/loopclosure/testdata/src/subtests/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/loopclosure/testdata/src/subtests/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/loopclosure/testdata/src/subtests/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3854,7 +3974,7 @@ diff -urN b/go/analysis/passes/loopclosure/testdata/src/subtests/BUILD.bazel c/g + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/loopclosure/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/loopclosure/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/loopclosure/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/loopclosure/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/loopclosure/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3873,20 +3993,25 @@ diff -urN b/go/analysis/passes/loopclosure/testdata/src/typeparams/BUILD.bazel c + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/lostcancel/BUILD.bazel c/go/analysis/passes/lostcancel/BUILD.bazel ---- b/go/analysis/passes/lostcancel/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/lostcancel/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/lostcancel/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,31 @@ +@@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "lostcancel", -+ srcs = ["lostcancel.go"], ++ srcs = [ ++ "doc.go", ++ "lostcancel.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/lostcancel", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/ctrlflow", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//go/cfg", + ], @@ -3901,6 +4026,7 @@ diff -urN b/go/analysis/passes/lostcancel/BUILD.bazel c/go/analysis/passes/lostc +go_test( + name = "lostcancel_test", + srcs = ["lostcancel_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":lostcancel", + "//go/analysis/analysistest", @@ -3908,7 +4034,7 @@ diff -urN b/go/analysis/passes/lostcancel/BUILD.bazel c/go/analysis/passes/lostc + ], +) diff -urN b/go/analysis/passes/lostcancel/cmd/lostcancel/BUILD.bazel c/go/analysis/passes/lostcancel/cmd/lostcancel/BUILD.bazel ---- b/go/analysis/passes/lostcancel/cmd/lostcancel/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/lostcancel/cmd/lostcancel/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/lostcancel/cmd/lostcancel/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -3930,7 +4056,7 @@ diff -urN b/go/analysis/passes/lostcancel/cmd/lostcancel/BUILD.bazel c/go/analys + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/lostcancel/testdata/src/a/BUILD.bazel c/go/analysis/passes/lostcancel/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/lostcancel/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/lostcancel/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/lostcancel/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3948,7 +4074,7 @@ diff -urN b/go/analysis/passes/lostcancel/testdata/src/a/BUILD.bazel c/go/analys + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/lostcancel/testdata/src/b/BUILD.bazel c/go/analysis/passes/lostcancel/testdata/src/b/BUILD.bazel ---- b/go/analysis/passes/lostcancel/testdata/src/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/lostcancel/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/lostcancel/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -3966,7 +4092,7 @@ diff -urN b/go/analysis/passes/lostcancel/testdata/src/b/BUILD.bazel c/go/analys + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/lostcancel/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/lostcancel/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/lostcancel/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/lostcancel/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/lostcancel/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -3984,19 +4110,24 @@ diff -urN b/go/analysis/passes/lostcancel/testdata/src/typeparams/BUILD.bazel c/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/nilfunc/BUILD.bazel c/go/analysis/passes/nilfunc/BUILD.bazel ---- b/go/analysis/passes/nilfunc/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/nilfunc/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/nilfunc/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "nilfunc", -+ srcs = ["nilfunc.go"], ++ srcs = [ ++ "doc.go", ++ "nilfunc.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/nilfunc", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//internal/typeparams", + ], @@ -4018,7 +4149,7 @@ diff -urN b/go/analysis/passes/nilfunc/BUILD.bazel c/go/analysis/passes/nilfunc/ + ], +) diff -urN b/go/analysis/passes/nilfunc/testdata/src/a/BUILD.bazel c/go/analysis/passes/nilfunc/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/nilfunc/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/nilfunc/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/nilfunc/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4036,7 +4167,7 @@ diff -urN b/go/analysis/passes/nilfunc/testdata/src/a/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/nilfunc/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/nilfunc/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/nilfunc/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/nilfunc/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/nilfunc/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4054,19 +4185,24 @@ diff -urN b/go/analysis/passes/nilfunc/testdata/src/typeparams/BUILD.bazel c/go/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/nilness/BUILD.bazel c/go/analysis/passes/nilness/BUILD.bazel ---- b/go/analysis/passes/nilness/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/nilness/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/nilness/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,33 @@ +@@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "nilness", -+ srcs = ["nilness.go"], ++ srcs = [ ++ "doc.go", ++ "nilness.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/nilness", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/buildssa", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ssa", + "//internal/typeparams", + ], @@ -4091,7 +4227,7 @@ diff -urN b/go/analysis/passes/nilness/BUILD.bazel c/go/analysis/passes/nilness/ + ], +) diff -urN b/go/analysis/passes/nilness/cmd/nilness/BUILD.bazel c/go/analysis/passes/nilness/cmd/nilness/BUILD.bazel ---- b/go/analysis/passes/nilness/cmd/nilness/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/nilness/cmd/nilness/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/nilness/cmd/nilness/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -4113,7 +4249,7 @@ diff -urN b/go/analysis/passes/nilness/cmd/nilness/BUILD.bazel c/go/analysis/pas + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/nilness/testdata/src/a/BUILD.bazel c/go/analysis/passes/nilness/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/nilness/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/nilness/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/nilness/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4131,7 +4267,7 @@ diff -urN b/go/analysis/passes/nilness/testdata/src/a/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/nilness/testdata/src/b/BUILD.bazel c/go/analysis/passes/nilness/testdata/src/b/BUILD.bazel ---- b/go/analysis/passes/nilness/testdata/src/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/nilness/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/nilness/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4149,7 +4285,7 @@ diff -urN b/go/analysis/passes/nilness/testdata/src/b/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/nilness/testdata/src/c/BUILD.bazel c/go/analysis/passes/nilness/testdata/src/c/BUILD.bazel ---- b/go/analysis/passes/nilness/testdata/src/c/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/nilness/testdata/src/c/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/nilness/testdata/src/c/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4167,7 +4303,7 @@ diff -urN b/go/analysis/passes/nilness/testdata/src/c/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/nilness/testdata/src/d/BUILD.bazel c/go/analysis/passes/nilness/testdata/src/d/BUILD.bazel ---- b/go/analysis/passes/nilness/testdata/src/d/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/nilness/testdata/src/d/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/nilness/testdata/src/d/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4185,7 +4321,7 @@ diff -urN b/go/analysis/passes/nilness/testdata/src/d/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/pkgfact/BUILD.bazel c/go/analysis/passes/pkgfact/BUILD.bazel ---- b/go/analysis/passes/pkgfact/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/pkgfact/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/pkgfact/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -4213,7 +4349,7 @@ diff -urN b/go/analysis/passes/pkgfact/BUILD.bazel c/go/analysis/passes/pkgfact/ + ], +) diff -urN b/go/analysis/passes/pkgfact/testdata/src/a/BUILD.bazel c/go/analysis/passes/pkgfact/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/pkgfact/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/pkgfact/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/pkgfact/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4231,7 +4367,7 @@ diff -urN b/go/analysis/passes/pkgfact/testdata/src/a/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/pkgfact/testdata/src/b/BUILD.bazel c/go/analysis/passes/pkgfact/testdata/src/b/BUILD.bazel ---- b/go/analysis/passes/pkgfact/testdata/src/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/pkgfact/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/pkgfact/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4249,7 +4385,7 @@ diff -urN b/go/analysis/passes/pkgfact/testdata/src/b/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/pkgfact/testdata/src/c/BUILD.bazel c/go/analysis/passes/pkgfact/testdata/src/c/BUILD.bazel ---- b/go/analysis/passes/pkgfact/testdata/src/c/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/pkgfact/testdata/src/c/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/pkgfact/testdata/src/c/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4267,17 +4403,19 @@ diff -urN b/go/analysis/passes/pkgfact/testdata/src/c/BUILD.bazel c/go/analysis/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/printf/BUILD.bazel c/go/analysis/passes/printf/BUILD.bazel ---- b/go/analysis/passes/printf/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/printf/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/printf/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,35 @@ +@@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "printf", + srcs = [ ++ "doc.go", + "printf.go", + "types.go", + ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/printf", + visibility = ["//visibility:public"], + deps = [ @@ -4302,11 +4440,12 @@ diff -urN b/go/analysis/passes/printf/BUILD.bazel c/go/analysis/passes/printf/BU + deps = [ + ":printf", + "//go/analysis/analysistest", ++ "//internal/testenv", + "//internal/typeparams", + ], +) diff -urN b/go/analysis/passes/printf/testdata/src/a/BUILD.bazel c/go/analysis/passes/printf/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/printf/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/printf/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/printf/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4324,7 +4463,7 @@ diff -urN b/go/analysis/passes/printf/testdata/src/a/BUILD.bazel c/go/analysis/p + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/printf/testdata/src/b/BUILD.bazel c/go/analysis/passes/printf/testdata/src/b/BUILD.bazel ---- b/go/analysis/passes/printf/testdata/src/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/printf/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/printf/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4342,7 +4481,7 @@ diff -urN b/go/analysis/passes/printf/testdata/src/b/BUILD.bazel c/go/analysis/p + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/printf/testdata/src/nofmt/BUILD.bazel c/go/analysis/passes/printf/testdata/src/nofmt/BUILD.bazel ---- b/go/analysis/passes/printf/testdata/src/nofmt/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/printf/testdata/src/nofmt/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/printf/testdata/src/nofmt/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4360,7 +4499,7 @@ diff -urN b/go/analysis/passes/printf/testdata/src/nofmt/BUILD.bazel c/go/analys + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/printf/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/printf/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/printf/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/printf/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/printf/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4381,19 +4520,24 @@ diff -urN b/go/analysis/passes/printf/testdata/src/typeparams/BUILD.bazel c/go/a + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/reflectvaluecompare/BUILD.bazel c/go/analysis/passes/reflectvaluecompare/BUILD.bazel ---- b/go/analysis/passes/reflectvaluecompare/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/reflectvaluecompare/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/reflectvaluecompare/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,29 @@ +@@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "reflectvaluecompare", -+ srcs = ["reflectvaluecompare.go"], ++ srcs = [ ++ "doc.go", ++ "reflectvaluecompare.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/reflectvaluecompare", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//go/types/typeutil", + ], @@ -4414,7 +4558,7 @@ diff -urN b/go/analysis/passes/reflectvaluecompare/BUILD.bazel c/go/analysis/pas + ], +) diff -urN b/go/analysis/passes/reflectvaluecompare/testdata/src/a/BUILD.bazel c/go/analysis/passes/reflectvaluecompare/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/reflectvaluecompare/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/reflectvaluecompare/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/reflectvaluecompare/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4432,19 +4576,24 @@ diff -urN b/go/analysis/passes/reflectvaluecompare/testdata/src/a/BUILD.bazel c/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/shadow/BUILD.bazel c/go/analysis/passes/shadow/BUILD.bazel ---- b/go/analysis/passes/shadow/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/shadow/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/shadow/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "shadow", -+ srcs = ["shadow.go"], ++ srcs = [ ++ "doc.go", ++ "shadow.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/shadow", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + ], +) @@ -4464,7 +4613,7 @@ diff -urN b/go/analysis/passes/shadow/BUILD.bazel c/go/analysis/passes/shadow/BU + ], +) diff -urN b/go/analysis/passes/shadow/cmd/shadow/BUILD.bazel c/go/analysis/passes/shadow/cmd/shadow/BUILD.bazel ---- b/go/analysis/passes/shadow/cmd/shadow/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/shadow/cmd/shadow/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/shadow/cmd/shadow/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -4486,7 +4635,7 @@ diff -urN b/go/analysis/passes/shadow/cmd/shadow/BUILD.bazel c/go/analysis/passe + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/shadow/testdata/src/a/BUILD.bazel c/go/analysis/passes/shadow/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/shadow/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/shadow/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/shadow/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4504,7 +4653,7 @@ diff -urN b/go/analysis/passes/shadow/testdata/src/a/BUILD.bazel c/go/analysis/p + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/shift/BUILD.bazel c/go/analysis/passes/shift/BUILD.bazel ---- b/go/analysis/passes/shift/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/shift/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/shift/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -4542,7 +4691,7 @@ diff -urN b/go/analysis/passes/shift/BUILD.bazel c/go/analysis/passes/shift/BUIL + ], +) diff -urN b/go/analysis/passes/shift/testdata/src/a/BUILD.bazel c/go/analysis/passes/shift/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/shift/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/shift/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/shift/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4560,7 +4709,7 @@ diff -urN b/go/analysis/passes/shift/testdata/src/a/BUILD.bazel c/go/analysis/pa + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/shift/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/shift/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/shift/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/shift/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/shift/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4578,19 +4727,24 @@ diff -urN b/go/analysis/passes/shift/testdata/src/typeparams/BUILD.bazel c/go/an + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/sigchanyzer/BUILD.bazel c/go/analysis/passes/sigchanyzer/BUILD.bazel ---- b/go/analysis/passes/sigchanyzer/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/sigchanyzer/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/sigchanyzer/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "sigchanyzer", -+ srcs = ["sigchanyzer.go"], ++ srcs = [ ++ "doc.go", ++ "sigchanyzer.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/sigchanyzer", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + ], +) @@ -4610,7 +4764,7 @@ diff -urN b/go/analysis/passes/sigchanyzer/BUILD.bazel c/go/analysis/passes/sigc + ], +) diff -urN b/go/analysis/passes/sigchanyzer/testdata/src/a/BUILD.bazel c/go/analysis/passes/sigchanyzer/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/sigchanyzer/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/sigchanyzer/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/sigchanyzer/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4627,20 +4781,96 @@ diff -urN b/go/analysis/passes/sigchanyzer/testdata/src/a/BUILD.bazel c/go/analy + actual = ":a", + visibility = ["//visibility:public"], +) -diff -urN b/go/analysis/passes/sortslice/BUILD.bazel c/go/analysis/passes/sortslice/BUILD.bazel ---- b/go/analysis/passes/sortslice/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/analysis/passes/sortslice/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,29 @@ +diff -urN b/go/analysis/passes/slog/BUILD.bazel c/go/analysis/passes/slog/BUILD.bazel +--- b/go/analysis/passes/slog/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/slog/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( -+ name = "sortslice", ++ name = "slog", ++ srcs = [ ++ "doc.go", ++ "slog.go", ++ ], ++ embedsrcs = ["doc.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/slog", ++ visibility = ["//visibility:public"], ++ deps = [ ++ "//go/analysis", ++ "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", ++ "//go/ast/inspector", ++ "//go/types/typeutil", ++ ], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":slog", ++ visibility = ["//visibility:public"], ++) ++ ++go_test( ++ name = "slog_test", ++ srcs = ["slog_test.go"], ++ embed = [":slog"], ++ deps = [ ++ "//go/analysis/analysistest", ++ "//internal/testenv", ++ ], ++) +diff -urN b/go/analysis/passes/slog/testdata/src/a/BUILD.bazel c/go/analysis/passes/slog/testdata/src/a/BUILD.bazel +--- b/go/analysis/passes/slog/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/slog/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,14 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "a", ++ srcs = ["a.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/slog/testdata/src/a", ++ visibility = ["//visibility:public"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":a", ++ visibility = ["//visibility:public"], ++) +diff -urN b/go/analysis/passes/slog/testdata/src/b/BUILD.bazel c/go/analysis/passes/slog/testdata/src/b/BUILD.bazel +--- b/go/analysis/passes/slog/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/slog/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,14 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "b", ++ srcs = ["b.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/slog/testdata/src/b", ++ visibility = ["//visibility:public"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":b", ++ visibility = ["//visibility:public"], ++) +diff -urN b/go/analysis/passes/sortslice/BUILD.bazel c/go/analysis/passes/sortslice/BUILD.bazel +--- b/go/analysis/passes/sortslice/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/sortslice/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,30 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") ++ ++go_library( ++ name = "sortslice", + srcs = ["analyzer.go"], + importpath = "golang.org/x/tools/go/analysis/passes/sortslice", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//go/types/typeutil", + ], @@ -4661,7 +4891,7 @@ diff -urN b/go/analysis/passes/sortslice/BUILD.bazel c/go/analysis/passes/sortsl + ], +) diff -urN b/go/analysis/passes/sortslice/testdata/src/a/BUILD.bazel c/go/analysis/passes/sortslice/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/sortslice/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/sortslice/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/sortslice/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4679,19 +4909,24 @@ diff -urN b/go/analysis/passes/sortslice/testdata/src/a/BUILD.bazel c/go/analysi + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/stdmethods/BUILD.bazel c/go/analysis/passes/stdmethods/BUILD.bazel ---- b/go/analysis/passes/stdmethods/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/stdmethods/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/stdmethods/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,29 @@ +@@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "stdmethods", -+ srcs = ["stdmethods.go"], ++ srcs = [ ++ "doc.go", ++ "stdmethods.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/stdmethods", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + ], +) @@ -4705,6 +4940,7 @@ diff -urN b/go/analysis/passes/stdmethods/BUILD.bazel c/go/analysis/passes/stdme +go_test( + name = "stdmethods_test", + srcs = ["stdmethods_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":stdmethods", + "//go/analysis/analysistest", @@ -4712,7 +4948,7 @@ diff -urN b/go/analysis/passes/stdmethods/BUILD.bazel c/go/analysis/passes/stdme + ], +) diff -urN b/go/analysis/passes/stdmethods/testdata/src/a/BUILD.bazel c/go/analysis/passes/stdmethods/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/stdmethods/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/stdmethods/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/stdmethods/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4733,7 +4969,7 @@ diff -urN b/go/analysis/passes/stdmethods/testdata/src/a/BUILD.bazel c/go/analys + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/stdmethods/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/stdmethods/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/stdmethods/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/stdmethods/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/stdmethods/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4751,19 +4987,24 @@ diff -urN b/go/analysis/passes/stdmethods/testdata/src/typeparams/BUILD.bazel c/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/stringintconv/BUILD.bazel c/go/analysis/passes/stringintconv/BUILD.bazel ---- b/go/analysis/passes/stringintconv/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/stringintconv/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/stringintconv/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "stringintconv", -+ srcs = ["string.go"], ++ srcs = [ ++ "doc.go", ++ "string.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/stringintconv", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//internal/typeparams", + ], @@ -4778,6 +5019,7 @@ diff -urN b/go/analysis/passes/stringintconv/BUILD.bazel c/go/analysis/passes/st +go_test( + name = "stringintconv_test", + srcs = ["string_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":stringintconv", + "//go/analysis/analysistest", @@ -4785,7 +5027,7 @@ diff -urN b/go/analysis/passes/stringintconv/BUILD.bazel c/go/analysis/passes/st + ], +) diff -urN b/go/analysis/passes/stringintconv/cmd/stringintconv/BUILD.bazel c/go/analysis/passes/stringintconv/cmd/stringintconv/BUILD.bazel ---- b/go/analysis/passes/stringintconv/cmd/stringintconv/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/stringintconv/cmd/stringintconv/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/stringintconv/cmd/stringintconv/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -4807,7 +5049,7 @@ diff -urN b/go/analysis/passes/stringintconv/cmd/stringintconv/BUILD.bazel c/go/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/stringintconv/testdata/src/a/BUILD.bazel c/go/analysis/passes/stringintconv/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/stringintconv/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/stringintconv/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/stringintconv/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4825,7 +5067,7 @@ diff -urN b/go/analysis/passes/stringintconv/testdata/src/a/BUILD.bazel c/go/ana + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/stringintconv/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/stringintconv/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/stringintconv/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/stringintconv/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/stringintconv/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4843,9 +5085,9 @@ diff -urN b/go/analysis/passes/stringintconv/testdata/src/typeparams/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/structtag/BUILD.bazel c/go/analysis/passes/structtag/BUILD.bazel ---- b/go/analysis/passes/structtag/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/structtag/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/structtag/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -4869,56 +5111,61 @@ diff -urN b/go/analysis/passes/structtag/BUILD.bazel c/go/analysis/passes/struct +go_test( + name = "structtag_test", + srcs = ["structtag_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":structtag", + "//go/analysis/analysistest", + ], +) -diff -urN b/go/analysis/passes/structtag/testdata/src/a/b/BUILD.bazel c/go/analysis/passes/structtag/testdata/src/a/b/BUILD.bazel ---- b/go/analysis/passes/structtag/testdata/src/a/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/analysis/passes/structtag/testdata/src/a/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +diff -urN b/go/analysis/passes/structtag/testdata/src/a/BUILD.bazel c/go/analysis/passes/structtag/testdata/src/a/BUILD.bazel +--- b/go/analysis/passes/structtag/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/structtag/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( -+ name = "b", -+ srcs = ["b.go"], -+ importpath = "golang.org/x/tools/go/analysis/passes/structtag/testdata/src/a/b", ++ name = "a", ++ srcs = ["a.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/structtag/testdata/src/a", + visibility = ["//visibility:public"], +) + +alias( + name = "go_default_library", -+ actual = ":b", ++ actual = ":a", + visibility = ["//visibility:public"], +) -diff -urN b/go/analysis/passes/structtag/testdata/src/a/BUILD.bazel c/go/analysis/passes/structtag/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/structtag/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/analysis/passes/structtag/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +diff -urN b/go/analysis/passes/structtag/testdata/src/a/b/BUILD.bazel c/go/analysis/passes/structtag/testdata/src/a/b/BUILD.bazel +--- b/go/analysis/passes/structtag/testdata/src/a/b/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/structtag/testdata/src/a/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( -+ name = "a", -+ srcs = ["a.go"], -+ importpath = "golang.org/x/tools/go/analysis/passes/structtag/testdata/src/a", ++ name = "b", ++ srcs = ["b.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/structtag/testdata/src/a/b", + visibility = ["//visibility:public"], +) + +alias( + name = "go_default_library", -+ actual = ":a", ++ actual = ":b", + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/testinggoroutine/BUILD.bazel c/go/analysis/passes/testinggoroutine/BUILD.bazel ---- b/go/analysis/passes/testinggoroutine/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/testinggoroutine/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/testinggoroutine/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,31 @@ +@@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "testinggoroutine", -+ srcs = ["testinggoroutine.go"], ++ srcs = [ ++ "doc.go", ++ "testinggoroutine.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/testinggoroutine", + visibility = ["//visibility:public"], + deps = [ @@ -4939,6 +5186,7 @@ diff -urN b/go/analysis/passes/testinggoroutine/BUILD.bazel c/go/analysis/passes +go_test( + name = "testinggoroutine_test", + srcs = ["testinggoroutine_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":testinggoroutine", + "//go/analysis/analysistest", @@ -4946,7 +5194,7 @@ diff -urN b/go/analysis/passes/testinggoroutine/BUILD.bazel c/go/analysis/passes + ], +) diff -urN b/go/analysis/passes/testinggoroutine/testdata/src/a/BUILD.bazel c/go/analysis/passes/testinggoroutine/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/testinggoroutine/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/testinggoroutine/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/testinggoroutine/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4967,7 +5215,7 @@ diff -urN b/go/analysis/passes/testinggoroutine/testdata/src/a/BUILD.bazel c/go/ + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/testinggoroutine/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/testinggoroutine/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/testinggoroutine/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/testinggoroutine/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/testinggoroutine/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -4985,19 +5233,23 @@ diff -urN b/go/analysis/passes/testinggoroutine/testdata/src/typeparams/BUILD.ba + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/tests/BUILD.bazel c/go/analysis/passes/tests/BUILD.bazel ---- b/go/analysis/passes/tests/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/tests/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/tests/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "tests", -+ srcs = ["tests.go"], ++ srcs = [ ++ "doc.go", ++ "tests.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/tests", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", -+ "//internal/analysisinternal", ++ "//go/analysis/passes/internal/analysisutil", + "//internal/typeparams", + ], +) @@ -5011,15 +5263,15 @@ diff -urN b/go/analysis/passes/tests/BUILD.bazel c/go/analysis/passes/tests/BUIL +go_test( + name = "tests_test", + srcs = ["tests_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":tests", + "//go/analysis/analysistest", -+ "//internal/analysisinternal", + "//internal/typeparams", + ], +) diff -urN b/go/analysis/passes/tests/testdata/src/a/BUILD.bazel c/go/analysis/passes/tests/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/tests/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/tests/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/tests/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -5047,7 +5299,7 @@ diff -urN b/go/analysis/passes/tests/testdata/src/a/BUILD.bazel c/go/analysis/pa + embed = [":a"], +) diff -urN b/go/analysis/passes/tests/testdata/src/b/BUILD.bazel c/go/analysis/passes/tests/testdata/src/b/BUILD.bazel ---- b/go/analysis/passes/tests/testdata/src/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/tests/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/tests/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5065,7 +5317,7 @@ diff -urN b/go/analysis/passes/tests/testdata/src/b/BUILD.bazel c/go/analysis/pa + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/tests/testdata/src/b_x_test/BUILD.bazel c/go/analysis/passes/tests/testdata/src/b_x_test/BUILD.bazel ---- b/go/analysis/passes/tests/testdata/src/b_x_test/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/tests/testdata/src/b_x_test/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/tests/testdata/src/b_x_test/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,6 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") @@ -5075,7 +5327,7 @@ diff -urN b/go/analysis/passes/tests/testdata/src/b_x_test/BUILD.bazel c/go/anal + srcs = ["b_test.go"], +) diff -urN b/go/analysis/passes/tests/testdata/src/divergent/BUILD.bazel c/go/analysis/passes/tests/testdata/src/divergent/BUILD.bazel ---- b/go/analysis/passes/tests/testdata/src/divergent/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/tests/testdata/src/divergent/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/tests/testdata/src/divergent/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -5099,7 +5351,7 @@ diff -urN b/go/analysis/passes/tests/testdata/src/divergent/BUILD.bazel c/go/ana + embed = [":divergent"], +) diff -urN b/go/analysis/passes/tests/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/tests/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/tests/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/tests/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/tests/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -5123,19 +5375,24 @@ diff -urN b/go/analysis/passes/tests/testdata/src/typeparams/BUILD.bazel c/go/an + embed = [":typeparams"], +) diff -urN b/go/analysis/passes/timeformat/BUILD.bazel c/go/analysis/passes/timeformat/BUILD.bazel ---- b/go/analysis/passes/timeformat/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/timeformat/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/timeformat/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,29 @@ +@@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "timeformat", -+ srcs = ["timeformat.go"], ++ srcs = [ ++ "doc.go", ++ "timeformat.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/timeformat", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//go/types/typeutil", + ], @@ -5150,13 +5407,14 @@ diff -urN b/go/analysis/passes/timeformat/BUILD.bazel c/go/analysis/passes/timef +go_test( + name = "timeformat_test", + srcs = ["timeformat_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":timeformat", + "//go/analysis/analysistest", + ], +) diff -urN b/go/analysis/passes/timeformat/testdata/src/a/BUILD.bazel c/go/analysis/passes/timeformat/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/timeformat/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/timeformat/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/timeformat/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5174,7 +5432,7 @@ diff -urN b/go/analysis/passes/timeformat/testdata/src/a/BUILD.bazel c/go/analys + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/timeformat/testdata/src/b/BUILD.bazel c/go/analysis/passes/timeformat/testdata/src/b/BUILD.bazel ---- b/go/analysis/passes/timeformat/testdata/src/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/timeformat/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/timeformat/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5192,19 +5450,24 @@ diff -urN b/go/analysis/passes/timeformat/testdata/src/b/BUILD.bazel c/go/analys + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/unmarshal/BUILD.bazel c/go/analysis/passes/unmarshal/BUILD.bazel ---- b/go/analysis/passes/unmarshal/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unmarshal/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unmarshal/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,31 @@ +@@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "unmarshal", -+ srcs = ["unmarshal.go"], ++ srcs = [ ++ "doc.go", ++ "unmarshal.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/unmarshal", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//go/types/typeutil", + "//internal/typeparams", @@ -5220,6 +5483,7 @@ diff -urN b/go/analysis/passes/unmarshal/BUILD.bazel c/go/analysis/passes/unmars +go_test( + name = "unmarshal_test", + srcs = ["unmarshal_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":unmarshal", + "//go/analysis/analysistest", @@ -5227,7 +5491,7 @@ diff -urN b/go/analysis/passes/unmarshal/BUILD.bazel c/go/analysis/passes/unmars + ], +) diff -urN b/go/analysis/passes/unmarshal/cmd/unmarshal/BUILD.bazel c/go/analysis/passes/unmarshal/cmd/unmarshal/BUILD.bazel ---- b/go/analysis/passes/unmarshal/cmd/unmarshal/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unmarshal/cmd/unmarshal/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unmarshal/cmd/unmarshal/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -5249,7 +5513,7 @@ diff -urN b/go/analysis/passes/unmarshal/cmd/unmarshal/BUILD.bazel c/go/analysis + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/unmarshal/testdata/src/a/BUILD.bazel c/go/analysis/passes/unmarshal/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/unmarshal/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unmarshal/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unmarshal/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5267,7 +5531,7 @@ diff -urN b/go/analysis/passes/unmarshal/testdata/src/a/BUILD.bazel c/go/analysi + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/unmarshal/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/unmarshal/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/unmarshal/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unmarshal/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unmarshal/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5285,19 +5549,24 @@ diff -urN b/go/analysis/passes/unmarshal/testdata/src/typeparams/BUILD.bazel c/g + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/unreachable/BUILD.bazel c/go/analysis/passes/unreachable/BUILD.bazel ---- b/go/analysis/passes/unreachable/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unreachable/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unreachable/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "unreachable", -+ srcs = ["unreachable.go"], ++ srcs = [ ++ "doc.go", ++ "unreachable.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/unreachable", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + ], +) @@ -5311,13 +5580,14 @@ diff -urN b/go/analysis/passes/unreachable/BUILD.bazel c/go/analysis/passes/unre +go_test( + name = "unreachable_test", + srcs = ["unreachable_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":unreachable", + "//go/analysis/analysistest", + ], +) diff -urN b/go/analysis/passes/unreachable/testdata/src/a/BUILD.bazel c/go/analysis/passes/unreachable/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/unreachable/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unreachable/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unreachable/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5335,14 +5605,18 @@ diff -urN b/go/analysis/passes/unreachable/testdata/src/a/BUILD.bazel c/go/analy + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/unsafeptr/BUILD.bazel c/go/analysis/passes/unsafeptr/BUILD.bazel ---- b/go/analysis/passes/unsafeptr/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unsafeptr/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unsafeptr/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "unsafeptr", -+ srcs = ["unsafeptr.go"], ++ srcs = [ ++ "doc.go", ++ "unsafeptr.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/unsafeptr", + visibility = ["//visibility:public"], + deps = [ @@ -5362,6 +5636,7 @@ diff -urN b/go/analysis/passes/unsafeptr/BUILD.bazel c/go/analysis/passes/unsafe +go_test( + name = "unsafeptr_test", + srcs = ["unsafeptr_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":unsafeptr", + "//go/analysis/analysistest", @@ -5369,7 +5644,7 @@ diff -urN b/go/analysis/passes/unsafeptr/BUILD.bazel c/go/analysis/passes/unsafe + ], +) diff -urN b/go/analysis/passes/unsafeptr/testdata/src/a/BUILD.bazel c/go/analysis/passes/unsafeptr/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/unsafeptr/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unsafeptr/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unsafeptr/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5390,7 +5665,7 @@ diff -urN b/go/analysis/passes/unsafeptr/testdata/src/a/BUILD.bazel c/go/analysi + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/unsafeptr/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/unsafeptr/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/unsafeptr/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unsafeptr/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unsafeptr/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5408,14 +5683,18 @@ diff -urN b/go/analysis/passes/unsafeptr/testdata/src/typeparams/BUILD.bazel c/g + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/unusedresult/BUILD.bazel c/go/analysis/passes/unusedresult/BUILD.bazel ---- b/go/analysis/passes/unusedresult/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unusedresult/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unusedresult/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,31 @@ +@@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "unusedresult", -+ srcs = ["unusedresult.go"], ++ srcs = [ ++ "doc.go", ++ "unusedresult.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/unusedresult", + visibility = ["//visibility:public"], + deps = [ @@ -5423,7 +5702,7 @@ diff -urN b/go/analysis/passes/unusedresult/BUILD.bazel c/go/analysis/passes/unu + "//go/analysis/passes/inspect", + "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", -+ "//internal/typeparams", ++ "//go/types/typeutil", + ], +) + @@ -5436,14 +5715,37 @@ diff -urN b/go/analysis/passes/unusedresult/BUILD.bazel c/go/analysis/passes/unu +go_test( + name = "unusedresult_test", + srcs = ["unusedresult_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":unusedresult", + "//go/analysis/analysistest", + "//internal/typeparams", + ], +) +diff -urN b/go/analysis/passes/unusedresult/cmd/unusedresult/BUILD.bazel c/go/analysis/passes/unusedresult/cmd/unusedresult/BUILD.bazel +--- b/go/analysis/passes/unusedresult/cmd/unusedresult/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/analysis/passes/unusedresult/cmd/unusedresult/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,18 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") ++ ++go_library( ++ name = "unusedresult_lib", ++ srcs = ["main.go"], ++ importpath = "golang.org/x/tools/go/analysis/passes/unusedresult/cmd/unusedresult", ++ visibility = ["//visibility:private"], ++ deps = [ ++ "//go/analysis/passes/unusedresult", ++ "//go/analysis/singlechecker", ++ ], ++) ++ ++go_binary( ++ name = "unusedresult", ++ embed = [":unusedresult_lib"], ++ visibility = ["//visibility:public"], ++) diff -urN b/go/analysis/passes/unusedresult/testdata/src/a/BUILD.bazel c/go/analysis/passes/unusedresult/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/unusedresult/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unusedresult/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unusedresult/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5461,7 +5763,7 @@ diff -urN b/go/analysis/passes/unusedresult/testdata/src/a/BUILD.bazel c/go/anal + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/unusedresult/testdata/src/typeparams/BUILD.bazel c/go/analysis/passes/unusedresult/testdata/src/typeparams/BUILD.bazel ---- b/go/analysis/passes/unusedresult/testdata/src/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unusedresult/testdata/src/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unusedresult/testdata/src/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5479,7 +5781,7 @@ diff -urN b/go/analysis/passes/unusedresult/testdata/src/typeparams/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/unusedresult/testdata/src/typeparams/userdefs/BUILD.bazel c/go/analysis/passes/unusedresult/testdata/src/typeparams/userdefs/BUILD.bazel ---- b/go/analysis/passes/unusedresult/testdata/src/typeparams/userdefs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unusedresult/testdata/src/typeparams/userdefs/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unusedresult/testdata/src/typeparams/userdefs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5497,19 +5799,24 @@ diff -urN b/go/analysis/passes/unusedresult/testdata/src/typeparams/userdefs/BUI + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/unusedwrite/BUILD.bazel c/go/analysis/passes/unusedwrite/BUILD.bazel ---- b/go/analysis/passes/unusedwrite/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unusedwrite/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unusedwrite/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "unusedwrite", -+ srcs = ["unusedwrite.go"], ++ srcs = [ ++ "doc.go", ++ "unusedwrite.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/unusedwrite", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/buildssa", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ssa", + ], +) @@ -5523,13 +5830,14 @@ diff -urN b/go/analysis/passes/unusedwrite/BUILD.bazel c/go/analysis/passes/unus +go_test( + name = "unusedwrite_test", + srcs = ["unusedwrite_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":unusedwrite", + "//go/analysis/analysistest", + ], +) diff -urN b/go/analysis/passes/unusedwrite/testdata/src/a/BUILD.bazel c/go/analysis/passes/unusedwrite/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/unusedwrite/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/unusedwrite/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/unusedwrite/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5547,19 +5855,24 @@ diff -urN b/go/analysis/passes/unusedwrite/testdata/src/a/BUILD.bazel c/go/analy + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/usesgenerics/BUILD.bazel c/go/analysis/passes/usesgenerics/BUILD.bazel ---- b/go/analysis/passes/usesgenerics/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/usesgenerics/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/usesgenerics/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,30 @@ +@@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "usesgenerics", -+ srcs = ["usesgenerics.go"], ++ srcs = [ ++ "doc.go", ++ "usesgenerics.go", ++ ], ++ embedsrcs = ["doc.go"], + importpath = "golang.org/x/tools/go/analysis/passes/usesgenerics", + visibility = ["//visibility:public"], + deps = [ + "//go/analysis", + "//go/analysis/passes/inspect", ++ "//go/analysis/passes/internal/analysisutil", + "//go/ast/inspector", + "//internal/typeparams/genericfeatures", + ], @@ -5574,6 +5887,7 @@ diff -urN b/go/analysis/passes/usesgenerics/BUILD.bazel c/go/analysis/passes/use +go_test( + name = "usesgenerics_test", + srcs = ["usesgenerics_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":usesgenerics", + "//go/analysis/analysistest", @@ -5581,7 +5895,7 @@ diff -urN b/go/analysis/passes/usesgenerics/BUILD.bazel c/go/analysis/passes/use + ], +) diff -urN b/go/analysis/passes/usesgenerics/testdata/src/a/BUILD.bazel c/go/analysis/passes/usesgenerics/testdata/src/a/BUILD.bazel ---- b/go/analysis/passes/usesgenerics/testdata/src/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/usesgenerics/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/usesgenerics/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5599,7 +5913,7 @@ diff -urN b/go/analysis/passes/usesgenerics/testdata/src/a/BUILD.bazel c/go/anal + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/usesgenerics/testdata/src/b/BUILD.bazel c/go/analysis/passes/usesgenerics/testdata/src/b/BUILD.bazel ---- b/go/analysis/passes/usesgenerics/testdata/src/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/usesgenerics/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/usesgenerics/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5617,7 +5931,7 @@ diff -urN b/go/analysis/passes/usesgenerics/testdata/src/b/BUILD.bazel c/go/anal + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/usesgenerics/testdata/src/c/BUILD.bazel c/go/analysis/passes/usesgenerics/testdata/src/c/BUILD.bazel ---- b/go/analysis/passes/usesgenerics/testdata/src/c/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/usesgenerics/testdata/src/c/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/usesgenerics/testdata/src/c/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5635,7 +5949,7 @@ diff -urN b/go/analysis/passes/usesgenerics/testdata/src/c/BUILD.bazel c/go/anal + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/passes/usesgenerics/testdata/src/d/BUILD.bazel c/go/analysis/passes/usesgenerics/testdata/src/d/BUILD.bazel ---- b/go/analysis/passes/usesgenerics/testdata/src/d/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/passes/usesgenerics/testdata/src/d/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/passes/usesgenerics/testdata/src/d/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5653,7 +5967,7 @@ diff -urN b/go/analysis/passes/usesgenerics/testdata/src/d/BUILD.bazel c/go/anal + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/singlechecker/BUILD.bazel c/go/analysis/singlechecker/BUILD.bazel ---- b/go/analysis/singlechecker/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/singlechecker/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/singlechecker/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -5677,17 +5991,14 @@ diff -urN b/go/analysis/singlechecker/BUILD.bazel c/go/analysis/singlechecker/BU + visibility = ["//visibility:public"], +) diff -urN b/go/analysis/unitchecker/BUILD.bazel c/go/analysis/unitchecker/BUILD.bazel ---- b/go/analysis/unitchecker/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/analysis/unitchecker/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/analysis/unitchecker/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,35 @@ +@@ -0,0 +1,69 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "unitchecker", -+ srcs = [ -+ "unitchecker.go", -+ "unitchecker112.go", -+ ], ++ srcs = ["unitchecker.go"], + importpath = "golang.org/x/tools/go/analysis/unitchecker", + visibility = ["//visibility:public"], + deps = [ @@ -5706,17 +6017,54 @@ diff -urN b/go/analysis/unitchecker/BUILD.bazel c/go/analysis/unitchecker/BUILD. + +go_test( + name = "unitchecker_test", -+ srcs = ["unitchecker_test.go"], ++ srcs = [ ++ "export_test.go", ++ "separate_test.go", ++ "unitchecker_test.go", ++ "vet_std_test.go", ++ ], ++ embed = [":unitchecker"], + deps = [ -+ ":unitchecker", ++ "//go/analysis/passes/appends", ++ "//go/analysis/passes/asmdecl", + "//go/analysis/passes/assign", ++ "//go/analysis/passes/atomic", ++ "//go/analysis/passes/bools", ++ "//go/analysis/passes/buildtag", ++ "//go/analysis/passes/cgocall", ++ "//go/analysis/passes/composite", ++ "//go/analysis/passes/copylock", ++ "//go/analysis/passes/defers", ++ "//go/analysis/passes/directive", ++ "//go/analysis/passes/errorsas", + "//go/analysis/passes/findcall", ++ "//go/analysis/passes/framepointer", ++ "//go/analysis/passes/httpresponse", ++ "//go/analysis/passes/ifaceassert", ++ "//go/analysis/passes/loopclosure", ++ "//go/analysis/passes/lostcancel", ++ "//go/analysis/passes/nilfunc", + "//go/analysis/passes/printf", ++ "//go/analysis/passes/shift", ++ "//go/analysis/passes/sigchanyzer", ++ "//go/analysis/passes/stdmethods", ++ "//go/analysis/passes/stringintconv", ++ "//go/analysis/passes/structtag", ++ "//go/analysis/passes/testinggoroutine", ++ "//go/analysis/passes/tests", ++ "//go/analysis/passes/timeformat", ++ "//go/analysis/passes/unmarshal", ++ "//go/analysis/passes/unreachable", ++ "//go/analysis/passes/unusedresult", ++ "//go/gcexportdata", ++ "//go/packages", + "//go/packages/packagestest", ++ "//internal/testenv", ++ "//txtar", + ], +) diff -urN b/go/ast/astutil/BUILD.bazel c/go/ast/astutil/BUILD.bazel ---- b/go/ast/astutil/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ast/astutil/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ast/astutil/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -5751,7 +6099,7 @@ diff -urN b/go/ast/astutil/BUILD.bazel c/go/ast/astutil/BUILD.bazel + deps = ["//internal/typeparams"], +) diff -urN b/go/ast/inspector/BUILD.bazel c/go/ast/inspector/BUILD.bazel ---- b/go/ast/inspector/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ast/inspector/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ast/inspector/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -5782,7 +6130,7 @@ diff -urN b/go/ast/inspector/BUILD.bazel c/go/ast/inspector/BUILD.bazel + ], +) diff -urN b/go/buildutil/BUILD.bazel c/go/buildutil/BUILD.bazel ---- b/go/buildutil/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/buildutil/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/buildutil/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -5821,9 +6169,9 @@ diff -urN b/go/buildutil/BUILD.bazel c/go/buildutil/BUILD.bazel + ], +) diff -urN b/go/callgraph/BUILD.bazel c/go/callgraph/BUILD.bazel ---- b/go/callgraph/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/callgraph/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/callgraph/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,34 @@ +@@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -5853,13 +6201,12 @@ diff -urN b/go/callgraph/BUILD.bazel c/go/callgraph/BUILD.bazel + "//go/callgraph/static", + "//go/callgraph/vta", + "//go/loader", -+ "//go/pointer", + "//go/ssa", + "//go/ssa/ssautil", + ], +) diff -urN b/go/callgraph/cha/BUILD.bazel c/go/callgraph/cha/BUILD.bazel ---- b/go/callgraph/cha/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/callgraph/cha/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/callgraph/cha/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,132 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -5995,7 +6342,7 @@ diff -urN b/go/callgraph/cha/BUILD.bazel c/go/callgraph/cha/BUILD.bazel + }), +) diff -urN b/go/callgraph/cha/testdata/BUILD.bazel c/go/callgraph/cha/testdata/BUILD.bazel ---- b/go/callgraph/cha/testdata/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/callgraph/cha/testdata/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/callgraph/cha/testdata/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -6013,9 +6360,9 @@ diff -urN b/go/callgraph/cha/testdata/BUILD.bazel c/go/callgraph/cha/testdata/BU + visibility = ["//visibility:public"], +) diff -urN b/go/callgraph/rta/BUILD.bazel c/go/callgraph/rta/BUILD.bazel ---- b/go/callgraph/rta/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/callgraph/rta/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/callgraph/rta/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,132 @@ +@@ -0,0 +1,133 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -6027,6 +6374,7 @@ diff -urN b/go/callgraph/rta/BUILD.bazel c/go/callgraph/rta/BUILD.bazel + "//go/callgraph", + "//go/ssa", + "//go/types/typeutil", ++ "//internal/compat", + ], +) + @@ -6039,7 +6387,7 @@ diff -urN b/go/callgraph/rta/BUILD.bazel c/go/callgraph/rta/BUILD.bazel +go_test( + name = "rta_test", + srcs = ["rta_test.go"], -+ data = glob(["testdata/**"]), ++ data = glob(["testdata/**"], allow_empty = True), + deps = select({ + "@io_bazel_rules_go//go/platform:aix": [ + ":rta", @@ -6149,7 +6497,7 @@ diff -urN b/go/callgraph/rta/BUILD.bazel c/go/callgraph/rta/BUILD.bazel + }), +) diff -urN b/go/callgraph/static/BUILD.bazel c/go/callgraph/static/BUILD.bazel ---- b/go/callgraph/static/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/callgraph/static/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/callgraph/static/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -6185,7 +6533,7 @@ diff -urN b/go/callgraph/static/BUILD.bazel c/go/callgraph/static/BUILD.bazel + ], +) diff -urN b/go/callgraph/vta/BUILD.bazel c/go/callgraph/vta/BUILD.bazel ---- b/go/callgraph/vta/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/callgraph/vta/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/callgraph/vta/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,50 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -6239,7 +6587,7 @@ diff -urN b/go/callgraph/vta/BUILD.bazel c/go/callgraph/vta/BUILD.bazel + ], +) diff -urN b/go/callgraph/vta/internal/trie/BUILD.bazel c/go/callgraph/vta/internal/trie/BUILD.bazel ---- b/go/callgraph/vta/internal/trie/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/callgraph/vta/internal/trie/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/callgraph/vta/internal/trie/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -6272,14 +6620,15 @@ diff -urN b/go/callgraph/vta/internal/trie/BUILD.bazel c/go/callgraph/vta/intern + embed = [":trie"], +) diff -urN b/go/callgraph/vta/testdata/src/BUILD.bazel c/go/callgraph/vta/testdata/src/BUILD.bazel ---- b/go/callgraph/vta/testdata/src/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/callgraph/vta/testdata/src/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/callgraph/vta/testdata/src/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,47 @@ +@@ -0,0 +1,48 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "src", + srcs = [ ++ "arrays_generics.go", + "callgraph_collections.go", + "callgraph_field_funcs.go", + "callgraph_fields.go", @@ -6323,7 +6672,7 @@ diff -urN b/go/callgraph/vta/testdata/src/BUILD.bazel c/go/callgraph/vta/testdat + visibility = ["//visibility:public"], +) diff -urN b/go/callgraph/vta/testdata/src/d/BUILD.bazel c/go/callgraph/vta/testdata/src/d/BUILD.bazel ---- b/go/callgraph/vta/testdata/src/d/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/callgraph/vta/testdata/src/d/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/callgraph/vta/testdata/src/d/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6341,7 +6690,7 @@ diff -urN b/go/callgraph/vta/testdata/src/d/BUILD.bazel c/go/callgraph/vta/testd + visibility = ["//visibility:public"], +) diff -urN b/go/callgraph/vta/testdata/src/t/BUILD.bazel c/go/callgraph/vta/testdata/src/t/BUILD.bazel ---- b/go/callgraph/vta/testdata/src/t/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/callgraph/vta/testdata/src/t/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/callgraph/vta/testdata/src/t/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6359,7 +6708,7 @@ diff -urN b/go/callgraph/vta/testdata/src/t/BUILD.bazel c/go/callgraph/vta/testd + visibility = ["//visibility:public"], +) diff -urN b/go/cfg/BUILD.bazel c/go/cfg/BUILD.bazel ---- b/go/cfg/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/cfg/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/cfg/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -6386,7 +6735,7 @@ diff -urN b/go/cfg/BUILD.bazel c/go/cfg/BUILD.bazel + embed = [":cfg"], +) diff -urN b/go/expect/BUILD.bazel c/go/expect/BUILD.bazel ---- b/go/expect/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/expect/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/expect/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -6414,7 +6763,7 @@ diff -urN b/go/expect/BUILD.bazel c/go/expect/BUILD.bazel + deps = [":expect"], +) diff -urN b/go/expect/testdata/BUILD.bazel c/go/expect/testdata/BUILD.bazel ---- b/go/expect/testdata/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/expect/testdata/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/expect/testdata/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6432,7 +6781,7 @@ diff -urN b/go/expect/testdata/BUILD.bazel c/go/expect/testdata/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/go/gccgoexportdata/BUILD.bazel c/go/gccgoexportdata/BUILD.bazel ---- b/go/gccgoexportdata/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/gccgoexportdata/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/gccgoexportdata/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -6454,13 +6803,13 @@ diff -urN b/go/gccgoexportdata/BUILD.bazel c/go/gccgoexportdata/BUILD.bazel +go_test( + name = "gccgoexportdata_test", + srcs = ["gccgoexportdata_test.go"], -+ data = glob(["testdata/**"]), ++ data = glob(["testdata/**"], allow_empty = True), + deps = [":gccgoexportdata"], +) diff -urN b/go/gcexportdata/BUILD.bazel c/go/gcexportdata/BUILD.bazel ---- b/go/gcexportdata/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/gcexportdata/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/gcexportdata/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,59 @@ +@@ -0,0 +1,56 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -6496,9 +6845,6 @@ diff -urN b/go/gcexportdata/BUILD.bazel c/go/gcexportdata/BUILD.bazel + "@io_bazel_rules_go//go/platform:freebsd": [ + ":gcexportdata", + ], -+ "@io_bazel_rules_go//go/platform:illumos": [ -+ ":gcexportdata", -+ ], + "@io_bazel_rules_go//go/platform:linux": [ + ":gcexportdata", + ], @@ -6521,7 +6867,7 @@ diff -urN b/go/gcexportdata/BUILD.bazel c/go/gcexportdata/BUILD.bazel + }), +) diff -urN b/go/internal/cgo/BUILD.bazel c/go/internal/cgo/BUILD.bazel ---- b/go/internal/cgo/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/internal/cgo/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/internal/cgo/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6543,9 +6889,9 @@ diff -urN b/go/internal/cgo/BUILD.bazel c/go/internal/cgo/BUILD.bazel + visibility = ["//go:__subpackages__"], +) diff -urN b/go/internal/gccgoimporter/BUILD.bazel c/go/internal/gccgoimporter/BUILD.bazel ---- b/go/internal/gccgoimporter/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/internal/gccgoimporter/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/internal/gccgoimporter/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,35 @@ +@@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -6578,11 +6924,12 @@ diff -urN b/go/internal/gccgoimporter/BUILD.bazel c/go/internal/gccgoimporter/BU + "parser_test.go", + "testenv_test.go", + ], -+ data = glob(["testdata/**"]), ++ data = glob(["testdata/**"], allow_empty = True), + embed = [":gccgoimporter"], ++ deps = ["//internal/testenv"], +) diff -urN b/go/internal/packagesdriver/BUILD.bazel c/go/internal/packagesdriver/BUILD.bazel ---- b/go/internal/packagesdriver/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/internal/packagesdriver/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/internal/packagesdriver/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6601,7 +6948,7 @@ diff -urN b/go/internal/packagesdriver/BUILD.bazel c/go/internal/packagesdriver/ + visibility = ["//go:__subpackages__"], +) diff -urN b/go/loader/BUILD.bazel c/go/loader/BUILD.bazel ---- b/go/loader/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/loader/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/loader/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -6642,7 +6989,7 @@ diff -urN b/go/loader/BUILD.bazel c/go/loader/BUILD.bazel + ], +) diff -urN b/go/loader/testdata/BUILD.bazel c/go/loader/testdata/BUILD.bazel ---- b/go/loader/testdata/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/loader/testdata/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/loader/testdata/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6664,7 +7011,7 @@ diff -urN b/go/loader/testdata/BUILD.bazel c/go/loader/testdata/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/go/loader/testdata/issue46877/BUILD.bazel c/go/loader/testdata/issue46877/BUILD.bazel ---- b/go/loader/testdata/issue46877/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/loader/testdata/issue46877/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/loader/testdata/issue46877/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6686,7 +7033,7 @@ diff -urN b/go/loader/testdata/issue46877/BUILD.bazel c/go/loader/testdata/issue + visibility = ["//visibility:public"], +) diff -urN b/go/packages/BUILD.bazel c/go/packages/BUILD.bazel ---- b/go/packages/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,47 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -6737,7 +7084,7 @@ diff -urN b/go/packages/BUILD.bazel c/go/packages/BUILD.bazel + ], +) diff -urN b/go/packages/gopackages/BUILD.bazel c/go/packages/gopackages/BUILD.bazel ---- b/go/packages/gopackages/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/gopackages/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/gopackages/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -6759,8 +7106,27 @@ diff -urN b/go/packages/gopackages/BUILD.bazel c/go/packages/gopackages/BUILD.ba + embed = [":gopackages_lib"], + visibility = ["//visibility:public"], +) +diff -urN b/go/packages/internal/nodecount/BUILD.bazel c/go/packages/internal/nodecount/BUILD.bazel +--- b/go/packages/internal/nodecount/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/packages/internal/nodecount/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,15 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") ++ ++go_library( ++ name = "nodecount_lib", ++ srcs = ["nodecount.go"], ++ importpath = "golang.org/x/tools/go/packages/internal/nodecount", ++ visibility = ["//visibility:private"], ++ deps = ["//go/packages"], ++) ++ ++go_binary( ++ name = "nodecount", ++ embed = [":nodecount_lib"], ++ visibility = ["//go/packages:__subpackages__"], ++) diff -urN b/go/packages/packagestest/BUILD.bazel c/go/packages/packagestest/BUILD.bazel ---- b/go/packages/packagestest/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,42 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -6806,7 +7172,7 @@ diff -urN b/go/packages/packagestest/BUILD.bazel c/go/packages/packagestest/BUIL + ], +) diff -urN b/go/packages/packagestest/testdata/BUILD.bazel c/go/packages/packagestest/testdata/BUILD.bazel ---- b/go/packages/packagestest/testdata/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -6833,7 +7199,7 @@ diff -urN b/go/packages/packagestest/testdata/BUILD.bazel c/go/packages/packages + embed = [":testdata"], +) diff -urN b/go/packages/packagestest/testdata/groups/one/modules/example.com/extra/BUILD.bazel c/go/packages/packagestest/testdata/groups/one/modules/example.com/extra/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/one/modules/example.com/extra/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/one/modules/example.com/extra/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/one/modules/example.com/extra/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6851,7 +7217,7 @@ diff -urN b/go/packages/packagestest/testdata/groups/one/modules/example.com/ext + visibility = ["//visibility:public"], +) diff -urN b/go/packages/packagestest/testdata/groups/one/primarymod/BUILD.bazel c/go/packages/packagestest/testdata/groups/one/primarymod/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/one/primarymod/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/one/primarymod/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/one/primarymod/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6869,7 +7235,7 @@ diff -urN b/go/packages/packagestest/testdata/groups/one/primarymod/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/BUILD.bazel c/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6887,7 +7253,7 @@ diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/ext + visibility = ["//visibility:public"], +) diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/geez/BUILD.bazel c/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/geez/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/geez/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/geez/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/geez/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6905,7 +7271,7 @@ diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/ext + visibility = ["//visibility:public"], +) diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/v2/BUILD.bazel c/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/v2/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/v2/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/v2/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/v2/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6923,7 +7289,7 @@ diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/ext + visibility = ["//visibility:public"], +) diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/v2/geez/BUILD.bazel c/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/v2/geez/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/v2/geez/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/v2/geez/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/two/modules/example.com/extra/v2/geez/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6941,7 +7307,7 @@ diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/ext + visibility = ["//visibility:public"], +) diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/tempmod/BUILD.bazel c/go/packages/packagestest/testdata/groups/two/modules/example.com/tempmod/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/two/modules/example.com/tempmod/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/two/modules/example.com/tempmod/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/two/modules/example.com/tempmod/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6959,7 +7325,7 @@ diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/tem + visibility = ["//visibility:public"], +) diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/what@v1.0.0/BUILD.bazel c/go/packages/packagestest/testdata/groups/two/modules/example.com/what@v1.0.0/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/two/modules/example.com/what@v1.0.0/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/two/modules/example.com/what@v1.0.0/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/two/modules/example.com/what@v1.0.0/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6977,7 +7343,7 @@ diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/wha + visibility = ["//visibility:public"], +) diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/what@v1.1.0/BUILD.bazel c/go/packages/packagestest/testdata/groups/two/modules/example.com/what@v1.1.0/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/two/modules/example.com/what@v1.1.0/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/two/modules/example.com/what@v1.1.0/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/two/modules/example.com/what@v1.1.0/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -6995,7 +7361,7 @@ diff -urN b/go/packages/packagestest/testdata/groups/two/modules/example.com/wha + visibility = ["//visibility:public"], +) diff -urN b/go/packages/packagestest/testdata/groups/two/primarymod/BUILD.bazel c/go/packages/packagestest/testdata/groups/two/primarymod/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/two/primarymod/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/two/primarymod/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/two/primarymod/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7013,7 +7379,7 @@ diff -urN b/go/packages/packagestest/testdata/groups/two/primarymod/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/go/packages/packagestest/testdata/groups/two/primarymod/expect/BUILD.bazel c/go/packages/packagestest/testdata/groups/two/primarymod/expect/BUILD.bazel ---- b/go/packages/packagestest/testdata/groups/two/primarymod/expect/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/packages/packagestest/testdata/groups/two/primarymod/expect/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/packages/packagestest/testdata/groups/two/primarymod/expect/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -7035,230 +7401,81 @@ diff -urN b/go/packages/packagestest/testdata/groups/two/primarymod/expect/BUILD + name = "expect_test", + srcs = ["yo_test.go"], +) -diff -urN b/go/pointer/BUILD.bazel c/go/pointer/BUILD.bazel ---- b/go/pointer/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/pointer/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,124 @@ +diff -urN b/go/ssa/BUILD.bazel c/go/ssa/BUILD.bazel +--- b/go/ssa/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/ssa/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,118 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( -+ name = "pointer", ++ name = "ssa", + srcs = [ -+ "analysis.go", -+ "api.go", -+ "callgraph.go", -+ "constraint.go", ++ "block.go", ++ "blockopt.go", ++ "builder.go", ++ "const.go", ++ "coretype.go", ++ "create.go", + "doc.go", -+ "gen.go", -+ "hvn.go", -+ "intrinsics.go", -+ "labels.go", -+ "opt.go", ++ "dom.go", ++ "emit.go", ++ "func.go", ++ "identical.go", ++ "identical_17.go", ++ "instantiate.go", ++ "lift.go", ++ "lvalue.go", ++ "methods.go", ++ "mode.go", ++ "parameterized.go", + "print.go", -+ "query.go", -+ "reflect.go", -+ "solve.go", ++ "sanity.go", ++ "source.go", ++ "ssa.go", ++ "subst.go", + "util.go", ++ "wrappers.go", + ], -+ importpath = "golang.org/x/tools/go/pointer", ++ importpath = "golang.org/x/tools/go/ssa", + visibility = ["//visibility:public"], + deps = [ -+ "//container/intsets", -+ "//go/callgraph", -+ "//go/ssa", ++ "//go/ast/astutil", + "//go/types/typeutil", + "//internal/typeparams", -+ "@org_golang_x_sys//execabs:go_default_library", + ], +) + +alias( + name = "go_default_library", -+ actual = ":pointer", ++ actual = ":ssa", + visibility = ["//visibility:public"], +) + +go_test( -+ name = "pointer_test", ++ name = "ssa_test", + srcs = [ ++ "builder_generic_test.go", ++ "builder_go117_test.go", ++ "builder_go120_test.go", ++ "builder_test.go", ++ "const_test.go", ++ "coretype_test.go", + "example_test.go", -+ "pointer_go117_test.go", -+ "pointer_race_test.go", -+ "pointer_test.go", -+ "query_test.go", ++ "identical_test.go", ++ "instantiate_test.go", ++ "methods_test.go", ++ "parameterized_test.go", ++ "source_test.go", + "stdlib_test.go", ++ "subst_test.go", ++ "testhelper_test.go", + ], -+ embed = [":pointer"], ++ data = glob(["testdata/**"], allow_empty = True), ++ embed = [":ssa"], + deps = [ -+ "//go/callgraph", -+ "//go/loader", -+ "//go/ssa", -+ "//go/ssa/ssautil", -+ ] + select({ -+ "@io_bazel_rules_go//go/platform:aix": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:darwin": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:dragonfly": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:freebsd": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:illumos": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:ios": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:js": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:linux": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:netbsd": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:openbsd": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:plan9": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:solaris": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "@io_bazel_rules_go//go/platform:windows": [ -+ "//go/packages", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+ "//conditions:default": [], -+ }), -+) -diff -urN b/go/pointer/testdata/BUILD.bazel c/go/pointer/testdata/BUILD.bazel ---- b/go/pointer/testdata/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/pointer/testdata/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,18 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") -+ -+go_library( -+ name = "testdata_lib", -+ srcs = [ -+ "finalizer.go", -+ "issue9002.go", -+ "rtti.go", -+ ], -+ importpath = "golang.org/x/tools/go/pointer/testdata", -+ visibility = ["//visibility:private"], -+) -+ -+go_binary( -+ name = "testdata", -+ embed = [":testdata_lib"], -+ visibility = ["//visibility:public"], -+) -diff -urN b/go/ssa/BUILD.bazel c/go/ssa/BUILD.bazel ---- b/go/ssa/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/ssa/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,117 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -+ -+go_library( -+ name = "ssa", -+ srcs = [ -+ "block.go", -+ "blockopt.go", -+ "builder.go", -+ "const.go", -+ "coretype.go", -+ "create.go", -+ "doc.go", -+ "dom.go", -+ "emit.go", -+ "func.go", -+ "identical.go", -+ "identical_17.go", -+ "instantiate.go", -+ "lift.go", -+ "lvalue.go", -+ "methods.go", -+ "mode.go", -+ "parameterized.go", -+ "print.go", -+ "sanity.go", -+ "source.go", -+ "ssa.go", -+ "subst.go", -+ "util.go", -+ "wrappers.go", -+ ], -+ importpath = "golang.org/x/tools/go/ssa", -+ visibility = ["//visibility:public"], -+ deps = [ -+ "//go/ast/astutil", -+ "//go/types/typeutil", -+ "//internal/typeparams", -+ ], -+) -+ -+alias( -+ name = "go_default_library", -+ actual = ":ssa", -+ visibility = ["//visibility:public"], -+) -+ -+go_test( -+ name = "ssa_test", -+ srcs = [ -+ "builder_generic_test.go", -+ "builder_go117_test.go", -+ "builder_go120_test.go", -+ "builder_test.go", -+ "const_test.go", -+ "coretype_test.go", -+ "example_test.go", -+ "identical_test.go", -+ "instantiate_test.go", -+ "methods_test.go", -+ "parameterized_test.go", -+ "source_test.go", -+ "stdlib_test.go", -+ "subst_test.go", -+ "testhelper_test.go", -+ ], -+ embed = [":ssa"], -+ deps = [ -+ "//go/ast/astutil", -+ "//go/buildutil", -+ "//go/expect", ++ "//go/ast/astutil", ++ "//go/buildutil", ++ "//go/expect", + "//go/loader", + "//go/ssa/ssautil", + "//internal/testenv", @@ -7307,7 +7524,7 @@ diff -urN b/go/ssa/BUILD.bazel c/go/ssa/BUILD.bazel + }), +) diff -urN b/go/ssa/interp/BUILD.bazel c/go/ssa/interp/BUILD.bazel ---- b/go/ssa/interp/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -7339,8 +7556,8 @@ diff -urN b/go/ssa/interp/BUILD.bazel c/go/ssa/interp/BUILD.bazel +go_test( + name = "interp_test", + srcs = [ -+ "interp_go117_test.go", + "interp_go120_test.go", ++ "interp_go121_test.go", + "interp_test.go", + ], + deps = [ @@ -7352,9 +7569,9 @@ diff -urN b/go/ssa/interp/BUILD.bazel c/go/ssa/interp/BUILD.bazel + ], +) diff -urN b/go/ssa/interp/testdata/fixedbugs/BUILD.bazel c/go/ssa/interp/testdata/fixedbugs/BUILD.bazel ---- b/go/ssa/interp/testdata/fixedbugs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/fixedbugs/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/fixedbugs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,18 @@ +@@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( @@ -7363,6 +7580,7 @@ diff -urN b/go/ssa/interp/testdata/fixedbugs/BUILD.bazel c/go/ssa/interp/testdat + "issue52342.go", + "issue52835.go", + "issue55086.go", ++ "issue55115.go", + ], + importpath = "golang.org/x/tools/go/ssa/interp/testdata/fixedbugs", + visibility = ["//visibility:private"], @@ -7374,7 +7592,7 @@ diff -urN b/go/ssa/interp/testdata/fixedbugs/BUILD.bazel c/go/ssa/interp/testdat + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/encoding/BUILD.bazel c/go/ssa/interp/testdata/src/encoding/BUILD.bazel ---- b/go/ssa/interp/testdata/src/encoding/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/encoding/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/encoding/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7392,7 +7610,7 @@ diff -urN b/go/ssa/interp/testdata/src/encoding/BUILD.bazel c/go/ssa/interp/test + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/errors/BUILD.bazel c/go/ssa/interp/testdata/src/errors/BUILD.bazel ---- b/go/ssa/interp/testdata/src/errors/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/errors/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/errors/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7410,7 +7628,7 @@ diff -urN b/go/ssa/interp/testdata/src/errors/BUILD.bazel c/go/ssa/interp/testda + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/fmt/BUILD.bazel c/go/ssa/interp/testdata/src/fmt/BUILD.bazel ---- b/go/ssa/interp/testdata/src/fmt/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/fmt/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/fmt/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7428,7 +7646,7 @@ diff -urN b/go/ssa/interp/testdata/src/fmt/BUILD.bazel c/go/ssa/interp/testdata/ + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/io/BUILD.bazel c/go/ssa/interp/testdata/src/io/BUILD.bazel ---- b/go/ssa/interp/testdata/src/io/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/io/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/io/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7446,7 +7664,7 @@ diff -urN b/go/ssa/interp/testdata/src/io/BUILD.bazel c/go/ssa/interp/testdata/s + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/log/BUILD.bazel c/go/ssa/interp/testdata/src/log/BUILD.bazel ---- b/go/ssa/interp/testdata/src/log/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/log/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/log/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7464,7 +7682,7 @@ diff -urN b/go/ssa/interp/testdata/src/log/BUILD.bazel c/go/ssa/interp/testdata/ + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/math/BUILD.bazel c/go/ssa/interp/testdata/src/math/BUILD.bazel ---- b/go/ssa/interp/testdata/src/math/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/math/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/math/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7482,7 +7700,7 @@ diff -urN b/go/ssa/interp/testdata/src/math/BUILD.bazel c/go/ssa/interp/testdata + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/os/BUILD.bazel c/go/ssa/interp/testdata/src/os/BUILD.bazel ---- b/go/ssa/interp/testdata/src/os/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/os/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/os/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7500,7 +7718,7 @@ diff -urN b/go/ssa/interp/testdata/src/os/BUILD.bazel c/go/ssa/interp/testdata/s + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/reflect/BUILD.bazel c/go/ssa/interp/testdata/src/reflect/BUILD.bazel ---- b/go/ssa/interp/testdata/src/reflect/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/reflect/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/reflect/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7521,7 +7739,7 @@ diff -urN b/go/ssa/interp/testdata/src/reflect/BUILD.bazel c/go/ssa/interp/testd + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/runtime/BUILD.bazel c/go/ssa/interp/testdata/src/runtime/BUILD.bazel ---- b/go/ssa/interp/testdata/src/runtime/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/runtime/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/runtime/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7539,7 +7757,7 @@ diff -urN b/go/ssa/interp/testdata/src/runtime/BUILD.bazel c/go/ssa/interp/testd + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/sort/BUILD.bazel c/go/ssa/interp/testdata/src/sort/BUILD.bazel ---- b/go/ssa/interp/testdata/src/sort/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/sort/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/sort/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7557,7 +7775,7 @@ diff -urN b/go/ssa/interp/testdata/src/sort/BUILD.bazel c/go/ssa/interp/testdata + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/strconv/BUILD.bazel c/go/ssa/interp/testdata/src/strconv/BUILD.bazel ---- b/go/ssa/interp/testdata/src/strconv/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/strconv/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/strconv/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7575,7 +7793,7 @@ diff -urN b/go/ssa/interp/testdata/src/strconv/BUILD.bazel c/go/ssa/interp/testd + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/strings/BUILD.bazel c/go/ssa/interp/testdata/src/strings/BUILD.bazel ---- b/go/ssa/interp/testdata/src/strings/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/strings/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/strings/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7593,7 +7811,7 @@ diff -urN b/go/ssa/interp/testdata/src/strings/BUILD.bazel c/go/ssa/interp/testd + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/sync/BUILD.bazel c/go/ssa/interp/testdata/src/sync/BUILD.bazel ---- b/go/ssa/interp/testdata/src/sync/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/sync/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/sync/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7611,7 +7829,7 @@ diff -urN b/go/ssa/interp/testdata/src/sync/BUILD.bazel c/go/ssa/interp/testdata + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/time/BUILD.bazel c/go/ssa/interp/testdata/src/time/BUILD.bazel ---- b/go/ssa/interp/testdata/src/time/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/time/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/time/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7629,7 +7847,7 @@ diff -urN b/go/ssa/interp/testdata/src/time/BUILD.bazel c/go/ssa/interp/testdata + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/unicode/utf8/BUILD.bazel c/go/ssa/interp/testdata/src/unicode/utf8/BUILD.bazel ---- b/go/ssa/interp/testdata/src/unicode/utf8/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/unicode/utf8/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/unicode/utf8/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7647,7 +7865,7 @@ diff -urN b/go/ssa/interp/testdata/src/unicode/utf8/BUILD.bazel c/go/ssa/interp/ + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/interp/testdata/src/unsafe/BUILD.bazel c/go/ssa/interp/testdata/src/unsafe/BUILD.bazel ---- b/go/ssa/interp/testdata/src/unsafe/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/interp/testdata/src/unsafe/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/interp/testdata/src/unsafe/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7665,7 +7883,7 @@ diff -urN b/go/ssa/interp/testdata/src/unsafe/BUILD.bazel c/go/ssa/interp/testda + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/ssautil/BUILD.bazel c/go/ssa/ssautil/BUILD.bazel ---- b/go/ssa/ssautil/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/ssautil/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/ssautil/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,81 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -7699,7 +7917,7 @@ diff -urN b/go/ssa/ssautil/BUILD.bazel c/go/ssa/ssautil/BUILD.bazel + "load_test.go", + "switch_test.go", + ], -+ data = glob(["testdata/**"]), ++ data = glob(["testdata/**"], allow_empty = True), + deps = [ + ":ssautil", + "//go/packages", @@ -7750,7 +7968,7 @@ diff -urN b/go/ssa/ssautil/BUILD.bazel c/go/ssa/ssautil/BUILD.bazel + }), +) diff -urN b/go/ssa/testdata/src/bytes/BUILD.bazel c/go/ssa/testdata/src/bytes/BUILD.bazel ---- b/go/ssa/testdata/src/bytes/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/bytes/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/bytes/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7768,7 +7986,7 @@ diff -urN b/go/ssa/testdata/src/bytes/BUILD.bazel c/go/ssa/testdata/src/bytes/BU + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/context/BUILD.bazel c/go/ssa/testdata/src/context/BUILD.bazel ---- b/go/ssa/testdata/src/context/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/context/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/context/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7786,7 +8004,7 @@ diff -urN b/go/ssa/testdata/src/context/BUILD.bazel c/go/ssa/testdata/src/contex + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/encoding/BUILD.bazel c/go/ssa/testdata/src/encoding/BUILD.bazel ---- b/go/ssa/testdata/src/encoding/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/encoding/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/encoding/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7804,7 +8022,7 @@ diff -urN b/go/ssa/testdata/src/encoding/BUILD.bazel c/go/ssa/testdata/src/encod + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/encoding/json/BUILD.bazel c/go/ssa/testdata/src/encoding/json/BUILD.bazel ---- b/go/ssa/testdata/src/encoding/json/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/encoding/json/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/encoding/json/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7822,7 +8040,7 @@ diff -urN b/go/ssa/testdata/src/encoding/json/BUILD.bazel c/go/ssa/testdata/src/ + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/encoding/xml/BUILD.bazel c/go/ssa/testdata/src/encoding/xml/BUILD.bazel ---- b/go/ssa/testdata/src/encoding/xml/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/encoding/xml/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/encoding/xml/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7840,7 +8058,7 @@ diff -urN b/go/ssa/testdata/src/encoding/xml/BUILD.bazel c/go/ssa/testdata/src/e + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/errors/BUILD.bazel c/go/ssa/testdata/src/errors/BUILD.bazel ---- b/go/ssa/testdata/src/errors/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/errors/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/errors/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7858,7 +8076,7 @@ diff -urN b/go/ssa/testdata/src/errors/BUILD.bazel c/go/ssa/testdata/src/errors/ + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/fmt/BUILD.bazel c/go/ssa/testdata/src/fmt/BUILD.bazel ---- b/go/ssa/testdata/src/fmt/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/fmt/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/fmt/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7876,7 +8094,7 @@ diff -urN b/go/ssa/testdata/src/fmt/BUILD.bazel c/go/ssa/testdata/src/fmt/BUILD. + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/io/BUILD.bazel c/go/ssa/testdata/src/io/BUILD.bazel ---- b/go/ssa/testdata/src/io/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/io/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/io/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7894,7 +8112,7 @@ diff -urN b/go/ssa/testdata/src/io/BUILD.bazel c/go/ssa/testdata/src/io/BUILD.ba + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/log/BUILD.bazel c/go/ssa/testdata/src/log/BUILD.bazel ---- b/go/ssa/testdata/src/log/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/log/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/log/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7912,7 +8130,7 @@ diff -urN b/go/ssa/testdata/src/log/BUILD.bazel c/go/ssa/testdata/src/log/BUILD. + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/math/BUILD.bazel c/go/ssa/testdata/src/math/BUILD.bazel ---- b/go/ssa/testdata/src/math/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/math/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/math/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7930,7 +8148,7 @@ diff -urN b/go/ssa/testdata/src/math/BUILD.bazel c/go/ssa/testdata/src/math/BUIL + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/os/BUILD.bazel c/go/ssa/testdata/src/os/BUILD.bazel ---- b/go/ssa/testdata/src/os/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/os/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/os/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7948,7 +8166,7 @@ diff -urN b/go/ssa/testdata/src/os/BUILD.bazel c/go/ssa/testdata/src/os/BUILD.ba + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/reflect/BUILD.bazel c/go/ssa/testdata/src/reflect/BUILD.bazel ---- b/go/ssa/testdata/src/reflect/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/reflect/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/reflect/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7966,7 +8184,7 @@ diff -urN b/go/ssa/testdata/src/reflect/BUILD.bazel c/go/ssa/testdata/src/reflec + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/runtime/BUILD.bazel c/go/ssa/testdata/src/runtime/BUILD.bazel ---- b/go/ssa/testdata/src/runtime/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/runtime/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/runtime/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -7984,7 +8202,7 @@ diff -urN b/go/ssa/testdata/src/runtime/BUILD.bazel c/go/ssa/testdata/src/runtim + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/sort/BUILD.bazel c/go/ssa/testdata/src/sort/BUILD.bazel ---- b/go/ssa/testdata/src/sort/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/sort/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/sort/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8002,7 +8220,7 @@ diff -urN b/go/ssa/testdata/src/sort/BUILD.bazel c/go/ssa/testdata/src/sort/BUIL + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/strconv/BUILD.bazel c/go/ssa/testdata/src/strconv/BUILD.bazel ---- b/go/ssa/testdata/src/strconv/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/strconv/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/strconv/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8020,7 +8238,7 @@ diff -urN b/go/ssa/testdata/src/strconv/BUILD.bazel c/go/ssa/testdata/src/strcon + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/strings/BUILD.bazel c/go/ssa/testdata/src/strings/BUILD.bazel ---- b/go/ssa/testdata/src/strings/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/strings/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/strings/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8037,44 +8255,44 @@ diff -urN b/go/ssa/testdata/src/strings/BUILD.bazel c/go/ssa/testdata/src/string + actual = ":strings", + visibility = ["//visibility:public"], +) -diff -urN b/go/ssa/testdata/src/sync/atomic/BUILD.bazel c/go/ssa/testdata/src/sync/atomic/BUILD.bazel ---- b/go/ssa/testdata/src/sync/atomic/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/ssa/testdata/src/sync/atomic/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +diff -urN b/go/ssa/testdata/src/sync/BUILD.bazel c/go/ssa/testdata/src/sync/BUILD.bazel +--- b/go/ssa/testdata/src/sync/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/ssa/testdata/src/sync/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( -+ name = "atomic", -+ srcs = ["atomic.go"], -+ importpath = "golang.org/x/tools/go/ssa/testdata/src/sync/atomic", ++ name = "sync", ++ srcs = ["sync.go"], ++ importpath = "golang.org/x/tools/go/ssa/testdata/src/sync", + visibility = ["//visibility:public"], +) + +alias( + name = "go_default_library", -+ actual = ":atomic", ++ actual = ":sync", + visibility = ["//visibility:public"], +) -diff -urN b/go/ssa/testdata/src/sync/BUILD.bazel c/go/ssa/testdata/src/sync/BUILD.bazel ---- b/go/ssa/testdata/src/sync/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/ssa/testdata/src/sync/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +diff -urN b/go/ssa/testdata/src/sync/atomic/BUILD.bazel c/go/ssa/testdata/src/sync/atomic/BUILD.bazel +--- b/go/ssa/testdata/src/sync/atomic/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/ssa/testdata/src/sync/atomic/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( -+ name = "sync", -+ srcs = ["sync.go"], -+ importpath = "golang.org/x/tools/go/ssa/testdata/src/sync", ++ name = "atomic", ++ srcs = ["atomic.go"], ++ importpath = "golang.org/x/tools/go/ssa/testdata/src/sync/atomic", + visibility = ["//visibility:public"], +) + +alias( + name = "go_default_library", -+ actual = ":sync", ++ actual = ":atomic", + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/time/BUILD.bazel c/go/ssa/testdata/src/time/BUILD.bazel ---- b/go/ssa/testdata/src/time/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/time/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/time/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8092,7 +8310,7 @@ diff -urN b/go/ssa/testdata/src/time/BUILD.bazel c/go/ssa/testdata/src/time/BUIL + visibility = ["//visibility:public"], +) diff -urN b/go/ssa/testdata/src/unsafe/BUILD.bazel c/go/ssa/testdata/src/unsafe/BUILD.bazel ---- b/go/ssa/testdata/src/unsafe/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/ssa/testdata/src/unsafe/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/ssa/testdata/src/unsafe/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8109,10 +8327,34 @@ diff -urN b/go/ssa/testdata/src/unsafe/BUILD.bazel c/go/ssa/testdata/src/unsafe/ + actual = ":unsafe", + visibility = ["//visibility:public"], +) +diff -urN b/go/types/internal/play/BUILD.bazel c/go/types/internal/play/BUILD.bazel +--- b/go/types/internal/play/BUILD.bazel 1970-01-01 08:00:00 ++++ c/go/types/internal/play/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,20 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") ++ ++go_library( ++ name = "play_lib", ++ srcs = ["play.go"], ++ importpath = "golang.org/x/tools/go/types/internal/play", ++ visibility = ["//visibility:private"], ++ deps = [ ++ "//go/ast/astutil", ++ "//go/packages", ++ "//go/types/typeutil", ++ "//internal/typeparams", ++ ], ++) ++ ++go_binary( ++ name = "play", ++ embed = [":play_lib"], ++ visibility = ["//go/types:__subpackages__"], ++) diff -urN b/go/types/objectpath/BUILD.bazel c/go/types/objectpath/BUILD.bazel ---- b/go/types/objectpath/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/types/objectpath/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/types/objectpath/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,29 @@ +@@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -8120,7 +8362,10 @@ diff -urN b/go/types/objectpath/BUILD.bazel c/go/types/objectpath/BUILD.bazel + srcs = ["objectpath.go"], + importpath = "golang.org/x/tools/go/types/objectpath", + visibility = ["//visibility:public"], -+ deps = ["//internal/typeparams"], ++ deps = [ ++ "//internal/typeparams", ++ "//internal/typesinternal", ++ ], +) + +alias( @@ -8143,7 +8388,7 @@ diff -urN b/go/types/objectpath/BUILD.bazel c/go/types/objectpath/BUILD.bazel + ], +) diff -urN b/go/types/typeutil/BUILD.bazel c/go/types/typeutil/BUILD.bazel ---- b/go/types/typeutil/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/go/types/typeutil/BUILD.bazel 1970-01-01 08:00:00 +++ c/go/types/typeutil/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8185,59 +8430,8 @@ diff -urN b/go/types/typeutil/BUILD.bazel c/go/types/typeutil/BUILD.bazel + "//internal/typeparams", + ], +) -diff -urN b/go/vcs/BUILD.bazel c/go/vcs/BUILD.bazel ---- b/go/vcs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/go/vcs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,26 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -+ -+go_library( -+ name = "vcs", -+ srcs = [ -+ "discovery.go", -+ "env.go", -+ "http.go", -+ "vcs.go", -+ ], -+ importpath = "golang.org/x/tools/go/vcs", -+ visibility = ["//visibility:public"], -+ deps = ["@org_golang_x_sys//execabs:go_default_library"], -+) -+ -+alias( -+ name = "go_default_library", -+ actual = ":vcs", -+ visibility = ["//visibility:public"], -+) -+ -+go_test( -+ name = "vcs_test", -+ srcs = ["vcs_test.go"], -+ embed = [":vcs"], -+) -diff -urN b/godoc/analysis/BUILD.bazel c/godoc/analysis/BUILD.bazel ---- b/godoc/analysis/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/godoc/analysis/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,17 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_library") -+ -+go_library( -+ name = "analysis", -+ srcs = [ -+ "analysis.go", -+ "json.go", -+ ], -+ importpath = "golang.org/x/tools/godoc/analysis", -+ visibility = ["//visibility:public"], -+) -+ -+alias( -+ name = "go_default_library", -+ actual = ":analysis", -+ visibility = ["//visibility:public"], -+) diff -urN b/godoc/BUILD.bazel c/godoc/BUILD.bazel ---- b/godoc/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/godoc/BUILD.bazel 1970-01-01 08:00:00 +++ c/godoc/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,66 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8306,8 +8500,29 @@ diff -urN b/godoc/BUILD.bazel c/godoc/BUILD.bazel + "//internal/typeparams", + ], +) +diff -urN b/godoc/analysis/BUILD.bazel c/godoc/analysis/BUILD.bazel +--- b/godoc/analysis/BUILD.bazel 1970-01-01 08:00:00 ++++ c/godoc/analysis/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,17 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "analysis", ++ srcs = [ ++ "analysis.go", ++ "json.go", ++ ], ++ importpath = "golang.org/x/tools/godoc/analysis", ++ visibility = ["//visibility:public"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":analysis", ++ visibility = ["//visibility:public"], ++) diff -urN b/godoc/redirect/BUILD.bazel c/godoc/redirect/BUILD.bazel ---- b/godoc/redirect/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/godoc/redirect/BUILD.bazel 1970-01-01 08:00:00 +++ c/godoc/redirect/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8331,7 +8546,7 @@ diff -urN b/godoc/redirect/BUILD.bazel c/godoc/redirect/BUILD.bazel + embed = [":redirect"], +) diff -urN b/godoc/static/BUILD.bazel c/godoc/static/BUILD.bazel ---- b/godoc/static/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/godoc/static/BUILD.bazel 1970-01-01 08:00:00 +++ c/godoc/static/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8359,7 +8574,7 @@ diff -urN b/godoc/static/BUILD.bazel c/godoc/static/BUILD.bazel + embed = [":static"], +) diff -urN b/godoc/util/BUILD.bazel c/godoc/util/BUILD.bazel ---- b/godoc/util/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/godoc/util/BUILD.bazel 1970-01-01 08:00:00 +++ c/godoc/util/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8381,7 +8596,7 @@ diff -urN b/godoc/util/BUILD.bazel c/godoc/util/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/godoc/vfs/BUILD.bazel c/godoc/vfs/BUILD.bazel ---- b/godoc/vfs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/godoc/vfs/BUILD.bazel 1970-01-01 08:00:00 +++ c/godoc/vfs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8417,7 +8632,7 @@ diff -urN b/godoc/vfs/BUILD.bazel c/godoc/vfs/BUILD.bazel + ], +) diff -urN b/godoc/vfs/gatefs/BUILD.bazel c/godoc/vfs/gatefs/BUILD.bazel ---- b/godoc/vfs/gatefs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/godoc/vfs/gatefs/BUILD.bazel 1970-01-01 08:00:00 +++ c/godoc/vfs/gatefs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8445,7 +8660,7 @@ diff -urN b/godoc/vfs/gatefs/BUILD.bazel c/godoc/vfs/gatefs/BUILD.bazel + ], +) diff -urN b/godoc/vfs/httpfs/BUILD.bazel c/godoc/vfs/httpfs/BUILD.bazel ---- b/godoc/vfs/httpfs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/godoc/vfs/httpfs/BUILD.bazel 1970-01-01 08:00:00 +++ c/godoc/vfs/httpfs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8464,7 +8679,7 @@ diff -urN b/godoc/vfs/httpfs/BUILD.bazel c/godoc/vfs/httpfs/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/godoc/vfs/mapfs/BUILD.bazel c/godoc/vfs/mapfs/BUILD.bazel ---- b/godoc/vfs/mapfs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/godoc/vfs/mapfs/BUILD.bazel 1970-01-01 08:00:00 +++ c/godoc/vfs/mapfs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8489,7 +8704,7 @@ diff -urN b/godoc/vfs/mapfs/BUILD.bazel c/godoc/vfs/mapfs/BUILD.bazel + embed = [":mapfs"], +) diff -urN b/godoc/vfs/zipfs/BUILD.bazel c/godoc/vfs/zipfs/BUILD.bazel ---- b/godoc/vfs/zipfs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/godoc/vfs/zipfs/BUILD.bazel 1970-01-01 08:00:00 +++ c/godoc/vfs/zipfs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8515,7 +8730,7 @@ diff -urN b/godoc/vfs/zipfs/BUILD.bazel c/godoc/vfs/zipfs/BUILD.bazel + deps = ["//godoc/vfs"], +) diff -urN b/imports/BUILD.bazel c/imports/BUILD.bazel ---- b/imports/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/imports/BUILD.bazel 1970-01-01 08:00:00 +++ c/imports/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8537,7 +8752,7 @@ diff -urN b/imports/BUILD.bazel c/imports/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/internal/analysisinternal/BUILD.bazel c/internal/analysisinternal/BUILD.bazel ---- b/internal/analysisinternal/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/analysisinternal/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/analysisinternal/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8555,7 +8770,7 @@ diff -urN b/internal/analysisinternal/BUILD.bazel c/internal/analysisinternal/BU + visibility = ["//:__subpackages__"], +) diff -urN b/internal/apidiff/BUILD.bazel c/internal/apidiff/BUILD.bazel ---- b/internal/apidiff/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/apidiff/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/apidiff/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,30 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8589,7 +8804,7 @@ diff -urN b/internal/apidiff/BUILD.bazel c/internal/apidiff/BUILD.bazel + ], +) diff -urN b/internal/apidiff/testdata/BUILD.bazel c/internal/apidiff/testdata/BUILD.bazel ---- b/internal/apidiff/testdata/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/apidiff/testdata/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/apidiff/testdata/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8607,7 +8822,7 @@ diff -urN b/internal/apidiff/testdata/BUILD.bazel c/internal/apidiff/testdata/BU + visibility = ["//:__subpackages__"], +) diff -urN b/internal/apidiff/testdata/exported_fields/BUILD.bazel c/internal/apidiff/testdata/exported_fields/BUILD.bazel ---- b/internal/apidiff/testdata/exported_fields/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/apidiff/testdata/exported_fields/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/apidiff/testdata/exported_fields/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8624,35 +8839,113 @@ diff -urN b/internal/apidiff/testdata/exported_fields/BUILD.bazel c/internal/api + actual = ":exported_fields", + visibility = ["//:__subpackages__"], +) -diff -urN b/internal/bug/BUILD.bazel c/internal/bug/BUILD.bazel ---- b/internal/bug/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/internal/bug/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +diff -urN b/internal/bisect/BUILD.bazel c/internal/bisect/BUILD.bazel +--- b/internal/bisect/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/bisect/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( -+ name = "bug", -+ srcs = ["bug.go"], -+ importpath = "golang.org/x/tools/internal/bug", ++ name = "bisect", ++ srcs = ["bisect.go"], ++ importpath = "golang.org/x/tools/internal/bisect", + visibility = ["//:__subpackages__"], +) + +alias( + name = "go_default_library", -+ actual = ":bug", ++ actual = ":bisect", + visibility = ["//:__subpackages__"], +) + +go_test( -+ name = "bug_test", -+ srcs = ["bug_test.go"], -+ embed = [":bug"], ++ name = "bisect_test", ++ srcs = ["bisect_test.go"], ++ embed = [":bisect"], +) -diff -urN b/internal/diff/BUILD.bazel c/internal/diff/BUILD.bazel ---- b/internal/diff/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/internal/diff/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,32 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +diff -urN b/internal/cmd/deadcode/BUILD.bazel c/internal/cmd/deadcode/BUILD.bazel +--- b/internal/cmd/deadcode/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/cmd/deadcode/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,34 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") ++ ++go_library( ++ name = "deadcode_lib", ++ srcs = [ ++ "deadcode.go", ++ "doc.go", ++ ], ++ embedsrcs = ["doc.go"], ++ importpath = "golang.org/x/tools/internal/cmd/deadcode", ++ visibility = ["//visibility:private"], ++ deps = [ ++ "//go/callgraph/rta", ++ "//go/packages", ++ "//go/ssa", ++ "//go/ssa/ssautil", ++ ], ++) ++ ++go_binary( ++ name = "deadcode", ++ embed = [":deadcode_lib"], ++ visibility = ["//:__subpackages__"], ++) ++ ++go_test( ++ name = "deadcode_test", ++ srcs = ["deadcode_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), ++ deps = [ ++ "//internal/testenv", ++ "//txtar", ++ ], ++) +diff -urN b/internal/compat/BUILD.bazel c/internal/compat/BUILD.bazel +--- b/internal/compat/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/compat/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,18 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "compat", ++ srcs = [ ++ "appendf.go", ++ "appendf_118.go", ++ "doc.go", ++ ], ++ importpath = "golang.org/x/tools/internal/compat", ++ visibility = ["//:__subpackages__"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":compat", ++ visibility = ["//:__subpackages__"], ++) +diff -urN b/internal/constraints/BUILD.bazel c/internal/constraints/BUILD.bazel +--- b/internal/constraints/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/constraints/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,14 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "constraints", ++ srcs = ["constraint.go"], ++ importpath = "golang.org/x/tools/internal/constraints", ++ visibility = ["//:__subpackages__"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":constraints", ++ visibility = ["//:__subpackages__"], ++) +diff -urN b/internal/diff/BUILD.bazel c/internal/diff/BUILD.bazel +--- b/internal/diff/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/diff/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,32 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "diff", @@ -8685,7 +8978,7 @@ diff -urN b/internal/diff/BUILD.bazel c/internal/diff/BUILD.bazel + ], +) diff -urN b/internal/diff/difftest/BUILD.bazel c/internal/diff/difftest/BUILD.bazel ---- b/internal/diff/difftest/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/diff/difftest/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/diff/difftest/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8713,7 +9006,7 @@ diff -urN b/internal/diff/difftest/BUILD.bazel c/internal/diff/difftest/BUILD.ba + ], +) diff -urN b/internal/diff/lcs/BUILD.bazel c/internal/diff/lcs/BUILD.bazel ---- b/internal/diff/lcs/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/diff/lcs/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/diff/lcs/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8746,7 +9039,7 @@ diff -urN b/internal/diff/lcs/BUILD.bazel c/internal/diff/lcs/BUILD.bazel + embed = [":lcs"], +) diff -urN b/internal/diff/myers/BUILD.bazel c/internal/diff/myers/BUILD.bazel ---- b/internal/diff/myers/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/diff/myers/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/diff/myers/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8773,8 +9066,58 @@ diff -urN b/internal/diff/myers/BUILD.bazel c/internal/diff/myers/BUILD.bazel + "//internal/diff/difftest", + ], +) +diff -urN b/internal/diffp/BUILD.bazel c/internal/diffp/BUILD.bazel +--- b/internal/diffp/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/diffp/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,22 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") ++ ++go_library( ++ name = "diffp", ++ srcs = ["diff.go"], ++ importpath = "golang.org/x/tools/internal/diffp", ++ visibility = ["//:__subpackages__"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":diffp", ++ visibility = ["//:__subpackages__"], ++) ++ ++go_test( ++ name = "diffp_test", ++ srcs = ["diff_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), ++ embed = [":diffp"], ++ deps = ["//txtar"], ++) +diff -urN b/internal/edit/BUILD.bazel c/internal/edit/BUILD.bazel +--- b/internal/edit/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/edit/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,20 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") ++ ++go_library( ++ name = "edit", ++ srcs = ["edit.go"], ++ importpath = "golang.org/x/tools/internal/edit", ++ visibility = ["//:__subpackages__"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":edit", ++ visibility = ["//:__subpackages__"], ++) ++ ++go_test( ++ name = "edit_test", ++ srcs = ["edit_test.go"], ++ embed = [":edit"], ++) diff -urN b/internal/event/BUILD.bazel c/internal/event/BUILD.bazel ---- b/internal/event/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8812,7 +9155,7 @@ diff -urN b/internal/event/BUILD.bazel c/internal/event/BUILD.bazel + ], +) diff -urN b/internal/event/core/BUILD.bazel c/internal/event/core/BUILD.bazel ---- b/internal/event/core/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/core/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/core/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8838,7 +9181,7 @@ diff -urN b/internal/event/core/BUILD.bazel c/internal/event/core/BUILD.bazel + visibility = ["//:__subpackages__"], +) diff -urN b/internal/event/export/BUILD.bazel c/internal/event/export/BUILD.bazel ---- b/internal/event/export/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/export/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/export/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8880,7 +9223,7 @@ diff -urN b/internal/event/export/BUILD.bazel c/internal/event/export/BUILD.baze + ], +) diff -urN b/internal/event/export/eventtest/BUILD.bazel c/internal/event/export/eventtest/BUILD.bazel ---- b/internal/event/export/eventtest/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/export/eventtest/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/export/eventtest/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8904,7 +9247,7 @@ diff -urN b/internal/event/export/eventtest/BUILD.bazel c/internal/event/export/ + visibility = ["//:__subpackages__"], +) diff -urN b/internal/event/export/metric/BUILD.bazel c/internal/event/export/metric/BUILD.bazel ---- b/internal/event/export/metric/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/export/metric/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/export/metric/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -8932,7 +9275,7 @@ diff -urN b/internal/event/export/metric/BUILD.bazel c/internal/event/export/met + visibility = ["//:__subpackages__"], +) diff -urN b/internal/event/export/ocagent/BUILD.bazel c/internal/event/export/ocagent/BUILD.bazel ---- b/internal/event/export/ocagent/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/export/ocagent/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/export/ocagent/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,44 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -8980,7 +9323,7 @@ diff -urN b/internal/event/export/ocagent/BUILD.bazel c/internal/event/export/oc + ], +) diff -urN b/internal/event/export/ocagent/wire/BUILD.bazel c/internal/event/export/ocagent/wire/BUILD.bazel ---- b/internal/event/export/ocagent/wire/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/export/ocagent/wire/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/export/ocagent/wire/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -9009,7 +9352,7 @@ diff -urN b/internal/event/export/ocagent/wire/BUILD.bazel c/internal/event/expo + embed = [":wire"], +) diff -urN b/internal/event/export/prometheus/BUILD.bazel c/internal/event/export/prometheus/BUILD.bazel ---- b/internal/event/export/prometheus/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/export/prometheus/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/export/prometheus/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9033,7 +9376,7 @@ diff -urN b/internal/event/export/prometheus/BUILD.bazel c/internal/event/export + visibility = ["//:__subpackages__"], +) diff -urN b/internal/event/keys/BUILD.bazel c/internal/event/keys/BUILD.bazel ---- b/internal/event/keys/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/keys/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/keys/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9055,7 +9398,7 @@ diff -urN b/internal/event/keys/BUILD.bazel c/internal/event/keys/BUILD.bazel + visibility = ["//:__subpackages__"], +) diff -urN b/internal/event/label/BUILD.bazel c/internal/event/label/BUILD.bazel ---- b/internal/event/label/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/label/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/label/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -9082,7 +9425,7 @@ diff -urN b/internal/event/label/BUILD.bazel c/internal/event/label/BUILD.bazel + ], +) diff -urN b/internal/event/tag/BUILD.bazel c/internal/event/tag/BUILD.bazel ---- b/internal/event/tag/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/event/tag/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/event/tag/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9101,9 +9444,9 @@ diff -urN b/internal/event/tag/BUILD.bazel c/internal/event/tag/BUILD.bazel + visibility = ["//:__subpackages__"], +) diff -urN b/internal/facts/BUILD.bazel c/internal/facts/BUILD.bazel ---- b/internal/facts/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/facts/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/facts/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,34 @@ +@@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -9118,13 +9461,14 @@ diff -urN b/internal/facts/BUILD.bazel c/internal/facts/BUILD.bazel + "//go/analysis", + "//go/types/objectpath", + "//internal/typeparams", ++ "//internal/typesinternal", + ], +) + +alias( + name = "go_default_library", + actual = ":facts", -+ visibility = ["//visibility:public"], ++ visibility = ["//:__subpackages__"], +) + +go_test( @@ -9139,7 +9483,7 @@ diff -urN b/internal/facts/BUILD.bazel c/internal/facts/BUILD.bazel + ], +) diff -urN b/internal/fakenet/BUILD.bazel c/internal/fakenet/BUILD.bazel ---- b/internal/fakenet/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/fakenet/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/fakenet/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9157,7 +9501,7 @@ diff -urN b/internal/fakenet/BUILD.bazel c/internal/fakenet/BUILD.bazel + visibility = ["//:__subpackages__"], +) diff -urN b/internal/fastwalk/BUILD.bazel c/internal/fastwalk/BUILD.bazel ---- b/internal/fastwalk/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/fastwalk/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/fastwalk/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,30 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -9191,9 +9535,9 @@ diff -urN b/internal/fastwalk/BUILD.bazel c/internal/fastwalk/BUILD.bazel + deps = [":fastwalk"], +) diff -urN b/internal/fuzzy/BUILD.bazel c/internal/fuzzy/BUILD.bazel ---- b/internal/fuzzy/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/fuzzy/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/fuzzy/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,28 @@ +@@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -9218,12 +9562,16 @@ diff -urN b/internal/fuzzy/BUILD.bazel c/internal/fuzzy/BUILD.bazel + srcs = [ + "input_test.go", + "matcher_test.go", ++ "self_test.go", + "symbol_test.go", + ], -+ deps = [":fuzzy"], ++ deps = [ ++ ":fuzzy", ++ "//go/packages", ++ ], +) diff -urN b/internal/gcimporter/BUILD.bazel c/internal/gcimporter/BUILD.bazel ---- b/internal/gcimporter/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/gcimporter/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/gcimporter/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,60 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -9231,7 +9579,6 @@ diff -urN b/internal/gcimporter/BUILD.bazel c/internal/gcimporter/BUILD.bazel +go_library( + name = "gcimporter", + srcs = [ -+ "bexport.go", + "bimport.go", + "exportdata.go", + "gcimporter.go", @@ -9248,6 +9595,7 @@ diff -urN b/internal/gcimporter/BUILD.bazel c/internal/gcimporter/BUILD.bazel + importpath = "golang.org/x/tools/internal/gcimporter", + visibility = ["//:__subpackages__"], + deps = [ ++ "//go/types/objectpath", + "//internal/pkgbits", + "//internal/tokeninternal", + "//internal/typeparams", @@ -9287,7 +9635,7 @@ diff -urN b/internal/gcimporter/BUILD.bazel c/internal/gcimporter/BUILD.bazel + ], +) diff -urN b/internal/gcimporter/testdata/a/BUILD.bazel c/internal/gcimporter/testdata/a/BUILD.bazel ---- b/internal/gcimporter/testdata/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/gcimporter/testdata/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/gcimporter/testdata/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9304,26 +9652,8 @@ diff -urN b/internal/gcimporter/testdata/a/BUILD.bazel c/internal/gcimporter/tes + actual = ":a", + visibility = ["//:__subpackages__"], +) -diff -urN b/internal/gcimporter/testdata/issue51836/a/BUILD.bazel c/internal/gcimporter/testdata/issue51836/a/BUILD.bazel ---- b/internal/gcimporter/testdata/issue51836/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/internal/gcimporter/testdata/issue51836/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_library") -+ -+go_library( -+ name = "a", -+ srcs = ["a.go"], -+ importpath = "golang.org/x/tools/internal/gcimporter/testdata/issue51836/a", -+ visibility = ["//:__subpackages__"], -+) -+ -+alias( -+ name = "go_default_library", -+ actual = ":a", -+ visibility = ["//:__subpackages__"], -+) diff -urN b/internal/gcimporter/testdata/issue51836/BUILD.bazel c/internal/gcimporter/testdata/issue51836/BUILD.bazel ---- b/internal/gcimporter/testdata/issue51836/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/gcimporter/testdata/issue51836/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/gcimporter/testdata/issue51836/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9344,8 +9674,26 @@ diff -urN b/internal/gcimporter/testdata/issue51836/BUILD.bazel c/internal/gcimp + actual = ":issue51836", + visibility = ["//:__subpackages__"], +) +diff -urN b/internal/gcimporter/testdata/issue51836/a/BUILD.bazel c/internal/gcimporter/testdata/issue51836/a/BUILD.bazel +--- b/internal/gcimporter/testdata/issue51836/a/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/gcimporter/testdata/issue51836/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,14 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "a", ++ srcs = ["a.go"], ++ importpath = "golang.org/x/tools/internal/gcimporter/testdata/issue51836/a", ++ visibility = ["//:__subpackages__"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":a", ++ visibility = ["//:__subpackages__"], ++) diff -urN b/internal/gcimporter/testdata/issue58296/a/BUILD.bazel c/internal/gcimporter/testdata/issue58296/a/BUILD.bazel ---- b/internal/gcimporter/testdata/issue58296/a/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/gcimporter/testdata/issue58296/a/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/gcimporter/testdata/issue58296/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9363,7 +9711,7 @@ diff -urN b/internal/gcimporter/testdata/issue58296/a/BUILD.bazel c/internal/gci + visibility = ["//:__subpackages__"], +) diff -urN b/internal/gcimporter/testdata/issue58296/b/BUILD.bazel c/internal/gcimporter/testdata/issue58296/b/BUILD.bazel ---- b/internal/gcimporter/testdata/issue58296/b/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/gcimporter/testdata/issue58296/b/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/gcimporter/testdata/issue58296/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9381,7 +9729,7 @@ diff -urN b/internal/gcimporter/testdata/issue58296/b/BUILD.bazel c/internal/gci + visibility = ["//:__subpackages__"], +) diff -urN b/internal/gcimporter/testdata/issue58296/c/BUILD.bazel c/internal/gcimporter/testdata/issue58296/c/BUILD.bazel ---- b/internal/gcimporter/testdata/issue58296/c/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/gcimporter/testdata/issue58296/c/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/gcimporter/testdata/issue58296/c/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9399,7 +9747,7 @@ diff -urN b/internal/gcimporter/testdata/issue58296/c/BUILD.bazel c/internal/gci + visibility = ["//:__subpackages__"], +) diff -urN b/internal/gcimporter/testdata/versions/BUILD.bazel c/internal/gcimporter/testdata/versions/BUILD.bazel ---- b/internal/gcimporter/testdata/versions/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/gcimporter/testdata/versions/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/gcimporter/testdata/versions/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9417,9 +9765,9 @@ diff -urN b/internal/gcimporter/testdata/versions/BUILD.bazel c/internal/gcimpor + visibility = ["//:__subpackages__"], +) diff -urN b/internal/gocommand/BUILD.bazel c/internal/gocommand/BUILD.bazel ---- b/internal/gocommand/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/gocommand/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/gocommand/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,32 @@ +@@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -9433,6 +9781,9 @@ diff -urN b/internal/gocommand/BUILD.bazel c/internal/gocommand/BUILD.bazel + visibility = ["//:__subpackages__"], + deps = [ + "//internal/event", ++ "//internal/event/keys", ++ "//internal/event/label", ++ "//internal/event/tag", + "@org_golang_x_mod//semver:go_default_library", + "@org_golang_x_sys//execabs:go_default_library", + ], @@ -9451,9 +9802,10 @@ diff -urN b/internal/gocommand/BUILD.bazel c/internal/gocommand/BUILD.bazel + "version_test.go", + ], + embed = [":gocommand"], ++ deps = ["//internal/testenv"], +) diff -urN b/internal/gopathwalk/BUILD.bazel c/internal/gopathwalk/BUILD.bazel ---- b/internal/gopathwalk/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/gopathwalk/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/gopathwalk/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -9478,7 +9830,7 @@ diff -urN b/internal/gopathwalk/BUILD.bazel c/internal/gopathwalk/BUILD.bazel + embed = [":gopathwalk"], +) diff -urN b/internal/goroot/BUILD.bazel c/internal/goroot/BUILD.bazel ---- b/internal/goroot/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/goroot/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/goroot/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9496,9 +9848,9 @@ diff -urN b/internal/goroot/BUILD.bazel c/internal/goroot/BUILD.bazel + visibility = ["//:__subpackages__"], +) diff -urN b/internal/imports/BUILD.bazel c/internal/imports/BUILD.bazel ---- b/internal/imports/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/imports/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/imports/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,48 @@ +@@ -0,0 +1,49 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -9515,6 +9867,7 @@ diff -urN b/internal/imports/BUILD.bazel c/internal/imports/BUILD.bazel + visibility = ["//:__subpackages__"], + deps = [ + "//go/ast/astutil", ++ "//internal/event", + "//internal/gocommand", + "//internal/gopathwalk", + "@org_golang_x_mod//module:go_default_library", @@ -9535,7 +9888,7 @@ diff -urN b/internal/imports/BUILD.bazel c/internal/imports/BUILD.bazel + "mod_cache_test.go", + "mod_test.go", + ], -+ data = glob(["testdata/**"]), ++ data = glob(["testdata/**"], allow_empty = True), + embed = [":imports"], + deps = [ + "//go/packages/packagestest", @@ -9548,9 +9901,9 @@ diff -urN b/internal/imports/BUILD.bazel c/internal/imports/BUILD.bazel + ], +) diff -urN b/internal/jsonrpc2/BUILD.bazel c/internal/jsonrpc2/BUILD.bazel ---- b/internal/jsonrpc2/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/jsonrpc2/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/jsonrpc2/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,41 @@ +@@ -0,0 +1,42 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -9590,10 +9943,11 @@ diff -urN b/internal/jsonrpc2/BUILD.bazel c/internal/jsonrpc2/BUILD.bazel + deps = [ + "//internal/event/export/eventtest", + "//internal/stack/stacktest", ++ "//internal/testenv", + ], +) diff -urN b/internal/jsonrpc2/servertest/BUILD.bazel c/internal/jsonrpc2/servertest/BUILD.bazel ---- b/internal/jsonrpc2/servertest/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/jsonrpc2/servertest/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/jsonrpc2/servertest/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -9619,9 +9973,9 @@ diff -urN b/internal/jsonrpc2/servertest/BUILD.bazel c/internal/jsonrpc2/servert + deps = ["//internal/jsonrpc2"], +) diff -urN b/internal/jsonrpc2_v2/BUILD.bazel c/internal/jsonrpc2_v2/BUILD.bazel ---- b/internal/jsonrpc2_v2/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/jsonrpc2_v2/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/jsonrpc2_v2/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,44 @@ +@@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( @@ -9664,200 +10018,11 @@ diff -urN b/internal/jsonrpc2_v2/BUILD.bazel c/internal/jsonrpc2_v2/BUILD.bazel + ":jsonrpc2_v2", + "//internal/event/export/eventtest", + "//internal/stack/stacktest", ++ "//internal/testenv", + ], +) -diff -urN b/internal/lockedfile/BUILD.bazel c/internal/lockedfile/BUILD.bazel ---- b/internal/lockedfile/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/internal/lockedfile/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,111 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -+ -+go_library( -+ name = "lockedfile", -+ srcs = [ -+ "lockedfile.go", -+ "lockedfile_filelock.go", -+ "lockedfile_plan9.go", -+ "mutex.go", -+ ], -+ importpath = "golang.org/x/tools/internal/lockedfile", -+ visibility = ["//:__subpackages__"], -+ deps = select({ -+ "@io_bazel_rules_go//go/platform:aix": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:android": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:darwin": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:dragonfly": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:freebsd": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:illumos": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:ios": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:js": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:linux": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:netbsd": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:openbsd": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:solaris": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:windows": [ -+ "//internal/lockedfile/internal/filelock", -+ ], -+ "//conditions:default": [], -+ }), -+) -+ -+alias( -+ name = "go_default_library", -+ actual = ":lockedfile", -+ visibility = ["//:__subpackages__"], -+) -+ -+go_test( -+ name = "lockedfile_test", -+ srcs = [ -+ "lockedfile_test.go", -+ "transform_test.go", -+ ], -+ deps = select({ -+ "@io_bazel_rules_go//go/platform:aix": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:android": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:darwin": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:dragonfly": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:freebsd": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:illumos": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:ios": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:linux": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:netbsd": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:openbsd": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:plan9": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:solaris": [ -+ ":lockedfile", -+ ], -+ "@io_bazel_rules_go//go/platform:windows": [ -+ ":lockedfile", -+ ], -+ "//conditions:default": [], -+ }), -+) -diff -urN b/internal/lockedfile/internal/filelock/BUILD.bazel c/internal/lockedfile/internal/filelock/BUILD.bazel ---- b/internal/lockedfile/internal/filelock/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 -+++ c/internal/lockedfile/internal/filelock/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,71 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -+ -+go_library( -+ name = "filelock", -+ srcs = [ -+ "filelock.go", -+ "filelock_fcntl.go", -+ "filelock_other.go", -+ "filelock_plan9.go", -+ "filelock_unix.go", -+ "filelock_windows.go", -+ ], -+ importpath = "golang.org/x/tools/internal/lockedfile/internal/filelock", -+ visibility = ["//:__subpackages__"], -+ deps = select({ -+ "@io_bazel_rules_go//go/platform:windows": [ -+ "@org_golang_x_sys//windows", -+ ], -+ "//conditions:default": [], -+ }), -+) -+ -+alias( -+ name = "go_default_library", -+ actual = ":filelock", -+ visibility = ["//:__subpackages__"], -+) -+ -+go_test( -+ name = "filelock_test", -+ srcs = ["filelock_test.go"], -+ deps = select({ -+ "@io_bazel_rules_go//go/platform:aix": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:android": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:darwin": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:dragonfly": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:freebsd": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:illumos": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:ios": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:linux": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:netbsd": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:openbsd": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:solaris": [ -+ ":filelock", -+ ], -+ "@io_bazel_rules_go//go/platform:windows": [ -+ ":filelock", -+ ], -+ "//conditions:default": [], -+ }), -+) diff -urN b/internal/memoize/BUILD.bazel c/internal/memoize/BUILD.bazel ---- b/internal/memoize/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/memoize/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/memoize/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -9882,7 +10047,7 @@ diff -urN b/internal/memoize/BUILD.bazel c/internal/memoize/BUILD.bazel + deps = [":memoize"], +) diff -urN b/internal/packagesinternal/BUILD.bazel c/internal/packagesinternal/BUILD.bazel ---- b/internal/packagesinternal/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/packagesinternal/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/packagesinternal/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9901,16 +10066,20 @@ diff -urN b/internal/packagesinternal/BUILD.bazel c/internal/packagesinternal/BU + visibility = ["//:__subpackages__"], +) diff -urN b/internal/persistent/BUILD.bazel c/internal/persistent/BUILD.bazel ---- b/internal/persistent/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/persistent/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/persistent/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,20 @@ +@@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "persistent", -+ srcs = ["map.go"], ++ srcs = [ ++ "map.go", ++ "set.go", ++ ], + importpath = "golang.org/x/tools/internal/persistent", + visibility = ["//:__subpackages__"], ++ deps = ["//internal/constraints"], +) + +alias( @@ -9921,11 +10090,15 @@ diff -urN b/internal/persistent/BUILD.bazel c/internal/persistent/BUILD.bazel + +go_test( + name = "persistent_test", -+ srcs = ["map_test.go"], ++ srcs = [ ++ "map_test.go", ++ "set_test.go", ++ ], + embed = [":persistent"], ++ deps = ["//internal/constraints"], +) diff -urN b/internal/pkgbits/BUILD.bazel c/internal/pkgbits/BUILD.bazel ---- b/internal/pkgbits/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/pkgbits/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/pkgbits/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -9954,8 +10127,33 @@ diff -urN b/internal/pkgbits/BUILD.bazel c/internal/pkgbits/BUILD.bazel + actual = ":pkgbits", + visibility = ["//:__subpackages__"], +) +diff -urN b/internal/pprof/BUILD.bazel c/internal/pprof/BUILD.bazel +--- b/internal/pprof/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/pprof/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,21 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") ++ ++go_library( ++ name = "pprof", ++ srcs = ["pprof.go"], ++ importpath = "golang.org/x/tools/internal/pprof", ++ visibility = ["//:__subpackages__"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":pprof", ++ visibility = ["//:__subpackages__"], ++) ++ ++go_test( ++ name = "pprof_test", ++ srcs = ["pprof_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), ++ deps = [":pprof"], ++) diff -urN b/internal/proxydir/BUILD.bazel c/internal/proxydir/BUILD.bazel ---- b/internal/proxydir/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/proxydir/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/proxydir/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -9979,8 +10177,134 @@ diff -urN b/internal/proxydir/BUILD.bazel c/internal/proxydir/BUILD.bazel + srcs = ["proxydir_test.go"], + embed = [":proxydir"], +) +diff -urN b/internal/refactor/inline/BUILD.bazel c/internal/refactor/inline/BUILD.bazel +--- b/internal/refactor/inline/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/refactor/inline/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,50 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") ++ ++go_library( ++ name = "inline", ++ srcs = [ ++ "callee.go", ++ "calleefx.go", ++ "doc.go", ++ "escape.go", ++ "falcon.go", ++ "inline.go", ++ "util.go", ++ ], ++ importpath = "golang.org/x/tools/internal/refactor/inline", ++ visibility = ["//:__subpackages__"], ++ deps = [ ++ "//go/ast/astutil", ++ "//go/types/typeutil", ++ "//imports", ++ "//internal/typeparams", ++ ], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":inline", ++ visibility = ["//:__subpackages__"], ++) ++ ++go_test( ++ name = "inline_test", ++ srcs = [ ++ "calleefx_test.go", ++ "everything_test.go", ++ "export_test.go", ++ "falcon_test.go", ++ "inline_test.go", ++ ], ++ data = glob(["testdata/**"], allow_empty = True), ++ embed = [":inline"], ++ deps = [ ++ "//go/ast/astutil", ++ "//go/expect", ++ "//go/packages", ++ "//go/types/typeutil", ++ "//internal/diff", ++ "//internal/testenv", ++ "//txtar", ++ ], ++) +diff -urN b/internal/refactor/inline/analyzer/BUILD.bazel c/internal/refactor/inline/analyzer/BUILD.bazel +--- b/internal/refactor/inline/analyzer/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/refactor/inline/analyzer/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,32 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") ++ ++go_library( ++ name = "analyzer", ++ srcs = ["analyzer.go"], ++ importpath = "golang.org/x/tools/internal/refactor/inline/analyzer", ++ visibility = ["//:__subpackages__"], ++ deps = [ ++ "//go/analysis", ++ "//go/analysis/passes/inspect", ++ "//go/ast/inspector", ++ "//go/types/typeutil", ++ "//internal/diff", ++ "//internal/refactor/inline", ++ ], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":analyzer", ++ visibility = ["//:__subpackages__"], ++) ++ ++go_test( ++ name = "analyzer_test", ++ srcs = ["analyzer_test.go"], ++ data = glob(["testdata/**"], allow_empty = True), ++ deps = [ ++ ":analyzer", ++ "//go/analysis/analysistest", ++ ], ++) +diff -urN b/internal/refactor/inline/analyzer/testdata/src/a/BUILD.bazel c/internal/refactor/inline/analyzer/testdata/src/a/BUILD.bazel +--- b/internal/refactor/inline/analyzer/testdata/src/a/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/refactor/inline/analyzer/testdata/src/a/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,14 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "a", ++ srcs = ["a.go"], ++ importpath = "golang.org/x/tools/internal/refactor/inline/analyzer/testdata/src/a", ++ visibility = ["//:__subpackages__"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":a", ++ visibility = ["//:__subpackages__"], ++) +diff -urN b/internal/refactor/inline/analyzer/testdata/src/b/BUILD.bazel c/internal/refactor/inline/analyzer/testdata/src/b/BUILD.bazel +--- b/internal/refactor/inline/analyzer/testdata/src/b/BUILD.bazel 1970-01-01 08:00:00 ++++ c/internal/refactor/inline/analyzer/testdata/src/b/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 +@@ -0,0 +1,14 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library") ++ ++go_library( ++ name = "b", ++ srcs = ["b.go"], ++ importpath = "golang.org/x/tools/internal/refactor/inline/analyzer/testdata/src/b", ++ visibility = ["//:__subpackages__"], ++) ++ ++alias( ++ name = "go_default_library", ++ actual = ":b", ++ visibility = ["//:__subpackages__"], ++) diff -urN b/internal/robustio/BUILD.bazel c/internal/robustio/BUILD.bazel ---- b/internal/robustio/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/robustio/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/robustio/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -10013,7 +10337,7 @@ diff -urN b/internal/robustio/BUILD.bazel c/internal/robustio/BUILD.bazel + deps = [":robustio"], +) diff -urN b/internal/stack/BUILD.bazel c/internal/stack/BUILD.bazel ---- b/internal/stack/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/stack/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/stack/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -10041,7 +10365,7 @@ diff -urN b/internal/stack/BUILD.bazel c/internal/stack/BUILD.bazel + deps = [":stack"], +) diff -urN b/internal/stack/gostacks/BUILD.bazel c/internal/stack/gostacks/BUILD.bazel ---- b/internal/stack/gostacks/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/stack/gostacks/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/stack/gostacks/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") @@ -10060,7 +10384,7 @@ diff -urN b/internal/stack/gostacks/BUILD.bazel c/internal/stack/gostacks/BUILD. + visibility = ["//:__subpackages__"], +) diff -urN b/internal/stack/stacktest/BUILD.bazel c/internal/stack/stacktest/BUILD.bazel ---- b/internal/stack/stacktest/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/stack/stacktest/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/stack/stacktest/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -10079,9 +10403,9 @@ diff -urN b/internal/stack/stacktest/BUILD.bazel c/internal/stack/stacktest/BUIL + visibility = ["//:__subpackages__"], +) diff -urN b/internal/testenv/BUILD.bazel c/internal/testenv/BUILD.bazel ---- b/internal/testenv/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/testenv/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/testenv/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,23 @@ +@@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( @@ -10096,6 +10420,7 @@ diff -urN b/internal/testenv/BUILD.bazel c/internal/testenv/BUILD.bazel + visibility = ["//:__subpackages__"], + deps = [ + "//internal/goroot", ++ "@org_golang_x_mod//modfile:go_default_library", + "@org_golang_x_sys//execabs:go_default_library", + ], +) @@ -10106,10 +10431,10 @@ diff -urN b/internal/testenv/BUILD.bazel c/internal/testenv/BUILD.bazel + visibility = ["//:__subpackages__"], +) diff -urN b/internal/tokeninternal/BUILD.bazel c/internal/tokeninternal/BUILD.bazel ---- b/internal/tokeninternal/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/tokeninternal/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/tokeninternal/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 -@@ -0,0 +1,14 @@ -+load("@io_bazel_rules_go//go:def.bzl", "go_library") +@@ -0,0 +1,20 @@ ++load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "tokeninternal", @@ -10123,8 +10448,14 @@ diff -urN b/internal/tokeninternal/BUILD.bazel c/internal/tokeninternal/BUILD.ba + actual = ":tokeninternal", + visibility = ["//:__subpackages__"], +) ++ ++go_test( ++ name = "tokeninternal_test", ++ srcs = ["tokeninternal_test.go"], ++ deps = [":tokeninternal"], ++) diff -urN b/internal/tool/BUILD.bazel c/internal/tool/BUILD.bazel ---- b/internal/tool/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/tool/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/tool/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -10142,7 +10473,7 @@ diff -urN b/internal/tool/BUILD.bazel c/internal/tool/BUILD.bazel + visibility = ["//:__subpackages__"], +) diff -urN b/internal/typeparams/BUILD.bazel c/internal/typeparams/BUILD.bazel ---- b/internal/typeparams/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/typeparams/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/typeparams/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -10185,7 +10516,7 @@ diff -urN b/internal/typeparams/BUILD.bazel c/internal/typeparams/BUILD.bazel + ], +) diff -urN b/internal/typeparams/genericfeatures/BUILD.bazel c/internal/typeparams/genericfeatures/BUILD.bazel ---- b/internal/typeparams/genericfeatures/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/typeparams/genericfeatures/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/typeparams/genericfeatures/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -10207,7 +10538,7 @@ diff -urN b/internal/typeparams/genericfeatures/BUILD.bazel c/internal/typeparam + visibility = ["//:__subpackages__"], +) diff -urN b/internal/typesinternal/BUILD.bazel c/internal/typesinternal/BUILD.bazel ---- b/internal/typesinternal/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/typesinternal/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/typesinternal/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -10217,12 +10548,12 @@ diff -urN b/internal/typesinternal/BUILD.bazel c/internal/typesinternal/BUILD.ba + srcs = [ + "errorcode.go", + "errorcode_string.go", ++ "objectpath.go", + "types.go", + "types_118.go", + ], + importpath = "golang.org/x/tools/internal/typesinternal", + visibility = ["//:__subpackages__"], -+ deps = ["//go/types/objectpath"], +) + +alias( @@ -10236,7 +10567,7 @@ diff -urN b/internal/typesinternal/BUILD.bazel c/internal/typesinternal/BUILD.ba + srcs = ["errorcode_test.go"], +) diff -urN b/internal/xcontext/BUILD.bazel c/internal/xcontext/BUILD.bazel ---- b/internal/xcontext/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/internal/xcontext/BUILD.bazel 1970-01-01 08:00:00 +++ c/internal/xcontext/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -10254,7 +10585,7 @@ diff -urN b/internal/xcontext/BUILD.bazel c/internal/xcontext/BUILD.bazel + visibility = ["//:__subpackages__"], +) diff -urN b/playground/BUILD.bazel c/playground/BUILD.bazel ---- b/playground/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/playground/BUILD.bazel 1970-01-01 08:00:00 +++ c/playground/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") @@ -10272,7 +10603,7 @@ diff -urN b/playground/BUILD.bazel c/playground/BUILD.bazel + visibility = ["//visibility:public"], +) diff -urN b/playground/socket/BUILD.bazel c/playground/socket/BUILD.bazel ---- b/playground/socket/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/playground/socket/BUILD.bazel 1970-01-01 08:00:00 +++ c/playground/socket/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -10301,7 +10632,7 @@ diff -urN b/playground/socket/BUILD.bazel c/playground/socket/BUILD.bazel + embed = [":socket"], +) diff -urN b/present/BUILD.bazel c/present/BUILD.bazel ---- b/present/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/present/BUILD.bazel 1970-01-01 08:00:00 +++ c/present/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,44 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -10345,11 +10676,11 @@ diff -urN b/present/BUILD.bazel c/present/BUILD.bazel + "parse_test.go", + "style_test.go", + ], -+ data = glob(["testdata/**"]), ++ data = glob(["testdata/**"], allow_empty = True), + embed = [":present"], +) diff -urN b/refactor/eg/BUILD.bazel c/refactor/eg/BUILD.bazel ---- b/refactor/eg/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/refactor/eg/BUILD.bazel 1970-01-01 08:00:00 +++ c/refactor/eg/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,93 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -10375,7 +10706,7 @@ diff -urN b/refactor/eg/BUILD.bazel c/refactor/eg/BUILD.bazel +go_test( + name = "eg_test", + srcs = ["eg_test.go"], -+ data = glob(["testdata/**"]), ++ data = glob(["testdata/**"], allow_empty = True), + deps = select({ + "@io_bazel_rules_go//go/platform:aix": [ + ":eg", @@ -10446,7 +10777,7 @@ diff -urN b/refactor/eg/BUILD.bazel c/refactor/eg/BUILD.bazel + }), +) diff -urN b/refactor/importgraph/BUILD.bazel c/refactor/importgraph/BUILD.bazel ---- b/refactor/importgraph/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/refactor/importgraph/BUILD.bazel 1970-01-01 08:00:00 +++ c/refactor/importgraph/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,75 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -10525,7 +10856,7 @@ diff -urN b/refactor/importgraph/BUILD.bazel c/refactor/importgraph/BUILD.bazel + }), +) diff -urN b/refactor/rename/BUILD.bazel c/refactor/rename/BUILD.bazel ---- b/refactor/rename/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/refactor/rename/BUILD.bazel 1970-01-01 08:00:00 +++ c/refactor/rename/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,42 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -10571,7 +10902,7 @@ diff -urN b/refactor/rename/BUILD.bazel c/refactor/rename/BUILD.bazel + ], +) diff -urN b/refactor/satisfy/BUILD.bazel c/refactor/satisfy/BUILD.bazel ---- b/refactor/satisfy/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/refactor/satisfy/BUILD.bazel 1970-01-01 08:00:00 +++ c/refactor/satisfy/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") @@ -10603,7 +10934,7 @@ diff -urN b/refactor/satisfy/BUILD.bazel c/refactor/satisfy/BUILD.bazel + ], +) diff -urN b/txtar/BUILD.bazel c/txtar/BUILD.bazel ---- b/txtar/BUILD.bazel 1970-01-01 00:00:00.000000000 +0000 +--- b/txtar/BUILD.bazel 1970-01-01 08:00:00 +++ c/txtar/BUILD.bazel 2000-01-01 00:00:00.000000000 -0000 @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")