all: merge master (9358add) into gopls-release-branch.0.9

Also add the replace directive to ./gopls/go.mod.

For golang/go#53412

Merge List:

+ 2022-06-30 9358addba internal/lsp/cache: remove unused function
+ 2022-06-30 c77473fa9 gopls: upgrade staticcheck to v0.3.2
+ 2022-06-30 e8e5b3708 internal/lsp/cache: don't construct a new metadata graph if no changes
+ 2022-06-30 8314b7aa0 go/analysis: add suggested fix for unkeyed composite literals
+ 2022-06-30 8865782bc internal/lsp: add text edits for unkeyed literals
+ 2022-06-30 1a196f049 internal/lsp/cache: don't build symbol info for non-Go files
+ 2022-06-29 b84d509d6 gopls/doc: regenerate documentation
+ 2022-06-29 c10541a14 go/analysis/passes/fieldalignment: document "false sharing"
+ 2022-06-28 7743d1d94 internal/lsp: respect range for inlay hints
+ 2022-06-28 024871439 internal/lsp: add additional instrumentation around package loading
+ 2022-06-28 e5b332499 internal/lsp: add InlayHint regtests
+ 2022-06-28 2a900561e go/gcexportdata: fix Find for Go modules

Change-Id: Id16dc1247a73be034ddfc70630254db082da7b6f
diff --git a/go/analysis/passes/composite/composite.go b/go/analysis/passes/composite/composite.go
index d3670ac..64e184d 100644
--- a/go/analysis/passes/composite/composite.go
+++ b/go/analysis/passes/composite/composite.go
@@ -7,6 +7,7 @@
 package composite
 
 import (
+	"fmt"
 	"go/ast"
 	"go/types"
 	"strings"
@@ -83,7 +84,8 @@
 		}
 		for _, typ := range structuralTypes {
 			under := deref(typ.Underlying())
-			if _, ok := under.(*types.Struct); !ok {
+			strct, ok := under.(*types.Struct)
+			if !ok {
 				// skip non-struct composite literals
 				continue
 			}
@@ -92,20 +94,47 @@
 				continue
 			}
 
-			// check if the CompositeLit contains an unkeyed field
+			// check if the struct contains an unkeyed field
 			allKeyValue := true
-			for _, e := range cl.Elts {
+			var suggestedFixAvailable = len(cl.Elts) == strct.NumFields()
+			var missingKeys []analysis.TextEdit
+			for i, e := range cl.Elts {
 				if _, ok := e.(*ast.KeyValueExpr); !ok {
 					allKeyValue = false
-					break
+					if i >= strct.NumFields() {
+						break
+					}
+					field := strct.Field(i)
+					if !field.Exported() {
+						// Adding unexported field names for structs not defined
+						// locally will not work.
+						suggestedFixAvailable = false
+						break
+					}
+					missingKeys = append(missingKeys, analysis.TextEdit{
+						Pos:     e.Pos(),
+						End:     e.Pos(),
+						NewText: []byte(fmt.Sprintf("%s: ", field.Name())),
+					})
 				}
 			}
 			if allKeyValue {
-				// all the composite literal fields are keyed
+				// all the struct fields are keyed
 				continue
 			}
 
-			pass.ReportRangef(cl, "%s composite literal uses unkeyed fields", typeName)
+			diag := analysis.Diagnostic{
+				Pos:     cl.Pos(),
+				End:     cl.End(),
+				Message: fmt.Sprintf("%s struct literal uses unkeyed fields", typeName),
+			}
+			if suggestedFixAvailable {
+				diag.SuggestedFixes = []analysis.SuggestedFix{{
+					Message:   "Add field names to struct literal",
+					TextEdits: missingKeys,
+				}}
+			}
+			pass.Report(diag)
 			return
 		}
 	})
diff --git a/go/analysis/passes/composite/composite_test.go b/go/analysis/passes/composite/composite_test.go
index 952de8b..7afaaa7 100644
--- a/go/analysis/passes/composite/composite_test.go
+++ b/go/analysis/passes/composite/composite_test.go
@@ -18,5 +18,5 @@
 	if typeparams.Enabled {
 		pkgs = append(pkgs, "typeparams")
 	}
-	analysistest.Run(t, testdata, composite.Analyzer, pkgs...)
+	analysistest.RunWithSuggestedFixes(t, testdata, composite.Analyzer, pkgs...)
 }
diff --git a/go/analysis/passes/composite/testdata/src/a/a.go b/go/analysis/passes/composite/testdata/src/a/a.go
index 3a5bc20..cd69d39 100644
--- a/go/analysis/passes/composite/testdata/src/a/a.go
+++ b/go/analysis/passes/composite/testdata/src/a/a.go
@@ -11,6 +11,7 @@
 	"go/scanner"
 	"go/token"
 	"image"
