diff --git a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClass.kt b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClass.kt index 20f4b9f2de5..84487eb3929 100644 --- a/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClass.kt +++ b/detekt-rules-style/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClass.kt @@ -22,6 +22,8 @@ import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.psiUtil.isAbstract import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassMemberScope +import org.jetbrains.kotlin.types.typeUtil.isInterface /** * This rule inspects `abstract` classes. In case an `abstract class` does not have any concrete members it should be @@ -86,6 +88,7 @@ class UnnecessaryAbstractClass(config: Config = Config.empty) : Rule(config) { val members = members() when { members.isNotEmpty() -> checkMembers(members, nameIdentifier) + hasInheritedMember(true) && isAnyParentAbstract() -> return !hasConstructorParameter() -> report(CodeSmell(issue, Entity.from(nameIdentifier), noConcreteMember)) else -> @@ -125,4 +128,9 @@ class UnnecessaryAbstractClass(config: Config = Config.empty) : Rule(config) { } } } + + private fun KtClass.isAnyParentAbstract() = + (bindingContext[BindingContext.CLASS, this]?.unsubstitutedMemberScope as? LazyClassMemberScope) + ?.supertypes + ?.all { it.isInterface() } == false } diff --git a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt index ba65e69f96c..d9b65a797bd 100644 --- a/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt +++ b/detekt-rules-style/src/test/kotlin/io/gitlab/arturbosch/detekt/rules/style/UnnecessaryAbstractClassSpec.kt @@ -86,8 +86,37 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) { } abstract class B : A() """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } + + @Test + fun `does not report abstract class that inherits from an abstract class and an interface in that order`() { + val code = """ + interface I + + @Deprecated("We don't care about this first class") + abstract class A { + abstract val i: Int + } + abstract class B: A(), I + """ val findings = subject.compileAndLintWithContext(env, code) - assertFindingMessage(findings, message) + assertThat(findings).isEmpty() + } + + @Test + fun `does not report abstract class that inherits from an interface and an abstract class in that order`() { + val code = """ + interface I + + @Deprecated("We don't care about this first class") + abstract class A { + abstract val i: Int + } + abstract class B: I, A() + """ + val findings = subject.compileAndLintWithContext(env, code) + assertThat(findings).isEmpty() } }