Skip to content

Commit aa94d89

Browse files
adonovangopherbot
authored andcommitted
gopls/internal/analysis/modernize: replace interface{} with any
Also, modernize our tests in this manner. Also, remove existing "useany" analyzer, which is subsumed by this one. The fact that useany was off by default suggest that perhaps modernize may need to be as well; but perhaps severity=INFO will suffice. Updates golang/go#70815 Change-Id: Iba1662993fb8a1c50b2d843ad9425bbddafc927f Reviewed-on: https://go-review.googlesource.com/c/tools/+/636276 Auto-Submit: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 93cc684 commit aa94d89

38 files changed

+143
-237
lines changed

gopls/doc/analyzers.md

+3-11
Original file line numberDiff line numberDiff line change
@@ -444,9 +444,10 @@ This analyzer reports opportunities for simplifying and clarifying
444444
existing code by using more modern features of Go, such as:
445445

446446
- replacing if/else conditional assignments by a call to the
447-
built-in min or max functions.
447+
built-in min or max functions added in go1.21;
448448
- replacing sort.Slice(x, func(i, j int) bool) { return s[i] < s[j] }
449-
by slices.Sort(s).
449+
by a call to slices.Sort(s), added in go1.21;
450+
- replacing interface{} by the 'any' type added in go1.18;
450451

451452
Default: on.
452453

@@ -983,15 +984,6 @@ Default: on.
983984

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

986-
<a id='useany'></a>
987-
## `useany`: check for constraints that could be simplified to "any"
988-
989-
990-
991-
Default: off. Enable by setting `"analyses": {"useany": true}`.
992-
993-
Package documentation: [useany](https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/useany)
994-
995987
<a id='waitgroup'></a>
996988
## `waitgroup`: check for misuses of sync.WaitGroup
997989

gopls/doc/features/diagnostics.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ dorky details and deletia:
299299
300300
- **Experimental analyzers**. Gopls has some analyzers that are not
301301
enabled by default, because they produce too high a rate of false
302-
positives. For example, fieldalignment, shadow, useany.
302+
positives. For example, fieldalignment, shadow.
303303
304304
Note: fillstruct is not a real analyzer.
305305

gopls/internal/analysis/modernize/doc.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
// existing code by using more modern features of Go, such as:
1313
//
1414
// - replacing if/else conditional assignments by a call to the
15-
// built-in min or max functions.
15+
// built-in min or max functions added in go1.21;
1616
// - replacing sort.Slice(x, func(i, j int) bool) { return s[i] < s[j] }
17-
// by slices.Sort(s).
17+
// by a call to slices.Sort(s), added in go1.21;
18+
// - replacing interface{} by the 'any' type added in go1.18;
1819
package modernize
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 modernize
6+
7+
import (
8+
"go/ast"
9+
"go/types"
10+
11+
"golang.org/x/tools/go/analysis"
12+
"golang.org/x/tools/go/analysis/passes/inspect"
13+
"golang.org/x/tools/go/ast/inspector"
14+
)
15+
16+
// The efaceany pass replaces interface{} with 'any'.
17+
func efaceany(pass *analysis.Pass) {
18+
// TODO(adonovan): opt: combine all these micro-passes into a single traversal.
19+
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
20+
for iface := range inspector.All[*ast.InterfaceType](inspect) {
21+
if iface.Methods.NumFields() == 0 {
22+
23+
// TODO(adonovan): opt: record the enclosing file as we go.
24+
f := enclosingFile(pass, iface.Pos())
25+
scope := pass.TypesInfo.Scopes[f].Innermost(iface.Pos())
26+
if _, obj := scope.LookupParent("any", iface.Pos()); obj != types.Universe.Lookup("any") {
27+
continue // 'any' is shadowed
28+
}
29+
30+
pass.Report(analysis.Diagnostic{
31+
Pos: iface.Pos(),
32+
End: iface.End(),
33+
Category: "efaceany",
34+
Message: "interface{} can be replaced by any",
35+
SuggestedFixes: []analysis.SuggestedFix{{
36+
Message: "Replace interface{} by any",
37+
TextEdits: []analysis.TextEdit{
38+
{
39+
Pos: iface.Pos(),
40+
End: iface.End(),
41+
NewText: []byte("any"),
42+
},
43+
},
44+
}},
45+
})
46+
}
47+
}
48+
}

