Skip to content

Commit a432b16

Browse files
adonovangopherbot
authored andcommitted
gopls/internal/analysis: disable ssa/ir analyzers on range-over-func
This change disables analyzers that cannot yet safely process go1.23 range-over-func statements, including buildssa and buildir. (This is done by poking in an additional Analyzer.Requires edge on a new temporary analyzer that fails when it sees a range-over-func.) We plan to revert this change when ssa and ir support the new feature, but this CL will unblock uses of it in the standard library which would otherwise cause gopls' tests to crash. Updates golang/go#67237 Updates dominikh/go-tools#1494 Change-Id: Ibed2a88da94fb84234b4410b6bc7562a493287ce Reviewed-on: https://go-review.googlesource.com/c/tools/+/583778 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]> Auto-Submit: Alan Donovan <[email protected]>
1 parent b426bc7 commit a432b16

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package norangeoverfunc
6+
7+
// TODO(adonovan): delete this when #67237 and dominikh/go-tools#1494 are fixed.
8+
9+
import (
10+
_ "embed"
11+
"fmt"
12+
"go/ast"
13+
"go/types"
14+
15+
"golang.org/x/tools/go/analysis"
16+
"golang.org/x/tools/go/analysis/passes/inspect"
17+
"golang.org/x/tools/go/ast/inspector"
18+
)
19+
20+
var Analyzer = &analysis.Analyzer{
21+
Name: "norangeoverfunc",
22+
Doc: `norangeoverfunc fails if a package uses go1.23 range-over-func
23+
24+
Require it from any analyzer that cannot yet safely process this new feature.`,
25+
Requires: []*analysis.Analyzer{inspect.Analyzer},
26+
Run: run,
27+
}
28+
29+
func run(pass *analysis.Pass) (any, error) {
30+
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
31+
filter := []ast.Node{(*ast.RangeStmt)(nil)}
32+
33+
// TODO(adonovan): opt: short circuit if not using go1.23.
34+
35+
var found *ast.RangeStmt
36+
inspect.Preorder(filter, func(n ast.Node) {
37+
if found == nil {
38+
stmt := n.(*ast.RangeStmt)
39+
if _, ok := pass.TypesInfo.TypeOf(stmt.X).Underlying().(*types.Signature); ok {
40+
found = stmt
41+
}
42+
}
43+
})
44+
if found != nil {
45+
return nil, fmt.Errorf("package %q uses go1.23 range-over-func; cannot build SSA or IR (#67237)",
46+
pass.Pkg.Path())
47+
}
48+
49+
return nil, nil
50+
}

