Current section

Files

Jump to
credence lib semantic unused_variable.ex
Raw

lib/semantic/unused_variable.ex

defmodule Credence.Semantic.UnusedVariable do
@moduledoc """
Fixes compiler warnings about unused variables by adding `_` prefix.
LLMs often generate destructuring patterns where not all bound variables
are used, causing `--warnings-as-errors` to fail compilation.
## Example
# Warning: variable "current_sum" is unused
{current_sum, max_sum} = Enum.reduce(...)
# Fixed:
{_current_sum, max_sum} = Enum.reduce(...)
"""
use Credence.Semantic.Rule
alias Credence.Issue
@impl true
def match?(%{severity: :warning, message: msg}) do
String.match?(msg, ~r/variable ".*" is unused/)
end
def match?(_), do: false
@impl true
def to_issue(%{message: msg, position: position}) do
%Issue{
rule: :unused_variable,
message: msg,
meta: %{line: extract_line(position)}
}
end
@impl true
def fix(source, %{message: msg, position: position}) do
line_no = extract_line(position)
var_name = extract_variable_name(msg)
if line_no && var_name && not String.starts_with?(var_name, "_") do
replace_on_line(source, line_no, var_name, "_#{var_name}")
else
source
end
end
defp extract_line({line, _col}) when is_integer(line), do: line
defp extract_line(line) when is_integer(line), do: line
defp extract_line(_), do: nil
defp extract_variable_name(msg) do
case Regex.run(~r/variable "([^"]+)" is unused/, msg) do
[_, name] -> name
_ -> nil
end
end
defp replace_on_line(source, line_no, old, new) do
source
|> String.split("\n")
|> Enum.with_index(1)
|> Enum.map(fn
{line, ^line_no} -> String.replace(line, old, new, global: false)
{line, _} -> line
end)
|> Enum.join("\n")
end
end