-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathHelpers.qll
More file actions
163 lines (148 loc) · 5.35 KB
/
Helpers.qll
File metadata and controls
163 lines (148 loc) · 5.35 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
/**
* Helpers.qll
* Provides helper classes and methods related to LINQ.
*/
import csharp
//#################### PREDICATES ####################
private Stmt firstStmt(ForeachStmt fes) {
if fes.getBody() instanceof BlockStmt
then result = fes.getBody().(BlockStmt).getStmt(0)
else result = fes.getBody()
}
private int numStmts(ForeachStmt fes) {
if fes.getBody() instanceof BlockStmt
then result = count(fes.getBody().(BlockStmt).getAStmt())
else result = 1
}
/** Holds if the type's qualified name is "System.Linq.Enumerable" */
predicate isEnumerableType(ValueOrRefType t) { t.hasQualifiedName("System.Linq.Enumerable") }
/** Holds if the type's qualified name starts with "System.Collections.Generic.IEnumerable" */
predicate isIEnumerableType(ValueOrRefType t) {
t.getQualifiedName().matches("System.Collections.Generic.IEnumerable%")
}
/**
* Holds if `foreach` statement `fes` could be converted to a `.All()` call.
* That is, the `ForeachStmt` contains a single `if` with a condition that
* accesses the loop variable and with a body that assigns `false` to a variable
* and `break`s out of the `foreach`.
*/
predicate missedAllOpportunity(ForeachStmt fes) {
exists(IfStmt is |
// The loop contains an if statement with no else case, and nothing else.
is = firstStmt(fes) and
numStmts(fes) = 1 and
not exists(is.getElse()) and
// The if statement accesses the loop variable.
is.getCondition().getAChildExpr*() = fes.getVariable().getAnAccess() and
// The then case of the if assigns false to something and breaks out of the loop.
exists(Assignment a, BoolLiteral bl |
a = is.getThen().getAChild*() and
bl = a.getRValue() and
bl.toString() = "false"
) and
exists(BreakStmt bs | bs = is.getThen().getAChild*())
)
}
/**
* Holds if `foreach` statement `fes` could be converted to a `.Cast()` call.
* That is, the loop variable is accessed only in the first statement of the
* block, and the access is a cast. The first statement needs to be a
* `LocalVariableDeclStmt`.
*/
predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
s = firstStmt(fes) and
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
va = s.getAVariableDeclExpr().getAChildExpr*()
) and
exists(CastExpr ce |
ce = s.getAVariableDeclExpr().getInitializer() and
ce.getExpr() = fes.getVariable().getAnAccess()
)
}
/**
* Holds if `foreach` statement `fes` could be converted to an `.OfType()` call.
* That is, the loop variable is accessed only in the first statement of the
* block, and the access is a cast with the `as` operator. The first statement
* needs to be a `LocalVariableDeclStmt`.
*/
predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
s = firstStmt(fes) and
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
va = s.getAVariableDeclExpr().getAChildExpr*()
) and
exists(AsExpr ae |
ae = s.getAVariableDeclExpr().getInitializer() and
ae.getExpr() = fes.getVariable().getAnAccess()
)
}
/**
* Holds if `foreach` statement `fes` could be converted to a `.Select()` call.
* That is, the loop variable is accessed only in the first statement of the
* block, and the access is not a cast. The first statement needs to be a
* `LocalVariableDeclStmt`.
*/
predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
s = firstStmt(fes) and
forex(VariableAccess va | va = fes.getVariable().getAnAccess() |
va = s.getAVariableDeclExpr().getAChildExpr*()
) and
not s.getAVariableDeclExpr().getInitializer() instanceof Cast
}
/**
* Holds if `foreach` statement `fes` could be converted to a `.Where()` call.
* That is, first statement of the loop is an `if`, which accesses the loop
* variable, and the body of the `if` is either a `continue` or there's nothing
* else in the loop than the `if`.
*/
predicate missedWhereOpportunity(ForeachStmt fes, IfStmt is) {
// The very first thing the foreach loop does is test its iteration variable.
is = firstStmt(fes) and
exists(VariableAccess va |
va.getTarget() = fes.getVariable() and
va = is.getCondition().getAChildExpr*()
) and
// It then either (a) continues, or (b) performs the entire body of the loop within the condition.
(
is.getThen() instanceof ContinueStmt
or
not exists(is.getElse()) and
numStmts(fes) = 1
)
}
//#################### CLASSES ####################
/** A LINQ Any(...) call. */
class AnyCall extends MethodCall {
AnyCall() {
exists(Method m |
m = getTarget() and
isEnumerableType(m.getDeclaringType()) and
m.hasName("Any")
)
}
}
/** A LINQ Count(...) call. */
class CountCall extends MethodCall {
CountCall() {
exists(Method m |
m = getTarget() and
isEnumerableType(m.getDeclaringType()) and
m.hasName("Count")
)
}
}
/** A variable of type IEnumerable<T>, for some T. */
class IEnumerableSequence extends Variable {
IEnumerableSequence() { isIEnumerableType(getType()) }
}
/** A LINQ Select(...) call. */
class SelectCall extends ExtensionMethodCall {
SelectCall() {
exists(Method m |
m = getTarget() and
isEnumerableType(m.getDeclaringType()) and
m.hasName("Select")
)
}
/** Gets the anonymous function expression supplied as the argument to the Select (if possible). */
AnonymousFunctionExpr getFunctionExpr() { result = getArgument(1) }
}