API Reference credence v#0.5.0

Copy Markdown

Modules

Credence — Semantic Linter for Elixir.

Defines the structured issue format for any rule violations.

Pattern phase — detects and fixes anti-patterns in Elixir code.

Performance rule: Detects Enum.count/1 on the result of String.graphemes/1 (without a predicate).

Performance rule: Detects the use of length/1 on the result of String.graphemes/1.

Fixes calls to guard functions that don't exist in Elixir.

Detects functions where the same positional parameter uses different variable names across clauses.

Readability rule: Detects anonymous functions applied with .() inside a pipeline.

Detects case expr do true -> …; false -> … end that should be if/else.

Detects cond with exactly two clauses where the second guard is true — a pattern that is just an if/else in disguise.

Detects patterns where a list is destructured into individual variables and then immediately reassembled into the same list.

Style rule: Detects @doc false placed before private functions (defp).

Performance rule: Detects sorting the same list twice — once ascending and once descending — when a single sort plus Enum.reverse/1 would suffice.

Performance rule: Detects Enum.with_index/1 passed directly as the enumerable argument to Enum.reduce/3 (or piped into it).

Performance rule: Flags Enum.at/2 inside recursive binary search functions.

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.

Performance heuristic rule: warns when Enum.at/2 is used inside loops.

Performance rule: Flags Enum.at/2 with a midpoint index inside non-recursive functions.

Detects Enum.at/2 called with a negative integer literal index.

Detects Enum.count/1 (without a predicate) and suggests length/1 or a more specific size function.

Performance rule: Detects Enum.drop(list, -n) where n is a positive integer literal.

Performance rule: Detects Enum.take(list, -n) where n is a positive integer literal.

Strict semantic rule: Flags ONLY explicit max-reduction patterns inside Enum.reduce/3.

Flags explicit min-reduction patterns inside Enum.reduce/3.

Flags explicit sum-reduction patterns inside Enum.reduce/3.

Readability & performance rule: Detects the pattern of decomposing a string into graphemes or a charlist, only to compare it with its own Enum.reverse.

Readability rule: Detects guard clauses that compare a parameter to a literal value with == when pattern matching in the function head would be clearer and more idiomatic.

Detects identity arithmetic used to coerce an integer to a float.

Detects Enum._by functions called with an identity function callback, which can be simplified to the non-_by variant.

Performance rule: Detects converting an integer to a string representation in a given base and then to a charlist, when Integer.digits/2 can extract the digits directly as a list of integers.

Detects is_nil(param) in function guards that can be replaced with pattern matching nil directly in the function head.

Detects def/defp functions with an is_ prefix, which in Elixir is reserved for guard-safe functions defined with defguard or Erlang BIFs.

Detects qualified Kernel.op/2 calls used as steps in a pipeline.

Idiomatic rule: fixes variables that shadow Kernel functions.

Detects Keyword.get(list, integer) where the key is an integer literal.

Detects n = length(list) followed by Enum.at(list, n - K) — a Python list[len(list) - 1] idiom. Elixir's Enum.at/2 natively supports negative indices, so Enum.at(list, -1) is the idiomatic equivalent.

Detects length(list) comparisons with small integers (0–5) that can be replaced with O(1) pattern matching.

Refactoring rule: Detects guards that check list length with a literal comparison that can be replaced by a pattern match in the function head.

Performance rule: Detects the use of length/1 inside guard clauses (when) in cases that cannot be automatically rewritten as pattern matches.

Performance rule: Detects the use of ++ inside looping constructs that cannot be auto-fixed.

Performance rule: Detects acc ++ [expr] passed directly in a recursive tail call, where a matching base case returns the accumulator.

Performance rule: Detects acc ++ [expr] as the return value inside Enum.reduce/3 when the initial accumulator is [].

Performance rule: Detects List.delete_at/2 inside looping constructs (for, Enum.reduce, Enum.map, Enum.flat_map) or recursive functions.

Detects usage of List.foldl/3 and List.foldr/3 and suggests Enum.reduce/3 instead.

