mirror of
https://github.com/jlengrand/detekt.git
synced 2026-03-10 08:11:23 +00:00
UnnecessaryInnerClass: fix false negative with this references (#4884)
This commit is contained in:
committed by
GitHub
parent
80bae4bdc5
commit
05cd79612f
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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) }
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user