cyclomatic_complexity

A Crystal library and CLI tool for calculating cyclomatic complexity of Crystal source code. This library is designed to integrate with quality measurement tools like **Criterion** for comprehensive Crystal code analysis.

Cyclomatic Complexity Analyzer for Crystal

A Crystal library and CLI tool for calculating cyclomatic complexity of Crystal source code. This library is designed to integrate with quality measurement tools like Criterion for comprehensive Crystal code analysis.

License: MIT

What is Cyclomatic Complexity?

Cyclomatic complexity is a software metric that measures the number of linearly independent paths through a program's source code. It was developed by Thomas J. McCabe in 1976 and is widely used to:

  • Assess code maintainability: Higher complexity means harder to maintain
  • Guide testing efforts: More complex code needs more thorough testing
  • Identify refactoring candidates: Methods with high complexity should be simplified
  • Ensure code quality: Keep complexity within reasonable bounds

Features

  • 🔍 Accurate Analysis: Parses Crystal AST for precise complexity calculation
  • 📊 Multiple Output Formats: Text and JSON reports
  • 🎯 Granular Metrics: Method-level and file-level complexity analysis
  • 📈 Quality Grades: A-F grading system for easy assessment
  • 🔧 CLI Tool: Command-line interface for CI/CD integration
  • 📚 Library API: Simple API for integration with other tools
  • 🎨 Risk Assessment: Categorizes methods by risk level

Installation

Add this to your application's shard.yml:

dependencies:
  cyclomatic_complexity:
    github: jadekharats/cyclomatic_complexity
    version: ~> 0.1.0

Then run:

shards install

CLI Usage

Installation

git clone https://github.com/jadekharats/cyclomatic_complexity.git
cd cyclomatic_complexity
shards build --release
cp ./bin/cyclomatic /usr/local/bin/

Examples

# Analyze a single file
cyclomatic -f src/main.cr

# Analyze entire directory
cyclomatic -p src/

# Generate JSON report
cyclomatic -p src/ -r json -o complexity_report.json

# Verbose output
cyclomatic -p . --verbose

# Get help
cyclomatic --help

CLI Options

Option Description
-f, --file=FILE Analyze a specific file
-p, --path=PATH Analyze a directory
-r, --reporter=FORMAT Output format (txt|json)
-o, --output=OUTPUT Output file path
-v, --verbose Verbose mode
-h, --help Show help
--version Show version

Library Usage

Basic API

require "cyclomatic_complexity"

# Analyze a single file
file_complexity = CyclomaticComplexity.analyze_file("src/my_file.cr")
puts "Average complexity: #{file_complexity.average_complexity}"
puts "Grade: #{file_complexity.grade}"

# Analyze entire directory
files = CyclomaticComplexity.analyze_directory("src/")
puts "Total files: #{files.size}"

# Generate reports
text_report = CyclomaticComplexity.generate_report(files, "text")
json_report = CyclomaticComplexity.generate_report(files, "json")

Advanced Usage

require "cyclomatic_complexity"

# Detailed analysis
files = CyclomaticComplexity.analyze_directory("src/")
report = CyclomaticComplexity::ComplexityReport.new(files)

puts "Overall grade: #{report.overall_grade}"
puts "High risk files: #{report.summary["high_risk_files"]}"

# Find most complex methods
report.worst_files(5).each do |file|
  puts "\n#{file.filename} (#{file.grade})"
  file.worst_methods(3).each do |method|
    puts "  #{method.name}: #{method.complexity} (#{method.risk_level})"
  end
end

Complexity Calculation

What Increases Complexity

Each of these language constructs adds +1 to complexity:

  • Conditionals: if, else, elsif, unless
  • Loops: while, until
  • Case statements: each when clause
  • Logical operators: &&, ||
  • Exception handling: each rescue clause

Base Complexity

Every method starts with a base complexity of 1.

Examples

# Complexity: 1 (base)
def simple_method
  puts "Hello"
end

# Complexity: 4 (1 base + 1 if + 1 elsif + 1 else)
def conditional_method(x)
  if x > 0
    "positive"
  elsif x < 0
    "negative"
  else
    "zero"
  end
