No Useless Increment
ID |
csharp.no_useless_increment |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
CSharp |
Tags |
dead-code, reliability, suspicious-assignment |
Description
Reports a postfix ` or `--` whose effect is discarded: `return i; (or the expression body
int Bump(int i) ⇒ i;`) on a local variable or a by-value parameter, where the returned value is
the one from before the increment, and `i = i;, which writes the old value straight back over the
incremented one.
Rationale
The postfix operator yields the value the variable had before it was changed. In return i;`
the caller therefore receives the old value, and the variable that was just incremented dies with
the method — the increment has no observable effect at all. In `i = i; the same old value is
assigned back to i, so the statement as a whole does nothing. Both shapes compile without a
warning and both read as if they did something, which is what makes them worth reporting.
The returned-value shape is reported only when the operand is a local variable or a by-value
parameter, that is, storage that dies with the call. A field keeps the incremented value, so
return _next++; is the ordinary way to write an id generator, and a ref or out parameter hands
the new value back to the caller. Neither is reported.
private int counter;
public int NextBroken()
{
int i = counter;
return i++; // FLAW — returns the old value; the increment is lost
}
public void ResetBroken(int i)
{
i = i++; // FLAW — no-op: the old value overwrites the new one
}
public int Bump(int i) => i++; // FLAW — same defect in an expression body
public int NextOk()
{
return ++counter; // OK — the incremented value is returned
}
public int NextIdOk()
{
return counter++; // OK — the field keeps the incremented value
}
public int TakeOk(ref int cursor)
{
return cursor++; // OK — the caller observes the increment
}
public void CountOk(int[] items)
{
int n = 0;
foreach (var item in items)
{
n++; // OK — the normal idiom
}
Use(n);
}
Remediation
For return i;` decide what the caller should get: `return i + 1;` to return the successor
without touching `i`, or `return i; when the update itself was the point and should be visible in
the returned value. If the counter is meant to survive the call, promote it to a field or a ref
parameter. For i = i;` drop the assignment and write `i;, or i = i + 1; if the intent was to
make the update explicit.