Fix "move suspicious reference into parentheses" for functional types

So #KT-21743 Fixed
This commit is contained in:
Toshiaki Kameyama
2018-02-19 17:44:37 +03:00
committed by Mikhail Glukhikh
parent 3fbf85dc37
commit fbd06dc74f
8 changed files with 82 additions and 10 deletions
@@ -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
@@ -0,0 +1,7 @@
// WITH_RUNTIME
class Test {
fun function(s: String): Boolean = true
fun test() {
"".let {<caret> this::function }
}
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
class Test {
fun function(s: String): Boolean = true
fun test() {
"".let(this::function)
}
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
class Test {
fun function(s: String): Boolean = true
fun test() {
"".let {<caret> ::function }
}
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
class Test {
fun function(s: String): Boolean = true
fun test() {
"".let(::function)
}
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
class Test {
val lambda = { s: String -> true }
fun test() {
"".let {<caret> lambda::invoke }
}
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
class Test {
val lambda = { s: String -> true }
fun test() {
"".let(lambda::invoke)
}
}
@@ -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")