end

# Complexity: 5 (1 base + 1 case + 2 when + 1 else)
def case_method(value)
  case value
  when 1
    "one"
  when 2
    "two"
  else
    "other"
  end
end

Grading System

Grade Complexity Range Risk Level Recommendation
A 1-10 Low Excellent, easy to test and maintain
B 11-20 Moderate Good, acceptable complexity
C 21-50 High Consider refactoring
D 51-100 Very High Refactor recommended
F 100+ Extreme Refactor required

Output Formats

Text Report

=== Cyclomatic Complexity Report ===

Global Statistics:
- Number of files: 15
- Number of methods: 87
- Total complexity: 312
- Average complexity: 3.59
- Overall grade: A

Top 5 most complex files:
1. parser.cr (Average: 15.2, Grade: B)
2. analyzer.cr (Average: 12.8, Grade: B)
3. visitor.cr (Average: 8.9, Grade: A)

High Risk Files: 2
- complex_parser.cr (Grade: D)
- legacy_handler.cr (Grade: F)

JSON Report

{
  "files": [
    {
      "path": "src/main.cr",
      "methods": [
        {
          "name": "main",
          "complexity": 5,
          "line_start": 1,
          "line_end": 20
        }
      ],
      "total_complexity": 5,
      "average_complexity": 5.0
    }
  ],
  "total_methods": 1,
  "total_complexity": 5,
  "average_complexity": 5.0,
  "analyzed_at": "2024-01-15T10:30:00Z"
}

Integration Examples

CI/CD Pipeline

# .github/workflows/quality.yml
name: Code Quality

on: [push, pull_request]

jobs:
  complexity:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Install Crystal
        uses: crystal-lang/install-crystal@v1
      - name: Install cyclomatic
        run: |
          git clone https://github.com/yourusername/cyclomatic_complexity.git
          cd cyclomatic_complexity && make install
      - name: Run complexity analysis
        run: |
          cyclomatic -p src/ -r json -o complexity.json
          # Fail if average complexity > 10
          if [ $(jq '.average_complexity' complexity.json | cut -d. -f1) -gt 10 ]; then
            echo "Average complexity too high!"
            exit 1
          fi

Pre-commit Hook

#!/bin/sh
# .git/hooks/pre-commit

# Run complexity analysis on staged Crystal files
staged_files=$(git diff --cached --name-only --diff-filter=ACM | grep '\.cr$')

if [ -n "$staged_files" ]; then
  echo "Checking complexity of staged files..."
  for file in $staged_files; do
    complexity=$(cyclomatic -f "$file" -r json | jq '.files[0].average_complexity')
    if [ $(echo "$complexity > 20" | bc) -eq 1 ]; then
      echo "❌ $file has high complexity: $complexity"
      exit 1
    fi
  done
  echo "✅ All files have acceptable complexity"
fi

Development

Building

shard build

Running Tests

crystal spec
crystal spec --verbose
crystal spec spec/models/  # Test specific directory

Contributing

  1. Fork it (https://github.com/jadekharats/cyclomatic_complexity/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Guidelines

  • Add tests for new features
  • Update documentation as needed
  • Follow Crystal coding conventions

Roadmap

  • ABC Complexity: Add Assignments, Branches, Conditions metric
  • Halstead Metrics: Volume, difficulty, effort calculations
  • Cognitive Complexity: Human-focused complexity metric
  • HTML Reporter: Rich web-based reports
  • Trend Analysis: Track complexity over time
  • IDE Integration: LSP server for real-time analysis
  • Configuration: Customizable complexity thresholds

License

The MIT License (MIT)

Copyright (c) 2024 Your Name

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Acknowledgments

  • Inspired by similar tools in other languages (rubocop, radon, etc.)
  • Built for integration with Criterion quality platform
  • Crystal community for language support and tooling

Made with ❤️ for the Crystal community

Repository

cyclomatic_complexity

Owner
Statistic
  • 0
  • 0
  • 0
  • 0
  • 0
  • 28 days ago
  • July 11, 2025
License

MIT License

Links
Synced at

Fri, 08 Aug 2025 09:47:08 GMT

Languages