Lambda to reference: check potential reference arguments by descriptors and not by names #KT-14420 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-10-24 19:19:53 +03:00
parent 97b45569fc
commit dc76f2a62f
4 changed files with 40 additions and 7 deletions
@@ -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
@@ -0,0 +1,9 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
fun foo() {
listOf(1).forEach { run { <caret>bar(it) } }
}
fun bar(i: Int) {
}
@@ -0,0 +1,9 @@
// IS_APPLICABLE: false
// WITH_RUNTIME
fun foo() {
listOf(1).forEach { run { <caret>it.bar() } }
}
fun Int.bar() {
}
@@ -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");