Skip to content

Commit

Permalink
UnnecessaryInnerClass: fix false negative with this references
Browse files Browse the repository at this point in the history
  • Loading branch information
t-kameyama committed Jun 1, 2022
1 parent f66a321 commit 05419b1
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 24 deletions.
Expand Up @@ -13,11 +13,12 @@ import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtExpressionWithLabel
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.KtThisExpression
import org.jetbrains.kotlin.psi.psiUtil.containingClass
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.utils.addToStdlib.safeAs

Expand Down Expand Up @@ -83,12 +84,11 @@ class UnnecessaryInnerClass(config: Config = Config.empty) : Rule(config) {

override fun visitReferenceExpression(expression: KtReferenceExpression) {
super.visitReferenceExpression(expression)
checkForOuterUsage(expression)
checkForOuterUsage { findResolvedContainingClassId(expression) }
}

override fun visitExpressionWithLabel(expression: KtExpressionWithLabel) {
super.visitExpressionWithLabel(expression)
checkForOuterUsage(expression)
override fun visitThisExpression(expression: KtThisExpression) {
checkForOuterUsage { expression.referenceClassId() }
}

// Replace this "constructor().apply{}" pattern with buildList() when the Kotlin
Expand All @@ -101,32 +101,16 @@ class UnnecessaryInnerClass(config: Config = Config.empty) : Rule(config) {
}
}

private fun checkForOuterUsage(expressionToResolve: KtReferenceExpression) {
private fun checkForOuterUsage(getTargetClassId: () -> ClassId?) {
val currentClass = classChain.peek() ?: return
val parentClasses = candidateClassToParentClasses[currentClass] ?: return

val resolvedContainingClassId = findResolvedContainingClassId(expressionToResolve)
val targetClassId = getTargetClassId() ?: return
/*
* If class A -> inner class B -> inner class C, and class C has outer usage of A,
* then both B and C should stay as inner classes.
*/
val index = parentClasses.indexOfFirst { it.getClassId() == resolvedContainingClassId }
if (index >= 0) {
candidateClassToParentClasses.remove(currentClass)
parentClasses.subList(0, index).forEach { candidateClassToParentClasses.remove(it) }
}
}

private fun checkForOuterUsage(expressionToResolve: KtExpressionWithLabel) {
val currentClass = classChain.peek() ?: return
val parentClasses = candidateClassToParentClasses[currentClass] ?: return

val resolvedContainingClassName = expressionToResolve.getLabelName()
/*
* If class A -> inner class B -> inner class C, and class C has outer usage of A,
* then both B and C should stay as inner classes.
*/
val index = parentClasses.indexOfFirst { it.name == resolvedContainingClassName }
val index = parentClasses.indexOfFirst { it.getClassId() == targetClassId }
if (index >= 0) {
candidateClassToParentClasses.remove(currentClass)
parentClasses.subList(0, index).forEach { candidateClassToParentClasses.remove(it) }
Expand All @@ -139,4 +123,13 @@ class UnnecessaryInnerClass(config: Config = Config.empty) : Rule(config) {
?.safeAs<ClassifierDescriptor>()
?.classId
}

private fun KtThisExpression.referenceClassId(): ClassId? {
return getResolvedCall(bindingContext)
?.resultingDescriptor
?.returnType
?.constructor
?.declarationDescriptor
?.classId
}
}
Expand Up @@ -446,4 +446,19 @@ class UnnecessaryInnerClassSpec(val env: KotlinCoreEnvironment) {

assertThat(subject.lintWithContext(env, code)).hasSize(1)
}

@Test
fun `reports when an inner class has this references`() {
val code = """
class A {
inner class B {
fun foo() {
this
}
}
}
""".trimIndent()

assertThat(subject.lintWithContext(env, code)).hasSize(1)
}
}

0 comments on commit 05419b1

Please sign in to comment.