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, &params)
 		return true, reply(ctx, resp, err)
+	case "textDocument/inlineValues": // req
+		var params InlineValuesParams
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.InlineValues(ctx, &params)
+		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(), &params); err != nil {
@@ -451,6 +467,13 @@
 		}
 		resp, err := server.Symbol(ctx, &params)
 		return true, reply(ctx, resp, err)
+	case "workspaceSymbol/resolve": // req
+		var params WorkspaceSymbol
+		if err := json.Unmarshal(r.Params(), &params); err != nil {
+			return true, sendParseError(ctx, reply, err)
+		}
+		resp, err := server.ResolveWorkspaceSymbol(ctx, &params)
+		return true, reply(ctx, resp, err)
 	case "textDocument/codeLens": // req
 		var params CodeLensParams
 		if err := json.Unmarshal(r.Params(), &params); 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)
-	}
 }