Current section
Files
Jump to
Current section
Files
lib/rule/no_integer_to_string_digits.ex
defmodule Credence.Rule.NoIntegerToStringDigits do
@moduledoc """
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.
The string conversion creates an intermediate binary and then a charlist,
both of which are unnecessary allocations when you just need the digits.
## Bad
String.to_charlist(Integer.to_string(number, 2))
Integer.to_string(number, 2) |> String.to_charlist()
## Good
Integer.digits(number, 2)
"""
@behaviour Credence.Rule
alias Credence.Issue
@impl true
def check(ast, _opts) do
{_ast, issues} =
Macro.prewalk(ast, [], fn
# Nested: String.to_charlist(Integer.to_string(n, base))
{{:., _, [{:__aliases__, _, [:String]}, :to_charlist]}, meta,
[
{{:., _, [{:__aliases__, _, [:Integer]}, :to_string]}, _, _args}
]} = node,
issues ->
{node, [build_issue(meta) | issues]}
# Piped: Integer.to_string(n, base) |> String.to_charlist()
{:|>, meta,
[
{{:., _, [{:__aliases__, _, [:Integer]}, :to_string]}, _, _args},
{{:., _, [{:__aliases__, _, [:String]}, :to_charlist]}, _, _}
]} = node,
issues ->
{node, [build_issue(meta) | issues]}
# Piped from var: n |> Integer.to_string(base) |> String.to_charlist()
# The outer pipe has String.to_charlist on the right, and the
# inner pipe has Integer.to_string on the right.
{:|>, meta,
[
{:|>, _, [_, {{:., _, [{:__aliases__, _, [:Integer]}, :to_string]}, _, _}]},
{{:., _, [{:__aliases__, _, [:String]}, :to_charlist]}, _, _}
]} = node,
issues ->
{node, [build_issue(meta) | issues]}
node, issues ->
{node, issues}
end)
Enum.reverse(issues)
end
defp build_issue(meta) do
%Issue{
rule: :no_integer_to_string_digits,
severity: :warning,
message:
"Avoid `Integer.to_string/2 |> String.to_charlist/1` to extract digits. " <>
"Use `Integer.digits/2` instead — it produces the digit list directly without intermediate string allocation.",
meta: %{line: Keyword.get(meta, :line)}
}
end
end