+	"sync"
 	"unicode"
 )
 
@@ -79,6 +80,18 @@
 	nil, // Value
 	"DefValue",
 }
+var tooManyFieldsStructLiteral = flag.Flag{ // want "unkeyed fields"
+	"Name",
+	"Usage",
+	nil, // Value
+	"DefValue",
+	"Extra Field",
+}
+var tooFewFieldsStructLiteral = flag.Flag{ // want "unkeyed fields"
+	"Name",
+	"Usage",
+	nil, // Value
+}
 
 var delta [3]rune
 
@@ -100,6 +113,10 @@
 	&scanner.Error{token.Position{}, "foobar"}, // want "unkeyed fields"
 }
 
+// sync.Mutex has unexported fields. We expect a diagnostic but no
+// suggested fix.
+var mu = sync.Mutex{0, 0} // want "unkeyed fields"
+
 // Check whitelisted structs: if vet is run with --compositewhitelist=false,
 // this line triggers an error.
 var whitelistedPoint = image.Point{1, 2}
diff --git a/go/analysis/passes/composite/testdata/src/a/a.go.golden b/go/analysis/passes/composite/testdata/src/a/a.go.golden
new file mode 100644
index 0000000..fe73a2e
--- /dev/null
+++ b/go/analysis/passes/composite/testdata/src/a/a.go.golden
@@ -0,0 +1,144 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains the test for untagged struct literals.
+
+package a
+
+import (
+	"flag"
+	"go/scanner"
+	"go/token"
+	"image"
+	"sync"
+	"unicode"
+)
+
+var Okay1 = []string{
+	"Name",
+	"Usage",
+	"DefValue",
+}
+
+var Okay2 = map[string]bool{
+	"Name":     true,
+	"Usage":    true,
+	"DefValue": true,
+}
+
+var Okay3 = struct {
+	X string
+	Y string
+	Z string
+}{
+	"Name",
+	"Usage",
+	"DefValue",
+}
+
+var Okay4 = []struct {
+	A int
+	B int
+}{
+	{1, 2},
+	{3, 4},
+}
+
+type MyStruct struct {
+	X string
+	Y string
+	Z string
+}
+
+var Okay5 = &MyStruct{
+	"Name",
+	"Usage",
+	"DefValue",
+}
+
+var Okay6 = []MyStruct{
+	{"foo", "bar", "baz"},
+	{"aa", "bb", "cc"},
+}
+
+var Okay7 = []*MyStruct{
+	{"foo", "bar", "baz"},
+	{"aa", "bb", "cc"},
+}
+
+// Testing is awkward because we need to reference things from a separate package
+// to trigger the warnings.
+
+var goodStructLiteral = flag.Flag{
+	Name:  "Name",
+	Usage: "Usage",
+}
+var badStructLiteral = flag.Flag{ // want "unkeyed fields"
+	Name:     "Name",
+	Usage:    "Usage",
+	Value:    nil, // Value
+	DefValue: "DefValue",
+}
+var tooManyFieldsStructLiteral = flag.Flag{ // want "unkeyed fields"
+	"Name",
+	"Usage",
+	nil, // Value
+	"DefValue",
+	"Extra Field",
+}
+var tooFewFieldsStructLiteral = flag.Flag{ // want "unkeyed fields"
+	"Name",
+	"Usage",
+	nil, // Value
+}
+
+var delta [3]rune
+
+// SpecialCase is a named slice of CaseRange to test issue 9171.
+var goodNamedSliceLiteral = unicode.SpecialCase{
+	{Lo: 1, Hi: 2, Delta: delta},
+	unicode.CaseRange{Lo: 1, Hi: 2, Delta: delta},
+}
+var badNamedSliceLiteral = unicode.SpecialCase{
+	{Lo: 1, Hi: 2, Delta: delta},                  // want "unkeyed fields"
+	unicode.CaseRange{Lo: 1, Hi: 2, Delta: delta}, // want "unkeyed fields"
+}
+
+// ErrorList is a named slice, so no warnings should be emitted.
+var goodScannerErrorList = scanner.ErrorList{
+	&scanner.Error{Msg: "foobar"},
+}
+var badScannerErrorList = scanner.ErrorList{
+	&scanner.Error{Pos: token.Position{}, Msg: "foobar"}, // want "unkeyed fields"
+}
+
+// sync.Mutex has unexported fields. We expect a diagnostic but no
+// suggested fix.
+var mu = sync.Mutex{0, 0} // want "unkeyed fields"
+
+// Check whitelisted structs: if vet is run with --compositewhitelist=false,
+// this line triggers an error.
+var whitelistedPoint = image.Point{1, 2}
+
+// Do not check type from unknown package.
+// See issue 15408.
+var unknownPkgVar = unicode.NoSuchType{"foo", "bar"}
+
+// A named pointer slice of CaseRange to test issue 23539. In
+// particular, we're interested in how some slice elements omit their
+// type.
+var goodNamedPointerSliceLiteral = []*unicode.CaseRange{
+	{Lo: 1, Hi: 2},
+	&unicode.CaseRange{Lo: 1, Hi: 2},
+}
+var badNamedPointerSliceLiteral = []*unicode.CaseRange{
+	{Lo: 1, Hi: 2, Delta: delta},                   // want "unkeyed fields"
+	&unicode.CaseRange{Lo: 1, Hi: 2, Delta: delta}, // want "unkeyed fields"
+}
+
+// unicode.Range16 is whitelisted, so there'll be no vet error
+var range16 = unicode.Range16{0xfdd0, 0xfdef, 1}
+
+// unicode.Range32 is whitelisted, so there'll be no vet error
+var range32 = unicode.Range32{0x1fffe, 0x1ffff, 1}
diff --git a/go/analysis/passes/composite/testdata/src/a/a_fuzz_test.go.golden b/go/analysis/passes/composite/testdata/src/a/a_fuzz_test.go.golden
new file mode 100644
index 0000000..20b652e
--- /dev/null
+++ b/go/analysis/passes/composite/testdata/src/a/a_fuzz_test.go.golden
@@ -0,0 +1,16 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build go1.18
+// +build go1.18
+
+package a
+
+import "testing"
+
+var fuzzTargets = []testing.InternalFuzzTarget{
+	{"Fuzz", Fuzz},
+}
+
+func Fuzz(f *testing.F) {}
diff --git a/go/analysis/passes/composite/testdata/src/typeparams/typeparams.go b/go/analysis/passes/composite/testdata/src/typeparams/typeparams.go
index dd5d57e..f9a5e1f 100644
--- a/go/analysis/passes/composite/testdata/src/typeparams/typeparams.go
+++ b/go/analysis/passes/composite/testdata/src/typeparams/typeparams.go
@@ -6,7 +6,7 @@
 
 import "typeparams/lib"
 
