Meaningless Size Comparison

ID

kotlin.meaningless_size_comparison

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Kotlin

Tags

collection, reliability, suspicious-comparison

Description

Reports a comparison of a size against a bound that size can never cross, so the test always yields the same answer. size on a collection, map or array, length on a CharSequence and the argument-less count() are all non-negative by construction: >= 0 and > -1 are therefore always true, and < 0 and ⇐ -1 always false.

Both operand orders are recognised, so 0 ⇐ items.size is reported alongside items.size >= 0.

Rationale

if (items.size >= 0) {          // always taken — guards nothing
    process(items)
}

if (items.size < 0) {           // never taken — the body is dead code
    reportCorruption()
}

if (items.count() > -1) {       // always taken
    process(items)
}

The condition compiles and runs, so nothing warns about it, yet it carries no information. Either the guard is redundant and hides the fact that the body is unconditional, or — far more often — an emptiness check was intended and the off-by-one bound now lets every input through, including the empty one the guard was written to reject.

Only the bounds 0 and -1 are considered. Every other bound states a real condition: items.size >= 42 and items.size < 1 are both perfectly meaningful. Equality is out of scope too, because items.size == 0 is a legitimate — if unidiomatic — emptiness check rather than a constant result.

A member named size is trusted to be non-negative when the receiver resolves to a standard collection, sequence, array or character-sequence type, or when the receiver’s type cannot be resolved at all. A receiver that resolves to a type declared in the analysed project is not trusted, since a hand-written val size: Int really can go negative. A bare identifier named size is never matched, for the same reason: a plain Int counter can be negative, so size >= 0 on one is a genuine test.

Remediation

Decide what the guard was meant to say and write that instead. When the intent was an emptiness check, use the idiomatic property rather than a comparison against zero.

if (items.isNotEmpty()) {       // what `items.size >= 0` usually meant
    process(items)
}

if (items.isEmpty()) {          // what `items.size < 0` usually meant
    reportEmptyInput()
}

if (items.size >= MIN_BATCH) {  // a bound the size can actually cross
    process(items)
}

When the guard really is redundant, delete it and let the body run unconditionally — that is what the code already does, and saying so removes a false suggestion that some inputs are filtered out.

© 2026 Xygeni. All rights reserved