Skip to content

Commit

Permalink
Allow to ignore overloaded methods for the complex interface rule (#5165
Browse files Browse the repository at this point in the history
) (#5173)

* Allow to ignore overloaded methods for complex interface rule (#5165)

* Fix name shadowing in complex interface tests

* Generate documentation for ignoreOverloaded in complex interface

Co-authored-by: Prat T <pt2121@users.noreply.github.com>
  • Loading branch information
pt2121 and pt2121 committed Aug 5, 2022
1 parent e0cf96b commit 829fae2
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 3 deletions.
1 change: 1 addition & 0 deletions detekt-core/src/main/resources/default-detekt-config.yml
Expand Up @@ -98,6 +98,7 @@ complexity:
threshold: 10
includeStaticDeclarations: false
includePrivateDeclarations: false
ignoreOverloaded: false
ComplexMethod:
active: true
threshold: 15
Expand Down
Expand Up @@ -18,6 +18,7 @@ import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtTypeParameterListOwner
import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration
import org.jetbrains.kotlin.psi.psiUtil.isPrivate

/**
Expand Down Expand Up @@ -51,6 +52,9 @@ class ComplexInterface(
@Configuration("whether private declarations should be included")
private val includePrivateDeclarations: Boolean by config(defaultValue = false)

@Configuration("ignore overloaded methods - only count once")
private val ignoreOverloaded: Boolean by config(defaultValue = false)

override fun visitClass(klass: KtClass) {
if (klass.isInterface()) {
val body = klass.body ?: return
Expand Down Expand Up @@ -81,10 +85,26 @@ class ComplexInterface(
fun PsiElement.considerPrivate() = includePrivateDeclarations ||
this is KtTypeParameterListOwner && !this.isPrivate()

fun PsiElement.isMember() = this is KtNamedFunction || this is KtProperty
fun countFunctions(psiElements: List<PsiElement>): Int {
val functions = psiElements.filterIsInstance<KtNamedFunction>()
return if (ignoreOverloaded) {
functions.distinctBy { function ->
val receiver = function.receiverTypeReference
if (function.isExtensionDeclaration() && receiver != null) {
"${receiver.text}.${function.name}"
} else {
function.name
}
}.size
} else {
functions.size
}
}

return body.children
val psiElements = body.children
.filter(PsiElement::considerPrivate)
.count(PsiElement::isMember)
val propertyCount = psiElements.count { it is KtProperty }
val functionCount = countFunctions(psiElements)
return propertyCount + functionCount
}
}
Expand Up @@ -14,6 +14,9 @@ private val staticDeclarationsConfig = TestConfig(
private val privateDeclarationsConfig = TestConfig(
defaultConfigMap + ("includePrivateDeclarations" to true)
)
private val ignoreOverloadedConfig = TestConfig(
defaultConfigMap + ("ignoreOverloaded" to true)
)

class ComplexInterfaceSpec {

Expand Down Expand Up @@ -141,6 +144,57 @@ class ComplexInterfaceSpec {
assertThat(rule.compileAndLint(code)).hasSize(1)
}
}

@Nested
inner class `overloaded methods` {
val code = """
interface I {
fun f1()
fun f1(i: Int)
val i1: Int
fun fImpl() {}
}
"""

@Test
fun `reports complex interface with overloaded methods`() {
assertThat(subject.compileAndLint(code)).hasSize(1)
}

@Test
fun `does not report simple interface with ignoreOverloaded`() {
val rule = ComplexInterface(ignoreOverloadedConfig)
assertThat(rule.compileAndLint(code)).isEmpty()
}

@Test
fun `reports complex interface with extension methods with a different receiver`() {
val interfaceWithExtension = """
interface I {
fun f1()
fun String.f1(i: Int)
val i1: Int
fun fImpl() {}
}
"""
val rule = ComplexInterface(ignoreOverloadedConfig)
assertThat(rule.compileAndLint(interfaceWithExtension)).hasSize(1)
}

@Test
fun `does not report simple interface with extension methods with the same receiver`() {
val interfaceWithOverloadedExtensions = """
interface I {
fun String.f1()
fun String.f1(i: Int)
val i1: Int
fun fImpl() {}
}
"""
val rule = ComplexInterface(ignoreOverloadedConfig)
assertThat(rule.compileAndLint(interfaceWithOverloadedExtensions)).isEmpty()
}
}
}

@Nested
Expand Down

0 comments on commit 829fae2

Please sign in to comment.