-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Expand file tree
/
Copy pathAstExtended.qll
More file actions
278 lines (218 loc) · 9.19 KB
/
AstExtended.qll
File metadata and controls
278 lines (218 loc) · 9.19 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
overlay[local]
module;
import python
private import semmle.python.internal.CachedStages
/** A syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
abstract class AstNode extends AstNode_ {
/*
* Special comment for documentation generation.
* All subclasses of `AstNode` that represent concrete syntax should have
* a comment of the form:
*/
/* syntax: ... */
/** Gets the scope that this node occurs in */
abstract Scope getScope();
/**
* Gets a flow node corresponding directly to this node.
* NOTE: For some statements and other purely syntactic elements,
* there may not be a `ControlFlowNode`
*/
cached
ControlFlowNode getAFlowNode() {
Stages::AST::ref() and
py_flow_bb_node(result, this, _, _)
}
/** Gets the location for this AST node */
cached
Location getLocation() { none() }
/**
* Whether this syntactic element is artificial, that is it is generated
* by the compiler and is not present in the source
*/
predicate isArtificial() { none() }
/**
* Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST
* and other experiments. The child-parent relation may not be meaningful.
* For a more meaningful relation in terms of dependency use
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
* Scope.getAStmt().
*/
cached
abstract AstNode getAChildNode();
/**
* Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST
* and other experiments. The child-parent relation may not be meaningful.
* For a more meaningful relation in terms of dependency use
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
* Scope.getAStmt() applied to the parent.
*/
cached
AstNode getParentNode() {
Stages::AST::ref() and
result.getAChildNode() = this
}
/** Whether this contains `inner` syntactically */
predicate contains(AstNode inner) { this.getAChildNode+() = inner }
pragma[nomagic]
private predicate containsInScope(AstNode inner, Scope scope) {
this.contains(inner) and
not inner instanceof Scope and
scope = this.getScope()
}
/** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */
predicate containsInScope(AstNode inner) { this.containsInScope(inner, inner.getScope()) }
}
/* Parents */
/** The parent of a `Function`. Internal implementation class */
class FunctionParent extends FunctionParent_ { }
/** The parent of an `Arguments` node. Internal implementation class */
class ArgumentsParent extends ArgumentsParent_ { }
/** The parent of an `ExprList`. Internal implementation class */
class ExprListParent extends ExprListParent_ { }
/** The parent of an `ExprContext`. Internal implementation class */
class ExprContextParent extends ExprContextParent_ { }
/** The parent of a `StmtList`. Internal implementation class */
class StmtListParent extends StmtListParent_ { }
/** The parent of a `StrList`. Internal implementation class */
class StrListParent extends StrListParent_ { }
/** The parent of an `Expr`. Internal implementation class */
class ExprParent extends ExprParent_ { }
/** The parent of a `PatternList`. Internal implementation class */
class PatternListParent extends PatternListParent_ { }
/** The parent of a `Pattern`. Internal implementation class */
class PatternParent extends PatternParent_ { }
class DictItem extends DictItem_, AstNode {
override string toString() { result = DictItem_.super.toString() }
override AstNode getAChildNode() { none() }
override Scope getScope() { none() }
}
/** A comprehension part, the 'for a in seq' part of [ a * a for a in seq ] */
class Comprehension extends Comprehension_, AstNode {
/** Gets the scope of this comprehension */
override Scope getScope() {
/* Comprehensions exists only in Python 2 list comprehensions, so their scope is that of the list comp. */
exists(ListComp l | this = l.getAGenerator() | result = l.getScope())
}
override string toString() { result = "Comprehension" }
override Location getLocation() {
Stages::AST::ref() and
result = Comprehension_.super.getLocation()
}
pragma[nomagic]
override AstNode getAChildNode() {
Stages::AST::ref() and
result = this.getASubExpression()
}
Expr getASubExpression() {
result = this.getIter() or
result = this.getAnIf() or
result = this.getTarget()
}
}
class BytesOrStr extends BytesOrStr_ { }
/**
* A part of a string literal formed by implicit concatenation.
* For example the string literal "abc" expressed in the source as `"a" "b" "c"`
* would be composed of three `StringPart`s.
*/
class StringPart extends StringPart_, AstNode {
override Scope getScope() {
exists(Bytes b | this = b.getAnImplicitlyConcatenatedPart() | result = b.getScope())
or
exists(Unicode u | this = u.getAnImplicitlyConcatenatedPart() | result = u.getScope())
}
override AstNode getAChildNode() { none() }
override string toString() { result = StringPart_.super.toString() }
override Location getLocation() { result = StringPart_.super.getLocation() }
/**
* Holds if the content of string `StringPart` is surrounded by
* a prefix (including a quote) of length `prefixLength` and
* a quote of length `quoteLength`.
*/
predicate contextSize(int prefixLength, int quoteLength) {
exists(int occurrenceOffset |
quoteLength = this.getText().regexpFind("\"{3}|\"{1}|'{3}|'{1}", 0, occurrenceOffset).length() and
prefixLength = occurrenceOffset + quoteLength
)
}
/**
* Gets the length of the content, that is the text between the prefix and the quote.
* See `context` for obtaining the prefix and the quote.
*/
int getContentLength() {
exists(int prefixLength, int quoteLength | this.contextSize(prefixLength, quoteLength) |
result = this.getText().length() - prefixLength - quoteLength
)
}
}
class StringPartList extends StringPartList_ { }
/* **** Lists ***/
/** A parameter list */
class ParameterList extends @py_parameter_list {
Function getParent() { py_parameter_lists(this, result) }
/** Gets a parameter */
Parameter getAnItem() {
/* Item can be a Name or a Tuple, both of which are expressions */
py_exprs(result, _, this, _)
}
/** Gets the nth parameter */
Parameter getItem(int index) {
/* Item can be a Name or a Tuple, both of which are expressions */
py_exprs(result, _, this, index)
}
string toString() { result = "ParameterList" }
}
/** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */
class ComprehensionList extends ComprehensionList_ { }
/** A list of expressions */
class ExprList extends ExprList_ {
/* syntax: Expr, ... */
}
/** A list of patterns */
class PatternList extends PatternList_ { }
class DictItemList extends DictItemList_ { }
class DictItemListParent extends DictItemListParent_ { }
/** A list of strings (the primitive type string not Bytes or Unicode) */
class StringList extends StringList_ { }
/** A list of aliases in an import statement */
class AliasList extends AliasList_ { }
/** A generic type parameter, as seen in function, class, and type alias definitions. */
class TypeParameter extends TypeParameter_, AstNode {
/** Gets a textual representation of this element */
override string toString() { result = TypeParameter_.super.toString() }
override AstNode getAChildNode() { none() }
override Scope getScope() {
exists(Function f | this = f.getATypeParameter() and result = f)
or
exists(ClassExpr c | this = c.getATypeParameter() and result = c.getInnerScope())
or
// For `TypeAlias`, this is not quite right. Instead, `TypeAlias`es should define their own scopes, cf. https://docs.python.org/3.12/reference/executionmodel.html#annotation-scopes
exists(TypeAlias t | this = t.getATypeParameter() and result = t.getScope())
}
/** Gets the location of this element */
override Location getLocation() { result = TypeParameter_.super.getLocation() }
}
/** A list of type parameters */
class TypeParameterList extends TypeParameterList_ { }
/** A parent of a `TypeParameterList`. Internal implementation class. */
class TypeParameterListParent extends TypeParameterListParent_ { }
/** A type alias statement, such as `type T[T1,T2] = T3`. */
class TypeAlias extends TypeAlias_, Stmt {
/** Gets the name of this type alias. */
override Name getName() { result = super.getName() }
}
/** A type variable, with an optional bound, such as `T1` and `T2` in `type T[T1, T2: T3] = T4`. */
class TypeVar extends TypeVar_, TypeParameter {
override Name getName() { result = super.getName() }
override Expr getAChildNode() { result in [this.getName(), this.getBound()] }
}
/** A type var tuple parameter, such as `*T1` in `type T[*T1] = T2`. */
class TypeVarTuple extends TypeVarTuple_, TypeParameter {
override Name getName() { result = super.getName() }
override Expr getAChildNode() { result = this.getName() }
}
/** A param spec parameter, such as `**T1` in `type T[**T1] = T2`. */
class ParamSpec extends ParamSpec_, TypeParameter {
override Name getName() { result = super.getName() }
override Expr getAChildNode() { result = this.getName() }
}