Skip to content

Commit

Permalink
all: merge master (7eebab3) into gopls-release-branch.0.17
Browse files Browse the repository at this point in the history
Also add back the x/tools replace directive for gopls.

For golang/go#70301

Merge List:

+ 2024-12-04 7eebab3 gopls/internal/golang: support extract variable at top level
+ 2024-12-04 e334696 gopls/internal/golang: ignore effects for change signature refactoring
+ 2024-12-04 3901733 internal/refactor/inline: substitute groups of dependent arguments
+ 2024-12-03 61b2408 gopls/internal/golang: add "Extract constant" counterpart
+ 2024-12-03 c01eead internal/gcimporter: require binary export data header
+ 2024-12-03 9a04136 gopls/internal/golang/stubmethods: refine crash into bug report
+ 2024-12-03 01e0b05 internal/refactor/inline: fix condition for splice assignment strategy
+ 2024-12-03 557f540 gopls/internal/golang: don't offer to move variadic parameters
+ 2024-12-03 399ee16 internal/gcimporter: update FindExportData to return a non-negative size
+ 2024-12-03 25b0003 internal/refactor/inline: more precise redundant conversion detection
+ 2024-12-03 eb46939 gopls/internal/test/marker: add reproducers for moveparam bug bash bugs
+ 2024-12-03 4296223 gopls/internal/analysis/yield: add comment about dataflow
+ 2024-12-03 7a4f3b0 internal/gcimporter: require archive files
+ 2024-12-02 2f73c61 gopls/internal/golang: avoid crash in lookupDocLinkSymbol
+ 2024-12-02 ef3d603 gopls/internal/golang/completion: fix crash with extra results
+ 2024-12-02 8ffeaba gopls/internal/settings: enable 'waitgroup' analyzer
+ 2024-12-02 4317959 go/analysis/passes/waitgroup: report WaitGroup.Add in goroutine
+ 2024-12-02 72fdfa6 gopls/internal/golang: Disable test generation for generic functions
+ 2024-12-02 b80f1ed gopls/internal/analysis/yield: peephole-optimize phi(false, x)
+ 2024-11-28 e7bd227 gopls/internal/golang: fix folding range for function calls
+ 2024-11-28 e71702b internal/versions: remove constraint.GoVersion wrapper
+ 2024-11-28 c99edec gopls/internal/golang/completion: add alias support for literals
+ 2024-11-27 bfe3046 go/packages: add a unit test for golang/go#70394
+ 2024-11-27 df87831 gopls/internal/undeclaredname: add missing colon when appropriate
+ 2024-11-26 53efd30 gopls/internal/golang: simplify package name collection in add test

Change-Id: I476e80493f61732701784befe2b130ca967b259e
  • Loading branch information
findleyr committed Dec 4, 2024
2 parents c9ad642 + 7eebab3 commit 1eb875f
Show file tree
Hide file tree
Showing 72 changed files with 2,482 additions and 656 deletions.
25 changes: 6 additions & 19 deletions go/analysis/passes/buildtag/buildtag.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (

"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/internal/versions"
)

