diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ObjectLiteralToLambdaIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ObjectLiteralToLambdaIntention.kt index d00d0a8270d..ff41db6d329 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ObjectLiteralToLambdaIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ObjectLiteralToLambdaIntention.kt @@ -21,6 +21,7 @@ import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiComment import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.search.searches.ReferencesSearch +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.idea.caches.resolve.analyze @@ -38,7 +39,10 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.types.KotlinType class ObjectLiteralToLambdaInspection : IntentionBasedInspection(ObjectLiteralToLambdaIntention::class) @@ -61,22 +65,30 @@ class ObjectLiteralToLambdaIntention : SelfTargetingRangeIntention { it !is KtClassOrObject } - for (thisReference in thisReferences) { - val context = thisReference.analyze(BodyResolveMode.PARTIAL) - val thisDescriptor = context[BindingContext.REFERENCE_TARGET, thisReference.instanceReference] - if (thisDescriptor == functionDescriptor.containingDeclaration) { - return null - } - } + if (bodyExpression.anyDescendantOfType { thisReference -> + context[BindingContext.REFERENCE_TARGET, thisReference.instanceReference] == containingDeclaration + }) return null // Recursive call, skip labels if (ReferencesSearch.search(singleFunction, LocalSearchScope(bodyExpression)).any { it.element !is KtLabelReferenceExpression }) { return null } + fun ReceiverValue?.isImplicitClassFor(descriptor: DeclarationDescriptor) = + this is ImplicitClassReceiver && classDescriptor == descriptor + + if (bodyExpression.anyDescendantOfType { expression -> + val resolvedCall = expression.getResolvedCall(context) + resolvedCall?.let { + it.dispatchReceiver.isImplicitClassFor(containingDeclaration) || + it.extensionReceiver.isImplicitClassFor(containingDeclaration) + } ?: false + }) return null + return TextRange(element.objectDeclaration.getObjectKeyword()!!.startOffset, baseTypeRef.endOffset) } diff --git a/idea/testData/intentions/objectLiteralToLambda/ExplicitThis.kt b/idea/testData/intentions/objectLiteralToLambda/ExplicitThis.kt new file mode 100644 index 00000000000..25f8efaec74 --- /dev/null +++ b/idea/testData/intentions/objectLiteralToLambda/ExplicitThis.kt @@ -0,0 +1,10 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +fun foo() { + object : Runnable { + override fun run() { + this.hashCode() + } + }.run() +} \ No newline at end of file diff --git a/idea/testData/intentions/objectLiteralToLambda/ImplicitThis.kt b/idea/testData/intentions/objectLiteralToLambda/ImplicitThis.kt new file mode 100644 index 00000000000..f2d1ae7421c --- /dev/null +++ b/idea/testData/intentions/objectLiteralToLambda/ImplicitThis.kt @@ -0,0 +1,10 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +fun foo() { + object : Runnable { + override fun run() { + hashCode() + } + }.run() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index ac4654af9f9..f429b68287f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -11418,6 +11418,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("ExplicitThis.kt") + public void testExplicitThis() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/objectLiteralToLambda/ExplicitThis.kt"); + doTest(fileName); + } + @TestMetadata("ExpressionBody.kt") public void testExpressionBody() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/objectLiteralToLambda/ExpressionBody.kt"); @@ -11430,6 +11436,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("ImplicitThis.kt") + public void testImplicitThis() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/objectLiteralToLambda/ImplicitThis.kt"); + doTest(fileName); + } + @TestMetadata("MultipleBases.kt") public void testMultipleBases() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/objectLiteralToLambda/MultipleBases.kt");