diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/interfaces.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/interfaces.kt index a0afcd1dc80..f43e3d9bc48 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/interfaces.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/interfaces.kt @@ -51,9 +51,11 @@ interface Transformation { fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression - val chainCallCount: Int get() = 1 + + val shouldUseInputVariable: Boolean + get() = true } /** diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/matchAndConvert.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/matchAndConvert.kt index a8763ed19e1..ac3e9ad1cf3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/matchAndConvert.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/matchAndConvert.kt @@ -71,9 +71,17 @@ fun match(loop: KtForExpression): ResultTransformationMatch? { state = state.copy(statements = block.statements) } + val inputVariableUsed = state.inputVariable.hasUsages(state.statements) + for (matcher in MatcherRegistrar.resultMatchers) { val match = matcher.match(state) if (match != null) { + if (!inputVariableUsed + && (match.sequenceTransformations.any { it.shouldUseInputVariable } + || match.resultTransformation.shouldUseInputVariable)) { + return null + } + sequenceTransformations.addAll(match.sequenceTransformations) return ResultTransformationMatch(match.resultTransformation, sequenceTransformations) .let { mergeTransformations(it) } @@ -84,6 +92,10 @@ fun match(loop: KtForExpression): ResultTransformationMatch? { for (matcher in MatcherRegistrar.sequenceMatchers) { val match = matcher.match(state) if (match != null) { + if (!inputVariableUsed && match.transformations.any { it.shouldUseInputVariable }) { + return null + } + val newState = match.newState // check that old input variable is not needed anymore if (state.inputVariable != newState.inputVariable && state.inputVariable.hasUsages(newState.statements)) return null diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/CountTransformation.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/CountTransformation.kt index 27b2d015775..8fade26a3d7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/CountTransformation.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/CountTransformation.kt @@ -42,6 +42,9 @@ class CountTransformation( override val presentation: String get() = "count" + (if (filter != null) "{}" else "()") + override val shouldUseInputVariable: Boolean + get() = false + override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression { val call = if (filter != null) { val lambda = generateLambda(inputVariable, filter) diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndAssignTransformation.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndAssignTransformation.kt index bc1cfa4f235..66440e977b2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndAssignTransformation.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndAssignTransformation.kt @@ -46,6 +46,9 @@ class FindAndAssignTransformation( override val chainCallCount: Int get() = generator.chainCallCount + override val shouldUseInputVariable: Boolean + get() = generator.shouldUseInputVariable + override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression { return generator.generate(chainedCallGenerator, filter) } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndReturnTransformation.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndReturnTransformation.kt index cc403ff8c75..079fec15ee5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndReturnTransformation.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/FindAndReturnTransformation.kt @@ -50,6 +50,9 @@ class FindAndReturnTransformation( override val chainCallCount: Int get() = generator.chainCallCount + override val shouldUseInputVariable: Boolean + get() = generator.shouldUseInputVariable + override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression { return generator.generate(chainedCallGenerator, filter) } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/utils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/utils.kt index 5365cf8a6cc..440f3b47351 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/utils.kt @@ -115,6 +115,8 @@ fun KtProperty.hasWriteUsages(): Boolean { interface FindOperatorGenerator { val functionName: String + val shouldUseInputVariable: Boolean + fun generate(chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?): KtExpression val chainCallCount: Int @@ -140,7 +142,7 @@ fun buildFindOperationGenerator( } } - class SimpleGenerator(override val functionName: String) : FindOperatorGenerator { + class SimpleGenerator(override val functionName: String, override val shouldUseInputVariable: Boolean) : FindOperatorGenerator { override fun generate(chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?): KtExpression { return generateChainedCall(functionName, chainedCallGenerator, filter) } @@ -164,13 +166,13 @@ fun buildFindOperationGenerator( when { valueIfFound.isVariableReference(inputVariable) -> { - val generator = SimpleGenerator(if (findFirst) "firstOrNull" else "lastOrNull") + val generator = SimpleGenerator(if (findFirst) "firstOrNull" else "lastOrNull", shouldUseInputVariable = true) return generator.useElvisOperatorIfNeeded() } - valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> return SimpleGenerator("any") + valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> return SimpleGenerator("any", shouldUseInputVariable = false) - valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> return SimpleGenerator("none") + valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> return SimpleGenerator("none", shouldUseInputVariable = false) inputVariable.hasUsages(valueIfFound) -> { if (!findFirst) return null // too dangerous because of side effects @@ -185,6 +187,9 @@ fun buildFindOperationGenerator( override val functionName: String get() = "firstOrNull" + override val shouldUseInputVariable: Boolean + get() = true + override val chainCallCount: Int get() = 2 @@ -203,6 +208,9 @@ fun buildFindOperationGenerator( override val functionName: String get() = "firstOrNull" + override val shouldUseInputVariable: Boolean + get() = true + override val chainCallCount: Int get() = 2 // also includes "let" @@ -219,6 +227,9 @@ fun buildFindOperationGenerator( override val functionName: String get() = "any" + override val shouldUseInputVariable: Boolean + get() = false + override fun generate(chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?): KtExpression { val chainedCall = generateChainedCall(functionName, chainedCallGenerator, filter) return KtPsiFactory(chainedCall).createExpressionByPattern("if ($0) $1 else $2", chainedCall, valueIfFound, valueIfNotFound) diff --git a/idea/testData/intentions/loopToCallChain/filter_inputVarNotUsed.kt b/idea/testData/intentions/loopToCallChain/filter_inputVarNotUsed.kt new file mode 100644 index 00000000000..58c0773c7f5 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter_inputVarNotUsed.kt @@ -0,0 +1,13 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false +import java.util.* + +fun foo(list: List): String? { + val random = Random() + for (s in list) { + if (random.nextBoolean()) { + return s + } + } + return null +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/mapTo_inputVarNotUsed.kt b/idea/testData/intentions/loopToCallChain/mapTo_inputVarNotUsed.kt new file mode 100644 index 00000000000..c84b3d481a4 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/mapTo_inputVarNotUsed.kt @@ -0,0 +1,8 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false +fun foo(list: List, target: MutableList) { + for (s in list) { + if (s.length > 0) + target.add(0) + } +} \ No newline at end of file