all: merge master (3fca6a0) into gopls-release-branch.0.7
Merge List:
+ 2021-12-08 3fca6a08 cmd/gorename: log 'go build' output on failure
+ 2021-12-08 cadd57e3 docs: updated nvim-lspconfig link
+ 2021-12-07 fd2bfb79 go/analysis/passes/stdmethods: recognize any as alias for interface{}, for errors.As check
+ 2021-12-07 68cbf416 internal/lsp/template: add missed hover cases
+ 2021-12-07 d3358c1f go/internal/gcimporter: fix test for Go 1.18 any
+ 2021-12-06 feb39d0c internal/lsp/source: don't format generated files
+ 2021-12-03 c882a49e gopls/doc: fix rendering of example for the infertypeargs analyzer
+ 2021-12-03 f64c0f46 internal/lsp/analysis/fillreturns: update fillreturns for new type errs
+ 2021-12-02 e212aff8 internal/memoize: do not allow (*Generation).Acquire to fail
+ 2021-12-02 2ac48c60 go/types/typeutil: add support for mapping generic types
+ 2021-12-01 df48029e go/internal/gcimporter: allow reusing empty interfaces on the RHS of type decls
+ 2021-12-01 d99d6fae internal/lsp/protocol: fix whitespace in comments
+ 2021-12-01 3c63f308 gopls/internal/regtest/misc: temporarily skip TestGenerateProgress
+ 2021-12-01 615f9a6b internal/lsp/protocol: bring the LSP stubs up to date
+ 2021-11-30 1fd30d29 refactor/importgraph: set env from packagestest.Export and check errors from Build
+ 2021-11-30 2c9b078f internal/memoize: record the caller of Destroy
+ 2021-11-29 6e52f51f x/tools: temporarily skip a couple of tests
+ 2021-11-29 a6189239 internal/lsp/template: fix error that causes crashes
+ 2021-11-24 cb80a01b cmd/godoc: remove extra // characters from deprecation notice
+ 2021-11-23 7cf1f382 go/ssa: remove deprecated FindTests and CreateTestMainPackage
+ 2021-11-23 1e71a25a gopls: template suffix flags and documentation
+ 2021-11-22 c2c92fd2 go/callgraph/vta/internal/trie: fix build with go1.12
+ 2021-11-19 d0c72119 internal/lsp/cache: fix resolution of the go directive in multi-module workspaces
Change-Id: I35eaffd1d914065a619cb71a96f6ce3cc2815888
diff --git a/cmd/godoc/doc.go b/cmd/godoc/doc.go
index 14f5c3e..279b2b1 100644
--- a/cmd/godoc/doc.go
+++ b/cmd/godoc/doc.go
@@ -108,7 +108,7 @@
See "Godoc: documenting Go code" for how to write good comments for godoc:
https://golang.org/doc/articles/godoc_documenting_go_code.html
-// Deprecated: godoc cannot select what version of a package is displayed.
-// Instead, use golang.org/x/pkgsite/cmd/pkgsite.
+Deprecated: godoc cannot select what version of a package is displayed.
+Instead, use golang.org/x/pkgsite/cmd/pkgsite.
*/
package main // import "golang.org/x/tools/cmd/godoc"
diff --git a/cmd/gorename/gorename_test.go b/cmd/gorename/gorename_test.go
index 2928051..30b8796 100644
--- a/cmd/gorename/gorename_test.go
+++ b/cmd/gorename/gorename_test.go
@@ -331,8 +331,8 @@
bin += ".exe"
}
cmd := exec.Command("go", "build", "-o", bin)
- if err := cmd.Run(); err != nil {
- t.Fatalf("Building gorename: %v", err)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("Building gorename: %v\n%s", err, out)
}
return tmp, bin, func() { os.RemoveAll(tmp) }
}
diff --git a/go/analysis/passes/stdmethods/stdmethods.go b/go/analysis/passes/stdmethods/stdmethods.go
index 64a28ac..cc94971 100644
--- a/go/analysis/passes/stdmethods/stdmethods.go
+++ b/go/analysis/passes/stdmethods/stdmethods.go
@@ -61,7 +61,7 @@
// we let it go. But if it does have a fmt.ScanState, then the
// rest has to match.
var canonicalMethods = map[string]struct{ args, results []string }{
- "As": {[]string{"interface{}"}, []string{"bool"}}, // errors.As
+ "As": {[]string{"any"}, []string{"bool"}}, // errors.As
// "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
"Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter
"GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder
@@ -194,7 +194,9 @@
func matchParamType(expect string, actual types.Type) bool {
expect = strings.TrimPrefix(expect, "=")
// Overkill but easy.
- return typeString(actual) == expect
+ t := typeString(actual)
+ return t == expect ||
+ (t == "any" || t == "interface{}") && (expect == "any" || expect == "interface{}")
}
var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
diff --git a/go/analysis/passes/stdmethods/testdata/src/a/a.go b/go/analysis/passes/stdmethods/testdata/src/a/a.go
index 7f9e9ae..c95cf5d 100644
--- a/go/analysis/passes/stdmethods/testdata/src/a/a.go
+++ b/go/analysis/passes/stdmethods/testdata/src/a/a.go
@@ -47,7 +47,7 @@
func (E) Error() string { return "" } // E implements error.
-func (E) As() {} // want `method As\(\) should have signature As\(interface{}\) bool`
+func (E) As() {} // want `method As\(\) should have signature As\((any|interface\{\})\) bool`
func (E) Is() {} // want `method Is\(\) should have signature Is\(error\) bool`
func (E) Unwrap() {} // want `method Unwrap\(\) should have signature Unwrap\(\) error`
@@ -55,6 +55,10 @@
func (F) Error() string { return "" } // Both F and *F implement error.
-func (*F) As() {} // want `method As\(\) should have signature As\(interface{}\) bool`
+func (*F) As() {} // want `method As\(\) should have signature As\((any|interface\{\})\) bool`
func (*F) Is() {} // want `method Is\(\) should have signature Is\(error\) bool`
func (*F) Unwrap() {} // want `method Unwrap\(\) should have signature Unwrap\(\) error`
+
+type G int
+
+func (G) As(interface{}) bool // ok
diff --git a/go/analysis/passes/stdmethods/testdata/src/a/b.go b/go/analysis/passes/stdmethods/testdata/src/a/b.go
new file mode 100644
index 0000000..c0a16fb
--- /dev/null
+++ b/go/analysis/passes/stdmethods/testdata/src/a/b.go
@@ -0,0 +1,12 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.18
+// +build go1.18
+
+package a
+
+type H int
+
+func (H) As(any) bool // ok
diff --git a/go/analysis/passes/stdmethods/testdata/src/typeparams/typeparams.go b/go/analysis/passes/stdmethods/testdata/src/typeparams/typeparams.go
index 8ff8b35..72df30d 100644
--- a/go/analysis/passes/stdmethods/testdata/src/typeparams/typeparams.go
+++ b/go/analysis/passes/stdmethods/testdata/src/typeparams/typeparams.go
@@ -28,7 +28,7 @@
func (E[_]) Error() string { return "" } // E implements error.
-func (E[P]) As() {} // want `method As\(\) should have signature As\(interface{}\) bool`
+func (E[P]) As() {} // want `method As\(\) should have signature As\((any|interface\{\})\) bool`
func (E[_]) Is() {} // want `method Is\(\) should have signature Is\(error\) bool`
func (E[_]) Unwrap() {} // want `method Unwrap\(\) should have signature Unwrap\(\) error`
@@ -36,6 +36,6 @@
func (F[_]) Error() string { return "" } // Both F and *F implement error.
-func (*F[_]) As() {} // want `method As\(\) should have signature As\(interface{}\) bool`
+func (*F[_]) As() {} // want `method As\(\) should have signature As\((any|interface\{\})\) bool`
func (*F[_]) Is() {} // want `method Is\(\) should have signature Is\(error\) bool`
func (*F[_]) Unwrap() {} // want `method Unwrap\(\) should have signature Unwrap\(\) error`
diff --git a/go/callgraph/vta/internal/trie/bits.go b/go/callgraph/vta/internal/trie/bits.go
index c343d29..f2fd0ba 100644
--- a/go/callgraph/vta/internal/trie/bits.go
+++ b/go/callgraph/vta/internal/trie/bits.go
@@ -46,7 +46,7 @@
if p == 0 {
return 0
}
- return 1 << (bits.Len64(uint64(p)) - 1)
+ return bitpos(1) << uint(bits.Len64(uint64(p))-1) // uint conversion needed for go1.12
}
// zeroBit returns true if k has a 0 bit at position `b`.
diff --git a/go/callgraph/vta/internal/trie/bits_test.go b/go/callgraph/vta/internal/trie/bits_test.go
index f19acdf..07784cd 100644
--- a/go/callgraph/vta/internal/trie/bits_test.go
+++ b/go/callgraph/vta/internal/trie/bits_test.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.13
+// +build go1.13
+
package trie
import (
diff --git a/go/callgraph/vta/internal/trie/trie_test.go b/go/callgraph/vta/internal/trie/trie_test.go
index 68abb5b..c0651b0 100644
--- a/go/callgraph/vta/internal/trie/trie_test.go
+++ b/go/callgraph/vta/internal/trie/trie_test.go
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+//go:build go1.13
+// +build go1.13
+
package trie
import (
diff --git a/go/internal/gcimporter/gcimporter_test.go b/go/internal/gcimporter/gcimporter_test.go
index 9081cfa..1d61cfc 100644
--- a/go/internal/gcimporter/gcimporter_test.go
+++ b/go/internal/gcimporter/gcimporter_test.go
@@ -10,6 +10,7 @@
import (
"bytes"
"fmt"
+ "go/build"
"go/constant"
"go/types"
"io/ioutil"
@@ -255,7 +256,7 @@
{"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string)"},
// interfaces
- {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"},
+ {"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"},
{"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
{"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
@@ -264,6 +265,18 @@
{"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
}
+// TODO(rsc): Delete this init func after x/tools no longer needs to test successfully with Go 1.17.
+func init() {
+ if build.Default.ReleaseTags[len(build.Default.ReleaseTags)-1] <= "go1.17" {
+ for i := range importedObjectTests {
+ if importedObjectTests[i].name == "context.Context" {
+ // Expand any to interface{}.
+ importedObjectTests[i].want = "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"
+ }
+ }
+ }
+}
+
func TestImportedTypes(t *testing.T) {
testenv.NeedsGo1Point(t, 11)
// This package only handles gc export data.
@@ -275,6 +288,12 @@
continue // error reported elsewhere
}
got := types.ObjectString(obj, types.RelativeTo(obj.Pkg()))
+
+ // TODO(rsc): Delete this block once go.dev/cl/368254 lands.
+ if got != test.want && test.want == strings.ReplaceAll(got, "interface{}", "any") {
+ got = test.want
+ }
+
if got != test.want {
t.Errorf("%s: got %q; want %q", test.name, got, test.want)
}
diff --git a/go/internal/gcimporter/iexport_go118_test.go b/go/internal/gcimporter/iexport_go118_test.go
index 2dc5cc1..5fc1f86 100644
--- a/go/internal/gcimporter/iexport_go118_test.go
+++ b/go/internal/gcimporter/iexport_go118_test.go
@@ -23,10 +23,13 @@
"golang.org/x/tools/go/internal/gcimporter"
)
+// TODO(rfindley): migrate this to testdata, as has been done in the standard library.
func TestGenericExport(t *testing.T) {
const src = `
package generic
+type Any any
+
type T[A, B any] struct { Left A; Right B }
var X T[int, string] = T[int, string]{1, "hi"}
diff --git a/go/internal/gcimporter/iimport.go b/go/internal/gcimporter/iimport.go
index 3e31874..cdb332c 100644
--- a/go/internal/gcimporter/iimport.go
+++ b/go/internal/gcimporter/iimport.go
@@ -331,7 +331,7 @@
}
func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
- if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
+ if t, ok := p.typCache[off]; ok && canReuse(base, t) {
return t
}
@@ -343,12 +343,30 @@
r.declReader.Reset(p.declData[off-predeclReserved:])
t := r.doType(base)
- if base == nil || !isInterface(t) {
+ if canReuse(base, t) {
p.typCache[off] = t
}
return t
}
+// canReuse reports whether the type rhs on the RHS of the declaration for def
+// may be re-used.
+//
+// Specifically, if def is non-nil and rhs is an interface type with methods, it
+// may not be re-used because we have a convention of setting the receiver type
+// for interface methods to def.
+func canReuse(def *types.Named, rhs types.Type) bool {
+ if def == nil {
+ return true
+ }
+ iface, _ := rhs.(*types.Interface)
+ if iface == nil {
+ return true
+ }
+ // Don't use iface.Empty() here as iface may not be complete.
+ return iface.NumEmbeddeds() == 0 && iface.NumExplicitMethods() == 0
+}
+
type importReader struct {
p *iimporter
declReader bytes.Reader
diff --git a/go/ssa/testmain.go b/go/ssa/testmain.go
deleted file mode 100644
index b748332..0000000
--- a/go/ssa/testmain.go
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright 2013 The Go 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 ssa
-
-// CreateTestMainPackage synthesizes a main package that runs all the
-// tests of the supplied packages.
-// It is closely coupled to $GOROOT/src/cmd/go/test.go and $GOROOT/src/testing.
-//
-// TODO(adonovan): throws this all away now that x/tools/go/packages
-// provides access to the actual synthetic test main files.
-
-import (
- "bytes"
- "fmt"
- "go/ast"
- "go/parser"
- "go/types"
- "log"
- "os"
- "strings"
- "text/template"
-
- "golang.org/x/tools/internal/typeparams"
-)
-
-// FindTests returns the Test, Benchmark, and Example functions
-// (as defined by "go test") defined in the specified package,
-// and its TestMain function, if any.
-//
-// Deprecated: Use golang.org/x/tools/go/packages to access synthetic
-// testmain packages.
-func FindTests(pkg *Package) (tests, benchmarks, examples []*Function, main *Function) {
- prog := pkg.Prog
-
- // The first two of these may be nil: if the program doesn't import "testing",
- // it can't contain any tests, but it may yet contain Examples.
- var testSig *types.Signature // func(*testing.T)
- var benchmarkSig *types.Signature // func(*testing.B)
- var exampleSig = types.NewSignature(nil, nil, nil, false) // func()
-
- // Obtain the types from the parameters of testing.MainStart.
- if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
- mainStart := testingPkg.Func("MainStart")
- params := mainStart.Signature.Params()
- testSig = funcField(params.At(1).Type())
- benchmarkSig = funcField(params.At(2).Type())
-
- // Does the package define this function?
- // func TestMain(*testing.M)
- if f := pkg.Func("TestMain"); f != nil {
- sig := f.Type().(*types.Signature)
- starM := mainStart.Signature.Results().At(0).Type() // *testing.M
- if sig.Results().Len() == 0 &&
- sig.Params().Len() == 1 &&
- types.Identical(sig.Params().At(0).Type(), starM) {
- main = f
- }
- }
- }
-
- // TODO(adonovan): use a stable order, e.g. lexical.
- for _, mem := range pkg.Members {
- if f, ok := mem.(*Function); ok &&
- ast.IsExported(f.Name()) &&
- strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") {
-
- switch {
- case testSig != nil && isTestSig(f, "Test", testSig):
- tests = append(tests, f)
- case benchmarkSig != nil && isTestSig(f, "Benchmark", benchmarkSig):
- benchmarks = append(benchmarks, f)
- case isTestSig(f, "Example", exampleSig):
- examples = append(examples, f)
- default:
- continue
- }
- }
- }
- return
-}
-
-// Like isTest, but checks the signature too.
-func isTestSig(f *Function, prefix string, sig *types.Signature) bool {
- return isTest(f.Name(), prefix) && types.Identical(f.Signature, sig)
-}
-
-// Given the type of one of the three slice parameters of testing.Main,
-// returns the function type.
-func funcField(slice types.Type) *types.Signature {
- return slice.(*types.Slice).Elem().Underlying().(*types.Struct).Field(1).Type().(*types.Signature)
-}
-
-// isTest tells whether name looks like a test (or benchmark, according to prefix).
-// It is a Test (say) if there is a character after Test that is not a lower-case letter.
-// We don't want TesticularCancer.
-// Plundered from $GOROOT/src/cmd/go/test.go
-func isTest(name, prefix string) bool {
- if !strings.HasPrefix(name, prefix) {
- return false
- }
- if len(name) == len(prefix) { // "Test" is ok
- return true
- }
- return ast.IsExported(name[len(prefix):])
-}
-
-// CreateTestMainPackage creates and returns a synthetic "testmain"
-// package for the specified package if it defines tests, benchmarks or
-// executable examples, or nil otherwise. The new package is named
-// "main" and provides a function named "main" that runs the tests,
-// similar to the one that would be created by the 'go test' tool.
-//
-// Subsequent calls to prog.AllPackages include the new package.
-// The package pkg must belong to the program prog.
-//
-// Deprecated: Use golang.org/x/tools/go/packages to access synthetic
-// testmain packages.
-func (prog *Program) CreateTestMainPackage(pkg *Package) *Package {
- if pkg.Prog != prog {
- log.Fatal("Package does not belong to Program")
- }
-
- // Template data
- var data struct {
- Pkg *Package
- Tests, Benchmarks, Examples []*Function
- Main *Function
- Go18 bool
- }
- data.Pkg = pkg
-
- // Enumerate tests.
- data.Tests, data.Benchmarks, data.Examples, data.Main = FindTests(pkg)
- if data.Main == nil &&
- data.Tests == nil && data.Benchmarks == nil && data.Examples == nil {
- return nil
- }
-
- // Synthesize source for testmain package.
- path := pkg.Pkg.Path() + "$testmain"
- tmpl := testmainTmpl
- if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
- // In Go 1.8, testing.MainStart's first argument is an interface, not a func.
- data.Go18 = types.IsInterface(testingPkg.Func("MainStart").Signature.Params().At(0).Type())
- } else {
- // The program does not import "testing", but FindTests
- // returned non-nil, which must mean there were Examples
- // but no Test, Benchmark, or TestMain functions.
-
- // We'll simply call them from testmain.main; this will
- // ensure they don't panic, but will not check any
- // "Output:" comments.
- // (We should not execute an Example that has no
- // "Output:" comment, but it's impossible to tell here.)
- tmpl = examplesOnlyTmpl
- }
- var buf bytes.Buffer
- if err := tmpl.Execute(&buf, data); err != nil {
- log.Fatalf("internal error expanding template for %s: %v", path, err)
- }
- if false { // debugging
- fmt.Fprintln(os.Stderr, buf.String())
- }
-
- // Parse and type-check the testmain package.
- f, err := parser.ParseFile(prog.Fset, path+".go", &buf, parser.Mode(0))
- if err != nil {
- log.Fatalf("internal error parsing %s: %v", path, err)
- }
- conf := types.Config{
- DisableUnusedImportCheck: true,
- Importer: importer{pkg},
- }
- files := []*ast.File{f}
- info := &types.Info{
- Types: make(map[ast.Expr]types.TypeAndValue),
- Defs: make(map[*ast.Ident]types.Object),
- Uses: make(map[*ast.Ident]types.Object),
- Implicits: make(map[ast.Node]types.Object),
- Scopes: make(map[ast.Node]*types.Scope),
- Selections: make(map[*ast.SelectorExpr]*types.Selection),
- }
- typeparams.InitInstanceInfo(info)
- testmainPkg, err := conf.Check(path, prog.Fset, files, info)
- if err != nil {
- log.Fatalf("internal error type-checking %s: %v", path, err)
- }
-
- // Create and build SSA code.
- testmain := prog.CreatePackage(testmainPkg, files, info, false)
- testmain.SetDebugMode(false)
- testmain.Build()
- testmain.Func("main").Synthetic = "test main function"
- testmain.Func("init").Synthetic = "package initializer"
- return testmain
-}
-
-// An implementation of types.Importer for an already loaded SSA program.
-type importer struct {
- pkg *Package // package under test; may be non-importable
-}
-
-func (imp importer) Import(path string) (*types.Package, error) {
- if p := imp.pkg.Prog.ImportedPackage(path); p != nil {
- return p.Pkg, nil
- }
- if path == imp.pkg.Pkg.Path() {
- return imp.pkg.Pkg, nil
- }
- return nil, fmt.Errorf("not found") // can't happen
-}
-
-var testmainTmpl = template.Must(template.New("testmain").Parse(`
-package main
-
-import "io"
-import "os"
-import "testing"
-import p {{printf "%q" .Pkg.Pkg.Path}}
-
-{{if .Go18}}
-type deps struct{}
-
-func (deps) ImportPath() string { return "" }
-func (deps) MatchString(pat, str string) (bool, error) { return true, nil }
-func (deps) SetPanicOnExit0(bool) {}
-func (deps) StartCPUProfile(io.Writer) error { return nil }
-func (deps) StartTestLog(io.Writer) {}
-func (deps) StopCPUProfile() {}
-func (deps) StopTestLog() error { return nil }
-func (deps) WriteHeapProfile(io.Writer) error { return nil }
-func (deps) WriteProfileTo(string, io.Writer, int) error { return nil }
-
-var match deps
-{{else}}
-func match(_, _ string) (bool, error) { return true, nil }
-{{end}}
-
-func main() {
- tests := []testing.InternalTest{
-{{range .Tests}}
- { {{printf "%q" .Name}}, p.{{.Name}} },
-{{end}}
- }
- benchmarks := []testing.InternalBenchmark{
-{{range .Benchmarks}}
- { {{printf "%q" .Name}}, p.{{.Name}} },
-{{end}}
- }
- examples := []testing.InternalExample{
-{{range .Examples}}
- {Name: {{printf "%q" .Name}}, F: p.{{.Name}}},
-{{end}}
- }
- m := testing.MainStart(match, tests, benchmarks, examples)
-{{with .Main}}
- p.{{.Name}}(m)
-{{else}}
- os.Exit(m.Run())
-{{end}}
-}
-
-`))
-
-var examplesOnlyTmpl = template.Must(template.New("examples").Parse(`
-package main
-
-import p {{printf "%q" .Pkg.Pkg.Path}}
-
-func main() {
-{{range .Examples}}
- p.{{.Name}}()
-{{end}}
-}
-`))
diff --git a/go/ssa/testmain_test.go b/go/ssa/testmain_test.go
deleted file mode 100644
index e24b23b..0000000
--- a/go/ssa/testmain_test.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2014 The Go 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 ssa_test
-
-// Tests of FindTests. CreateTestMainPackage is tested via the interpreter.
-// TODO(adonovan): test the 'pkgs' result from FindTests.
-
-import (
- "fmt"
- "sort"
- "testing"
-
- "golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/ssa/ssautil"
-)
-
-func create(t *testing.T, content string) *ssa.Package {
- var conf loader.Config
- f, err := conf.ParseFile("foo_test.go", content)
- if err != nil {
- t.Fatal(err)
- }
- conf.CreateFromFiles("foo", f)
-
- lprog, err := conf.Load()
- if err != nil {
- t.Fatal(err)
- }
-
- // We needn't call Build.
- foo := lprog.Package("foo").Pkg
- return ssautil.CreateProgram(lprog, ssa.SanityCheckFunctions).Package(foo)
-}
-
-func TestFindTests(t *testing.T) {
- test := `
-package foo
-
-import "testing"
-
-type T int
-
-// Tests:
-func Test(t *testing.T) {}
-func TestA(t *testing.T) {}
-func TestB(t *testing.T) {}
-
-// Not tests:
-func testC(t *testing.T) {}
-func TestD() {}
-func testE(t *testing.T) int { return 0 }
-func (T) Test(t *testing.T) {}
-
-// Benchmarks:
-func Benchmark(*testing.B) {}
-func BenchmarkA(b *testing.B) {}
-func BenchmarkB(*testing.B) {}
-
-// Not benchmarks:
-func benchmarkC(t *testing.T) {}
-func BenchmarkD() {}
-func benchmarkE(t *testing.T) int { return 0 }
-func (T) Benchmark(t *testing.T) {}
-
-// Examples:
-func Example() {}
-func ExampleA() {}
-
-// Not examples:
-func exampleC() {}
-func ExampleD(t *testing.T) {}
-func exampleE() int { return 0 }
-func (T) Example() {}
-`
- pkg := create(t, test)
- tests, benchmarks, examples, _ := ssa.FindTests(pkg)
-
- sort.Sort(funcsByPos(tests))
- if got, want := fmt.Sprint(tests), "[foo.Test foo.TestA foo.TestB]"; got != want {
- t.Errorf("FindTests.tests = %s, want %s", got, want)
- }
-
- sort.Sort(funcsByPos(benchmarks))
- if got, want := fmt.Sprint(benchmarks), "[foo.Benchmark foo.BenchmarkA foo.BenchmarkB]"; got != want {
- t.Errorf("FindTests.benchmarks = %s, want %s", got, want)
- }
-
- sort.Sort(funcsByPos(examples))
- if got, want := fmt.Sprint(examples), "[foo.Example foo.ExampleA]"; got != want {
- t.Errorf("FindTests examples = %s, want %s", got, want)
- }
-}
-
-func TestFindTestsTesting(t *testing.T) {
- test := `
-package foo
-
-// foo does not import "testing", but defines Examples.
-
-func Example() {}
-func ExampleA() {}
-`
- pkg := create(t, test)
- tests, benchmarks, examples, _ := ssa.FindTests(pkg)
- if len(tests) > 0 {
- t.Errorf("FindTests.tests = %s, want none", tests)
- }
- if len(benchmarks) > 0 {
- t.Errorf("FindTests.benchmarks = %s, want none", benchmarks)
- }
- sort.Sort(funcsByPos(examples))
- if got, want := fmt.Sprint(examples), "[foo.Example foo.ExampleA]"; got != want {
- t.Errorf("FindTests examples = %s, want %s", got, want)
- }
-}
-
-type funcsByPos []*ssa.Function
-
-func (p funcsByPos) Len() int { return len(p) }
-func (p funcsByPos) Less(i, j int) bool { return p[i].Pos() < p[j].Pos() }
-func (p funcsByPos) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
diff --git a/go/types/typeutil/map.go b/go/types/typeutil/map.go
index c7f7545..490ee90 100644
--- a/go/types/typeutil/map.go
+++ b/go/types/typeutil/map.go
@@ -11,6 +11,8 @@
"fmt"
"go/types"
"reflect"
+
+ "golang.org/x/tools/internal/typeparams"
)
// Map is a hash-table-based mapping from types (types.Type) to
@@ -211,11 +213,29 @@
// Call MakeHasher to create a Hasher.
type Hasher struct {
memo map[types.Type]uint32
+
+ // ptrMap records pointer identity.
+ ptrMap map[interface{}]uint32
+
+ // sigTParams holds type parameters from the signature being hashed.
+ // Signatures are considered identical modulo renaming of type parameters, so
+ // within the scope of a signature type the identity of the signature's type
+ // parameters is just their index.
+ //
+ // Since the language does not currently support referring to uninstantiated
+ // generic types or functions, and instantiated signatures do not have type
+ // parameter lists, we should never encounter a second non-empty type
+ // parameter list when hashing a generic signature.
+ sigTParams *typeparams.TypeParamList
}
// MakeHasher returns a new Hasher instance.
func MakeHasher() Hasher {
- return Hasher{make(map[types.Type]uint32)}
+ return Hasher{
+ memo: make(map[types.Type]uint32),
+ ptrMap: make(map[interface{}]uint32),
+ sigTParams: nil,
+ }
}
// Hash computes a hash value for the given type t such that
@@ -273,17 +293,62 @@
if t.Variadic() {
hash *= 8863
}
+
+ // Use a separate hasher for types inside of the signature, where type
+ // parameter identity is modified to be (index, constraint). We must use a
+ // new memo for this hasher as type identity may be affected by this
+ // masking. For example, in func[T any](*T), the identity of *T depends on
+ // whether we are mapping the argument in isolation, or recursively as part
+ // of hashing the signature.
+ //
+ // We should never encounter a generic signature while hashing another
+ // generic signature, but defensively set sigTParams only if h.mask is
+ // unset.
+ tparams := typeparams.ForSignature(t)
+ if h.sigTParams == nil && tparams.Len() != 0 {
+ h = Hasher{
+ // There may be something more efficient than discarding the existing
+ // memo, but it would require detecting whether types are 'tainted' by
+ // references to type parameters.
+ memo: make(map[types.Type]uint32),
+ // Re-using ptrMap ensures that pointer identity is preserved in this
+ // hasher.
+ ptrMap: h.ptrMap,
+ sigTParams: tparams,
+ }
+ }
+
+ for i := 0; i < tparams.Len(); i++ {
+ tparam := tparams.At(i)
+ hash += 7 * h.Hash(tparam.Constraint())
+ }
+
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
+ case *typeparams.Union:
+ return h.hashUnion(t)
+
case *types.Interface:
+ // Interfaces are identical if they have the same set of methods, with
+ // identical names and types, and they have the same set of type
+ // restrictions. See go/types.identical for more details.
var hash uint32 = 9103
+
+ // Hash methods.
for i, n := 0, t.NumMethods(); i < n; i++ {
- // See go/types.identicalMethods for rationale.
// Method order is not significant.
// Ignore m.Pkg().
m := t.Method(i)
hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type())
}
+
+ // Hash type restrictions.
+ terms, err := typeparams.InterfaceTermSet(t)
+ // if err != nil t has invalid type restrictions.
+ if err == nil {
+ hash += h.hashTermSet(terms)
+ }
+
return hash
case *types.Map:
@@ -293,13 +358,22 @@
return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
case *types.Named:
- // Not safe with a copying GC; objects may move.
- return uint32(reflect.ValueOf(t.Obj()).Pointer())
+ hash := h.hashPtr(t.Obj())
+ targs := typeparams.NamedTypeArgs(t)
+ for i := 0; i < targs.Len(); i++ {
+ targ := targs.At(i)
+ hash += 2 * h.Hash(targ)
+ }
+ return hash
+
+ case *typeparams.TypeParam:
+ return h.hashTypeParam(t)
case *types.Tuple:
return h.hashTuple(t)
}
- panic(t)
+
+ panic(fmt.Sprintf("%T: %v", t, t))
}
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
@@ -311,3 +385,57 @@
}
return hash
}
+
+func (h Hasher) hashUnion(t *typeparams.Union) uint32 {
+ // Hash type restrictions.
+ terms, err := typeparams.UnionTermSet(t)
+ // if err != nil t has invalid type restrictions. Fall back on a non-zero
+ // hash.
+ if err != nil {
+ return 9151
+ }
+ return h.hashTermSet(terms)
+}
+
+func (h Hasher) hashTermSet(terms []*typeparams.Term) uint32 {
+ var hash uint32 = 9157 + 2*uint32(len(terms))
+ for _, term := range terms {
+ // term order is not significant.
+ termHash := h.Hash(term.Type())
+ if term.Tilde() {
+ termHash *= 9161
+ }
+ hash += 3 * termHash
+ }
+ return hash
+}
+
+// hashTypeParam returns a hash of the type parameter t, with a hash value
+// depending on whether t is contained in h.sigTParams.
+//
+// If h.sigTParams is set and contains t, then we are in the process of hashing
+// a signature, and the hash value of t must depend only on t's index and
+// constraint: signatures are considered identical modulo type parameter
+// renaming.
+//
+// Otherwise the hash of t depends only on t's pointer identity.
+func (h Hasher) hashTypeParam(t *typeparams.TypeParam) uint32 {
+ if h.sigTParams != nil {
+ i := t.Index()
+ if i >= 0 && i < h.sigTParams.Len() && t == h.sigTParams.At(i) {
+ return 9173 + 2*h.Hash(t.Constraint()) + 3*uint32(i)
+ }
+ }
+ return h.hashPtr(t.Obj())
+}
+
+// hashPtr hashes the pointer identity of ptr. It uses h.ptrMap to ensure that
+// pointers values are not dependent on the GC.
+func (h Hasher) hashPtr(ptr interface{}) uint32 {
+ if hash, ok := h.ptrMap[ptr]; ok {
+ return hash
+ }
+ hash := uint32(reflect.ValueOf(ptr).Pointer())
+ h.ptrMap[ptr] = hash
+ return hash
+}
diff --git a/go/types/typeutil/map_test.go b/go/types/typeutil/map_test.go
index d4b0f63..17f87ed 100644
--- a/go/types/typeutil/map_test.go
+++ b/go/types/typeutil/map_test.go
@@ -10,10 +10,14 @@
// (e.g. all types generated by type-checking some body of real code).
import (
+ "go/ast"
+ "go/parser"
+ "go/token"
"go/types"
"testing"
"golang.org/x/tools/go/types/typeutil"
+ "golang.org/x/tools/internal/typeparams"
)
var (
@@ -172,3 +176,190 @@
t.Errorf("Len(): got %q, want %q", s, "")
}
}
+
+func TestMapGenerics(t *testing.T) {
+ if !typeparams.Enabled {
+ t.Skip("type params are not enabled at this Go version")
+ }
+
+ const src = `
+package p
+
+// Basic defined types.
+type T1 int
+type T2 int
+
+// Identical methods.
+func (T1) M(int) {}
+func (T2) M(int) {}
+
+// A constraint interface.
+type C interface {
+ ~int | string
+}
+
+type I interface {
+}
+
+// A generic type.
+type G[P C] int
+
+// Generic functions with identical signature.
+func Fa1[P C](p P) {}
+func Fa2[Q C](q Q) {}
+
+// Fb1 and Fb2 are identical and should be mapped to the same entry, even if we
+// map their arguments first.
+func Fb1[P any](x *P) {
+ var y *P // Map this first.
+ _ = y
+}
+func Fb2[Q any](x *Q) {
+}
+
+// G1 and G2 are mutally recursive, and have identical methods.
+type G1[P any] struct{
+ Field *G2[P]
+}
+func (G1[P]) M(G1[P], G2[P]) {}
+type G2[Q any] struct{
+ Field *G1[Q]
+}
+func (G2[P]) M(G1[P], G2[P]) {}
+
+// Method type expressions on different generic types are different.
+var ME1 = G1[int].M
+var ME2 = G2[int].M
+
+// ME1Type should have identical type as ME1.
+var ME1Type func(G1[int], G1[int], G2[int])
+`
+
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, "p.go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var conf types.Config
+ pkg, err := conf.Check("", fset, []*ast.File{file}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Collect types.
+ scope := pkg.Scope()
+ var (
+ T1 = scope.Lookup("T1").Type().(*types.Named)
+ T2 = scope.Lookup("T2").Type().(*types.Named)
+ T1M = T1.Method(0).Type()
+ T2M = T2.Method(0).Type()
+ G = scope.Lookup("G").Type()
+ GInt1 = instantiate(t, G, types.Typ[types.Int])
+ GInt2 = instantiate(t, G, types.Typ[types.Int])
+ GStr = instantiate(t, G, types.Typ[types.String])
+ C = scope.Lookup("C").Type()
+ CI = C.Underlying().(*types.Interface)
+ I = scope.Lookup("I").Type()
+ II = I.Underlying().(*types.Interface)
+ U = CI.EmbeddedType(0).(*typeparams.Union)
+ Fa1 = scope.Lookup("Fa1").Type().(*types.Signature)
+ Fa2 = scope.Lookup("Fa2").Type().(*types.Signature)
+ Fa1P = typeparams.ForSignature(Fa1).At(0)
+ Fa2Q = typeparams.ForSignature(Fa2).At(0)
+ Fb1 = scope.Lookup("Fb1").Type().(*types.Signature)
+ Fb1x = Fb1.Params().At(0).Type()
+ Fb1y = scope.Lookup("Fb1").(*types.Func).Scope().Lookup("y").Type()
+ Fb2 = scope.Lookup("Fb2").Type().(*types.Signature)
+ Fb2x = Fb2.Params().At(0).Type()
+ G1 = scope.Lookup("G1").Type().(*types.Named)
+ G1M = G1.Method(0).Type()
+ G1IntM1 = instantiate(t, G1, types.Typ[types.Int]).(*types.Named).Method(0).Type()
+ G1IntM2 = instantiate(t, G1, types.Typ[types.Int]).(*types.Named).Method(0).Type()
+ G1StrM = instantiate(t, G1, types.Typ[types.String]).(*types.Named).Method(0).Type()
+ G2 = scope.Lookup("G2").Type()
+ // See below.
+ // G2M = G2.Method(0).Type()
+ G2IntM = instantiate(t, G2, types.Typ[types.Int]).(*types.Named).Method(0).Type()
+ ME1 = scope.Lookup("ME1").Type()
+ ME1Type = scope.Lookup("ME1Type").Type()
+ ME2 = scope.Lookup("ME2").Type()
+ )
+
+ tmap := new(typeutil.Map)
+
+ steps := []struct {
+ typ types.Type
+ name string
+ newEntry bool
+ }{
+ {T1, "T1", true},
+ {T2, "T2", true},
+ {G, "G", true},
+ {C, "C", true},
+ {CI, "CI", true},
+ {U, "U", true},
+ {I, "I", true},
+ {II, "II", true}, // should not be identical to CI
+
+ // Methods can be identical, even with distinct receivers.
+ {T1M, "T1M", true},
+ {T2M, "T2M", false},
+
+ // Identical instances should map to the same entry.
+ {GInt1, "GInt1", true},
+ {GInt2, "GInt2", false},
+ // ..but instantiating with different arguments should yield a new entry.
+ {GStr, "GStr", true},
+
+ // F1 and F2 should have identical signatures.
+ {Fa1, "F1", true},
+ {Fa2, "F2", false},
+
+ // The identity of P and Q should not have been affected by type parameter
+ // masking during signature hashing.
+ {Fa1P, "F1P", true},
+ {Fa2Q, "F2Q", true},
+
+ {Fb1y, "Fb1y", true},
+ {Fb1x, "Fb1x", false},
+ {Fb2x, "Fb2x", true},
+ {Fb1, "Fb1", true},
+
+ // Mapping elements of the function scope should not affect the identity of
+ // Fb2 or Fb1.
+ {Fb2, "Fb1", false},
+
+ {G1, "G1", true},
+ {G1M, "G1M", true},
+ {G2, "G2", true},
+
+ // See golang/go#49912: receiver type parameter names should be ignored
+ // when comparing method identity.
+ // {G2M, "G2M", false},
+ {G1IntM1, "G1IntM1", true},
+ {G1IntM2, "G1IntM2", false},
+ {G1StrM, "G1StrM", true},
+ {G2IntM, "G2IntM", false}, // identical to G1IntM1
+
+ {ME1, "ME1", true},
+ {ME1Type, "ME1Type", false},
+ {ME2, "ME2", true},
+ }
+
+ for _, step := range steps {
+ existing := tmap.At(step.typ)
+ if (existing == nil) != step.newEntry {
+ t.Errorf("At(%s) = %v, want new entry: %t", step.name, existing, step.newEntry)
+ }
+ tmap.Set(step.typ, step.name)
+ }
+}
+
+func instantiate(t *testing.T, origin types.Type, targs ...types.Type) types.Type {
+ inst, err := typeparams.Instantiate(nil, origin, targs, true)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return inst
+}
diff --git a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md
index 3a5ae5f..ddb8ffc 100644
--- a/gopls/doc/analyzers.md
+++ b/gopls/doc/analyzers.md
@@ -191,11 +191,11 @@
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
-}
+ func f[T any](T) {}
+
+ func _() {
+ f[string]("foo") // string could be inferred
+ }
**Enabled by default.**
@@ -578,7 +578,7 @@
## **fillreturns**
-suggested fixes for "wrong number of return values (want %d, got %d)"
+suggest fixes for errors due to an incorrect number of return values
This checker provides suggested fixes for type errors of the
type "wrong number of return values (want %d, got %d)". For example:
diff --git a/gopls/doc/features.md b/gopls/doc/features.md
index 9cb6864..ccfe138 100644
--- a/gopls/doc/features.md
+++ b/gopls/doc/features.md
@@ -4,10 +4,10 @@
currently under construction, so, for a comprehensive list, see the
[Language Server Protocol](https://microsoft.github.io/language-server-protocol/).
-For now, only special features outside of the LSP are described below.
-
## Special features
+Here, only special features outside of the LSP are described.
+
### Symbol Queries
Gopls supports some extended syntax for `workspace/symbol` requests, when using
@@ -21,4 +21,26 @@
| `^` | `^printf` | exact prefix |
| `$` | `printf$` | exact suffix |
+## Template Files
+
+Gopls provides some support for Go template files, that is, files that
+are parsed by `text/template` or `html/template`.
+Gopls recognizes template files based on their file extension.
+By default it looks for files ending in `.tmpl` or `.gotmpl`,
+but this list may be configured by the
+[`templateExtensions`](https://github.com/golang/tools/blob/master/gopls/doc/settings.md#templateextensions-string) setting.
+Making this list empty turns off template support.
+
+In template files, template support works inside
+the default `{{` delimiters. (Go template parsing
+allows the user to specify other delimiters, but
+gopls does not know how to do that.)
+
+Gopls template support includes the following features:
++ **Diagnostics**: if template parsing returns an error,
+it is presented as a diagnostic. (Missing functions do not produce errors.)
++ **Syntax Highlighting**: syntax highlighting is provided for template files.
++ **Definitions**: gopls provides jump-to-definition inside templates, though it does not understand scoping (all templates are considered to be in one global scope).
++ **References**: gopls provides find-references, with the same scoping limitation as definitions.
++ **Completions**: gopls will attempt to suggest completions inside templates.
<!--TODO(rstambler): Automatically generate a list of supported features.-->
diff --git a/gopls/doc/settings.md b/gopls/doc/settings.md
index 375aab3..542eb16 100644
--- a/gopls/doc/settings.md
+++ b/gopls/doc/settings.md
@@ -72,12 +72,6 @@
Default: `["-node_modules"]`.
-#### **templateSupport** *bool*
-
-templateSupport can be used to turn off support for template files.
-
-Default: `true`.
-
#### **templateExtensions** *[]string*
templateExtensions gives the extensions of file names that are treateed
diff --git a/gopls/doc/vim.md b/gopls/doc/vim.md
index a6b40a4..51f19dd 100644
--- a/gopls/doc/vim.md
+++ b/gopls/doc/vim.md
@@ -225,5 +225,5 @@
[govim-install]: https://github.com/myitcv/govim/blob/master/README.md#govim---go-development-plugin-for-vim8
[nvim-docs]: https://neovim.io/doc/user/lsp.html
[nvim-install]: https://github.com/neovim/neovim/wiki/Installing-Neovim
-[nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig/blob/master/CONFIG.md#gopls
+[nvim-lspconfig]: https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#gopls
[nvim-lspconfig-imports]: https://github.com/neovim/nvim-lspconfig/issues/115
diff --git a/gopls/internal/regtest/misc/fix_test.go b/gopls/internal/regtest/misc/fix_test.go
index 8c5662a..8318ae5 100644
--- a/gopls/internal/regtest/misc/fix_test.go
+++ b/gopls/internal/regtest/misc/fix_test.go
@@ -78,7 +78,8 @@
env.OpenFile("main.go")
var d protocol.PublishDiagnosticsParams
env.Await(OnceMet(
- env.DiagnosticAtRegexpWithMessage("main.go", `return`, "wrong number of return values"),
+ // The error message here changed in 1.18; "return values" covers both forms.
+ env.DiagnosticAtRegexpWithMessage("main.go", `return`, "return values"),
ReadDiagnostics("main.go", &d),
))
codeActions := env.CodeAction("main.go", d.Diagnostics)
diff --git a/gopls/internal/regtest/misc/formatting_test.go b/gopls/internal/regtest/misc/formatting_test.go
index 1e14237..67e7939 100644
--- a/gopls/internal/regtest/misc/formatting_test.go
+++ b/gopls/internal/regtest/misc/formatting_test.go
@@ -268,3 +268,36 @@
})
}
}
+
+func TestFormattingOfGeneratedFile_Issue49555(t *testing.T) {
+ const input = `
+-- main.go --
+// Code generated by generator.go. DO NOT EDIT.
+
+package main
+
+import "fmt"
+
+func main() {
+
+
+
+
+ fmt.Print("hello")
+}
+`
+
+ Run(t, input, func(t *testing.T, env *Env) {
+ wantErrSuffix := "file is generated"
+
+ env.OpenFile("main.go")
+ err := env.Editor.FormatBuffer(env.Ctx, "main.go")
+ if err == nil {
+ t.Fatal("expected error, got nil")
+ }
+ // Check only the suffix because an error contains a dynamic path to main.go
+ if !strings.HasSuffix(err.Error(), wantErrSuffix) {
+ t.Fatalf("unexpected error %q, want suffix %q", err.Error(), wantErrSuffix)
+ }
+ })
+}
diff --git a/gopls/internal/regtest/misc/generate_test.go b/gopls/internal/regtest/misc/generate_test.go
index 4478951..1dc22d7 100644
--- a/gopls/internal/regtest/misc/generate_test.go
+++ b/gopls/internal/regtest/misc/generate_test.go
@@ -16,6 +16,8 @@
)
func TestGenerateProgress(t *testing.T) {
+ t.Skipf("skipping flaky test: https://golang.org/issue/49901")
+
const generatedWorkspace = `
-- go.mod --
module fake.test
diff --git a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/regtest/workspace/workspace_test.go
index 11ab507..666f023 100644
--- a/gopls/internal/regtest/workspace/workspace_test.go
+++ b/gopls/internal/regtest/workspace/workspace_test.go
@@ -855,6 +855,7 @@
-- moda/a/go.mod --
module a.com
+go 1.15
-- moda/a/a.go --
package main
@@ -863,6 +864,8 @@
}
-- modb/go.mod --
module b.com
+
+go 1.16
-- modb/b/b.go --
package main
@@ -892,7 +895,7 @@
t.Fatalf("reading expected workspace modfile: %v", err)
}
got := string(gotb)
- for _, want := range []string{"a.com v1.9999999.0-goplsworkspace", "b.com v1.9999999.0-goplsworkspace"} {
+ for _, want := range []string{"go 1.16", "a.com v1.9999999.0-goplsworkspace", "b.com v1.9999999.0-goplsworkspace"} {
if !strings.Contains(got, want) {
// want before got here, since the go.mod is multi-line
t.Fatalf("workspace go.mod missing %q. got:\n%s", want, got)
diff --git a/internal/lsp/analysis/fillreturns/fillreturns.go b/internal/lsp/analysis/fillreturns/fillreturns.go
index d34baf5..4607f37 100644
--- a/internal/lsp/analysis/fillreturns/fillreturns.go
+++ b/internal/lsp/analysis/fillreturns/fillreturns.go
@@ -14,7 +14,6 @@
"go/format"
"go/types"
"regexp"
- "strconv"
"strings"
"golang.org/x/tools/go/analysis"
@@ -23,7 +22,7 @@
"golang.org/x/tools/internal/typeparams"
)
-const Doc = `suggested fixes for "wrong number of return values (want %d, got %d)"
+const Doc = `suggest fixes for errors due to an incorrect number of return values
This checker provides suggested fixes for type errors of the
type "wrong number of return values (want %d, got %d)". For example:
@@ -46,8 +45,6 @@
RunDespiteErrors: true,
}
-var wrongReturnNumRegex = regexp.MustCompile(`wrong number of return values \(want (\d+), got (\d+)\)`)
-
func run(pass *analysis.Pass) (interface{}, error) {
info := pass.TypesInfo
if info == nil {
@@ -58,7 +55,7 @@
outer:
for _, typeErr := range errors {
// Filter out the errors that are not relevant to this analyzer.
- if !FixesError(typeErr.Msg) {
+ if !FixesError(typeErr) {
continue
}
var file *ast.File
@@ -79,20 +76,32 @@
}
typeErrEndPos := analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), typeErr.Pos)
+ // TODO(rfindley): much of the error handling code below returns, when it
+ // should probably continue.
+
// Get the path for the relevant range.
path, _ := astutil.PathEnclosingInterval(file, typeErr.Pos, typeErrEndPos)
if len(path) == 0 {
return nil, nil
}
- // Check to make sure the node of interest is a ReturnStmt.
- ret, ok := path[0].(*ast.ReturnStmt)
- if !ok {
+
+ // Find the enclosing return statement.
+ var ret *ast.ReturnStmt
+ var retIdx int
+ for i, n := range path {
+ if r, ok := n.(*ast.ReturnStmt); ok {
+ ret = r
+ retIdx = i
+ break
+ }
+ }
+ if ret == nil {
return nil, nil
}
// Get the function type that encloses the ReturnStmt.
var enclosingFunc *ast.FuncType
- for _, n := range path {
+ for _, n := range path[retIdx+1:] {
switch node := n.(type) {
case *ast.FuncLit:
enclosingFunc = node.Type
@@ -109,7 +118,7 @@
// Skip any generic enclosing functions, since type parameters don't
// have 0 values.
- // TODO(rstambler): We should be able to handle this if the return
+ // TODO(rfindley): We should be able to handle this if the return
// values are all concrete types.
if tparams := typeparams.ForFuncType(enclosingFunc); tparams != nil && tparams.NumFields() > 0 {
return nil, nil
@@ -127,7 +136,8 @@
return nil, nil
}
- // Skip any return statements that contain function calls with multiple return values.
+ // Skip any return statements that contain function calls with multiple
+ // return values.
for _, expr := range ret.Results {
e, ok := expr.(*ast.CallExpr)
if !ok {
@@ -244,16 +254,23 @@
return types.AssignableTo(want, got) || types.ConvertibleTo(want, got)
}
-func FixesError(msg string) bool {
- matches := wrongReturnNumRegex.FindStringSubmatch(strings.TrimSpace(msg))
- if len(matches) < 3 {
- return false
+// Error messages have changed across Go versions. These regexps capture recent
+// incarnations.
+//
+// TODO(rfindley): once error codes are exported and exposed via go/packages,
+// use error codes rather than string matching here.
+var wrongReturnNumRegexes = []*regexp.Regexp{
+ regexp.MustCompile(`wrong number of return values \(want (\d+), got (\d+)\)`),
+ regexp.MustCompile(`too many return values`),
+ regexp.MustCompile(`not enough return values`),
+}
+
+func FixesError(err types.Error) bool {
+ msg := strings.TrimSpace(err.Msg)
+ for _, rx := range wrongReturnNumRegexes {
+ if rx.MatchString(msg) {
+ return true
+ }
}
- if _, err := strconv.Atoi(matches[1]); err != nil {
- return false
- }
- if _, err := strconv.Atoi(matches[2]); err != nil {
- return false
- }
- return true
+ return false
}
diff --git a/internal/lsp/analysis/fillreturns/testdata/src/a/a.go b/internal/lsp/analysis/fillreturns/testdata/src/a/a.go
index 44cb25f..7ab0ff1 100644
--- a/internal/lsp/analysis/fillreturns/testdata/src/a/a.go
+++ b/internal/lsp/analysis/fillreturns/testdata/src/a/a.go
@@ -25,80 +25,82 @@
return errors.New("foo")
}
+// The error messages below changed in 1.18; "return values" covers both forms.
+
func b() (string, int, error) {
- return "", errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
+ return "", errors.New("foo") // want "return values"
}
func c() (string, int, error) {
- return 7, errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
+ return 7, errors.New("foo") // want "return values"
}
func d() (string, int, error) {
- return "", 7 // want "wrong number of return values \\(want 3, got 2\\)"
+ return "", 7 // want "return values"
}
func e() (T, error, *bool) {
- return (z(http.ListenAndServe))("", nil) // want "wrong number of return values \\(want 3, got 1\\)"
+ return (z(http.ListenAndServe))("", nil) // want "return values"
}
func preserveLeft() (int, int, error) {
- return 1, errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
+ return 1, errors.New("foo") // want "return values"
}
func matchValues() (int, error, string) {
- return errors.New("foo"), 3 // want "wrong number of return values \\(want 3, got 2\\)"
+ return errors.New("foo"), 3 // want "return values"
}
func preventDataOverwrite() (int, string) {
- return errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
+ return errors.New("foo") // want "return values"
}
func closure() (string, error) {
_ = func() (int, error) {
- return // want "wrong number of return values \\(want 2, got 0\\)"
+ return // want "return values"
}
- return // want "wrong number of return values \\(want 2, got 0\\)"
+ return // want "return values"
}
func basic() (uint8, uint16, uint32, uint64, int8, int16, int32, int64, float32, float64, complex64, complex128, byte, rune, uint, int, uintptr, string, bool, error) {
- return // want "wrong number of return values \\(want 20, got 0\\)"
+ return // want "return values"
}
func complex() (*int, []int, [2]int, map[int]int) {
- return // want "wrong number of return values \\(want 4, got 0\\)"
+ return // want "return values"
}
func structsAndInterfaces() (T, url.URL, T1, I, I1, io.Reader, Client, ast2.Stmt) {
- return // want "wrong number of return values \\(want 8, got 0\\)"
+ return // want "return values"
}
func m() (int, error) {
if 1 == 2 {
- return // want "wrong number of return values \\(want 2, got 0\\)"
+ return // want "return values"
} else if 1 == 3 {
- return errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
+ return errors.New("foo") // want "return values"
} else {
- return 1 // want "wrong number of return values \\(want 2, got 1\\)"
+ return 1 // want "return values"
}
- return // want "wrong number of return values \\(want 2, got 0\\)"
+ return // want "return values"
}
func convertibleTypes() (ast2.Expr, int) {
- return &ast2.ArrayType{} // want "wrong number of return values \\(want 2, got 1\\)"
+ return &ast2.ArrayType{} // want "return values"
}
func assignableTypes() (map[string]int, int) {
type X map[string]int
var x X
- return x // want "wrong number of return values \\(want 2, got 1\\)"
+ return x // want "return values"
}
func interfaceAndError() (I, int) {
- return errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
+ return errors.New("foo") // want "return values"
}
func funcOneReturn() (string, error) {
- return strconv.Itoa(1) // want "wrong number of return values \\(want 2, got 1\\)"
+ return strconv.Itoa(1) // want "return values"
}
func funcMultipleReturn() (int, error, string) {
@@ -110,16 +112,16 @@
}
func multipleUnused() (int, string, string, string) {
- return 3, 4, 5 // want "wrong number of return values \\(want 4, got 3\\)"
+ return 3, 4, 5 // want "return values"
}
func gotTooMany() int {
if true {
- return 0, "" // want "wrong number of return values \\(want 1, got 2\\)"
+ return 0, "" // want "return values"
} else {
- return 1, 0, nil // want "wrong number of return values \\(want 1, got 3\\)"
+ return 1, 0, nil // want "return values"
}
- return 0, 5, false // want "wrong number of return values \\(want 1, got 3\\)"
+ return 0, 5, false // want "return values"
}
func fillVars() (int, string, ast.Node, bool, error) {
@@ -128,10 +130,10 @@
var t bool
if true {
err := errors.New("fail")
- return // want "wrong number of return values \\(want 5, got 0\\)"
+ return // want "return values"
}
n := ast.NewIdent("ident")
int := 3
var b bool
- return "" // want "wrong number of return values \\(want 5, got 1\\)"
+ return "" // want "return values"
}
diff --git a/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden b/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden
index 1435ea0..f007a5f 100644
--- a/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden
+++ b/internal/lsp/analysis/fillreturns/testdata/src/a/a.go.golden
@@ -25,80 +25,82 @@
return errors.New("foo")
}
+// The error messages below changed in 1.18; "return values" covers both forms.
+
func b() (string, int, error) {
- return "", 0, errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
+ return "", 0, errors.New("foo") // want "return values"
}
func c() (string, int, error) {
- return "", 7, errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
+ return "", 7, errors.New("foo") // want "return values"
}
func d() (string, int, error) {
- return "", 7, nil // want "wrong number of return values \\(want 3, got 2\\)"
+ return "", 7, nil // want "return values"
}
func e() (T, error, *bool) {
- return T{}, (z(http.ListenAndServe))("", nil), nil // want "wrong number of return values \\(want 3, got 1\\)"
+ return T{}, (z(http.ListenAndServe))("", nil), nil // want "return values"
}
func preserveLeft() (int, int, error) {
- return 1, 0, errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
+ return 1, 0, errors.New("foo") // want "return values"
}
func matchValues() (int, error, string) {
- return 3, errors.New("foo"), "" // want "wrong number of return values \\(want 3, got 2\\)"
+ return 3, errors.New("foo"), "" // want "return values"
}
func preventDataOverwrite() (int, string) {
- return 0, "", errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
+ return 0, "", errors.New("foo") // want "return values"
}
func closure() (string, error) {
_ = func() (int, error) {
- return 0, nil // want "wrong number of return values \\(want 2, got 0\\)"
+ return 0, nil // want "return values"
}
- return "", nil // want "wrong number of return values \\(want 2, got 0\\)"
+ return "", nil // want "return values"
}
func basic() (uint8, uint16, uint32, uint64, int8, int16, int32, int64, float32, float64, complex64, complex128, byte, rune, uint, int, uintptr, string, bool, error) {
- return 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", false, nil // want "wrong number of return values \\(want 20, got 0\\)"
+ return 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", false, nil // want "return values"
}
func complex() (*int, []int, [2]int, map[int]int) {
- return nil, nil, nil, nil // want "wrong number of return values \\(want 4, got 0\\)"
+ return nil, nil, nil, nil // want "return values"
}
func structsAndInterfaces() (T, url.URL, T1, I, I1, io.Reader, Client, ast2.Stmt) {
- return T{}, url.URL{}, T{}, nil, nil, nil, Client{}, nil // want "wrong number of return values \\(want 8, got 0\\)"
+ return T{}, url.URL{}, T{}, nil, nil, nil, Client{}, nil // want "return values"
}
func m() (int, error) {
if 1 == 2 {
- return 0, nil // want "wrong number of return values \\(want 2, got 0\\)"
+ return 0, nil // want "return values"
} else if 1 == 3 {
- return 0, errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
+ return 0, errors.New("foo") // want "return values"
} else {
- return 1, nil // want "wrong number of return values \\(want 2, got 1\\)"
+ return 1, nil // want "return values"
}
- return 0, nil // want "wrong number of return values \\(want 2, got 0\\)"
+ return 0, nil // want "return values"
}
func convertibleTypes() (ast2.Expr, int) {
- return &ast2.ArrayType{}, 0 // want "wrong number of return values \\(want 2, got 1\\)"
+ return &ast2.ArrayType{}, 0 // want "return values"
}
func assignableTypes() (map[string]int, int) {
type X map[string]int
var x X
- return x, 0 // want "wrong number of return values \\(want 2, got 1\\)"
+ return x, 0 // want "return values"
}
func interfaceAndError() (I, int) {
- return errors.New("foo"), 0 // want "wrong number of return values \\(want 2, got 1\\)"
+ return errors.New("foo"), 0 // want "return values"
}
func funcOneReturn() (string, error) {
- return strconv.Itoa(1), nil // want "wrong number of return values \\(want 2, got 1\\)"
+ return strconv.Itoa(1), nil // want "return values"
}
func funcMultipleReturn() (int, error, string) {
@@ -110,16 +112,16 @@
}
func multipleUnused() (int, string, string, string) {
- return 3, "", "", "", 4, 5 // want "wrong number of return values \\(want 4, got 3\\)"
+ return 3, "", "", "", 4, 5 // want "return values"
}
func gotTooMany() int {
if true {
- return 0 // want "wrong number of return values \\(want 1, got 2\\)"
+ return 0 // want "return values"
} else {
- return 1 // want "wrong number of return values \\(want 1, got 3\\)"
+ return 1 // want "return values"
}
- return 5 // want "wrong number of return values \\(want 1, got 3\\)"
+ return 5 // want "return values"
}
func fillVars() (int, string, ast.Node, bool, error) {
@@ -128,10 +130,10 @@
var t bool
if true {
err := errors.New("fail")
- return eint, s, nil, false, err // want "wrong number of return values \\(want 5, got 0\\)"
+ return eint, s, nil, false, err // want "return values"
}
n := ast.NewIdent("ident")
int := 3
var b bool
- return int, "", n, b, nil // want "wrong number of return values \\(want 5, got 1\\)"
+ return int, "", n, b, nil // want "return values"
}
diff --git a/internal/lsp/analysis/infertypeargs/infertypeargs.go b/internal/lsp/analysis/infertypeargs/infertypeargs.go
index 34e6e72..119de50 100644
--- a/internal/lsp/analysis/infertypeargs/infertypeargs.go
+++ b/internal/lsp/analysis/infertypeargs/infertypeargs.go
@@ -16,11 +16,11 @@
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
-}
+ func f[T any](T) {}
+
+ func _() {
+ f[string]("foo") // string could be inferred
+ }
`
var Analyzer = &analysis.Analyzer{
diff --git a/internal/lsp/cache/imports.go b/internal/lsp/cache/imports.go
index ed9919f..b8e0146 100644
--- a/internal/lsp/cache/imports.go
+++ b/internal/lsp/cache/imports.go
@@ -138,7 +138,7 @@
// Take an extra reference to the snapshot so that its workspace directory
// (if any) isn't destroyed while we're using it.
- release := snapshot.generation.Acquire(ctx)
+ release := snapshot.generation.Acquire()
_, inv, cleanupInvocation, err := snapshot.goCommandInvocation(ctx, source.LoadWorkspace, &gocommand.Invocation{
WorkingDir: snapshot.view.rootURI.Filename(),
})
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index 48acf46..a65b8fe 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -254,7 +254,7 @@
initCtx, initCancel := context.WithCancel(xcontext.Detach(ctx))
v.initCancelFirstAttempt = initCancel
snapshot := v.snapshot
- release := snapshot.generation.Acquire(initCtx)
+ release := snapshot.generation.Acquire()
go func() {
defer release()
snapshot.initialize(initCtx, true)
@@ -264,7 +264,7 @@
event.Error(ctx, "copying workspace dir", err)
}
}()
- return v, snapshot, snapshot.generation.Acquire(ctx), nil
+ return v, snapshot, snapshot.generation.Acquire(), nil
}
// View returns the view by name.
@@ -367,16 +367,24 @@
func (s *Session) updateView(ctx context.Context, view *View, options *source.Options) (*View, error) {
s.viewMu.Lock()
defer s.viewMu.Unlock()
+
+ // Preserve the snapshot ID if we are recreating the view.
+ view.snapshotMu.Lock()
+ if view.snapshot == nil {
+ view.snapshotMu.Unlock()
+ panic("updateView called after View was already shut down")
+ }
+ snapshotID := view.snapshot.id
+ view.snapshotMu.Unlock()
+
i, err := s.dropView(ctx, view)
if err != nil {
return nil, err
}
- // Preserve the snapshot ID if we are recreating the view.
- view.snapshotMu.Lock()
- snapshotID := view.snapshot.id
- view.snapshotMu.Unlock()
+
v, _, release, err := s.createView(ctx, view.name, view.folder, view.tempWorkspace, options, snapshotID)
release()
+
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
@@ -530,7 +538,7 @@
defer s.viewMu.RUnlock()
var snapshots []*snapshot
for _, v := range s.views {
- snapshot, release := v.getSnapshot(ctx)
+ snapshot, release := v.getSnapshot()
defer release()
snapshots = append(snapshots, snapshot)
}
@@ -720,7 +728,7 @@
defer s.viewMu.RUnlock()
patterns := map[string]struct{}{}
for _, view := range s.views {
- snapshot, release := view.getSnapshot(ctx)
+ snapshot, release := view.getSnapshot()
for k, v := range snapshot.fileWatchingGlobPatterns(ctx) {
patterns[k] = v
}
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index 486a228..53f97f4 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -159,7 +159,7 @@
}
func (s *snapshot) Templates() map[span.URI]source.VersionedFileHandle {
- if !s.view.Options().TemplateSupport {
+ if len(s.view.Options().TemplateExtensions) == 0 {
return nil
}
@@ -2290,7 +2290,8 @@
if file == nil || parsed.Module == nil {
return nil, fmt.Errorf("no module declaration for %s", modURI)
}
- if parsed.Go != nil && semver.Compare(goVersion, parsed.Go.Version) < 0 {
+ // Prepend "v" to go versions to make them valid semver.
+ if parsed.Go != nil && semver.Compare("v"+goVersion, "v"+parsed.Go.Version) < 0 {
goVersion = parsed.Go.Version
}
path := parsed.Module.Mod.Path
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index 881d7f1..fcff02a 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -74,7 +74,7 @@
initCancelFirstAttempt context.CancelFunc
snapshotMu sync.Mutex
- snapshot *snapshot
+ snapshot *snapshot // nil after shutdown has been called
// initialWorkspaceLoad is closed when the first workspace initialization has
// completed. If we failed to load, we only retry if the go.mod file changes,
@@ -347,7 +347,7 @@
}
func (s *snapshot) locateTemplateFiles(ctx context.Context) {
- if !s.view.Options().TemplateSupport {
+ if len(s.view.Options().TemplateExtensions) == 0 {
return
}
suffixes := s.view.Options().TemplateExtensions
@@ -505,7 +505,10 @@
}
v.mu.Unlock()
v.snapshotMu.Lock()
- go v.snapshot.generation.Destroy()
+ if v.snapshot != nil {
+ go v.snapshot.generation.Destroy("View.shutdown")
+ v.snapshot = nil
+ }
v.snapshotMu.Unlock()
v.importsState.destroy()
}
@@ -551,13 +554,16 @@
}
func (v *View) Snapshot(ctx context.Context) (source.Snapshot, func()) {
- return v.getSnapshot(ctx)
+ return v.getSnapshot()
}
-func (v *View) getSnapshot(ctx context.Context) (*snapshot, func()) {
+func (v *View) getSnapshot() (*snapshot, func()) {
v.snapshotMu.Lock()
defer v.snapshotMu.Unlock()
- return v.snapshot, v.snapshot.generation.Acquire(ctx)
+ if v.snapshot == nil {
+ panic("getSnapshot called after shutdown")
+ }
+ return v.snapshot, v.snapshot.generation.Acquire()
}
func (s *snapshot) initialize(ctx context.Context, firstAttempt bool) {
@@ -670,6 +676,9 @@
// invalidateContent invalidates the content of a Go file,
// including any position and type information that depends on it.
+//
+// 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()) {
// Detach the context so that content invalidation cannot be canceled.
ctx = xcontext.Detach(ctx)
@@ -678,6 +687,10 @@
v.snapshotMu.Lock()
defer v.snapshotMu.Unlock()
+ if v.snapshot == nil {
+ panic("invalidateContent called after shutdown")
+ }
+
// Cancel all still-running previous requests, since they would be
// operating on stale data.
v.snapshot.cancel()
@@ -694,9 +707,9 @@
event.Error(ctx, "copying workspace dir", err)
}
}
- go oldSnapshot.generation.Destroy()
+ go oldSnapshot.generation.Destroy("View.invalidateContent")
- return v.snapshot, v.snapshot.generation.Acquire(ctx)
+ return v.snapshot, v.snapshot.generation.Acquire()
}
func (v *View) updateWorkspace(ctx context.Context) error {
@@ -714,7 +727,11 @@
// all changes to the workspace module, only that it is eventually consistent
// with the workspace module of the latest snapshot.
func (v *View) updateWorkspaceLocked(ctx context.Context) error {
- release := v.snapshot.generation.Acquire(ctx)
+ if v.snapshot == nil {
+ return errors.New("view is shutting down")
+ }
+
+ release := v.snapshot.generation.Acquire()
defer release()
src, err := v.snapshot.getWorkspaceDir(ctx)
if err != nil {
diff --git a/internal/lsp/protocol/tsclient.go b/internal/lsp/protocol/tsclient.go
index 646beb0..87bd313 100644
--- a/internal/lsp/protocol/tsclient.go
+++ b/internal/lsp/protocol/tsclient.go
@@ -6,8 +6,8 @@
// Package protocol contains data types and code for LSP jsonrpcs
// generated automatically from vscode-languageserver-node
-// commit: 10b56de150ad67c3c330da8e2df53ebf2cf347c4
-// last fetched Wed Sep 29 2021 12:31:31 GMT-0400 (Eastern Daylight Time)
+// commit: d959faf4be476a6e0a08d5612e91fcac14ff9929
+// last fetched Mon Nov 29 2021 15:51:05 GMT-0500 (Eastern Standard Time)
// Code generated (see typescript/README.md) DO NOT EDIT.
@@ -26,7 +26,7 @@
PublishDiagnostics(context.Context, *PublishDiagnosticsParams) error
Progress(context.Context, *ProgressParams) error
WorkspaceFolders(context.Context) ([]WorkspaceFolder /*WorkspaceFolder[] | null*/, error)
- Configuration(context.Context, *ParamConfiguration) ([]interface{}, error)
+ Configuration(context.Context, *ParamConfiguration) ([]LSPAny, error)
WorkDoneProgressCreate(context.Context, *WorkDoneProgressCreateParams) error
ShowDocument(context.Context, *ShowDocumentParams) (*ShowDocumentResult, error)
RegisterCapability(context.Context, *RegistrationParams) error
@@ -160,8 +160,8 @@
return result, nil
}
-func (s *clientDispatcher) Configuration(ctx context.Context, params *ParamConfiguration) ([]interface{}, error) {
- var result []interface{}
+func (s *clientDispatcher) Configuration(ctx context.Context, params *ParamConfiguration) ([]LSPAny, error) {
+ var result []LSPAny
if err := s.sender.Call(ctx, "workspace/configuration", params, &result); err != nil {
return nil, err
}
diff --git a/internal/lsp/protocol/tsprotocol.go b/internal/lsp/protocol/tsprotocol.go
index f587d0f..c22ed9c 100644
--- a/internal/lsp/protocol/tsprotocol.go
+++ b/internal/lsp/protocol/tsprotocol.go
@@ -4,8 +4,8 @@
// Package protocol contains data types and code for LSP jsonrpcs
// generated automatically from vscode-languageserver-node
-// commit: 10b56de150ad67c3c330da8e2df53ebf2cf347c4
-// last fetched Wed Sep 29 2021 12:31:31 GMT-0400 (Eastern Daylight Time)
+// commit: d959faf4be476a6e0a08d5612e91fcac14ff9929
+// last fetched Wed Dec 01 2021 09:27:34 GMT-0500 (Eastern Standard Time)
package protocol
// Code generated (see typescript/README.md) DO NOT EDIT.
@@ -145,7 +145,7 @@
* A data entry field that is preserved between a call hierarchy prepare and
* incoming calls or outgoing calls requests.
*/
- Data interface{} `json:"data,omitempty"`
+ Data LSPAny `json:"data,omitempty"`
}
/**
@@ -359,7 +359,7 @@
*
* @since 3.16.0
*/
- Data interface{} `json:"data,omitempty"`
+ Data LSPAny `json:"data,omitempty"`
}
/**
@@ -456,6 +456,12 @@
* can omit computing them.
*/
Only []CodeActionKind `json:"only,omitempty"`
+ /**
+ * The reason why code actions were requested.
+ *
+ * @since 3.17.0
+ */
+ TriggerKind CodeActionTriggerKind `json:"triggerKind,omitempty"`
}
/**
@@ -505,6 +511,13 @@
}
/**
+ * The reason why code actions were requested.
+ *
+ * @since 3.17.0 - proposed state
+ */
+type CodeActionTriggerKind float64
+
+/**
* Structure to capture a description for an error code.
*
* @since 3.16.0
@@ -537,7 +550,7 @@
* a [CodeLensRequest](#CodeLensRequest) and a [CodeLensResolveRequest]
* (#CodeLensResolveRequest)
*/
- Data interface{} `json:"data,omitempty"`
+ Data LSPAny `json:"data,omitempty"`
}
/**
@@ -800,7 +813,7 @@
* when accepting a completion item that uses multi line
* text in either `insertText` or `textEdit`.
*
- * @since 3.17.0
+ * @since 3.17.0 - proposed state
*/
InsertTextMode InsertTextMode `json:"insertTextMode,omitempty"`
/**
@@ -808,6 +821,25 @@
* `textDocument/completion` request.
*/
ContextSupport bool `json:"contextSupport,omitempty"`
+ /**
+ * The client supports the following `CompletionList` specific
+ * capabilities.
+ *
+ * @since 3.17.0 - proposed state
+ */
+ CompletionList struct {
+ /**
+ * The client supports the the following itemDefaults on
+ * a completion list.
+ *
+ * The value lists the supported property names of the
+ * `CompletionList.itemDefaults` object. If omitted
+ * no properties are supported.
+ *
+ * @since 3.17.0 - proposed state
+ */
+ ItemDefaults []string `json:"itemDefaults,omitempty"`
+ } `json:"completionList,omitempty"`
}
/**
@@ -908,6 +940,8 @@
* The format of the insert text. The format applies to both the `insertText` property
* and the `newText` property of a provided `textEdit`. If omitted defaults to
* `InsertTextFormat.PlainText`.
+ *
+ * Please note that the insertTextFormat doesn't apply to `additionalTextEdits`.
*/
InsertTextFormat InsertTextFormat `json:"insertTextFormat,omitempty"`
/**
@@ -963,7 +997,7 @@
* A data entry field that is preserved on a completion item between a
* [CompletionRequest](#CompletionRequest) and a [CompletionResolveRequest](#CompletionResolveRequest).
*/
- Data interface{} `json:"data,omitempty"`
+ Data LSPAny `json:"data,omitempty"`
}
/**
@@ -1007,6 +1041,47 @@
*/
IsIncomplete bool `json:"isIncomplete"`
/**
+ * In many cases the items of an actual completion result share the same
+ * value for properties like `commitCharacters` or the range of a text
+ * edit. A completion list can therefore define item defaults which will
+ * be used if a completion item itself doesn't specify the value.
+ *
+ * If a completion list specifies a default value and a completion item
+ * also specifies a corresponding value the one from the item is used.
+ *
+ * Servers are only allowed to return default values if the client
+ * signals support for this via the `completionList.itemDefaults`
+ * capability.
+ *
+ * @since 3.17.0 - proposed state
+ */
+ ItemDefaults struct {
+ /**
+ * A default commit character set.
+ *
+ * @since 3.17.0 - proposed state
+ */
+ CommitCharacters []string `json:"commitCharacters,omitempty"`
+ /**
+ * A default edit range
+ *
+ * @since 3.17.0 - proposed state
+ */
+ EditRange Range/*Range | { insert: Range; replace: Range; }*/ `json:"editRange,omitempty"`
+ /**
+ * A default insert text format
+ *
+ * @since 3.17.0 - proposed state
+ */
+ InsertTextFormat InsertTextFormat `json:"insertTextFormat,omitempty"`
+ /**
+ * A default insert text mode
+ *
+ * @since 3.17.0 - proposed state
+ */
+ InsertTextMode InsertTextMode `json:"insertTextMode,omitempty"`
+ } `json:"itemDefaults,omitempty"`
+ /**
* The completion items.
*/
Items []CompletionItem `json:"items"`
@@ -1323,6 +1398,7 @@
Code interface{}/*integer | string*/ `json:"code,omitempty"`
/**
* An optional property to describe the error code.
+ * Requires the code field (above) to be present/not null.
*
* @since 3.16.0
*/
@@ -1354,7 +1430,7 @@
*
* @since 3.16.0
*/
- Data interface{} `json:"data,omitempty"`
+ Data LSPAny `json:"data,omitempty"`
}
/**
@@ -1399,7 +1475,7 @@
/**
* The actual changed settings
*/
- Settings interface{} `json:"settings"`
+ Settings LSPAny `json:"settings"`
}
/**
@@ -1697,7 +1773,7 @@
* A data entry field that is preserved on a document link between a
* DocumentLinkRequest and a DocumentLinkResolveRequest.
*/
- Data interface{} `json:"data,omitempty"`
+ Data LSPAny `json:"data,omitempty"`
}
/**
@@ -1851,7 +1927,7 @@
*/
Kind SymbolKind `json:"kind"`
/**
- * Tags for this completion item.
+ * Tags for this document symbol.
*
* @since 3.16.0
*/
@@ -2378,7 +2454,7 @@
/**
* The list of requests for which the client
* will retry the request if it receives a
- * response with error code `ContentModified``
+ * response with error code `ContentModified`
*/
RetryOnContentModified []string `json:"retryOnContentModified"`
} `json:"staleRequestSupport,omitempty"`
@@ -2530,11 +2606,11 @@
/**
* User provided initialization options.
*/
- InitializationOptions interface{} `json:"initializationOptions,omitempty"`
+ InitializationOptions LSPAny `json:"initializationOptions,omitempty"`
/**
* The initial trace setting. If omitted trace is disabled ('off').
*/
- Trace string/*'off' | 'messages' | 'verbose'*/ `json:"trace,omitempty"`
+ Trace string/* 'off' | 'messages' | 'compact' | 'verbose' */ `json:"trace,omitempty"`
/**
* The actual configured workspace folders.
*/
@@ -2570,6 +2646,158 @@
}
/**
+ * Inline value information can be provided by different means:
+ * - directly as a text value (class InlineValueText).
+ * - as a name to use for a variable lookup (class InlineValueVariableLookup)
+ * - as an evaluatable expression (class InlineValueEvaluatableExpression)
+ * The InlineValue types combines all inline value types into one type.
+ *
+ * @since 3.17.0 - proposed state
+ */
+type InlineValue = interface{} /* InlineValueText | InlineValueVariableLookup | InlineValueEvaluatableExpression*/
+
+/**
+ * Provide an inline value through an expression evaluation.
+ * If only a range is specified, the expression will be extracted from the underlying document.
+ * An optional expression can be used to override the extracted expression.
+ *
+ * @since 3.17.0 - proposed state
+ */
+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"`
+ /**
+ * If specified the expression overrides the extracted expression.
+ */
+ Expression string `json:"expression,omitempty"`
+}
+
+/**
+ * Provide inline value as text.
+ *
+ * @since 3.17.0 - proposed state
+ */
+type InlineValueText struct {
+ /**
+ * The document range for which the inline value applies.
+ */
+ Range Range `json:"range"`
+ /**
+ * The text of the inline value.
+ */
+ Text string `json:"text"`
+}
+
+/**
+ * Provide inline value through a variable lookup.
+ * If only a range is specified, the variable name will be extracted from the underlying document.
+ * An optional variable name can be used to override the extracted name.
+ *
+ * @since 3.17.0 - proposed state
+ */
+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"`
+ /**
+ * If specified the name of the variable to look up.
+ */
+ VariableName string `json:"variableName,omitempty"`
+ /**
+ * How to perform the lookup.
+ */
+ CaseSensitiveLookup bool `json:"caseSensitiveLookup"`
+}
+
+/**
+ * Client capabilities specific to inline values.
+ *
+ * @since 3.17.0 - proposed state
+ */
+type InlineValuesClientCapabilities struct {
+ /**
+ * Whether implementation supports dynamic registration for inline value providers.
+ */
+ DynamicRegistration bool `json:"dynamicRegistration,omitempty"`
+}
+
+/**
+ * @since 3.17.0 - proposed state
+ */
+type InlineValuesContext struct {
+ /**
+ * The document range where execution has stopped.
+ * Typically the end position of the range denotes the line where the inline values are shown.
+ */
+ StoppedLocation Range `json:"stoppedLocation"`
+}
+
+/**
+ * Inline values options used during static registration.
+ *
+ * @since 3.17.0 - proposed state
+ */
+type InlineValuesOptions struct {
+ WorkDoneProgressOptions
+}
+
+/**
+ * A parameter literal used in inline values requests.
+ *
+ * @since 3.17.0 - proposed state
+ */
+type InlineValuesParams struct {
+ /**
+ * The text document.
+ */
+ TextDocument TextDocumentIdentifier `json:"textDocument"`
+ /**
+ * The visible document range for which inline values should be computed.
+ */
+ ViewPort Range `json:"viewPort"`
+ /**
+ * Additional information about the context in which inline values were
+ * requested.
+ */
+ Context InlineValuesContext `json:"context"`
+ WorkDoneProgressParams
+}
+
+/**
+ * Inline value options used during static or dynamic registration.
+ *
+ * @since 3.17.0 - proposed state
+ */
+type InlineValuesRegistrationOptions struct {
+ InlineValuesOptions
+ TextDocumentRegistrationOptions
+ StaticRegistrationOptions
+}
+
+/**
+ * Client workspace capabilities specific to inline values.
+ *
+ * @since 3.17.0 - proposed state
+ */
+type InlineValuesWorkspaceClientCapabilities struct {
+ /**
+ * Whether the client implementation supports a refresh request sent from the
+ * server to the client.
+ *
+ * Note that this event is global and will force the client to refresh all
+ * inline values currently shown. It should be used with absolute care and is
+ * useful for situation where a server for example detect a project wide
+ * change that requires such a calculation.
+ */
+ RefreshSupport bool `json:"refreshSupport,omitempty"`
+}
+
+/**
* A special text edit to provide an insert and a replace operation.
*
* @since 3.16.0
@@ -2604,6 +2832,27 @@
type InsertTextMode float64
/**
+ * The LSP any type
+ *
+ * @since 3.17.0
+ */
+type LSPAny = interface{} /* LSPObject | LSPArray | string | int32 | uint32 | Decimal | bool | float64*/
+
+/**
+ * LSP arrays.
+ *
+ * @since 3.17.0
+ */
+type LSPArray = []LSPAny
+
+/**
+ * LSP object definition.
+ *
+ * @since 3.17.0
+ */
+type LSPObject = map[string]interface{} /*[key: string]: LSPAny*/
+
+/**
* Client capabilities for the linked editing range request.
*
* @since 3.16.0
@@ -2722,6 +2971,13 @@
* The version of the parser.
*/
Version string `json:"version,omitempty"`
+ /**
+ * A list of HTML tags that the client allows / supports in
+ * Markdown.
+ *
+ * @since 3.17.0
+ */
+ AllowedTags []string `json:"allowedTags,omitempty"`
}
/**
@@ -3107,7 +3363,7 @@
/**
* Options necessary for the registration.
*/
- RegisterOptions interface{} `json:"registerOptions,omitempty"`
+ RegisterOptions LSPAny `json:"registerOptions,omitempty"`
}
type RegistrationParams struct {
@@ -3432,6 +3688,28 @@
* Whether the client supports tokens that can span multiple lines.
*/
MultilineTokenSupport bool `json:"multilineTokenSupport,omitempty"`
+ /**
+ * Whether the client allows the server to actively cancel a
+ * semantic token request, e.g. supports returning
+ * LSPErrorCodes.ServerCancelled. If a server does the client
+ * needs to retrigger the request.
+ *
+ * @since 3.17.0
+ */
+ ServerCancelSupport bool `json:"serverCancelSupport,omitempty"`
+ /**
+ * Whether the client uses semantic tokens to augment existing
+ * syntax tokens. If set to `true` client side created syntax
+ * tokens and semantic tokens are both used for colorization. If
+ * set to `false` the client only uses the returned semantic tokens
+ * for colorization.
+ *
+ * If the value is `undefined` then the client behavior is not
+ * specified.
+ *
+ * @since 3.17.0
+ */
+ AugmentsSyntaxTokens bool `json:"augmentsSyntaxTokens,omitempty"`
}
/**
@@ -3700,6 +3978,12 @@
*/
TypeHierarchyProvider interface{}/* bool | TypeHierarchyOptions | TypeHierarchyRegistrationOptions*/ `json:"typeHierarchyProvider,omitempty"`
/**
+ * The server provides inline values.
+ *
+ * @since 3.17.0 - proposed state
+ */
+ InlineValuesProvider interface{}/* bool | InlineValuesOptions | InlineValuesOptions | InlineValuesRegistrationOptions*/ `json:"inlineValuesProvider,omitempty"`
+ /**
* Experimental server capabilities.
*/
Experimental interface{} `json:"experimental,omitempty"`
@@ -3823,15 +4107,27 @@
*/
Signatures []SignatureInformation `json:"signatures"`
/**
- * The active signature. Set to `null` if no
- * signatures exist.
+ * The active signature. If omitted or the value lies outside the
+ * range of `signatures` the value defaults to zero or is ignored if
+ * the `SignatureHelp` has no signatures.
+ *
+ * Whenever possible implementors should make an active decision about
+ * the active signature and shouldn't rely on a default value.
+ *
+ * In future version of the protocol this property might become
+ * mandatory to better express this.
*/
- ActiveSignature uint32/*uinteger | null*/ `json:"activeSignature"`
+ ActiveSignature uint32 `json:"activeSignature,omitempty"`
/**
- * The active parameter of the active signature. Set to `null`
- * if the active signature has no parameters.
+ * The active parameter of the active signature. If omitted or the value
+ * lies outside the range of `signatures[activeSignature].parameters`
+ * defaults to 0 if the active signature has parameters. If
+ * the active signature has no parameters it is ignored.
+ * In future version of the protocol this property might become
+ * mandatory to better express the active parameter if the
+ * active signature does have any.
*/
- ActiveParameter uint32/*uinteger | null*/ `json:"activeParameter"`
+ ActiveParameter uint32 `json:"activeParameter,omitempty"`
}
/**
@@ -4188,6 +4484,12 @@
* @since 3.17.0 - proposed state
*/
TypeHierarchy TypeHierarchyClientCapabilities `json:"typeHierarchy,omitempty"`
+ /**
+ * Capabilities specific to the `textDocument/inlineValues` request.
+ *
+ * @since 3.17.0 - proposed state
+ */
+ InlineValues InlineValuesClientCapabilities `json:"inlineValues,omitempty"`
}
/**
@@ -4369,7 +4671,7 @@
type TokenFormat = string
-type TraceValues = string /*'off' | 'messages' | 'verbose'*/
+type TraceValues = string /* 'off' | 'messages' | 'compact' | 'verbose' */
/**
* Since 3.6.0
@@ -4458,7 +4760,7 @@
* type hierarchy in the server, helping improve the performance on
* resolving supertypes and subtypes.
*/
- Data interface{} `json:"data,omitempty"`
+ Data LSPAny `json:"data,omitempty"`
}
/**
@@ -4767,6 +5069,13 @@
* Since 3.16.0
*/
FileOperations FileOperationClientCapabilities `json:"fileOperations,omitempty"`
+ /**
+ * Capabilities specific to the inline values requests scoped to the
+ * workspace.
+ *
+ * @since 3.17.0.
+ */
+ InlineValues InlineValuesWorkspaceClientCapabilities `json:"inlineValues,omitempty"`
}
/**
@@ -4822,7 +5131,7 @@
/**
* Holds changes to existing resources.
*/
- Changes map[string][]TextEdit/*[uri: string]: TextEdit[];*/ `json:"changes,omitempty"`
+ Changes map[DocumentURI][]TextEdit/*[uri: DocumentUri]: TextEdit[];*/ `json:"changes,omitempty"`
/**
* Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes
* are either an array of `TextDocumentEdit`s to express changes to n different text documents
@@ -4844,7 +5153,7 @@
*
* @since 3.16.0
*/
- ChangeAnnotations map[string]ChangeAnnotationIdentifier/*[id: string * ChangeAnnotationIdentifier *]: ChangeAnnotation;*/ `json:"changeAnnotations,omitempty"`
+ ChangeAnnotations map[string]ChangeAnnotationIdentifier/*[id: ChangeAnnotationIdentifier]: ChangeAnnotation;*/ `json:"changeAnnotations,omitempty"`
}
type WorkspaceEditClientCapabilities struct {
@@ -4958,6 +5267,25 @@
}
/**
+ * A special workspace symbol that supports locations without a range
+ *
+ * @since 3.17.0 - proposed state
+ */
+type WorkspaceSymbol struct {
+ /**
+ * The location of the symbol.
+ *
+ * See SymbolInformation#location for more details.
+ */
+ Location Location/*Location | { uri: DocumentUri; }*/ `json:"location"`
+ /**
+ * A data entry field that is preserved on a workspace symbol between a
+ * workspace symbol request and a workspace symbol resolve request.
+ */
+ Data LSPAny `json:"data,omitempty"`
+}
+
+/**
* Client capabilities for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest).
*/
type WorkspaceSymbolClientCapabilities struct {
@@ -4993,12 +5321,33 @@
*/
ValueSet []SymbolTag `json:"valueSet"`
} `json:"tagSupport,omitempty"`
+ /**
+ * The client support partial workspace symbols. The client will send the
+ * request `workspaceSymbol/resolve` to the server to resolve additional
+ * properties.
+ *
+ * @since 3.17.0 - proposedState
+ */
+ ResolveSupport struct {
+ /**
+ * The properties that a client can resolve lazily. Usually
+ * `location.range`
+ */
+ Properties []string `json:"properties"`
+ } `json:"resolveSupport,omitempty"`
}
/**
* Server capabilities for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest).
*/
type WorkspaceSymbolOptions struct {
+ /**
+ * The server provides support to resolve additional
+ * information for a workspace symbol.
+ *
+ * @since 3.17.0 - proposed state
+ */
+ ResolveProvider bool `json:"resolveProvider,omitempty"`
WorkDoneProgressOptions
}
@@ -5109,32 +5458,45 @@
* @since 3.15.0
*/
- SourceFixAll CodeActionKind = "source.fixAll"
- 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
+ SourceFixAll CodeActionKind = "source.fixAll"
+ /**
+ * Code actions were explicitly requested by the user or by an extension.
+ */
+
+ 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
+ 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
/**
* Render a completion as obsolete, usually using a strike-out.
*/
@@ -5554,6 +5916,14 @@
FileOperations *FileOperationClientCapabilities `json:"fileOperations,omitempty"`
/**
+ * Capabilities specific to the inline values requests scoped to the
+ * workspace.
+ *
+ * @since 3.17.0.
+ */
+ InlineValues InlineValuesWorkspaceClientCapabilities `json:"inlineValues,omitempty"`
+
+ /**
* The client has support for workspace folders
*
* @since 3.6.0
@@ -5624,6 +5994,14 @@
FileOperations *FileOperationClientCapabilities `json:"fileOperations,omitempty"`
/**
+ * Capabilities specific to the inline values requests scoped to the
+ * workspace.
+ *
+ * @since 3.17.0.
+ */
+ InlineValues InlineValuesWorkspaceClientCapabilities `json:"inlineValues,omitempty"`
+
+ /**
* The client has support for workspace folders
*
* @since 3.6.0
@@ -5721,6 +6099,14 @@
FileOperations *FileOperationClientCapabilities `json:"fileOperations,omitempty"`
/**
+ * Capabilities specific to the inline values requests scoped to the
+ * workspace.
+ *
+ * @since 3.17.0.
+ */
+ InlineValues InlineValuesWorkspaceClientCapabilities `json:"inlineValues,omitempty"`
+
+ /**
* The client has support for workspace folders
*
* @since 3.6.0
diff --git a/internal/lsp/protocol/tsserver.go b/internal/lsp/protocol/tsserver.go
index dec6d41..26ca4db 100644
--- a/internal/lsp/protocol/tsserver.go
+++ b/internal/lsp/protocol/tsserver.go
@@ -6,8 +6,8 @@
// Package protocol contains data types and code for LSP jsonrpcs
// generated automatically from vscode-languageserver-node
-// commit: 10b56de150ad67c3c330da8e2df53ebf2cf347c4
-// last fetched Wed Sep 29 2021 12:31:31 GMT-0400 (Eastern Daylight Time)
+// commit: d959faf4be476a6e0a08d5612e91fcac14ff9929
+// last fetched Mon Nov 29 2021 15:51:05 GMT-0500 (Eastern Standard Time)
// Code generated (see typescript/README.md) DO NOT EDIT.
@@ -58,6 +58,8 @@
PrepareTypeHierarchy(context.Context, *TypeHierarchyPrepareParams) ([]TypeHierarchyItem /*TypeHierarchyItem[] | null*/, error)
Supertypes(context.Context, *TypeHierarchySupertypesParams) ([]TypeHierarchyItem /*TypeHierarchyItem[] | null*/, error)
Subtypes(context.Context, *TypeHierarchySubtypesParams) ([]TypeHierarchyItem /*TypeHierarchyItem[] | null*/, error)
+ InlineValues(context.Context, *InlineValuesParams) ([]InlineValue /*InlineValue[] | null*/, error)
+ InlineValuesRefresh(context.Context) error
Initialize(context.Context, *ParamInitialize) (*InitializeResult, error)
Shutdown(context.Context) error
WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit /*TextEdit[] | null*/, error)
@@ -71,7 +73,8 @@
DocumentSymbol(context.Context, *DocumentSymbolParams) ([]interface{} /*SymbolInformation[] | DocumentSymbol[] | null*/, error)
CodeAction(context.Context, *CodeActionParams) ([]CodeAction /*(Command | CodeAction)[] | null*/, error)
ResolveCodeAction(context.Context, *CodeAction) (*CodeAction, error)
- Symbol(context.Context, *WorkspaceSymbolParams) ([]SymbolInformation /*SymbolInformation[] | null*/, error)
+ Symbol(context.Context, *WorkspaceSymbolParams) ([]SymbolInformation /*SymbolInformation[] | WorkspaceSymbol[] | null*/, error)
+ ResolveWorkspaceSymbol(context.Context, *WorkspaceSymbol) (*WorkspaceSymbol, error)
CodeLens(context.Context, *CodeLensParams) ([]CodeLens /*CodeLens[] | null*/, error)
ResolveCodeLens(context.Context, *CodeLens) (*CodeLens, error)
CodeLensRefresh(context.Context) error
@@ -82,7 +85,7 @@
OnTypeFormatting(context.Context, *DocumentOnTypeFormattingParams) ([]TextEdit /*TextEdit[] | null*/, error)
Rename(context.Context, *RenameParams) (*WorkspaceEdit /*WorkspaceEdit | null*/, error)
PrepareRename(context.Context, *PrepareRenameParams) (*Range /*Range | { range: Range, placeholder: string } | { defaultBehavior: boolean } | null*/, error)
- ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{} /*any | null*/, error)
+ ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{} /* LSPAny | void | float64*/, error)
Diagnostic(context.Context, *string) (*string, error)
DiagnosticWorkspace(context.Context, *WorkspaceDiagnosticParams) (*WorkspaceDiagnosticReport, error)
DiagnosticRefresh(context.Context) error
@@ -352,6 +355,19 @@
}
resp, err := server.Subtypes(ctx, ¶ms)
return true, reply(ctx, resp, err)
+ case "textDocument/inlineValues": // req
+ var params InlineValuesParams
+ if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
+ return true, sendParseError(ctx, reply, err)
+ }
+ resp, err := server.InlineValues(ctx, ¶ms)
+ return true, reply(ctx, resp, err)
+ case "workspace/inlineValues/refresh": // req
+ if len(r.Params()) > 0 {
+ return true, reply(ctx, nil, errors.Errorf("%w: expected no params", jsonrpc2.ErrInvalidParams))
+ }
+ err := server.InlineValuesRefresh(ctx)
+ return true, reply(ctx, nil, err)
case "initialize": // req
var params ParamInitialize
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
@@ -451,6 +467,13 @@
}
resp, err := server.Symbol(ctx, ¶ms)
return true, reply(ctx, resp, err)
+ case "workspaceSymbol/resolve": // req
+ var params WorkspaceSymbol
+ if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
+ return true, sendParseError(ctx, reply, err)
+ }
+ resp, err := server.ResolveWorkspaceSymbol(ctx, ¶ms)
+ return true, reply(ctx, resp, err)
case "textDocument/codeLens": // req
var params CodeLensParams
if err := json.Unmarshal(r.Params(), ¶ms); err != nil {
@@ -788,6 +811,18 @@
return result, nil
}
+func (s *serverDispatcher) InlineValues(ctx context.Context, params *InlineValuesParams) ([]InlineValue /*InlineValue[] | null*/, error) {
+ var result []InlineValue /*InlineValue[] | null*/
+ if err := s.sender.Call(ctx, "textDocument/inlineValues", params, &result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+func (s *serverDispatcher) InlineValuesRefresh(ctx context.Context) error {
+ return s.sender.Call(ctx, "workspace/inlineValues/refresh", nil, nil)
+}
+
func (s *serverDispatcher) Initialize(ctx context.Context, params *ParamInitialize) (*InitializeResult, error) {
var result *InitializeResult
if err := s.sender.Call(ctx, "initialize", params, &result); err != nil {
@@ -888,14 +923,22 @@
return result, nil
}
-func (s *serverDispatcher) Symbol(ctx context.Context, params *WorkspaceSymbolParams) ([]SymbolInformation /*SymbolInformation[] | null*/, error) {
- var result []SymbolInformation /*SymbolInformation[] | null*/
+func (s *serverDispatcher) Symbol(ctx context.Context, params *WorkspaceSymbolParams) ([]SymbolInformation /*SymbolInformation[] | WorkspaceSymbol[] | null*/, error) {
+ var result []SymbolInformation /*SymbolInformation[] | WorkspaceSymbol[] | null*/
if err := s.sender.Call(ctx, "workspace/symbol", params, &result); err != nil {
return nil, err
}
return result, nil
}
+func (s *serverDispatcher) ResolveWorkspaceSymbol(ctx context.Context, params *WorkspaceSymbol) (*WorkspaceSymbol, error) {
+ var result *WorkspaceSymbol
+ if err := s.sender.Call(ctx, "workspaceSymbol/resolve", params, &result); err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
func (s *serverDispatcher) CodeLens(ctx context.Context, params *CodeLensParams) ([]CodeLens /*CodeLens[] | null*/, error) {
var result []CodeLens /*CodeLens[] | null*/
if err := s.sender.Call(ctx, "textDocument/codeLens", params, &result); err != nil {
@@ -972,8 +1015,8 @@
return result, nil
}
-func (s *serverDispatcher) ExecuteCommand(ctx context.Context, params *ExecuteCommandParams) (interface{} /*any | null*/, error) {
- var result interface{} /*any | null*/
+func (s *serverDispatcher) ExecuteCommand(ctx context.Context, params *ExecuteCommandParams) (interface{} /* LSPAny | void | float64*/, error) {
+ var result interface{} /* LSPAny | void | float64*/
if err := s.sender.Call(ctx, "workspace/executeCommand", params, &result); err != nil {
return nil, err
}
diff --git a/internal/lsp/protocol/typescript/README.md b/internal/lsp/protocol/typescript/README.md
index 61ba187..74bcd18 100644
--- a/internal/lsp/protocol/typescript/README.md
+++ b/internal/lsp/protocol/typescript/README.md
@@ -48,7 +48,7 @@
3. (There's a good chance that soon you will be asked to upgrade your new npm. `sudo npm install -g npm` is the command.)
4. For either system, node and nvm should now be available. Running `node -v` and `npm -v` should produce version numbers.
5. `npm install typescript`
- 1. This will likely give warning messages that indicate you've failed to set up a project. Ignore them.
+ 1. This may give warning messages that indicate you've failed to set up a project. Ignore them.
2. Your home directory will now have new directories `.npm` and `node_modules` (and a `package_lock.json` file)
3. The typescript executable `tsc` will be in `node_modules/.bin`, so put that directory in your path.
4. `tsc -v` should print "Version 4.2.4" (or later). If not you may (as I did) have an obsolete tsc earlier in your path.
diff --git a/internal/lsp/protocol/typescript/code.ts b/internal/lsp/protocol/typescript/code.ts
index a262058..807800a 100644
--- a/internal/lsp/protocol/typescript/code.ts
+++ b/internal/lsp/protocol/typescript/code.ts
@@ -532,6 +532,7 @@
function sameType(a: ts.TypeNode, b: ts.TypeNode): boolean {
if (a.kind !== b.kind) return false;
if (a.kind === ts.SyntaxKind.BooleanKeyword) return true;
+ if (a.kind === ts.SyntaxKind.StringKeyword) return true;
if (ts.isTypeReferenceNode(a) && ts.isTypeReferenceNode(b) &&
a.typeName.getText() === b.typeName.getText()) return true;
if (ts.isArrayTypeNode(a) && ts.isArrayTypeNode(b)) return sameType(a.elementType, b.elementType);
@@ -540,7 +541,7 @@
if (a.members.length === 1) return a.members[0].name.getText() === b.members[0].name.getText();
if (loc(a) === loc(b)) return true;
}
- throw new Error(`546 sameType? ${strKind(a)} ${strKind(b)}`);
+ throw new Error(`544 sameType? ${strKind(a)} ${strKind(b)} ${a.getText()}`);
}
type CreateMutable<Type> = {
-readonly [Property in keyof Type]: Type[Property];
@@ -721,6 +722,7 @@
const f = function (n: ts.ExpressionWithTypeArguments) {
if (!ts.isIdentifier(n.expression))
throw new Error(`Interface ${nm} heritage ${strKind(n.expression)} `);
+ if (n.expression.getText() === 'Omit') return; // Type modification type
ans = ans.concat(goName(n.expression.getText()), '\n');
};
d.as.forEach((n: ts.HeritageClause) => n.types.forEach(f));
@@ -874,9 +876,8 @@
if (a == 'NumberKeyword' && b == 'StringKeyword') { // ID
return `interface{} ${help}`;
}
- if (b == 'NullKeyword' || n.types[1].getText() === 'null') {
- // PJW: fix this. it looks like 'null' is now being parsed as LiteralType
- // and check the other keyword cases
+ // for null, b is not useful (LiternalType)
+ if (n.types[1].getText() === 'null') {
if (nm == 'textDocument/codeAction') {
// (Command | CodeAction)[] | null
return `[]CodeAction ${help}`;
@@ -896,9 +897,11 @@
return `*TextEdit ${help}`;
}
if (a == 'TypeReference') {
- if (nm == 'edits') return `${goType(n.types[0], '715')} ${help}`;
+ if (nm == 'edits') return `${goType(n.types[0], '901')} ${help}`;
if (a == b) return `interface{} ${help}`;
if (nm == 'code') return `interface{} ${help}`;
+ if (nm == 'editRange') return `${goType(n.types[0], '904')} ${help}`;
+ if (nm === 'location') return `${goType(n.types[0], '905')} ${help}`;
}
if (a == 'StringKeyword') return `string ${help}`;
if (a == 'TypeLiteral' && nm == 'TextDocumentContentChangeEvent') {
@@ -915,6 +918,7 @@
const aa = strKind(n.types[0]);
const bb = strKind(n.types[1]);
const cc = strKind(n.types[2]);
+ if (nm === 'workspace/symbol') return `${goType(n.types[0], '930')} ${help}`;
if (nm == 'DocumentFilter') {
// not really a union. the first is enough, up to a missing
// omitempty but avoid repetitious comments
@@ -942,9 +946,11 @@
case 4:
if (nm == 'documentChanges') return `TextDocumentEdit ${help} `;
if (nm == 'textDocument/prepareRename') return `Range ${help} `;
- // eslint-disable-next-line no-fallthrough
+ break;
+ case 8: // LSPany
+ break;
default:
- throw new Error(`goUnionType len=${n.types.length} nm=${nm}`);
+ throw new Error(`957 goUnionType len=${n.types.length} nm=${nm} ${n.getText()}`);
}
// Result will be interface{} with a comment
@@ -1048,7 +1054,7 @@
case 'TypeReference': {
if (!ts.isTypeReferenceNode(te)) throw new Error(`1047 impossible ${strKind(te)}`);
const d = seenTypes.get(goName(te.typeName.getText()));
- if (d === undefined) return false;
+ if (d === undefined || d.properties.length == 0) return false;
if (d.properties.length > 1) return true;
// alias or interface with a single property (The alias is Uinteger, which we ignore later)
if (d.alias) return false;
@@ -1067,6 +1073,10 @@
if (ts.isPropertySignature(nx)) {
let json = u.JSON(nx);
let typ = goType(nx.type, nx.name.getText());
+ // }/*\n*/`json:v` is not legal, the comment is a newline
+ if (typ.includes('\n') && typ.indexOf('*/') === typ.length - 2) {
+ typ = typ.replace(/\n\t*/g, ' ');
+ }
const v = getComments(nx) || '';
starred.forEach(([a, b]) => {
if (a != nm || b != typ.toLowerCase()) return;
@@ -1080,12 +1090,16 @@
const comment = nx.getText().replace(/[/]/g, '');
if (nx.getText() == '[uri: string]: TextEdit[];') {
res = 'map[string][]TextEdit';
- } else if (nx.getText() == '[id: string /* ChangeAnnotationIdentifier */]: ChangeAnnotation;') {
+ } else if (nx.getText().startsWith('[id: ChangeAnnotationIdentifier]')) {
res = 'map[string]ChangeAnnotationIdentifier';
} else if (nx.getText().startsWith('[uri: string')) {
res = 'map[string]interface{}';
+ } else if (nx.getText().startsWith('[uri: DocumentUri')) {
+ res = 'map[DocumentURI][]TextEdit';
+ } else if (nx.getText().startsWith('[key: string')) {
+ res = 'map[string]interface{}';
} else {
- throw new Error(`1088 handle ${nx.getText()} ${loc(nx)}`);
+ throw new Error(`1100 handle ${nx.getText()} ${loc(nx)}`);
}
res += ` /*${comment}*/`;
ans.push(res);
diff --git a/internal/lsp/protocol/typescript/util.ts b/internal/lsp/protocol/typescript/util.ts
index 84fdcf3..165ba9b 100644
--- a/internal/lsp/protocol/typescript/util.ts
+++ b/internal/lsp/protocol/typescript/util.ts
@@ -15,7 +15,7 @@
`${dir}/${srcDir}/protocol/src/browser/main.ts`, `${dir}${srcDir}/types/src/main.ts`,
`${dir}${srcDir}/jsonrpc/src/node/main.ts`
];
-export const gitHash = '10b56de150ad67c3c330da8e2df53ebf2cf347c4';
+export const gitHash = 'd959faf4be476a6e0a08d5612e91fcac14ff9929';
let outFname = 'tsprotocol.go';
let fda: number, fdb: number, fde: number; // file descriptors
@@ -112,7 +112,7 @@
let pref = new Map<string, string>([
['DiagnosticSeverity', 'Severity'], ['WatchKind', 'Watch'],
['SignatureHelpTriggerKind', 'Sig'], ['CompletionItemTag', 'Compl'],
- ['Integer', 'INT_'], ['Uinteger', 'UINT_']
+ ['Integer', 'INT_'], ['Uinteger', 'UINT_'], ['CodeActionTriggerKind', 'CodeAction']
]); // typeName->prefix
let suff = new Map<string, string>([
['CompletionItemKind', 'Completion'], ['InsertTextFormat', 'TextFormat'],
diff --git a/internal/lsp/server_gen.go b/internal/lsp/server_gen.go
index 3ed3952..85d84cf 100644
--- a/internal/lsp/server_gen.go
+++ b/internal/lsp/server_gen.go
@@ -144,6 +144,14 @@
return s.initialized(ctx, params)
}
+func (s *Server) InlineValues(context.Context, *protocol.InlineValuesParams) ([]protocol.InlineValue, error) {
+ return nil, notImplemented("InlineValues")
+}
+
+func (s *Server) InlineValuesRefresh(context.Context) error {
+ return notImplemented("InlineValuesRefresh")
+}
+
func (s *Server) LinkedEditingRange(context.Context, *protocol.LinkedEditingRangeParams) (*protocol.LinkedEditingRanges, error) {
return nil, notImplemented("LinkedEditingRange")
}
@@ -208,6 +216,10 @@
return nil, notImplemented("ResolveDocumentLink")
}
+func (s *Server) ResolveWorkspaceSymbol(context.Context, *protocol.WorkspaceSymbol) (*protocol.WorkspaceSymbol, error) {
+ return nil, notImplemented("ResolveWorkspaceSymbol")
+}
+
func (s *Server) SelectionRange(context.Context, *protocol.SelectionRangeParams) ([]protocol.SelectionRange, error) {
return nil, notImplemented("SelectionRange")
}
diff --git a/internal/lsp/source/api_json.go b/internal/lsp/source/api_json.go
index b536df2..5c4e51d 100755
--- a/internal/lsp/source/api_json.go
+++ b/internal/lsp/source/api_json.go
@@ -45,19 +45,6 @@
Hierarchy: "build",
},
{
- Name: "templateSupport",
- Type: "bool",
- Doc: "templateSupport can be used to turn off support for template files.\n",
- EnumKeys: EnumKeys{
- ValueType: "",
- Keys: nil,
- },
- EnumValues: nil,
- Default: "true",
- Status: "",
- Hierarchy: "build",
- },
- {
Name: "templateExtensions",
Type: "[]string",
Doc: "templateExtensions gives the extensions of file names that are treateed\nas template files. (The extension\nis the part of the file name after the final dot.)\n",
@@ -456,7 +443,7 @@
},
{
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\nfunc f[T any](T) {}\n\nfunc _() {\n\tf[string](\"foo\") // string could be inferred\n}\n",
+ 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",
},
{
@@ -576,7 +563,7 @@
},
{
Name: "\"fillreturns\"",
- Doc: "suggested fixes for \"wrong number of return values (want %d, got %d)\"\n\nThis checker provides suggested fixes for type errors of the\ntype \"wrong number of return values (want %d, got %d)\". For example:\n\tfunc m() (int, string, *bool, error) {\n\t\treturn\n\t}\nwill turn into\n\tfunc m() (int, string, *bool, error) {\n\t\treturn 0, \"\", nil, nil\n\t}\n\nThis functionality is similar to https://github.com/sqs/goreturns.\n",
+ Doc: "suggest fixes for errors due to an incorrect number of return values\n\nThis checker provides suggested fixes for type errors of the\ntype \"wrong number of return values (want %d, got %d)\". For example:\n\tfunc m() (int, string, *bool, error) {\n\t\treturn\n\t}\nwill turn into\n\tfunc m() (int, string, *bool, error) {\n\t\treturn 0, \"\", nil, nil\n\t}\n\nThis functionality is similar to https://github.com/sqs/goreturns.\n",
Default: "true",
},
{
@@ -1034,7 +1021,7 @@
},
{
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\nfunc f[T any](T) {}\n\nfunc _() {\n\tf[string](\"foo\") // string could be inferred\n}\n",
+ 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,
},
{
@@ -1154,7 +1141,7 @@
},
{
Name: "fillreturns",
- Doc: "suggested fixes for \"wrong number of return values (want %d, got %d)\"\n\nThis checker provides suggested fixes for type errors of the\ntype \"wrong number of return values (want %d, got %d)\". For example:\n\tfunc m() (int, string, *bool, error) {\n\t\treturn\n\t}\nwill turn into\n\tfunc m() (int, string, *bool, error) {\n\t\treturn 0, \"\", nil, nil\n\t}\n\nThis functionality is similar to https://github.com/sqs/goreturns.\n",
+ Doc: "suggest fixes for errors due to an incorrect number of return values\n\nThis checker provides suggested fixes for type errors of the\ntype \"wrong number of return values (want %d, got %d)\". For example:\n\tfunc m() (int, string, *bool, error) {\n\t\treturn\n\t}\nwill turn into\n\tfunc m() (int, string, *bool, error) {\n\t\treturn 0, \"\", nil, nil\n\t}\n\nThis functionality is similar to https://github.com/sqs/goreturns.\n",
Default: true,
},
{
diff --git a/internal/lsp/source/format.go b/internal/lsp/source/format.go
index 16e72c2..4f02e4f 100644
--- a/internal/lsp/source/format.go
+++ b/internal/lsp/source/format.go
@@ -29,6 +29,11 @@
ctx, done := event.Start(ctx, "source.Format")
defer done()
+ // Generated files shouldn't be edited. So, don't format them
+ if IsGenerated(ctx, snapshot, fh.URI()) {
+ return nil, fmt.Errorf("can't format %q: file is generated", fh.URI().Filename())
+ }
+
pgf, err := snapshot.ParseGo(ctx, fh, ParseFull)
if err != nil {
return nil, err
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index 41a0288..147bb9e 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -114,7 +114,6 @@
ExperimentalPackageCacheKey: true,
MemoryMode: ModeNormal,
DirectoryFilters: []string{"-node_modules"},
- TemplateSupport: true,
TemplateExtensions: []string{"tmpl", "gotmpl"},
},
UIOptions: UIOptions{
@@ -233,9 +232,6 @@
// Include only project_a, but not node_modules inside it: `-`, `+project_a`, `-project_a/node_modules`
DirectoryFilters []string
- // TemplateSupport can be used to turn off support for template files.
- TemplateSupport bool
-
// TemplateExtensions gives the extensions of file names that are treateed
// as template files. (The extension
// is the part of the file name after the final dot.)
@@ -940,22 +936,23 @@
case "experimentalWorkspaceModule":
result.setBool(&o.ExperimentalWorkspaceModule)
- case "experimentalTemplateSupport", // remove after June 2022
- "templateSupport":
- if name == "experimentalTemplateSupport" {
- result.State = OptionDeprecated
- result.Replacement = "templateSupport"
- }
- result.setBool(&o.TemplateSupport)
+ case "experimentalTemplateSupport": // remove after June 2022
+ result.State = OptionDeprecated
case "templateExtensions":
- iexts, ok := value.([]string)
- if !ok {
- result.errorf("invalid type %T, expect []string", value)
+ if iexts, ok := value.([]interface{}); ok {
+ ans := []string{}
+ for _, x := range iexts {
+ ans = append(ans, fmt.Sprint(x))
+ }
+ o.TemplateExtensions = ans
break
}
- o.TemplateExtensions = iexts
-
+ if value == nil {
+ o.TemplateExtensions = nil
+ break
+ }
+ result.errorf(fmt.Sprintf("unexpected type %T not []string", value))
case "experimentalDiagnosticsDelay", "diagnosticsDelay":
if name == "experimentalDiagnosticsDelay" {
result.State = OptionDeprecated
diff --git a/internal/lsp/template/completion.go b/internal/lsp/template/completion.go
index 4ec7623..55b59f8 100644
--- a/internal/lsp/template/completion.go
+++ b/internal/lsp/template/completion.go
@@ -78,13 +78,15 @@
// 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 {
+ if tk.Start < offset && offset <= tk.End {
return tk.Start
}
}
@@ -244,7 +246,7 @@
ans[len(ans)-1] = "." + lit
} else if tok == token.IDENT && len(ans) > 0 && ans[len(ans)-1] == "$" {
ans[len(ans)-1] = "$" + lit
- } else {
+ } else if lit != "" {
ans = append(ans, lit)
}
}
diff --git a/internal/lsp/template/completion_test.go b/internal/lsp/template/completion_test.go
index 7d17ab1..b7ef89f 100644
--- a/internal/lsp/template/completion_test.go
+++ b/internal/lsp/template/completion_test.go
@@ -23,40 +23,41 @@
}
// Test completions in templates that parse enough (if completion needs symbols)
+// Seen characters up to the ^
func TestParsed(t *testing.T) {
var tests = []tparse{
- {"{{^if}}", []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}}{{.m^f", []string{"Modified"}},
- {"{{^$ }}", []string{"$"}},
- {"{{$a =3}}{{^$", []string{"$a"}},
+ {`<table class="chroma" data-new-comment-url="{{if $.PageIsPullFiles}}{{$.Issue.HTMLURL}}/files/reviews/new_comment{{else}}{{$.CommitHTML}}/new_comment^{{end}}">`, 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"}},
- {"{{i^n{{Internal}}", []string{"index", "Internal", "if"}},
+ {`{{.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"}},
+ {"{{`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)
- ans, err := c.complete()
- if err != nil {
- t.Fatal(err)
- }
var v []string
- for _, a := range ans.Items {
- v = append(v, a.Label)
+ 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 %v, wanted %v", tx.marked, v, tx.wanted)
@@ -75,16 +76,18 @@
func testCompleter(t *testing.T, tx tparse) *completer {
t.Helper()
- col := strings.Index(tx.marked, "^") + 1
- offset := strings.LastIndex(tx.marked[:col], string(Left))
- if offset < 0 {
- t.Fatalf("no {{ before ^: %q", tx.marked)
- }
+ // 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{
diff --git a/internal/lsp/template/implementations.go b/internal/lsp/template/implementations.go
index 193b973..2db0341 100644
--- a/internal/lsp/template/implementations.go
+++ b/internal/lsp/template/implementations.go
@@ -66,7 +66,7 @@
}
func skipTemplates(s source.Snapshot) bool {
- return !s.View().Options().TemplateSupport
+ return len(s.View().Options().TemplateExtensions) == 0
}
// Definition finds the definitions of the symbol at loc. It
@@ -118,6 +118,12 @@
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)
}
diff --git a/internal/memoize/memoize.go b/internal/memoize/memoize.go
index d4b8773..0037342 100644
--- a/internal/memoize/memoize.go
+++ b/internal/memoize/memoize.go
@@ -62,6 +62,8 @@
destroyed uint32
store *Store
name string
+ // destroyedBy describes the caller that togged destroyed from 0 to 1.
+ destroyedBy string
// wg tracks the reference count of this generation.
wg sync.WaitGroup
}
@@ -69,10 +71,16 @@
// Destroy waits for all operations referencing g to complete, then removes
// all references to g from cache entries. Cache entries that no longer
// reference any non-destroyed generation are removed. Destroy must be called
-// exactly once for each generation.
-func (g *Generation) Destroy() {
+// exactly once for each generation, and destroyedBy describes the caller.
+func (g *Generation) Destroy(destroyedBy string) {
g.wg.Wait()
- atomic.StoreUint32(&g.destroyed, 1)
+
+ prevDestroyedBy := g.destroyedBy
+ g.destroyedBy = destroyedBy
+ if ok := atomic.CompareAndSwapUint32(&g.destroyed, 0, 1); !ok {
+ panic("Destroy on generation " + g.name + " already destroyed by " + prevDestroyedBy)
+ }
+
g.store.mu.Lock()
defer g.store.mu.Unlock()
for k, e := range g.store.handles {
@@ -94,13 +102,10 @@
// Acquire creates a new reference to g, and returns a func to release that
// reference.
-func (g *Generation) Acquire(ctx context.Context) func() {
+func (g *Generation) Acquire() func() {
destroyed := atomic.LoadUint32(&g.destroyed)
- if ctx.Err() != nil {
- return func() {}
- }
if destroyed != 0 {
- panic("acquire on destroyed generation " + g.name)
+ panic("acquire on generation " + g.name + " destroyed by " + g.destroyedBy)
}
g.wg.Add(1)
return g.wg.Done
@@ -175,7 +180,7 @@
panic("the function passed to bind must not be nil")
}
if atomic.LoadUint32(&g.destroyed) != 0 {
- panic("operation on destroyed generation " + g.name)
+ panic("operation on generation " + g.name + " destroyed by " + g.destroyedBy)
}
g.store.mu.Lock()
defer g.store.mu.Unlock()
@@ -233,7 +238,7 @@
func (g *Generation) Inherit(hs ...*Handle) {
for _, h := range hs {
if atomic.LoadUint32(&g.destroyed) != 0 {
- panic("inherit on destroyed generation " + g.name)
+ panic("inherit on generation " + g.name + " destroyed by " + g.destroyedBy)
}
h.mu.Lock()
@@ -266,7 +271,7 @@
// If the value is not yet ready, the underlying function will be invoked.
// If ctx is cancelled, Get returns nil.
func (h *Handle) Get(ctx context.Context, g *Generation, arg Arg) (interface{}, error) {
- release := g.Acquire(ctx)
+ release := g.Acquire()
defer release()
if ctx.Err() != nil {
@@ -311,7 +316,7 @@
function := h.function // Read under the lock
// Make sure that the generation isn't destroyed while we're running in it.
- release := g.Acquire(ctx)
+ release := g.Acquire()
go func() {
defer release()
// Just in case the function does something expensive without checking
diff --git a/internal/memoize/memoize_test.go b/internal/memoize/memoize_test.go
index 41f20d0..f05966b 100644
--- a/internal/memoize/memoize_test.go
+++ b/internal/memoize/memoize_test.go
@@ -60,11 +60,11 @@
expectGet(t, h2, g2, "res")
// With g1 destroyed, g2 should still work.
- g1.Destroy()
+ g1.Destroy("TestGenerations")
expectGet(t, h2, g2, "res")
// With all generations destroyed, key should be re-evaluated.
- g2.Destroy()
+ g2.Destroy("TestGenerations")
g3 := s.Generation("g3")
h3 := g3.Bind("key", func(context.Context, memoize.Arg) interface{} { return "new res" }, nil)
expectGet(t, h3, g3, "new res")
@@ -89,7 +89,7 @@
g2 := s.Generation("g2")
g2.Inherit(h1, h2)
- g1.Destroy()
+ g1.Destroy("TestCleanup")
expectGet(t, h1, g2, &v1)
expectGet(t, h2, g2, &v2)
for k, v := range map[string]*bool{"key1": &v1, "key2": &v2} {
@@ -97,7 +97,7 @@
t.Errorf("after destroying g1, bound value %q is cleaned up", k)
}
}
- g2.Destroy()
+ g2.Destroy("TestCleanup")
if got, want := v1, false; got != want {
t.Error("after destroying g2, v1 is cleaned up")
}
diff --git a/internal/typeparams/normalize.go b/internal/typeparams/normalize.go
index f41ec6e..090f142 100644
--- a/internal/typeparams/normalize.go
+++ b/internal/typeparams/normalize.go
@@ -23,9 +23,9 @@
//
// Structural type restrictions of a type parameter are created via
// non-interface types embedded in its constraint interface (directly, or via a
-// chain of interface embeddings). For example, in the declaration `type T[P
-// interface{~int; m()}] int`, the structural restriction of the type parameter
-// P is ~int.
+// chain of interface embeddings). For example, in the declaration
+// type T[P interface{~int; m()}] int
+// the structural restriction of the type parameter P is ~int.
//
// With interface embedding and unions, the specification of structural type
// restrictions may be arbitrarily complex. For example, consider the
@@ -67,7 +67,31 @@
if iface == nil {
return nil, fmt.Errorf("constraint is %T, not *types.Interface", constraint.Underlying())
}
- tset, err := computeTermSet(iface, make(map[types.Type]*termSet), 0)
+ return InterfaceTermSet(iface)
+}
+
+// InterfaceTermSet computes the normalized terms for a constraint interface,
+// returning an error if the term set cannot be computed or is empty. In the
+// latter case, the error will be ErrEmptyTypeSet.
+//
+// See the documentation of StructuralTerms for more information on
+// normalization.
+func InterfaceTermSet(iface *types.Interface) ([]*Term, error) {
+ return computeTermSet(iface)
+}
+
+// UnionTermSet computes the normalized terms for a union, returning an error
+// if the term set cannot be computed or is empty. In the latter case, the
+// error will be ErrEmptyTypeSet.
+//
+// See the documentation of StructuralTerms for more information on
+// normalization.
+func UnionTermSet(union *Union) ([]*Term, error) {
+ return computeTermSet(union)
+}
+
+func computeTermSet(typ types.Type) ([]*Term, error) {
+ tset, err := computeTermSetInternal(typ, make(map[types.Type]*termSet), 0)
if err != nil {
return nil, err
}
@@ -98,7 +122,7 @@
fmt.Fprintf(os.Stderr, strings.Repeat(".", depth)+format+"\n", args...)
}
-func computeTermSet(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) {
+func computeTermSetInternal(t types.Type, seen map[types.Type]*termSet, depth int) (res *termSet, err error) {
if t == nil {
panic("nil type")
}
@@ -139,7 +163,7 @@
if _, ok := embedded.Underlying().(*TypeParam); ok {
return nil, fmt.Errorf("invalid embedded type %T", embedded)
}
- tset2, err := computeTermSet(embedded, seen, depth+1)
+ tset2, err := computeTermSetInternal(embedded, seen, depth+1)
if err != nil {
return nil, err
}
@@ -153,7 +177,7 @@
var terms termlist
switch t.Type().Underlying().(type) {
case *types.Interface:
- tset2, err := computeTermSet(t.Type(), seen, depth+1)
+ tset2, err := computeTermSetInternal(t.Type(), seen, depth+1)
if err != nil {
return nil, err
}
diff --git a/internal/typeparams/typeparams_go117.go b/internal/typeparams/typeparams_go117.go
index 6ad3a43..e509daf 100644
--- a/internal/typeparams/typeparams_go117.go
+++ b/internal/typeparams/typeparams_go117.go
@@ -75,6 +75,7 @@
// this Go version. Its methods panic on use.
type TypeParam struct{ types.Type }
+func (*TypeParam) Index() int { unsupported(); return 0 }
func (*TypeParam) Constraint() types.Type { unsupported(); return nil }
func (*TypeParam) Obj() *types.TypeName { unsupported(); return nil }
diff --git a/refactor/importgraph/graph_test.go b/refactor/importgraph/graph_test.go
index 2ab54e2..7526383 100644
--- a/refactor/importgraph/graph_test.go
+++ b/refactor/importgraph/graph_test.go
@@ -10,7 +10,9 @@
package importgraph_test
import (
+ "fmt"
"go/build"
+ "os"
"sort"
"strings"
"testing"
@@ -30,10 +32,40 @@
var gopath string
for _, env := range exported.Config.Env {
- if !strings.HasPrefix(env, "GOPATH=") {
+ eq := strings.Index(env, "=")
+ if eq == 0 {
+ // We sometimes see keys with a single leading "=" in the environment on Windows.
+ // TODO(#49886): What is the correct way to parse them in general?
+ eq = strings.Index(env[1:], "=") + 1
+ }
+ if eq < 0 {
+ t.Fatalf("invalid variable in exported.Config.Env: %q", env)
+ }
+ k := env[:eq]
+ v := env[eq+1:]
+ if k == "GOPATH" {
+ gopath = v
+ }
+
+ if os.Getenv(k) == v {
continue
}
- gopath = strings.TrimPrefix(env, "GOPATH=")
+ defer func(prev string, prevOK bool) {
+ if !prevOK {
+ if err := os.Unsetenv(k); err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ if err := os.Setenv(k, prev); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }(os.LookupEnv(k))
+
+ if err := os.Setenv(k, v); err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("%s=%s", k, v)
}
if gopath == "" {
t.Fatal("Failed to fish GOPATH out of env: ", exported.Config.Env)
@@ -41,45 +73,97 @@
var buildContext = build.Default
buildContext.GOPATH = gopath
+ buildContext.Dir = exported.Config.Dir
- forward, reverse, errors := importgraph.Build(&buildContext)
+ forward, reverse, errs := importgraph.Build(&buildContext)
+ for path, err := range errs {
+ t.Errorf("%s: %s", path, err)
+ }
+ if t.Failed() {
+ return
+ }
+
+ // Log the complete graph before the errors, so that the errors are near the
+ // end of the log (where we expect them to be).
+ nodePrinted := map[string]bool{}
+ printNode := func(direction string, from string) {
+ key := fmt.Sprintf("%s[%q]", direction, from)
+ if nodePrinted[key] {
+ return
+ }
+ nodePrinted[key] = true
+
+ var g importgraph.Graph
+ switch direction {
+ case "forward":
+ g = forward
+ case "reverse":
+ g = reverse
+ default:
+ t.Helper()
+ t.Fatalf("bad direction: %q", direction)
+ }
+
+ t.Log(key)
+ var pkgs []string
+ for pkg := range g[from] {
+ pkgs = append(pkgs, pkg)
+ }
+ sort.Strings(pkgs)
+ for _, pkg := range pkgs {
+ t.Logf("\t%s", pkg)
+ }
+ }
+
+ if testing.Verbose() {
+ printNode("forward", this)
+ printNode("reverse", this)
+ }
// Test direct edges.
// We throw in crypto/hmac to prove that external test files
// (such as this one) are inspected.
for _, p := range []string{"go/build", "testing", "crypto/hmac"} {
if !forward[this][p] {
- t.Errorf("forward[importgraph][%s] not found", p)
+ printNode("forward", this)
+ t.Errorf("forward[%q][%q] not found", this, p)
}
if !reverse[p][this] {
- t.Errorf("reverse[%s][importgraph] not found", p)
+ printNode("reverse", p)
+ t.Errorf("reverse[%q][%q] not found", p, this)
}
}
// Test non-existent direct edges
for _, p := range []string{"errors", "reflect"} {
if forward[this][p] {
- t.Errorf("unexpected: forward[importgraph][%s] found", p)
+ printNode("forward", this)
+ t.Errorf("unexpected: forward[%q][%q] found", this, p)
}
if reverse[p][this] {
- t.Errorf("unexpected: reverse[%s][importgraph] found", p)
+ printNode("reverse", p)
+ t.Errorf("unexpected: reverse[%q][%q] found", p, this)
}
}
// Test Search is reflexive.
if !forward.Search(this)[this] {
+ printNode("forward", this)
t.Errorf("irreflexive: forward.Search(importgraph)[importgraph] not found")
}
if !reverse.Search(this)[this] {
+ printNode("reverse", this)
t.Errorf("irrefexive: reverse.Search(importgraph)[importgraph] not found")
}
// Test Search is transitive. (There is no direct edge to these packages.)
for _, p := range []string{"errors", "reflect", "unsafe"} {
if !forward.Search(this)[p] {
+ printNode("forward", this)
t.Errorf("intransitive: forward.Search(importgraph)[%s] not found", p)
}
if !reverse.Search(p)[this] {
+ printNode("reverse", p)
t.Errorf("intransitive: reverse.Search(%s)[importgraph] not found", p)
}
}
@@ -95,26 +179,10 @@
!forward.Search("io")["fmt"] ||
!reverse.Search("fmt")["io"] ||
!reverse.Search("io")["fmt"] {
+ printNode("forward", "fmt")
+ printNode("forward", "io")
+ printNode("reverse", "fmt")
+ printNode("reverse", "io")
t.Errorf("fmt and io are not mutually reachable despite being in the same SCC")
}
-
- // debugging
- if false {
- for path, err := range errors {
- t.Logf("%s: %s", path, err)
- }
- printSorted := func(direction string, g importgraph.Graph, start string) {
- t.Log(direction)
- var pkgs []string
- for pkg := range g.Search(start) {
- pkgs = append(pkgs, pkg)
- }
- sort.Strings(pkgs)
- for _, pkg := range pkgs {
- t.Logf("\t%s", pkg)
- }
- }
- printSorted("forward", forward, this)
- printSorted("reverse", reverse, this)
- }
}