From a059d50c8f0ee8552c45495c2b90f7d98388e0b9 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Wed, 25 Jul 2018 04:23:04 +0300 Subject: [PATCH] "Call chain may be simplified": fix false positive on Java Map So #KT-25089 Fixed --- .../SimplifiableCallChainInspection.kt | 18 +++++++++++------- .../joinToStringOnHashMap.kt | 6 ++++++ .../joinToStringOnHashMap2.kt | 9 +++++++++ .../LocalInspectionTestGenerated.java | 10 ++++++++++ 4 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnHashMap.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnHashMap2.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt index a2dbfd800c2..0a22f59ca87 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt @@ -8,10 +8,13 @@ package org.jetbrains.kotlin.idea.inspections.collections import com.intellij.codeInspection.ProblemHighlightType import com.intellij.codeInspection.ProblemsHolder import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.qualifiedExpressionVisitor import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf +import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.builtIns import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf @@ -20,16 +23,12 @@ class SimplifiableCallChainInspection : AbstractCallChainChecker() { qualifiedExpressionVisitor(fun(expression) { val conversion = findQualifiedConversion(expression, conversionGroups) check@{ conversion, firstResolvedCall, _, context -> // Do not apply on maps due to lack of relevant stdlib functions - val builtIns = context[BindingContext.EXPRESSION_TYPE_INFO, expression]?.type?.builtIns ?: return@check false val firstReceiverType = firstResolvedCall.extensionReceiver?.type if (firstReceiverType != null) { if (conversion.replacement == "mapNotNull" && KotlinBuiltIns.isPrimitiveArray(firstReceiverType)) return@check false + val builtIns = context[BindingContext.EXPRESSION_TYPE_INFO, expression]?.type?.builtIns ?: return@check false val firstReceiverRawType = firstReceiverType.constructor.declarationDescriptor?.defaultType - if (firstReceiverRawType != null) { - if (firstReceiverRawType.isSubtypeOf(builtIns.map.defaultType) || - firstReceiverRawType.isSubtypeOf(builtIns.mutableMap.defaultType) - ) return@check false - } + if (firstReceiverRawType.isMap(builtIns)) return@check false } if (conversion.replacement.startsWith("joinTo")) { // Function parameter in map must have String result type @@ -83,4 +82,9 @@ class SimplifiableCallChainInspection : AbstractCallChainChecker() { Conversion("kotlin.collections.listOf", "kotlin.collections.filterNotNull", "listOfNotNull") ) } -} \ No newline at end of file +} + +private fun KotlinType?.isMap(builtIns: KotlinBuiltIns): Boolean { + val classDescriptor = this?.constructor?.declarationDescriptor as? ClassDescriptor ?: return false + return classDescriptor.name.asString().endsWith("Map") && classDescriptor.isSubclassOf(builtIns.map) +} diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnHashMap.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnHashMap.kt new file mode 100644 index 00000000000..2f93cb9d57f --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnHashMap.kt @@ -0,0 +1,6 @@ +// PROBLEM: none +// WITH_RUNTIME + +fun test(data: HashMap) { + val result = data.map { "${it.key}: ${it.value}" }.joinToString("\n") +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnHashMap2.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnHashMap2.kt new file mode 100644 index 00000000000..e9845798f62 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnHashMap2.kt @@ -0,0 +1,9 @@ +// PROBLEM: none +// WITH_RUNTIME +import java.util.* + +class MyMap: HashMap() + +fun test(data: MyMap) { + val result = data.map { "${it.key}: ${it.value}" }.joinToString("\n") +} \ 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 a7994742ddb..eae8a59e52d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -579,6 +579,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToString.kt"); } + @TestMetadata("joinToStringOnHashMap.kt") + public void testJoinToStringOnHashMap() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnHashMap.kt"); + } + + @TestMetadata("joinToStringOnHashMap2.kt") + public void testJoinToStringOnHashMap2() throws Exception { + runTest("idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnHashMap2.kt"); + } + @TestMetadata("joinToStringOnMap.kt") public void testJoinToStringOnMap() throws Exception { runTest("idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringOnMap.kt");