Search documentation of Ragex

# Ragex.Analysis.DeadCode (Ragex v0.4.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

### Types

confidence()
dead_function()
function_ref()
suggestion()

### Functions

analyze_file(file_path, opts \\ [])

Analyzes a file for intraprocedural dead code patterns.

analyze_files(file_paths, opts \\ [])

Analyzes multiple files for intraprocedural dead code patterns.

confidence_score(func_ref, metadata)

Calculates a confidence score for dead code detection.

find_all_unused(opts \\ [])

Finds all unused functions (both public and private).

find_dead_code()

Finds all dead code in the knowledge graph.

find_in_module(module, opts \\ [])

Finds unused functions within a specific module.

find_unused_exports(opts \\ [])

Finds unused public (exported) functions.

find_unused_modules(opts \\ [])

Finds unused modules (modules with no external references).

find_unused_private(opts \\ [])

Finds unused private functions.

removal_suggestions(opts \\ [])

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...