Suspicious callable reference: no quick fix with invalid reference type
#KT-23467 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
0ac969a617
commit
b91f30ab15
+33
-6
@@ -13,20 +13,23 @@ 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.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
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
|
||||
import org.jetbrains.kotlin.psi.lambdaExpressionVisitor
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParameterForArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getParentResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
|
||||
class MoveSuspiciousCallableReferenceIntoParenthesesInspection : AbstractKotlinInspection() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
@@ -47,17 +50,41 @@ class MoveSuspiciousCallableReferenceIntoParenthesesInspection : AbstractKotlinI
|
||||
}
|
||||
}
|
||||
}
|
||||
val quickFix = if (canMove(lambdaExpression, callableReference, context))
|
||||
IntentionWrapper(MoveIntoParenthesesIntention(), lambdaExpression.containingFile)
|
||||
else
|
||||
null
|
||||
holder.registerProblem(
|
||||
lambdaExpression,
|
||||
"Suspicious callable reference as the only lambda element",
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
IntentionWrapper(MoveIntoParenthesesIntention(), lambdaExpression.containingFile)
|
||||
quickFix
|
||||
)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun canMove(
|
||||
lambdaExpression: KtLambdaExpression,
|
||||
callableReference: KtCallableReferenceExpression,
|
||||
context: BindingContext
|
||||
): Boolean {
|
||||
val lambdaDescriptor = context[BindingContext.FUNCTION, lambdaExpression.functionLiteral] ?: return false
|
||||
val lambdaParameter = lambdaDescriptor.extensionReceiverParameter ?: lambdaDescriptor.valueParameters.singleOrNull()
|
||||
|
||||
val functionReceiver = callableReference.receiverExpression?.mainReference?.resolveToDescriptors(context)?.firstOrNull()
|
||||
if (functionReceiver == lambdaParameter) return true
|
||||
val lambdaParameterType = lambdaParameter?.type
|
||||
if (functionReceiver is VariableDescriptor && functionReceiver.type == lambdaParameterType) return true
|
||||
if (functionReceiver is ClassDescriptor && functionReceiver == lambdaParameterType?.constructor?.declarationDescriptor) return true
|
||||
|
||||
if (lambdaParameterType == null) return false
|
||||
val functionDescriptor =
|
||||
callableReference.callableReference.mainReference.resolveToDescriptors(context).firstOrNull() as? FunctionDescriptor
|
||||
val functionParameterType = functionDescriptor?.valueParameters?.firstOrNull()?.type ?: return false
|
||||
return functionParameterType == lambdaParameterType
|
||||
}
|
||||
|
||||
class MoveIntoParenthesesIntention : ConvertLambdaToReferenceIntention(
|
||||
"Move suspicious callable reference into parentheses '()'"
|
||||
) {
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
// FIX: none
|
||||
fun test(x: Int) {
|
||||
fun p(s: String): Boolean = true
|
||||
x.let {<caret> ::p }
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
// FIX: none
|
||||
fun test(x: Int) {
|
||||
val p = { _: String -> true }
|
||||
x.run {<caret> p::invoke }
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// FIX: none
|
||||
fun test(x: Int, y: String) {
|
||||
x.run {<caret> y::length }
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
// FIX: none
|
||||
fun test(x: Int) {
|
||||
x.run {<caret> String::length }
|
||||
}
|
||||
@@ -190,7 +190,12 @@ abstract class AbstractLocalInspectionTest : KotlinLightCodeInsightFixtureTestCa
|
||||
|
||||
val allLocalFixActions = highlightInfos.flatMap { it.quickFixActionMarkers ?: emptyList() }.map { it.first.action }
|
||||
|
||||
val localFixActions = allLocalFixActions.filter { fix -> localFixTextString == null || fix.text == localFixTextString }
|
||||
val localFixActions = if (localFixTextString == null || localFixTextString == "none") {
|
||||
allLocalFixActions
|
||||
} else {
|
||||
allLocalFixActions.filter { fix -> fix.text == localFixTextString }
|
||||
}
|
||||
|
||||
val availableDescription = allLocalFixActions.joinToString { it.text }
|
||||
|
||||
val fixDescription = localFixTextString?.let { "with specified text '$localFixTextString'" } ?: ""
|
||||
@@ -201,6 +206,10 @@ abstract class AbstractLocalInspectionTest : KotlinLightCodeInsightFixtureTestCa
|
||||
)
|
||||
|
||||
val localFixAction = localFixActions.singleOrNull { it !is EmptyIntentionAction }
|
||||
if (localFixTextString == "none") {
|
||||
Assert.assertTrue("Expected no fix action", localFixAction == null)
|
||||
return false
|
||||
}
|
||||
TestCase.assertTrue(
|
||||
"More than one fix action $fixDescription\n" +
|
||||
"Available actions: $availableDescription",
|
||||
|
||||
+20
@@ -3252,6 +3252,26 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
runTest("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/implicitThisReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invalidFunctionReference.kt")
|
||||
public void testInvalidFunctionReference() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/invalidFunctionReference.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invalidFunctionReference2.kt")
|
||||
public void testInvalidFunctionReference2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/invalidFunctionReference2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invalidFunctionReference3.kt")
|
||||
public void testInvalidFunctionReference3() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/invalidFunctionReference3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("invalidFunctionReference4.kt")
|
||||
public void testInvalidFunctionReference4() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/invalidFunctionReference4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("it.kt")
|
||||
public void testIt() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/moveSuspiciousCallableReferenceIntoParentheses/it.kt");
|
||||
|
||||
Reference in New Issue
Block a user