Skip to content

Commit 0ffdb82

Browse files
committed
gopls/internal/analysis/gofix: add vet analyzer
Add a second analyzer that checks for valid go:fix inline directives without suggesting changes. Change-Id: I0b9ad3da79f554caef01dda66ef954c59718015d Reviewed-on: https://go-review.googlesource.com/c/tools/+/651656 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 2839096 commit 0ffdb82

File tree

5 files changed

+172
-3
lines changed

5 files changed

+172
-3
lines changed

gopls/internal/analysis/gofix/doc.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
/*
66
Package gofix defines an Analyzer that inlines calls to functions
77
and uses of constants
8-
marked with a "//go:fix inline" doc comment.
8+
marked with a "//go:fix inline" directive.
9+
A second analyzer only checks uses of the directive.
910
1011
# Analyzer gofix
1112
@@ -81,5 +82,12 @@ The proposal https://go.dev/issue/32816 introduces the "//go:fix" directives.
8182
You can use this (officially unsupported) command to apply gofix fixes en masse:
8283
8384
$ go run golang.org/x/tools/gopls/internal/analysis/gofix/cmd/gofix@latest -test ./...
85+
86+
# Analyzer gofixdirective
87+
88+
gofixdirective: validate uses of gofix comment directives
89+
90+
The gofixdirective analyzer checks "//go:fix inline" directives for correctness.
91+
See the documentation for the gofix analyzer for more about "/go:fix inline".
8492
*/
8593
package gofix