-type localStruct struct { F int }
+type localStruct struct{ F int }
 
 func F[
 	T1 ~struct{ f int },
@@ -20,8 +20,8 @@
 	_ = T1{2}
 	_ = T2a{2}
 	_ = T2b{2} // want "unkeyed fields"
-	_ = T3{1,2}
-	_ = T4{1,2}
-	_ = T5{1:2}
-	_ = T6{1:2}
+	_ = T3{1, 2}
+	_ = T4{1, 2}
+	_ = T5{1: 2}
+	_ = T6{1: 2}
 }
diff --git a/go/analysis/passes/composite/testdata/src/typeparams/typeparams.go.golden b/go/analysis/passes/composite/testdata/src/typeparams/typeparams.go.golden
new file mode 100644
index 0000000..66cd915
--- /dev/null
+++ b/go/analysis/passes/composite/testdata/src/typeparams/typeparams.go.golden
@@ -0,0 +1,27 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package typeparams
+
+import "typeparams/lib"
+
+type localStruct struct{ F int }
+
+func F[
+	T1 ~struct{ f int },
+	T2a localStruct,
+	T2b lib.Struct,
+	T3 ~[]int,
+	T4 lib.Slice,
+	T5 ~map[int]int,
+	T6 lib.Map,
+]() {
+	_ = T1{2}
+	_ = T2a{2}
+	_ = T2b{F: 2} // want "unkeyed fields"
+	_ = T3{1, 2}
+	_ = T4{1, 2}
+	_ = T5{1: 2}
+	_ = T6{1: 2}
+}
diff --git a/go/analysis/passes/fieldalignment/fieldalignment.go b/go/analysis/passes/fieldalignment/fieldalignment.go
index 78afe94..aff6630 100644
--- a/go/analysis/passes/fieldalignment/fieldalignment.go
+++ b/go/analysis/passes/fieldalignment/fieldalignment.go
@@ -23,7 +23,7 @@
 const Doc = `find structs that would use less memory if their fields were sorted
 
 This analyzer find structs that can be rearranged to use less memory, and provides
-a suggested edit with the optimal order.
+a suggested edit with the most compact order.
 
 Note that there are two different diagnostics reported. One checks struct size,
 and the other reports "pointer bytes" used. Pointer bytes is how many bytes of the
@@ -41,6 +41,11 @@
 	struct { string; uint32 }
 
 has 8 because it can stop immediately after the string pointer.
+
+Be aware that the most compact order is not always the most efficient.
+In rare cases it may cause two variables each updated by its own goroutine
+to occupy the same CPU cache line, inducing a form of memory contention
+known as "false sharing" that slows down both goroutines.
 `
 
 var Analyzer = &analysis.Analyzer{
diff --git a/go/gcexportdata/example_test.go b/go/gcexportdata/example_test.go
index e81e705..cdd68f4 100644
--- a/go/gcexportdata/example_test.go
+++ b/go/gcexportdata/example_test.go
@@ -30,7 +30,6 @@
 		log.Fatalf("can't find export data for fmt")
 	}
 	fmt.Printf("Package path:       %s\n", path)
