From bdb9f00c756ef5640caaeadb0183c30f02b69e5f Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Mon, 19 Jun 2017 16:00:30 +0300 Subject: [PATCH] Introduce "Useless call on collection type" inspection Related to KT-12165 Supported functions: filterNotNull, filterIsInstance, mapNotNull, mapNotNullTo, mapIndexedNotNull, mapIndexedNotNullTo Also, "Useless cal on not-null" improved a bit --- .../UselessCallOnCollection.html | 5 + idea/src/META-INF/plugin.xml | 8 ++ .../AbstractUselessCallInspection.kt | 69 ++++++++++ .../collections/RenameUselessCallFix.kt | 8 +- .../UselessCallOnCollectionInspection.kt | 100 +++++++++++++++ .../UselessCallOnNotNullInspection.kt | 120 ++++++++---------- .../uselessCallOnCollection/.inspection | 1 + .../FilterIsExactInstance.kt | 3 + .../FilterIsExactInstance.kt.after | 3 + .../FilterIsExactInstanceFake.kt | 4 + .../FilterIsForFlexible.kt | 4 + .../FilterIsSupertypeInstance.kt | 3 + .../FilterIsSupertypeInstance.kt.after | 3 + .../FilterIsSupertypeInstanceFake.kt | 4 + .../uselessCallOnCollection/FilterNotNull.kt | 3 + .../FilterNotNull.kt.after | 3 + .../MapIndexedNotNullTo.kt | 3 + .../MapIndexedNotNullTo.kt.after | 3 + .../uselessCallOnCollection/MapNotNullTo.kt | 3 + .../MapNotNullTo.kt.after | 3 + .../MapNotNullWithLambda.kt | 3 + .../MapNotNullWithLambda.kt.after | 3 + .../MapNotNullWithLambdaFake.kt | 4 + .../MapNotNullWithReference.kt | 3 + .../MapNotNullWithReference.kt.after | 3 + .../MapNotNullWithReferenceFake.kt | 6 + .../filterNotNullFake.kt | 4 + .../uselessCallOnNotNull/NullOrBlankSafe.kt | 1 + .../NullOrBlankSafe.kt.after | 1 + .../uselessCallOnNotNull/NullOrEmptySafe.kt | 5 + .../NullOrEmptySafe.kt.after | 5 + .../LocalInspectionTestGenerated.java | 93 ++++++++++++++ 32 files changed, 411 insertions(+), 73 deletions(-) create mode 100644 idea/resources/inspectionDescriptions/UselessCallOnCollection.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/collections/AbstractUselessCallInspection.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnCollectionInspection.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/.inspection create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstance.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstance.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsExactInstanceFake.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsForFlexible.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstance.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstance.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterIsSupertypeInstanceFake.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNull.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/FilterNotNull.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapIndexedNotNullTo.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapIndexedNotNullTo.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullTo.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullTo.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambda.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambda.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithLambdaFake.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReference.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReference.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/MapNotNullWithReferenceFake.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnCollection/filterNotNullFake.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrEmptySafe.kt create mode 100644 idea/testData/inspectionsLocal/collections/uselessCallOnNotNull/NullOrEmptySafe.kt.after 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");