Search documentation of Ragex

# Ragex.Analysis.Quality (Ragex v0.4.0)

View Source

High-level API for code quality analysis.

Provides a unified interface for analyzing code quality metrics and storing/querying results. Combines MetastaticBridge for analysis and QualityStore for persistence.

## Features

- File and directory analysis with complexity metrics
- Automatic storage in knowledge graph
- Quality reporting and statistics
- Finding complex code patterns
- Purity analysis

## Usage

    alias Ragex.Analysis.Quality

    # Analyze single file
    {:ok, result} = Quality.analyze_file("lib/my_module.ex")

    # Analyze with options
    {:ok, result} = Quality.analyze_file("lib/my_module.ex",
      metrics: [:cyclomatic, :cognitive],
      store: true
    )

    # Analyze directory
    {:ok, results} = Quality.analyze_directory("lib/")

    # Get statistics
    stats = Quality.statistics()

    # Find complex files
    complex = Quality.find_complex(metric: :cyclomatic, threshold: 10)

## Summary

### Types

analysis_result()
quality_report()

### Functions

analyze_directory(path, opts \\ [])

Analyzes all files in a directory.

analyze_file(path, opts \\ [])

Analyzes a single file for code quality metrics.

analyze_function(module, function, arity, opts \\ [])

Analyzes a specific function's quality metrics.

analyze_module(module, opts \\ [])

Analyzes all functions in a module.

analyze_quality(path, opts \\ [])

Analyzes directory quality metrics.

clear_all()

Clears all stored quality metrics.

comprehensive_report(path, opts \\ [])

Generates a comprehensive report including both quality and security metrics.

count()

Returns the number of files with stored quality metrics.

find_complex(opts \\ [])

Finds files exceeding complexity thresholds.

find_complex_code(path, opts \\ [])

Finds complex code in a directory.

find_impure()

Finds impure files (files with side effects).

find_with_warnings()

Finds files with analysis warnings.

generate_report(opts \\ [])

Generates a comprehensive quality report.

get_metrics(path)

Retrieves stored quality metrics for a file.

most_complex(opts \\ [])

Returns the most complex files.

statistics()

Returns project-wide quality statistics.

statistics_by_language()

Returns quality statistics grouped by language.

## Types

# analysis_result()

    @type analysis_result() :: %{
      path: String.t(),
      language: atom(),
      complexity: map(),
      purity: map(),
      warnings: [String.t()],
      timestamp: DateTime.t()
    }

# quality_report()

    @type quality_report() :: %{
      total_files: non_neg_integer(),
      avg_cyclomatic: float(),
      avg_cognitive: float(),
      avg_nesting: float(),
      max_cyclomatic: non_neg_integer(),
      max_cognitive: non_neg_integer(),
      max_nesting: non_neg_integer(),
      files_with_warnings: non_neg_integer(),
      impure_files: non_neg_integer(),
      languages: %{required(atom()) => non_neg_integer()}
    }

## Functions

# analyze_directory(path, opts \\ [])

    @spec analyze_directory(
      String.t(),
      keyword()
    ) :: {:ok, ok: analysis_result(), error: term()} | {:error, term()}

Analyzes all files in a directory.

Recursively analyzes all supported source files in the directory and optionally stores results in the knowledge graph.

## Parameters

- `path`: Directory path to analyze
- `opts`: Keyword list of options
  - `:recursive` - Recursively analyze subdirectories (default: true)
  - `:store` - Store results in knowledge graph (default: true)
  - `:metrics` - List of metrics to calculate (default: all)
  - `:parallel` - Use parallel processing (default: true)
  - `:max_concurrency` - Maximum concurrent analyses (default: System.schedulers_online())

## Returns

- `{:ok, results}` - List of analysis results (mix of `{:ok, result}` and `{:error, reason}`)
- `{:error, reason}` - Error if directory access fails

