From b28d01670923eda4bf87c850704fd2e95d70a5ac Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Fri, 14 Oct 2016 16:47:39 +0300 Subject: [PATCH] count() to merge with filterNotNull and filterIsInstance + changed test for KT-14191 to use sum() --- .../result/CountTransformation.kt | 11 ++-- .../result/FindTransformationMatcher.kt | 2 +- .../sequence/FilterTransformation.kt | 55 +++++++++++++------ .../intentions/loopToCallChain/KT14191.kt | 12 ++++ .../loopToCallChain/KT14191.kt.after | 9 +++ .../loopToCallChain/KT14191.kt.after2 | 10 ++++ .../loopToCallChain/count/KT14191.kt | 12 ---- .../loopToCallChain/count/KT14191.kt.after | 9 --- .../loopToCallChain/count/KT14191.kt.after2 | 10 ---- .../loopToCallChain/count/countIsInstance.kt | 12 ++++ .../count/countIsInstance.kt.after | 7 +++ .../loopToCallChain/count/countNotNull.kt | 12 ++++ .../count/countNotNull.kt.after | 7 +++ .../count/countSomethingAndNotNull.kt | 14 +++++ .../count/countSomethingAndNotNull.kt.after | 7 +++ .../intentions/IntentionTest2Generated.java | 24 +++++++- .../intentions/IntentionTestGenerated.java | 24 +++++++- 17 files changed, 177 insertions(+), 60 deletions(-) create mode 100644 idea/testData/intentions/loopToCallChain/KT14191.kt create mode 100644 idea/testData/intentions/loopToCallChain/KT14191.kt.after create mode 100644 idea/testData/intentions/loopToCallChain/KT14191.kt.after2 delete mode 100644 idea/testData/intentions/loopToCallChain/count/KT14191.kt delete mode 100644 idea/testData/intentions/loopToCallChain/count/KT14191.kt.after delete mode 100644 idea/testData/intentions/loopToCallChain/count/KT14191.kt.after2 create mode 100644 idea/testData/intentions/loopToCallChain/count/countIsInstance.kt create mode 100644 idea/testData/intentions/loopToCallChain/count/countIsInstance.kt.after create mode 100644 idea/testData/intentions/loopToCallChain/count/countNotNull.kt create mode 100644 idea/testData/intentions/loopToCallChain/count/countNotNull.kt.after create mode 100644 idea/testData/intentions/loopToCallChain/count/countSomethingAndNotNull.kt create mode 100644 idea/testData/intentions/loopToCallChain/count/countSomethingAndNotNull.kt.after 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 c139a71d1ec..decbf495207 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 @@ -20,7 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.intentions.loopToCallChain.* -import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FilterTransformation +import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FilterTransformationBase import org.jetbrains.kotlin.psi.* class CountTransformation( @@ -31,10 +31,13 @@ class CountTransformation( ) : AssignToVariableResultTransformation(loop, initialization) { override fun mergeWithPrevious(previousTransformation: SequenceTransformation): ResultTransformation? { - if (previousTransformation !is FilterTransformation) return null + if (previousTransformation !is FilterTransformationBase) return null if (previousTransformation.indexVariable != null) return null - assert(filter == null) { "Should not happen because no 2 consecutive FilterTransformation's possible"} - return CountTransformation(loop, previousTransformation.inputVariable, initialization, previousTransformation.effectiveCondition()) + val newFilter = if (filter == null) + previousTransformation.effectiveCondition + else + KtPsiFactory(filter).createExpressionByPattern("$0 && $1", previousTransformation.effectiveCondition, filter) + return CountTransformation(loop, previousTransformation.inputVariable, initialization, newFilter) } override val presentation: String 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 c13862a2432..631cef084ab 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 @@ -224,7 +224,7 @@ object FindTransformationMatcher : TransformationMatcher { assert(valueIfFound.isPhysical) assert(valueIfNotFound.isPhysical) - val filter = filterTransformation?.effectiveCondition() + val filter = filterTransformation?.effectiveCondition if (indexVariable != null) { if (filterTransformation == null) return null // makes no sense, indexVariable must be always null diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/sequence/FilterTransformation.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/sequence/FilterTransformation.kt index 7482dd46f43..07bc406fb87 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/sequence/FilterTransformation.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/sequence/FilterTransformation.kt @@ -24,15 +24,26 @@ import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.blockExpressionsOrSingle +abstract class FilterTransformationBase : SequenceTransformation { + abstract val effectiveCondition: KtExpression + abstract val inputVariable: KtCallableDeclaration + abstract val indexVariable: KtCallableDeclaration? + + override val affectsIndex: Boolean + get() = true +} + class FilterTransformation( override val loop: KtForExpression, - val inputVariable: KtCallableDeclaration, - val indexVariable: KtCallableDeclaration?, + override val inputVariable: KtCallableDeclaration, + override val indexVariable: KtCallableDeclaration?, val condition: KtExpression, val isInverse: Boolean -) : SequenceTransformation { +) : FilterTransformationBase() { - fun effectiveCondition() = if (isInverse) condition.negate() else condition + override val effectiveCondition: KtExpression by lazy { + if (isInverse) condition.negate() else condition + } private val functionName = when { indexVariable != null -> "filterIndexed" @@ -40,15 +51,12 @@ class FilterTransformation( else -> "filter" } - override val affectsIndex: Boolean - get() = true - override val presentation: String get() = "$functionName{}" override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression { val lambda = if (indexVariable != null) - generateLambda(inputVariable, indexVariable, effectiveCondition()) + generateLambda(inputVariable, indexVariable, effectiveCondition) else generateLambda(inputVariable, condition) return chainedCallGenerator.generate("$0$1:'{}'", functionName, lambda) @@ -97,7 +105,7 @@ class FilterTransformation( val indexVariable = transformation.indexVariable ?: nextTransformation.indexVariable val mergedCondition = KtPsiFactory(state.outerLoop).createExpressionByPattern( - "$0 && $1", transformation.effectiveCondition(), nextTransformation.effectiveCondition()) + "$0 && $1", transformation.effectiveCondition, nextTransformation.effectiveCondition) transformation = FilterTransformation(state.outerLoop, transformation.inputVariable, indexVariable, mergedCondition, isInverse = false) //TODO: build filterNot in some cases? currentState = nextState } @@ -167,7 +175,7 @@ class FilterTransformation( ) { val typeRef = effectiveCondition.typeReference if (typeRef != null) { - return FilterIsInstanceTransformation(loop, typeRef) + return FilterIsInstanceTransformation(loop, inputVariable, typeRef) } } @@ -176,7 +184,7 @@ class FilterTransformation( && effectiveCondition.right.isNullExpression() && effectiveCondition.left.isSimpleName(inputVariable.nameAsSafeName) ) { - return FilterNotNullTransformation(loop) + return FilterNotNullTransformation(loop, inputVariable) } return FilterTransformation(loop, inputVariable, null, condition, isInverse) @@ -186,11 +194,15 @@ class FilterTransformation( class FilterIsInstanceTransformation( override val loop: KtForExpression, + override val inputVariable: KtCallableDeclaration, private val type: KtTypeReference -) : SequenceTransformation { +) : FilterTransformationBase() { - override val affectsIndex: Boolean - get() = true + override val effectiveCondition by lazy { + KtPsiFactory(loop).createExpressionByPattern("$0 is $1", inputVariable.nameAsSafeName, type) + } + + override val indexVariable: KtCallableDeclaration? get() = null override val presentation: String get() = "filterIsInstance<>()" @@ -200,7 +212,17 @@ class FilterIsInstanceTransformation( } } -class FilterNotNullTransformation(override val loop: KtForExpression) : SequenceTransformation { +class FilterNotNullTransformation( + override val loop: KtForExpression, + override val inputVariable: KtCallableDeclaration +) : FilterTransformationBase() { + + override val effectiveCondition: KtExpression by lazy { + KtPsiFactory(loop).createExpressionByPattern("$0 != null", inputVariable.nameAsSafeName) + } + + override val indexVariable: KtCallableDeclaration? get() = null + override fun mergeWithPrevious(previousTransformation: SequenceTransformation): SequenceTransformation? { if (previousTransformation is MapTransformation) { return MapTransformation(loop, previousTransformation.inputVariable, previousTransformation.indexVariable, previousTransformation.mapping, mapNotNull = true) @@ -208,9 +230,6 @@ class FilterNotNullTransformation(override val loop: KtForExpression) : Sequence return null } - override val affectsIndex: Boolean - get() = true - override val presentation: String get() = "filterNotNull()" diff --git a/idea/testData/intentions/loopToCallChain/KT14191.kt b/idea/testData/intentions/loopToCallChain/KT14191.kt new file mode 100644 index 00000000000..98d217de3e1 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/KT14191.kt @@ -0,0 +1,12 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterNotNull().sum()'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().sum()'" +fun f(list: List): Int{ + var r = 0 + for (d in list) { + if (d != null) { + r += d + } + } + return r +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/KT14191.kt.after b/idea/testData/intentions/loopToCallChain/KT14191.kt.after new file mode 100644 index 00000000000..66e67079fec --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/KT14191.kt.after @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterNotNull().sum()'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().sum()'" +fun f(list: List): Int{ + val r = list + .filterNotNull() + .sum() + return r +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/KT14191.kt.after2 b/idea/testData/intentions/loopToCallChain/KT14191.kt.after2 new file mode 100644 index 00000000000..eee1e4d4476 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/KT14191.kt.after2 @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterNotNull().sum()'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().sum()'" +fun f(list: List): Int{ + val r = list + .asSequence() + .filterNotNull() + .sum() + return r +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/count/KT14191.kt b/idea/testData/intentions/loopToCallChain/count/KT14191.kt deleted file mode 100644 index 28a2990a62a..00000000000 --- a/idea/testData/intentions/loopToCallChain/count/KT14191.kt +++ /dev/null @@ -1,12 +0,0 @@ -// WITH_RUNTIME -// INTENTION_TEXT: "Replace with 'filterNotNull().count()'" -// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().count()'" -fun f11(list: List): Int{ - var objs = 0 - for (d in list) { - if (d != null) { - objs++ - } - } - return objs -} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/count/KT14191.kt.after b/idea/testData/intentions/loopToCallChain/count/KT14191.kt.after deleted file mode 100644 index e03c7254fcd..00000000000 --- a/idea/testData/intentions/loopToCallChain/count/KT14191.kt.after +++ /dev/null @@ -1,9 +0,0 @@ -// WITH_RUNTIME -// INTENTION_TEXT: "Replace with 'filterNotNull().count()'" -// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().count()'" -fun f11(list: List): Int{ - val objs = list - .filterNotNull() - .count() - return objs -} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/count/KT14191.kt.after2 b/idea/testData/intentions/loopToCallChain/count/KT14191.kt.after2 deleted file mode 100644 index d8b3cb28f9c..00000000000 --- a/idea/testData/intentions/loopToCallChain/count/KT14191.kt.after2 +++ /dev/null @@ -1,10 +0,0 @@ -// WITH_RUNTIME -// INTENTION_TEXT: "Replace with 'filterNotNull().count()'" -// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().count()'" -fun f11(list: List): Int{ - val objs = list - .asSequence() - .filterNotNull() - .count() - return objs -} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/count/countIsInstance.kt b/idea/testData/intentions/loopToCallChain/count/countIsInstance.kt new file mode 100644 index 00000000000..a8172b18210 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/count/countIsInstance.kt @@ -0,0 +1,12 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'count{}'" +// IS_APPLICABLE_2: false +fun f11(list: List): Int{ + var c = 0 + for (d in list) { + if (d is String) { + c++ + } + } + return c +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/count/countIsInstance.kt.after b/idea/testData/intentions/loopToCallChain/count/countIsInstance.kt.after new file mode 100644 index 00000000000..4161c7525f4 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/count/countIsInstance.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'count{}'" +// IS_APPLICABLE_2: false +fun f11(list: List): Int{ + val c = list.count { it is String } + return c +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/count/countNotNull.kt b/idea/testData/intentions/loopToCallChain/count/countNotNull.kt new file mode 100644 index 00000000000..bbd7a80af9a --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/count/countNotNull.kt @@ -0,0 +1,12 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'count{}'" +// IS_APPLICABLE_2: false +fun f11(list: List): Int{ + var c = 0 + for (d in list) { + if (d != null) { + c++ + } + } + return c +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/count/countNotNull.kt.after b/idea/testData/intentions/loopToCallChain/count/countNotNull.kt.after new file mode 100644 index 00000000000..9bded739631 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/count/countNotNull.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'count{}'" +// IS_APPLICABLE_2: false +fun f11(list: List): Int{ + val c = list.count { it != null } + return c +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/count/countSomethingAndNotNull.kt b/idea/testData/intentions/loopToCallChain/count/countSomethingAndNotNull.kt new file mode 100644 index 00000000000..98b96a2512d --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/count/countSomethingAndNotNull.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'count{}'" +// IS_APPLICABLE_2: false +fun f(list: List): Int{ + var c = 0 + for (d in list) { + if (d == "") continue + if (d != null) { + if (d is Int) continue + c++ + } + } + return c +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/count/countSomethingAndNotNull.kt.after b/idea/testData/intentions/loopToCallChain/count/countSomethingAndNotNull.kt.after new file mode 100644 index 00000000000..b4c8d11caa1 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/count/countSomethingAndNotNull.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'count{}'" +// IS_APPLICABLE_2: false +fun f(list: List): Int{ + val c = list.count { it != "" && it != null && it !is Int } + return c +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTest2Generated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTest2Generated.java index 612694abc11..43e64c177bb 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTest2Generated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTest2Generated.java @@ -83,6 +83,12 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 { doTest(fileName); } + @TestMetadata("KT14191.kt") + public void testKT14191() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/KT14191.kt"); + doTest(fileName); + } + @TestMetadata("lastOrNull_ifAssign.kt") public void testLastOrNull_ifAssign() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/lastOrNull_ifAssign.kt"); @@ -235,9 +241,21 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/loopToCallChain/count"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); } - @TestMetadata("KT14191.kt") - public void testKT14191() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/count/KT14191.kt"); + @TestMetadata("countIsInstance.kt") + public void testCountIsInstance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/count/countIsInstance.kt"); + doTest(fileName); + } + + @TestMetadata("countNotNull.kt") + public void testCountNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/count/countNotNull.kt"); + doTest(fileName); + } + + @TestMetadata("countSomethingAndNotNull.kt") + public void testCountSomethingAndNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/count/countSomethingAndNotNull.kt"); doTest(fileName); } diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 07c56e004e0..44f4d95a980 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -7991,6 +7991,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("KT14191.kt") + public void testKT14191() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/KT14191.kt"); + doTest(fileName); + } + @TestMetadata("lastOrNull_ifAssign.kt") public void testLastOrNull_ifAssign() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/lastOrNull_ifAssign.kt"); @@ -8143,9 +8149,21 @@ public class IntentionTestGenerated extends AbstractIntentionTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/loopToCallChain/count"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); } - @TestMetadata("KT14191.kt") - public void testKT14191() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/count/KT14191.kt"); + @TestMetadata("countIsInstance.kt") + public void testCountIsInstance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/count/countIsInstance.kt"); + doTest(fileName); + } + + @TestMetadata("countNotNull.kt") + public void testCountNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/count/countNotNull.kt"); + doTest(fileName); + } + + @TestMetadata("countSomethingAndNotNull.kt") + public void testCountSomethingAndNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/count/countSomethingAndNotNull.kt"); doTest(fileName); }