1
2
3
4
5
6
7 package analysisinternal
8
9 import (
10 "bytes"
11 "fmt"
12 "go/ast"
13 "go/token"
14 "go/types"
15 "os"
16 "strconv"
17
18 "golang.org/x/tools/go/analysis"
19 "golang.org/x/tools/internal/aliases"
20 )
21
22 func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos {
23
24 offset, end := fset.PositionFor(start, false).Offset, start
25 if offset >= len(src) {
26 return end
27 }
28 if width := bytes.IndexAny(src[offset:], " \n,():;[]+-*"); width > 0 {
29 end = start + token.Pos(width)
30 }
31 return end
32 }
33
34 func ZeroValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
35
36 under := aliases.Unalias(typ)
37
38
39 if n, ok := under.(*types.Named); ok {
40 under = n.Underlying()
41 }
42 switch under := under.(type) {
43 case *types.Basic:
44 switch {
45 case under.Info()&types.IsNumeric != 0:
46 return &ast.BasicLit{Kind: token.INT, Value: "0"}
47 case under.Info()&types.IsBoolean != 0:
48 return &ast.Ident{Name: "false"}
49 case under.Info()&types.IsString != 0:
50 return &ast.BasicLit{Kind: token.STRING, Value: `""`}
51 default:
52 panic(fmt.Sprintf("unknown basic type %v", under))
53 }
54 case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice, *types.Array:
55 return ast.NewIdent("nil")
56 case *types.Struct:
57 texpr := TypeExpr(f, pkg, typ)
58 if texpr == nil {
59 return nil
60 }
61 return &ast.CompositeLit{
62 Type: texpr,
63 }
64 }
65 return nil
66 }
67
68
69
70 func IsZeroValue(expr ast.Expr) bool {
71 switch e := expr.(type) {
72 case *ast.BasicLit:
73 return e.Value == "0" || e.Value == `""`
74 case *ast.Ident:
75 return e.Name == "nil" || e.Name == "false"
76 default:
77 return false
78 }
79 }
80
81
82
83
84 func TypeExpr(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr {
85 switch t := typ.(type) {
86 case *types.Basic:
87 switch t.Kind() {
88 case types.UnsafePointer:
89 return &ast.SelectorExpr{X: ast.NewIdent("unsafe"), Sel: ast.NewIdent("Pointer")}
90 default:
91 return ast.NewIdent(t.Name())
92 }
93 case *types.Pointer:
94 x := TypeExpr(f, pkg, t.Elem())
95 if x == nil {
96 return nil
97 }
98 return &ast.UnaryExpr{
99 Op: token.MUL,
100 X: x,
101 }
102 case *types.Array:
103 elt := TypeExpr(f, pkg, t.Elem())
104 if elt == nil {
105 return nil
106 }
107 return &ast.ArrayType{
108 Len: &ast.BasicLit{
109 Kind: token.INT,
110 Value: fmt.Sprintf("%d", t.Len()),
111 },
112 Elt: elt,
113 }
114 case *types.Slice:
115 elt := TypeExpr(f, pkg, t.Elem())
116 if elt == nil {
117 return nil
118 }
119 return &ast.ArrayType{
120 Elt: elt,
121 }
122 case *types.Map:
123 key := TypeExpr(f, pkg, t.Key())
124 value := TypeExpr(f, pkg, t.Elem())
125 if key == nil || value == nil {
126 return nil
127 }
128 return &ast.MapType{
129 Key: key,
130 Value: value,
131 }
132 case *types.Chan:
133 dir := ast.ChanDir(t.Dir())
134 if t.Dir() == types.SendRecv {
135 dir = ast.SEND | ast.RECV
136 }
137 value := TypeExpr(f, pkg, t.Elem())
138 if value == nil {
139 return nil
140 }
141 return &ast.ChanType{
142 Dir: dir,
143 Value: value,
144 }
145 case *types.Signature:
146 var params []*ast.Field
147 for i := 0; i < t.Params().Len(); i++ {
148 p := TypeExpr(f, pkg, t.Params().At(i).Type())
149 if p == nil {
150 return nil
151 }
152 params = append(params, &ast.Field{
153 Type: p,
154 Names: []*ast.Ident{
155 {
156 Name: t.Params().At(i).Name(),
157 },
158 },
159 })
160 }
161 if t.Variadic() {
162 last := params[len(params)-1]
163 last.Type = &ast.Ellipsis{Elt: last.Type.(*ast.ArrayType).Elt}
164 }
165 var returns []*ast.Field
166 for i := 0; i < t.Results().Len(); i++ {
167 r := TypeExpr(f, pkg, t.Results().At(i).Type())
168 if r == nil {
169 return nil
170 }
171 returns = append(returns, &ast.Field{
172 Type: r,
173 })
174 }
175 return &ast.FuncType{
176 Params: &ast.FieldList{
177 List: params,
178 },
179 Results: &ast.FieldList{
180 List: returns,
181 },
182 }
183 case interface{ Obj() *types.TypeName }:
184 if t.Obj().Pkg() == nil {
185 return ast.NewIdent(t.Obj().Name())
186 }
187 if t.Obj().Pkg() == pkg {
188 return ast.NewIdent(t.Obj().Name())
189 }
190 pkgName := t.Obj().Pkg().Name()
191
192
193 for _, cand := range f.Imports {
194 if path, _ := strconv.Unquote(cand.Path.Value); path == t.Obj().Pkg().Path() {
195 if cand.Name != nil && cand.Name.Name != "" {
196 pkgName = cand.Name.Name
197 }
198 }
199 }
200 if pkgName == "." {
201 return ast.NewIdent(t.Obj().Name())
202 }
203 return &ast.SelectorExpr{
204 X: ast.NewIdent(pkgName),
205 Sel: ast.NewIdent(t.Obj().Name()),
206 }
207 case *types.Struct:
208 return ast.NewIdent(t.String())
209 case *types.Interface:
210 return ast.NewIdent(t.String())
211 default:
212 return nil
213 }
214 }
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231 func StmtToInsertVarBefore(path []ast.Node) ast.Stmt {
232 enclosingIndex := -1
233 for i, p := range path {
234 if _, ok := p.(ast.Stmt); ok {
235 enclosingIndex = i
236 break
237 }
238 }
239 if enclosingIndex == -1 {
240 return nil
241 }
242 enclosingStmt := path[enclosingIndex]
243 switch enclosingStmt.(type) {
244 case *ast.IfStmt:
245
246
247
248 return baseIfStmt(path, enclosingIndex)
249 case *ast.CaseClause:
250
251
252 for i := enclosingIndex + 1; i < len(path); i++ {
253 if node, ok := path[i].(*ast.SwitchStmt); ok {
254 return node
255 } else if node, ok := path[i].(*ast.TypeSwitchStmt); ok {
256 return node
257 }
258 }
259 }
260 if len(path) <= enclosingIndex+1 {
261 return enclosingStmt.(ast.Stmt)
262 }
263
264 switch expr := path[enclosingIndex+1].(type) {
265 case *ast.IfStmt:
266
267 return baseIfStmt(path, enclosingIndex+1)
268 case *ast.ForStmt:
269 if expr.Init == enclosingStmt || expr.Post == enclosingStmt {
270 return expr
271 }
272 }
273 return enclosingStmt.(ast.Stmt)
274 }
275
276
277
278 func baseIfStmt(path []ast.Node, index int) ast.Stmt {
279 stmt := path[index]
280 for i := index + 1; i < len(path); i++ {
281 if node, ok := path[i].(*ast.IfStmt); ok && node.Else == stmt {
282 stmt = node
283 continue
284 }
285 break
286 }
287 return stmt.(ast.Stmt)
288 }
289
290
291
292 func WalkASTWithParent(n ast.Node, f func(n ast.Node, parent ast.Node) bool) {
293 var ancestors []ast.Node
294 ast.Inspect(n, func(n ast.Node) (recurse bool) {
295 if n == nil {
296 ancestors = ancestors[:len(ancestors)-1]
297 return false
298 }
299
300 var parent ast.Node
301 if len(ancestors) > 0 {
302 parent = ancestors[len(ancestors)-1]
303 }
304 ancestors = append(ancestors, n)
305 return f(n, parent)
306 })
307 }
308
309
310
311
312
313 func MatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *types.Info, pkg *types.Package) map[types.Type][]string {
314
315
316 matches := make(map[types.Type][]string)
317 for _, typ := range typs {
318 if typ == nil {
319 continue
320 }
321 matches[typ] = nil
322 }
323
324 seen := map[types.Object]struct{}{}
325 ast.Inspect(node, func(n ast.Node) bool {
326 if n == nil {
327 return false
328 }
329
330
331
332
333
334
335 if assign, ok := n.(*ast.AssignStmt); ok && pos > assign.Pos() && pos <= assign.End() {
336 return false
337 }
338 if n.End() > pos {
339 return n.Pos() <= pos
340 }
341 ident, ok := n.(*ast.Ident)
342 if !ok || ident.Name == "_" {
343 return true
344 }
345 obj := info.Defs[ident]
346 if obj == nil || obj.Type() == nil {
347 return true
348 }
349 if _, ok := obj.(*types.TypeName); ok {
350 return true
351 }
352
353 if _, ok = seen[obj]; ok {
354 return true
355 }
356 seen[obj] = struct{}{}
357
358
359 innerScope := pkg.Scope().Innermost(pos)
360 if innerScope == nil {
361 return true
362 }
363 _, foundObj := innerScope.LookupParent(ident.Name, pos)
364 if foundObj != obj {
365 return true
366 }
367
368
369 if names, ok := matches[obj.Type()]; ok {
370 matches[obj.Type()] = append(names, ident.Name)
371 } else {
372
373
374
375 for typ := range matches {
376 if equivalentTypes(obj.Type(), typ) {
377 matches[typ] = append(matches[typ], ident.Name)
378 }
379 }
380 }
381 return true
382 })
383 return matches
384 }
385
386 func equivalentTypes(want, got types.Type) bool {
387 if types.Identical(want, got) {
388 return true
389 }
390
391 if rhs, ok := want.(*types.Basic); ok && rhs.Info()&types.IsUntyped > 0 {
392 if lhs, ok := got.Underlying().(*types.Basic); ok {
393 return rhs.Info()&types.IsConstType == lhs.Info()&types.IsConstType
394 }
395 }
396 return types.AssignableTo(want, got)
397 }
398
399
400 func MakeReadFile(pass *analysis.Pass) func(filename string) ([]byte, error) {
401 return func(filename string) ([]byte, error) {
402 if err := checkReadable(pass, filename); err != nil {
403 return nil, err
404 }
405 return os.ReadFile(filename)
406 }
407 }
408
409
410 func checkReadable(pass *analysis.Pass, filename string) error {
411 if slicesContains(pass.OtherFiles, filename) ||
412 slicesContains(pass.IgnoredFiles, filename) {
413 return nil
414 }
415 for _, f := range pass.Files {
416
417 if pass.Fset.File(f.Pos()).Name() == filename {
418 return nil
419 }
420 }
421 return fmt.Errorf("Pass.ReadFile: %s is not among OtherFiles, IgnoredFiles, or names of Files", filename)
422 }
423
424
425 func slicesContains[S ~[]E, E comparable](slice S, x E) bool {
426 for _, elem := range slice {
427 if elem == x {
428 return true
429 }
430 }
431 return false
432 }
433
View as plain text