## Examples

    # Analyze entire lib directory
    {:ok, results} = Quality.analyze_directory("lib/")

    # Analyze without storing
    {:ok, results} = Quality.analyze_directory("lib/", store: false)

    # Sequential analysis (useful for debugging)
    {:ok, results} = Quality.analyze_directory("lib/", parallel: false)

# analyze_file(path, opts \\ [])

    @spec analyze_file(
      String.t(),
      keyword()
    ) :: {:ok, analysis_result()} | {:error, term()}

Analyzes a single file for code quality metrics.

Performs comprehensive quality analysis including complexity metrics and purity analysis. Optionally stores results in the knowledge graph for later querying.

## Parameters

- `path`: Path to the file to analyze
- `opts`: Keyword list of options
  - `:metrics` - List of specific metrics to calculate (default: all)
  - `:store` - Store results in knowledge graph (default: true)
  - `:thresholds` - Custom threshold map for warnings
  - `:language` - Explicit language (default: auto-detect)

## Returns

- `{:ok, analysis_result}` - Analysis results with metrics
- `{:error, reason}` - Error if analysis fails

## Examples

    # Analyze with default options (all metrics, auto-store)
    {:ok, result} = Quality.analyze_file("lib/my_module.ex")
    result.complexity.cyclomatic  # => 5

    # Analyze specific metrics without storing
    {:ok, result} = Quality.analyze_file("lib/my_module.ex",
      metrics: [:cyclomatic, :cognitive],
      store: false
    )

    # Analyze with custom thresholds
    {:ok, result} = Quality.analyze_file("lib/my_module.ex",
      thresholds: %{cyclomatic: 15, cognitive: 10}
    )

# analyze_function(module, function, arity, opts \\ [])

    @spec analyze_function(module(), atom(), non_neg_integer(), keyword()) ::
      {:ok, map()} | {:error, term()}

Analyzes a specific function's quality metrics.

Extracts function-level metrics from the file's stored analysis results. The file must have been analyzed and stored first using `analyze_file/2`.

## Parameters

- `module`: Module name atom
- `function`: Function name atom
- `arity`: Function arity (non-negative integer)
- `opts`: Keyword list of options
  - `:path` - Explicit file path (default: lookup from module in graph)
  - `:analyze_if_missing` - Analyze file if not found in store (default: false)

## Returns

- `{:ok, function_metrics}` - Map with function-specific metrics
- `{:error, :not_found}` - File not analyzed or function not found
- `{:error, reason}` - Other errors

## Function Metrics Structure

    %{
      module: MyModule,
      function: :my_function,
      arity: 2,
      cyclomatic: 3,
      cognitive: 2,
      nesting: 1,
      halstead: %{...},
      loc: %{total: 10, code: 8, comments: 2}
    }

## Examples

    # Analyze a specific function (file must be analyzed first)
    {:ok, result} = Quality.analyze_file("lib/my_module.ex")
    {:ok, func_metrics} = Quality.analyze_function(MyModule, :my_function, 2)

    # With auto-analysis if not found
    {:ok, func_metrics} = Quality.analyze_function(MyModule, :my_function, 2,
      analyze_if_missing: true
    )

    # With explicit path
    {:ok, func_metrics} = Quality.analyze_function(MyModule, :my_function, 2,
      path: "lib/my_module.ex"
    )

# analyze_module(module, opts \\ [])

    @spec analyze_module(
      module(),
      keyword()
    ) :: {:ok, map()} | {:error, term()}

Analyzes all functions in a module.

Retrieves quality metrics for all functions in the specified module. The module's file must have been analyzed and stored first.

## Parameters

- `module`: Module name atom
- `opts`: Keyword list of options
  - `:path` - Explicit file path (default: lookup from module in graph)
  - `:analyze_if_missing` - Analyze file if not found in store (default: false)
  - `:sort_by` - Sort functions by metric: `:cyclomatic`, `:cognitive`, `:nesting`, `:name` (default: `:name`)
  - `:threshold` - Only return functions exceeding this complexity (optional)

## Returns