Performance & style rule: Detects converting a list to a tuple via List.to_tuple/1 and then accessing elements with elem/2. Tuples are meant for small, fixed-size collections. Copying a dynamically-sized list into a tuple just for indexed access defeats the purpose and allocates a full copy of the data. Use pattern matching ([a, b | _] = list) or Enum.at/2 on the list directly instead. For string processing, use binary_part/3 or binary pattern matching.

Performance and idiomatic code rule: warns when Enum.uniq/1 is manually reimplemented using Enum.reduce/3 and MapSet.

Readability rule: Detects manual frequency counting with Enum.reduce(list, %{}, fn x, acc -> Map.update(acc, x, 1, ...) end).

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

Detects if expressions that manually reimplement Kernel.max/2.

Detects if expressions that manually reimplement Kernel.min/2.

Readability & performance rule: Detects the pattern String.graphemes(s) |> Enum.reverse() |> Enum.join() (and the nested equivalent Enum.join(Enum.reverse(String.graphemes(s)))) which is a manual reimplementation of String.reverse/1.

Style rule: Detects using a Map with boolean literal values (true/false) purely for membership tracking, when MapSet is more appropriate.

Detects Map.get(map, key, -1) followed by a comparison against the sentinel — a Python dict.get(key, -1) idiom that leaks into LLM-generated Elixir.

Detects Map.keys(var) piped into an Enum function whose callback also looks up values from the same map variable.

Performance rule: Detects Map.values(map) or Map.keys(map) passed directly into an Enum function, which creates an unnecessary intermediate list.

Detects Map.values(map) or Map.keys(map) passed directly into Enum functions that return complex structures and cannot be safely auto-fixed.

Detects Enum.map/2 immediately followed by a terminal aggregation like Enum.max/1, Enum.min/1, or Enum.sum/1, which creates an unnecessary intermediate list.

Performance rule: Detects calling Map.update/4 (or Map.update!/3) on a map variable and then immediately reading the same key back with Map.fetch!/2 or Map.get/2.

Detects Logger macro calls without a require Logger in the enclosing module.

Readability & performance rule: Detects multiple Enum.at/2 calls on the same variable with literal indices. Each Enum.at/2 traverses the list from the head, so N calls cost O(N × len). Pattern matching destructures the list in a single pass.

Detects Enum.member?/2 calls nested inside another Enum.* traversal of the same enumerable and rewrites them to use MapSet.member?/2.

Detects nested Enum.* calls operating on the same enumerable where the inner call cannot be safely auto-fixed.

Style & correctness rule: Detects rebinding of parameter names inside anonymous function (fn) bodies. When a variable from the parameter destructure is rebound inside the body, readers lose track of which binding is "live" at each point. This is a common source of subtle bugs, especially in Enum.reduce callbacks where the accumulator is destructured.

Detects Regex.replace used as a pipe target and replaces it with String.replace, which accepts the string as its first argument.

Detects a variable (or tuple/list of plain variables) being assigned and immediately returned as the last two statements of a block.

Detects string literals needlessly wrapped in <<>> binary syntax.

Readability rule: Detects Enum.join("") and Enum.map_join("", mapper) where the empty-string separator is passed explicitly.

Detects multiple traversals of the same list that could be merged into a single pass.

Detects guard clauses that are logically redundant because a preceding clause of the same function already handles the complementary case.

Performance rule: warns when the same variable is traversed multiple times using different Enum functions.

Detects inefficient patterns where a full sort is performed only to retrieve a single element (the minimum or maximum).

Detects inefficient patterns where a full sort is performed only to retrieve a small number of elements (top-k) that cannot be reduced to a single Enum.min/1 or Enum.max/1 call.

Performance rule (fixable): Detects Enum.sort |> Enum.at(index) where the index is a literal 0 or -1 and the sort direction can be statically determined. These can be safely replaced with Enum.min/1 or Enum.max/1, avoiding the O(n log n) sort entirely.

Performance & readability rule: Detects the pattern of calling Enum.sort/1,2 followed by Enum.reverse/1 on the result, where the sort direction can be statically determined.

