From dc76f2a62f106b336250bdbb0baa31e8d317a0bc Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 24 Oct 2016 19:19:53 +0300 Subject: [PATCH] Lambda to reference: check potential reference arguments by descriptors and not by names #KT-14420 Fixed --- .../ConvertLambdaToReferenceIntention.kt | 17 ++++++++++------- .../convertLambdaToReference/nestedLambda.kt | 9 +++++++++ .../nestedLambdaWithReceiver.kt | 9 +++++++++ .../idea/intentions/IntentionTestGenerated.java | 12 ++++++++++++ 4 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 idea/testData/intentions/convertLambdaToReference/nestedLambda.kt create mode 100644 idea/testData/intentions/convertLambdaToReference/nestedLambdaWithReceiver.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertLambdaToReferenceIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertLambdaToReferenceIntention.kt index 56d132af062..380514307ed 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertLambdaToReferenceIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertLambdaToReferenceIntention.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext.FUNCTION import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor @@ -105,10 +106,14 @@ class ConvertLambdaToReferenceIntention : SelfTargetingOffsetIndependentIntentio val receiverShift = if (callHasReceiver) 1 else 0 val parametersCount = if (hasSpecification) lambdaExpression.valueParameters.size else 1 if (parametersCount != callableArgumentsCount + receiverShift) return false + val lambdaValueParameters = context[FUNCTION, lambdaExpression.functionLiteral]?.valueParameters ?: return false if (explicitReceiver != null) { if (explicitReceiver !is KtNameReferenceExpression) return false - val callReceiverDescriptor = context[REFERENCE_TARGET, explicitReceiver] as? ParameterDescriptor ?: return false - val receiverType = callReceiverDescriptor.type + if (lambdaValueParameters.isEmpty()) return false + val explicitReceiverTarget = context[REFERENCE_TARGET, explicitReceiver] as? ParameterDescriptor ?: return false + if (explicitReceiverTarget != lambdaValueParameters[0]) return false + + val receiverType = explicitReceiverTarget.type // No exotic receiver types if (receiverType.isTypeParameter() || receiverType.isError || receiverType.isDynamic() || !receiverType.constructor.isDenotable || receiverType.isFunctionType) return false @@ -121,16 +126,14 @@ class ConvertLambdaToReferenceIntention : SelfTargetingOffsetIndependentIntentio explicitReceiver, null, context, explicitReceiver.getResolutionFacade() )) return false } - - val parameterName = if (hasSpecification) lambdaExpression.valueParameters[0].name else "it" - if (explicitReceiver.getReferencedName() != parameterName) return false } // Same lambda / references function parameter order if (callableExpression is KtCallExpression) { + if (lambdaValueParameters.size < receiverShift + callableExpression.valueArguments.size) return false callableExpression.valueArguments.forEachIndexed { i, argument -> val argumentExpression = argument.getArgumentExpression() as? KtNameReferenceExpression ?: return false - val parameterName = if (hasSpecification) lambdaExpression.valueParameters[i + receiverShift].name else "it" - if (argumentExpression.getReferencedName() != parameterName) return false + val argumentTarget = context[REFERENCE_TARGET, argumentExpression] as? ValueParameterDescriptor ?: return false + if (argumentTarget != lambdaValueParameters[i + receiverShift]) return false } } return true diff --git a/idea/testData/intentions/convertLambdaToReference/nestedLambda.kt b/idea/testData/intentions/convertLambdaToReference/nestedLambda.kt new file mode 100644 index 00000000000..6b35f3fbd87 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/nestedLambda.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +fun foo() { + listOf(1).forEach { run { bar(it) } } +} + +fun bar(i: Int) { +} \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/nestedLambdaWithReceiver.kt b/idea/testData/intentions/convertLambdaToReference/nestedLambdaWithReceiver.kt new file mode 100644 index 00000000000..a9915897475 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/nestedLambdaWithReceiver.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +fun foo() { + listOf(1).forEach { run { it.bar() } } +} + +fun Int.bar() { +} \ 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 4100a6e48fb..464756ae471 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -4123,6 +4123,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("nestedLambda.kt") + public void testNestedLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/nestedLambda.kt"); + doTest(fileName); + } + + @TestMetadata("nestedLambdaWithReceiver.kt") + public void testNestedLambdaWithReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/nestedLambdaWithReceiver.kt"); + doTest(fileName); + } + @TestMetadata("nullable.kt") public void testNullable() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/nullable.kt");