diff options
Diffstat (limited to 'vendor/golang.org/x/tools/go/ast/inspector/inspector.go')
-rw-r--r-- | vendor/golang.org/x/tools/go/ast/inspector/inspector.go | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/vendor/golang.org/x/tools/go/ast/inspector/inspector.go b/vendor/golang.org/x/tools/go/ast/inspector/inspector.go new file mode 100644 index 0000000..3084508 --- /dev/null +++ b/vendor/golang.org/x/tools/go/ast/inspector/inspector.go | |||
@@ -0,0 +1,182 @@ | |||
1 | // Copyright 2018 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | // Package inspector provides helper functions for traversal over the | ||
6 | // syntax trees of a package, including node filtering by type, and | ||
7 | // materialization of the traversal stack. | ||
8 | // | ||
9 | // During construction, the inspector does a complete traversal and | ||
10 | // builds a list of push/pop events and their node type. Subsequent | ||
11 | // method calls that request a traversal scan this list, rather than walk | ||
12 | // the AST, and perform type filtering using efficient bit sets. | ||
13 | // | ||
14 | // Experiments suggest the inspector's traversals are about 2.5x faster | ||
15 | // than ast.Inspect, but it may take around 5 traversals for this | ||
16 | // benefit to amortize the inspector's construction cost. | ||
17 | // If efficiency is the primary concern, do not use Inspector for | ||
18 | // one-off traversals. | ||
19 | package inspector | ||
20 | |||
21 | // There are four orthogonal features in a traversal: | ||
22 | // 1 type filtering | ||
23 | // 2 pruning | ||
24 | // 3 postorder calls to f | ||
25 | // 4 stack | ||
26 | // Rather than offer all of them in the API, | ||
27 | // only a few combinations are exposed: | ||
28 | // - Preorder is the fastest and has fewest features, | ||
29 | // but is the most commonly needed traversal. | ||
30 | // - Nodes and WithStack both provide pruning and postorder calls, | ||
31 | // even though few clients need it, because supporting two versions | ||
32 | // is not justified. | ||
33 | // More combinations could be supported by expressing them as | ||
34 | // wrappers around a more generic traversal, but this was measured | ||
35 | // and found to degrade performance significantly (30%). | ||
36 | |||
37 | import ( | ||
38 | "go/ast" | ||
39 | ) | ||
40 | |||
41 | // An Inspector provides methods for inspecting | ||
42 | // (traversing) the syntax trees of a package. | ||
43 | type Inspector struct { | ||
44 | events []event | ||
45 | } | ||
46 | |||
47 | // New returns an Inspector for the specified syntax trees. | ||
48 | func New(files []*ast.File) *Inspector { | ||
49 | return &Inspector{traverse(files)} | ||
50 | } | ||
51 | |||
52 | // An event represents a push or a pop | ||
53 | // of an ast.Node during a traversal. | ||
54 | type event struct { | ||
55 | node ast.Node | ||
56 | typ uint64 // typeOf(node) | ||
57 | index int // 1 + index of corresponding pop event, or 0 if this is a pop | ||
58 | } | ||
59 | |||
60 | // Preorder visits all the nodes of the files supplied to New in | ||
61 | // depth-first order. It calls f(n) for each node n before it visits | ||
62 | // n's children. | ||
63 | // | ||
64 | // The types argument, if non-empty, enables type-based filtering of | ||
65 | // events. The function f if is called only for nodes whose type | ||
66 | // matches an element of the types slice. | ||
67 | func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) { | ||
68 | // Because it avoids postorder calls to f, and the pruning | ||
69 | // check, Preorder is almost twice as fast as Nodes. The two | ||
70 | // features seem to contribute similar slowdowns (~1.4x each). | ||
71 | |||
72 | mask := maskOf(types) | ||
73 | for i := 0; i < len(in.events); { | ||
74 | ev := in.events[i] | ||
75 | if ev.typ&mask != 0 { | ||
76 | if ev.index > 0 { | ||
77 | f(ev.node) | ||
78 | } | ||
79 | } | ||
80 | i++ | ||
81 | } | ||
82 | } | ||
83 | |||
84 | // Nodes visits the nodes of the files supplied to New in depth-first | ||
85 | // order. It calls f(n, true) for each node n before it visits n's | ||
86 | // children. If f returns true, Nodes invokes f recursively for each | ||
87 | // of the non-nil children of the node, followed by a call of | ||
88 | // f(n, false). | ||
89 | // | ||
90 | // The types argument, if non-empty, enables type-based filtering of | ||
91 | // events. The function f if is called only for nodes whose type | ||
92 | // matches an element of the types slice. | ||
93 | func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proceed bool)) { | ||
94 | mask := maskOf(types) | ||
95 | for i := 0; i < len(in.events); { | ||
96 | ev := in.events[i] | ||
97 | if ev.typ&mask != 0 { | ||
98 | if ev.index > 0 { | ||
99 | // push | ||
100 | if !f(ev.node, true) { | ||
101 | i = ev.index // jump to corresponding pop + 1 | ||
102 | continue | ||
103 | } | ||
104 | } else { | ||
105 | // pop | ||
106 | f(ev.node, false) | ||
107 | } | ||
108 | } | ||
109 | i++ | ||
110 | } | ||
111 | } | ||
112 | |||
113 | // WithStack visits nodes in a similar manner to Nodes, but it | ||
114 | // supplies each call to f an additional argument, the current | ||
115 | // traversal stack. The stack's first element is the outermost node, | ||
116 | // an *ast.File; its last is the innermost, n. | ||
117 | func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (proceed bool)) { | ||
118 | mask := maskOf(types) | ||
119 | var stack []ast.Node | ||
120 | for i := 0; i < len(in.events); { | ||
121 | ev := in.events[i] | ||
122 | if ev.index > 0 { | ||
123 | // push | ||
124 | stack = append(stack, ev.node) | ||
125 | if ev.typ&mask != 0 { | ||
126 | if !f(ev.node, true, stack) { | ||
127 | i = ev.index | ||
128 | stack = stack[:len(stack)-1] | ||
129 | continue | ||
130 | } | ||
131 | } | ||
132 | } else { | ||
133 | // pop | ||
134 | if ev.typ&mask != 0 { | ||
135 | f(ev.node, false, stack) | ||
136 | } | ||
137 | stack = stack[:len(stack)-1] | ||
138 | } | ||
139 | i++ | ||
140 | } | ||
141 | } | ||
142 | |||
143 | // traverse builds the table of events representing a traversal. | ||
144 | func traverse(files []*ast.File) []event { | ||
145 | // Preallocate approximate number of events | ||
146 | // based on source file extent. | ||
147 | // This makes traverse faster by 4x (!). | ||
148 | var extent int | ||
149 | for _, f := range files { | ||
150 | extent += int(f.End() - f.Pos()) | ||
151 | } | ||
152 | // This estimate is based on the net/http package. | ||
153 | events := make([]event, 0, extent*33/100) | ||
154 | |||
155 | var stack []event | ||
156 | for _, f := range files { | ||
157 | ast.Inspect(f, func(n ast.Node) bool { | ||
158 | if n != nil { | ||
159 | // push | ||
160 | ev := event{ | ||
161 | node: n, | ||
162 | typ: typeOf(n), | ||
163 | index: len(events), // push event temporarily holds own index | ||
164 | } | ||
165 | stack = append(stack, ev) | ||
166 | events = append(events, ev) | ||
167 | } else { | ||
168 | // pop | ||
169 | ev := stack[len(stack)-1] | ||
170 | stack = stack[:len(stack)-1] | ||
171 | |||
172 | events[ev.index].index = len(events) + 1 // make push refer to pop | ||
173 | |||
174 | ev.index = 0 // turn ev into a pop event | ||
175 | events = append(events, ev) | ||
176 | } | ||
177 | return true | ||
178 | }) | ||
179 | } | ||
180 | |||
181 | return events | ||
182 | } | ||