diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceExplicitFunctionLiteralParamWithItIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceExplicitFunctionLiteralParamWithItIntention.kt index c1eb2217969..e94c5f8fdb8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceExplicitFunctionLiteralParamWithItIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ReplaceExplicitFunctionLiteralParamWithItIntention.kt @@ -27,14 +27,16 @@ import com.intellij.usageView.UsageInfo import org.jetbrains.kotlin.descriptors.ParameterDescriptor import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.analysis.analyzeAsReplacement +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.core.copied import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors -import org.jetbrains.kotlin.psi.KtFunctionLiteral -import org.jetbrains.kotlin.psi.KtNameReferenceExpression -import org.jetbrains.kotlin.psi.KtSimpleNameExpression -import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType -import org.jetbrains.kotlin.psi.psiUtil.endOffset -import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class ReplaceExplicitFunctionLiteralParamWithItIntention : PsiElementBaseIntentionAction() { override fun getFamilyName() = KotlinBundle.message("replace.explicit.lambda.parameter.with.it") @@ -50,6 +52,19 @@ class ReplaceExplicitFunctionLiteralParamWithItIntention : PsiElementBaseIntenti literal.usesName(element.text) && (!literal.hasParameterSpecification() || literal.usesName("it")) }) return false + val lambda = functionLiteral.parent as? KtLambdaExpression ?: return false + val call = lambda.getStrictParentOfType() + if (call != null) { + val argumentIndex = call.valueArguments.indexOfFirst { it.getArgumentExpression() == lambda } + val callOrQualified = call.getQualifiedExpressionForSelectorOrThis() + val newCallOrQualified = callOrQualified.copied() + val newCall = newCallOrQualified.safeAs()?.callExpression ?: newCallOrQualified.safeAs() ?: return false + val newArgument = newCall.valueArguments.getOrNull(argumentIndex) ?: newCall.lambdaArguments.singleOrNull() ?: return false + newArgument.replace(KtPsiFactory(element).createLambdaExpression("", "TODO()")) + val newContext = newCallOrQualified.analyzeAsReplacement(callOrQualified, callOrQualified.analyze(BodyResolveMode.PARTIAL)) + if (newCallOrQualified.getResolvedCall(newContext)?.resultingDescriptor == null) return false + } + text = KotlinBundle.message("replace.explicit.parameter.0.with.it", parameter.name.toString()) return true } diff --git a/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_inPropertyInitializer.kt b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_inPropertyInitializer.kt new file mode 100644 index 00000000000..24da93f73b2 --- /dev/null +++ b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_inPropertyInitializer.kt @@ -0,0 +1,5 @@ +fun foo() { + val a: (Int) -> Unit = { a -> bar(a) } +} + +fun bar(i: Int) {} \ No newline at end of file diff --git a/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_inPropertyInitializer.kt.after b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_inPropertyInitializer.kt.after new file mode 100644 index 00000000000..7c53e2f449a --- /dev/null +++ b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_inPropertyInitializer.kt.after @@ -0,0 +1,5 @@ +fun foo() { + val a: (Int) -> Unit = { bar(it) } +} + +fun bar(i: Int) {} \ No newline at end of file diff --git a/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_qualifiedExpression.kt b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_qualifiedExpression.kt new file mode 100644 index 00000000000..3e472c92928 --- /dev/null +++ b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_qualifiedExpression.kt @@ -0,0 +1,7 @@ +fun test() { + C().foo { i -> i + 1 } +} + +class C { + fun foo(f: (Int) -> Int) {} +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_qualifiedExpression.kt.after b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_qualifiedExpression.kt.after new file mode 100644 index 00000000000..0c309ce819c --- /dev/null +++ b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_qualifiedExpression.kt.after @@ -0,0 +1,7 @@ +fun test() { + C().foo { it + 1 } +} + +class C { + fun foo(f: (Int) -> Int) {} +} \ No newline at end of file diff --git a/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_overloadResolutionAmbiguity.kt b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_overloadResolutionAmbiguity.kt new file mode 100644 index 00000000000..c5e32310789 --- /dev/null +++ b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_overloadResolutionAmbiguity.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false +fun test() { + foo { i -> i + 1 } +} + +fun foo(f: (Int) -> Int) {} +fun foo(f: (Int, Int) -> Int) {} \ No newline at end of file diff --git a/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_overloadResolutionAmbiguity2.kt b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_overloadResolutionAmbiguity2.kt new file mode 100644 index 00000000000..06f7a61fb9b --- /dev/null +++ b/idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_overloadResolutionAmbiguity2.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false +fun test() { + C().foo { i -> i + 1 } +} + +class C { + fun foo(f: (Int) -> Int) {} + fun foo(f: (Int, Int) -> Int) {} +} \ 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 f96d93abfec..3aa5c19f764 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -14634,11 +14634,21 @@ public class IntentionTestGenerated extends AbstractIntentionTest { runTest("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_formatsProperly.kt"); } + @TestMetadata("applicable_inPropertyInitializer.kt") + public void testApplicable_inPropertyInitializer() throws Exception { + runTest("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_inPropertyInitializer.kt"); + } + @TestMetadata("applicable_nestedLiteralsNoUseInside.kt") public void testApplicable_nestedLiteralsNoUseInside() throws Exception { runTest("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_nestedLiteralsNoUseInside.kt"); } + @TestMetadata("applicable_qualifiedExpression.kt") + public void testApplicable_qualifiedExpression() throws Exception { + runTest("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/applicable_qualifiedExpression.kt"); + } + @TestMetadata("notApplicable_alreadyUsesImplicitIt.kt") public void testNotApplicable_alreadyUsesImplicitIt() throws Exception { runTest("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_alreadyUsesImplicitIt.kt"); @@ -14674,6 +14684,16 @@ public class IntentionTestGenerated extends AbstractIntentionTest { runTest("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_notFunctionLiteralParameter.kt"); } + @TestMetadata("notApplicable_overloadResolutionAmbiguity.kt") + public void testNotApplicable_overloadResolutionAmbiguity() throws Exception { + runTest("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_overloadResolutionAmbiguity.kt"); + } + + @TestMetadata("notApplicable_overloadResolutionAmbiguity2.kt") + public void testNotApplicable_overloadResolutionAmbiguity2() throws Exception { + runTest("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_overloadResolutionAmbiguity2.kt"); + } + @TestMetadata("notApplicable_parameterHasExplicitType.kt") public void testNotApplicable_parameterHasExplicitType() throws Exception { runTest("idea/testData/intentions/replaceExplicitFunctionLiteralParamWithIt/notApplicable_parameterHasExplicitType.kt");