3
3
* Provides helper classes and methods related to LINQ.
4
4
*/
5
5
6
- import csharp
6
+ private import csharp
7
+ private import semmle.code.csharp.frameworks.system.collections.Generic as GenericCollections
8
+ private import semmle.code.csharp.frameworks.system.Collections as Collections
7
9
8
10
//#################### PREDICATES ####################
9
11
private Stmt firstStmt ( ForeachStmt fes ) {
@@ -29,13 +31,40 @@ predicate isIEnumerableType(ValueOrRefType t) {
29
31
)
30
32
}
31
33
34
+ /**
35
+ * A class of foreach statements where the iterable expression
36
+ * supports the use of the LINQ extension methods on `IEnumerable<T>`.
37
+ */
38
+ class ForeachStmtGenericEnumerable extends ForeachStmt {
39
+ ForeachStmtGenericEnumerable ( ) {
40
+ exists ( ValueOrRefType t | t = this .getIterableExpr ( ) .getType ( ) |
41
+ t .getABaseType * ( ) .getUnboundDeclaration ( ) instanceof
42
+ GenericCollections:: SystemCollectionsGenericIEnumerableTInterface or
43
+ t .( ArrayType ) .getRank ( ) = 1
44
+ )
45
+ }
46
+ }
47
+
48
+ /**
49
+ * A class of foreach statements where the iterable expression
50
+ * supports the use of the LINQ extension methods on `IEnumerable`.
51
+ */
52
+ class ForeachStmtEnumerable extends ForeachStmt {
53
+ ForeachStmtEnumerable ( ) {
54
+ exists ( ValueOrRefType t | t = this .getIterableExpr ( ) .getType ( ) |
55
+ t .getABaseType * ( ) instanceof Collections:: SystemCollectionsIEnumerableInterface or
56
+ t .( ArrayType ) .getRank ( ) = 1
57
+ )
58
+ }
59
+ }
60
+
32
61
/**
33
62
* Holds if `foreach` statement `fes` could be converted to a `.All()` call.
34
63
* That is, the `ForeachStmt` contains a single `if` with a condition that
35
64
* accesses the loop variable and with a body that assigns `false` to a variable
36
65
* and `break`s out of the `foreach`.
37
66
*/
38
- predicate missedAllOpportunity ( ForeachStmt fes ) {
67
+ predicate missedAllOpportunity ( ForeachStmtGenericEnumerable fes ) {
39
68
exists ( IfStmt is |
40
69
// The loop contains an if statement with no else case, and nothing else.
41
70
is = firstStmt ( fes ) and
@@ -54,12 +83,12 @@ predicate missedAllOpportunity(ForeachStmt fes) {
54
83
}
55
84
56
85
/**
57
- * Holds if `foreach` statement `fes` could be converted to a `.Cast()` call.
86
+ * Holds if the `foreach` statement `fes` can be converted to a `.Cast()` call.
58
87
* That is, the loop variable is accessed only in the first statement of the
59
- * block, and the access is a cast. The first statement needs to be a
60
- * `LocalVariableDeclStmt `.
88
+ * block, the access is a cast, and the first statement is a
89
+ * local variable declaration statement `s `.
61
90
*/
62
- predicate missedCastOpportunity ( ForeachStmt fes , LocalVariableDeclStmt s ) {
91
+ predicate missedCastOpportunity ( ForeachStmtEnumerable fes , LocalVariableDeclStmt s ) {
63
92
s = firstStmt ( fes ) and
64
93
forex ( VariableAccess va | va = fes .getVariable ( ) .getAnAccess ( ) |
65
94
va = s .getAVariableDeclExpr ( ) .getAChildExpr * ( )
@@ -71,12 +100,12 @@ predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
71
100
}
72
101
73
102
/**
74
- * Holds if `foreach` statement `fes` could be converted to an `.OfType()` call.
103
+ * Holds if `foreach` statement `fes` can be converted to an `.OfType()` call.
75
104
* That is, the loop variable is accessed only in the first statement of the
76
- * block, and the access is a cast with the `as` operator. The first statement
77
- * needs to be a `LocalVariableDeclStmt `.
105
+ * block, the access is a cast with the `as` operator, and the first statement
106
+ * is a local variable declaration statement `s `.
78
107
*/
79
- predicate missedOfTypeOpportunity ( ForeachStmt fes , LocalVariableDeclStmt s ) {
108
+ predicate missedOfTypeOpportunity ( ForeachStmtEnumerable fes , LocalVariableDeclStmt s ) {
80
109
s = firstStmt ( fes ) and
81
110
forex ( VariableAccess va | va = fes .getVariable ( ) .getAnAccess ( ) |
82
111
va = s .getAVariableDeclExpr ( ) .getAChildExpr * ( )
@@ -88,12 +117,12 @@ predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
88
117
}
89
118
90
119
/**
91
- * Holds if `foreach` statement `fes` could be converted to a `.Select()` call.
120
+ * Holds if `foreach` statement `fes` can be converted to a `.Select()` call.
92
121
* That is, the loop variable is accessed only in the first statement of the
93
- * block, and the access is not a cast. The first statement needs to be a
94
- * `LocalVariableDeclStmt `.
122
+ * block, the access is not a cast, and the first statement is a
123
+ * local variable declaration statement `s `.
95
124
*/
96
- predicate missedSelectOpportunity ( ForeachStmt fes , LocalVariableDeclStmt s ) {
125
+ predicate missedSelectOpportunity ( ForeachStmtGenericEnumerable fes , LocalVariableDeclStmt s ) {
97
126
s = firstStmt ( fes ) and
98
127
forex ( VariableAccess va | va = fes .getVariable ( ) .getAnAccess ( ) |
99
128
va = s .getAVariableDeclExpr ( ) .getAChildExpr * ( )
@@ -107,7 +136,7 @@ predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
107
136
* variable, and the body of the `if` is either a `continue` or there's nothing
108
137
* else in the loop than the `if`.
109
138
*/
110
- predicate missedWhereOpportunity ( ForeachStmt fes , IfStmt is ) {
139
+ predicate missedWhereOpportunity ( ForeachStmtGenericEnumerable fes , IfStmt is ) {
111
140
// The very first thing the foreach loop does is test its iteration variable.
112
141
is = firstStmt ( fes ) and
113
142
exists ( VariableAccess va |
0 commit comments