gopls/internal/analysis/modernize/modernize.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,34 @@ var Analyzer = &analysis.Analyzer{
3030
}
3131

3232
func run(pass *analysis.Pass) (any, error) {
33+
// Decorate pass.Report to suppress diagnostics in generated files.
34+
//
35+
// TODO(adonovan): opt: do this more efficiently by interleaving
36+
// the micro-passes (as described below) and preemptively skipping
37+
// the entire subtree for each generated *ast.File.
38+
{
39+
// Gather information whether file is generated or not.
40+
generated := make(map[*token.File]bool)
41+
for _, file := range pass.Files {
42+
if ast.IsGenerated(file) {
43+
generated[pass.Fset.File(file.FileStart)] = true
44+
}
45+
}
46+
report := pass.Report
47+
pass.Report = func(diag analysis.Diagnostic) {
48+
if _, ok := generated[pass.Fset.File(diag.Pos)]; ok {
49+
return // skip checking if it's generated code
50+
}
51+
report(diag)
52+
}
53+
}
54+
3355
minmax(pass)
3456
sortslice(pass)
57+
efaceany(pass)
3558

3659
// TODO(adonovan): more modernizers here; see #70815.
37-
// Consider interleaving passes with the same inspection
38-
// criteria (e.g. CallExpr).
60+
// TODO(adonovan): opt: interleave these micro-passes within a single inspection.
3961

4062
return nil, nil
4163
}

