Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed UnnecessaryAbstractClass if it inherits from a abstract class #5009

Merged
merged 6 commits into from Jul 10, 2022
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 ->
Expand All @@ -122,4 +126,9 @@ class UnnecessaryAbstractClass(config: Config = Config.empty) : Rule(config) {
}
}
}
private fun KtClass.isParentInterface() =
(bindingContext[BindingContext.CLASS, this]?.unsubstitutedMemberScope as? LazyClassMemberScope)
?.supertypes
?.firstOrNull()
?.isInterface() == true
}
Expand Up @@ -86,8 +86,22 @@ 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 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: A(), I
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
abstract class B: A(), I
abstract class B: A(), I
abstract class C: I, A()

We should check both cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! Let me add another test case as well!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gouri-panda I have an idea to solve this in a generic manner. If you're okay I can raise a PR on your fork branch gouri-panda:fix-4753?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking if we check one of the parent is abstract, then we can return. But go for it :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gouri-panda
I have pushed my changes as a PR to your fork's branch here
In summary I have checked if not all the parents are interfaces, which is equivalent of the fact that at least one parent is abstract.
Please refer to this commit (there are a few other merge commits)

"""
val findings = subject.compileAndLintWithContext(env, code)
assertFindingMessage(findings, message)
assertThat(findings).isEmpty()
}
}

Expand Down