This document provides detailed explanations of the complexity metrics supported by swift-complexity.
Cyclomatic complexity measures the number of linearly independent paths through a program's source code. It quantifies the complexity of a program by counting the number of decision points.
The cyclomatic complexity is calculated as the number of decision points plus 1:
Base complexity: 1 (entry point)
The following Swift language constructs contribute to cyclomatic complexity:
ifstatements: +1 for eachifguardstatements: +1 for eachguardelse ifclauses: +1 for eachelse if- Ternary operators (
? :): +1
whileloops: +1forloops: +1repeat-whileloops: +1
switchstatements: +1 for the switch itself- Each
caseclause: +1 (includingdefault)
&&(logical AND): +1 for each occurrence||(logical OR): +1 for each occurrence
catchblocks: +1 for eachcatch
func calculateDiscount(amount: Double, customerType: String) -> Double {
// Base complexity: 1
if amount > 1000 { // +1
if customerType == "premium" { // +1
return amount * 0.15
} else if customerType == "gold" { // +1
return amount * 0.10
} else {
return amount * 0.05
}
} else if amount > 500 { // +1
return amount * 0.03
}
return 0
}
// Total cyclomatic complexity: 5Cognitive complexity measures how difficult the code is for humans to understand. Unlike cyclomatic complexity, it takes into account the nesting level of control structures, as nested code is harder to understand.
Cognitive complexity is calculated by assigning points for:
- Control flow structures
- Nesting increments
- Logical operator sequences
if,else if,elseswitch,casefor,while,repeat-whileguardcatchbreak,continue(when jumping to a label)
For each level of nesting inside the following structures:
if,else if,elseswitch,casefor,while,repeat-whilecatch
The nesting increment is added to the base score of nested control structures.
- First
&&or||in a sequence: +0 - Each additional
&&or||in the same sequence: +1
func processData(items: [String]) {
for item in items { // +1 (base)
if item.isEmpty { // +1 (base) + 1 (nesting) = +2
continue
}
if item.count > 10 { // +1 (base) + 1 (nesting) = +2
for char in item { // +1 (base) + 1 (nesting) = +2
if char.isNumber { // +1 (base) + 2 (nesting) = +3
// Process number
}
}
}
}
}
// Total cognitive complexity: 10func validateUser(name: String, age: Int, email: String) -> Bool {
// First condition in sequence doesn't add to cognitive complexity
if !name.isEmpty && age >= 18 && email.contains("@") { // +1 (if) + 0 (first &&) + 1 (second &&) + 1 (third &&) = +3
return true
}
return false
}
// Total cognitive complexity: 3| Aspect | Cyclomatic Complexity | Cognitive Complexity |
|---|---|---|
| Purpose | Measure testing complexity | Measure readability |
| Nesting | Not considered | Heavily weighted |
| Logical Operators | Each operator +1 | Sequences weighted |
| Best for | Test case planning | Code review |
- 1-10: Simple, easy to test
- 11-20: Moderate complexity, acceptable
- 21-50: Complex, should be simplified
- 50+: Very complex, refactor immediately
- 1-5: Very readable
- 6-10: Readable
- 11-15: Moderate difficulty
- 16-25: Hard to understand
- 25+: Very hard to understand, refactor
Optional chaining (?.) is not counted as it doesn't add control flow complexity.
Complex pattern matching in switch statements may have higher cognitive complexity due to nesting.
Closures are analyzed as separate units when they contain control flow.
try?andtry!: Not counteddo-catchblocks: Counted normallythrowsfunctions: Not counted (complexity is in the caller)
- Cross-function complexity: Metrics are calculated per function/method only
- Semantic complexity: Does not consider algorithmic complexity
- Context ignorance: Cannot distinguish between different types of complexity