diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/MoveSuspiciousCallableReferenceIntoParenthesesInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/MoveSuspiciousCallableReferenceIntoParenthesesInspection.kt index fd02c8408ea..278fec89f2b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/MoveSuspiciousCallableReferenceIntoParenthesesInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/MoveSuspiciousCallableReferenceIntoParenthesesInspection.kt @@ -13,9 +13,11 @@ import com.intellij.psi.PsiElementVisitor import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall import org.jetbrains.kotlin.idea.intentions.ConvertLambdaToReferenceIntention +import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor import org.jetbrains.kotlin.psi.KtCallableReferenceExpression import org.jetbrains.kotlin.psi.KtLambdaExpression import org.jetbrains.kotlin.psi.ValueArgument @@ -33,7 +35,7 @@ class MoveSuspiciousCallableReferenceIntoParenthesesInspection : AbstractKotlinI val parentResolvedCall = lambdaExpression.getParentResolvedCall(context) if (parentResolvedCall != null) { val originalParameterDescriptor = - parentResolvedCall.getParameterForArgument(lambdaExpression.parent as? ValueArgument)?.original + parentResolvedCall.getParameterForArgument(lambdaExpression.parent as? ValueArgument)?.original if (originalParameterDescriptor != null) { val expectedType = originalParameterDescriptor.type if (expectedType.isBuiltinFunctionalType) { @@ -43,10 +45,10 @@ class MoveSuspiciousCallableReferenceIntoParenthesesInspection : AbstractKotlinI } } holder.registerProblem( - lambdaExpression, - "Suspicious callable reference as the only lambda element", - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - IntentionWrapper(MoveIntoParenthesesIntention(), lambdaExpression.containingFile) + lambdaExpression, + "Suspicious callable reference as the only lambda element", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + IntentionWrapper(MoveIntoParenthesesIntention(), lambdaExpression.containingFile) ) } @@ -54,15 +56,25 @@ class MoveSuspiciousCallableReferenceIntoParenthesesInspection : AbstractKotlinI } class MoveIntoParenthesesIntention : ConvertLambdaToReferenceIntention( - "Move suspicious callable reference into parentheses '()'" + "Move suspicious callable reference into parentheses '()'" ) { override fun buildReferenceText(element: KtLambdaExpression): String? { val callableReferenceExpression = - element.bodyExpression?.statements?.singleOrNull() as? KtCallableReferenceExpression ?: return null + element.bodyExpression?.statements?.singleOrNull() as? KtCallableReferenceExpression ?: return null val callableReference = callableReferenceExpression.callableReference - val resolvedCall = callableReference.resolveToCall(BodyResolveMode.FULL) - val receiverValue = resolvedCall?.let { it.extensionReceiver ?: it.dispatchReceiver } - return (receiverValue?.let { "${it.type}" } ?: "") + "::${callableReference.text}" + val receiverExpression = callableReferenceExpression.receiverExpression + val receiver = if (receiverExpression == null) { + "" + } else { + val descriptor = receiverExpression.getCallableDescriptor() + if (descriptor == null || descriptor is ValueParameterDescriptor) + callableReference.resolveToCall(BodyResolveMode.FULL) + ?.let { it.extensionReceiver ?: it.dispatchReceiver } + ?.let { "${it.type}" } ?: "" + else + receiverExpression.text + } + return "$receiver::${callableReference.text}" } override fun isApplicableTo(element: KtLambdaExpression) = true diff --git a/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/explicitThisReceiver.kt b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/explicitThisReceiver.kt new file mode 100644 index 00000000000..a374dd6eb94 --- /dev/null +++ b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/explicitThisReceiver.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME +class Test { + fun function(s: String): Boolean = true + fun test() { + "".let { this::function } + } +} diff --git a/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/explicitThisReceiver.kt.after b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/explicitThisReceiver.kt.after new file mode 100644 index 00000000000..e7d22ef99b6 --- /dev/null +++ b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/explicitThisReceiver.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME +class Test { + fun function(s: String): Boolean = true + fun test() { + "".let(this::function) + } +} diff --git a/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/implicitThisReceiver.kt b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/implicitThisReceiver.kt new file mode 100644 index 00000000000..2146d654a81 --- /dev/null +++ b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/implicitThisReceiver.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME +class Test { + fun function(s: String): Boolean = true + fun test() { + "".let { ::function } + } +} diff --git a/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/implicitThisReceiver.kt.after b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/implicitThisReceiver.kt.after new file mode 100644 index 00000000000..17e2ef3d905 --- /dev/null +++ b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/implicitThisReceiver.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME +class Test { + fun function(s: String): Boolean = true + fun test() { + "".let(::function) + } +} diff --git a/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/variableReceiver.kt b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/variableReceiver.kt new file mode 100644 index 00000000000..1b009f70cba --- /dev/null +++ b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/variableReceiver.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME +class Test { + val lambda = { s: String -> true } + fun test() { + "".let { lambda::invoke } + } +} diff --git a/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/variableReceiver.kt.after b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/variableReceiver.kt.after new file mode 100644 index 00000000000..8348529cdc3 --- /dev/null +++ b/idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/variableReceiver.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME +class Test { + val lambda = { s: String -> true } + fun test() { + "".let(lambda::invoke) + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 3b29c7c7afe..3c70b9257c7 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -2301,6 +2301,18 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { doTest(fileName); } + @TestMetadata("explicitThisReceiver.kt") + public void testExplicitThisReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/explicitThisReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("implicitThisReceiver.kt") + public void testImplicitThisReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/implicitThisReceiver.kt"); + doTest(fileName); + } + @TestMetadata("it.kt") public void testIt() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/it.kt"); @@ -2348,6 +2360,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/parameter2.kt"); doTest(fileName); } + + @TestMetadata("variableReceiver.kt") + public void testVariableReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/variableReceiver.kt"); + doTest(fileName); + } } @TestMetadata("idea/testData/inspectionsLocal/nullChecksToSafeCall")