gopls/internal/analysis/gofix/gofix.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,20 @@ var Analyzer = &analysis.Analyzer{
3434
Name: "gofix",
3535
Doc: analysisinternal.MustExtractDoc(doc, "gofix"),
3636
URL: "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/gofix",
37-
Run: run,
37+
Run: func(pass *analysis.Pass) (any, error) { return run(pass, true) },
38+
FactTypes: []analysis.Fact{
39+
(*goFixInlineFuncFact)(nil),
40+
(*goFixInlineConstFact)(nil),
41+
(*goFixInlineAliasFact)(nil),
42+
},
43+
Requires: []*analysis.Analyzer{inspect.Analyzer},
44+
}
45+
46+
var DirectiveAnalyzer = &analysis.Analyzer{
47+
Name: "gofixdirective",
48+
Doc: analysisinternal.MustExtractDoc(doc, "gofixdirective"),
49+
URL: "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/gofix",
50+
Run: func(pass *analysis.Pass) (any, error) { return run(pass, false) },
3851
FactTypes: []analysis.Fact{
3952
(*goFixInlineFuncFact)(nil),
4053
(*goFixInlineConstFact)(nil),
@@ -46,6 +59,7 @@ var Analyzer = &analysis.Analyzer{
4659
// analyzer holds the state for this analysis.
4760
type analyzer struct {
4861
pass *analysis.Pass
62+
fix bool // only suggest fixes if true; else, just check directives
4963
root cursor.Cursor
5064
// memoization of repeated calls for same file.
5165
fileContent map[string][]byte
@@ -55,9 +69,10 @@ type analyzer struct {
5569
inlinableAliases map[*types.TypeName]*goFixInlineAliasFact
5670
}
5771

58-
func run(pass *analysis.Pass) (any, error) {
72+
func run(pass *analysis.Pass, fix bool) (any, error) {
5973
a := &analyzer{
6074
pass: pass,
75+
fix: fix,
6176
root: cursor.Root(pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)),
6277
fileContent: make(map[string][]byte),
6378
inlinableFuncs: make(map[*types.Func]*inline.Callee),
@@ -256,6 +271,10 @@ func (a *analyzer) inlineCall(call *ast.CallExpr, cur cursor.Cursor) {
256271
a.pass.Reportf(call.Lparen, "%v", err)
257272
return
258273
}
274+
if !a.fix {
275+
return
276+
}
277+
259278
if res.Literalized {
260279
// Users are not fond of inlinings that literalize
261280
// f(x) to func() { ... }(), so avoid them.
@@ -533,6 +552,9 @@ func (a *analyzer) inlineConst(con *types.Const, cur cursor.Cursor) {
533552

534553
// reportInline reports a diagnostic for fixing an inlinable name.
535554
func (a *analyzer) reportInline(kind, capKind string, ident ast.Expr, edits []analysis.TextEdit, newText string) {
555+
if !a.fix {
556+
return
557+
}
536558
edits = append(edits, analysis.TextEdit{
537559
Pos: ident.Pos(),
538560
End: ident.End(),

gopls/internal/analysis/gofix/gofix_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ func TestAnalyzer(t *testing.T) {
2222
analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), Analyzer, "a", "b")
2323
}
2424

25+
func TestDirectiveAnalyzer(t *testing.T) {
26+
analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), DirectiveAnalyzer, "directive")
27+
28+
}
29+
2530
func TestTypesWithNames(t *testing.T) {
2631
// Test setup inspired by internal/analysisinternal/addimport_test.go.
2732
testenv.NeedsDefaultImporter(t)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package directive
2+
3+
// Functions.
4+
5+
func f() {
6+
One()
7+
8+
new(T).Two()
9+
}
10+
11+
type T struct{}
12+
13+
//go:fix inline
14+
func One() int { return one } // want One:`goFixInline directive.One`
15+
16+
const one = 1
17+
18+
//go:fix inline
19+
func (T) Two() int { return 2 } // want Two:`goFixInline \(directive.T\).Two`
20+
21+
// Constants.
22+
23+
const Uno = 1
24+
25+
//go:fix inline
26+
const In1 = Uno // want In1: `goFixInline const "directive".Uno`
27+
28+
const (
29+
no1 = one
30+
31+
//go:fix inline
32+
In2 = one // want In2: `goFixInline const "directive".one`
33+
)
34+
35+
//go:fix inline
36+
const bad1 = 1 // want `invalid //go:fix inline directive: const value is not the name of another constant`
37+
38+
//go:fix inline
39+
const in5,
40+
in6,
41+
bad2 = one, one,
42+
one + 1 // want `invalid //go:fix inline directive: const value is not the name of another constant`
43+
44+
// Make sure we don't crash on iota consts, but still process the whole decl.
45+
//
46+
//go:fix inline
47+
const (
48+
a = iota // want `invalid //go:fix inline directive: const value is iota`
49+
b
50+
in7 = one
51+
)
52+
53+
const (
54+
x = 1
55+
//go:fix inline
56+
in8 = x
57+
)
58+
59+
//go:fix inline
60+
const in9 = iota // want `invalid //go:fix inline directive: const value is iota`
61+
62+
//go:fix inline
63+
type E = map[[Uno]string][]*T // want `invalid //go:fix inline directive: array types not supported`
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package golden
2+
3+
import "a/internal"
4+
5+
// Functions.
6+
7+
func f() {
8+
One()
9+
10+
new(T).Two()
11+
}
12+
13+
type T struct{}
14+
15+
//go:fix inline
16+
func One() int { return one }
17+
18+
const one = 1
19+
20+
//go:fix inline
21+
func (T) Two() int { return 2 }
22+
23+
// Constants.
24+
25+
const Uno = 1
26+
27+
//go:fix inline
28+
const In1 = Uno // want In1: `goFixInline const "a".Uno`
29+
30+
const (
31+
no1 = one
32+
33+
//go:fix inline
34+
In2 = one // want In2: `goFixInline const "a".one`
35+
)
36+
37+
//go:fix inline
38+
const bad1 = 1 // want `invalid //go:fix inline directive: const value is not the name of another constant`
39+
40+
//go:fix inline
41+
const in5,
42+
in6,
43+
bad2 = one, one,
44+
one + 1 // want `invalid //go:fix inline directive: const value is not the name of another constant`
45+
46+
// Make sure we don't crash on iota consts, but still process the whole decl.
47+
//
48+
//go:fix inline
49+
const (
50+
a = iota // want `invalid //go:fix inline directive: const value is iota`
51+
b
52+
in7 = one
53+
)
54+
55+
const (
56+
x = 1
57+
//go:fix inline
58+
in8 = x
59+
)
60+
61+
//go:fix inline
62+
const a = iota // want `invalid //go:fix inline directive: const value is iota`
63+
64+
//go:fix inline
65+
type E = map[[Uno]string][]*T // want `invalid //go:fix inline directive: array types not supported`
66+
67+
// literal array lengths are OK
68+
//
69+
//go:fix inline
70+
type EL = map[[2]string][]*T // want EL: `goFixInline alias`
71+

0 commit comments

Comments
 (0)