1
2
3
4
5 package modernize
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/token"
11 "go/types"
12
13 "golang.org/x/tools/go/analysis"
14 "golang.org/x/tools/go/analysis/passes/inspect"
15 "golang.org/x/tools/go/ast/edge"
16 "golang.org/x/tools/go/ast/inspector"
17 "golang.org/x/tools/internal/analysis/analyzerutil"
18 "golang.org/x/tools/internal/astutil"
19 "golang.org/x/tools/internal/goplsexport"
20 "golang.org/x/tools/internal/refactor"
21 "golang.org/x/tools/internal/typesinternal"
22 "golang.org/x/tools/internal/versions"
23 )
24
25
26
27
28
29
30
31
32 var unsafeFuncsAnalyzer = &analysis.Analyzer{
33 Name: "unsafefuncs",
34 Doc: analyzerutil.MustExtractDoc(doc, "unsafefuncs"),
35 Requires: []*analysis.Analyzer{inspect.Analyzer},
36 Run: unsafefuncs,
37 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#unsafefuncs",
38 }
39
40 func init() {
41
42 goplsexport.UnsafeFuncsModernizer = unsafeFuncsAnalyzer
43 }
44
45 func unsafefuncs(pass *analysis.Pass) (any, error) {
46
47
48
49 if !typesinternal.Imports(pass.Pkg, "unsafe") {
50 return nil, nil
51 }
52
53 var (
54 inspect = pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
55 info = pass.TypesInfo
56 tUnsafePointer = types.Typ[types.UnsafePointer]
57 )
58
59 isInteger := func(t types.Type) bool {
60 basic, ok := t.Underlying().(*types.Basic)
61 return ok && basic.Info()&types.IsInteger != 0
62 }
63
64
65
66 isConversion := func(curExpr inspector.Cursor) (t types.Type, x inspector.Cursor) {
67 e := curExpr.Node().(ast.Expr)
68 if conv, ok := ast.Unparen(e).(*ast.CallExpr); ok && len(conv.Args) == 1 {
69 if tv := pass.TypesInfo.Types[conv.Fun]; tv.IsType() {
70 return tv.Type, curExpr.ChildAt(edge.CallExpr_Args, 0)
71 }
72 }
73 return
74 }
75
76
77
78
79
80
81
82
83
84
85
86
87 for curSum := range inspect.Root().Preorder((*ast.BinaryExpr)(nil)) {
88 if sum, ok := curSum.Node().(*ast.BinaryExpr); ok &&
89 sum.Op == token.ADD &&
90 types.Identical(info.TypeOf(sum.X), types.Typ[types.Uintptr]) &&
91 astutil.IsChildOf(curSum, edge.CallExpr_Args) {
92
93 curX := curSum.ChildAt(edge.BinaryExpr_X, -1)
94 curY := curSum.ChildAt(edge.BinaryExpr_Y, -1)
95
96
97 curResult := curSum.Parent()
98 if t, _ := isConversion(curResult); !(t != nil && types.Identical(t, tUnsafePointer)) {
99 continue
100 }
101
102
103
104 _, curPtr := isConversion(curX)
105 if !astutil.CursorValid(curPtr) {
106 continue
107 }
108 ptr := curPtr.Node().(ast.Expr)
109 if !types.Identical(info.TypeOf(ptr), tUnsafePointer) {
110 continue
111 }
112
113
114 file := astutil.EnclosingFile(curSum)
115 if !analyzerutil.FileUsesGoVersion(pass, file, versions.Go1_17) {
116 continue
117 }
118
119
120 unsafedot, edits := refactor.AddImport(info, file, "unsafe", "unsafe", "Add", sum.Pos())
121
122
123
124
125 edits = append(edits, deleteConv(curResult)...)
126
127
128
129
130 edits = append(edits, []analysis.TextEdit{
131 {
132 Pos: sum.Pos(),
133 End: ptr.Pos(),
134 NewText: fmt.Appendf(nil, "%sAdd(", unsafedot),
135 },
136 {
137 Pos: ptr.End(),
138 End: sum.Y.Pos(),
139 NewText: []byte(", "),
140 },
141 {
142 Pos: sum.Y.End(),
143 End: sum.Y.End(),
144 NewText: []byte(")"),
145 },
146 }...)
147
148
149
150
151
152
153
154 if t, _ := isConversion(curY); t != nil && isInteger(t) {
155 edits = append(edits, deleteConv(curY)...)
156 }
157
158 pass.Report(analysis.Diagnostic{
159 Pos: sum.Pos(),
160 End: sum.End(),
161 Message: "pointer + integer can be simplified using unsafe.Add",
162 SuggestedFixes: []analysis.SuggestedFix{{
163 Message: "Simplify pointer addition using unsafe.Add",
164 TextEdits: edits,
165 }},
166 })
167 }
168 }
169
170 return nil, nil
171 }
172
173
174 func deleteConv(cur inspector.Cursor) []analysis.TextEdit {
175 conv := cur.Node().(*ast.CallExpr)
176
177 usesPrec := func(n ast.Node) bool {
178 switch n.(type) {
179 case *ast.BinaryExpr, *ast.UnaryExpr:
180 return true
181 }
182 return false
183 }
184
185
186
187 if usesPrec(cur.Parent().Node()) && usesPrec(conv.Args[0]) {
188
189
190
191 return []analysis.TextEdit{{
192 Pos: conv.Fun.Pos(),
193 End: conv.Fun.End(),
194 }}
195 }
196
197
198
199
200 return []analysis.TextEdit{
201 {
202 Pos: conv.Pos(),
203 End: conv.Args[0].Pos(),
204 },
205 {
206 Pos: conv.Args[0].End(),
207 End: conv.End(),
208 },
209 }
210 }
211
View as plain text