No Tautological Math Comparison
ID |
csharp.no_tautological_math_comparison |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
CSharp |
Tags |
arithmetic, reliability, suspicious-comparison |
Description
Reports a relational comparison whose result is fixed because the constant it tests against lies
outside the range the other operand can represent — a float against double.MaxValue, an int
against long.MaxValue, a char against a literal beyond 65535.
Rationale
A comparison that cannot come out both ways is dead logic. The guard it forms never guards anything: the branch behind it is always taken or never taken, and the input validation it appears to perform does not happen. Because both sides read as sensible numeric code, nothing draws attention to it.
The usual origin is a narrowed type. A field that was a double becomes a float, a counter that
was a long becomes an int, and the range check left behind keeps naming the old, wider type. The
check then silently stops rejecting anything.
Only certainties are reported. The variable must resolve to a declared numeric type, and the constant
must be a MaxValue / MinValue member of a strictly wider type, or an integer literal outside the
variable range. Nullable declarations are skipped, because a null operand makes any relational
comparison false and the outcome is then no longer fixed.
public bool InRange(float value)
{
return value <= double.MaxValue; // FLAW — always true; no float exceeds double.MaxValue
}
public bool TooLarge(int count)
{
return count > long.MaxValue; // FLAW — always false
}
public bool AboveAsciiRange(char c)
{
return c < 100000; // FLAW — always true; a char stops at 65535
}
public bool InRangeOk(int count)
{
return count < int.MaxValue; // OK — int.MaxValue is a value count can reach
}
public bool NarrowerBoundOk(long count)
{
return count < int.MaxValue; // OK — a long can exceed int.MaxValue
}
public bool NullableOk(int? count)
{
return count < long.MaxValue; // OK — a null operand makes the result false
}
Remediation
Compare against the limit of the operand type rather than a wider one: a float against
float.MaxValue, an int against int.MaxValue. If the intent was to check a value against a wider
type before narrowing it, do the comparison on the wide value — before the cast or conversion, where
the constant is meaningful — or use a checked conversion and handle the overflow. If the check turns
out to be genuinely unnecessary, delete it instead of leaving a guard that never fires.