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 5d4e526a5497..f5c0799efa4d 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 @@ -21,6 +21,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 @@ -79,7 +81,7 @@ class UnnecessaryAbstractClass(config: Config = Config.empty) : Rule(config) { super.visitClass(klass) } - @Suppress("ComplexMethod") + @Suppress("ComplexMethod", "ReturnCount") private fun KtClass.check() { val nameIdentifier = this.nameIdentifier ?: return if (annotationExcluder.shouldExclude(annotationEntries) || isInterface() || !isAbstract()) return @@ -98,6 +100,8 @@ class UnnecessaryAbstractClass(config: Config = Config.empty) : Rule(config) { report(CodeSmell(issue, Entity.from(nameIdentifier), noConcreteMember)) } } + + hasInheritedMember(true) && !isParentInterface() -> return !hasConstructorParameter() -> report(CodeSmell(issue, Entity.from(nameIdentifier), noConcreteMember)) else -> @@ -122,4 +126,7 @@ class UnnecessaryAbstractClass(config: Config = Config.empty) : Rule(config) { } } } + private fun KtClass.isParentInterface() = + (bindingContext[BindingContext.CLASS, this]?.unsubstitutedMemberScope as? LazyClassMemberScope) + ?.supertypes?.firstOrNull()?.isInterface() == true } 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 ba65e69f96ce..9af41be3366e 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,6 +86,16 @@ class UnnecessaryAbstractClassSpec(val env: KotlinCoreEnvironment) { } abstract class B : A() """ + assertThat(subject.compileAndLintWithContext(env, code)).isEmpty() + } + + @Test + fun `that inherits from another empty abstract class`() { + val code = """ + @Deprecated("We don't care about this first class") + abstract class A + abstract class B : A() + """ val findings = subject.compileAndLintWithContext(env, code) assertFindingMessage(findings, message) }