all: merge master (e8cdaf4) into gopls-release-branch.0.13
For golang/go#61583
Merge List:
+ 2023-07-26 e8cdaf46d gopls/internal/lsp/cache: fast-path for type-checking active packages
+ 2023-07-26 da5abd36a gopls/internal/lsp/cache: fix use of time.NewTimer instead of NewTicker
+ 2023-07-26 b38978515 gopls: fix tests that depend on log sequencing
+ 2023-07-26 2ffc4dc5c all: fix some typos
Change-Id: I7a3de6330a6b6d0dca51de6094603cde2cec1b6a
diff --git a/cmd/compilebench/main.go b/cmd/compilebench/main.go
index 754acdc..6900b25 100644
--- a/cmd/compilebench/main.go
+++ b/cmd/compilebench/main.go
@@ -567,10 +567,10 @@
return nil
}
-// genSymAbisFile runs the assembler on the target packge asm files
+// genSymAbisFile runs the assembler on the target package asm files
// with "-gensymabis" to produce a symabis file that will feed into
// the Go source compilation. This is fairly hacky in that if the
-// asm invocation convenion changes it will need to be updated
+// asm invocation convention changes it will need to be updated
// (hopefully that will not be needed too frequently).
func genSymAbisFile(pkg *Pkg, symAbisFile, incdir string) error {
args := []string{"-gensymabis", "-o", symAbisFile,
diff --git a/go/analysis/passes/buildssa/testdata/src/c/c.go b/go/analysis/passes/buildssa/testdata/src/c/c.go
index 387a3b0..d6ce8b8 100644
--- a/go/analysis/passes/buildssa/testdata/src/c/c.go
+++ b/go/analysis/passes/buildssa/testdata/src/c/c.go
@@ -19,6 +19,6 @@
m := b.G.Load()
f := b.Load(&b.G)
if f != m {
- panic("loads of b.G are expected to be indentical")
+ panic("loads of b.G are expected to be identical")
}
}
diff --git a/go/analysis/passes/errorsas/testdata/src/a/a.go b/go/analysis/passes/errorsas/testdata/src/a/a.go
index 7a9ae89..222b279 100644
--- a/go/analysis/passes/errorsas/testdata/src/a/a.go
+++ b/go/analysis/passes/errorsas/testdata/src/a/a.go
@@ -29,7 +29,7 @@
ei interface{}
)
errors.As(nil, &e) // want `second argument to errors.As should not be \*error`
- errors.As(nil, &m) // *T where T implemements error
+ errors.As(nil, &m) // *T where T implements error
errors.As(nil, &f) // *interface
errors.As(nil, perr()) // want `second argument to errors.As should not be \*error`
errors.As(nil, ei) // empty interface
diff --git a/go/analysis/passes/errorsas/testdata/src/typeparams/typeparams.go b/go/analysis/passes/errorsas/testdata/src/typeparams/typeparams.go
index 4f7ae84..16a974c 100644
--- a/go/analysis/passes/errorsas/testdata/src/typeparams/typeparams.go
+++ b/go/analysis/passes/errorsas/testdata/src/typeparams/typeparams.go
@@ -26,7 +26,7 @@
tw twice[myError[int]]
)
errors.As(nil, &e)
- errors.As(nil, &m) // *T where T implemements error
+ errors.As(nil, &m) // *T where T implements error
errors.As(nil, &tw.t) // *T where T implements error
errors.As(nil, perr[error]()) // want `second argument to errors.As should not be \*error`
diff --git a/go/ssa/builder_go120_test.go b/go/ssa/builder_go120_test.go
index acdd182..2472a9d 100644
--- a/go/ssa/builder_go120_test.go
+++ b/go/ssa/builder_go120_test.go
@@ -36,7 +36,7 @@
// as []rune, pointers to rune arrays, rune arrays, or strings.
//
// Comments listed given the current emitted instructions [approximately].
- // If multiple conversions are needed, these are seperated by |.
+ // If multiple conversions are needed, these are separated by |.
// rune was selected as it leads to string casts (byte is similar).
// The length 2 is not significant.
// Multiple array lengths may occur in a cast in practice (including 0).
diff --git a/go/ssa/subst.go b/go/ssa/subst.go
index 89c41a8..23d19ae 100644
--- a/go/ssa/subst.go
+++ b/go/ssa/subst.go
@@ -388,7 +388,7 @@
// no type params to substitute
// (2)generic method and recv needs to be substituted.
- // Recievers can be either:
+ // Receivers can be either:
// named
// pointer to named
// interface
diff --git a/go/ssa/util.go b/go/ssa/util.go
index 7735dd8..68cc971 100644
--- a/go/ssa/util.go
+++ b/go/ssa/util.go
@@ -304,7 +304,7 @@
return T
}
-// A type for representating an canonized list of types.
+// A type for representing a canonized list of types.
type typeList []types.Type
func (l *typeList) identical(ts []types.Type) bool {
diff --git a/gopls/internal/lsp/analysis/stubmethods/stubmethods.go b/gopls/internal/lsp/analysis/stubmethods/stubmethods.go
index e24447d..930cb99 100644
--- a/gopls/internal/lsp/analysis/stubmethods/stubmethods.go
+++ b/gopls/internal/lsp/analysis/stubmethods/stubmethods.go
@@ -115,7 +115,7 @@
//
// TODO(adonovan): this function (and its following 5 helpers) tries
// to deduce a pair of (concrete, interface) types that are related by
-// an assignment, either explictly or through a return statement or
+// an assignment, either explicitly or through a return statement or
// function call. This is essentially what the refactor/satisfy does,
// more generally. Refactor to share logic, after auditing 'satisfy'
// for safety on ill-typed code.
diff --git a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.go
index 5d36f5e..6852c74 100644
--- a/gopls/internal/lsp/cache/check.go
+++ b/gopls/internal/lsp/cache/check.go
@@ -93,10 +93,30 @@
// the type-checking operation.
func (s *snapshot) TypeCheck(ctx context.Context, ids ...PackageID) ([]source.Package, error) {
pkgs := make([]source.Package, len(ids))
- post := func(i int, pkg *Package) {
- pkgs[i] = pkg
+
+ var (
+ needIDs []PackageID // ids to type-check
+ indexes []int // original index of requested ids
+ )
+
+ // Check for existing active packages, as any package will do.
+ //
+ // This is also done inside forEachPackage, but doing it here avoids
+ // unnecessary set up for type checking (e.g. assembling the package handle
+ // graph).
+ for i, id := range ids {
+ if pkg := s.getActivePackage(id); pkg != nil {
+ pkgs[i] = pkg
+ } else {
+ needIDs = append(needIDs, id)
+ indexes = append(indexes, i)
+ }
}
- return pkgs, s.forEachPackage(ctx, ids, nil, post)
+
+ post := func(i int, pkg *Package) {
+ pkgs[indexes[i]] = pkg
+ }
+ return pkgs, s.forEachPackage(ctx, needIDs, nil, post)
}
// getImportGraph returns a shared import graph use for this snapshot, or nil.
diff --git a/gopls/internal/lsp/cache/parse.go b/gopls/internal/lsp/cache/parse.go
index 7afa5d9..42353df 100644
--- a/gopls/internal/lsp/cache/parse.go
+++ b/gopls/internal/lsp/cache/parse.go
@@ -398,7 +398,7 @@
//
// }
//
-// The resulting bool reports whether any fixing occured.
+// The resulting bool reports whether any fixing occurred.
func fixEmptySwitch(body *ast.BlockStmt, tok *token.File, src []byte) bool {
// We only care about empty switch statements.
if len(body.List) > 0 || !body.Rbrace.IsValid() {
@@ -475,7 +475,7 @@
//
// TODO(rfindley): should this constitute an ast 'fix'?
//
-// The resulting bool reports whether any fixing occured.
+// The resulting bool reports whether any fixing occurred.
func fixPhantomSelector(sel *ast.SelectorExpr, tf *token.File, src []byte) bool {
if !isPhantomUnderscore(sel.Sel, tf, src) {
return false
@@ -525,7 +525,7 @@
// parser is looking for the conditional expression. However, "i := 0"
// are not valid expressions, so we get a BadExpr.
//
-// The resulting bool reports whether any fixing occured.
+// The resulting bool reports whether any fixing occurred.
func fixInitStmt(bad *ast.BadExpr, parent ast.Node, tok *token.File, src []byte) bool {
if !bad.Pos().IsValid() || !bad.End().IsValid() {
return false
diff --git a/gopls/internal/lsp/cache/parse_cache.go b/gopls/internal/lsp/cache/parse_cache.go
index b1febce..438cc62 100644
--- a/gopls/internal/lsp/cache/parse_cache.go
+++ b/gopls/internal/lsp/cache/parse_cache.go
@@ -254,7 +254,7 @@
func (c *parseCache) gc() {
const period = 10 * time.Second // gc period
- timer := time.NewTimer(period)
+ timer := time.NewTicker(period)
defer timer.Stop()
for {
diff --git a/gopls/internal/lsp/fake/workdir.go b/gopls/internal/lsp/fake/workdir.go
index c064b28..d5e8eb2 100644
--- a/gopls/internal/lsp/fake/workdir.go
+++ b/gopls/internal/lsp/fake/workdir.go
@@ -120,7 +120,7 @@
// fileID identifies a file version on disk.
type fileID struct {
mtime time.Time
- hash string // empty if mtime is old enough to be reliabe; otherwise a file digest
+ hash string // empty if mtime is old enough to be reliable; otherwise a file digest
}
func hashFile(data []byte) string {
@@ -363,7 +363,7 @@
return nil
}
- // Opt: avoid reading the file if mtime is sufficently old to be reliable.
+ // Opt: avoid reading the file if mtime is sufficiently old to be reliable.
//
// If mtime is recent, it may not sufficiently identify the file contents:
// a subsequent write could result in the same mtime. For these cases, we
diff --git a/gopls/internal/lsp/regtest/expectation.go b/gopls/internal/lsp/regtest/expectation.go
index ece9aaa..0136870 100644
--- a/gopls/internal/lsp/regtest/expectation.go
+++ b/gopls/internal/lsp/regtest/expectation.go
@@ -212,20 +212,6 @@
}
}
-// ReadLogs is an expectation that may be used with AfterChange or OnceMet
-// conditions to copy logs into the provided slice.
-func ReadLogs(into *[]*protocol.LogMessageParams) Expectation {
- check := func(s State) Verdict {
- *into = make([]*protocol.LogMessageParams, len(s.logs))
- copy(*into, s.logs)
- return Met
- }
- return Expectation{
- Check: check,
- Description: "read logs",
- }
-}
-
// NoOutstandingWork asserts that there is no work initiated using the LSP
// $/progress API that has not completed.
func NoOutstandingWork() Expectation {
@@ -521,6 +507,10 @@
// The count argument specifies the expected number of matching logs. If
// atLeast is set, this is a lower bound, otherwise there must be exactly count
// matching logs.
+//
+// Logs are asynchronous to other LSP messages, so this expectation should not
+// be used with combinators such as OnceMet or AfterChange that assert on
+// ordering with respect to other operations.
func LogMatching(typ protocol.MessageType, re string, count int, atLeast bool) Expectation {
rec, err := regexp.Compile(re)
if err != nil {
@@ -537,6 +527,11 @@
if found == count || (found >= count && atLeast) {
return Met
}
+ // If we require an exact count, and have received more than expected, the
+ // expectation can never be met.
+ if found > count && !atLeast {
+ return Unmeetable
+ }
return Unmet
}
desc := fmt.Sprintf("log message matching %q expected %v times", re, count)
diff --git a/gopls/internal/lsp/source/rename.go b/gopls/internal/lsp/source/rename.go
index 40810dd..02bdaed 100644
--- a/gopls/internal/lsp/source/rename.go
+++ b/gopls/internal/lsp/source/rename.go
@@ -1061,7 +1061,7 @@
}
// Find all identifiers in the package that define or use a
- // renamed object. We iterate over info as it is more efficent
+ // renamed object. We iterate over info as it is more efficient
// than calling ast.Inspect for each of r.pkg.CompiledGoFiles().
type item struct {
node ast.Node // Ident, ImportSpec (obj=PkgName), or CaseClause (obj=Var)
diff --git a/gopls/internal/regtest/bench/repo_test.go b/gopls/internal/regtest/bench/repo_test.go
index 18c6edf..c3b8b3b 100644
--- a/gopls/internal/regtest/bench/repo_test.go
+++ b/gopls/internal/regtest/bench/repo_test.go
@@ -115,7 +115,7 @@
tb.Fatalf("repo %s does not exist", name)
}
if !repo.short && testing.Short() {
- tb.Skipf("large repo %s does not run whith -short", repo.name)
+ tb.Skipf("large repo %s does not run with -short", repo.name)
}
return repo
}
diff --git a/gopls/internal/regtest/diagnostics/diagnostics_test.go b/gopls/internal/regtest/diagnostics/diagnostics_test.go
index 29a54f1..623cd72 100644
--- a/gopls/internal/regtest/diagnostics/diagnostics_test.go
+++ b/gopls/internal/regtest/diagnostics/diagnostics_test.go
@@ -8,7 +8,6 @@
"context"
"fmt"
"os/exec"
- "strings"
"testing"
"golang.org/x/tools/gopls/internal/bug"
@@ -433,7 +432,11 @@
func TestMissingDependency(t *testing.T) {
Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
env.OpenFile("print.go")
- env.Await(LogMatching(protocol.Error, "initial workspace load failed", 1, false))
+ env.Await(
+ // Log messages are asynchronous to other events on the LSP stream, so we
+ // can't use OnceMet or AfterChange here.
+ LogMatching(protocol.Error, "initial workspace load failed", 1, false),
+ )
})
}
@@ -1303,42 +1306,18 @@
)
env.OpenFile("a/a_exclude.go")
- countLoads := func(logs []*protocol.LogMessageParams) int {
- count := 0
- for _, log := range logs {
- if strings.Contains(log.Message, `go/packages.Load`) {
- count++
- }
- }
- return count
- }
-
- var before []*protocol.LogMessageParams
- env.AfterChange(
- Diagnostics(env.AtRegexp("a/a_exclude.go", "package (a)")),
- ReadLogs(&before),
- )
+ loadOnce := LogMatching(protocol.Info, "query=.*file=.*a_exclude.go", 1, false)
+ env.Await(loadOnce) // can't use OnceMet or AfterChange as logs are async
// Check that orphaned files are not reloaded, by making a change in
// a.go file and confirming that the workspace diagnosis did not reload
// a_exclude.go.
//
- // Currently, typing in a_exclude.go does result in a reload, because we
- // aren't precise about which changes could result in an unloadable file
- // becoming loadable.
- loads0 := countLoads(before)
- if loads0 == 0 {
- t.Fatal("got 0 Load log messages, want at least 1", loads0)
- }
+ // This is racy (but fails open) because logs are asynchronous to other LSP
+ // operations. There's a chance gopls _did_ log, and we just haven't seen
+ // it yet.
env.RegexpReplace("a/a.go", "package a", "package a // arbitrary comment")
- var after []*protocol.LogMessageParams
- env.AfterChange(
- Diagnostics(env.AtRegexp("a/a_exclude.go", "package (a)")),
- ReadLogs(&after),
- )
- if loads := countLoads(after); loads != loads0 {
- t.Errorf("got %d Load log messages after change to orphaned file, want %d", loads, loads0)
- }
+ env.AfterChange(loadOnce)
})
}
@@ -1853,8 +1832,10 @@
`
Run(t, files, func(t *testing.T, env *Env) {
env.OpenFile("go.mod")
- env.AfterChange(
+ env.Await(
// Check that we have only loaded "<dir>/..." once.
+ // Log messages are asynchronous to other events on the LSP stream, so we
+ // can't use OnceMet or AfterChange here.
LogMatching(protocol.Info, `.*query=.*\.\.\..*`, 1, false),
)
})
diff --git a/gopls/internal/regtest/watch/watch_test.go b/gopls/internal/regtest/watch/watch_test.go
index f485b74..dccf869 100644
--- a/gopls/internal/regtest/watch/watch_test.go
+++ b/gopls/internal/regtest/watch/watch_test.go
@@ -383,7 +383,9 @@
).Run(t, pkg, func(t *testing.T, env *Env) {
env.OpenFile("a/a.go")
env.OpenFile("a/a_unneeded.go")
- env.AfterChange(
+ env.Await(
+ // Log messages are asynchronous to other events on the LSP stream, so we
+ // can't use OnceMet or AfterChange here.
LogMatching(protocol.Info, "a_unneeded.go", 1, false),
)
@@ -395,7 +397,7 @@
Diagnostics(env.AtRegexp("a/a.go", "fmt")),
)
env.SaveBuffer("a/a.go")
- env.AfterChange(
+ env.Await(
// There should only be one log message containing
// a_unneeded.go, from the initial workspace load, which we
// check for earlier. If there are more, there's a bug.
@@ -411,7 +413,7 @@
).Run(t, pkg, func(t *testing.T, env *Env) {
env.OpenFile("a/a.go")
env.OpenFile("a/a_unneeded.go")
- env.AfterChange(
+ env.Await(
LogMatching(protocol.Info, "a_unneeded.go", 1, false),
)
@@ -423,7 +425,7 @@
Diagnostics(env.AtRegexp("a/a.go", "fmt")),
)
env.SaveBuffer("a/a.go")
- env.AfterChange(
+ env.Await(
// There should only be one log message containing
// a_unneeded.go, from the initial workspace load, which we
// check for earlier. If there are more, there's a bug.
diff --git a/gopls/internal/regtest/workspace/workspace_test.go b/gopls/internal/regtest/workspace/workspace_test.go
index 02e3a8c..fa04a41 100644
--- a/gopls/internal/regtest/workspace/workspace_test.go
+++ b/gopls/internal/regtest/workspace/workspace_test.go
@@ -177,7 +177,7 @@
replace random.org => %s
`, env.ReadWorkspaceFile("pkg/go.mod"), dir)
env.WriteWorkspaceFile("pkg/go.mod", goModWithReplace)
- env.AfterChange(
+ env.Await(
LogMatching(protocol.Info, `packages\.Load #\d+\n`, 2, false),
)
})
diff --git a/internal/persistent/map.go b/internal/persistent/map.go
index a5d3083..a9d878f 100644
--- a/internal/persistent/map.go
+++ b/internal/persistent/map.go
@@ -18,7 +18,7 @@
// * Each value is reference counted by nodes which hold it.
// * Each node is reference counted by its parent nodes.
// * Each map is considered a top-level parent node from reference counting perspective.
-// * Each change does always effectivelly produce a new top level node.
+// * Each change does always effectively produce a new top level node.
//
// Functions which operate directly with nodes do have a notation in form of
// `foo(arg1:+n1, arg2:+n2) (ret1:+n3)`.