-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Expand file tree
/
Copy pathInlineCallGraphTest.ql
More file actions
135 lines (124 loc) · 5.24 KB
/
InlineCallGraphTest.ql
File metadata and controls
135 lines (124 loc) · 5.24 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
import python
import utils.test.InlineExpectationsTest
private import semmle.python.dataflow.new.internal.DataFlowDispatch as TT
private import LegacyPointsTo
/** Holds when `call` is resolved to `callable` using points-to based call-graph. */
predicate pointsToCallEdge(CallNode call, Function callable) {
exists(call.getLocation().getFile().getRelativePath()) and
exists(callable.getLocation().getFile().getRelativePath()) and
// I did try using viableCallable from `DataFlowDispatchPointsTo` (from temporary copy
// of `dataflow.new.internal` that still uses points-to) instead of direct
// `getACall()` on a Value, but it only added results for `__init__` methods, not for
// anything else.
exists(PythonFunctionValue funcValue |
funcValue.getScope() = callable and
call = funcValue.getACall()
)
}
/** Holds when `call` is resolved to `callable` using type-tracking based call-graph. */
predicate typeTrackerCallEdge(CallNode call, Function callable) {
exists(call.getLocation().getFile().getRelativePath()) and
exists(callable.getLocation().getFile().getRelativePath()) and
exists(TT::DataFlowCallable dfCallable, TT::DataFlowCall dfCall |
dfCallable.getScope() = callable and
dfCall.getNode() = call and
dfCallable = TT::viableCallable(dfCall)
)
}
/** Holds if the call edge is from a class call. */
predicate typeTrackerClassCall(CallNode call, Function callable) {
exists(call.getLocation().getFile().getRelativePath()) and
exists(callable.getLocation().getFile().getRelativePath()) and
TT::resolveCall(call, callable, any(TT::TCallType t | t instanceof TT::CallTypeClass))
}
module CallGraphTest implements TestSig {
string getARelevantTag() { result in ["pt", "tt"] }
predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and
exists(CallNode call, Function target |
tag = "tt" and
typeTrackerCallEdge(call, target)
or
tag = "pt" and
pointsToCallEdge(call, target)
|
location = call.getLocation() and
element = call.toString() and
value = getCallEdgeValue(call, target)
)
}
}
import MakeTest<CallGraphTest>
bindingset[call, target]
string getCallEdgeValue(CallNode call, Function target) {
if call.getLocation().getFile() = target.getLocation().getFile()
then result = betterQualName(target)
else
exists(string fixedRelativePath |
fixedRelativePath =
target.getLocation().getFile().getAbsolutePath().regexpCapture(".*/CallGraph[^/]*/(.*)", 1)
|
// the value needs to be enclosed in quotes to allow special characters
result = "\"" + fixedRelativePath + ":" + betterQualName(target) + "\""
)
}
bindingset[func]
string betterQualName(Function func) {
// note: `target.getQualifiedName` for Lambdas is just "lambda", so is not very useful :|
not func.isLambda() and
if
strictcount(Function f |
f.getEnclosingModule() = func.getEnclosingModule() and
f.getQualifiedName() = func.getQualifiedName()
) = 1
then result = func.getQualifiedName()
else result = func.getLocation().getStartLine() + ":" + func.getQualifiedName()
or
func.isLambda() and
result =
"lambda[" + func.getLocation().getFile().getShortName() + ":" +
func.getLocation().getStartLine() + ":" + func.getLocation().getStartColumn() + "]"
}
query predicate debug_callableNotUnique(Function callable, string message) {
exists(callable.getLocation().getFile().getRelativePath()) and
exists(Function f |
f != callable and
betterQualName(f) = betterQualName(callable) and
f.getLocation().getFile() = callable.getLocation().getFile()
) and
message =
"Qualified function name '" + callable.getQualifiedName() +
"' is not unique within its file. Please fix."
}
query predicate pointsTo_found_typeTracker_notFound(CallNode call, string qualname) {
exists(Function target |
pointsToCallEdge(call, target) and
not typeTrackerCallEdge(call, target) and
qualname = getCallEdgeValue(call, target) and
// ignore SPURIOUS call edges
not exists(FalsePositiveTestExpectation spuriousResult |
spuriousResult.getTag() = "pt" and
spuriousResult.getValue() = getCallEdgeValue(call, target) and
spuriousResult.getLocation().getFile() = call.getLocation().getFile() and
spuriousResult.getLocation().getStartLine() = call.getLocation().getStartLine()
)
)
}
query predicate typeTracker_found_pointsTo_notFound(CallNode call, string qualname) {
exists(Function target |
not pointsToCallEdge(call, target) and
typeTrackerCallEdge(call, target) and
qualname = getCallEdgeValue(call, target) and
// We filter out result differences for points-to and type-tracking for class calls,
// since otherwise it gives too much noise (these are just handled differently
// between the two).
not typeTrackerClassCall(call, target) and
// ignore SPURIOUS call edges
not exists(FalsePositiveTestExpectation spuriousResult |
spuriousResult.getTag() = "tt" and
spuriousResult.getValue() = getCallEdgeValue(call, target) and
spuriousResult.getLocation().getFile() = call.getLocation().getFile() and
spuriousResult.getLocation().getStartLine() = call.getLocation().getStartLine()
)
)
}