mirror of
https://github.com/jlengrand/detekt.git
synced 2026-03-10 08:11:23 +00:00
Add cognitive complexity in complexity report (#2727)
* Add metric processor for cognitive complexity * Add cognitive complexity to complexity report
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package io.gitlab.arturbosch.detekt.cli.console
|
||||
|
||||
import io.github.detekt.metrics.CognitiveComplexity
|
||||
import io.gitlab.arturbosch.detekt.api.Detektion
|
||||
import io.github.detekt.metrics.processors.commentLinesKey
|
||||
import io.github.detekt.metrics.processors.complexityKey
|
||||
@@ -10,6 +11,7 @@ import io.github.detekt.metrics.processors.sourceLinesKey
|
||||
class ComplexityMetric(detektion: Detektion) {
|
||||
|
||||
val mcc = detektion.getData(complexityKey)
|
||||
val cognitiveComplexity = detektion.getData(CognitiveComplexity.KEY)
|
||||
val loc = detektion.getData(linesKey)
|
||||
val sloc = detektion.getData(sourceLinesKey)
|
||||
val lloc = detektion.getData(logicalLinesKey)
|
||||
|
||||
@@ -22,7 +22,8 @@ class ComplexityReportGenerator(private val complexityMetric: ComplexityMetric)
|
||||
"%,d source lines of code (sloc)".format(Locale.US, complexityMetric.sloc),
|
||||
"%,d logical lines of code (lloc)".format(Locale.US, complexityMetric.lloc),
|
||||
"%,d comment lines of code (cloc)".format(Locale.US, complexityMetric.cloc),
|
||||
"%,d McCabe complexity (mcc)".format(Locale.US, complexityMetric.mcc),
|
||||
"%,d cyclomatic complexity (mcc)".format(Locale.US, complexityMetric.mcc),
|
||||
"%,d cognitive complexity".format(Locale.US, complexityMetric.cognitiveComplexity),
|
||||
"%,d number of total code smells".format(Locale.US, numberOfSmells),
|
||||
"%,d%% comment source ratio".format(Locale.US, commentSourceRatio),
|
||||
"%,d mcc per 1,000 lloc".format(Locale.US, mccPerThousandLines),
|
||||
@@ -32,15 +33,18 @@ class ComplexityReportGenerator(private val complexityMetric: ComplexityMetric)
|
||||
|
||||
private fun cannotGenerate(): Boolean {
|
||||
return when {
|
||||
complexityMetric.mcc == null -> true
|
||||
null in setOf(
|
||||
complexityMetric.mcc,
|
||||
complexityMetric.cloc,
|
||||
complexityMetric.cognitiveComplexity
|
||||
) -> true
|
||||
complexityMetric.lloc == null || complexityMetric.lloc == 0 -> true
|
||||
complexityMetric.sloc == null || complexityMetric.sloc == 0 -> true
|
||||
complexityMetric.cloc == null -> true
|
||||
else -> {
|
||||
numberOfSmells = complexityMetric.findings.sumBy { it.value.size }
|
||||
smellPerThousandLines = numberOfSmells * 1000 / complexityMetric.lloc
|
||||
mccPerThousandLines = complexityMetric.mcc * 1000 / complexityMetric.lloc
|
||||
commentSourceRatio = complexityMetric.cloc * 100 / complexityMetric.sloc
|
||||
mccPerThousandLines = complexityMetric.mcc!! * 1000 / complexityMetric.lloc
|
||||
commentSourceRatio = complexityMetric.cloc!! * 100 / complexityMetric.sloc
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ io.github.detekt.metrics.processors.ClassCountProcessor
|
||||
io.github.detekt.metrics.processors.FunctionCountProcessor
|
||||
io.github.detekt.metrics.processors.PropertyCountProcessor
|
||||
io.github.detekt.metrics.processors.ProjectComplexityProcessor
|
||||
io.github.detekt.metrics.processors.ProjectCognitiveComplexityProcessor
|
||||
io.github.detekt.metrics.processors.ProjectLLOCProcessor
|
||||
io.github.detekt.metrics.processors.ProjectCLOCProcessor
|
||||
io.github.detekt.metrics.processors.ProjectLOCProcessor
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.gitlab.arturbosch.detekt.cli.console
|
||||
|
||||
import io.github.detekt.metrics.CognitiveComplexity
|
||||
import io.gitlab.arturbosch.detekt.api.Detektion
|
||||
import io.gitlab.arturbosch.detekt.cli.createFinding
|
||||
import io.github.detekt.metrics.processors.commentLinesKey
|
||||
@@ -31,7 +32,8 @@ internal class ComplexityReportGeneratorSpec : Spek({
|
||||
"6 source lines of code (sloc)",
|
||||
"5 logical lines of code (lloc)",
|
||||
"4 comment lines of code (cloc)",
|
||||
"2 McCabe complexity (mcc)",
|
||||
"2 cyclomatic complexity (mcc)",
|
||||
"2 cognitive complexity",
|
||||
"1 number of total code smells",
|
||||
"66% comment source ratio",
|
||||
"400 mcc per 1,000 lloc",
|
||||
@@ -75,6 +77,7 @@ internal class ComplexityReportGeneratorSpec : Spek({
|
||||
|
||||
private fun addData(detektion: Detektion) {
|
||||
detektion.addData(complexityKey, 2)
|
||||
detektion.addData(CognitiveComplexity.KEY, 2)
|
||||
detektion.addData(linesKey, 1000)
|
||||
detektion.addData(sourceLinesKey, 6)
|
||||
detektion.addData(logicalLinesKey, 5)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.gitlab.arturbosch.detekt.cli.console
|
||||
|
||||
import io.github.detekt.metrics.CognitiveComplexity
|
||||
import io.gitlab.arturbosch.detekt.api.Detektion
|
||||
import io.gitlab.arturbosch.detekt.cli.createFinding
|
||||
import io.gitlab.arturbosch.detekt.core.DetektResult
|
||||
@@ -23,9 +24,7 @@ internal class ComplexityReportSpec : Spek({
|
||||
val expectedContent = readResource("complexity-report.txt")
|
||||
val detektion = createDetektion()
|
||||
addData(detektion)
|
||||
// Casting expectedContent to Any is workaround for
|
||||
// https://github.com/joel-costigliola/assertj-core/issues/1440#issuecomment-465032464
|
||||
assertThat(report.render(detektion)).isEqualTo(expectedContent as Any)
|
||||
assertThat(report.render(detektion)).isEqualTo(expectedContent)
|
||||
}
|
||||
|
||||
it("returns null for missing complexity metrics in report") {
|
||||
@@ -41,6 +40,7 @@ private fun createDetektion(): Detektion = DetektResult(mapOf(Pair("Key", listOf
|
||||
|
||||
private fun addData(detektion: Detektion) {
|
||||
detektion.addData(complexityKey, 2)
|
||||
detektion.addData(CognitiveComplexity.KEY, 2)
|
||||
detektion.addData(linesKey, 10)
|
||||
detektion.addData(sourceLinesKey, 6)
|
||||
detektion.addData(logicalLinesKey, 5)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.gitlab.arturbosch.detekt.cli.out
|
||||
|
||||
import io.github.detekt.metrics.CognitiveComplexity
|
||||
import io.github.detekt.metrics.processors.commentLinesKey
|
||||
import io.github.detekt.metrics.processors.complexityKey
|
||||
import io.github.detekt.metrics.processors.linesKey
|
||||
@@ -114,6 +115,7 @@ class HtmlOutputReportSpec : Spek({
|
||||
it("renders the complexity report correctly") {
|
||||
val detektion = TestDetektion()
|
||||
detektion.addData(complexityKey, 10)
|
||||
detektion.addData(CognitiveComplexity.KEY, 10)
|
||||
detektion.addData(sourceLinesKey, 20)
|
||||
detektion.addData(logicalLinesKey, 10)
|
||||
detektion.addData(commentLinesKey, 2)
|
||||
|
||||
@@ -3,7 +3,8 @@ Complexity Report:
|
||||
- 6 source lines of code (sloc)
|
||||
- 5 logical lines of code (lloc)
|
||||
- 4 comment lines of code (cloc)
|
||||
- 2 McCabe complexity (mcc)
|
||||
- 2 cyclomatic complexity (mcc)
|
||||
- 2 cognitive complexity
|
||||
- 1 number of total code smells
|
||||
- 66% comment source ratio
|
||||
- 400 mcc per 1,000 lloc
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.github.detekt.metrics
|
||||
|
||||
import io.gitlab.arturbosch.detekt.api.DetektVisitor
|
||||
import org.jetbrains.kotlin.com.intellij.openapi.util.Key
|
||||
import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
@@ -175,6 +176,8 @@ class CognitiveComplexity private constructor() : DetektVisitor() {
|
||||
|
||||
companion object {
|
||||
|
||||
val KEY = Key<Int>("detekt.metrics.cognitive_complexity")
|
||||
|
||||
private val logicalOps = setOf(KtTokens.ANDAND, KtTokens.OROR)
|
||||
|
||||
fun calculate(element: KtElement): Int {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.github.detekt.metrics.processors
|
||||
|
||||
import io.github.detekt.metrics.CognitiveComplexity
|
||||
import io.gitlab.arturbosch.detekt.api.DetektVisitor
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class ProjectCognitiveComplexityProcessor : AbstractProcessor() {
|
||||
|
||||
override val visitor = object : DetektVisitor() {
|
||||
|
||||
override fun visitKtFile(file: KtFile) {
|
||||
val complexity = CognitiveComplexity.calculate(file)
|
||||
file.putUserData(CognitiveComplexity.KEY, complexity)
|
||||
}
|
||||
}
|
||||
|
||||
override val key = CognitiveComplexity.KEY
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.github.detekt.metrics.processors
|
||||
|
||||
import io.github.detekt.metrics.CognitiveComplexity
|
||||
import io.github.detekt.test.utils.compileContentForTest
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.spekframework.spek2.Spek
|
||||
import org.spekframework.spek2.style.specification.describe
|
||||
|
||||
class CognitiveComplexityProcessorSpec : Spek({
|
||||
|
||||
describe("CognitiveComplexityProcessor") {
|
||||
|
||||
it("counts the complexity for the whole file") {
|
||||
val file = compileContentForTest(complexClass)
|
||||
|
||||
val value = MetricProcessorTester(file)
|
||||
.test(ProjectCognitiveComplexityProcessor(), CognitiveComplexity.KEY)
|
||||
|
||||
assertThat(value).isEqualTo(46)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,50 @@
|
||||
package io.github.detekt.metrics.processors
|
||||
|
||||
import io.gitlab.arturbosch.detekt.api.Detektion
|
||||
import io.gitlab.arturbosch.detekt.api.Finding
|
||||
import io.gitlab.arturbosch.detekt.api.Notification
|
||||
import io.gitlab.arturbosch.detekt.api.ProjectMetric
|
||||
import io.gitlab.arturbosch.detekt.api.RuleSetId
|
||||
import org.jetbrains.kotlin.com.intellij.openapi.util.Key
|
||||
import org.jetbrains.kotlin.com.intellij.util.keyFMap.KeyFMap
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class MetricProcessorTester(
|
||||
private val file: KtFile,
|
||||
private val result: Detektion = MetricResults()
|
||||
) {
|
||||
|
||||
fun <T : Any> test(processor: AbstractProcessor, key: Key<T>): T {
|
||||
with(processor) {
|
||||
onStart(listOf(file))
|
||||
onProcess(file)
|
||||
onProcessComplete(file, emptyMap())
|
||||
onFinish(listOf(file), result)
|
||||
}
|
||||
return checkNotNull(result.getData(key))
|
||||
}
|
||||
}
|
||||
|
||||
private class MetricResults : Detektion {
|
||||
override val findings: Map<RuleSetId, List<Finding>>
|
||||
get() = throw UnsupportedOperationException()
|
||||
override val notifications: Collection<Notification>
|
||||
get() = throw UnsupportedOperationException()
|
||||
override val metrics: MutableList<ProjectMetric> = mutableListOf()
|
||||
|
||||
private var data = KeyFMap.EMPTY_MAP
|
||||
|
||||
override fun <V> getData(key: Key<V>): V? = data.get(key)
|
||||
|
||||
override fun <V> addData(key: Key<V>, value: V) {
|
||||
data = data.plus(key, value)
|
||||
}
|
||||
|
||||
override fun add(notification: Notification) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override fun add(projectMetric: ProjectMetric) {
|
||||
metrics.add(projectMetric)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user