From 834fe9d5359c60265cfba49771a415a6cdb8971f Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Fri, 22 Apr 2016 22:11:19 +0300 Subject: [PATCH] Supported contains() --- .../result/FindTransformationMatcher.kt | 60 +++++++++++++++---- .../intentions/loopToCallChain/contains.kt | 10 ++++ .../loopToCallChain/contains.kt.after | 5 ++ .../intentions/loopToCallChain/contains2.kt | 11 ++++ .../loopToCallChain/contains2.kt.after | 5 ++ .../intentions/loopToCallChain/contains3.kt | 10 ++++ .../loopToCallChain/contains3.kt.after | 5 ++ .../intentions/loopToCallChain/contains4.kt | 10 ++++ .../loopToCallChain/contains4.kt.after | 5 ++ 9 files changed, 108 insertions(+), 13 deletions(-) create mode 100644 idea/testData/intentions/loopToCallChain/contains.kt create mode 100644 idea/testData/intentions/loopToCallChain/contains.kt.after create mode 100644 idea/testData/intentions/loopToCallChain/contains2.kt create mode 100644 idea/testData/intentions/loopToCallChain/contains2.kt.after create mode 100644 idea/testData/intentions/loopToCallChain/contains3.kt create mode 100644 idea/testData/intentions/loopToCallChain/contains3.kt.after create mode 100644 idea/testData/intentions/loopToCallChain/contains4.kt create mode 100644 idea/testData/intentions/loopToCallChain/contains4.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindTransformationMatcher.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindTransformationMatcher.kt index 4bfa1a789e6..0b736b18067 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindTransformationMatcher.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindTransformationMatcher.kt @@ -21,11 +21,13 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression import org.jetbrains.kotlin.idea.intentions.loopToCallChain.* import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FilterTransformation +import org.jetbrains.kotlin.idea.intentions.negate import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange import org.jetbrains.kotlin.types.typeUtil.TypeNullability import org.jetbrains.kotlin.types.typeUtil.nullability +import org.jetbrains.kotlin.utils.addToStdlib.check /** * Matches: @@ -237,15 +239,45 @@ object FindTransformationMatcher : TransformationMatcher { } } + fun buildGeneratorForAny(negated: Boolean): FindOperationGenerator { + if (filterCondition != null) { + val containsArgument = filterCondition.isFilterForContainsOperation(inputVariable, filterTransformation!!.loop) + if (containsArgument != null) { + return object: FindOperationGenerator { + override val functionName: String + get() = "contains" + + override val hasFilter: Boolean + get() = false + + override val shouldUseInputVariable: Boolean + get() = false + + override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression { + val expression = chainedCallGenerator.generate("contains($0)", containsArgument) + return if (negated) expression.negate() else expression + } + } + } + } + + if (negated) { + return SimpleGenerator("none", shouldUseInputVariable = false) + } + else { + return SimpleGenerator("any", shouldUseInputVariable = false) + } + } + when { valueIfFound.isVariableReference(inputVariable) -> { val generator = SimpleGenerator(if (findFirst) "firstOrNull" else "lastOrNull", shouldUseInputVariable = true) return generator.useElvisOperatorIfNeeded() } - valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> return SimpleGenerator("any", shouldUseInputVariable = false) + valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> return buildGeneratorForAny(negated = false) - valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> return SimpleGenerator("none", shouldUseInputVariable = false) + valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> return buildGeneratorForAny(negated = true) inputVariable.hasUsages(valueIfFound) -> { if (!findFirst) return null // too dangerous because of side effects @@ -302,18 +334,10 @@ object FindTransformationMatcher : TransformationMatcher { } else -> { - return object: FindOperationGenerator { - override val functionName: String - get() = "any" - - override val hasFilter: Boolean - get() = filterCondition != null - - override val shouldUseInputVariable: Boolean - get() = false - + val generatorForAny = buildGeneratorForAny(negated = false) + return object: FindOperationGenerator by generatorForAny { override fun generate(chainedCallGenerator: ChainedCallGenerator): KtExpression { - val chainedCall = generateChainedCall(functionName, chainedCallGenerator, filterCondition) + val chainedCall = generatorForAny.generate(chainedCallGenerator) return KtPsiFactory(chainedCall).createExpressionByPattern("if ($0) $1 else $2", chainedCall, valueIfFound, valueIfNotFound) } } @@ -321,4 +345,14 @@ object FindTransformationMatcher : TransformationMatcher { } } } + + private fun KtExpression.isFilterForContainsOperation(inputVariable: KtCallableDeclaration, loop: KtForExpression): KtExpression? { + if (this !is KtBinaryExpression) return null + if (operationToken != KtTokens.EQEQ) return null + return when { + left.isVariableReference(inputVariable) -> right?.check { it.isStableInLoop(loop, false) } + right.isVariableReference(inputVariable) -> left?.check { it.isStableInLoop(loop, false) } + else -> null + } + } } diff --git a/idea/testData/intentions/loopToCallChain/contains.kt b/idea/testData/intentions/loopToCallChain/contains.kt new file mode 100644 index 00000000000..37b0af5fbfc --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/contains.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'contains()'" +fun foo(list: List): Boolean { + for (s in list) { + if (s == "a") { + return true + } + } + return false +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/contains.kt.after b/idea/testData/intentions/loopToCallChain/contains.kt.after new file mode 100644 index 00000000000..94cf93a1eca --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/contains.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'contains()'" +fun foo(list: List): Boolean { + return list.contains("a") +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/contains2.kt b/idea/testData/intentions/loopToCallChain/contains2.kt new file mode 100644 index 00000000000..bed3e2c7d08 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/contains2.kt @@ -0,0 +1,11 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'contains()'" +fun foo(list: List) { + var v = true + for (s in list) { + if (s == "a") { + v = false + break + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/contains2.kt.after b/idea/testData/intentions/loopToCallChain/contains2.kt.after new file mode 100644 index 00000000000..0f4611dd6c4 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/contains2.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'contains()'" +fun foo(list: List) { + val v = !list.contains("a") +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/contains3.kt b/idea/testData/intentions/loopToCallChain/contains3.kt new file mode 100644 index 00000000000..babb299c143 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/contains3.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'contains()'" +fun foo(list: List) { + var v = false + for (s in list) { + if (s == "a") { + v = true + } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/contains3.kt.after b/idea/testData/intentions/loopToCallChain/contains3.kt.after new file mode 100644 index 00000000000..c9b4777ee52 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/contains3.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'contains()'" +fun foo(list: List) { + val v = list.contains("a") +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/contains4.kt b/idea/testData/intentions/loopToCallChain/contains4.kt new file mode 100644 index 00000000000..1f3d2c6dc58 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/contains4.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'contains()'" +fun foo(list: List): Int { + for (s in list) { + if (s == "a") { + return 1 + } + } + return 0 +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/contains4.kt.after b/idea/testData/intentions/loopToCallChain/contains4.kt.after new file mode 100644 index 00000000000..e2017a7bed1 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/contains4.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'contains()'" +fun foo(list: List): Int { + return if (list.contains("a")) 1 else 0 +} \ No newline at end of file