Don't apply "object->lambda" if 'this' is used inside #KT-15250 Fixed

This commit is contained in:
Mikhail Glukhikh
2017-03-22 17:07:00 +03:00
parent ca92ec0b7b
commit 0baae16601
4 changed files with 52 additions and 8 deletions
@@ -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<KtObjectLiteralExpression>(ObjectLiteralToLambdaIntention::class)
@@ -61,22 +65,30 @@ class ObjectLiteralToLambdaIntention : SelfTargetingRangeIntention<KtObjectLiter
if (singleFunction.valueParameters.any { it.name == null }) return null
val bodyExpression = singleFunction.bodyExpression!!
val context = bodyExpression.analyze()
val containingDeclaration = functionDescriptor.containingDeclaration
// this-reference
val thisReferences = bodyExpression.collectDescendantsOfType<KtThisExpression> { 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<KtThisExpression> { 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<KtExpression> { 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)
}
@@ -0,0 +1,10 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
fun foo() {
<caret>object : Runnable {
override fun run() {
this.hashCode()
}
}.run()
}
@@ -0,0 +1,10 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
fun foo() {
<caret>object : Runnable {
override fun run() {
hashCode()
}
}.run()
}
@@ -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");