1
2
3
4
5
6
7
8
9
10
11
12 package gofixdirective
13
14
15
16 import (
17 "go/ast"
18 "go/token"
19 "go/types"
20
21 "golang.org/x/tools/go/analysis"
22 "golang.org/x/tools/go/ast/inspector"
23 internalastutil "golang.org/x/tools/internal/astutil"
24 )
25
26
27 type Handler interface {
28 HandleFunc(*ast.FuncDecl)
29 HandleAlias(*ast.TypeSpec)
30 HandleConst(name, rhs *ast.Ident)
31 }
32
33
34
35
36 func Find(pass *analysis.Pass, root inspector.Cursor, h Handler) {
37 for cur := range root.Preorder((*ast.FuncDecl)(nil), (*ast.GenDecl)(nil)) {
38 switch decl := cur.Node().(type) {
39 case *ast.FuncDecl:
40 findFunc(decl, h)
41
42 case *ast.GenDecl:
43 if decl.Tok != token.CONST && decl.Tok != token.TYPE {
44 continue
45 }
46 declInline := hasFixInline(decl.Doc)
47
48 for _, spec := range decl.Specs {
49 switch spec := spec.(type) {
50 case *ast.TypeSpec:
51 findAlias(pass, spec, declInline, h)
52
53 case *ast.ValueSpec:
54 findConst(pass, spec, declInline, h)
55 }
56 }
57 }
58 }
59 }
60
61 func findFunc(decl *ast.FuncDecl, h Handler) {
62 if !hasFixInline(decl.Doc) {
63 return
64 }
65 if h != nil {
66 h.HandleFunc(decl)
67 }
68 }
69
70 func findAlias(pass *analysis.Pass, spec *ast.TypeSpec, declInline bool, h Handler) {
71 if !declInline && !hasFixInline(spec.Doc) {
72 return
73 }
74 if !spec.Assign.IsValid() {
75 pass.Reportf(spec.Pos(), "invalid //go:fix inline directive: not a type alias")
76 return
77 }
78
79
80
81
82
83
84
85
86
87 for n := range ast.Preorder(spec.Type) {
88 if ar, ok := n.(*ast.ArrayType); ok && ar.Len != nil {
89
90 if lit, ok := ast.Unparen(ar.Len).(*ast.BasicLit); ok && lit.Kind == token.INT {
91 continue
92 }
93 pass.Reportf(spec.Pos(), "invalid //go:fix inline directive: array types not supported")
94 return
95 }
96 }
97 if h != nil {
98 h.HandleAlias(spec)
99 }
100 }
101
102 func findConst(pass *analysis.Pass, spec *ast.ValueSpec, declInline bool, h Handler) {
103 specInline := hasFixInline(spec.Doc)
104 if declInline || specInline {
105 for i, nameIdent := range spec.Names {
106 if i >= len(spec.Values) {
107
108 break
109 }
110 var rhsIdent *ast.Ident
111 switch val := spec.Values[i].(type) {
112 case *ast.Ident:
113
114 if pass.TypesInfo.Uses[val] == builtinIota {
115 pass.Reportf(val.Pos(), "invalid //go:fix inline directive: const value is iota")
116 return
117 }
118 rhsIdent = val
119 case *ast.SelectorExpr:
120 rhsIdent = val.Sel
121 default:
122 pass.Reportf(val.Pos(), "invalid //go:fix inline directive: const value is not the name of another constant")
123 return
124 }
125 if h != nil {
126 h.HandleConst(nameIdent, rhsIdent)
127 }
128 }
129 }
130 }
131
132
133
134 func hasFixInline(cg *ast.CommentGroup) bool {
135 for _, d := range internalastutil.Directives(cg) {
136 if d.Tool == "go" && d.Name == "fix" && d.Args == "inline" {
137 return true
138 }
139 }
140 return false
141 }
142
143 var builtinIota = types.Universe.Lookup("iota")
144
View as plain text