Moved shouldUseInputVariable from Transformation to TransformationMatcher and changed its semantics a bit
This commit is contained in:
@@ -116,6 +116,10 @@ fun KtVariableDeclaration.hasWriteUsages(inElement: KtElement): Boolean {
|
||||
}
|
||||
}
|
||||
|
||||
fun KtCallableDeclaration.hasDifferentSetsOfUsages(elements1: Collection<KtElement>, elements2: Collection<KtElement>): Boolean {
|
||||
return countUsages(elements1 - elements2) != countUsages(elements2 - elements1)
|
||||
}
|
||||
|
||||
fun KtExpressionWithLabel.targetLoop(): KtLoopExpression? {
|
||||
val label = getTargetLabel()
|
||||
if (label == null) {
|
||||
|
||||
@@ -60,9 +60,6 @@ interface Transformation {
|
||||
|
||||
val chainCallCount: Int
|
||||
get() = 1
|
||||
|
||||
val shouldUseInputVariable: Boolean
|
||||
get() = true
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,6 +119,12 @@ interface TransformationMatcher {
|
||||
*/
|
||||
val indexVariableAllowed: Boolean
|
||||
|
||||
/**
|
||||
* Override with false value if the result of the match should be rejected if the matched part uses neither the input variable nor the index variable.
|
||||
*/
|
||||
val shouldUseInputVariables: Boolean
|
||||
get() = true
|
||||
|
||||
/**
|
||||
* Implementors should return true if they match some constructs with expression-embedded break or continue.
|
||||
* In this case they are obliged to deal with them and filter out invalid cases.
|
||||
|
||||
@@ -87,17 +87,23 @@ fun match(loop: KtForExpression, useLazySequence: Boolean): MatchResult? {
|
||||
MatchersLoop@
|
||||
for (matcher in MatcherRegistrar.matchers) {
|
||||
if (state.indexVariable != null && !matcher.indexVariableAllowed) continue
|
||||
if (matcher.shouldUseInputVariables && !inputVariableUsed && state.indexVariable == null) continue
|
||||
|
||||
val match = matcher.match(state)
|
||||
if (match != null) {
|
||||
if (!inputVariableUsed && match.allTransformations.any { it.shouldUseInputVariable }) return null
|
||||
|
||||
when (match) {
|
||||
is TransformationMatch.Sequence -> {
|
||||
// check that old input variable is not needed anymore
|
||||
var newState = match.newState
|
||||
if (state.inputVariable != newState.inputVariable && state.inputVariable.hasUsages(newState.statements)) return null
|
||||
|
||||
if (matcher.shouldUseInputVariables
|
||||
&& !state.inputVariable.hasDifferentSetsOfUsages(state.statements, newState.statements)
|
||||
&& !(state.indexVariable?.hasDifferentSetsOfUsages(state.statements, newState.statements) ?: false)) {
|
||||
// matched part of the loop uses neither input variable nor index variable
|
||||
continue@MatchersLoop
|
||||
}
|
||||
|
||||
if (state.indexVariable != null && match.sequenceTransformations.any { it.affectsIndex }) {
|
||||
// index variable is still needed but index in the new sequence is different
|
||||
if (state.indexVariable!!.hasUsages(newState.statements)) return null
|
||||
|
||||
+3
-3
@@ -40,9 +40,6 @@ 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)
|
||||
@@ -72,6 +69,9 @@ class CountTransformation(
|
||||
override val indexVariableAllowed: Boolean
|
||||
get() = false
|
||||
|
||||
override val shouldUseInputVariables: Boolean
|
||||
get() = false
|
||||
|
||||
override fun match(state: MatchingState): TransformationMatch.Result? {
|
||||
val operand = state.statements.singleOrNull()?.isPlusPlusOf() ?: return null
|
||||
val initialization = operand.isVariableInitializedBeforeLoop(state.outerLoop, checkNoOtherUsagesInLoop = true) ?: return null
|
||||
|
||||
+3
-6
@@ -54,6 +54,9 @@ object FindTransformationMatcher : TransformationMatcher {
|
||||
override val indexVariableAllowed: Boolean
|
||||
get() = false
|
||||
|
||||
override val shouldUseInputVariables: Boolean
|
||||
get() = false
|
||||
|
||||
override fun match(state: MatchingState): TransformationMatch.Result? {
|
||||
return matchWithFilterBefore(state, null)
|
||||
}
|
||||
@@ -128,9 +131,6 @@ object FindTransformationMatcher : TransformationMatcher {
|
||||
override val chainCallCount: Int
|
||||
get() = generator.chainCallCount
|
||||
|
||||
override val shouldUseInputVariable: Boolean
|
||||
get() = false
|
||||
|
||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||
return generator.generate(chainedCallGenerator)
|
||||
}
|
||||
@@ -157,9 +157,6 @@ object FindTransformationMatcher : TransformationMatcher {
|
||||
override val chainCallCount: Int
|
||||
get() = generator.chainCallCount
|
||||
|
||||
override val shouldUseInputVariable: Boolean
|
||||
get() = false
|
||||
|
||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||
return generator.generate(chainedCallGenerator)
|
||||
}
|
||||
|
||||
+3
@@ -37,6 +37,9 @@ object IntroduceIndexMatcher : TransformationMatcher {
|
||||
override val indexVariableAllowed: Boolean
|
||||
get() = false // old index variable is still needed - cannot introduce another one
|
||||
|
||||
override val shouldUseInputVariables: Boolean
|
||||
get() = false
|
||||
|
||||
override fun match(state: MatchingState): TransformationMatch.Sequence? {
|
||||
for (statement in state.statements) {
|
||||
val unaryExpressions = statement.collectDescendantsOfType<KtUnaryExpression>(
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
// IS_APPLICABLE: false
|
||||
// IS_APPLICABLE_2: false
|
||||
import java.util.*
|
||||
|
||||
fun foo(list: List<String>) {
|
||||
val random = Random()
|
||||
<caret>for (s in list) {
|
||||
if (random.nextBoolean()) continue
|
||||
print(s)
|
||||
}
|
||||
}
|
||||
@@ -473,6 +473,12 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("filter_inputVariableNotUsed.kt")
|
||||
public void testFilter_inputVariableNotUsed() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter_inputVariableNotUsed.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("filter_mergeMultiple.kt")
|
||||
public void testFilter_mergeMultiple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter_mergeMultiple.kt");
|
||||
|
||||
@@ -7591,6 +7591,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("filter_inputVariableNotUsed.kt")
|
||||
public void testFilter_inputVariableNotUsed() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter_inputVariableNotUsed.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("filter_mergeMultiple.kt")
|
||||
public void testFilter_mergeMultiple() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter_mergeMultiple.kt");
|
||||
|
||||
Reference in New Issue
Block a user