Loop Var Never Changed
ID |
csharp.loop_var_never_changed |
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 update clause advances something other than the counter its condition
tests, and where nothing else advances that counter either. for (int i = 0; i < 10; sum++) never
moves i towards the bound.
Rationale
The three clauses of a for header have to agree, and this is the shape where they do not. The
counter is declared, the condition tests it, and the update clause moves a different variable —
usually because a loop header was copy-pasted and only two of the three names were adjusted. The
result compiles cleanly and, unless the body breaks out for another reason, spins forever.
The counter examined is the single variable the loop header declares, so the analysis is anchored on a
name the loop itself owns. A loop is only reported when that counter is written nowhere else: not in
the update clause, not in the condition, and not in the body. Advancing the counter from inside the
body is untidy but legal, so it silences the rule — as does handing the counter to a method by ref
or out.
public int Sum(int[] values)
{
int total = 0;
for (int i = 0; i < values.Length; total++) // FLAW — i never advances
{
total += values[i];
}
return total;
}
public void Advanced(int limit)
{
for (int i = 0; i < limit; i++) // OK — the counter advances
{
Log(i);
}
}
public void AdvancedInBody(int limit, int other)
{
for (int i = 0; i < limit; other++) // OK — the body advances the counter
{
i += 2;
}
}
public void Endless()
{
for (;;) // OK — the idiomatic endless loop
{
if (Done()) break;
}
}
Remediation
Put the counter back in the update clause — for (int i = 0; i < values.Length; i++) — and move the
other variable’s update into the body, where it belongs. When the collection is walked from start to
end and the index is only used to fetch the element, foreach removes the counter and the whole class
of mistake with it.