count() to merge with filterNotNull and filterIsInstance + changed test for KT-14191 to use sum()

This commit is contained in:
Valentin Kipyatkov
2016-10-14 16:47:39 +03:00
parent afd25548a2
commit b28d016709
17 changed files with 177 additions and 60 deletions
@@ -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
@@ -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
@@ -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()"
+12
View File
@@ -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?>): Int{
var r = 0
<caret>for (d in list) {
if (d != null) {
r += d
}
}
return r
}
@@ -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?>): Int{
val r = list
.filterNotNull()
.sum()
return r
}
@@ -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?>): Int{
val r = list
.asSequence()
.filterNotNull()
.sum()
return r
}
@@ -1,12 +0,0 @@
// WITH_RUNTIME
// INTENTION_TEXT: "Replace with 'filterNotNull().count()'"
// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().count()'"
fun f11(list: List<Any?>): Int{
var objs = 0
<caret>for (d in list) {
if (d != null) {
objs++
}
}
return objs
}
@@ -1,9 +0,0 @@
// WITH_RUNTIME
// INTENTION_TEXT: "Replace with 'filterNotNull().count()'"
// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().count()'"
fun f11(list: List<Any?>): Int{
val <caret>objs = list
.filterNotNull()
.count()
return objs
}
@@ -1,10 +0,0 @@
// WITH_RUNTIME
// INTENTION_TEXT: "Replace with 'filterNotNull().count()'"
// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().count()'"
fun f11(list: List<Any?>): Int{
val <caret>objs = list
.asSequence()
.filterNotNull()
.count()
return objs
}
@@ -0,0 +1,12 @@
// WITH_RUNTIME
// INTENTION_TEXT: "Replace with 'count{}'"
// IS_APPLICABLE_2: false
fun f11(list: List<Any?>): Int{
var c = 0
<caret>for (d in list) {
if (d is String) {
c++
}
}
return c
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
// INTENTION_TEXT: "Replace with 'count{}'"
// IS_APPLICABLE_2: false
fun f11(list: List<Any?>): Int{
val <caret>c = list.count { it is String }
return c
}
@@ -0,0 +1,12 @@
// WITH_RUNTIME
// INTENTION_TEXT: "Replace with 'count{}'"
// IS_APPLICABLE_2: false
fun f11(list: List<Any?>): Int{
var c = 0
<caret>for (d in list) {
if (d != null) {
c++
}
}
return c
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
// INTENTION_TEXT: "Replace with 'count{}'"
// IS_APPLICABLE_2: false
fun f11(list: List<Any?>): Int{
val <caret>c = list.count { it != null }
return c
}
@@ -0,0 +1,14 @@
// WITH_RUNTIME
// INTENTION_TEXT: "Replace with 'count{}'"
// IS_APPLICABLE_2: false
fun f(list: List<Any?>): Int{
var c = 0
<caret>for (d in list) {
if (d == "") continue
if (d != null) {
if (d is Int) continue
c++
}
}
return c
}
@@ -0,0 +1,7 @@
// WITH_RUNTIME
// INTENTION_TEXT: "Replace with 'count{}'"
// IS_APPLICABLE_2: false
fun f(list: List<Any?>): Int{
val <caret>c = list.count { it != "" && it != null && it !is Int }
return c
}
@@ -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);
}
@@ -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);
}