From 36be1fdaefdb302fd4b89922407792cf2ea4c588 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Wed, 21 Jun 2017 19:54:35 +0300 Subject: [PATCH] Introduce "simplifiable call chain on collection" inspection Related to KT-12165 So #KT-18274 Fixed So #KT-17198 Fixed --- .../SimplifiableCallChain.html | 5 + idea/src/META-INF/plugin.xml | 8 ++ .../inspections/collections/FunctionUtils.kt | 35 ++++++ .../SimplifiableCallChainInspection.kt | 117 ++++++++++++++++++ .../collections/SimplifyCallChainFix.kt | 64 ++++++++++ .../UselessCallOnCollectionInspection.kt | 11 +- .../simplifiableCallChain/.inspection | 1 + .../simplifiableCallChain/filterFirst.kt | 4 + .../filterFirst.kt.after | 4 + .../simplifiableCallChain/filterFirstFake.kt | 4 + .../filterFirstFakeReference.kt | 4 + .../filterFirstOrNullReference.kt | 3 + .../filterFirstOrNullReference.kt.after | 3 + .../simplifiableCallChain/filterIsEmpty.kt | 3 + .../filterIsEmpty.kt.after | 3 + .../simplifiableCallChain/filterIsNotEmpty.kt | 3 + .../filterIsNotEmpty.kt.after | 3 + .../filterLastExplicit.kt | 3 + .../filterLastExplicit.kt.after | 3 + .../filterTextIsEmpty.kt | 3 + .../filterTextIsEmpty.kt.after | 3 + .../filterTextSingleOrNull.kt | 3 + .../filterTextSingleOrNull.kt.after | 3 + .../simplifiableCallChain/joinTo.kt | 4 + .../simplifiableCallChain/joinTo.kt.after | 4 + .../simplifiableCallChain/joinToFake.kt | 5 + .../joinToFakeWithLambda.kt | 5 + .../simplifiableCallChain/joinToString.kt | 3 + .../joinToString.kt.after | 3 + .../joinToStringWithReference.kt | 3 + .../joinToStringWithReference.kt.after | 3 + .../joinToStringWithReferenceFake.kt | 4 + .../simplifiableCallChain/mapNotNull.kt | 3 + .../simplifiableCallChain/mapNotNull.kt.after | 3 + .../LocalInspectionTestGenerated.java | 105 ++++++++++++++++ 35 files changed, 428 insertions(+), 10 deletions(-) create mode 100644 idea/resources/inspectionDescriptions/SimplifiableCallChain.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifyCallChainFix.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/.inspection create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirst.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirst.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstFake.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstFakeReference.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstOrNullReference.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstOrNullReference.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsEmpty.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsEmpty.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsNotEmpty.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsNotEmpty.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterLastExplicit.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterLastExplicit.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextIsEmpty.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextIsEmpty.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextSingleOrNull.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextSingleOrNull.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinTo.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinTo.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToFake.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToFakeWithLambda.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToString.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToString.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReference.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReference.kt.after create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReferenceFake.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt create mode 100644 idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt.after diff --git a/idea/resources/inspectionDescriptions/SimplifiableCallChain.html b/idea/resources/inspectionDescriptions/SimplifiableCallChain.html new file mode 100644 index 00000000000..09e019f2619 --- /dev/null +++ b/idea/resources/inspectionDescriptions/SimplifiableCallChain.html @@ -0,0 +1,5 @@ + + +This inspection reports two-call chains replaceable by single call, e.g. map {}.filterNotNull() to mapNotNull {} + + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 427cf1179df..189a5a6b038 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -2266,6 +2266,14 @@ language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt new file mode 100644 index 00000000000..885617cbd53 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt @@ -0,0 +1,35 @@ +/* + * 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 org.jetbrains.kotlin.builtins.getFunctionalClassKind +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getType +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.types.KotlinType + +fun KotlinType.isFunctionOfAnyKind() = constructor.declarationDescriptor?.getFunctionalClassKind() != null + +fun ResolvedCall<*>.hasLastFunctionalParameterWithResult(context: BindingContext, predicate: (KotlinType) -> Boolean): Boolean { + val lastParameter = resultingDescriptor.valueParameters.lastOrNull() ?: return false + val lastArgument = valueArguments[lastParameter]?.arguments?.singleOrNull() ?: return false + val functionalType = lastArgument.getArgumentExpression()?.getType(context) ?: return false + // Both Function & KFunction must pass here + if (!functionalType.isFunctionOfAnyKind()) return false + val resultType = functionalType.arguments.lastOrNull()?.type ?: return false + return predicate(resultType) +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt new file mode 100644 index 00000000000..2a6c0137249 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifiableCallChainInspection.kt @@ -0,0 +1,117 @@ +/* + * 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.codeInspection.ProblemsHolder +import com.intellij.openapi.util.TextRange +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType +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.calls.model.DefaultValueArgument +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull + +class SimplifiableCallChainInspection : AbstractKotlinInspection() { + + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = + object : KtVisitorVoid() { + override fun visitQualifiedExpression(expression: KtQualifiedExpression) { + super.visitQualifiedExpression(expression) + + val firstQualifiedExpression = expression.receiverExpression as? KtQualifiedExpression ?: return + val firstCallExpression = firstQualifiedExpression.selectorExpression as? KtCallExpression ?: return + val secondCallExpression = expression.selectorExpression as? KtCallExpression ?: return + + val actualConversions = conversionGroups[ + firstCallExpression.calleeExpression?.text to secondCallExpression.calleeExpression?.text + ] ?: return + + val context = expression.analyze(BodyResolveMode.PARTIAL) + val firstResolvedCall = firstQualifiedExpression.getResolvedCall(context) ?: return + val conversion = actualConversions.firstOrNull { + firstResolvedCall.resultingDescriptor.fqNameOrNull()?.asString() == it.firstFqName + } ?: return + + val secondResolvedCall = expression.getResolvedCall(context) ?: return + val secondResultingDescriptor = secondResolvedCall.resultingDescriptor + if (secondResultingDescriptor.fqNameOrNull()?.asString() != conversion.secondFqName) return + if (secondResolvedCall.valueArguments.any { (parameter, resolvedArgument) -> + parameter.type.isFunctionOfAnyKind() && + resolvedArgument !is DefaultValueArgument + }) return + + if (conversion.replacement.startsWith("joinTo")) { + // Function parameter in map must have String result type + if (!firstResolvedCall.hasLastFunctionalParameterWithResult(context) { KotlinBuiltIns.isString(it) }) return + } + + val descriptor = holder.manager.createProblemDescriptor( + expression, + TextRange(firstCallExpression.startOffset, firstCallExpression.endOffset).shiftRight(-expression.startOffset), + "Call chain on collection type may be simplified", + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + isOnTheFly, + SimplifyCallChainFix(conversion.replacement) + ) + holder.registerProblem(descriptor) + } + } + + companion object { + + private val conversions = listOf( + Conversion("kotlin.collections.filter", "kotlin.collections.first", "first"), + Conversion("kotlin.collections.filter", "kotlin.collections.firstOrNull", "firstOrNull"), + Conversion("kotlin.collections.filter", "kotlin.collections.last", "last"), + Conversion("kotlin.collections.filter", "kotlin.collections.lastOrNull", "lastOrNull"), + Conversion("kotlin.collections.filter", "kotlin.collections.single", "single"), + Conversion("kotlin.collections.filter", "kotlin.collections.singleOrNull", "singleOrNull"), + Conversion("kotlin.collections.filter", "kotlin.collections.isNotEmpty", "any"), + Conversion("kotlin.collections.filter", "kotlin.collections.List.isEmpty", "none"), + + Conversion("kotlin.text.filter", "kotlin.text.first", "first"), + Conversion("kotlin.text.filter", "kotlin.text.firstOrNull", "firstOrNull"), + Conversion("kotlin.text.filter", "kotlin.text.last", "last"), + Conversion("kotlin.text.filter", "kotlin.text.lastOrNull", "lastOrNull"), + Conversion("kotlin.text.filter", "kotlin.text.single", "single"), + Conversion("kotlin.text.filter", "kotlin.text.singleOrNull", "singleOrNull"), + Conversion("kotlin.text.filter", "kotlin.text.isNotEmpty", "any"), + Conversion("kotlin.text.filter", "kotlin.text.isEmpty", "none"), + + Conversion("kotlin.collections.map", "kotlin.collections.joinTo", "joinTo"), + Conversion("kotlin.collections.map", "kotlin.collections.joinToString", "joinToString"), + Conversion("kotlin.collections.map", "kotlin.collections.filterNotNull", "mapNotNull") + ) + + private val conversionGroups = conversions.groupBy { it.firstName to it.secondName } + + data class Conversion(val firstFqName: String, val secondFqName: String, val replacement: String) { + private fun String.convertToShort() = takeLastWhile { it != '.' } + + val firstName = firstFqName.convertToShort() + + val secondName = secondFqName.convertToShort() + } + + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifyCallChainFix.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifyCallChainFix.kt new file mode 100644 index 00000000000..db29d0ec09f --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/SimplifyCallChainFix.kt @@ -0,0 +1,64 @@ +/* + * 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.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.idea.core.replaced +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.utils.addToStdlib.ifNotEmpty + +class SimplifyCallChainFix(val newName: String) : LocalQuickFix { + override fun getName() = "Merge call chain to '$newName'" + + override fun getFamilyName() = name + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + (descriptor.psiElement as? KtQualifiedExpression)?.let { secondQualifiedExpression -> + val factory = KtPsiFactory(secondQualifiedExpression) + val firstQualifiedExpression = secondQualifiedExpression.receiverExpression as? KtQualifiedExpression ?: return + val operationSign = if (firstQualifiedExpression is KtSafeQualifiedExpression) "?." else "." + val firstCallExpression = firstQualifiedExpression.selectorExpression as? KtCallExpression ?: return + val secondCallExpression = secondQualifiedExpression.selectorExpression as? KtCallExpression ?: return + + val lastArgumentPrefix = if (newName.startsWith("joinTo")) "transform = " else "" + val arguments = secondCallExpression.valueArgumentList?.arguments.orEmpty().map { it.text } + + firstCallExpression.valueArgumentList?.arguments.orEmpty().map { "$lastArgumentPrefix${it.text}"} + val lambdaArgument = firstCallExpression.lambdaArguments.singleOrNull() + + val argumentsText = arguments.ifNotEmpty { joinToString(prefix = "(", postfix = ")") } ?: "" + val newQualifiedExpression = if (lambdaArgument != null) factory.createExpressionByPattern( + "$0$1$2 $3 $4", + firstQualifiedExpression.receiverExpression, + operationSign, + newName, + argumentsText, + lambdaArgument.getLambdaExpression().text + ) + else factory.createExpressionByPattern( + "$0$1$2 $3", + firstQualifiedExpression.receiverExpression, + operationSign, + newName, + argumentsText + ) + + secondQualifiedExpression.replaced(newQualifiedExpression) + } + } +} \ 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 index e0f9e1d761c..6155955dd3b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnCollectionInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/UselessCallOnCollectionInspection.kt @@ -18,9 +18,6 @@ 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 @@ -61,13 +58,7 @@ class UselessCallOnCollectionInspection : AbstractUselessCallInspection() { 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 + if (!resolvedCall.hasLastFunctionalParameterWithResult(context) { !TypeUtils.isNullableType(it) }) return } } diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/.inspection b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/.inspection new file mode 100644 index 00000000000..b980466f40c --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.collections.SimplifiableCallChainInspection \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirst.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirst.kt new file mode 100644 index 00000000000..ced1c738267 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirst.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val nullableList: List? = listOf("1", "") +val x = nullableList?.filter { it.isNotEmpty() }?.first() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirst.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirst.kt.after new file mode 100644 index 00000000000..662755aff63 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirst.kt.after @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val nullableList: List? = listOf("1", "") +val x = nullableList?.first { it.isNotEmpty() } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstFake.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstFake.kt new file mode 100644 index 00000000000..ce02728a413 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstFake.kt @@ -0,0 +1,4 @@ +// PROBLEM: none +// WITH_RUNTIME + +val x = listOf("1", "").filter { it.isNotEmpty() }.first { it[0] == 'A' } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstFakeReference.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstFakeReference.kt new file mode 100644 index 00000000000..7d85716ebd8 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstFakeReference.kt @@ -0,0 +1,4 @@ +// PROBLEM: none +// WITH_RUNTIME + +val x = listOf('1', 'a', 0.toChar()).filter { it.toInt() != 0 }.first(Char::isLetter) \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstOrNullReference.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstOrNullReference.kt new file mode 100644 index 00000000000..fda1c2161a2 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstOrNullReference.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf("1", "").filter(String::isNotEmpty).firstOrNull() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstOrNullReference.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstOrNullReference.kt.after new file mode 100644 index 00000000000..5a8a9e6d250 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstOrNullReference.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf("1", "").firstOrNull(String::isNotEmpty) \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsEmpty.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsEmpty.kt new file mode 100644 index 00000000000..4c2a249d736 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsEmpty.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf(1, 2, 3).filter { it % 2 != 0 }.isEmpty() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsEmpty.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsEmpty.kt.after new file mode 100644 index 00000000000..62034587209 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsEmpty.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf(1, 2, 3).none { it % 2 != 0 } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsNotEmpty.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsNotEmpty.kt new file mode 100644 index 00000000000..8d096627a7d --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsNotEmpty.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf(1, 2, 3).filter { it % 2 != 0 }.isNotEmpty() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsNotEmpty.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsNotEmpty.kt.after new file mode 100644 index 00000000000..3b8f7ac7f6f --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsNotEmpty.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf(1, 2, 3).any { it % 2 != 0 } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterLastExplicit.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterLastExplicit.kt new file mode 100644 index 00000000000..43ad7e5fb7d --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterLastExplicit.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf("1", "").filter { element -> element.isNotEmpty() }.last() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterLastExplicit.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterLastExplicit.kt.after new file mode 100644 index 00000000000..81d3e4481e8 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterLastExplicit.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf("1", "").last { element -> element.isNotEmpty() } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextIsEmpty.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextIsEmpty.kt new file mode 100644 index 00000000000..9c54460eb45 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextIsEmpty.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = "abcdef".filter { it.isDigit() }.isEmpty() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextIsEmpty.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextIsEmpty.kt.after new file mode 100644 index 00000000000..b5b5890d4bd --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextIsEmpty.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = "abcdef".none { it.isDigit() } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextSingleOrNull.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextSingleOrNull.kt new file mode 100644 index 00000000000..caea5be3bba --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextSingleOrNull.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = "5abc".filter { it.isDigit() }.singleOrNull() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextSingleOrNull.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextSingleOrNull.kt.after new file mode 100644 index 00000000000..16da3c09766 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextSingleOrNull.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = "5abc".singleOrNull { it.isDigit() } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinTo.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinTo.kt new file mode 100644 index 00000000000..9939c2b031d --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinTo.kt @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val sb = StringBuilder() +val x = listOf(1, 2, 3).map { "$it*$it" }.joinTo(buffer = sb, prefix = "= ", separator = " + ") \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinTo.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinTo.kt.after new file mode 100644 index 00000000000..2c698b4bf4d --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinTo.kt.after @@ -0,0 +1,4 @@ +// WITH_RUNTIME + +val sb = StringBuilder() +val x = listOf(1, 2, 3).joinTo(buffer = sb, prefix = "= ", separator = " + ") { "$it*$it" } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToFake.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToFake.kt new file mode 100644 index 00000000000..2c57550a3eb --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToFake.kt @@ -0,0 +1,5 @@ +// PROBLEM: none +// WITH_RUNTIME + +val sb = StringBuilder() +val x = listOf(1, 2, 3).map { "$it*$it" }.joinTo(buffer = sb, prefix = "= ", separator = " + ", transform = String::capitalize) \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToFakeWithLambda.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToFakeWithLambda.kt new file mode 100644 index 00000000000..72e22d3a41e --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToFakeWithLambda.kt @@ -0,0 +1,5 @@ +// PROBLEM: none +// WITH_RUNTIME + +val sb = StringBuilder() +val x = listOf(1, 2, 3).map { "$it*$it" }.joinTo(buffer = sb, prefix = "= ", separator = " + ") { "${it.substring(1)}"} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToString.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToString.kt new file mode 100644 index 00000000000..e97fabddcd3 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToString.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf(1, 2, 3).map { "$it*$it" }.joinToString(prefix = "= ", separator = " + ") \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToString.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToString.kt.after new file mode 100644 index 00000000000..2f97dd7ba5b --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToString.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf(1, 2, 3).joinToString(prefix = "= ", separator = " + ") { "$it*$it" } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReference.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReference.kt new file mode 100644 index 00000000000..84a8b81b215 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReference.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf(1, 2, 3).map(Int::toString).joinToString(prefix = "= ", separator = " + ") \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReference.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReference.kt.after new file mode 100644 index 00000000000..cdeb86c64f2 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReference.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf(1, 2, 3).joinToString(prefix = "= ", separator = " + ", transform = Int::toString) \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReferenceFake.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReferenceFake.kt new file mode 100644 index 00000000000..4972dcc3cbc --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReferenceFake.kt @@ -0,0 +1,4 @@ +// PROBLEM: none +// WITH_RUNTIME + +val x = listOf(1, 2, 3).map(Int::toDouble).joinToString(prefix = "= ", separator = " + ") \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt new file mode 100644 index 00000000000..4e06e43850a --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf(1, 0, 2).map { if (it != 0) it else null }.filterNotNull() \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt.after b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt.after new file mode 100644 index 00000000000..ef63f8229b0 --- /dev/null +++ b/idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf(1, 0, 2).mapNotNull { if (it != 0) it else null } \ 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 502022c3d0a..ba59914e14d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -59,6 +59,111 @@ 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/simplifiableCallChain") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SimplifiableCallChain extends AbstractLocalInspectionTest { + public void testAllFilesPresentInSimplifiableCallChain() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/collections/simplifiableCallChain"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("filterFirst.kt") + public void testFilterFirst() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirst.kt"); + doTest(fileName); + } + + @TestMetadata("filterFirstFake.kt") + public void testFilterFirstFake() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstFake.kt"); + doTest(fileName); + } + + @TestMetadata("filterFirstFakeReference.kt") + public void testFilterFirstFakeReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstFakeReference.kt"); + doTest(fileName); + } + + @TestMetadata("filterFirstOrNullReference.kt") + public void testFilterFirstOrNullReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterFirstOrNullReference.kt"); + doTest(fileName); + } + + @TestMetadata("filterIsEmpty.kt") + public void testFilterIsEmpty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsEmpty.kt"); + doTest(fileName); + } + + @TestMetadata("filterIsNotEmpty.kt") + public void testFilterIsNotEmpty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterIsNotEmpty.kt"); + doTest(fileName); + } + + @TestMetadata("filterLastExplicit.kt") + public void testFilterLastExplicit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterLastExplicit.kt"); + doTest(fileName); + } + + @TestMetadata("filterTextIsEmpty.kt") + public void testFilterTextIsEmpty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextIsEmpty.kt"); + doTest(fileName); + } + + @TestMetadata("filterTextSingleOrNull.kt") + public void testFilterTextSingleOrNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/filterTextSingleOrNull.kt"); + doTest(fileName); + } + + @TestMetadata("joinTo.kt") + public void testJoinTo() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinTo.kt"); + doTest(fileName); + } + + @TestMetadata("joinToFake.kt") + public void testJoinToFake() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToFake.kt"); + doTest(fileName); + } + + @TestMetadata("joinToFakeWithLambda.kt") + public void testJoinToFakeWithLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToFakeWithLambda.kt"); + doTest(fileName); + } + + @TestMetadata("joinToString.kt") + public void testJoinToString() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToString.kt"); + doTest(fileName); + } + + @TestMetadata("joinToStringWithReference.kt") + public void testJoinToStringWithReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReference.kt"); + doTest(fileName); + } + + @TestMetadata("joinToStringWithReferenceFake.kt") + public void testJoinToStringWithReferenceFake() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/joinToStringWithReferenceFake.kt"); + doTest(fileName); + } + + @TestMetadata("mapNotNull.kt") + public void testMapNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/collections/simplifiableCallChain/mapNotNull.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/inspectionsLocal/collections/uselessCallOnCollection") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)