Credence.Pattern.NoReduceForMapBuilding (credence v0.7.0)

Copy Markdown

Detects Enum.reduce/3 with an empty-map accumulator %{} and a body consisting solely of Map.put(acc, key, value), and suggests Map.new/2 instead. Also detects Enum.reduce/3 building a MapSet via MapSet.put/2 and suggests MapSet.new/1.

Why this matters

LLMs frequently produce:

Enum.reduce(list, %{}, fn x, acc ->
  Map.put(acc, x, String.length(x))
end)

when the idiomatic Elixir equivalent is:

Map.new(list, fn x -> {x, String.length(x)} end)

Similarly, LLMs may build a MapSet via reduce:

Enum.reduce(list, MapSet.new(), fn x, acc -> MapSet.put(acc, x) end)

when the idiomatic equivalent is:

MapSet.new(list)

Map.new/2 and MapSet.new/1 communicate intent directly and avoid manual accumulator threading.

Flagged patterns

PatternSuggested replacement
Enum.reduce(enum, %{}, fn x, acc -> Map.put(acc, k, v) end)Map.new(enum, fn x -> {k, v} end)

| enum |> Enum.reduce(%{}, fn x, acc -> Map.put(acc, k, v) end) | enum |> Map.new(fn x -> {k, v} end) | | Enum.reduce(enum, MapSet.new(), fn x, acc -> MapSet.put(acc, x) end) | MapSet.new(enum) | | enum |> Enum.reduce(MapSet.new(), fn x, acc -> MapSet.put(acc, x) end) | enum |> MapSet.new() |

Only the simplest case is matched: the anonymous function body must be a single Map.put(acc, key, value) or MapSet.put(acc, value) call (or a single-expression block), and neither key/value nor the MapSet value may reference acc.