Packages
metastatic
0.20.1
0.26.0
0.25.0
0.24.1
0.24.0
0.23.0
0.22.2
0.22.1
0.22.0
0.21.3
0.21.2
0.21.1
0.21.0
0.20.3
0.20.2
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.1
0.15.0
0.14.2
0.14.1
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.0
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.2
0.9.1
0.9.0
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.2
0.4.1
0.4.0
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Cross-language code meta-model library using unified MetaAST representation. Parse, transform, and translate code across Python, Elixir, Ruby, Erlang, Haskell, and more via a shared three-tuple AST format.
Current section
Files
Jump to
Current section
Files
lib/metastatic/analysis/dead_code_analyzer.ex
defmodule Metastatic.Analysis.DeadCodeAnalyzer do
@moduledoc """
Plugin analyzer wrapper for dead code detection.
This module wraps the existing `Metastatic.Analysis.DeadCode` module as an
`Analyzer` behaviour plugin for use with the Runner system. It detects:
- **Unreachable code after returns** - Code following early_return nodes
- **Constant conditionals** - Branches that can never execute (if true/false)
- **Other dead patterns** - Unused assignments, etc.
## Usage
alias Metastatic.{Document, Analysis.Runner}
# Register as plugin
Registry.register(DeadCodeAnalyzer)
# Run via Runner
{:ok, report} = Runner.run(doc)
## Configuration
- `:min_confidence` - Minimum confidence to report (default: :low)
- `:low` - Report all dead code
- `:medium` - Report high-confidence issues only
- `:high` - Report only definite dead code
## Examples
# Unreachable after return
iex> ast = {:block, [], [
...> {:early_return, [], [{:literal, [subtype: :integer], 1}]},
...> {:literal, [subtype: :integer], 2}
...> ]}
iex> doc = Metastatic.Document.new(ast, :python)
iex> {:ok, report} = Metastatic.Analysis.Runner.run(doc,
...> analyzers: [Metastatic.Analysis.DeadCodeAnalyzer])
iex> length(report.issues)
1
iex> [issue | _] = report.issues
iex> issue.category
:correctness
"""
@behaviour Metastatic.Analysis.Analyzer
alias Metastatic.Analysis.{Analyzer, DeadCode}
# ----- Behaviour Callbacks -----
@impl true
def info do
%{
name: :dead_code,
category: :correctness,
description: "Detects unreachable and dead code patterns",
severity: :warning,
explanation: """
Dead code is code that can never be executed. This includes:
- Code after return statements
- Branches in constant conditionals
- Unreachable paths
Dead code should be removed to keep the codebase clean and maintainable.
""",
configurable: true
}
end
@impl true
def run_before(context) do
# Convert config from map to keyword list if needed
opts =
case context.config do
config when is_list(config) -> config
config when is_map(config) -> Map.to_list(config)
_other -> []
end
# Run the standalone DeadCode analyzer on the full document
case DeadCode.analyze(context.document, opts) do
{:ok, result} ->
# Store result for later use
context = Map.put(context, :dead_code_result, result)
{:ok, context}
{:error, reason} ->
{:skip, reason}
end
end
@impl true
def analyze(_node, _context) do
# Individual node analysis not needed; all analysis done in run_before
[]
end
@impl true
def run_after(context, issues) do
# Convert dead code results to analyzer issues
case Map.get(context, :dead_code_result) do
nil ->
issues
result ->
new_issues = convert_dead_code_results(result, context)
issues ++ new_issues
end
end
# ----- Private Helpers -----
defp convert_dead_code_results(result, context) do
dead_locations = Map.get(result, :dead_locations, [])
Enum.map(dead_locations, fn location ->
severity =
case Map.get(location, :confidence, :low) do
:high -> :warning
:medium -> :info
:low -> :info
end
node = Map.get(location, :context, %{}) |> Map.get(:ast)
# Let Analyzer.issue/1 extract location from node metadata automatically
# Don't override with explicit nil location
Analyzer.issue(
analyzer: __MODULE__,
category: :correctness,
severity: severity,
message: Map.get(location, :reason, "Dead code detected"),
node: node || context.document.ast,
suggestion:
Analyzer.suggestion(
type: :remove,
replacement: nil,
message: Map.get(location, :suggestion, "Remove dead code")
),
metadata: Map.put(location, :type, Map.get(location, :type))
)
end)
end
end