Ragex.Analysis.DeadCode (Ragex v0.12.0)

View Source

Dead code detection for identifying unused functions and code.

Provides two complementary approaches:

Interprocedural Analysis (Graph-based)

Analyzes the knowledge graph to find:

  • Unused public functions (exported but never called externally)
  • Unused private functions (never called within module)
  • Functions with low confidence of being dead (potential entry points, callbacks)

Intraprocedural Analysis (AST-based via Metastatic)

Analyzes individual files for:

  • Unreachable code after early returns
  • Constant conditionals with unreachable branches
  • Other dead code patterns within function bodies

Provides confidence scores to help distinguish between truly dead code and potential entry points (callbacks, GenServer handlers, etc.).

Summary

Functions

Analyzes a file for intraprocedural dead code patterns.

Analyzes multiple files for intraprocedural dead code patterns.

Calculates a confidence score for dead code detection.

Finds all unused functions (both public and private).

Finds all dead code in the knowledge graph.

Finds unused functions within a specific module.

Finds unused public (exported) functions.

Finds unused modules (modules with no external references).

Finds unused private functions.

Generates removal suggestions based on dead code analysis.

Types

confidence()

@type confidence() :: float()

dead_function()

@type dead_function() :: %{
  function: function_ref(),
  confidence: confidence(),
  reason: String.t(),
  visibility: :public | :private,
  module: module(),
  metadata: map()
}

function_ref()

@type function_ref() ::
  {:function, module(), atom(), non_neg_integer()}
  | %{type: :function, module: module(), name: atom(), arity: non_neg_integer()}

suggestion()

@type suggestion() :: %{
  type: :remove_function | :review_function | :potential_callback,
  confidence: confidence(),
  target: function_ref(),
  description: String.t(),
  metadata: map()
}

Functions

analyze_file(file_path, opts \\ [])

@spec analyze_file(
  String.t(),
  keyword()
) :: {:ok, Metastatic.Analysis.DeadCode.Result.t()} | {:error, term()}

Analyzes a file for intraprocedural dead code patterns.

Uses Metastatic's AST-level analysis to detect:

  • Unreachable code after early returns
  • Constant conditionals (if true/false) with unreachable branches
  • Other dead code patterns within function bodies

This is complementary to the interprocedural analysis (find_unused_exports/1, etc.) which detects unused functions based on the call graph.

Parameters

  • file_path - Path to the file to analyze
  • opts - Keyword list of options
    • :min_confidence - Minimum confidence level (:high, :medium, :low, default: :low)

Returns

  • {:ok, result} - Metastatic.Analysis.DeadCode.Result struct
  • {:error, reason} - Error if analysis fails

Examples

iex> {:ok, result} = analyze_file("lib/my_module.ex")
iex> result.has_dead_code?
true

analyze_files(file_paths, opts \\ [])

@spec analyze_files(
  [String.t()],
  keyword()
) :: {:ok, %{required(String.t()) => any()}} | {:error, term()}

Analyzes multiple files for intraprocedural dead code patterns.

Batch version of analyze_file/2 that processes multiple files in parallel.

Parameters

  • file_paths - List of file paths to analyze
  • opts - Keyword list of options (same as analyze_file/2)

Returns

  • {:ok, results_map} - Map of file_path => result
  • {:error, reason} - Error if analysis fails

Examples

iex> {:ok, results} = analyze_files(["lib/a.ex", "lib/b.ex"])
iex> is_map(results)
true

confidence_score(func_ref, metadata)

@spec confidence_score(function_ref(), map()) :: confidence()

Calculates a confidence score for dead code detection.

Takes into account:

  • Function name patterns (callbacks, entry points)
  • Visibility (public vs private)
  • Number of callers
  • Module characteristics (test, Mix task, etc.)

Returns

  • Float between 0.0 (definitely not dead) and 1.0 (definitely dead)

find_all_unused(opts \\ [])

@spec find_all_unused(keyword()) :: {:ok, [dead_function()]} | {:error, term()}

Finds all unused functions (both public and private).

Combines results from find_unused_exports/1 and find_unused_private/1.

Parameters

  • opts: Keyword list of options (same as individual functions)

Returns

  • {:ok, [dead_function]} - Combined list of unused functions

Examples

{:ok, all_dead} = find_all_unused(min_confidence: 0.6)

