Stop Scanning On Found

ID

swift.stop_scanning_on_found

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Efficiency

Language

Swift

Tags

efficiency, performance, suspicious-code

Description

Reports a for-style loop whose body sets a result variable via a plain = assignment and keeps iterating without break, return, or throw. The shape is the classic manual "find the first element matching a predicate" implemented as a full scan — every later matching element overwrites the same slot, so the work after the first match is wasted.

var found: Element? = nil
for x in items {
    if predicate(x) {
        found = x          // FLAW — keeps iterating after the first match
    }
}

Rationale

When the assignment is to a variable declared in an enclosing scope, the only effect of subsequent iterations is to replace the stored value with one of equal "match-ness". The author almost always meant "the first match" — break (or return) makes that intent explicit and turns an O(n) loop into early-exit. Swift’s first(where:) already encapsulates this short-circuit.

Remediation

Add a break after the assignment, or use first(where:):

// 1) break right after the assignment
var found: Element? = nil
for x in items {
    if predicate(x) {
        found = x
        break
    }
}

// 2) use the standard-library short-circuit form
let found = items.first(where: predicate)

If the loop is meant to collect every match, use an array accumulator instead — result.append(x) or result += [x] is unambiguous and the rule will not fire on it.

When not to fire

The rule is intentionally conservative and skips:

  • compound-assign operators (+=, -=, …​) — those are accumulation, not "found";

  • member or subscript writes (self.x = …​, dict[k] = v) — those are mutations of a structured target;

  • variables declared inside the loop body — those are loop-local state, not an outer "result" slot;

  • loops whose body already contains break, return, throw, or continue after the assignment;

  • the same variable being mutated elsewhere in the loop via += / .append(…​) / .insert(…​).