-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathDependency.qll
More file actions
502 lines (468 loc) · 17.3 KB
/
Dependency.qll
File metadata and controls
502 lines (468 loc) · 17.3 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
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
import semmle.code.cpp.Element
import semmle.code.cpp.Declaration
import semmle.code.cpp.Function
import semmle.code.cpp.Variable
/**
* Options that control the dependencies generated by
* this library.
*/
class DependencyOptions extends string {
DependencyOptions() { this = "DependencyOptions" }
/**
* Holds if dependencies should only be generated in templates rather than
* in both templates and instantiations, where possible. This is expensive
* to compute, but tends to produce dependencies that are easier to read.
*/
cached
predicate preferTemplateDeps() { any() }
}
/**
* Gets the `DependencyOptions`.
*/
DependencyOptions getDependencyOptions() { any() }
/**
* An Element that can be the source of a transitive dependency. This is any
* Element that is not in a template instantiation, plus declarations of template
* specializations (even though they are technically in an instantiation) because
* we need to generate (at least) a dependency from them to the general declaration.
*/
class DependsSource extends Element {
DependsSource() {
// not inside a template instantiation
not this.isFromTemplateInstantiation(_) or
// allow DeclarationEntrys of template specializations
this.(DeclarationEntry).getDeclaration().(Function).isConstructedFrom(_) or
this.(DeclarationEntry).getDeclaration().(Class).isConstructedFrom(_)
}
}
/**
* A program element which can be the target of inter-function or inter-file dependencies.
*
* This is the union of Declaration, DeclarationEntry and Macro, minus various kinds of declaration:
* * FriendDecl is not included, as a friend declaration cannot be the target of a dependency (nor, as it happens, can they be a source).
* * TemplateParameter and related UserTypes are not included, as they are intrinsic sub-components of their associated template.
* * Template instantiations are excluded, as the template itself is more useful as a dependency target.
* * Stack variables and local types are excluded, as they are lexically tied to their enclosing function, and intra-function dependencies
* can only be inter-file dependencies in pathological cases.
* * Builtin functions and macros are excluded, as dependencies on them do not translate to inter-file dependencies (note that static functions
* and declarations within anonymous namespaces cannot be excluded for this reason, as the declaration can be in a header).
* * DeclarationEntrys are only needed if they're not definitions, for the definition to declaration dependency.
*/
class Symbol extends DependsSource {
Symbol() {
(
exists(EnumConstant ec | this = ec and not ec.getDeclaringEnum() instanceof LocalEnum)
or
this instanceof Macro and this.getFile().getAbsolutePath() != ""
or
this instanceof DeclarationEntry and
not this.(VariableDeclarationEntry).getVariable() instanceof LocalScopeVariable and
not this.(FunctionDeclarationEntry).getFunction() instanceof BuiltInFunction and
not this.(TypeDeclarationEntry).getType() instanceof LocalEnum and
not this.(TypeDeclarationEntry).getType() instanceof LocalClass and
not this.(TypeDeclarationEntry).getType() instanceof LocalTypedefType and
not this.(TypeDeclarationEntry).getType() instanceof TypeTemplateParameter
or
this instanceof NamespaceDeclarationEntry
)
}
/**
* Gets an element which depends upon this symbol.
*
* To a first approximation, dependent elements can be thought of as occurrences of the symbol's name: instances of `VariableAccess`
* for `Variable` symbols, instances of `MacroInvocation` for `Macro` symbols, and so on.
*
* category:
* 1 - C/C++ compile-time dependency
* 2 - C/C++ link-time dependency (or transitive dependency with a link-time component)
*/
cached
Element getADependentElement(int category) { dependsOnFull(result, this, category) }
}
/**
* Associates a Declaration with it's DeclarationEntries, or (for a template
* instantiation) with the DeclarationEntries of its template.
*/
cached
predicate getDeclarationEntries(Declaration decl, DeclarationEntry de) {
(
decl = de.getDeclaration() or
decl.(Function).isConstructedFrom(de.getDeclaration()) or
decl.(Class).isConstructedFrom(de.getDeclaration())
) and
// ParameterDeclarationEntries are special, as (a) they can only be accessed
// from within the definition, and (b) non-definition PDEs may be commonly
// included. Thus, for PDEs, we point only to the definition.
(de instanceof ParameterDeclarationEntry implies de.isDefinition())
}
/**
* A 'simple' dependency from src to dest. This type of dependency
* does not make any special account of templates.
*
* Consider using Symbol.getADependentElement() rather than directly
* accessing this predicate.
*/
predicate dependsOnSimple(Element src, Element dest) {
dependsOnSimpleInline(src, dest) or
dependency_macroUse(src, dest)
}
/**
* A 'simple' dependency that might be inlined.
*/
private predicate dependsOnSimpleInline(Element src, Element dest) {
dependency_functionUse(src, dest) or
dependency_typeUse(src, dest) or
dependency_variableUse(src, dest) or
dependency_usingDeclaration(src, dest) or
dependency_usingNamespace(src, dest) or
dependency_enumConstantUse(src, dest) or
dependency_outOfLineDeclaration(src, dest) or
dependency_outOfLineInitializer(src, dest) or
dependency_functionSpecialization(src, dest) or
dependency_classSpecialization(src, dest)
}
/**
* Holds if a simple, non-template dependency exists between two Locations
* specified by the parameters.
*/
private predicate dependsLocation(
File f1, int sl1, int sc1, int el1, int ec1, File f2, int sl2, int sc2, int el2, int ec2
) {
exists(Element src, Element dest, Location loc1, Location loc2 |
dependsOnSimpleInline(src, dest) and
src instanceof DependsSource and
loc1 = src.getLocation() and
f1 = loc1.getFile() and
sl1 = loc1.getStartLine() and
sc1 = loc1.getStartColumn() and
el1 = loc1.getEndLine() and
ec1 = loc1.getEndColumn() and
loc2 = dest.getLocation() and
f2 = loc2.getFile() and
sl2 = loc2.getStartLine() and
sc2 = loc2.getStartColumn() and
el2 = loc2.getEndLine() and
ec2 = loc2.getEndColumn()
)
}
/**
* Holds if a simple dependency from `loc` to `loc2` in a template has a
* non-template alternative?
* (if `DependencyOptions.preferTemplateDeps()` is enabled)
*/
private predicate dependsNonTemplateAlternative(Location loc1, Location loc2) {
getDependencyOptions().preferTemplateDeps() and
exists(Element src, Element dest |
dependsOnSimpleInline(src, dest) and
src.isFromTemplateInstantiation(_) and
src.getLocation() = loc1 and
dest.getLocation() = loc2
) and
dependsLocation(loc1.getFile(), loc1.getStartLine(), loc1.getStartColumn(), loc1.getEndLine(),
loc1.getEndColumn(), loc2.getFile(), loc2.getStartLine(), loc2.getStartColumn(),
loc2.getEndLine(), loc2.getEndColumn())
}
/**
* A simple dependency from src to a declaration dest, where the definition is not
* needed at compile time.
*/
predicate dependsOnDeclOnly(Element src, Element dest) {
dependency_functionUse(src, dest) or
dependency_variableUse(src, dest) or
dependency_pointerTypeUse(src, dest)
}
/**
* A dependency from src to dest. This predicate inlines
* template dependencies.
*/
private predicate dependsOnViaTemplate(Declaration src, Element dest) {
// A template instantiation depends on everything that anything
// inside it depends upon. This effectively inlines the things
// inside at the point where the template is called or
// referenced.
exists(Element internal, Location internalLocation, Location destLocation |
// internal is an element in the template {function or class} instantiation that cannot
// itself be a transitive dependency source
internal.isFromTemplateInstantiation(src) and
// don't generate template dependencies through a member function of a template class;
// these dependencies are also generated through the class, which has to be referenced
// somewhere anyway.
not exists(Class c |
internal.isFromTemplateInstantiation(c) and
src.getDeclaringType() = c
) and
// dest is anything that the internal element depends upon
dependsOnSimpleInline(internal, dest) and
// is there something in the template (not the instantiation) that's generating
// (better) dependencies from internal anyway?
internalLocation = internal.getLocation() and
destLocation = dest.getLocation() and
not dependsNonTemplateAlternative(internalLocation, destLocation)
)
}
/**
* Holds if `src` is related to `dest` by one `dependsOnSimple` and any
* number of `dependsOnViaTemplate` steps.
*
* Consider using `Symbol.getADependentElement()` rather than directly
* accessing this predicate.
*/
predicate dependsOnTransitive(DependsSource src, Element dest) {
exists(Element mid1 |
// begin with a simple step
dependsOnSimpleInline(src, mid1) and
// any number of recursive steps
(
mid1 = dest or // mid1 is not necessarily a Declaration
dependsOnViaTemplate+(mid1, dest)
)
)
or
dependency_macroUse(src, dest)
}
/**
* A dependency that targets a TypeDeclarationEntry.
*/
private predicate dependsOnTde(Element src, Type t, TypeDeclarationEntry dest) {
dependsOnTransitive(src, t) and
getDeclarationEntries(t, dest)
}
/**
* A dependency that targets a visible TypeDeclarationEntry.
*/
pragma[noopt]
private predicate dependsOnVisibleTde(Element src, Type t, TypeDeclarationEntry dest) {
dependsOnTde(src, t, dest) and
exists(File g | g = dest.getFile() |
exists(File f | f = src.getFile() | f.getAnIncludedFile*() = g)
)
}
/**
* A dependency that targets a DeclarationEntry
*/
private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest) {
exists(Type t |
// dependency from a Type use -> unique visible TDE
dependsOnVisibleTde(src, t, dest) and
strictcount(TypeDeclarationEntry alt | dependsOnVisibleTde(src, t, alt)) = 1
)
or
exists(TypedefType mid |
// dependency from a TypedefType use -> any (visible) TDE
dependsOnTransitive(src, mid) and
getDeclarationEntries(mid, dest.(TypeDeclarationEntry))
)
or
exists(Declaration mid |
// dependency from a Variable / Function use -> any (visible) declaration entry
dependsOnTransitive(src, mid) and
not mid instanceof Type and
not mid instanceof EnumConstant and
getDeclarationEntries(mid, dest) and
not dest instanceof TypeDeclarationEntry
)
or
exists(Declaration mid |
// dependency from a Type / Variable / Function use -> any (visible) definition
dependsOnTransitive(src, mid) and
not mid instanceof EnumConstant and
getDeclarationEntries(mid, dest) and
// must be definition
dest.isDefinition()
)
}
/**
* The full dependsOn relation, made up of dependsOnTransitive plus some logic
* to fix up the results for Declarations to most reasonable DeclarationEntrys.
*/
private predicate dependsOnFull(DependsSource src, Symbol dest, int category) {
// direct result
dependsOnTransitive(src, dest) and
category = 1
or
// result to a visible DeclarationEntry
dependsOnDeclarationEntry(src, dest) and
src.getFile().getAnIncludedFile*() = dest.getFile() and
category = 1
or
exists(Declaration mid |
// dependency from a Variable / Function use -> non-visible definition (link time)
dependsOnTransitive(src, mid) and
not mid instanceof EnumConstant and
getDeclarationEntries(mid, dest) and
not dest instanceof TypeDeclarationEntry and
// must be definition
dest.(DeclarationEntry).isDefinition() and
// must not be visible (else covered above)
not src.getFile().getAnIncludedFile*() = dest.getFile() and
// filter out FDEs that are only defined in the dummy link target
(
(
dest instanceof FunctionDeclarationEntry and
isLinkerAwareExtracted()
)
implies
exists(LinkTarget lt | not lt.isDummy() |
lt.getAFunction() = dest.(FunctionDeclarationEntry).getFunction()
)
) and
category = 2
)
}
/**
* A dependency caused by a function call / use.
*/
private predicate dependency_functionUse(Element src, Function dest) {
funbind(unresolveElement(src), unresolveElement(dest))
}
/**
* A Type which refers to a UserType.
*/
cached
private predicate refersToUserType(Type a, UserType b) { a.refersTo(b) }
/**
* A Type which refers to a type directly, without using a pointer or reference.
*/
private predicate refersToDirectlyNonPointer(Type a, Type b) {
a.refersToDirectly(b) and
not a instanceof PointerType and
not a instanceof ReferenceType
}
/**
* A Type which refers to a UserType, but only through a pointer or reference.
*/
cached
private predicate refersToUserTypePointer(Type a, UserType b) {
refersToUserType(a, b) and
not refersToDirectlyNonPointer*(a, b)
}
/**
* A dependency caused by a type use.
*/
private predicate dependency_typeUse(Element src, UserType dest) {
refersToUserType(typeUsedBy(src), dest)
}
/**
* A dependency caused by a pointer/reference type use only.
*/
predicate dependency_pointerTypeUse(Element src, UserType dest) {
refersToUserTypePointer(typeUsedBy(src), dest)
}
/**
* The Types that must be defined for a particular Element.
*/
private Type typeUsedBy(Element src) {
result = src.(VariableDeclarationEntry).getType() and
not src.(VariableDeclarationEntry).getVariable().declaredUsingAutoType()
or
result = src.(FunctionDeclarationEntry).getType()
or
result = src.(Cast).getType() and not src.(Cast).isImplicit()
or
result = src.(ClassDerivation).getBaseClass()
or
result = src.(TypeDeclarationEntry).getType().(TypedefType).getBaseType()
or
result = src.(TypeDeclarationEntry).getDeclaration().(Enum).getExplicitUnderlyingType()
or
result = src.(SizeofTypeOperator).getTypeOperand()
or
exists(Function f |
funbind(unresolveElement(src), unresolveElement(f)) and result = f.getATemplateArgument()
)
or
result = src.(NewExpr).getType() and not result.(Class).hasConstructor()
or
result = src.(NewArrayExpr).getType() and
not result.(ArrayType).getBaseType().(Class).hasConstructor()
or
result = src.(DeleteExpr).getExpr().getType() and
not result.(PointerType).getBaseType().(Class).hasDestructor()
or
result = src.(DeleteArrayExpr).getExpr().getType() and
not result.(PointerType).getBaseType().(Class).hasDestructor()
}
/**
* A dependency caused by a variable use.
*/
private predicate dependency_variableUse(VariableAccess src, Variable dest) {
src.getTarget() = dest and
not dest instanceof LocalScopeVariable
}
/**
* A dependency caused by an enum constant use.
*/
private predicate dependency_enumConstantUse(EnumConstantAccess src, EnumConstant dest) {
src.getTarget() = dest
}
/**
* A dependency caused by a macro access.
*/
private predicate dependency_macroUse(MacroAccess src, Macro dest) { src.getMacro() = dest }
/**
* A dependency caused by a 'using' declaration 'using X::Y'.
*/
private predicate dependency_usingDeclaration(UsingDeclarationEntry src, Declaration dest) {
src.getDeclaration() = dest
}
/**
* A dependency caused by a 'using' directive 'using namespace X'.
*/
private predicate dependency_usingNamespace(UsingDirectiveEntry src, NamespaceDeclarationEntry dest) {
exists(Namespace nsdecl |
nsdecl = src.getNamespace() and
dest.getNamespace() = nsdecl and
dest.getFile().getAnIncludedFile*() = src.getFile() and
(
dest.getFile() = src.getFile()
implies
dest.getLocation().getStartLine() < src.getLocation().getStartLine()
) and
none() // temporarily disabled until we have suitable UI in Architect
)
}
/**
* A dependency from the definition of a class member to a corresponding declaration. This
* ensures that an externally defined class member has a dependency on (something in) the
* class definition.
*/
private predicate dependency_outOfLineDeclaration(DeclarationEntry src, DeclarationEntry dest) {
src.getDeclaration().hasDeclaringType() and
src.isDefinition() and
(
dest.getDeclaration() = src.getDeclaration()
or
// also permit out of line declarations to jump from the declaration of a specialized
// function to it's definition in the primary template. Note that the specialization
// in this case may be on a template class parameter.
src.getDeclaration().(Function).isConstructedFrom(dest.getDeclaration())
) and
not dest.isDefinition()
}
/**
* A dependency from an initialization of a (static) class member to a corresponding
* declaration.
*/
private predicate dependency_outOfLineInitializer(Initializer src, DeclarationEntry dest) {
src.getDeclaration().hasDeclaringType() and
dest.getDeclaration() = src.getDeclaration() and
not dest.isDefinition()
}
/**
* A dependency from a template function specialization to the general one.
*/
private predicate dependency_functionSpecialization(DeclarationEntry src, DeclarationEntry dest) {
exists(FunctionTemplateSpecialization fts |
src.getDeclaration() = fts and
dest.getDeclaration() = fts.getPrimaryTemplate()
)
}
/**
* A dependency from a template class specialization to the most general one.
*/
private predicate dependency_classSpecialization(DeclarationEntry src, DeclarationEntry dest) {
exists(ClassTemplateSpecialization cts |
src.getDeclaration() = cts and
dest.getDeclaration() = cts.getPrimaryTemplate()
)
}