JavaMapForEachInspection: report for expression with implicit receiver

#KT-31833 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-11-15 10:37:51 +09:00
committed by Yan Zhulanow
parent 85e8b56a02
commit 93db78e7ac
4 changed files with 39 additions and 20 deletions
@@ -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>(
KtDotQualifiedExpression::class.java
class JavaMapForEachInspection : AbstractApplicabilityBasedInspection<KtCallExpression>(
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})")
)
}
@@ -0,0 +1,10 @@
// RUNTIME_WITH_FULL_JDK
fun test(map: Map<Int, String>) {
map.run {
<caret>forEach { key, value ->
foo(key, value)
}
}
}
fun foo(i: Int, s: String) {}
@@ -0,0 +1,10 @@
// RUNTIME_WITH_FULL_JDK
fun test(map: Map<Int, String>) {
map.run {
forEach { (key, value) ->
foo(key, value)
}
}
}
fun foo(i: Int, s: String) {}
@@ -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");