find_dead_code()

@spec find_dead_code() :: {:ok, [map()]} | {:error, term()}

Finds all dead code in the knowledge graph.

Convenience function combining unused exports and private functions.

Examples

{:ok, dead_functions} = DeadCode.find_dead_code()

find_in_module(module, opts \\ [])

@spec find_in_module(
  module(),
  keyword()
) :: {:ok, [dead_function()]} | {:error, term()}

Finds unused functions within a specific module.

Analyzes all functions (public and private) in the specified module to identify potentially dead code. This is useful for focused refactoring of a single module.

Parameters

  • module: Module name atom
  • opts: Keyword list of options
    • :min_confidence - Minimum confidence threshold (default: 0.5)
    • :include_callbacks - Include potential callbacks (default: false)
    • :visibility - Filter by :public, :private, or :all (default: :all)

Returns

  • {:ok, [dead_function]} - List of unused functions in the module
  • {:error, reason} - Error if module not found or analysis fails

Examples

# Find all dead code in MyModule
{:ok, dead} = find_in_module(MyModule)

# Find only dead private functions with high confidence
{:ok, dead_private} = find_in_module(MyModule, visibility: :private, min_confidence: 0.8)

# Include potential callbacks
{:ok, all} = find_in_module(MyModule, include_callbacks: true, min_confidence: 0.3)

find_unused_exports(opts \\ [])

@spec find_unused_exports(keyword()) :: {:ok, [dead_function()]} | {:error, term()}

Finds unused public (exported) functions.

Public functions with no callers are potentially dead code, but may be:

  • Entry points (main, CLI commands)
  • Callbacks (GenServer, Supervisor, Phoenix)
  • API functions not yet used
  • Test helpers

Returns functions with confidence scores indicating likelihood of being dead.

Parameters

  • opts: Keyword list of options
    • :min_confidence - Minimum confidence threshold (0.0-1.0, default: 0.5)
    • :exclude_tests - Exclude test modules (default: true)
    • :include_callbacks - Include potential callbacks (default: false)
    • :ai_refine - Use AI to refine confidence scores (default: from config)

Returns

  • {:ok, [dead_function]} - List of potentially unused functions with confidence scores
  • {:error, reason} - Error if analysis fails

Examples

# Find unused exports with high confidence
{:ok, dead} = find_unused_exports(min_confidence: 0.8)

# Include potential callbacks
{:ok, all} = find_unused_exports(include_callbacks: true, min_confidence: 0.3)

find_unused_modules(opts \\ [])

@spec find_unused_modules(keyword()) :: {:ok, [module()]} | {:error, term()}

Finds unused modules (modules with no external references).

Delegates to DependencyGraph.find_unused/1 for consistency.

Parameters

  • opts: Keyword list of options (passed to DependencyGraph)

Returns

  • {:ok, [module_name]} - List of unused modules
  • {:error, reason} - Error if analysis fails

find_unused_private(opts \\ [])

@spec find_unused_private(keyword()) :: {:ok, [dead_function()]} | {:error, term()}

Finds unused private functions.

Private functions with no callers within their module are likely dead code. Higher confidence than public functions since private functions are not part of the API.

Parameters

  • opts: Keyword list of options
    • :min_confidence - Minimum confidence threshold (default: 0.7)
    • :exclude_tests - Exclude test modules (default: true)
    • :ai_refine - Use AI to refine confidence scores (default: from config)

Returns

  • {:ok, [dead_function]} - List of unused private functions
  • {:error, reason} - Error if analysis fails

Examples

{:ok, dead} = find_unused_private()

removal_suggestions(opts \\ [])

@spec removal_suggestions(keyword()) :: {:ok, [suggestion()]} | {:error, term()}

Generates removal suggestions based on dead code analysis.

Categorizes dead code into:

  • Safe to remove (high confidence)
  • Review recommended (medium confidence)
  • Potential callbacks (low confidence, may be entry points)

Parameters

  • opts: Keyword list of options
    • :min_confidence - Minimum confidence for suggestions (default: 0.5)
    • :group_by_module - Group suggestions by module (default: true)

Returns

  • {:ok, [suggestion]} - List of removal suggestions
  • {:error, reason} - Error if analysis fails

Examples

{:ok, suggestions} = removal_suggestions()
# Process suggestions...