Skip to content

Commit 170621d

Browse files
committed
C#: Address review comments
1 parent 4b32ee7 commit 170621d

File tree

15 files changed

+333
-545
lines changed

15 files changed

+333
-545
lines changed

csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll

Lines changed: 65 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -428,25 +428,25 @@ class DereferenceableExpr extends Expr {
428428

429429
/**
430430
* An expression that evaluates to a collection. That is, an expression whose
431-
* (transitive, reflexive) base type is `IEnumerable`(`<T>`).
431+
* (transitive, reflexive) base type is `IEnumerable`.
432432
*/
433433
class CollectionExpr extends Expr {
434434
CollectionExpr() {
435-
exists(Interface i |
436-
i = this.getType().(ValueOrRefType).getABaseType*().getSourceDeclaration()
437-
|
438-
i instanceof SystemCollectionsIEnumerableInterface or
439-
i instanceof SystemCollectionsGenericIEnumerableTInterface
440-
)
435+
this.getType().(ValueOrRefType).getABaseType*() instanceof SystemCollectionsIEnumerableInterface
441436
}
442437

443-
/** Gets an expression that computes the size of this collection. */
444-
private Expr getASizeExpr() {
438+
/**
439+
* Gets an expression that computes the size of this collection. `lowerBound`
440+
* indicates whether the expression only computes a lower bound.
441+
*/
442+
private Expr getASizeExpr(boolean lowerBound) {
443+
lowerBound = false and
445444
result = any(PropertyRead pr |
446445
this = pr.getQualifier() and
447446
pr.getTarget() = any(SystemArrayClass x).getLengthProperty()
448447
)
449448
or
449+
lowerBound = false and
450450
result = any(PropertyRead pr |
451451
this = pr.getQualifier() and
452452
pr
@@ -459,59 +459,61 @@ class CollectionExpr extends Expr {
459459
or
460460
result = any(MethodCall mc |
461461
mc.getTarget().getSourceDeclaration() = any(SystemLinq::SystemLinqEnumerableClass x)
462-
.getCountMethod() and
463-
this = mc.getArgument(0)
462+
.getACountMethod() and
463+
this = mc.getArgument(0) and
464+
if mc.getNumberOfArguments() = 1 then lowerBound = false else lowerBound = true
464465
)
465466
}
466467

467468
private Expr getABooleanEmptinessCheck(BooleanValue v, boolean isEmpty) {
468469
exists(boolean branch | branch = v.getValue() |
469-
exists(Expr sizeOf | sizeOf = this.getASizeExpr() |
470-
result = any(ComparisonTest ct |
471-
ct.getAnArgument() = sizeOf and
472-
(
473-
// x.Length == 0
474-
ct.getComparisonKind().isEquality() and
475-
ct.getAnArgument().getValue().toInt() = 0 and
476-
branch = isEmpty
477-
or
478-
// x.Length == k, k > 0
479-
ct.getComparisonKind().isEquality() and
480-
ct.getAnArgument().getValue().toInt() > 0 and
481-
branch = true and
482-
isEmpty = false
483-
or
484-
// x.Length != 0
485-
ct.getComparisonKind().isInequality() and
486-
ct.getAnArgument().getValue().toInt() = 0 and
487-
branch = isEmpty.booleanNot()
488-
or
489-
// x.Length != k, k != 0
490-
ct.getComparisonKind().isInequality() and
491-
ct.getAnArgument().getValue().toInt() != 0 and
492-
branch = false and
493-
isEmpty = false
494-
or
495-
// x.Length > k, k >= 0
496-
ct.getComparisonKind().isLessThan() and
497-
ct.getFirstArgument().getValue().toInt() >= 0 and
498-
branch = true and
499-
isEmpty = false
500-
or
501-
// x.Length >= k, k > 0
502-
ct.getComparisonKind().isLessThanEquals() and
503-
ct.getFirstArgument().getValue().toInt() > 0 and
504-
branch = true and
505-
isEmpty = false
506-
)
507-
).getExpr()
508-
)
470+
result = any(ComparisonTest ct |
471+
exists(boolean lowerBound |
472+
ct.getAnArgument() = this.getASizeExpr(lowerBound) and
473+
if isEmpty = true then lowerBound = false else any()
474+
|
475+
// x.Length == 0
476+
ct.getComparisonKind().isEquality() and
477+
ct.getAnArgument().getValue().toInt() = 0 and
478+
branch = isEmpty
479+
or
480+
// x.Length == k, k > 0
481+
ct.getComparisonKind().isEquality() and
482+
ct.getAnArgument().getValue().toInt() > 0 and
483+
branch = true and
484+
isEmpty = false
485+
or
486+
// x.Length != 0
487+
ct.getComparisonKind().isInequality() and
488+
ct.getAnArgument().getValue().toInt() = 0 and
489+
branch = isEmpty.booleanNot()
490+
or
491+
// x.Length != k, k != 0
492+
ct.getComparisonKind().isInequality() and
493+
ct.getAnArgument().getValue().toInt() != 0 and
494+
branch = false and
495+
isEmpty = false
496+
or
497+
// x.Length > k, k >= 0
498+
ct.getComparisonKind().isLessThan() and
499+
ct.getFirstArgument().getValue().toInt() >= 0 and
500+
branch = true and
501+
isEmpty = false
502+
or
503+
// x.Length >= k, k > 0
504+
ct.getComparisonKind().isLessThanEquals() and
505+
ct.getFirstArgument().getValue().toInt() > 0 and
506+
branch = true and
507+
isEmpty = false
508+
)
509+
).getExpr()
509510
or
510511
result = any(MethodCall mc |
511512
mc.getTarget().getSourceDeclaration() = any(SystemLinq::SystemLinqEnumerableClass x)
512-
.getAnyMethod() and
513+
.getAnAnyMethod() and
513514
this = mc.getArgument(0) and
514-
branch = isEmpty.booleanNot()
515+
branch = isEmpty.booleanNot() and
516+
if branch = false then mc.getNumberOfArguments() = 1 else any()
515517
)
516518
)
517519
}
@@ -526,8 +528,8 @@ class CollectionExpr extends Expr {
526528
* For example, if the expression `x.Length != 0` evaluates to `true` then the
527529
* expression `x` is guaranteed to be non-empty.
528530
*/
529-
Expr getAnEmptinessCheck(AbstractValue v, boolean isNull) {
530-
result = this.getABooleanEmptinessCheck(v, isNull)
531+
Expr getAnEmptinessCheck(AbstractValue v, boolean isEmpty) {
532+
result = this.getABooleanEmptinessCheck(v, isEmpty)
531533
}
532534
}
533535

@@ -1646,6 +1648,8 @@ module Internal {
16461648
predicate emptyValue(Expr e) {
16471649
e.(ArrayCreation).getALengthArgument().getValue().toInt() = 0
16481650
or
1651+
e.(ArrayInitializer).hasNoElements()
1652+
or
16491653
exists(Expr mid | emptyValue(mid) |
16501654
mid = e.(AssignExpr).getRValue()
16511655
or
@@ -1655,8 +1659,8 @@ module Internal {
16551659
exists(PreSsa::Definition def | emptyDef(def) | firstReadSameVarUniquePredecesssor(def, e))
16561660
or
16571661
exists(MethodCall mc |
1658-
mc.getTarget().getSourceDeclaration() = any(SystemCollectionsGenericListClass c)
1659-
.getClearMethod() and
1662+
mc.getTarget().getAnUltimateImplementee().getSourceDeclaration() = any(SystemCollectionsGenericICollectionInterface c
1663+
).getClearMethod() and
16601664
adjacentReadPairSameVarUniquePredecessor(mc.getQualifier(), e)
16611665
)
16621666
}
@@ -1667,6 +1671,8 @@ module Internal {
16671671
length.getValue().toInt() != 0
16681672
)
16691673
or
1674+
e.(ArrayInitializer).getNumberOfElements() > 0
1675+
or
16701676
exists(Expr mid | nonEmptyValue(mid) |
16711677
mid = e.(AssignExpr).getRValue()
16721678
or
@@ -1678,8 +1684,8 @@ module Internal {
16781684
)
16791685
or
16801686
exists(MethodCall mc |
1681-
mc.getTarget().getSourceDeclaration() = any(SystemCollectionsGenericListClass c)
1682-
.getAddMethod() and
1687+
mc.getTarget().getAnUltimateImplementee().getSourceDeclaration() = any(SystemCollectionsGenericICollectionInterface c
1688+
).getAddMethod() and
16831689
adjacentReadPairSameVarUniquePredecessor(mc.getQualifier(), e)
16841690
)
16851691
}

csharp/ql/src/semmle/code/csharp/controlflow/internal/Splitting.qll

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,16 +1056,6 @@ module LoopUnrollingSplitting {
10561056
* (the loop condition evaluating to `false`).
10571057
*/
10581058
abstract predicate pruneLoopCondition(ControlFlowElement pred, ConditionalCompletion c);
1059-
1060-
/** Gets a descendant that belongs to the body of this loop. */
1061-
ControlFlowElement getABodyDescendant() {
1062-
result = this.getBody()
1063-
or
1064-
exists(ControlFlowElement mid |
1065-
mid = this.getABodyDescendant() and
1066-
result = getAChild(mid, mid.getEnclosingCallable())
1067-
)
1068-
}
10691059
}
10701060

10711061
private class UnrollableForeachStmt extends UnrollableLoopStmt, ForeachStmt {
@@ -1083,9 +1073,8 @@ module LoopUnrollingSplitting {
10831073
}
10841074

10851075
override predicate stopUnroll(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
1086-
pred = last(this.getBody(), c) and
1087-
succ = succ(pred, c) and
1088-
not succ = this.getABodyDescendant()
1076+
pred = this and
1077+
succ = succ(pred, c)
10891078
}
10901079

10911080
override predicate pruneLoopCondition(ControlFlowElement pred, ConditionalCompletion c) {
@@ -1159,26 +1148,25 @@ module LoopUnrollingSplitting {
11591148
* Holds if this split applies to control flow element `pred`, where `pred`
11601149
* is a valid predecessor.
11611150
*/
1162-
private predicate appliesToPredecessor(ControlFlowElement pred) {
1151+
private predicate appliesToPredecessor(ControlFlowElement pred, Completion c) {
11631152
this.appliesTo(pred) and
1164-
(exists(succ(pred, _)) or exists(succExit(pred, _)))
1153+
(exists(succ(pred, c)) or exists(succExit(pred, c))) and
1154+
not loop.pruneLoopCondition(pred, c)
11651155
}
11661156

11671157
override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
1168-
this.appliesToPredecessor(pred) and
1158+
this.appliesToPredecessor(pred, c) and
11691159
loop.stopUnroll(pred, succ, c)
11701160
}
11711161

11721162
override Callable hasExit(ControlFlowElement pred, Completion c) {
1173-
this.appliesToPredecessor(pred) and
1174-
result = succExit(pred, c) and
1175-
not loop.pruneLoopCondition(pred, c)
1163+
this.appliesToPredecessor(pred, c) and
1164+
result = succExit(pred, c)
11761165
}
11771166

11781167
override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
1179-
this.appliesToPredecessor(pred) and
1168+
this.appliesToPredecessor(pred, c) and
11801169
succ = succ(pred, c) and
1181-
not loop.pruneLoopCondition(pred, c) and
11821170
not loop.stopUnroll(pred, succ, c)
11831171
}
11841172
}

csharp/ql/src/semmle/code/csharp/frameworks/system/Linq.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ module SystemLinq {
2323
class SystemLinqEnumerableClass extends Class {
2424
SystemLinqEnumerableClass() { this.hasName("Enumerable") }
2525

26-
/** Gets the `Count()` method. */
27-
csharp::ExtensionMethod getCountMethod() { result = this.getAMethod("Count") }
26+
/** Gets a `Count()` method. */
27+
csharp::ExtensionMethod getACountMethod() { result = this.getAMethod("Count") }
2828

29-
/** Gets the `Empty()` method. */
30-
csharp::ExtensionMethod getAnyMethod() { result = this.getAMethod("Any") }
29+
/** Gets an `Any()` method. */
30+
csharp::ExtensionMethod getAnAnyMethod() { result = this.getAMethod("Any") }
3131
}
3232
}

csharp/ql/src/semmle/code/csharp/frameworks/system/collections/Generic.qll

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,6 @@ class SystemCollectionsGenericListClass extends SystemCollectionsGenericUnboundG
9999
this.hasName("List<>") and
100100
this.getNumberOfTypeParameters() = 1
101101
}
102-
103-
Method getClearMethod() { result = this.getAMethod("Clear") }
104-
105-
Method getAddMethod() { result = this.getAMethod("Add") }
106102
}
107103

108104
/** The `System.Collections.Generic.KeyValuePair<TKey, TValue>` structure. */
@@ -133,4 +129,10 @@ class SystemCollectionsGenericICollectionInterface extends SystemCollectionsGene
133129

134130
/** Gets the `Count` property. */
135131
Property getCountProperty() { result = this.getProperty("Count") }
132+
133+
/** Gets the `Clear` method. */
134+
Method getClearMethod() { result = this.getAMethod("Clear") }
135+
136+
/** Gets the `Add` method. */
137+
Method getAddMethod() { result = this.getAMethod("Add") }
136138
}

csharp/ql/test/library-tests/controlflow/graph/BasicBlock.expected

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -288,43 +288,35 @@
288288
| LoopUnrolling.cs:7:10:7:11 | enter M1 | LoopUnrolling.cs:9:13:9:28 | ... == ... | 7 |
289289
| LoopUnrolling.cs:7:10:7:11 | exit M1 | LoopUnrolling.cs:7:10:7:11 | exit M1 | 1 |
290290
| LoopUnrolling.cs:10:13:10:19 | return ...; | LoopUnrolling.cs:10:13:10:19 | return ...; | 1 |
291-
| LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | 1 |
292-
| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:12:13:12:34 | call to method WriteLine | 4 |
293-
| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:12:13:12:34 | [unroll (line 11)] call to method WriteLine | 6 |
294-
| LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:19:13:19:32 | [unroll (line 18)] call to method WriteLine | 15 |
291+
| LoopUnrolling.cs:11:21:11:23 | String arg | LoopUnrolling.cs:11:9:12:35 | foreach (... ... in ...) ... | 5 |
292+
| LoopUnrolling.cs:11:28:11:31 | access to parameter args | LoopUnrolling.cs:11:9:12:35 | [unroll (line 11)] foreach (... ... in ...) ... | 2 |
293+
| LoopUnrolling.cs:15:10:15:11 | enter M2 | LoopUnrolling.cs:18:9:19:33 | [unroll (line 18)] foreach (... ... in ...) ... | 11 |
295294
| LoopUnrolling.cs:15:10:15:11 | exit M2 | LoopUnrolling.cs:15:10:15:11 | exit M2 | 1 |
296-
| LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | 1 |
297-
| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:19:13:19:32 | call to method WriteLine | 4 |
295+
| LoopUnrolling.cs:18:21:18:21 | String x | LoopUnrolling.cs:18:9:19:33 | foreach (... ... in ...) ... | 5 |
298296
| LoopUnrolling.cs:22:10:22:11 | enter M3 | LoopUnrolling.cs:24:29:24:32 | access to parameter args | 3 |
299297
| LoopUnrolling.cs:22:10:22:11 | exit M3 | LoopUnrolling.cs:22:10:22:11 | exit M3 | 1 |
300298
| LoopUnrolling.cs:24:9:26:40 | foreach (... ... in ...) ... | LoopUnrolling.cs:24:9:26:40 | foreach (... ... in ...) ... | 1 |
301-
| LoopUnrolling.cs:24:22:24:24 | Char arg | LoopUnrolling.cs:26:17:26:39 | [unroll (line 25)] call to method WriteLine | 7 |
302-
| LoopUnrolling.cs:25:13:26:40 | foreach (... ... in ...) ... | LoopUnrolling.cs:25:13:26:40 | foreach (... ... in ...) ... | 1 |
303-
| LoopUnrolling.cs:25:26:25:29 | Char arg0 | LoopUnrolling.cs:26:17:26:39 | call to method WriteLine | 4 |
299+
| LoopUnrolling.cs:24:22:24:24 | Char arg | LoopUnrolling.cs:25:13:26:40 | [unroll (line 25)] foreach (... ... in ...) ... | 3 |
300+
| LoopUnrolling.cs:25:26:25:29 | Char arg0 | LoopUnrolling.cs:25:13:26:40 | foreach (... ... in ...) ... | 5 |
304301
| LoopUnrolling.cs:29:10:29:11 | enter M4 | LoopUnrolling.cs:32:26:32:27 | access to local variable xs | 7 |
305302
| LoopUnrolling.cs:29:10:29:11 | exit M4 | LoopUnrolling.cs:29:10:29:11 | exit M4 | 1 |
306303
| LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | LoopUnrolling.cs:32:9:33:33 | foreach (... ... in ...) ... | 1 |
307304
| LoopUnrolling.cs:32:21:32:21 | String x | LoopUnrolling.cs:33:13:33:32 | call to method WriteLine | 4 |
308-
| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:42:17:42:40 | [unroll (line 40), unroll (line 41)] call to method WriteLine | 27 |
305+
| LoopUnrolling.cs:36:10:36:11 | enter M5 | LoopUnrolling.cs:40:9:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | 18 |
309306
| LoopUnrolling.cs:36:10:36:11 | exit M5 | LoopUnrolling.cs:36:10:36:11 | exit M5 | 1 |
310307
| LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:40:9:42:41 | foreach (... ... in ...) ... | 1 |
311-
| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:42:17:42:40 | [unroll (line 41)] call to method WriteLine | 9 |
312-
| LoopUnrolling.cs:41:13:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | LoopUnrolling.cs:41:13:42:41 | [unroll (line 40)] foreach (... ... in ...) ... | 1 |
313-
| LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | 1 |
314-
| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:42:17:42:40 | call to method WriteLine | 6 |
315-
| LoopUnrolling.cs:41:25:41:25 | [unroll (line 40)] String y | LoopUnrolling.cs:42:17:42:40 | [unroll (line 40)] call to method WriteLine | 6 |
316-
| LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:49:9:52:9 | [unroll (line 48)] {...} | 13 |
317-
| LoopUnrolling.cs:50:13:50:17 | [unroll (line 48)] Label: | LoopUnrolling.cs:51:13:51:23 | [unroll (line 48)] goto ...; | 5 |
318-
| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:60:17:60:17 | [unroll (line 58)] access to parameter b | 15 |
308+
| LoopUnrolling.cs:40:21:40:21 | String x | LoopUnrolling.cs:41:13:42:41 | [unroll (line 41)] foreach (... ... in ...) ... | 3 |
309+
| LoopUnrolling.cs:41:25:41:25 | String y | LoopUnrolling.cs:41:13:42:41 | foreach (... ... in ...) ... | 7 |
310+
| LoopUnrolling.cs:45:10:45:11 | enter M6 | LoopUnrolling.cs:49:9:52:9 | {...} | 13 |
311+
| LoopUnrolling.cs:50:13:50:17 | Label: | LoopUnrolling.cs:51:13:51:23 | goto ...; | 5 |
312+
| LoopUnrolling.cs:55:10:55:11 | enter M7 | LoopUnrolling.cs:60:17:60:17 | access to parameter b | 15 |
319313
| LoopUnrolling.cs:55:10:55:11 | exit M7 | LoopUnrolling.cs:55:10:55:11 | exit M7 | 1 |
320-
| LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | 1 |
321-
| LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | 1 |
322-
| LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | LoopUnrolling.cs:62:17:62:17 | [b (line 55): false] access to parameter b | 6 |
323-
| LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | LoopUnrolling.cs:63:17:63:36 | [b (line 55): true] call to method WriteLine | 12 |
324-
| LoopUnrolling.cs:61:17:61:37 | [b (line 55): true, unroll (line 58)] ...; | LoopUnrolling.cs:63:17:63:36 | [b (line 55): true, unroll (line 58)] call to method WriteLine | 8 |
325-
| LoopUnrolling.cs:62:13:63:37 | [b (line 55): false, unroll (line 58)] if (...) ... | LoopUnrolling.cs:62:17:62:17 | [b (line 55): false, unroll (line 58)] access to parameter b | 2 |
326-
| LoopUnrolling.cs:67:10:67:11 | enter M7 | LoopUnrolling.cs:69:14:69:23 | call to method Any | 6 |
327-
| LoopUnrolling.cs:67:10:67:11 | exit M7 | LoopUnrolling.cs:67:10:67:11 | exit M7 | 1 |
314+
| LoopUnrolling.cs:58:21:58:21 | [b (line 55): false] String x | LoopUnrolling.cs:60:17:60:17 | [b (line 55): false] access to parameter b | 4 |
315+
| LoopUnrolling.cs:58:21:58:21 | [b (line 55): true] String x | LoopUnrolling.cs:60:17:60:17 | [b (line 55): true] access to parameter b | 4 |
316+
| LoopUnrolling.cs:61:17:61:37 | [b (line 55): true] ...; | LoopUnrolling.cs:58:9:64:9 | [b (line 55): true] foreach (... ... in ...) ... | 9 |
317+
| LoopUnrolling.cs:62:13:63:37 | [b (line 55): false] if (...) ... | LoopUnrolling.cs:58:9:64:9 | [b (line 55): false] foreach (... ... in ...) ... | 3 |
318+
| LoopUnrolling.cs:67:10:67:11 | enter M8 | LoopUnrolling.cs:69:14:69:23 | call to method Any | 6 |
319+
| LoopUnrolling.cs:67:10:67:11 | exit M8 | LoopUnrolling.cs:67:10:67:11 | exit M8 | 1 |
328320
| LoopUnrolling.cs:70:13:70:19 | return ...; | LoopUnrolling.cs:70:13:70:19 | return ...; | 1 |
329321
| LoopUnrolling.cs:71:9:71:21 | ...; | LoopUnrolling.cs:72:28:72:31 | access to parameter args | 4 |
330322
| LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | LoopUnrolling.cs:72:9:73:35 | foreach (... ... in ...) ... | 1 |

0 commit comments

Comments
 (0)