33 * Provides helper classes and methods related to LINQ.
44 */
55
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
79
810//#################### PREDICATES ####################
911private Stmt firstStmt ( ForeachStmt fes ) {
@@ -29,13 +31,40 @@ predicate isIEnumerableType(ValueOrRefType t) {
2931 )
3032}
3133
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+
3261/**
3362 * Holds if `foreach` statement `fes` could be converted to a `.All()` call.
3463 * That is, the `ForeachStmt` contains a single `if` with a condition that
3564 * accesses the loop variable and with a body that assigns `false` to a variable
3665 * and `break`s out of the `foreach`.
3766 */
38- predicate missedAllOpportunity ( ForeachStmt fes ) {
67+ predicate missedAllOpportunity ( ForeachStmtGenericEnumerable fes ) {
3968 exists ( IfStmt is |
4069 // The loop contains an if statement with no else case, and nothing else.
4170 is = firstStmt ( fes ) and
@@ -54,12 +83,12 @@ predicate missedAllOpportunity(ForeachStmt fes) {
5483}
5584
5685/**
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.
5887 * 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 `.
6190 */
62- predicate missedCastOpportunity ( ForeachStmt fes , LocalVariableDeclStmt s ) {
91+ predicate missedCastOpportunity ( ForeachStmtEnumerable fes , LocalVariableDeclStmt s ) {
6392 s = firstStmt ( fes ) and
6493 forex ( VariableAccess va | va = fes .getVariable ( ) .getAnAccess ( ) |
6594 va = s .getAVariableDeclExpr ( ) .getAChildExpr * ( )
@@ -71,12 +100,12 @@ predicate missedCastOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
71100}
72101
73102/**
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.
75104 * 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 `.
78107 */
79- predicate missedOfTypeOpportunity ( ForeachStmt fes , LocalVariableDeclStmt s ) {
108+ predicate missedOfTypeOpportunity ( ForeachStmtEnumerable fes , LocalVariableDeclStmt s ) {
80109 s = firstStmt ( fes ) and
81110 forex ( VariableAccess va | va = fes .getVariable ( ) .getAnAccess ( ) |
82111 va = s .getAVariableDeclExpr ( ) .getAChildExpr * ( )
@@ -88,12 +117,12 @@ predicate missedOfTypeOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
88117}
89118
90119/**
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.
92121 * 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 `.
95124 */
96- predicate missedSelectOpportunity ( ForeachStmt fes , LocalVariableDeclStmt s ) {
125+ predicate missedSelectOpportunity ( ForeachStmtGenericEnumerable fes , LocalVariableDeclStmt s ) {
97126 s = firstStmt ( fes ) and
98127 forex ( VariableAccess va | va = fes .getVariable ( ) .getAnAccess ( ) |
99128 va = s .getAVariableDeclExpr ( ) .getAChildExpr * ( )
@@ -107,7 +136,7 @@ predicate missedSelectOpportunity(ForeachStmt fes, LocalVariableDeclStmt s) {
107136 * variable, and the body of the `if` is either a `continue` or there's nothing
108137 * else in the loop than the `if`.
109138 */
110- predicate missedWhereOpportunity ( ForeachStmt fes , IfStmt is ) {
139+ predicate missedWhereOpportunity ( ForeachStmtGenericEnumerable fes , IfStmt is ) {
111140 // The very first thing the foreach loop does is test its iteration variable.
112141 is = firstStmt ( fes ) and
113142 exists ( VariableAccess va |
0 commit comments