API Reference credence v#0.3.2
Copy MarkdownModules
Credence (Semantic Linter for Elixir) Main entry point for analyzing Elixir code.
Defines the structured issue format for any rule violations.
Behaviour for all Credence semantic rules.
Performance rule: Detects the use of Enum.count/1 or Enum.count/2 on
the result of String.graphemes/1.
Performance rule: Detects the use of length/1 on the result of
String.graphemes/1.
Maintainability rule: Detects single-letter variable names in function signatures.
Detects functions where the same positional parameter uses different variable names across clauses.
Readability rule: Detects anonymous functions applied with .() inside
a pipeline.
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 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 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: warns when variables shadow Kernel functions.
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 rule: Flags usage of List.last/1.
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.
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 expr * 1.0 used to coerce an integer result to a float.
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.
Readability rule: Detects Enum.join("") and Enum.map_join("", mapper)
where the empty-string separator is passed explicitly.
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 subset): Detects Enum.sort |> Enum.at(index) where
the index is a literal 0 or -1. These can be safely replaced with
Enum.min/1 or Enum.max/1, avoiding the O(n log n) sort entirely.
Performance rule (flag-only companion): Detects Enum.sort |> Enum.at(index)
patterns where the index is not a compile-time literal 0 or -1, or the
sort direction cannot be statically determined.
Performance & readability rule: Detects the pattern of calling Enum.sort/1,2
followed by Enum.reverse/1 on the result.
Readability rule: Detects the variable-mediated pattern of assigning
Enum.sort/1,2 to a variable and then calling Enum.reverse/1 on that
variable later in the same scope.
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 function clauses where every argument is a wildcard and the
body does nothing but raise.
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.
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]).
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.
Detects @doc, @moduledoc, and @typedoc strings that contain
escaped newlines (\n) and should use heredoc syntax instead.