Credence.Rule.NoEnumAtInLoop (credence v0.3.1)

Copy Markdown

Performance rule: Detects Enum.at/2 inside looping constructs (Enum.reduce, Enum.map, Enum.each, Enum.filter, Enum.flat_map, for comprehensions) or recursive functions.

Enum.at/2 is O(n) on linked lists because it traverses from the head to the given index. Inside a loop this compounds to O(n²) or worse. This is one of the most common performance traps for developers coming from languages with array-based lists.

This rule is not auto-fixable because every remedy requires non-local changes: inserting List.to_tuple/1 in an outer scope, restructuring a callback signature for Enum.with_index/1, or rewriting the algorithm to use pattern matching. These transformations depend on surrounding context and cannot be expressed as a mechanical AST node swap.

Bad

defp expand(graphemes, left, right, count) do
  if Enum.at(graphemes, left) == Enum.at(graphemes, right) do
    expand(graphemes, left - 1, right + 1, count)
  else
    0
  end
end

Enum.reduce(0..n, 0, fn i, acc ->
  acc + Enum.at(list, i)
end)

Good

# Option 1: Convert to tuple for O(1) indexed access
tuple = List.to_tuple(graphemes)
elem(tuple, left) == elem(tuple, right)

# Option 2: Use pattern matching / recursion on the list directly
defp process([head | tail]), do: ...

# Option 3: Use Enum.with_index or Enum.zip to pair values with indices
Enum.reduce(Enum.with_index(list), 0, fn {val, _idx}, acc -> acc + val end)