- `{:ok, module_analysis}` - Map with module-level and per-function metrics
- `{:error, reason}` - Error if module not found or analysis fails

## Module Analysis Structure

    %{
      module: MyModule,
      file: "lib/my_module.ex",
      language: :elixir,
      total_cyclomatic: 25,
      total_cognitive: 18,
      function_count: 5,
      functions: [
        %{function: :func1, arity: 2, cyclomatic: 3, ...},
        %{function: :func2, arity: 1, cyclomatic: 5, ...}
      ]
    }

## Examples

    # Analyze all functions in a module
    {:ok, analysis} = Quality.analyze_module(MyModule)
    analysis.function_count  # => 5

    # Sort by complexity
    {:ok, analysis} = Quality.analyze_module(MyModule, sort_by: :cyclomatic)

    # Only complex functions
    {:ok, analysis} = Quality.analyze_module(MyModule, threshold: 10)

    # With auto-analysis
    {:ok, analysis} = Quality.analyze_module(MyModule, analyze_if_missing: true)

# analyze_quality(path, opts \\ [])

    @spec analyze_quality(
      String.t(),
      keyword()
    ) :: {:ok, map()} | {:error, term()}

Analyzes directory quality metrics.

Convenience function that returns a quality score and statistics.

## Options

- `:min_complexity` - Complexity threshold (default: 10)

## Examples

    {:ok, metrics} = Quality.analyze_quality("lib/")
    metrics.overall_score  # => 75

# clear_all()

    @spec clear_all() :: :ok

Clears all stored quality metrics.

Removes all quality_metrics nodes from the knowledge graph. Does not affect other graph data (modules, functions, etc.).

## Examples

    :ok = Quality.clear_all()

# comprehensive_report(path, opts \\ [])

    @spec comprehensive_report(
      String.t(),
      keyword()
    ) :: {:ok, map()} | {:error, term()}

Generates a comprehensive report including both quality and security metrics.

This convenience function combines quality analysis with security scanning to provide a holistic view of code health.

## Parameters

- `path`: Directory path to analyze
- `opts`: Keyword list of options
  - `:min_severity` - Minimum security severity to report (default: `:medium`)
  - `:include_security` - Include security analysis (default: `true`)
  - All options from `analyze_directory/2`

## Returns

- `{:ok, report}` - Comprehensive report map
- `{:error, reason}` - Analysis failed

## Report Structure

    %{
      quality: %{
        statistics: quality_report(),
        most_complex: [{path, complexity}],
        with_warnings: [{path, warnings}],
        impure_files: [path]
      },
      security: %{
        total_vulnerabilities: integer(),
        by_severity: %{critical: integer(), high: integer(), ...},
        files_with_vulnerabilities: [path],
        summary: string()
      }
    }

## Examples

    # Full analysis with security
    {:ok, report} = Quality.comprehensive_report("lib/")
    report.quality.statistics.avg_cyclomatic  # => 4.5
    report.security.total_vulnerabilities     # => 3

    # Quality only
    {:ok, report} = Quality.comprehensive_report("lib/", include_security: false)

    # Only critical security issues
    {:ok, report} = Quality.comprehensive_report("lib/", min_severity: :critical)

# count()

    @spec count() :: non_neg_integer()

Returns the number of files with stored quality metrics.

## Examples

    count = Quality.count()  # => 42

# find_complex(opts \\ [])

    @spec find_complex(keyword()) :: [String.t()]

Finds files exceeding complexity thresholds.

## Parameters

- `opts`: Keyword list of options
  - `:metric` - Metric to evaluate: `:cyclomatic`, `:cognitive`, `:nesting` (default: `:cyclomatic`)
  - `:threshold` - Threshold value (default: 10)
  - `:operator` - Comparison: `:gt`, `:gte`, `:lt`, `:lte`, `:eq` (default: `:gt`)
  - `:limit` - Maximum results (default: 20)

## Returns

- List of file paths exceeding threshold

