cyclomatic_complexity
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.
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
- Fork it (https://github.com/jadekharats/cyclomatic_complexity/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - 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
cyclomatic_complexity
- 0
- 0
- 0
- 0
- 0
- 28 days ago
- July 11, 2025
MIT License
Fri, 08 Aug 2025 09:47:08 GMT