const Doc = "check //go:build and // +build directives"
Expand Down Expand Up @@ -371,11 +370,6 @@ func (check *checker) finish() {

// tags reports issues in go versions in tags within the expression e.
func (check *checker) tags(pos token.Pos, e constraint.Expr) {
// Check that constraint.GoVersion is meaningful (>= go1.21).
if versions.ConstraintGoVersion == nil {
return
}

// Use Eval to visit each tag.
_ = e.Eval(func(tag string) bool {
if malformedGoTag(tag) {
Expand All @@ -393,26 +387,19 @@ func malformedGoTag(tag string) bool {
// Check for close misspellings of the "go1." prefix.
for _, pre := range []string{"go.", "g1.", "go"} {
suffix := strings.TrimPrefix(tag, pre)
if suffix != tag {
if valid, ok := validTag("go1." + suffix); ok && valid {
return true
}
if suffix != tag && validGoVersion("go1."+suffix) {
return true
}
}
return false
}

// The tag starts with "go1" so it is almost certainly a GoVersion.
// Report it if it is not a valid build constraint.
valid, ok := validTag(tag)
return ok && !valid
return !validGoVersion(tag)
}

// validTag returns (valid, ok) where valid reports when a tag is valid,
// and ok reports determining if the tag is valid succeeded.
func validTag(tag string) (valid bool, ok bool) {
if versions.ConstraintGoVersion != nil {
return versions.ConstraintGoVersion(&constraint.TagExpr{Tag: tag}) != "", true
}
return false, false
// validGoVersion reports when a tag is a valid go version.
func validGoVersion(tag string) bool {
return constraint.GoVersion(&constraint.TagExpr{Tag: tag}) != ""
}
34 changes: 34 additions & 0 deletions go/analysis/passes/waitgroup/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2024 The Go 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 waitgroup defines an Analyzer that detects simple misuses
// of sync.WaitGroup.
//
// # Analyzer waitgroup
//
// waitgroup: check for misuses of sync.WaitGroup
//
// This analyzer detects mistaken calls to the (*sync.WaitGroup).Add
// method from inside a new goroutine, causing Add to race with Wait:
//
// // WRONG
// var wg sync.WaitGroup
// go func() {
// wg.Add(1) // "WaitGroup.Add called from inside new goroutine"
// defer wg.Done()
// ...
// }()
// wg.Wait() // (may return prematurely before new goroutine starts)
//
// The correct code calls Add before starting the goroutine:
//
// // RIGHT
// var wg sync.WaitGroup
// wg.Add(1)
// go func() {
// defer wg.Done()
// ...
// }()
// wg.Wait()
package waitgroup
16 changes: 16 additions & 0 deletions go/analysis/passes/waitgroup/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2024 The Go 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 ignore

// The waitgroup command applies the golang.org/x/tools/go/analysis/passes/waitgroup
// analysis to the specified packages of Go source code.
package main

import (
"golang.org/x/tools/go/analysis/passes/waitgroup"
"golang.org/x/tools/go/analysis/singlechecker"
)

func main() { singlechecker.Main(waitgroup.Analyzer) }
14 changes: 14 additions & 0 deletions go/analysis/passes/waitgroup/testdata/src/a/a.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package a

import "sync"

func f() {
var wg sync.WaitGroup
wg.Add(1) // ok
go func() {
wg.Add(1) // want "WaitGroup.Add called from inside new goroutine"
// ...
wg.Add(1) // ok
}()
wg.Add(1) // ok
}
105 changes: 105 additions & 0 deletions go/analysis/passes/waitgroup/waitgroup.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package waitgroup defines an Analyzer that detects simple misuses
// of sync.WaitGroup.
package waitgroup

import (
_ "embed"
"go/ast"
"go/types"
"reflect"

"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
"golang.org/x/tools/internal/typesinternal"
)

//go:embed doc.go
var doc string

var Analyzer = &analysis.Analyzer{
Name: "waitgroup",
Doc: analysisutil.MustExtractDoc(doc, "waitgroup"),
URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/waitgroup",
Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run,
}

func run(pass *analysis.Pass) (any, error) {
if !analysisutil.Imports(pass.Pkg, "sync") {
return nil, nil // doesn't directly import sync
}

inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
nodeFilter := []ast.Node{
(*ast.CallExpr)(nil),
}

inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) (proceed bool) {
if push {
call := n.(*ast.CallExpr)
if fn, ok := typeutil.Callee(pass.TypesInfo, call).(*types.Func); ok &&
isMethodNamed(fn, "sync", "WaitGroup", "Add") &&
hasSuffix(stack, wantSuffix) &&
backindex(stack, 1) == backindex(stack, 2).(*ast.BlockStmt).List[0] { // ExprStmt must be Block's first stmt

pass.Reportf(call.Lparen, "WaitGroup.Add called from inside new goroutine")
}
}
return true
})

return nil, nil
}

// go func() {
// wg.Add(1)
// ...
// }()
var wantSuffix = []ast.Node{
(*ast.GoStmt)(nil),
(*ast.CallExpr)(nil),
(*ast.FuncLit)(nil),
(*ast.BlockStmt)(nil),
(*ast.ExprStmt)(nil),
(*ast.CallExpr)(nil),
}

// hasSuffix reports whether stack has the matching suffix,
// considering only node types.
func hasSuffix(stack, suffix []ast.Node) bool {
// TODO(adonovan): the inspector could implement this for us.
if len(stack) < len(suffix) {
return false
}
for i := range len(suffix) {
if reflect.TypeOf(backindex(stack, i)) != reflect.TypeOf(backindex(suffix, i)) {
return false
}
}
return true
}

// isMethodNamed reports whether f is a method with the specified
// package, receiver type, and method names.
func isMethodNamed(fn *types.Func, pkg, recv, name string) bool {
if fn.Pkg() != nil && fn.Pkg().Path() == pkg && fn.Name() == name {
if r := fn.Type().(*types.Signature).Recv(); r != nil {
if _, gotRecv := typesinternal.ReceiverNamed(r); gotRecv != nil {
return gotRecv.Obj().Name() == recv
}
}
}
return false
}

// backindex is like [slices.Index] but from the back of the slice.
func backindex[T any](slice []T, i int) T {
return slice[len(slice)-1-i]
}
16 changes: 16 additions & 0 deletions go/analysis/passes/waitgroup/waitgroup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2024 The Go 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 waitgroup_test

import (
"testing"

"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/go/analysis/passes/waitgroup"
)

func Test(t *testing.T) {
analysistest.Run(t, analysistest.TestData(), waitgroup.Analyzer, "a")
}
22 changes: 8 additions & 14 deletions go/gcexportdata/gcexportdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,24 +106,18 @@ func Find(importPath, srcDir string) (filename, path string) {
// additional trailing data beyond the end of the export data.
func NewReader(r io.Reader) (io.Reader, error) {
buf := bufio.NewReader(r)
_, size, err := gcimporter.FindExportData(buf)
size, err := gcimporter.FindExportData(buf)
if err != nil {
return nil, err
}

if size >= 0 {
// We were given an archive and found the __.PKGDEF in it.
// This tells us the size of the export data, and we don't
// need to return the entire file.
return &io.LimitedReader{
R: buf,
N: size,
}, nil
} else {
// We were given an object file. As such, we don't know how large
// the export data is and must return the entire file.
return buf, nil
}
// We were given an archive and found the __.PKGDEF in it.
// This tells us the size of the export data, and we don't
// need to return the entire file.
return &io.LimitedReader{
R: buf,
N: size,
}, nil
}

// readAll works the same way as io.ReadAll, but avoids allocations and copies
Expand Down
45 changes: 45 additions & 0 deletions go/packages/packages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3177,6 +3177,51 @@ func TestIssue69606b(t *testing.T) {
}
}

// TestIssue70394 tests materializing an alias type defined in a package (m/a)
// in another package (m/b) where the types for m/b are coming from the compiler,
// e.g. `go list -compiled=true ... m/b`.
func TestIssue70394(t *testing.T) {
// TODO(taking): backport https://go.dev/cl/604099 so that this works on 23.
testenv.NeedsGo1Point(t, 24)
testenv.NeedsTool(t, "go") // requires go list.
testenv.NeedsGoBuild(t) // requires the compiler for export data.

t.Setenv("GODEBUG", "gotypesalias=1")

dir := t.TempDir()
overlay := map[string][]byte{
filepath.Join(dir, "go.mod"): []byte("module m"), // go version of the module does not matter.
filepath.Join(dir, "a/a.go"): []byte(`package a; type A = int32`),
filepath.Join(dir, "b/b.go"): []byte(`package b; import "m/a"; var V a.A`),
}
cfg := &packages.Config{
Dir: dir,
Mode: packages.NeedTypes, // just NeedsTypes allows for loading export data.
Overlay: overlay,
Env: append(os.Environ(), "GOFLAGS=-mod=vendor", "GOWORK=off"),
}
pkgs, err := packages.Load(cfg, "m/b")
if err != nil {
t.Fatal(err)
}
if errs := packages.PrintErrors(pkgs); errs > 0 {
t.Fatalf("Got %d errors while loading packages.", errs)
}
if len(pkgs) != 1 {
t.Fatalf("Loaded %d packages. expected 1", len(pkgs))
}

pkg := pkgs[0]
scope := pkg.Types.Scope()
obj := scope.Lookup("V")
if obj == nil {
t.Fatalf("Failed to find object %q in package %q", "V", pkg)
}
if _, ok := obj.Type().(*types.Alias); !ok {
t.Errorf("Object %q has type %q. expected an alias", obj, obj.Type())
}
}

// TestNeedTypesInfoOnly tests when NeedTypesInfo was set and NeedSyntax & NeedTypes were not,
// Load should include the TypesInfo of packages properly
func TestLoadTypesInfoWithoutSyntaxOrTypes(t *testing.T) {
Expand Down
31 changes: 31 additions & 0 deletions gopls/doc/analyzers.md
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,37 @@ Default: off. Enable by setting `"analyses": {"useany": true}`.

Package documentation: [useany](https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/useany)

<a id='waitgroup'></a>
## `waitgroup`: check for misuses of sync.WaitGroup


This analyzer detects mistaken calls to the (*sync.WaitGroup).Add
method from inside a new goroutine, causing Add to race with Wait:

// WRONG
var wg sync.WaitGroup
go func() {
wg.Add(1) // "WaitGroup.Add called from inside new goroutine"
defer wg.Done()
...
}()
wg.Wait() // (may return prematurely before new goroutine starts)

The correct code calls Add before starting the goroutine:

// RIGHT
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
...
}()
wg.Wait()

Default: on.

Package documentation: [waitgroup](https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/waitgroup)

<a id='yield'></a>
## `yield`: report calls to yield where the result is ignored

Expand Down
5 changes: 3 additions & 2 deletions gopls/doc/features/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,12 +213,13 @@ func main() {
}
```

The quick fix would be:
The quick fix would insert a declaration with a default
value inferring its type from the context:

```go
func main() {
x := 42
y :=
y := 0
min(x, y)
}
```
Expand Down
Loading

0 comments on commit 1eb875f

Please sign in to comment.