Meaningless Size Comparison
ID |
swift.meaningless_size_comparison |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Swift |
Tags |
collection, reliability, suspicious-comparison |
Description
Reports a collection count compared against a bound the count can never cross, so the
comparison is a constant. count on any Collection — an array, a dictionary, a set, a string —
is never negative, which makes >= 0 and > -1 always true, and < 0 and ⇐ -1 always false.
Both operand orders are recognised, so 0 ⇐ items.count is reported too. Only the bounds 0 and
-1 are considered; every other bound states a real condition and is left alone.
Rationale
if items.count >= 0 { // always taken — the guard protects nothing
process(items)
}
if items.count < 0 { // never taken — the branch is dead
reportCorruption()
}
A comparison that cannot change its answer either guards nothing at all or, far more often, was meant to be an emptiness check and now lets every input through. Both readings are defects: the first leaves a guard that reads as meaningful and is not, the second silently disables a branch that a reader — and a reviewer — will assume runs.
The count’s declaring type is not resolved: a member named count is trusted to be a collection
size. A bare identifier named count is deliberately not matched, because a plain Int counter
really can go negative and count >= 0 on one is a legitimate test. Only a member access
(items.count, page.rows.count) qualifies. An arithmetic operand such as
offset + items.count >= 0 is likewise not reported — the sum can be negative.
Remediation
Decide what the test was meant to say. An emptiness check is spelled isEmpty; a real threshold
uses a bound the count can actually cross; a guard that turns out to protect nothing should be
deleted along with the branch it wrapped.
if !items.isEmpty { // the emptiness check the constant was standing in for
process(items)
}
if items.count >= maxBatch { // a bound the count can cross
flush()
}
The emptiness shapes against zero — count == 0, count != 0, count > 0, count ⇐ 0 — are
reported separately by swift.avoid_comparing_count_zero, which advises isEmpty; that rule
leaves the four constant combinations to this one, because isEmpty is not the remediation for a
comparison whose answer is already fixed.