-	fmt.Printf("Export data:        %s\n", filepath.Base(filename))
 
 	// Open and read the file.
 	f, err := os.Open(filename)
@@ -80,7 +79,6 @@
 	// Output:
 	//
 	// Package path:       fmt
-	// Export data:        fmt.a
 	// Package members:    Println found
 	// Println type:       func(a ...any) (n int, err error)
 	// Println location:   $GOROOT/src/fmt/print.go:123:1
diff --git a/go/gcexportdata/gcexportdata.go b/go/gcexportdata/gcexportdata.go
index d50826d..ddc276c 100644
--- a/go/gcexportdata/gcexportdata.go
+++ b/go/gcexportdata/gcexportdata.go
@@ -22,26 +22,42 @@
 import (
 	"bufio"
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"go/token"
 	"go/types"
 	"io"
 	"io/ioutil"
+	"os/exec"
 
 	"golang.org/x/tools/go/internal/gcimporter"
 )
 
 // Find returns the name of an object (.o) or archive (.a) file
 // containing type information for the specified import path,
-// using the workspace layout conventions of go/build.
+// using the go command.
 // If no file was found, an empty filename is returned.
 //
 // A relative srcDir is interpreted relative to the current working directory.
 //
 // Find also returns the package's resolved (canonical) import path,
 // reflecting the effects of srcDir and vendoring on importPath.
+//
+// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
+// which is more efficient.
 func Find(importPath, srcDir string) (filename, path string) {
-	return gcimporter.FindPkg(importPath, srcDir)
+	cmd := exec.Command("go", "list", "-json", "-export", "--", importPath)
+	cmd.Dir = srcDir
+	out, err := cmd.CombinedOutput()
+	if err != nil {
+		return "", ""
+	}
+	var data struct {
+		ImportPath string
+		Export     string
+	}
+	json.Unmarshal(out, &data)
+	return data.Export, data.ImportPath
 }
 
 // NewReader returns a reader for the export data section of an object
diff --git a/go/gcexportdata/importer.go b/go/gcexportdata/importer.go
index fe6ed93..37a7247 100644
--- a/go/gcexportdata/importer.go
+++ b/go/gcexportdata/importer.go
@@ -22,6 +22,9 @@
 // version-skew problems described in the documentation of this package,
 // or to control the FileSet or access the imports map populated during
 // package loading.
+//
+// Deprecated: Use the higher-level API in golang.org/x/tools/go/packages,
+// which is more efficient.
 func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
 	return importer{fset, imports}
 }
diff --git a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md
index f5c83d5..fd65c3a 100644
--- a/gopls/doc/analyzers.md
+++ b/gopls/doc/analyzers.md
@@ -131,7 +131,7 @@
 find structs that would use less memory if their fields were sorted
 
 This analyzer find structs that can be rearranged to use less memory, and provides
-a suggested edit with the optimal order.
+a suggested edit with the most compact order.
 
 Note that there are two different diagnostics reported. One checks struct size,
 and the other reports "pointer bytes" used. Pointer bytes is how many bytes of the
@@ -150,6 +150,11 @@
 
 has 8 because it can stop immediately after the string pointer.
 
+Be aware that the most compact order is not always the most efficient.
+In rare cases it may cause two variables each updated by its own goroutine
+to occupy the same CPU cache line, inducing a form of memory contention
+known as "false sharing" that slows down both goroutines.
+
 
 **Disabled by default. Enable it by setting `"analyses": {"fieldalignment": true}`.**
 
diff --git a/gopls/go.mod b/gopls/go.mod
index 4f48fd1..bf50da8 100644
--- a/gopls/go.mod
+++ b/gopls/go.mod
@@ -12,7 +12,7 @@
 	golang.org/x/sys v0.0.0-20220209214540-3681064d5158
 	golang.org/x/tools v0.1.12-0.20220627200741-67ab769fbfbf
 	golang.org/x/vuln v0.0.0-20220613164644-4eb5ba49563c
