diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt index 2859e1576da..2f36f176703 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCall.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.psi.KtPrefixExpression import org.jetbrains.kotlin.psi.psiUtil.isDotSelector import org.jetbrains.kotlin.resolve.calls.util.getCalleeExpressionIfAny import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall +import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence /** * Reports all method or constructor invocations that are forbidden. @@ -113,14 +114,20 @@ class ForbiddenMethodCall(config: Config = Config.empty) : Rule(config) { } private fun check(expression: KtExpression) { - val descriptors = expression.getResolvedCall(bindingContext)?.resultingDescriptor?.let { - val foundDescriptors = if (it is PropertyDescriptor) { - listOfNotNull(it.unwrappedGetMethod, it.unwrappedSetMethod) - } else { - listOf(it) - } - foundDescriptors + foundDescriptors.flatMap(CallableDescriptor::getOverriddenDescriptors) - } ?: return + val descriptors: Set = + expression.getResolvedCall(bindingContext)?.resultingDescriptor?.let { callableDescriptor -> + val foundDescriptors = if (callableDescriptor is PropertyDescriptor) { + setOfNotNull( + callableDescriptor.unwrappedGetMethod, + callableDescriptor.unwrappedSetMethod + ) + } else { + setOf(callableDescriptor) + } + foundDescriptors.flatMapTo(mutableSetOf()) { + it.overriddenTreeUniqueAsSequence(true).toSet() + } + } ?: return for (descriptor in descriptors) { methods.find { it.value.match(descriptor) }?.let { forbidden -> diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt index b2f64a47235..431fecd1742 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/ForbiddenMethodCallSpec.kt @@ -433,6 +433,62 @@ class ForbiddenMethodCallSpec(val env: KotlinCoreEnvironment) { assertThat(findings).hasSize(1) } + @Test + fun `should report class grandparent interface default method is not allowed`() { + val code = """ + package org.example.com + open class SimpleComparator : Comparator { + override fun compare(p0: String?, p1: String?): Int { + return 0 + } + } + + class ComplexComparator : SimpleComparator() { + override fun compare(p0: String?, p1: String?): Int { + return 0 + } + } + + fun foo(bar1: ComplexComparator) { + bar1.reversed() + val bar2 = ComplexComparator() + bar2.reversed() + } + """.trimIndent() + val findings = ForbiddenMethodCall( + TestConfig(METHODS to listOf("java.util.Comparator.reversed")) + ).compileAndLintWithContext(env, code) + assertThat(findings).hasSize(2) + } + + @Test + fun `should report class when grandparent default interface is not allowed extending other class`() { + val code = """ + package org.example.com + open class Parent + open class SimpleComparator : Parent(), Comparator { + override fun compare(p0: String?, p1: String?): Int { + return 0 + } + } + + class ComplexComparator : SimpleComparator() { + override fun compare(p0: String?, p1: String?): Int { + return 0 + } + } + + fun foo() { + val bar = ComplexComparator() + bar.reversed() + } + """.trimIndent() + val findings = ForbiddenMethodCall( + TestConfig(METHODS to listOf("java.util.Comparator.reversed")) + ).compileAndLintWithContext(env, code) + assertThat(findings).hasSize(1) + } + @Test fun `should report functions with lambda params`() { val code = """