-
Notifications
You must be signed in to change notification settings - Fork 226
Expand file tree
/
Copy pathNNode.java
More file actions
389 lines (336 loc) · 11 KB
/
NNode.java
File metadata and controls
389 lines (336 loc) · 11 KB
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
/**
* Copyright 2009, Google Inc. All rights reserved.
* Licensed to PSF under a Contributor Agreement.
*/
package org.python.indexer.ast;
import org.python.indexer.Indexer;
import org.python.indexer.IndexingException;
import org.python.indexer.NBinding;
import org.python.indexer.Scope;
import org.python.indexer.types.NClassType;
import org.python.indexer.types.NFuncType;
import org.python.indexer.types.NType;
import org.python.indexer.types.NUnionType;
import org.python.indexer.types.NUnknownType;
import java.util.List;
public abstract class NNode implements java.io.Serializable {
static final long serialVersionUID = 3682719481356964898L;
private int start = 0;
private int end = 1;
protected NNode parent = null;
/**
* This is marked transient to prevent serialization. We re-resolve ASTs
* after deserializing them. It is private to ensure that the type is never
* {@code null}, as much code in the indexer assumes this precondition.
*/
private transient NType type = Indexer.idx.builtins.None;
public NNode() {
}
public NNode(int start, int end) {
setStart(start);
setEnd(end);
}
public void setParent(NNode parent) {
this.parent = parent;
}
public NNode getParent() {
return parent;
}
public NNode getAstRoot() {
if (parent == null) {
return this;
}
return parent.getAstRoot();
}
public void setStart(int start) {
this.start = start;
}
public void setEnd(int end) {
this.end = end;
}
public int start() {
return start;
}
public int end() {
return end;
}
public int length() {
return end - start;
}
/**
* Utility alias for {@code getType().getTable()}.
*/
public Scope getTable() {
return getType().getTable();
}
/**
* Returns the type for this node. It is never {@code null}.
* If the node has not been resolved, the type will default to
* {@code Indexer.idx.builtins.None}.
*/
public NType getType() {
if (type == null) {
type = Indexer.idx.builtins.None;
}
return type;
}
/**
* Sets the type for the node.
* @param newType the new type
* @return {@code newType}
* @throws IllegalArgumentException if {@code newType} is {@code null}
*/
public NType setType(NType newType) {
if (newType == null) {
throw new IllegalArgumentException();
}
return type = newType;
}
/**
* Adds a new type for the node, creating a union of the previous type
* and the new type.
* @param newType the new type
* @return the resulting type for the node
* @throws IllegalArgumentException if {@code newType} is {@code null}
*/
public NType addType(NType newType) {
if (newType == null) {
throw new IllegalArgumentException();
}
return type = NUnionType.union(getType(), newType);
}
/**
* Returns {@code true} if this is a name-binding node. Includes functions/lambdas,
* function/lambda params, classes, assignments, imports, and implicit assignment via for
* statements and except clauses.
*
* @see <a href="http://www.python.org/dev/peps/pep-0227">PEP 227</a>
*/
public boolean bindsName() {
return false;
}
/**
* Called by resolver to bind names into the passed scope.
*/
protected void bindNames(Scope s) throws Exception {
throw new UnsupportedOperationException("Not a name-binding node type");
}
/**
* @return the path to the code that generated this AST
*/
public String getFile() {
return parent != null ? parent.getFile() : null;
}
public void addChildren(NNode... nodes) {
if (nodes != null) {
for (NNode n : nodes) {
if (n != null) {
n.setParent(this);
}
}
}
}
public void addChildren(List<? extends NNode> nodes) {
if (nodes != null) {
for (NNode n : nodes) {
if (n != null) {
n.setParent(this);
}
}
}
}
private static NType handleExceptionInResolve(NNode n, Throwable t) {
Indexer.idx.handleException("Unable to resolve: " + n + " in " + n.getFile(), t);
return new NUnknownType();
}
public static NType resolveExpr(NNode n, Scope s) {
if (n == null) {
return new NUnknownType();
}
// This try-catch enables error recovery when there are bugs in
// the indexer. Rather than unwinding all the way up to the module
// level (and failing to load the module), we record an error for this
// node and continue.
try {
NType result = n.resolve(s);
if (result == null) {
Indexer.idx.warn(n + " resolved to a null type");
return n.setType(new NUnknownType());
}
return result;
} catch (IndexingException ix) {
throw ix;
} catch (Exception x) {
return handleExceptionInResolve(n, x);
} catch (StackOverflowError soe) {
String msg = "Unable to resolve: " + n + " in " + n.getFile() + " (stack overflow)";
Indexer.idx.warn(msg);
return handleExceptionInResolve(n, soe);
}
}
/**
* Node should set the resolved type in its {@link #type} field
* and also return it.
*/
public NType resolve(Scope s) throws Exception {
return getType();
}
public boolean isCall() {
return this instanceof NCall;
}
public boolean isModule() {
return this instanceof NModule;
}
public boolean isClassDef() {
return false;
}
public boolean isFunctionDef() {
return false;
}
public boolean isLambda() {
return false;
}
public boolean isName() {
return this instanceof NName;
}
protected void visitNode(NNode n, NNodeVisitor v) {
if (n != null) {
n.visit(v);
}
}
protected void visitNodeList(List<? extends NNode> nodes, NNodeVisitor v) {
if (nodes != null) {
for (NNode n : nodes) {
if (n != null) {
n.visit(v);
}
}
}
}
/**
* Visits this node and optionally its children. <p>
*
* @param visitor the object to call with this node.
* If the visitor returns {@code true}, the node also
* passes its children to the visitor.
*/
public abstract void visit(NNodeVisitor visitor);
/**
* Returns the innermost enclosing scope for doing (non-attribute) name
* lookups. If the current node defines a scope, it returns the parent
* scope for name lookups.
*
* @return the enclosing function, class, instance, module or builtin scope.
* If this node has not yet been resolved, returns the builtin
* namespace.
*/
public Scope getEnclosingNamespace() {
if (parent == null || this.isModule()) {
return Indexer.idx.globaltable;
}
NNode up = this;
while ((up = up.parent) != null) {
if (up.isFunctionDef() || up.isClassDef() || up.isModule()) {
NType type = up.getType();
if (type == null || type.getTable() == null) {
return Indexer.idx.globaltable;
}
return type.getTable();
}
}
return Indexer.idx.globaltable;
}
protected void addWarning(String msg) {
Indexer.idx.putProblem(this, msg);
}
protected void addWarning(NNode loc, String msg) {
Indexer.idx.putProblem(loc, msg);
}
protected void addError(String msg) {
Indexer.idx.putProblem(this, msg);
}
protected void addError(NNode loc, String msg) {
Indexer.idx.putProblem(loc, msg);
}
/**
* Utility method to resolve every node in {@code nodes} and
* return the union of their types. If {@code nodes} is empty or
* {@code null}, returns a new {@link NUnknownType}.
*/
protected NType resolveListAsUnion(List<? extends NNode> nodes, Scope s) {
if (nodes == null || nodes.isEmpty()) {
return new NUnknownType();
}
NType result = null;
for (NNode node : nodes) {
NType nodeType = resolveExpr(node, s);
if (result == null) {
result = nodeType;
} else {
result = NUnionType.union(result, nodeType);
}
}
return result;
}
/**
* Resolves each element of a node list in the passed scope.
* Node list may be empty or {@code null}.
*/
protected void resolveList(List<? extends NNode> nodes, Scope s) {
if (nodes != null) {
for (NNode n : nodes) {
resolveExpr(n, s);
}
}
}
/**
* Assumes nodes are always traversed in increasing order of their start
* positions.
*/
static class DeepestOverlappingNodeFinder extends GenericNodeVisitor {
private int offset;
private NNode deepest;
public DeepestOverlappingNodeFinder(int offset) {
this.offset = offset;
}
/**
* Returns the deepest node overlapping the desired source offset.
* @return the node, or {@code null} if no node overlaps the offset
*/
public NNode getNode() {
return deepest;
}
@Override
public boolean dispatch(NNode node) {
// This node ends before the offset, so don't look inside it.
if (offset > node.end) {
return false; // don't traverse children, but do keep going
}
if (offset >= node.start) {
deepest = node;
return true; // visit kids
}
// this node starts after the offset, so we're done
throw new NNodeVisitor.StopIterationException();
}
}
/**
* Searches the AST for the deepest node that overlaps the specified source
* offset. Can be called from any node in the AST, as it traverses to the
* parent before beginning the search.
* @param sourceOffset the spot at which to look for a node
* @return the deepest AST node whose start is greater than or equal to the offset,
* and whose end is less than or equal to the offset. Returns {@code null}
* if no node overlaps {@code sourceOffset}.
*/
public NNode getDeepestNodeAtOffset(int sourceOffset) {
NNode ast = getAstRoot();
DeepestOverlappingNodeFinder finder = new DeepestOverlappingNodeFinder(sourceOffset);
try {
ast.visit(finder);
} catch (NNodeVisitor.StopIterationException six) {
// expected
}
return finder.getNode();
}
}