## Examples

    # Find files with cyclomatic complexity > 10
    complex = Quality.find_complex(threshold: 10)

    # Find files with cognitive complexity >= 15
    complex = Quality.find_complex(
      metric: :cognitive,
      threshold: 15,
      operator: :gte
    )

    # Find top 5 most complex files
    complex = Quality.find_complex(threshold: 5, limit: 5)

# find_complex_code(path, opts \\ [])

    @spec find_complex_code(
      String.t(),
      keyword()
    ) :: {:ok, [map()]} | {:error, term()}

Finds complex code in a directory.

Convenience function that analyzes a directory and returns functions exceeding the complexity threshold.

## Options

- `:min_complexity` - Minimum cyclomatic complexity (default: 10)

## Examples

    {:ok, functions} = Quality.find_complex_code("lib/", min_complexity: 15)

# find_impure()

    @spec find_impure() :: [String.t()]

Finds impure files (files with side effects).

## Returns

- List of file paths with side effects detected

## Examples

    impure = Quality.find_impure()
    # => ["lib/database.ex", "lib/logger.ex"]

# find_with_warnings()

    @spec find_with_warnings() :: [{String.t(), [String.t()]}]

Finds files with analysis warnings.

## Returns

- List of `{path, warnings}` tuples

## Examples

    files_with_warnings = Quality.find_with_warnings()
    # => [{"lib/complex.ex", ["High cyclomatic complexity: 15"]}]

# generate_report(opts \\ [])

    @spec generate_report(keyword()) :: map() | String.t()

Generates a comprehensive quality report.

## Parameters

- `opts`: Keyword list of options
  - `:type` - Report type: `:summary`, `:detailed`, `:by_language` (default: `:summary`)
  - `:format` - Output format: `:text`, `:map` (default: `:map`)

## Returns

- Report content (map or formatted string based on format option)

## Examples

    # Summary report as map
    report = Quality.generate_report()

    # Detailed report as text
    report = Quality.generate_report(type: :detailed, format: :text)

    # Language breakdown
    report = Quality.generate_report(type: :by_language)

# get_metrics(path)

    @spec get_metrics(String.t()) :: {:ok, map()} | {:error, :not_found}

Retrieves stored quality metrics for a file.

## Parameters

- `path`: File path

## Returns

- `{:ok, metrics}` - Stored metrics map
- `{:error, :not_found}` - File not analyzed or metrics not stored

## Examples

    {:ok, metrics} = Quality.get_metrics("lib/my_module.ex")
    metrics.cyclomatic  # => 5

# most_complex(opts \\ [])

    @spec most_complex(keyword()) :: [{String.t(), number()}]

Returns the most complex files.

## Parameters

- `opts`: Keyword list of options
  - `:metric` - Metric to rank by: `:cyclomatic`, `:cognitive`, `:nesting` (default: `:cyclomatic`)
  - `:limit` - Number of results (default: 10)

## Returns

- List of `{path, metric_value}` tuples, sorted by complexity

## Examples

    # Top 10 most complex files by cyclomatic complexity
    top = Quality.most_complex()

    # Top 5 by cognitive complexity
    top = Quality.most_complex(metric: :cognitive, limit: 5)

# statistics()

    @spec statistics() :: quality_report()

Returns project-wide quality statistics.

Aggregates metrics from all analyzed files stored in the knowledge graph.

## Returns

- Quality statistics map with averages, maximums, and counts

## Examples

    stats = Quality.statistics()
    stats.total_files        # => 42
    stats.avg_cyclomatic     # => 3.5
    stats.max_cognitive      # => 25

# statistics_by_language()

    @spec statistics_by_language() :: %{required(atom()) => quality_report()}

Returns quality statistics grouped by language.

## Returns

- Map of language => statistics

## Examples

    by_lang = Quality.statistics_by_language()
    by_lang[:elixir].avg_cyclomatic  # => 4.2
    by_lang[:python].avg_cognitive   # => 5.1