gopls/internal/analysis/modernize/modernize_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ import (
1414
func Test(t *testing.T) {
1515
analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), modernize.Analyzer,
1616
"minmax",
17-
"sortslice")
17+
"sortslice",
18+
"efaceany")
1819
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package efaceany
2+
3+
func _(x interface{}) {} // want "interface{} can be replaced by any"
4+
5+
func _() {
6+
var x interface{} // want "interface{} can be replaced by any"
7+
const any = 1
8+
var y interface{} // nope: any is shadowed here
9+
_, _ = x, y
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package efaceany
2+
3+
func _(x any) {} // want "interface{} can be replaced by any"
4+
5+
func _() {
6+
var x any // want "interface{} can be replaced by any"
7+
const any = 1
8+
var y interface{} // nope: any is shadowed here
9+
_, _ = x, y
10+
}

gopls/internal/analysis/useany/testdata/src/a/a.go

-25
This file was deleted.

gopls/internal/analysis/useany/testdata/src/a/a.go.golden

-25
This file was deleted.

gopls/internal/analysis/useany/useany.go

-98
This file was deleted.

gopls/internal/analysis/useany/useany_test.go

-17
This file was deleted.

gopls/internal/doc/api.json

+2-13
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@
467467
},
468468
{
469469
"Name": "\"modernize\"",
470-
"Doc": "simplify code by using modern constructs\n\nThis analyzer reports opportunities for simplifying and clarifying\nexisting code by using more modern features of Go, such as:\n\n - replacing if/else conditional assignments by a call to the\n built-in min or max functions.\n - replacing sort.Slice(x, func(i, j int) bool) { return s[i] \u003c s[j] }\n by slices.Sort(s).",
470+
"Doc": "simplify code by using modern constructs\n\nThis analyzer reports opportunities for simplifying and clarifying\nexisting code by using more modern features of Go, such as:\n\n - replacing if/else conditional assignments by a call to the\n built-in min or max functions added in go1.21;\n - replacing sort.Slice(x, func(i, j int) bool) { return s[i] \u003c s[j] }\n by a call to slices.Sort(s), added in go1.21;\n - replacing interface{} by the 'any' type added in go1.18;",
471471
"Default": "true"
472472
},
473473
{
@@ -605,11 +605,6 @@
605605
"Doc": "checks for unused writes\n\nThe analyzer reports instances of writes to struct fields and\narrays that are never read. Specifically, when a struct object\nor an array is copied, its elements are copied implicitly by\nthe compiler, and any element write to this copy does nothing\nwith the original object.\n\nFor example:\n\n\ttype T struct { x int }\n\n\tfunc f(input []T) {\n\t\tfor i, v := range input { // v is a copy\n\t\t\tv.x = i // unused write to field x\n\t\t}\n\t}\n\nAnother example is about non-pointer receiver:\n\n\ttype T struct { x int }\n\n\tfunc (t T) f() { // t is a copy\n\t\tt.x = i // unused write to field x\n\t}",
606606
"Default": "true"
607607
},
608-
{
609-
"Name": "\"useany\"",
610-
"Doc": "check for constraints that could be simplified to \"any\"",
611-
"Default": "false"
612-
},
613608
{
614609
"Name": "\"waitgroup\"",
615610
"Doc": "check for misuses of sync.WaitGroup\n\nThis analyzer detects mistaken calls to the (*sync.WaitGroup).Add\nmethod from inside a new goroutine, causing Add to race with Wait:\n\n\t// WRONG\n\tvar wg sync.WaitGroup\n\tgo func() {\n\t wg.Add(1) // \"WaitGroup.Add called from inside new goroutine\"\n\t defer wg.Done()\n\t ...\n\t}()\n\twg.Wait() // (may return prematurely before new goroutine starts)\n\nThe correct code calls Add before starting the goroutine:\n\n\t// RIGHT\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t...\n\t}()\n\twg.Wait()",
@@ -1138,7 +1133,7 @@
11381133
},
11391134
{
11401135
"Name": "modernize",
1141-
"Doc": "simplify code by using modern constructs\n\nThis analyzer reports opportunities for simplifying and clarifying\nexisting code by using more modern features of Go, such as:\n\n - replacing if/else conditional assignments by a call to the\n built-in min or max functions.\n - replacing sort.Slice(x, func(i, j int) bool) { return s[i] \u003c s[j] }\n by slices.Sort(s).",
1136+
"Doc": "simplify code by using modern constructs\n\nThis analyzer reports opportunities for simplifying and clarifying\nexisting code by using more modern features of Go, such as:\n\n - replacing if/else conditional assignments by a call to the\n built-in min or max functions added in go1.21;\n - replacing sort.Slice(x, func(i, j int) bool) { return s[i] \u003c s[j] }\n by a call to slices.Sort(s), added in go1.21;\n - replacing interface{} by the 'any' type added in go1.18;",
11421137
"URL": "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/modernize",
11431138
"Default": true
11441139
},
@@ -1304,12 +1299,6 @@
13041299
"URL": "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedwrite",
13051300
"Default": true
13061301
},
1307-
{
1308-
"Name": "useany",
1309-
"Doc": "check for constraints that could be simplified to \"any\"",
1310-
"URL": "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/useany",
1311-
"Default": false
1312-
},
13131302
{
13141303
"Name": "waitgroup",
13151304
"Doc": "check for misuses of sync.WaitGroup\n\nThis analyzer detects mistaken calls to the (*sync.WaitGroup).Add\nmethod from inside a new goroutine, causing Add to race with Wait:\n\n\t// WRONG\n\tvar wg sync.WaitGroup\n\tgo func() {\n\t wg.Add(1) // \"WaitGroup.Add called from inside new goroutine\"\n\t defer wg.Done()\n\t ...\n\t}()\n\twg.Wait() // (may return prematurely before new goroutine starts)\n\nThe correct code calls Add before starting the goroutine:\n\n\t// RIGHT\n\tvar wg sync.WaitGroup\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\t...\n\t}()\n\twg.Wait()",

gopls/internal/settings/analysis.go

-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ import (
5858
"golang.org/x/tools/gopls/internal/analysis/simplifyslice"
5959
"golang.org/x/tools/gopls/internal/analysis/unusedparams"
6060
"golang.org/x/tools/gopls/internal/analysis/unusedvariable"
61-
"golang.org/x/tools/gopls/internal/analysis/useany"
6261
"golang.org/x/tools/gopls/internal/analysis/yield"
6362
"golang.org/x/tools/gopls/internal/protocol"
6463
)
@@ -162,7 +161,6 @@ func init() {
162161

163162
// disabled due to high false positives
164163
{analyzer: shadow.Analyzer, enabled: false}, // very noisy
165-
{analyzer: useany.Analyzer, enabled: false}, // never a bug
166164
// fieldalignment is not even off-by-default; see #67762.
167165

168166
// "simplifiers": analyzers that offer mere style fixes

0 commit comments

Comments
 (0)