Loop Counter Wrong Direction
ID |
csharp.loop_counter_wrong_direction |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
CSharp |
Tags |
control-flow, loop, reliability |
Description
Reports a for loop whose counter moves away from the bound its condition tests — the condition
caps the counter from above while the update decrements it, or bounds it from below while the
update increments it.
Rationale
Each of the three clauses is plausible on its own, so the defect only becomes visible when they are
read together. for (int i = 0; i < upperBound; i--) never approaches upperBound: the counter walks
away from it. An integral counter does not loop forever — it wraps around at the limit of its type
and eventually satisfies the condition — but it takes billions of iterations to get there, which is
indistinguishable from a hang, and every iteration in between works on an index the author never
intended.
Only updates whose direction is certain are considered: ++ and -- in either position, and
+= / -= against a positive integer literal. A step whose sign is unknown (i += step) and a
loop with no update clause are left alone, as is a converging loop where the condition compares two
counters that the update moves towards each other.
public int SumDown(int[] values, int upperBound)
{
int total = 0;
for (int i = 0; i < upperBound; i--) // FLAW — i walks away from upperBound
{
total += values[i];
}
return total;
}
public void Forward(int limit)
{
for (int i = 0; i < limit; i++) // OK — the counter approaches the bound
{
Log(i);
}
}
public void Converging(int limit)
{
for (int i = 0, j = limit; i < j; i++, j--) // OK — both ends move towards each other
{
Log(i + j);
}
}
public void UnknownStep(int limit, int step)
{
for (int i = 0; i < limit; i += step) // OK — the sign of step is not known here
{
Log(i);
}
}
Remediation
Decide which clause is wrong and fix that one. To count up towards an upper bound, the update must
increase the counter (i++); to count down towards a lower bound, it must decrease it
(for (int i = upperBound; i > 0; i--)). When the loop walks an indexable collection in reverse, the
conventional form is for (int i = values.Length - 1; i >= 0; i--); foreach over a reversed
sequence removes the counter altogether.