Metastatic.Analysis.UnusedVariables (Metastatic v0.10.4)

View Source

Unused variable detection at the MetaAST level.

Identifies variables that are assigned but never read, including function parameters, loop iterators, and pattern matches. Works across all supported languages by operating on the unified MetaAST representation.

This module implements both:

  • Standalone API - Direct analysis via analyze/2 returning structured results
  • Analyzer behaviour - Plugin integration for batch analysis via Runner

Detection Categories

  • Local variables - Assigned but never referenced
  • Function parameters - Lambda parameters never used in body
  • Loop iterators - Loop variables never accessed
  • Pattern matches - Variables bound in patterns but not used

Standalone Usage

alias Metastatic.{Document, Analysis.UnusedVariables}

# Analyze for unused variables
ast = {:block, [], [
  {:assignment, [], [{:variable, [], "x"}, {:literal, [subtype: :integer], 42}]},
  {:literal, [subtype: :integer], 5}
]}
doc = Document.new(ast, :python)
{:ok, result} = UnusedVariables.analyze(doc)

result.has_unused?       # => true
result.total_unused      # => 1
result.unused_variables  # => [%{name: "x", category: :local, ...}]

Plugin Usage

alias Metastatic.Analysis.{Registry, Runner}

# Register as analyzer plugin
Registry.register(UnusedVariables)

# Run via Runner
{:ok, report} = Runner.run(doc)

Configuration (when used as plugin)

  • :ignore_prefix - Ignore variables starting with this prefix (default: "_")
  • :ignore_names - List of variable names to ignore (default: [])

Examples

# No unused variables
iex> ast = {:assignment, [], [{:variable, [], "x"}, {:literal, [subtype: :integer], 5}]}
iex> doc = Metastatic.Document.new(ast, :python)
iex> {:ok, result} = Metastatic.Analysis.UnusedVariables.analyze(doc)
iex> result.has_unused?
false

# Unused local variable
iex> ast = {:block, [], [
...>   {:assignment, [], [{:variable, [], "x"}, {:literal, [subtype: :integer], 1}]},
...>   {:literal, [subtype: :integer], 2}
...> ]}
iex> doc = Metastatic.Document.new(ast, :python)
iex> {:ok, result} = Metastatic.Analysis.UnusedVariables.analyze(doc)
iex> result.has_unused?
true
iex> [var | _] = result.unused_variables
iex> var.name
"x"
iex> var.category
:local

Summary

Functions

analyze(language_or_doc, source_or_ast_or_opts \\ [], opts \\ [])

@spec analyze(Metastatic.language(), term(), keyword()) ::
  {:ok, map()} | {:error, term()}

Analyzes a document for unused variables.

Returns {:ok, result} where result is a Metastatic.Analysis.UnusedVariables.Result struct.

Options

  • :ignore_underscore - Ignore variables starting with underscore (default: true)
  • :categories - List of categories to check (default: all)

Examples

iex> ast = {:literal, [subtype: :integer], 42}
iex> doc = Metastatic.Document.new(ast, :elixir)
iex> {:ok, result} = Metastatic.Analysis.UnusedVariables.analyze(doc)
iex> result.has_unused?
false

analyze!(language_or_doc, source_or_ast_or_opts \\ [], opts \\ [])

@spec analyze!(Metastatic.language(), term(), keyword()) :: map()

Analyzes a document for unused variables.

Returns {:ok, result} where result is a Metastatic.Analysis.UnusedVariables.Result struct.

Options

  • :ignore_underscore - Ignore variables starting with underscore (default: true)
  • :categories - List of categories to check (default: all)

Examples

iex> ast = {:literal, [subtype: :integer], 42}
iex> doc = Metastatic.Document.new(ast, :elixir)
iex> {:ok, result} = Metastatic.Analysis.UnusedVariables.analyze(doc)
iex> result.has_unused?
false

Unlike not-banged version, this one either returns a result or raises

info()