[kotlin] 'Redundant explicit this' inspection: fix false negative with overloaded extension functions of Int

^KTIJ-28762 Fixed

closes https://github.com/JetBrains/intellij-community/pull/2667

GitOrigin-RevId: d5f9122282ad1191011ad006821529913f4d6dd6
This commit is contained in:
Toshiaki Kameyama
2024-02-22 20:37:56 +00:00
committed by intellij-monorepo-bot
parent 50bee645b4
commit 5be8877345
6 changed files with 44 additions and 18 deletions
@@ -14,14 +14,15 @@ import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.inspections.ExplicitThisInspection.Util.thisAsReceiverOrNull
import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor
import org.jetbrains.kotlin.idea.intentions.receiverType
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
class ExplicitThisInspection : AbstractKotlinInspection() {
@@ -39,7 +40,7 @@ class ExplicitThisInspection : AbstractKotlinInspection() {
}
object Util {
fun KtExpression.thisAsReceiverOrNull() = when (this) {
fun KtExpression.thisAsReceiverOrNull(): KtThisExpression? = when (this) {
is KtCallableReferenceExpression -> receiverExpression as? KtThisExpression
is KtDotQualifiedExpression -> receiverExpression as? KtThisExpression
else -> null
@@ -52,15 +53,14 @@ class ExplicitThisInspection : AbstractKotlinInspection() {
is KtDotQualifiedExpression -> expression.selectorExpression as? KtReferenceExpression
else -> null
} ?: return false
val context = expression.analyze()
val scope = expression.getResolutionScope(context) ?: return false
val referenceExpression = reference as? KtNameReferenceExpression ?: reference.getChildOfType() ?: return false
if (context.diagnostics.forElement(referenceExpression).any { it.factory == Errors.UNRESOLVED_REFERENCE }) return false
val receiverType = context[BindingContext.EXPRESSION_TYPE_INFO, thisExpression]?.type ?: return false
val referenceTarget = context[BindingContext.REFERENCE_TARGET, reference.referenceExpression()]
//we avoid overload-related problems by enforcing that there is only one candidate
val context = expression.analyze()
if (context.diagnostics.forElement(referenceExpression).any { it.factory == Errors.UNRESOLVED_REFERENCE }) return false
val scope = expression.getResolutionScope(context) ?: return false
val receiverType = context[BindingContext.EXPRESSION_TYPE_INFO, thisExpression]?.type ?: return false
val referenceTarget = context[BindingContext.REFERENCE_TARGET, reference.referenceExpression()]
val name = referenceExpression.getReferencedNameAsName()
val candidates = if (reference is KtCallExpression
|| (expression is KtCallableReferenceExpression && referenceTarget is FunctionDescriptor)
@@ -70,16 +70,16 @@ class ExplicitThisInspection : AbstractKotlinInspection() {
} else {
scope.getAllAccessibleVariables(name)
}
if (referenceExpression.getCallableDescriptor() is SyntheticJavaPropertyDescriptor) {
if (candidates.map { it.containingDeclaration }.distinct().size != 1) return false
} else {
val candidate = candidates.singleOrNull() ?: return false
val extensionType = candidate.extensionReceiverParameter?.type
if (extensionType != null && extensionType != receiverType && receiverType.isSubtypeOf(extensionType)) return false
}
if (candidates.any { it.receiverType() == null }) return false
val implicitThis = reference.getResolutionScope(context)?.getImplicitReceiversHierarchy()?.firstOrNull() ?: return false
val explicitThis = thisExpression.getResolvedCall(context)?.resultingDescriptor ?: return false
val candidate = (if (implicitThis == explicitThis) candidates.firstOrNull() else candidates.singleOrNull()) ?: return false
val extensionType = candidate.extensionReceiverParameter?.type
if (extensionType != null && extensionType != receiverType && receiverType.isSubtypeOf(extensionType)) return false
val expressionFactory = scope.getFactoryForImplicitReceiverWithSubtypeOf(receiverType) ?: return false
val label = thisExpression.getLabelName() ?: ""
return expressionFactory.matchesLabel(label)
}
@@ -5166,11 +5166,21 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
runTest("testData/inspectionsLocal/explicitThis/nestedReceiversExtensionDifferentParameters.kt");
}
@TestMetadata("overloadedIntFunction.kt")
public void testOverloadedIntFunction() throws Exception {
runTest("testData/inspectionsLocal/explicitThis/overloadedIntFunction.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("testData/inspectionsLocal/explicitThis/property.kt");
}
@TestMetadata("propertyNamedCode.kt")
public void testPropertyNamedCode() throws Exception {
runTest("testData/inspectionsLocal/explicitThis/propertyNamedCode.kt");
}
@TestMetadata("propertyReference.kt")
public void testPropertyReference() throws Exception {
runTest("testData/inspectionsLocal/explicitThis/propertyReference.kt");
@@ -0,0 +1,3 @@
fun Int.foo(): Int {
return <caret>this.times(3)
}
@@ -0,0 +1,3 @@
fun Int.foo(): Int {
return times(3)
}
@@ -0,0 +1,5 @@
class A(private var code: String) {
fun foo() {
<caret>this.code
}
}
@@ -0,0 +1,5 @@
class A(private var code: String) {
fun foo() {
code
}
}