New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Separating ComplexMethod rule into CyclomaticComplexMethod and CognitiveComplexMethod #5442
Merged
Merged
Changes from 8 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
e5112cd
feature: add rule CognitiveComplexMethod
sanggggg d71cc8e
test: add test for CognitiveComplexMethod rule
sanggggg 74749ba
move: ComplexMethod -> CyclomaticComplexMethod
sanggggg 812b119
fix: remove referencing ComplexMethod
sanggggg 7609ec4
fix: detekt correct
sanggggg cde1341
fix: set alias
sanggggg 4545cd7
config: deactivate default cognitive complex method rule
sanggggg b7bd1b0
test: add positive and negative test cases
sanggggg 236d3ce
Update detekt-rules-complexity/src/main/kotlin/io/gitlab/arturbosch/d…
sanggggg 2a0ae60
fix: remove conflicting activeByDefault and sync
sanggggg File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
62 changes: 62 additions & 0 deletions
62
...ty/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/CognitiveComplexMethod.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package io.gitlab.arturbosch.detekt.rules.complexity | ||
|
||
import io.github.detekt.metrics.CognitiveComplexity | ||
import io.gitlab.arturbosch.detekt.api.Config | ||
import io.gitlab.arturbosch.detekt.api.Debt | ||
import io.gitlab.arturbosch.detekt.api.Entity | ||
import io.gitlab.arturbosch.detekt.api.Issue | ||
import io.gitlab.arturbosch.detekt.api.Metric | ||
import io.gitlab.arturbosch.detekt.api.Rule | ||
import io.gitlab.arturbosch.detekt.api.Severity | ||
import io.gitlab.arturbosch.detekt.api.ThresholdedCodeSmell | ||
import io.gitlab.arturbosch.detekt.api.config | ||
import io.gitlab.arturbosch.detekt.api.internal.ActiveByDefault | ||
import io.gitlab.arturbosch.detekt.api.internal.Configuration | ||
import org.jetbrains.kotlin.psi.KtNamedFunction | ||
|
||
/** | ||
* Complex methods are hard to understand and read. It might not be obvious what side-effects a complex method has. | ||
* Prefer splitting up complex methods into smaller methods that are in turn easier to understand. | ||
* Smaller methods can also be named much clearer which leads to improved readability of the code. | ||
* | ||
* This rule measures and restricts the complexity of the method through the [Cognitive Complexity metric of Sonasource](https://www.sonarsource.com/docs/CognitiveComplexity.pdf). | ||
* Which improves McCabe's Cyclomatic Complexity ({@link CyclomaticComplexMethod}) considering the programmer's mental model. | ||
* | ||
* Similar to cyclomatic complexity, it is a mathematical model that increases +1 complexity for flow control statements, | ||
* but increases additional complexity when the statements are deeply nested. | ||
* | ||
* The statements that increase the complexity or the nesting level are as follows. | ||
* - __Complexity Increments__ - `if`, `when`, `for`, `while`, `do while`, `catch`, `labeled break`, `labeled continue`, `labeled return`, `recursion call`, `&&`, `||` | ||
* - __Nesting Level Increments__ - `if`, `when`, `for`, `while`, `do while`, `catch`, `nested function` | ||
* - __Additional Complexity Increments by Nesting Level__ - `if`, `when`, `for`, `while`, `do while`, `catch` | ||
*/ | ||
@ActiveByDefault(since = "1.22.0") | ||
class CognitiveComplexMethod(config: Config = Config.empty) : Rule(config) { | ||
|
||
override val issue = Issue( | ||
"CognitiveComplexMethod", | ||
Severity.Maintainability, | ||
"Prefer splitting up complex methods into smaller, easier to understand methods.", | ||
Debt.TWENTY_MINS | ||
) | ||
|
||
@Configuration("Cognitive Complexity number for a method.") | ||
private val threshold: Int by config(defaultValue = 15) | ||
|
||
override fun visitNamedFunction(function: KtNamedFunction) { | ||
val complexity = CognitiveComplexity.calculate(function) | ||
|
||
if (complexity >= threshold) { | ||
report( | ||
ThresholdedCodeSmell( | ||
issue, | ||
Entity.atName(function), | ||
Metric("CC", complexity, threshold), | ||
"The function ${function.nameAsSafeName} appears to be too complex " + | ||
"based on Cognitive Complexity (complexity: $complexity). " + | ||
"Defined complexity threshold for methods is set to '$threshold'" | ||
) | ||
) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
...rc/test/kotlin/io/gitlab/arturbosch/detekt/rules/complexity/CognitiveComplexMethodSpec.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package io.gitlab.arturbosch.detekt.rules.complexity | ||
|
||
import io.gitlab.arturbosch.detekt.api.SourceLocation | ||
import io.gitlab.arturbosch.detekt.test.TestConfig | ||
import io.gitlab.arturbosch.detekt.test.assertThat | ||
import io.gitlab.arturbosch.detekt.test.compileAndLint | ||
import io.gitlab.arturbosch.detekt.test.isThresholded | ||
import org.junit.jupiter.api.Test | ||
|
||
class CognitiveComplexMethodSpec { | ||
|
||
private val testConfig = TestConfig("threshold" to "1") | ||
|
||
@Test | ||
fun `should report complex function`() { | ||
val code = """ | ||
fun sumOfPrimes(max: Int): Int { // total cognitive complexity is 7 | ||
var total = 0 | ||
next@ for (i in 1..max) { // +1 | ||
for (j in 2 until i) { // +1 +1 | ||
if (i % j == 0) { // +1 +2 | ||
continue@next // +1 | ||
} | ||
} | ||
println(i) | ||
total++ | ||
} | ||
return total | ||
} | ||
""".trimIndent() | ||
val findings = CognitiveComplexMethod(testConfig).compileAndLint(code) | ||
assertThat(findings).hasSize(1) | ||
assertThat(findings).hasStartSourceLocations(SourceLocation(1, 5)) | ||
assertThat(findings.first()).isThresholded().withValue(7) | ||
} | ||
|
||
@Test | ||
fun `should not report simple function`() { | ||
val code = """ | ||
fun add(a: Int, b: Int): Int { // total cognitive complexity is 0 | ||
return a + b | ||
} | ||
""".trimIndent() | ||
val findings = CognitiveComplexMethod(testConfig).compileAndLint(code) | ||
assertThat(findings).isEmpty() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should remove this line. And probably run
./gradlew generateDocumentation
because CI is complaining that the documentation is not on sync (probablydetekt-core/src/main/resources/default-detekt-config.yml
ordetekt-core/src/main/resources/deprecation.properties
)That couls also fix the othere CI issues... I don't understand why this rule is executed...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removing ActiveByDefault & sync config works fine 😄
2a0ae60