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? {
|
fun KtExpressionWithLabel.targetLoop(): KtLoopExpression? {
|
||||||
val label = getTargetLabel()
|
val label = getTargetLabel()
|
||||||
if (label == null) {
|
if (label == null) {
|
||||||
|
|||||||
@@ -60,9 +60,6 @@ interface Transformation {
|
|||||||
|
|
||||||
val chainCallCount: Int
|
val chainCallCount: Int
|
||||||
get() = 1
|
get() = 1
|
||||||
|
|
||||||
val shouldUseInputVariable: Boolean
|
|
||||||
get() = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,6 +119,12 @@ interface TransformationMatcher {
|
|||||||
*/
|
*/
|
||||||
val indexVariableAllowed: Boolean
|
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.
|
* 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.
|
* 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@
|
MatchersLoop@
|
||||||
for (matcher in MatcherRegistrar.matchers) {
|
for (matcher in MatcherRegistrar.matchers) {
|
||||||
if (state.indexVariable != null && !matcher.indexVariableAllowed) continue
|
if (state.indexVariable != null && !matcher.indexVariableAllowed) continue
|
||||||
|
if (matcher.shouldUseInputVariables && !inputVariableUsed && state.indexVariable == null) continue
|
||||||
|
|
||||||
val match = matcher.match(state)
|
val match = matcher.match(state)
|
||||||
if (match != null) {
|
if (match != null) {
|
||||||
if (!inputVariableUsed && match.allTransformations.any { it.shouldUseInputVariable }) return null
|
|
||||||
|
|
||||||
when (match) {
|
when (match) {
|
||||||
is TransformationMatch.Sequence -> {
|
is TransformationMatch.Sequence -> {
|
||||||
// check that old input variable is not needed anymore
|
// check that old input variable is not needed anymore
|
||||||
var newState = match.newState
|
var newState = match.newState
|
||||||
if (state.inputVariable != newState.inputVariable && state.inputVariable.hasUsages(newState.statements)) return null
|
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 }) {
|
if (state.indexVariable != null && match.sequenceTransformations.any { it.affectsIndex }) {
|
||||||
// index variable is still needed but index in the new sequence is different
|
// index variable is still needed but index in the new sequence is different
|
||||||
if (state.indexVariable!!.hasUsages(newState.statements)) return null
|
if (state.indexVariable!!.hasUsages(newState.statements)) return null
|
||||||
|
|||||||
+3
-3
@@ -40,9 +40,6 @@ class CountTransformation(
|
|||||||
override val presentation: String
|
override val presentation: String
|
||||||
get() = "count" + (if (filter != null) "{}" else "()")
|
get() = "count" + (if (filter != null) "{}" else "()")
|
||||||
|
|
||||||
override val shouldUseInputVariable: Boolean
|
|
||||||
get() = false
|
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val call = if (filter != null) {
|
val call = if (filter != null) {
|
||||||
val lambda = generateLambda(inputVariable, filter)
|
val lambda = generateLambda(inputVariable, filter)
|
||||||
@@ -72,6 +69,9 @@ class CountTransformation(
|
|||||||
override val indexVariableAllowed: Boolean
|
override val indexVariableAllowed: Boolean
|
||||||
get() = false
|
get() = false
|
||||||
|
|
||||||
|
override val shouldUseInputVariables: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
override fun match(state: MatchingState): TransformationMatch.Result? {
|
override fun match(state: MatchingState): TransformationMatch.Result? {
|
||||||
val operand = state.statements.singleOrNull()?.isPlusPlusOf() ?: return null
|
val operand = state.statements.singleOrNull()?.isPlusPlusOf() ?: return null
|
||||||
val initialization = operand.isVariableInitializedBeforeLoop(state.outerLoop, checkNoOtherUsagesInLoop = true) ?: 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
|
override val indexVariableAllowed: Boolean
|
||||||
get() = false
|
get() = false
|
||||||
|
|
||||||
|
override val shouldUseInputVariables: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
override fun match(state: MatchingState): TransformationMatch.Result? {
|
override fun match(state: MatchingState): TransformationMatch.Result? {
|
||||||
return matchWithFilterBefore(state, null)
|
return matchWithFilterBefore(state, null)
|
||||||
}
|
}
|
||||||
@@ -128,9 +131,6 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
override val chainCallCount: Int
|
override val chainCallCount: Int
|
||||||
get() = generator.chainCallCount
|
get() = generator.chainCallCount
|
||||||
|
|
||||||
override val shouldUseInputVariable: Boolean
|
|
||||||
get() = false
|
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
return generator.generate(chainedCallGenerator)
|
return generator.generate(chainedCallGenerator)
|
||||||
}
|
}
|
||||||
@@ -157,9 +157,6 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
override val chainCallCount: Int
|
override val chainCallCount: Int
|
||||||
get() = generator.chainCallCount
|
get() = generator.chainCallCount
|
||||||
|
|
||||||
override val shouldUseInputVariable: Boolean
|
|
||||||
get() = false
|
|
||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
return generator.generate(chainedCallGenerator)
|
return generator.generate(chainedCallGenerator)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -37,6 +37,9 @@ object IntroduceIndexMatcher : TransformationMatcher {
|
|||||||
override val indexVariableAllowed: Boolean
|
override val indexVariableAllowed: Boolean
|
||||||
get() = false // old index variable is still needed - cannot introduce another one
|
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? {
|
override fun match(state: MatchingState): TransformationMatch.Sequence? {
|
||||||
for (statement in state.statements) {
|
for (statement in state.statements) {
|
||||||
val unaryExpressions = statement.collectDescendantsOfType<KtUnaryExpression>(
|
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);
|
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")
|
@TestMetadata("filter_mergeMultiple.kt")
|
||||||
public void testFilter_mergeMultiple() throws Exception {
|
public void testFilter_mergeMultiple() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter_mergeMultiple.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter_mergeMultiple.kt");
|
||||||
|
|||||||
@@ -7591,6 +7591,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("filter_mergeMultiple.kt")
|
||||||
public void testFilter_mergeMultiple() throws Exception {
|
public void testFilter_mergeMultiple() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter_mergeMultiple.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter_mergeMultiple.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user