-	honnef.co/go/tools v0.3.0
+	honnef.co/go/tools v0.3.2
 	mvdan.cc/gofumpt v0.3.0
 	mvdan.cc/xurls/v2 v2.4.0
 )
@@ -24,3 +24,5 @@
 	golang.org/x/text v0.3.7 // indirect
 	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 )
+
+replace golang.org/x/tools => ../
diff --git a/gopls/go.sum b/gopls/go.sum
index a401bea..b4ddfb0 100644
--- a/gopls/go.sum
+++ b/gopls/go.sum
@@ -105,6 +105,8 @@
 honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
 honnef.co/go/tools v0.3.0 h1:2LdYUZ7CIxnYgskbUZfY7FPggmqnh6shBqfWa8Tn3XU=
 honnef.co/go/tools v0.3.0/go.mod h1:vlRD9XErLMGT+mDuofSr0mMMquscM/1nQqtRSsh6m70=
+honnef.co/go/tools v0.3.2 h1:ytYb4rOqyp1TSa2EPvNVwtPQJctSELKaMyLfqNP4+34=
+honnef.co/go/tools v0.3.2/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw=
 mvdan.cc/gofumpt v0.3.0 h1:kTojdZo9AcEYbQYhGuLf/zszYthRdhDNDUi2JKTxas4=
 mvdan.cc/gofumpt v0.3.0/go.mod h1:0+VyGZWleeIj5oostkOex+nDBA0eyavuDnDusAJ8ylo=
 mvdan.cc/unparam v0.0.0-20211214103731-d0ef000c54e5 h1:Jh3LAeMt1eGpxomyu3jVkmVZWW2MxZ1qIIV2TZ/nRio=
diff --git a/gopls/internal/regtest/inlayHints/inlayHints_test.go b/gopls/internal/regtest/inlayHints/inlayHints_test.go
new file mode 100644
index 0000000..67931fb
--- /dev/null
+++ b/gopls/internal/regtest/inlayHints/inlayHints_test.go
@@ -0,0 +1,72 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+package inlayHint
+
+import (
+	"testing"
+
+	"golang.org/x/tools/gopls/internal/hooks"
+	"golang.org/x/tools/internal/lsp/bug"
+	. "golang.org/x/tools/internal/lsp/regtest"
+	"golang.org/x/tools/internal/lsp/source"
+	"golang.org/x/tools/internal/testenv"
+)
+
+func TestMain(m *testing.M) {
+	bug.PanicOnBugs = true
+	Main(m, hooks.Options)
+}
+func TestEnablingInlayHints(t *testing.T) {
+	testenv.NeedsGo1Point(t, 14) // Test fails on 1.13.
+	const workspace = `
+-- go.mod --
+module inlayHint.test
+go 1.12
+-- lib.go --
+package lib
+type Number int
+const (
+	Zero Number = iota
+	One
+	Two
+)
+`
+	tests := []struct {
+		label         string
+		enabled       map[string]bool
+		wantInlayHint bool
+	}{
+		{
+			label:         "default",
+			wantInlayHint: false,
+		},
+		{
+			label:         "enable const",
+			enabled:       map[string]bool{source.ConstantValues: true},
+			wantInlayHint: true,
+		},
+		{
+			label:         "enable parameter names",
+			enabled:       map[string]bool{source.ParameterNames: true},
+			wantInlayHint: false,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.label, func(t *testing.T) {
+			WithOptions(
+				EditorConfig{
+					Settings: map[string]interface{}{
+						"hints": test.enabled,
+					},
+				},
+			).Run(t, workspace, func(t *testing.T, env *Env) {
+				env.OpenFile("lib.go")
+				lens := env.InlayHints("lib.go")
+				if gotInlayHint := len(lens) > 0; gotInlayHint != test.wantInlayHint {
+					t.Errorf("got inlayHint: %t, want %t", gotInlayHint, test.wantInlayHint)
+				}
+			})
+		})
+	}
+}
diff --git a/internal/lsp/cache/graph.go b/internal/lsp/cache/graph.go
index dc7d4fa..88c9f14 100644
--- a/internal/lsp/cache/graph.go
+++ b/internal/lsp/cache/graph.go
@@ -32,6 +32,10 @@
 // Clone creates a new metadataGraph, applying the given updates to the
 // receiver.
 func (g *metadataGraph) Clone(updates map[PackageID]*KnownMetadata) *metadataGraph {
+	if len(updates) == 0 {
+		// Optimization: since the graph is immutable, we can return the receiver.
+		return g
+	}
 	result := &metadataGraph{metadata: make(map[PackageID]*KnownMetadata, len(g.metadata))}
 	// Copy metadata.
 	for id, m := range g.metadata {
@@ -154,18 +158,3 @@
 	visitAll(ids)
 	return seen
 }
-
-func collectReverseTransitiveClosure(g *metadataGraph, includeInvalid bool, ids []PackageID, seen map[PackageID]struct{}) {
-	for _, id := range ids {
-		if _, ok := seen[id]; ok {
-			continue
-		}
-		m := g.metadata[id]
-		// Only use invalid metadata if we support it.
-		if m == nil || !(m.Valid || includeInvalid) {
-			continue
-		}
-		seen[id] = struct{}{}
-		collectReverseTransitiveClosure(g, includeInvalid, g.importedBy[id], seen)
-	}
-}
diff --git a/internal/lsp/cache/load.go b/internal/lsp/cache/load.go
index db9a06d..da0b246 100644
--- a/internal/lsp/cache/load.go
+++ b/internal/lsp/cache/load.go
@@ -15,6 +15,7 @@
 	"path/filepath"
 	"sort"
 	"strings"
+	"sync/atomic"
 	"time"
 
 	"golang.org/x/tools/go/packages"
@@ -28,12 +29,17 @@
 	"golang.org/x/tools/internal/span"
 )
 
