From 93db78e7acc0d4b3f97ca031ca3d91be4b5cca99 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Fri, 15 Nov 2019 10:37:51 +0900 Subject: [PATCH] JavaMapForEachInspection: report for expression with implicit receiver #KT-31833 Fixed --- .../inspections/JavaMapForEachInspection.kt | 34 ++++++++----------- .../javaMapForEach/implicitReceiver.kt | 10 ++++++ .../javaMapForEach/implicitReceiver.kt.after | 10 ++++++ .../LocalInspectionTestGenerated.java | 5 +++ 4 files changed, 39 insertions(+), 20 deletions(-) create mode 100644 idea/testData/inspectionsLocal/javaMapForEach/implicitReceiver.kt create mode 100644 idea/testData/inspectionsLocal/javaMapForEach/implicitReceiver.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/JavaMapForEachInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/JavaMapForEachInspection.kt index 0302b149e61..ba42ab1cc23 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/JavaMapForEachInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/JavaMapForEachInspection.kt @@ -13,49 +13,43 @@ import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.getLastLambdaExpression import org.jetbrains.kotlin.idea.inspections.collections.isMap -import org.jetbrains.kotlin.idea.intentions.callExpression -import org.jetbrains.kotlin.idea.util.calleeTextRangeInThis +import org.jetbrains.kotlin.idea.util.textRangeIn import org.jetbrains.kotlin.psi.KtCallExpression -import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtLambdaExpression import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall -import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.synthetic.isResolvedWithSamConversions -class JavaMapForEachInspection : AbstractApplicabilityBasedInspection( - KtDotQualifiedExpression::class.java +class JavaMapForEachInspection : AbstractApplicabilityBasedInspection( + KtCallExpression::class.java ) { - override fun isApplicable(element: KtDotQualifiedExpression): Boolean { - val callExpression = element.callExpression ?: return false - val calleeExpression = callExpression.calleeExpression ?: return false + override fun isApplicable(element: KtCallExpression): Boolean { + val calleeExpression = element.calleeExpression ?: return false if (calleeExpression.text != "forEach") return false - if (callExpression.valueArguments.size != 1) return false + if (element.valueArguments.size != 1) return false - val lambda = callExpression.lambda() ?: return false + val lambda = element.lambda() ?: return false val lambdaParameters = lambda.valueParameters if (lambdaParameters.size != 2 || lambdaParameters.any { it.destructuringDeclaration != null }) return false val context = element.analyze(BodyResolveMode.PARTIAL) - if (!element.receiverExpression.getType(context).isMap(DefaultBuiltIns.Instance)) return false - val resolvedCall = callExpression.getResolvedCall(context) ?: return false - return resolvedCall.isResolvedWithSamConversions() + val resolvedCall = element.getResolvedCall(context) ?: return false + return resolvedCall.dispatchReceiver?.type?.isMap(DefaultBuiltIns.Instance) == true && resolvedCall.isResolvedWithSamConversions() } - override fun inspectionHighlightRangeInElement(element: KtDotQualifiedExpression): TextRange? = element.calleeTextRangeInThis() + override fun inspectionHighlightRangeInElement(element: KtCallExpression): TextRange? = element.calleeExpression?.textRangeIn(element) - override fun inspectionText(element: KtDotQualifiedExpression) = + override fun inspectionText(element: KtCallExpression) = KotlinBundle.message("java.map.foreach.method.call.should.be.replaced.with.kotlin.s.foreach") override val defaultFixText get() = KotlinBundle.message("replace.with.kotlin.s.foreach") - override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) { - val call = element.callExpression ?: return - val lambda = call.lambda() ?: return + override fun applyTo(element: KtCallExpression, project: Project, editor: Editor?) { + val lambda = element.lambda() ?: return val valueParameters = lambda.valueParameters lambda.functionLiteral.valueParameterList?.replace( - KtPsiFactory(call).createLambdaParameterList("(${valueParameters[0].text}, ${valueParameters[1].text})") + KtPsiFactory(element).createLambdaParameterList("(${valueParameters[0].text}, ${valueParameters[1].text})") ) } diff --git a/idea/testData/inspectionsLocal/javaMapForEach/implicitReceiver.kt b/idea/testData/inspectionsLocal/javaMapForEach/implicitReceiver.kt new file mode 100644 index 00000000000..4b9e732b5d6 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaMapForEach/implicitReceiver.kt @@ -0,0 +1,10 @@ +// RUNTIME_WITH_FULL_JDK +fun test(map: Map) { + map.run { + forEach { key, value -> + foo(key, value) + } + } +} + +fun foo(i: Int, s: String) {} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/javaMapForEach/implicitReceiver.kt.after b/idea/testData/inspectionsLocal/javaMapForEach/implicitReceiver.kt.after new file mode 100644 index 00000000000..cfee6efc8d8 --- /dev/null +++ b/idea/testData/inspectionsLocal/javaMapForEach/implicitReceiver.kt.after @@ -0,0 +1,10 @@ +// RUNTIME_WITH_FULL_JDK +fun test(map: Map) { + map.run { + forEach { (key, value) -> + foo(key, value) + } + } +} + +fun foo(i: Int, s: String) {} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index bfeed2be4a9..2d2a1515f79 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -5417,6 +5417,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/javaMapForEach/destructuringDeclaration.kt"); } + @TestMetadata("implicitReceiver.kt") + public void testImplicitReceiver() throws Exception { + runTest("idea/testData/inspectionsLocal/javaMapForEach/implicitReceiver.kt"); + } + @TestMetadata("java.kt") public void testJava() throws Exception { runTest("idea/testData/inspectionsLocal/javaMapForEach/java.kt");