forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCallGraphTest.qll
More file actions
147 lines (126 loc) · 4.96 KB
/
CallGraphTest.qll
File metadata and controls
147 lines (126 loc) · 4.96 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
import python
/** Gets the comment on the line above `ast` */
Comment commentFor(AstNode ast) {
exists(int line | line = ast.getLocation().getStartLine() - 1 |
result
.getLocation()
.hasLocationInfo(ast.getLocation().getFile().getAbsolutePath(), line, _, line, _)
)
}
/** Gets the value from `tag:value` in the comment for `ast` */
string getAnnotation(AstNode ast, string tag) {
exists(Comment comment, string match, string theRegex |
theRegex = "([\\w]+):([\\w.]+)" and
comment = commentFor(ast) and
match = comment.getText().regexpFind(theRegex, _, _) and
tag = match.regexpCapture(theRegex, 1) and
result = match.regexpCapture(theRegex, 2)
)
}
/** Gets a callable annotated with `name:name` */
Function annotatedCallable(string name) { name = getAnnotation(result, "name") }
/** Gets a call annotated with `calls:name` */
Call annotatedCall(string name) { name = getAnnotation(result, "calls") }
predicate missingAnnotationForCallable(string name, Call call) {
call = annotatedCall(name) and
not exists(annotatedCallable(name))
}
predicate nonUniqueAnnotationForCallable(string name, Function callable) {
strictcount(annotatedCallable(name)) > 1 and
callable = annotatedCallable(name)
}
predicate missingAnnotationForCall(string name, Function callable) {
not exists(annotatedCall(name)) and
callable = annotatedCallable(name)
}
/** There is an obvious problem with the annotation `name` */
predicate nameInErrorState(string name) {
missingAnnotationForCallable(name, _)
or
nonUniqueAnnotationForCallable(name, _)
or
missingAnnotationForCall(name, _)
}
/** Source code has annotation with `name` showing that `call` will call `callable` */
predicate annotatedCallEdge(string name, Call call, Function callable) {
not nameInErrorState(name) and
call = annotatedCall(name) and
callable = annotatedCallable(name)
}
// ------------------------- Annotation debug query predicates -------------------------
query predicate debug_missingAnnotationForCallable(Call call, string message) {
exists(string name |
message =
"This call is annotated with '" + name +
"', but no callable with that annotation was extracted. Please fix." and
missingAnnotationForCallable(name, call)
)
}
query predicate debug_nonUniqueAnnotationForCallable(Function callable, string message) {
exists(string name |
message = "Multiple callables are annotated with '" + name + "'. Please fix." and
nonUniqueAnnotationForCallable(name, callable)
)
}
query predicate debug_missingAnnotationForCall(Function callable, string message) {
exists(string name |
message =
"This callable is annotated with '" + name +
"', but no call with that annotation was extracted. Please fix." and
missingAnnotationForCall(name, callable)
)
}
// ------------------------- Call Graph resolution -------------------------
private newtype TCallGraphResolver =
TPointsToResolver() or
TTypeTrackerResolver()
/** Describes a method of call graph resolution */
abstract class CallGraphResolver extends TCallGraphResolver {
abstract predicate callEdge(Call call, Function callable);
/**
* Holds if annotations show that `call` will call `callable`,
* but our call graph resolver was not able to figure that out
*/
predicate expectedCallEdgeNotFound(Call call, Function callable) {
annotatedCallEdge(_, call, callable) and
not this.callEdge(call, callable)
}
/**
* Holds if there are no annotations that show that `call` will call `callable` (where at least one of these are annotated),
* but the call graph resolver claims that `call` will call `callable`
*/
predicate unexpectedCallEdgeFound(Call call, Function callable, string message) {
this.callEdge(call, callable) and
not annotatedCallEdge(_, call, callable) and
(
exists(string name |
message = "Call resolved to the callable named '" + name + "' but was not annotated as such" and
callable = annotatedCallable(name) and
not nameInErrorState(name)
)
or
exists(string name |
message = "Annotated call resolved to unannotated callable" and
call = annotatedCall(name) and
not nameInErrorState(name) and
not exists( | callable = annotatedCallable(_))
)
)
}
string toString() { result = "CallGraphResolver" }
}
/** A call graph resolver based on the existing points-to analysis */
class PointsToResolver extends CallGraphResolver, TPointsToResolver {
override predicate callEdge(Call call, Function callable) {
exists(PythonFunctionValue funcValue |
funcValue.getScope() = callable and
call = funcValue.getACall().getNode()
)
}
override string toString() { result = "PointsToResolver" }
}
/** A call graph resolved based on Type Trackers */
class TypeTrackerResolver extends CallGraphResolver, TTypeTrackerResolver {
override predicate callEdge(Call call, Function callable) { none() }
override string toString() { result = "TypeTrackerResolver" }
}