+var loadID uint64 // atomic identifier for loads
+
 // load calls packages.Load for the given scopes, updating package metadata,
 // import graph, and mapped files with the result.
 //
 // The resulting error may wrap the moduleErrorMap error type, representing
 // errors associated with specific modules.
 func (s *snapshot) load(ctx context.Context, allowNetwork bool, scopes ...interface{}) (err error) {
+	id := atomic.AddUint64(&loadID, 1)
+	eventName := fmt.Sprintf("go/packages.Load #%d", id) // unique name for logging
+
 	var query []string
 	var containsDir bool // for logging
 
@@ -138,9 +144,9 @@
 		return ctx.Err()
 	}
 	if err != nil {
-		event.Error(ctx, "go/packages.Load", err, tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
+		event.Error(ctx, eventName, err, tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
 	} else {
-		event.Log(ctx, "go/packages.Load", tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
+		event.Log(ctx, eventName, tag.Snapshot.Of(s.ID()), tag.Directory.Of(cfg.Dir), tag.Query.Of(query), tag.PackageCount.Of(len(pkgs)))
 	}
 	if len(pkgs) == 0 {
 		if err == nil {
@@ -168,7 +174,7 @@
 		}
 
 		if !containsDir || s.view.Options().VerboseOutput {
-			event.Log(ctx, "go/packages.Load",
+			event.Log(ctx, eventName,
 				tag.Snapshot.Of(s.ID()),
 				tag.Package.Of(pkg.ID),
 				tag.Files.Of(pkg.CompiledGoFiles))
@@ -209,6 +215,8 @@
 		loadedIDs = append(loadedIDs, id)
 	}
 
+	event.Log(ctx, fmt.Sprintf("%s: updating metadata for %d packages", eventName, len(updates)))
+
 	s.mu.Lock()
 
 	// invalidate the reverse transitive closure of packages that have changed.
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index 8194750..e94ad7b 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -1010,6 +1010,10 @@
 		result   = make(map[span.URI][]source.Symbol)
 	)
 	s.files.Range(func(uri span.URI, f source.VersionedFileHandle) {
+		if s.View().FileKind(f) != source.Go {
+			return // workspace symbols currently supports only Go files.
+		}
+
 		// TODO(adonovan): upgrade errgroup and use group.SetLimit(nprocs).
 		iolimit <- struct{}{} // acquire token
 		group.Go(func() error {
diff --git a/internal/lsp/fake/editor.go b/internal/lsp/fake/editor.go
index 06b90bb..0fc99a0 100644
--- a/internal/lsp/fake/editor.go
+++ b/internal/lsp/fake/editor.go
@@ -1114,6 +1114,27 @@
 	return ans, err
 }
 
+// CodeLens executes a codelens request on the server.
+func (e *Editor) InlayHint(ctx context.Context, path string) ([]protocol.InlayHint, error) {
+	if e.Server == nil {
+		return nil, nil
+	}
+	e.mu.Lock()
+	_, ok := e.buffers[path]
+	e.mu.Unlock()
+	if !ok {
+		return nil, fmt.Errorf("buffer %q is not open", path)
+	}
+	params := &protocol.InlayHintParams{
+		TextDocument: e.textDocumentIdentifier(path),
+	}
+	hints, err := e.Server.InlayHint(ctx, params)
+	if err != nil {
+		return nil, err
+	}
+	return hints, nil
+}
+
 // References executes a reference request on the server.
 func (e *Editor) References(ctx context.Context, path string, pos Pos) ([]protocol.Location, error) {
 	if e.Server == nil {
diff --git a/internal/lsp/protocol/tsprotocol.go b/internal/lsp/protocol/tsprotocol.go
index 5dd3d09..3a284bf 100644
--- a/internal/lsp/protocol/tsprotocol.go
+++ b/internal/lsp/protocol/tsprotocol.go
@@ -2745,6 +2745,14 @@
 	 */
 	Kind InlayHintKind `json:"kind,omitempty"`
 	/**
+	 * Optional text edits that are performed when accepting this inlay hint.
+	 *
+	 * *Note* that edits are expected to change the document so that the inlay
+	 * hint (or its nearest variant) is now part of the document and the inlay
+	 * hint itself is now obsolete.
+	 */
+	TextEdits []TextEdit `json:"textEdits,omitempty"`
+	/**
 	 * The tooltip text when you hover over this item.
 	 */
 	Tooltip string/*string | MarkupContent*/ `json:"tooltip,omitempty"`
diff --git a/internal/lsp/regtest/wrappers.go b/internal/lsp/regtest/wrappers.go
index 9031e71..96e2de9 100644
--- a/internal/lsp/regtest/wrappers.go
+++ b/internal/lsp/regtest/wrappers.go
@@ -358,6 +358,17 @@
 	}
 }
 
+// InlayHints calls textDocument/inlayHints for the given path, calling t.Fatal on
+// any error.
+func (e *Env) InlayHints(path string) []protocol.InlayHint {
+	e.T.Helper()
+	hints, err := e.Editor.InlayHint(e.Ctx, path)
+	if err != nil {
+		e.T.Fatal(err)
+	}
+	return hints
+}
+
 // WorkspaceSymbol calls workspace/symbol
 func (e *Env) WorkspaceSymbol(sym string) []protocol.SymbolInformation {
 	e.T.Helper()
diff --git a/internal/lsp/source/api_json.go b/internal/lsp/source/api_json.go
index ef683f3..4e2183c 100755
--- a/internal/lsp/source/api_json.go
+++ b/internal/lsp/source/api_json.go
@@ -280,7 +280,7 @@
 						},
 						{
 							Name:    "\"fieldalignment\"",
-							Doc:     "find structs that would use less memory if their fields were sorted\n\nThis analyzer find structs that can be rearranged to use less memory, and provides\na suggested edit with the optimal order.\n\nNote that there are two different diagnostics reported. One checks struct size,\nand the other reports \"pointer bytes\" used. Pointer bytes is how many bytes of the\nobject that the garbage collector has to potentially scan for pointers, for example:\n\n\tstruct { uint32; string }\n\nhave 16 pointer bytes because the garbage collector has to scan up through the string's\ninner pointer.\n\n\tstruct { string; *uint32 }\n\nhas 24 pointer bytes because it has to scan further through the *uint32.\n\n\tstruct { string; uint32 }\n\nhas 8 because it can stop immediately after the string pointer.\n",
+							Doc:     "find structs that would use less memory if their fields were sorted\n\nThis analyzer find structs that can be rearranged to use less memory, and provides\na suggested edit with the most compact order.\n\nNote that there are two different diagnostics reported. One checks struct size,\nand the other reports \"pointer bytes\" used. Pointer bytes is how many bytes of the\nobject that the garbage collector has to potentially scan for pointers, for example:\n\n\tstruct { uint32; string }\n\nhave 16 pointer bytes because the garbage collector has to scan up through the string's\ninner pointer.\n\n\tstruct { string; *uint32 }\n\nhas 24 pointer bytes because it has to scan further through the *uint32.\n\n\tstruct { string; uint32 }\n\nhas 8 because it can stop immediately after the string pointer.\n\nBe aware that the most compact order is not always the most efficient.\nIn rare cases it may cause two variables each updated by its own goroutine\nto occupy the same CPU cache line, inducing a form of memory contention\nknown as \"false sharing\" that slows down both goroutines.\n",
 							Default: "false",
 						},
 						{
@@ -866,7 +866,7 @@
 		},
 		{
 			Name: "fieldalignment",
-			Doc:  "find structs that would use less memory if their fields were sorted\n\nThis analyzer find structs that can be rearranged to use less memory, and provides\na suggested edit with the optimal order.\n\nNote that there are two different diagnostics reported. One checks struct size,\nand the other reports \"pointer bytes\" used. Pointer bytes is how many bytes of the\nobject that the garbage collector has to potentially scan for pointers, for example:\n\n\tstruct { uint32; string }\n\nhave 16 pointer bytes because the garbage collector has to scan up through the string's\ninner pointer.\n\n\tstruct { string; *uint32 }\n\nhas 24 pointer bytes because it has to scan further through the *uint32.\n\n\tstruct { string; uint32 }\n\nhas 8 because it can stop immediately after the string pointer.\n",
+			Doc:  "find structs that would use less memory if their fields were sorted\n\nThis analyzer find structs that can be rearranged to use less memory, and provides\na suggested edit with the most compact order.\n\nNote that there are two different diagnostics reported. One checks struct size,\nand the other reports \"pointer bytes\" used. Pointer bytes is how many bytes of the\nobject that the garbage collector has to potentially scan for pointers, for example:\n\n\tstruct { uint32; string }\n\nhave 16 pointer bytes because the garbage collector has to scan up through the string's\ninner pointer.\n\n\tstruct { string; *uint32 }\n\nhas 24 pointer bytes because it has to scan further through the *uint32.\n\n\tstruct { string; uint32 }\n\nhas 8 because it can stop immediately after the string pointer.\n\nBe aware that the most compact order is not always the most efficient.\nIn rare cases it may cause two variables each updated by its own goroutine\nto occupy the same CPU cache line, inducing a form of memory contention\nknown as \"false sharing\" that slows down both goroutines.\n",
 		},
 		{
 			Name:    "httpresponse",
diff --git a/internal/lsp/source/completion/completion.go b/internal/lsp/source/completion/completion.go
index bb1c68d..0c1ff3f 100644
--- a/internal/lsp/source/completion/completion.go
+++ b/internal/lsp/source/completion/completion.go
@@ -441,7 +441,7 @@
 		items, surrounding, innerErr := packageClauseCompletions(ctx, snapshot, fh, protoPos)
 		if innerErr != nil {
 			// return the error for GetParsedFile since it's more relevant in this situation.
-			return nil, nil, fmt.Errorf("getting file for Completion: %w (package completions: %v)", err, innerErr)
+			return nil, nil, fmt.Errorf("getting file %s for Completion: %w (package completions: %v)", fh.URI(), err, innerErr)
 		}
 		return items, surrounding, nil
 	}
diff --git a/internal/lsp/source/inlay_hint.go b/internal/lsp/source/inlay_hint.go
index 0c14728..967752b 100644
--- a/internal/lsp/source/inlay_hint.go
+++ b/internal/lsp/source/inlay_hint.go
@@ -104,7 +104,7 @@
 	},
 }
 
-func InlayHint(ctx context.Context, snapshot Snapshot, fh FileHandle, _ protocol.Range) ([]protocol.InlayHint, error) {
+func InlayHint(ctx context.Context, snapshot Snapshot, fh FileHandle, pRng protocol.Range) ([]protocol.InlayHint, error) {
 	ctx, done := event.Start(ctx, "source.InlayHint")
 	defer done()
 
@@ -132,8 +132,23 @@
 	info := pkg.GetTypesInfo()
 	q := Qualifier(pgf.File, pkg.GetTypes(), info)
 
+	// Set the range to the full file if the range is not valid.
+	start, end := pgf.File.Pos(), pgf.File.End()
+	if pRng.Start.Line < pRng.End.Line || pRng.Start.Character < pRng.End.Character {
+		// Adjust start and end for the specified range.
+		rng, err := pgf.Mapper.RangeToSpanRange(pRng)
+		if err != nil {
+			return nil, err
+		}
+		start, end = rng.Start, rng.End
+	}
+
 	var hints []protocol.InlayHint
 	ast.Inspect(pgf.File, func(node ast.Node) bool {
+		// If not in range, we can stop looking.
+		if node == nil || node.End() < start || node.Pos() > end {
+			return false
+		}
 		for _, fn := range enabledHints {
 			hints = append(hints, fn(node, tmap, info, &q)...)
 		}
@@ -329,7 +344,7 @@
 	}
 
 	var hints []protocol.InlayHint
-
+	var allEdits []protocol.TextEdit
 	for i, v := range compLit.Elts {
 		if _, ok := v.(*ast.KeyValueExpr); !ok {
 			start, ok := tmap.Position(v.Pos())
@@ -345,8 +360,17 @@
 				Kind:         protocol.Parameter,
 				PaddingRight: true,
 			})
+			allEdits = append(allEdits, protocol.TextEdit{
+				Range:   protocol.Range{Start: start, End: start},
+				NewText: strct.Field(i).Name() + ": ",
+			})
 		}
 	}
+	// It is not allowed to have a mix of keyed and unkeyed fields, so
+	// have the text edits add keys to all fields.
+	for i := range hints {
+		hints[i].TextEdits = allEdits
+	}
 	return hints
 }