Credence.Pattern.NoEnumCountForLength
(credence v0.7.0)
Copy Markdown
Detects Enum.count/1 (without a predicate) on a provably-list argument
and rewrites it to length/1.
Why this matters
Enum.count/1 goes through the Enumerable protocol, adding dispatch
overhead. When the argument is a list, length/1 is a BIF that does the same
traversal without protocol dispatch:
# Flagged — argument is provably a list
total = String.graphemes(text) |> Enum.count()
# Idiomatic — BIF, no protocol overhead
total = length(String.graphemes(text))Why "provably a list"
length/1 only accepts lists — length(1..5) raises ArgumentError, while
Enum.count(1..5) returns 5. So the rewrite is behaviour-preserving only
when the argument is known to be a list. The rule therefore fires only when the
argument is:
- a list literal (
[a, b, c]), - a
++concatenation, or - a call (or pipe ending in a call) to a list-returning function —
String.graphemes/split/...,Map.keys/values,Enum.map/filter/sort/...,List.*, etc.
A bare variable (Enum.count(chars)) is not flagged: its type is unknown,
and it might be a range, map, or stream rather than a list.
Flagged patterns
Only the single-argument form Enum.count(x) (direct or piped). The
two-argument Enum.count(x, predicate) is not flagged (it filters and counts
in one pass).