-
Notifications
You must be signed in to change notification settings - Fork 0
/
walker.go
116 lines (100 loc) · 2.42 KB
/
walker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package main
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/packages"
)
type walker struct {
ctxt *context
pkg *packages.Package
visit func(*[]*nameChecker, *ast.Ident)
}
func (w *walker) walkFunc(typ *ast.FuncType, body *ast.BlockStmt) {
w.walkFieldList(&w.ctxt.checkers.param, typ.Params.List)
// TODO(Quasilyte): add results scope and walk them?
if body != nil {
w.walkLocalNames(body)
}
}
func (w *walker) walkNames(f *ast.File) {
// TODO(Quasilyte): walk function param names
// both in anonymous functions and in interface decls.
for _, decl := range f.Decls {
switch decl := decl.(type) {
case *ast.FuncDecl:
if decl.Recv != nil {
w.walkFieldList(&w.ctxt.checkers.receiver, decl.Recv.List)
}
w.walkFunc(decl.Type, decl.Body)
case *ast.GenDecl:
w.walkGenDecl(&w.ctxt.checkers.global, decl)
}
}
}
func (w *walker) walkFieldList(checkers *[]*nameChecker, fields []*ast.Field) {
for _, field := range fields {
for _, id := range field.Names {
w.visit(checkers, id)
}
}
}
func (w *walker) walkLocalNames(b *ast.BlockStmt) {
ast.Inspect(b, func(x ast.Node) bool {
switch x := x.(type) {
case *ast.FuncLit:
w.walkFunc(x.Type, x.Body)
return false
case *ast.AssignStmt:
if x.Tok != token.DEFINE {
return false
}
for _, lhs := range x.Lhs {
id, ok := lhs.(*ast.Ident)
if !ok || w.pkg.TypesInfo.Defs[id] == nil {
continue
}
w.visit(&w.ctxt.checkers.local, id)
}
return false
case *ast.GenDecl:
w.walkGenDecl(&w.ctxt.checkers.local, x)
return false
}
return true
})
}
func (w *walker) walkGenDecl(checkers *[]*nameChecker, decl *ast.GenDecl) {
switch decl.Tok {
case token.VAR, token.CONST:
for _, spec := range decl.Specs {
spec := spec.(*ast.ValueSpec)
w.walkIdentList(checkers, spec.Names)
}
case token.TYPE:
for _, spec := range decl.Specs {
spec := spec.(*ast.TypeSpec)
w.walkTypeExprNames(spec.Type)
}
}
}
func (w *walker) walkIdentList(checkers *[]*nameChecker, idents []*ast.Ident) {
for _, id := range idents {
w.visit(checkers, id)
}
}
func (w *walker) walkTypeExprNames(e ast.Expr) {
n, ok := e.(*ast.StructType)
if !ok {
return
}
for _, field := range n.Fields.List {
if n, ok := field.Type.(*ast.StructType); ok {
// Anonymous struct type. Need to visit its fields.
w.walkTypeExprNames(n)
continue
}
for _, id := range field.Names {
w.visit(&w.ctxt.checkers.field, id)
}
}
}