JavaMapForEachInspection: report for expression with implicit receiver
#KT-31833 Fixed
This commit is contained in:
committed by
Yan Zhulanow
parent
85e8b56a02
commit
93db78e7ac
@@ -13,49 +13,43 @@ import org.jetbrains.kotlin.idea.KotlinBundle
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.getLastLambdaExpression
|
import org.jetbrains.kotlin.idea.core.getLastLambdaExpression
|
||||||
import org.jetbrains.kotlin.idea.inspections.collections.isMap
|
import org.jetbrains.kotlin.idea.inspections.collections.isMap
|
||||||
import org.jetbrains.kotlin.idea.intentions.callExpression
|
import org.jetbrains.kotlin.idea.util.textRangeIn
|
||||||
import org.jetbrains.kotlin.idea.util.calleeTextRangeInThis
|
|
||||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
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.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.synthetic.isResolvedWithSamConversions
|
import org.jetbrains.kotlin.synthetic.isResolvedWithSamConversions
|
||||||
|
|
||||||
class JavaMapForEachInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>(
|
class JavaMapForEachInspection : AbstractApplicabilityBasedInspection<KtCallExpression>(
|
||||||
KtDotQualifiedExpression::class.java
|
KtCallExpression::class.java
|
||||||
) {
|
) {
|
||||||
override fun isApplicable(element: KtDotQualifiedExpression): Boolean {
|
override fun isApplicable(element: KtCallExpression): Boolean {
|
||||||
val callExpression = element.callExpression ?: return false
|
val calleeExpression = element.calleeExpression ?: return false
|
||||||
val calleeExpression = callExpression.calleeExpression ?: return false
|
|
||||||
if (calleeExpression.text != "forEach") 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
|
val lambdaParameters = lambda.valueParameters
|
||||||
if (lambdaParameters.size != 2 || lambdaParameters.any { it.destructuringDeclaration != null }) return false
|
if (lambdaParameters.size != 2 || lambdaParameters.any { it.destructuringDeclaration != null }) return false
|
||||||
|
|
||||||
val context = element.analyze(BodyResolveMode.PARTIAL)
|
val context = element.analyze(BodyResolveMode.PARTIAL)
|
||||||
if (!element.receiverExpression.getType(context).isMap(DefaultBuiltIns.Instance)) return false
|
val resolvedCall = element.getResolvedCall(context) ?: return false
|
||||||
val resolvedCall = callExpression.getResolvedCall(context) ?: return false
|
return resolvedCall.dispatchReceiver?.type?.isMap(DefaultBuiltIns.Instance) == true && resolvedCall.isResolvedWithSamConversions()
|
||||||
return 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")
|
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 val defaultFixText get() = KotlinBundle.message("replace.with.kotlin.s.foreach")
|
||||||
|
|
||||||
override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) {
|
override fun applyTo(element: KtCallExpression, project: Project, editor: Editor?) {
|
||||||
val call = element.callExpression ?: return
|
val lambda = element.lambda() ?: return
|
||||||
val lambda = call.lambda() ?: return
|
|
||||||
val valueParameters = lambda.valueParameters
|
val valueParameters = lambda.valueParameters
|
||||||
lambda.functionLiteral.valueParameterList?.replace(
|
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) {}
|
||||||
+5
@@ -5417,6 +5417,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
runTest("idea/testData/inspectionsLocal/javaMapForEach/destructuringDeclaration.kt");
|
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")
|
@TestMetadata("java.kt")
|
||||||
public void testJava() throws Exception {
|
public void testJava() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/javaMapForEach/java.kt");
|
runTest("idea/testData/inspectionsLocal/javaMapForEach/java.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user