diff --git a/idea/resources/inspectionDescriptions/UselessCallOnCollection.html b/idea/resources/inspectionDescriptions/UselessCallOnCollection.html
new file mode 100644
index 00000000000..e65ff5de01d
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/UselessCallOnCollection.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports filter-like calls on already filtered collections, e.g. listOf("abc").filterNotNull()
+
+
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 13ef197560c..427cf1179df 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -2258,6 +2258,14 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/AbstractUselessCallInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/AbstractUselessCallInspection.kt
new file mode 100644
index 00000000000..474cfcddaf0
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/AbstractUselessCallInspection.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.inspections.collections
+
+import com.intellij.codeInspection.ProblemsHolder
+import org.jetbrains.kotlin.idea.caches.resolve.analyze
+import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
+import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.KtExpression
+import org.jetbrains.kotlin.psi.KtQualifiedExpression
+import org.jetbrains.kotlin.psi.KtVisitorVoid
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
+import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
+import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
+
+abstract class AbstractUselessCallInspection : AbstractKotlinInspection() {
+
+ protected abstract val uselessFqNames: Map
+
+ protected abstract val uselessNames: Set
+
+ protected abstract fun QualifiedExpressionVisitor.suggestConversionIfNeeded(
+ expression: KtQualifiedExpression,
+ calleeExpression: KtExpression,
+ context: BindingContext,
+ conversion: Conversion
+ )
+
+ inner class QualifiedExpressionVisitor internal constructor(val holder: ProblemsHolder, val isOnTheFly: Boolean) : KtVisitorVoid() {
+ override fun visitQualifiedExpression(expression: KtQualifiedExpression) {
+ super.visitQualifiedExpression(expression)
+ val selector = expression.selectorExpression as? KtCallExpression ?: return
+ val calleeExpression = selector.calleeExpression ?: return
+ if (calleeExpression.text !in uselessNames) return
+
+ val context = expression.analyze(BodyResolveMode.PARTIAL)
+ val resolvedCall = expression.getResolvedCall(context) ?: return
+ val conversion = uselessFqNames[resolvedCall.resultingDescriptor.fqNameOrNull()?.asString()] ?: return
+
+ suggestConversionIfNeeded(expression, calleeExpression, context, conversion)
+ }
+ }
+
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = QualifiedExpressionVisitor(holder, isOnTheFly)
+
+
+ protected companion object {
+ data class Conversion(val replacementName: String? = null)
+
+ val deleteConversion = Conversion()
+
+ fun Set.toShortNames() = mapTo(mutableSetOf()) { fqName -> fqName.takeLastWhile { it != '.' } }
+ }
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/RenameUselessCallFix.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/RenameUselessCallFix.kt
index 0c5e1108051..33416fea1cb 100644
--- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/RenameUselessCallFix.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/RenameUselessCallFix.kt
@@ -20,10 +20,9 @@ import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.idea.core.replaced
+import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtQualifiedExpression
-import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression
-import org.jetbrains.kotlin.psi.createExpressionByPattern
class RenameUselessCallFix(val newName: String) : LocalQuickFix {
override fun getName() = "Rename useless call to '$newName'"
@@ -33,8 +32,9 @@ class RenameUselessCallFix(val newName: String) : LocalQuickFix {
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
(descriptor.psiElement as? KtQualifiedExpression)?.let {
val factory = KtPsiFactory(it)
- val sign = if (it is KtSafeQualifiedExpression) "?." else "."
- it.replaced(factory.createExpressionByPattern("$0$sign$newName()", it.receiverExpression))
+ val selectorCallExpression = it.selectorExpression as? KtCallExpression
+ val calleeExpression = selectorCallExpression?.calleeExpression ?: return
+ calleeExpression.replaced(factory.createExpression(newName))
}
}
}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnCollectionInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnCollectionInspection.kt
new file mode 100644
index 00000000000..e0f9e1d761c
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnCollectionInspection.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.inspections.collections
+
+import com.intellij.codeInspection.ProblemHighlightType
+import com.intellij.openapi.util.TextRange
+import org.jetbrains.kotlin.builtins.KotlinBuiltIns
+import org.jetbrains.kotlin.builtins.getFunctionalClassKind
+import org.jetbrains.kotlin.builtins.isFunctionType
+import org.jetbrains.kotlin.psi.KtExpression
+import org.jetbrains.kotlin.psi.KtQualifiedExpression
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
+import org.jetbrains.kotlin.resolve.calls.callUtil.getType
+import org.jetbrains.kotlin.types.TypeUtils
+import org.jetbrains.kotlin.types.isFlexible
+import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
+
+class UselessCallOnCollectionInspection : AbstractUselessCallInspection() {
+ override val uselessFqNames = mapOf("kotlin.collections.filterNotNull" to deleteConversion,
+ "kotlin.collections.filterIsInstance" to deleteConversion,
+ "kotlin.collections.mapNotNull" to Conversion("map"),
+ "kotlin.collections.mapNotNullTo" to Conversion("mapTo"),
+ "kotlin.collections.mapIndexedNotNull" to Conversion("mapIndexed"),
+ "kotlin.collections.mapIndexedNotNullTo" to Conversion("mapIndexedTo"))
+
+ override val uselessNames = uselessFqNames.keys.toShortNames()
+
+ override fun QualifiedExpressionVisitor.suggestConversionIfNeeded(
+ expression: KtQualifiedExpression,
+ calleeExpression: KtExpression,
+ context: BindingContext,
+ conversion: Conversion
+ ) {
+ val receiverType = expression.receiverExpression.getType(context) ?: return
+ val receiverTypeArgument = receiverType.arguments.singleOrNull()?.type ?: return
+ val resolvedCall = expression.getResolvedCall(context) ?: return
+ if (calleeExpression.text == "filterIsInstance") {
+ val typeParameterDescriptor = resolvedCall.candidateDescriptor.typeParameters.singleOrNull() ?: return
+ val argumentType = resolvedCall.typeArguments[typeParameterDescriptor] ?: return
+ if (receiverTypeArgument.isFlexible() || !receiverTypeArgument.isSubtypeOf(argumentType)) return
+ }
+ else {
+ // xxxNotNull
+ if (TypeUtils.isNullableType(receiverTypeArgument)) return
+ if (calleeExpression.text != "filterNotNull") {
+ // Also check last argument functional type to have not-null result
+ val lastParameter = resolvedCall.resultingDescriptor.valueParameters.lastOrNull() ?: return
+ val lastArgument = resolvedCall.valueArguments[lastParameter]?.arguments?.singleOrNull() ?: return
+ val functionalType = lastArgument.getArgumentExpression()?.getType(context) ?: return
+ // Both Function & KFunction must pass here
+ if (functionalType.constructor.declarationDescriptor?.getFunctionalClassKind() == null) return
+ val resultType = functionalType.arguments.lastOrNull()?.type ?: return
+ if (TypeUtils.isNullableType(resultType)) return
+ }
+ }
+
+ val newName = conversion.replacementName
+ if (newName != null) {
+ val descriptor = holder.manager.createProblemDescriptor(
+ expression,
+ TextRange(expression.operationTokenNode.startOffset - expression.startOffset,
+ calleeExpression.endOffset - expression.startOffset),
+ "Call on collection type may be reduced",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ isOnTheFly,
+ RenameUselessCallFix(newName)
+ )
+ holder.registerProblem(descriptor)
+ }
+ else {
+ val descriptor = holder.manager.createProblemDescriptor(
+ expression,
+ TextRange(expression.operationTokenNode.startOffset - expression.startOffset,
+ calleeExpression.endOffset - expression.startOffset),
+ "Useless call on collection type",
+ ProblemHighlightType.LIKE_UNUSED_SYMBOL,
+ isOnTheFly,
+ RemoveUselessCallFix()
+ )
+ holder.registerProblem(descriptor)
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnNotNullInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnNotNullInspection.kt
index d2904887b02..170309c03f2 100644
--- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnNotNullInspection.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnNotNullInspection.kt
@@ -18,88 +18,70 @@ package org.jetbrains.kotlin.idea.inspections.collections
import com.intellij.codeInspection.IntentionWrapper
import com.intellij.codeInspection.ProblemHighlightType
-import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
-import com.intellij.psi.PsiElementVisitor
-import org.jetbrains.kotlin.idea.caches.resolve.analyze
-import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.quickfix.ReplaceWithDotCallFix
-import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression
-import org.jetbrains.kotlin.psi.KtVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
-import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
+import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
-import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
-import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.TypeUtils
-class UselessCallOnNotNullInspection : AbstractKotlinInspection() {
+class UselessCallOnNotNullInspection : AbstractUselessCallInspection() {
+ override val uselessFqNames = mapOf("kotlin.collections.orEmpty" to deleteConversion,
+ "kotlin.text.orEmpty" to deleteConversion,
+ "kotlin.text.isNullOrEmpty" to Conversion("isEmpty"),
+ "kotlin.text.isNullOrBlank" to Conversion("isBlank"))
- override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
- return object : KtVisitorVoid() {
- override fun visitQualifiedExpression(expression: KtQualifiedExpression) {
- super.visitQualifiedExpression(expression)
- val selector = expression.selectorExpression as? KtCallExpression ?: return
- val calleeExpression = selector.calleeExpression ?: return
- if (calleeExpression.text !in names) return
+ override val uselessNames = uselessFqNames.keys.toShortNames()
- val context = expression.analyze(BodyResolveMode.PARTIAL)
- val resolvedCall = expression.getResolvedCall(context) ?: return
- val conversion = fqNames[resolvedCall.resultingDescriptor.fqNameOrNull()?.asString()] ?: return
- val newName = conversion.replacementName
+ override fun QualifiedExpressionVisitor.suggestConversionIfNeeded(
+ expression: KtQualifiedExpression,
+ calleeExpression: KtExpression,
+ context: BindingContext,
+ conversion: Conversion
+ ) {
+ val newName = conversion.replacementName
- val safeExpression = expression as? KtSafeQualifiedExpression
- val notNullType = expression.receiverExpression.getType(context)?.let { TypeUtils.isNullableType(it) } == false
- if (newName != null && (notNullType || safeExpression != null)) {
- val descriptor = holder.manager.createProblemDescriptor(
- expression,
- TextRange(expression.operationTokenNode.startOffset - expression.startOffset,
- calleeExpression.endOffset - expression.startOffset),
- "Call on not-null type may be reduced",
- ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
- isOnTheFly,
- RenameUselessCallFix(newName)
- )
- holder.registerProblem(descriptor)
- }
- else if (notNullType) {
- val descriptor = holder.manager.createProblemDescriptor(
- expression,
- TextRange(expression.operationTokenNode.startOffset - expression.startOffset,
- calleeExpression.endOffset - expression.startOffset),
- "Useless call on not-null type",
- ProblemHighlightType.LIKE_UNUSED_SYMBOL,
- isOnTheFly,
- RemoveUselessCallFix()
- )
- holder.registerProblem(descriptor)
- }
- else if (safeExpression != null) {
- holder.registerProblem(
- safeExpression.operationTokenNode.psi,
- "This call is useless with ?.",
- ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
- IntentionWrapper(ReplaceWithDotCallFix(safeExpression), safeExpression.containingKtFile)
- )
- }
- }
+ val safeExpression = expression as? KtSafeQualifiedExpression
+ val notNullType = expression.receiverExpression.getType(context)?.let { TypeUtils.isNullableType(it) } == false
+ val defaultRange = TextRange(expression.operationTokenNode.startOffset, calleeExpression.endOffset)
+ .shiftRight(-expression.startOffset)
+ if (newName != null && (notNullType || safeExpression != null)) {
+ val fixes = listOf(RenameUselessCallFix(newName)) + listOfNotNull(safeExpression?.let {
+ IntentionWrapper(ReplaceWithDotCallFix(safeExpression), safeExpression.containingKtFile)
+ })
+ val descriptor = holder.manager.createProblemDescriptor(
+ expression,
+ defaultRange,
+ "Call on not-null type may be reduced",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ isOnTheFly,
+ *fixes.toTypedArray()
+ )
+ holder.registerProblem(descriptor)
+ }
+ else if (notNullType) {
+ val descriptor = holder.manager.createProblemDescriptor(
+ expression,
+ defaultRange,
+ "Useless call on not-null type",
+ ProblemHighlightType.LIKE_UNUSED_SYMBOL,
+ isOnTheFly,
+ RemoveUselessCallFix()
+ )
+ holder.registerProblem(descriptor)
+ }
+ else if (safeExpression != null) {
+ holder.registerProblem(
+ safeExpression.operationTokenNode.psi,
+ "This call is useless with ?.",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ IntentionWrapper(ReplaceWithDotCallFix(safeExpression), safeExpression.containingKtFile)
+ )
}
- }
-
- companion object {
- private data class Conversion(val replacementName: String? = null)
-
- private val deleteConversion = Conversion()
-
- private val fqNames = mapOf("kotlin.collections.orEmpty" to deleteConversion,
- "kotlin.text.orEmpty" to deleteConversion,
- "kotlin.text.isNullOrEmpty" to Conversion("isEmpty"),
- "kotlin.text.isNullOrBlank" to Conversion("isBlank"))
-
- private val names = fqNames.keys.mapTo(mutableSetOf()) { fqName -> fqName.takeLastWhile { it != '.' } }
}
}
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/.inspection b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/.inspection
new file mode 100644
index 00000000000..c378a54268f
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.collections.UselessCallOnCollectionInspection
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstance.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstance.kt
new file mode 100644
index 00000000000..aa0178d3305
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstance.kt
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1").filterIsInstance()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstance.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstance.kt.after
new file mode 100644
index 00000000000..e020a804f02
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstance.kt.after
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1")
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceFake.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceFake.kt
new file mode 100644
index 00000000000..a3c513a18ca
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceFake.kt
@@ -0,0 +1,4 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+val x = listOf(true, "1").filterIsInstance()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsForFlexible.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsForFlexible.kt
new file mode 100644
index 00000000000..c31cc2b85e0
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsForFlexible.kt
@@ -0,0 +1,4 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+val x = listOf(System.getProperty("")).filterIsInstance()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstance.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstance.kt
new file mode 100644
index 00000000000..85d0440582a
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstance.kt
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1").filterIsInstance()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstance.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstance.kt.after
new file mode 100644
index 00000000000..e020a804f02
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstance.kt.after
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1")
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstanceFake.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstanceFake.kt
new file mode 100644
index 00000000000..bf816ff387c
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstanceFake.kt
@@ -0,0 +1,4 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+val x = listOf("1", null).filterIsInstance()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNull.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNull.kt
new file mode 100644
index 00000000000..f19f18ded64
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNull.kt
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1").filterNotNull()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNull.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNull.kt.after
new file mode 100644
index 00000000000..e020a804f02
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNull.kt.after
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1")
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapIndexedNotNullTo.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapIndexedNotNullTo.kt
new file mode 100644
index 00000000000..64ee2cf0cf1
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapIndexedNotNullTo.kt
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val someList = listOf("alpha", "beta").mapIndexedNotNullTo(destination = hashSetOf()) { index, value -> index + value.length }
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapIndexedNotNullTo.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapIndexedNotNullTo.kt.after
new file mode 100644
index 00000000000..a5f5e55dfa0
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapIndexedNotNullTo.kt.after
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val someList = listOf("alpha", "beta").mapIndexedTo(destination = hashSetOf()) { index, value -> index + value.length }
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullTo.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullTo.kt
new file mode 100644
index 00000000000..3c80c8615d8
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullTo.kt
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1").mapNotNullTo(mutableSetOf()) { it.toInt() }
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullTo.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullTo.kt.after
new file mode 100644
index 00000000000..aef4ecd5d99
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullTo.kt.after
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1").mapTo(mutableSetOf()) { it.toInt() }
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambda.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambda.kt
new file mode 100644
index 00000000000..3a9c0eac5c0
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambda.kt
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1").mapNotNull { it.toInt() }
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambda.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambda.kt.after
new file mode 100644
index 00000000000..b4e2aaf78d4
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambda.kt.after
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1").map { it.toInt() }
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambdaFake.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambdaFake.kt
new file mode 100644
index 00000000000..f591432669b
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambdaFake.kt
@@ -0,0 +1,4 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+val x = listOf("1").mapNotNull { if (it.isNotEmpty()) it.toInt() else null }
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReference.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReference.kt
new file mode 100644
index 00000000000..534d0dd9807
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReference.kt
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1").mapNotNull(String::toInt)
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReference.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReference.kt.after
new file mode 100644
index 00000000000..43f1542b973
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReference.kt.after
@@ -0,0 +1,3 @@
+// WITH_RUNTIME
+
+val x = listOf("1").map(String::toInt)
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReferenceFake.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReferenceFake.kt
new file mode 100644
index 00000000000..f2c7e3e84da
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReferenceFake.kt
@@ -0,0 +1,6 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+fun String.toNullableInt(): Int? = null
+
+val x = listOf("1").mapNotNull(String::toNullableInt)
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/filterNotNullFake.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/filterNotNullFake.kt
new file mode 100644
index 00000000000..39cf8e50da9
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnCollection/filterNotNullFake.kt
@@ -0,0 +1,4 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+val x = listOf("1", null).filterNotNull()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrBlankSafe.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrBlankSafe.kt
index 43fee35ad1e..4ffa64de9ca 100644
--- a/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrBlankSafe.kt
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrBlankSafe.kt
@@ -1,4 +1,5 @@
// WITH_RUNTIME
+// FIX: Rename useless call to 'isBlank'
val s: String? = ""
val blank = s?.isNullOrBlank()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrBlankSafe.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrBlankSafe.kt.after
index 1e519a5c5b3..d52577468f6 100644
--- a/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrBlankSafe.kt.after
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrBlankSafe.kt.after
@@ -1,4 +1,5 @@
// WITH_RUNTIME
+// FIX: Rename useless call to 'isBlank'
val s: String? = ""
val blank = s?.isBlank()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrEmptySafe.kt b/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrEmptySafe.kt
new file mode 100644
index 00000000000..cd7e9ec6124
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrEmptySafe.kt
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+// FIX: Replace with dot call
+
+val s: String? = ""
+val empty = s?.isNullOrEmpty()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrEmptySafe.kt.after b/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrEmptySafe.kt.after
new file mode 100644
index 00000000000..031eee691c4
--- /dev/null
+++ b/idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrEmptySafe.kt.after
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+// FIX: Replace with dot call
+
+val s: String? = ""
+val empty = s.isNullOrEmpty()
\ 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 c6374efb7eb..502022c3d0a 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -59,6 +59,93 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/collections"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}
+ @TestMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class UselessCallOnCollection extends AbstractLocalInspectionTest {
+ public void testAllFilesPresentInUselessCallOnCollection() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/collections/uselessCallOnCollection"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("FilterIsExactInstance.kt")
+ public void testFilterIsExactInstance() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstance.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FilterIsExactInstanceFake.kt")
+ public void testFilterIsExactInstanceFake() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceFake.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FilterIsForFlexible.kt")
+ public void testFilterIsForFlexible() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsForFlexible.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FilterIsSupertypeInstance.kt")
+ public void testFilterIsSupertypeInstance() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstance.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FilterIsSupertypeInstanceFake.kt")
+ public void testFilterIsSupertypeInstanceFake() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstanceFake.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("FilterNotNull.kt")
+ public void testFilterNotNull() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNull.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("filterNotNullFake.kt")
+ public void testFilterNotNullFake() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/filterNotNullFake.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("MapIndexedNotNullTo.kt")
+ public void testMapIndexedNotNullTo() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapIndexedNotNullTo.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("MapNotNullTo.kt")
+ public void testMapNotNullTo() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullTo.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("MapNotNullWithLambda.kt")
+ public void testMapNotNullWithLambda() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambda.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("MapNotNullWithLambdaFake.kt")
+ public void testMapNotNullWithLambdaFake() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambdaFake.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("MapNotNullWithReference.kt")
+ public void testMapNotNullWithReference() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReference.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("MapNotNullWithReferenceFake.kt")
+ public void testMapNotNullWithReferenceFake() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReferenceFake.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnNotNull")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -97,6 +184,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
doTest(fileName);
}
+ @TestMetadata("NullOrEmptySafe.kt")
+ public void testNullOrEmptySafe() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrEmptySafe.kt");
+ doTest(fileName);
+ }
+
@TestMetadata("OrEmptyFake.kt")
public void testOrEmptyFake() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/OrEmptyFake.kt");