Credence.Pattern.NoManualListLast (credence v0.4.0)

Copy Markdown

Detects hand-rolled reimplementations of List.last/1.

Why this matters

When NoListLast flags List.last/1, LLMs "fix" it by writing the exact same O(n) traversal under a different name:

# Flagged — this IS List.last, just hand-rolled
defp get_last_element([val]), do: val
defp get_last_element([_ | rest]), do: get_last_element(rest)

This has the same performance characteristics as List.last/1 but adds unnecessary code. The real fix is to restructure the algorithm to avoid needing the last element:

  • Track the value in an accumulator during a reduce
  • Reverse the list and take the head
  • Destructure from the other end

Detection scope

A two-clause defp (or def) function with arity 1 where:

  1. One clause matches [val] (single-element list) and returns val
  2. The other clause matches [_ | rest] and recurses with rest

Auto-fix

Replaces the hand-rolled function with a List.last/1 delegation and rewrites call sites within the same source file.