-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathInlineCallGraphTest.ql
More file actions
112 lines (102 loc) · 4.05 KB
/
InlineCallGraphTest.ql
File metadata and controls
112 lines (102 loc) · 4.05 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
import python
import TestUtilities.InlineExpectationsTest
private import semmle.python.dataflow.new.internal.DataFlowDispatch as TT
/** 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
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
exists(TT::NormalCall cc |
cc = TT::TNormalCall(call, _, any(TT::TCallType t | t instanceof TT::CallTypeClass)) and
TT::TFunction(callable) = TT::viableCallable(cc)
)
}
class CallGraphTest extends InlineExpectationsTest {
CallGraphTest() { this = "CallGraphTest" }
override string getARelevantTag() { result in ["pt", "tt"] }
override 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
if call.getLocation().getFile() = target.getLocation().getFile()
then value = betterQualName(target)
else
exists(string fixedRelativePath |
fixedRelativePath =
target
.getLocation()
.getFile()
.getRelativePath()
.regexpCapture(".*/CallGraph[^/]*/(.*)", 1)
|
// the value needs to be enclosed in quotes to allow special characters
value = "\"" + 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
result = 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
f.getQualifiedName() = callable.getQualifiedName() 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 = betterQualName(target)
)
}
query predicate typeTracker_found_pointsTo_notFound(CallNode call, string qualname) {
exists(Function target |
not pointsToCallEdge(call, target) and
typeTrackerCallEdge(call, target) and
qualname = betterQualName(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)
)
}