diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ExplicitThisInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ExplicitThisInspection.kt index ff21378f13df..9111b491e8d0 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ExplicitThisInspection.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ExplicitThisInspection.kt @@ -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) } diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 22af60ff8f25..6b7d4bcebcd4 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -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"); diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/overloadedIntFunction.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/overloadedIntFunction.kt new file mode 100644 index 000000000000..741c3a1fe789 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/overloadedIntFunction.kt @@ -0,0 +1,3 @@ +fun Int.foo(): Int { + return this.times(3) +} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/overloadedIntFunction.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/overloadedIntFunction.kt.after new file mode 100644 index 000000000000..9ee50a3e3b9a --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/overloadedIntFunction.kt.after @@ -0,0 +1,3 @@ +fun Int.foo(): Int { + return times(3) +} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/propertyNamedCode.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/propertyNamedCode.kt new file mode 100644 index 000000000000..ac107eaea82b --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/propertyNamedCode.kt @@ -0,0 +1,5 @@ +class A(private var code: String) { + fun foo() { + this.code + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/propertyNamedCode.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/propertyNamedCode.kt.after new file mode 100644 index 000000000000..7bafb88af63f --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/explicitThis/propertyNamedCode.kt.after @@ -0,0 +1,5 @@ +class A(private var code: String) { + fun foo() { + code + } +} \ No newline at end of file