Detects length(String.split(string, separator)) - 1 used to count substring occurrences.

Performance rule: Detects string concatenation with <> inside Enum.reduce calls with an empty string initial accumulator that can be automatically fixed.

Performance rule: Detects string concatenation with <> inside complex looping constructs that cannot be safely auto-fixed.

Performance rule: Detects String.length(x) == 1 (or != 1) used to validate that a string is a single character. String.length/1 traverses the entire string to count grapheme clusters, making it O(n). For a simple single-character check, pattern matching on the result of String.graphemes/1 is more expressive and idiomatic. This rule automatically rewrites the comparison to use match?/2 with String.graphemes/1, which produces a clean boolean result that works in any expression context.

Detects Enum.take_while/2 piped into length/1 or Enum.count/1, which materializes an intermediate list only to count it.

Detects @doc, @moduledoc, and @typedoc strings that contain a trailing \n escape sequence.

Detects function names that use a leading underscore to indicate privacy, a convention borrowed from Python that is non-idiomatic in Elixir.

Detects unless ... do ... else ... end — a style guide violation.

Detects function clauses where every argument is a wildcard and the body does nothing but raise.

Fixes function clauses that are not grouped together.

Prefer Enum.sort(nums, :desc) |> Enum.take(n) over Enum.sort(nums) |> Enum.take(-n). This is about readability and intent clarity, not performance.

Performance rule: Flags Enum.reverse(list) ++ other_list. Enum.reverse/1 creates a new list, and ++ traverses that new list entirely to append the second. This is a 2-pass operation. Using Enum.reverse/2 performs both actions in a single optimized pass.

Readability and Intent rule: Flags usage of Enum.drop/2 followed by Enum.take/2. Calling Enum.drop(list, start) piped into Enum.take(length) is a verbose way of slicing a collection. It can be confusing to read at a glance. Elixir provides Enum.slice/3, which explicitly communicates the intent of extracting a sublist and handles the operation cleanly.

Replaces bare-variable float coercion tricks with explicit :erlang.float/1.

Detects @doc, @moduledoc, and @typedoc strings that contain escaped newlines (\n) and should use heredoc syntax instead.

Detects Map.has_key?/2 used in if/cond conditions, which typically leads to a double map lookup — once to check existence, again to get the value.

Detects redundant is_list/1 guards on variables already bound by a cons pattern [head | tail]).

Behaviour for pattern-level rules that detect and fix anti-patterns.

Detects the common n-gram generation pipeline that converts a string to graphemes, creates sliding window chunks of step 1, and joins them back into strings. This pattern is automatically fixed by replacing it with String.slice/3 which avoids intermediate list allocations.

Detects inefficient string transformation pipelines that convert strings to graphemes or codepoints, perform chunking or grouping, and reconstruct strings from the result. These patterns cannot be automatically fixed.

Detects Enum.map/2 chained into Enum.join/1 or Enum.join/2, and suggests Enum.map_join/3 instead.

Shared utilities used by all three Credence phases (Syntax, Semantic, Pattern).

Semantic phase — fixes compiler warnings and errors.

Fixes test modules that are missing use ExUnit.Case.

Fixes compiler warnings about outdented heredoc lines by re-indenting all body content to match the closing delimiter.

Behaviour for semantic-level rules that fix compiler warnings.

Fixes compiler warnings about undefined or deprecated functions with known replacements.

Fixes compiler errors about undefined local functions with known replacements.

Fixes the common LLM hallucination where String.alphanumeric?/1 is called as if it were a real Elixir standard library function. It is not.

Fixes compiler warnings about unused variables by adding _ prefix.

Fixes compiler warnings about underscored variables that are referenced after being set, by removing the leading underscore.

Syntax phase — fixes code that won't parse.

Fixes div and rem used as infix operators (Python // and % style).

Fixes @spec declarations where the :: return type separator is misplaced inside the argument parentheses.

Fixes Python-style scientific notation that is invalid in Elixir.

Removes non-Elixir access modifier keywords prepended to def/defp/defmacro/defmacrop.

Behaviour for syntax-level rules that fix code which won't parse.