Moved shouldUseInputVariable from Transformation to TransformationMatcher and changed its semantics a bit

This commit is contained in:
Valentin Kipyatkov
2016-08-12 17:14:37 +03:00
parent a5c7d4f1fd
commit e32724330f
9 changed files with 51 additions and 14 deletions
@@ -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
@@ -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
@@ -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)
}
@@ -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");