gopls/internal/settings/analysis.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"golang.org/x/tools/go/analysis/passes/atomic"
1313
"golang.org/x/tools/go/analysis/passes/atomicalign"
1414
"golang.org/x/tools/go/analysis/passes/bools"
15+
"golang.org/x/tools/go/analysis/passes/buildssa"
1516
"golang.org/x/tools/go/analysis/passes/buildtag"
1617
"golang.org/x/tools/go/analysis/passes/cgocall"
1718
"golang.org/x/tools/go/analysis/passes/composite"
@@ -49,6 +50,7 @@ import (
4950
"golang.org/x/tools/gopls/internal/analysis/fillreturns"
5051
"golang.org/x/tools/gopls/internal/analysis/infertypeargs"
5152
"golang.org/x/tools/gopls/internal/analysis/nonewvars"
53+
"golang.org/x/tools/gopls/internal/analysis/norangeoverfunc"
5254
"golang.org/x/tools/gopls/internal/analysis/noresultvalues"
5355
"golang.org/x/tools/gopls/internal/analysis/simplifycompositelit"
5456
"golang.org/x/tools/gopls/internal/analysis/simplifyrange"
@@ -59,6 +61,7 @@ import (
5961
"golang.org/x/tools/gopls/internal/analysis/unusedvariable"
6062
"golang.org/x/tools/gopls/internal/analysis/useany"
6163
"golang.org/x/tools/gopls/internal/protocol"
64+
"honnef.co/go/tools/staticcheck"
6265
)
6366

6467
// Analyzer augments a [analysis.Analyzer] with additional LSP configuration.
@@ -104,6 +107,33 @@ func (a *Analyzer) String() string { return a.analyzer.String() }
104107
var DefaultAnalyzers = make(map[string]*Analyzer) // initialized below
105108

106109
func init() {
110+
// Emergency workaround for #67237 to allow standard library
111+
// to use range over func: disable SSA-based analyses of
112+
// go1.23 packages that use range-over-func.
113+
suppressOnRangeOverFunc := func(a *analysis.Analyzer) {
114+
a.Requires = append(a.Requires, norangeoverfunc.Analyzer)
115+
}
116+
suppressOnRangeOverFunc(buildssa.Analyzer)
117+
// buildir is non-exported so we have to scan the Analysis.Requires graph to find it.
118+
var buildir *analysis.Analyzer
119+
for _, a := range staticcheck.Analyzers {
120+
for _, req := range a.Analyzer.Requires {
121+
if req.Name == "buildir" {
122+
buildir = req
123+
}
124+
}
125+
126+
// Temporarily disable SA4004 CheckIneffectiveLoop as
127+
// it crashes when encountering go1.23 range-over-func
128+
// (#67237, dominikh/go-tools#1494).
129+
if a.Analyzer.Name == "SA4004" {
130+
suppressOnRangeOverFunc(a.Analyzer)
131+
}
132+
}
133+
if buildir != nil {
134+
suppressOnRangeOverFunc(buildir)
135+
}
136+
107137
// The traditional vet suite:
108138
analyzers := []*Analyzer{
109139
{analyzer: appends.Analyzer, enabled: true},
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
2+
This test verifies that SSA-based analyzers don't run on packages that
3+
use range-over-func. This is an emergency fix of #67237 (for buildssa)
4+
until we land https://go.dev/cl/555075.
5+
6+
Similarly, it is an emergency fix of dominikh/go-tools#1494 (for
7+
buildir) until that package is similarly fixed for go1.23.
8+
9+
Explanation:
10+
- Package p depends on q and r, and analyzers buildssa and buildir
11+
depend on norangeoverfunc.
12+
- Analysis pass norangeoverfunc@q fails, thus norangeoverfunc@p is not
13+
executed; but norangeoverfunc@r is ok
14+
- nilness requires buildssa, which is not facty, so it can run on p and r.
15+
- SA1025 requires buildir, which is facty, so SA1025 can run only on r.
16+
17+
-- flags --
18+
-min_go=go1.23
19+
20+
-- settings.json --
21+
{
22+
"staticcheck": true
23+
}
24+
25+
-- go.mod --
26+
module example.com
27+
28+
go 1.23
29+
30+
-- p/p.go --
31+
package p // a dependency uses range-over-func, so nilness runs but SA1025 cannot (buildir is facty)
32+
33+
import (
34+
_ "example.com/q"
35+
_ "example.com/r"
36+
"fmt"
37+
)
38+
39+
func _(ptr *int) {
40+
if ptr == nil {
41+
println(*ptr) //@diag(re"[*]ptr", re"nil dereference in load")
42+
}
43+
_ = fmt.Sprintf("%s", "abc") // no SA1025 finding
44+
}
45+
46+
-- q/q.go --
47+
package q // uses range-over-func, so no diagnostics from nilness or SA1025
48+
49+
import "fmt"
50+
51+
type iterSeq[T any] func(yield func(T) bool)
52+
53+
func _(seq iterSeq[int]) {
54+
for x := range seq {
55+
println(x)
56+
}
57+
58+
_ = fmt.Sprintf("%s", "abc")
59+
}
60+
61+
func _(ptr *int) {
62+
if ptr == nil {
63+
println(*ptr) // no nilness finding
64+
}
65+
}
66+
67+
-- r/r.go --
68+
package r // does not use range-over-func, so nilness and SA1025 report diagnosticcs
69+
70+
import "fmt"
71+
72+
func _(ptr *int) {
73+
if ptr == nil {
74+
println(*ptr) //@diag(re"[*]ptr", re"nil dereference in load")
75+
}
76+
_ = fmt.Sprintf("%s", "abc") //@diag(re"fmt", re"no need to use fmt.Sprintf")
77+
}

0 commit comments

Comments
 (0)