-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathMacro.qll
More file actions
306 lines (273 loc) · 10.6 KB
/
Macro.qll
File metadata and controls
306 lines (273 loc) · 10.6 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
import cpp
/**
* A macro. For example, the macro `MYMACRO` in the following code:
* ```
* #define MYMACRO 1
* ```
*/
class Macro extends PreprocessorDirective, @ppd_define {
/**
* Gets the head of this macro. For example, `MAX(x,y)` in
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
*/
override string getHead() { preproctext(underlyingElement(this), result, _) }
override string getAPrimaryQlClass() { result = "Macro" }
/**
* Gets the body of this macro. For example, `(((x)>(y))?(x):(y))` in
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
*/
string getBody() { preproctext(underlyingElement(this), _, result) }
/** Gets an invocation of this macro. */
MacroInvocation getAnInvocation() { result.getMacro() = this }
override string toString() {
if this.getBody() = ""
then result = "#define " + this.getHead()
else result = "#define " + this.getHead() + " " + this.getBody()
}
/**
* Gets the name of the macro. For example, `MAX` in
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
*/
string getName() { result = this.getHead().regexpCapture("([^(]*+).*", 1) }
/** Holds if the macro has name `name`. */
predicate hasName(string name) { this.getName() = name }
}
/**
* A macro access. For example:
* ```
* #ifdef MACRO1 // this line contains a MacroAccess
* int x = MACRO2; // this line contains a MacroAccess
* #endif
* ```
*
* See also `MacroInvocation`, which represents only macro accesses
* that are expanded (such as in the second line of the example above).
*/
class MacroAccess extends Locatable, @macroinvocation {
/** Gets the macro that is being accessed. */
Macro getMacro() { macroinvocations(underlyingElement(this), unresolveElement(result), _, _) }
/**
* Gets the location of the outermost macro access that triggered this macro
* access. This is equivalent to calling
* `this.getOutermostMacroAccess().getActualLocation()`. For example, the
* location of the invocation of `C` in `P(C)` will be the whole source range
* starting with `P` and ending with `)`.
*/
override Location getLocation() { result = this.getOutermostMacroAccess().getActualLocation() }
override string getAPrimaryQlClass() { result = "MacroAccess" }
/**
* Gets the location of this macro access. For a nested access, where
* `exists(this.getParentInvocation())`, this yields a location either inside
* a `#define` directive or inside an argument to another macro.
*/
Location getActualLocation() { macroinvocations(underlyingElement(this), _, result, _) }
/**
* Gets the parent macro invocation, if any. For example:
*
* ```
* 1: #define C 0
* 2: #define P C
* 3: static int x = P;
* ```
*
* The invocation of `P` on line 3 also invokes `C`. The invocation of
* `P` is the parent of the invocation of `C`.
*
* A macro invocation occurring in a macro argument often also establishes a
* parent relationship. This is due to the "call-by-name" evaluation order of
* C macros, where macro arguments are first substituted textually into the
* macro body before macro expansion is again performed on the body, invoking
* the macros present in the original macro argument. For example:
*
* ```
* 1: #define C 0
* 2: #define P(c) c + c
* 3: static int x = P(C);
* ```
*
* In this case, `P(C)` first expands to `C + C`, which triggers an
* invocation of `C` whose parent is the invocation of `P`. Had `c` not
* occurred in the body of `P`, there would have been no invocation of `C`.
* There is only a single invocation even though `c` occurs twice; this is an
* optimization for efficiency.
*/
MacroInvocation getParentInvocation() {
macroparent(underlyingElement(this), unresolveElement(result))
}
/**
* Gets the outermost `MacroAccess` along the chain of `getParentInvocation`.
* If `this` has no parent, the result will be `this` itself.
*/
MacroAccess getOutermostMacroAccess() {
if not exists(this.getParentInvocation())
then result = this
else result = this.getParentInvocation().getOutermostMacroAccess()
}
override string toString() { result = this.getMacro().getHead() }
/** Gets the name of the accessed macro. */
string getMacroName() { result = this.getMacro().getName() }
}
/**
* A macro invocation (macro access that is expanded). For example:
* ```
* #ifdef MACRO1
* int x = MACRO2; // this line contains a MacroInvocation
* #endif
* ```
*
* See also `MacroAccess`, which also represents macro accesses where the macro
* is checked but not expanded (such as in the first line of the example above).
*/
class MacroInvocation extends MacroAccess {
MacroInvocation() { macroinvocations(underlyingElement(this), _, _, 1) }
override string getAPrimaryQlClass() { result = "MacroInvocation" }
/**
* Gets an element that occurs in this macro invocation or a nested macro
* invocation.
*/
Locatable getAnExpandedElement() {
inmacroexpansion(unresolveElement(result), underlyingElement(this))
}
/**
* Gets an element that is (partially) affected by a macro
* invocation. This is a superset of the set of expanded elements and
* includes elements that are not completely enclosed by the expansion as
* well.
*/
Locatable getAnAffectedElement() {
inmacroexpansion(unresolveElement(result), underlyingElement(this))
or
macrolocationbind(underlyingElement(this), result.getLocation()) and this != result
}
/**
* Gets an element that is either completely in the macro expansion, or
* (if it is a statement) 'almost' in the macro expansion (for instance
* up to a trailing semicolon). Useful for common patterns in which
* macros are almost syntactically complete elements but not quite.
*/
Locatable getAGeneratedElement() {
result = this.getAnExpandedElement() or
result.(Stmt).getGeneratingMacro() = this
}
/**
* Gets a function that includes an expression that is affected by this macro
* invocation. If the macro expansion includes the end of one function and
* the beginning of another, this predicate will get both.
*/
Function getEnclosingFunction() {
result = this.getAnAffectedElement().(Expr).getEnclosingFunction()
}
/**
* Gets a top-level expression associated with this macro invocation,
* if any. Note that this predicate will fail if the top-level expanded
* element is not an expression (for example if it is a statement).
*
* This macro is intended to be used with macros that expand to a complete
* expression. In other cases, it may have multiple results or no results.
*/
Expr getExpr() {
result = this.getAnExpandedElement() and
not result.getParent() = this.getAnExpandedElement() and
not result instanceof Conversion
}
/**
* Gets the top-level statement associated with this macro invocation, if
* any. Note that this predicate will fail if the top-level expanded
* element is not a statement (for example if it is an expression).
*/
Stmt getStmt() {
result = this.getAnExpandedElement() and
not result.getParent() = this.getAnExpandedElement()
}
/**
* Gets the `i`th _unexpanded_ argument of this macro invocation, where the
* first argument has `i = 0`. The result has been expanded for macro
* parameters but _not_ for macro invocations. This means that for macro
* invocations not inside a `#define`, which can have no macro parameters in
* their arguments, the result is equivalent to what is in the source text,
* modulo whitespace.
*
* In the following code example, the argument of the outermost invocation is
* `ID(1)` in unexpanded form and `1` in expanded form.
*
* ```
* #define ID(x) x
* ID(ID(1))
* ```
*
* In the following example code, the last line contains an invocation of
* macro `A` and a child invocation of macro `ID`. The argument to `ID` is
* `1` in both unexpanded and expanded form because macro parameters (here,
* `x`) are expanded in both cases.
*
* ```
* #define ID(x) x
* #define A(x) ID(x)
* A(1)
* ```
*
* The `...` parameter in variadic macros counts as one parameter that always
* receives one argument, which may contain commas.
*
* Use `getExpandedArgument` to get the expanded form.
*/
string getUnexpandedArgument(int i) {
macro_argument_unexpanded(underlyingElement(this), i, result)
}
/** Gets the number of arguments for this macro invocation. */
int getNumberOfArguments() { result = count(int i | exists(this.getUnexpandedArgument(i)) | i) }
/**
* Gets the `i`th _expanded_ argument of this macro invocation, where the
* first argument has `i = 0`. The result has been expanded for macros _and_
* macro parameters. If the macro definition does not use this argument, the
* extractor will avoid computing the expanded form for efficiency, and the
* result will be "".
*
* See the documentation of `getUnexpandedArgument` for examples of the
* differences between expanded and unexpanded arguments.
*/
string getExpandedArgument(int i) { macro_argument_expanded(underlyingElement(this), i, result) }
}
/** Holds if `l` is the location of a macro. */
predicate macroLocation(Location l) { macrolocationbind(_, l) }
/** Holds if `element` is in the expansion of a macro. */
predicate inMacroExpansion(Locatable element) {
inmacroexpansion(unresolveElement(element), _)
or
macroLocation(element.getLocation()) and
not topLevelMacroAccess(element) and
not element.getLocation() instanceof UnknownLocation
}
/**
* Holds if `ma` is a `MacroAccess` that is not nested inside another
* macro invocation.
*/
private predicate topLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
/**
* Holds if `element` is in the expansion of a macro from
* a system header.
*/
predicate inSystemMacroExpansion(Locatable element) {
exists(MacroInvocation m |
element = m.getAnExpandedElement() and
not exists(m.getMacro().getLocation().getFile().getRelativePath())
)
}
/** Holds if `element` is affected by a macro. */
predicate affectedByMacro(Locatable element) {
inMacroExpansion(element) or
affectedbymacroexpansion(unresolveElement(element), _)
}
/** Holds if there is a macro invocation on line `line` of file `f`. */
predicate macroLine(File f, int line) {
exists(MacroInvocation mi, Location l |
l = mi.getLocation() and
l.getFile() = f and
(l.getStartLine() = line or l.getEndLine() = line)
)
}
/** Holds if there might be a macro invocation at location `l`. */
predicate possibleMacroLocation(Location l) {
macroLine(l.getFile(), l.getStartLine()) or
macroLine(l.getFile(), l.getEndLine())
}