diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt index d07adea8f5e..ba823176f7c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt @@ -22,7 +22,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers @@ -164,8 +163,8 @@ private fun KtExpression.specialNegation(): KtExpression? { if (operationReference.getReferencedName() == "!") { val baseExpression = baseExpression if (baseExpression != null) { - val context = baseExpression.analyzeAndGetResult().bindingContext - val type = context.getType(baseExpression) + val bindingContext = baseExpression.analyze(BodyResolveMode.PARTIAL) + val type = bindingContext.getType(baseExpression) if (type != null && KotlinBuiltIns.isBoolean(type)) { return KtPsiUtil.safeDeparenthesize(baseExpression) } diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/AddToCollectionTransformation.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/AddToCollectionTransformation.kt index 069e99d70d9..afaf78568ce 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/AddToCollectionTransformation.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/result/AddToCollectionTransformation.kt @@ -22,11 +22,7 @@ import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.intentions.loopToCallChain.* -import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FilterNotNullTransformation -import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FilterTransformation -import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FlatMapTransformation -import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.MapTransformation -import org.jetbrains.kotlin.idea.intentions.negate +import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.* import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* @@ -41,7 +37,9 @@ class AddToCollectionTransformation( override fun mergeWithPrevious(previousTransformation: SequenceTransformation): ResultTransformation? { return when (previousTransformation) { is FilterTransformation -> { - FilterToTransformation.create(loop, previousTransformation.inputVariable, previousTransformation.indexVariable, targetCollection, previousTransformation.condition, previousTransformation.isInverse) + FilterToTransformation.create( + loop, previousTransformation.inputVariable, previousTransformation.indexVariable, + targetCollection, previousTransformation.effectiveCondition, previousTransformation.isFilterNot) } is FilterNotNullTransformation -> { @@ -201,15 +199,19 @@ class FilterToTransformation private constructor( private val inputVariable: KtCallableDeclaration, private val indexVariable: KtCallableDeclaration?, private val targetCollection: KtExpression, - private val condition: KtExpression, - private val isInverse: Boolean + private val effectiveCondition: Condition, + private val isFilterNot: Boolean ) : ReplaceLoopResultTransformation(loop) { - private fun effectiveCondition() = if (isInverse) condition.negate() else condition + init { + if (isFilterNot) { + assert(indexVariable == null) + } + } private val functionName = when { indexVariable != null -> "filterIndexedTo" - isInverse -> "filterNotTo" + isFilterNot -> "filterNotTo" else -> "filterTo" } @@ -218,9 +220,9 @@ class FilterToTransformation private constructor( override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression { val lambda = if (indexVariable != null) - generateLambda(inputVariable, indexVariable, effectiveCondition()) + generateLambda(inputVariable, indexVariable, effectiveCondition.asExpression()) else - generateLambda(inputVariable, condition) + generateLambda(inputVariable, if (isFilterNot) effectiveCondition.asNegatedExpression() else effectiveCondition.asExpression()) return chainedCallGenerator.generate("$functionName($0) $1:'{}'", targetCollection, lambda) } @@ -230,16 +232,16 @@ class FilterToTransformation private constructor( inputVariable: KtCallableDeclaration, indexVariable: KtCallableDeclaration?, targetCollection: KtExpression, - condition: KtExpression, - isInverse: Boolean + condition: Condition, + isFilterNot: Boolean ): ResultTransformation { val initialization = targetCollection.findVariableInitializationBeforeLoop(loop, checkNoOtherUsagesInLoop = true) if (initialization != null && initialization.initializer.hasNoSideEffect()) { - val transformation = FilterToTransformation(loop, inputVariable, indexVariable, initialization.initializer, condition, isInverse) + val transformation = FilterToTransformation(loop, inputVariable, indexVariable, initialization.initializer, condition, isFilterNot) return AssignToVariableResultTransformation.createDelegated(transformation, initialization) } else { - return FilterToTransformation(loop, inputVariable, indexVariable, targetCollection, condition, isInverse) + return FilterToTransformation(loop, inputVariable, indexVariable, targetCollection, condition, isFilterNot) } } } 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 decbf495207..a26069259dc 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 @@ -34,9 +34,9 @@ class CountTransformation( if (previousTransformation !is FilterTransformationBase) return null if (previousTransformation.indexVariable != null) return null val newFilter = if (filter == null) - previousTransformation.effectiveCondition + previousTransformation.effectiveCondition.asExpression() else - KtPsiFactory(filter).createExpressionByPattern("$0 && $1", previousTransformation.effectiveCondition, filter) + KtPsiFactory(filter).createExpressionByPattern("$0 && $1", previousTransformation.effectiveCondition.asExpression(), filter) return CountTransformation(loop, previousTransformation.inputVariable, initialization, newFilter) } 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 a3c9a48496d..f57d5ef41ad 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 @@ -225,7 +225,7 @@ object FindTransformationMatcher : TransformationMatcher { assert(valueIfFound.isPhysical) assert(valueIfNotFound.isPhysical) - val filter = filterTransformation?.effectiveCondition + val filter = filterTransformation?.effectiveCondition?.asExpression() 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/Condition.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/sequence/Condition.kt new file mode 100644 index 00000000000..a277d402dc9 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/sequence/Condition.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence + +import org.jetbrains.kotlin.idea.intentions.loopToCallChain.hasUsages +import org.jetbrains.kotlin.idea.intentions.negate +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* + +interface Condition { + fun asExpression(): KtExpression + fun asNegatedExpression(): KtExpression + fun toAtomicConditions(): List + + companion object { + fun create(expression: KtExpression, negated: Boolean = false): Condition { + if (negated) { + if (expression is KtBinaryExpression && expression.operationToken == KtTokens.OROR) { + //TODO: check Boolean type for operands + val left = expression.left + val right = expression.right + if (left != null && right != null) { + val leftCondition = create(left, negated = true) + val rightCondition = create(right, negated = true) + return CompositeCondition.create(leftCondition.toAtomicConditions() + rightCondition.toAtomicConditions()) + } + } + } + else { + if (expression is KtBinaryExpression && expression.operationToken == KtTokens.ANDAND) { + //TODO: check Boolean type for operands + val left = expression.left + val right = expression.right + if (left != null && right != null) { + val leftCondition = create(left) + val rightCondition = create(right) + return CompositeCondition.create(leftCondition.toAtomicConditions() + rightCondition.toAtomicConditions()) + } + } + } + return AtomicCondition(expression, negated) + } + } +} + +class AtomicCondition(val expression: KtExpression, val isNegated: Boolean = false) : Condition { + init { + assert(expression.isPhysical) + } + + override fun asExpression() = if (isNegated) expression.negate() else expression + override fun asNegatedExpression() = if (isNegated) expression else expression.negate() + override fun toAtomicConditions() = listOf(this) + + fun negate() = AtomicCondition(expression, !isNegated) +} + +class CompositeCondition private constructor(val conditions: List) : Condition { + override fun asExpression(): KtExpression { + val factory = KtPsiFactory(conditions.first().expression) + return factory.buildExpression { + for ((index, condition) in conditions.withIndex()) { + if (index > 0) { + appendFixedText("&&") + } + appendExpression(condition.asExpression()) + } + } + } + + override fun asNegatedExpression(): KtExpression { + return asExpression().negate() //TODO? + } + + override fun toAtomicConditions() = conditions + + companion object { + fun create(conditions: List): Condition { + return conditions.singleOrNull() ?: CompositeCondition(conditions) + } + } +} + +fun Condition.hasUsagesOf(variable: KtCallableDeclaration): Boolean { + return toAtomicConditions().any { variable.hasUsages(it.expression) } +} + 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 95f1edbb933..430071d8fef 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 @@ -16,17 +16,25 @@ package org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression import org.jetbrains.kotlin.idea.intentions.loopToCallChain.* import org.jetbrains.kotlin.idea.intentions.loopToCallChain.result.FindTransformationMatcher import org.jetbrains.kotlin.idea.intentions.loopToCallChain.result.MaxOrMinTransformation import org.jetbrains.kotlin.idea.intentions.negate +import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.blockExpressionsOrSingle +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.utils.addToStdlib.singletonList +import java.util.* abstract class FilterTransformationBase : SequenceTransformation { - abstract val effectiveCondition: KtExpression + abstract val effectiveCondition: Condition + abstract val inputVariable: KtCallableDeclaration abstract val indexVariable: KtCallableDeclaration? @@ -66,31 +74,75 @@ abstract class FilterTransformationBase : SequenceTransformation { var (transformation, currentState) = matchOneTransformation(state) ?: return null assert(currentState.indexVariable == state.indexVariable) // indexVariable should not change - if (transformation is FilterTransformation) { - while (true) { - currentState = currentState.unwrapBlock() + if (transformation !is FilterTransformationBase) { + return TransformationMatch.Sequence(transformation, currentState) + } - if (MaxOrMinTransformation.Matcher.match(currentState) != null) break // do not take 'if' which is required for min/max matcher + val atomicConditions = transformation.effectiveCondition.toAtomicConditions().toMutableList() + while (true) { + currentState = currentState.unwrapBlock() - val (nextTransformation, nextState) = matchOneTransformation(currentState) ?: break - if (nextTransformation !is FilterTransformation) break - assert(nextState.indexVariable == currentState.indexVariable) // indexVariable should not change + if (MaxOrMinTransformation.Matcher.match(currentState) != null) break // do not take 'if' which is required for min/max matcher - val indexVariable = transformation.indexVariable ?: nextTransformation.indexVariable - val mergedCondition = KtPsiFactory(state.outerLoop).createExpressionByPattern( - "$0 && $1", transformation.effectiveCondition, nextTransformation.effectiveCondition) - transformation = FilterTransformation(state.outerLoop, transformation.inputVariable, indexVariable, mergedCondition, isInverse = false) //TODO: build filterNot in some cases? - currentState = nextState + val (nextTransformation, nextState) = matchOneTransformation(currentState) ?: break + if (nextTransformation !is FilterTransformationBase) break + assert(nextState.indexVariable == currentState.indexVariable) // indexVariable should not change + + atomicConditions.addAll(nextTransformation.effectiveCondition.toAtomicConditions()) + + currentState = nextState + } + + val transformations = createTransformationsByAtomicConditions( + currentState.outerLoop, + currentState.inputVariable, + currentState.indexVariable, + atomicConditions, + currentState.statements) + assert(transformations.isNotEmpty()) + + val findTransformationMatch = FindTransformationMatcher.matchWithFilterBefore(currentState, transformations.last()) + if (findTransformationMatch != null) { + return TransformationMatch.Result(findTransformationMatch.resultTransformation, + transformations.dropLast(1) + findTransformationMatch.sequenceTransformations) + } + else { + return TransformationMatch.Sequence(transformations, currentState) + } + } + + private fun createTransformationsByAtomicConditions( + loop: KtForExpression, + inputVariable: KtCallableDeclaration, + indexVariable: KtCallableDeclaration?, + conditions: List, + restStatements: List + ): List { + //TODO: indexVariable case + + if (conditions.size == 1) { + return createFilterTransformation(loop, inputVariable, indexVariable, conditions.single()).singletonList() + } + + val transformations = ArrayList() + for (condition in conditions) { + val transformation = createFilterTransformation(loop, inputVariable, indexVariable, condition) + if (transformation !is FilterTransformation && isSmartCastUsed(inputVariable, restStatements)) { // filterIsInstance of filterNotNull + transformations.add(transformation) + } + else { + val prevFilter = transformations.lastOrNull() as? FilterTransformation + if (prevFilter != null) { + val mergedCondition = CompositeCondition.create(prevFilter.effectiveCondition.toAtomicConditions() + transformation.effectiveCondition.toAtomicConditions()) + val mergedTransformation = createFilterTransformation(loop, inputVariable, indexVariable, mergedCondition, onlyFilterOrFilterNot = true) + transformations[transformations.lastIndex] = mergedTransformation + } + else { + transformations.add(createFilterTransformation(loop, inputVariable, indexVariable, condition, onlyFilterOrFilterNot = true)) + } } - } - - if (transformation is FilterTransformationBase) { - FindTransformationMatcher.matchWithFilterBefore(currentState, transformation) - ?.let { return it } - } - - return TransformationMatch.Sequence(transformation, currentState) + return transformations } private fun matchOneTransformation(state: MatchingState): Pair? { @@ -104,7 +156,7 @@ abstract class FilterTransformationBase : SequenceTransformation { if (!state.inputVariable.hasUsages(condition) && (state.indexVariable == null || !state.indexVariable.hasUsages(condition))) return null if (state.statements.size == 1) { - val transformation = createFilterTransformation(state.outerLoop, state.inputVariable, state.indexVariable, condition, isInverse = false) + val transformation = createFilterTransformation(state.outerLoop, state.inputVariable, state.indexVariable, Condition.create(condition)) val newState = state.copy(statements = listOf(then)) return transformation to newState } @@ -113,7 +165,7 @@ abstract class FilterTransformationBase : SequenceTransformation { when (statement) { is KtContinueExpression -> { if (statement.targetLoop() != state.innerLoop) return null - val transformation = createFilterTransformation(state.outerLoop, state.inputVariable, state.indexVariable, condition, isInverse = true) + val transformation = createFilterTransformation(state.outerLoop, state.inputVariable, state.indexVariable, Condition.create(condition, negated = true)) val newState = state.copy(statements = state.statements.drop(1)) return transformation to newState } @@ -130,40 +182,52 @@ abstract class FilterTransformationBase : SequenceTransformation { } } - //TODO: choose filter or filterNot depending on condition private fun createFilterTransformation( loop: KtForExpression, inputVariable: KtCallableDeclaration, indexVariable: KtCallableDeclaration?, - condition: KtExpression, - isInverse: Boolean + condition: Condition, + onlyFilterOrFilterNot: Boolean = false ): FilterTransformationBase { - val effectiveCondition = if (isInverse) condition.negate() else condition - - if (indexVariable != null && indexVariable.hasUsages(condition)) { - return FilterTransformation(loop, inputVariable, indexVariable, effectiveCondition, isInverse = false) + if (indexVariable != null && condition.hasUsagesOf(indexVariable)) { + return FilterTransformation(loop, inputVariable, indexVariable, condition, isFilterNot = false) } - if (effectiveCondition is KtIsExpression - && !effectiveCondition.isNegated - && effectiveCondition.leftHandSide.isSimpleName(inputVariable.nameAsSafeName) // we cannot use isVariableReference here because expression can be non-physical - ) { - val typeRef = effectiveCondition.typeReference - if (typeRef != null) { - return FilterIsInstanceTransformation(loop, inputVariable, typeRef) + val conditionAsExpression = condition.asExpression() + if (!onlyFilterOrFilterNot) { + if (conditionAsExpression is KtIsExpression + && !conditionAsExpression.isNegated + && conditionAsExpression.leftHandSide.isSimpleName(inputVariable.nameAsSafeName) // we cannot use isVariableReference here because expression can be non-physical + ) { + val typeRef = conditionAsExpression.typeReference + if (typeRef != null) { + return FilterIsInstanceTransformation(loop, inputVariable, typeRef, condition) + } + } + + if (conditionAsExpression is KtBinaryExpression + && conditionAsExpression.operationToken == KtTokens.EXCLEQ + && conditionAsExpression.right.isNullExpression() + && conditionAsExpression.left.isSimpleName(inputVariable.nameAsSafeName) + ) { + return FilterNotNullTransformation(loop, inputVariable, condition) } } - if (effectiveCondition is KtBinaryExpression - && effectiveCondition.operationToken == KtTokens.EXCLEQ - && effectiveCondition.right.isNullExpression() - && effectiveCondition.left.isSimpleName(inputVariable.nameAsSafeName) - ) { - return FilterNotNullTransformation(loop, inputVariable) + if (conditionAsExpression is KtPrefixExpression && conditionAsExpression.operationToken == KtTokens.EXCL) { + return FilterTransformation(loop, inputVariable, null, condition, isFilterNot = true) } - return FilterTransformation(loop, inputVariable, null, condition, isInverse) + return FilterTransformation(loop, inputVariable, null, condition, isFilterNot = false) + } + + private fun isSmartCastUsed(inputVariable: KtCallableDeclaration, statements: List): Boolean { + return statements.any { statement -> + statement.anyDescendantOfType { + it.mainReference.resolve() == inputVariable && it.analyze(BodyResolveMode.PARTIAL)[BindingContext.SMARTCAST, it] != null + } + } } } } @@ -172,17 +236,19 @@ class FilterTransformation( override val loop: KtForExpression, override val inputVariable: KtCallableDeclaration, override val indexVariable: KtCallableDeclaration?, - val condition: KtExpression, - val isInverse: Boolean + override val effectiveCondition: Condition, + val isFilterNot: Boolean ) : FilterTransformationBase() { - override val effectiveCondition: KtExpression by lazy { - if (isInverse) condition.negate() else condition + init { + if (isFilterNot) { + assert(indexVariable == null) + } } private val functionName = when { indexVariable != null -> "filterIndexed" - isInverse -> "filterNot" + isFilterNot -> "filterNot" else -> "filter" } @@ -191,9 +257,9 @@ class FilterTransformation( override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression { val lambda = if (indexVariable != null) - generateLambda(inputVariable, indexVariable, effectiveCondition) + generateLambda(inputVariable, indexVariable, effectiveCondition.asExpression()) else - generateLambda(inputVariable, condition) + generateLambda(inputVariable, if (isFilterNot) effectiveCondition.asNegatedExpression() else effectiveCondition.asExpression()) return chainedCallGenerator.generate("$0$1:'{}'", functionName, lambda) } } @@ -201,13 +267,10 @@ class FilterTransformation( class FilterIsInstanceTransformation( override val loop: KtForExpression, override val inputVariable: KtCallableDeclaration, - private val type: KtTypeReference + private val type: KtTypeReference, + override val effectiveCondition: Condition ) : FilterTransformationBase() { - override val effectiveCondition by lazy { - KtPsiFactory(loop).createExpressionByPattern("$0 is $1", inputVariable.nameAsSafeName, type) - } - override val indexVariable: KtCallableDeclaration? get() = null override val presentation: String @@ -220,13 +283,10 @@ class FilterIsInstanceTransformation( class FilterNotNullTransformation( override val loop: KtForExpression, - override val inputVariable: KtCallableDeclaration + override val inputVariable: KtCallableDeclaration, + override val effectiveCondition: Condition ) : 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? { diff --git a/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterIsInstance.kt b/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterIsInstance.kt new file mode 100644 index 00000000000..08a76416b8b --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterIsInstance.kt @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterIndexedTo(){}'" +// IS_APPLICABLE_2: false +fun foo(list: List, out: MutableList){ + for ((i, any) in list.withIndex()) { + if (any is String && i % 2 == 0) + out.add(any) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterIsInstance.kt.after b/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterIsInstance.kt.after new file mode 100644 index 00000000000..1f055dbcfc9 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterIsInstance.kt.after @@ -0,0 +1,6 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterIndexedTo(){}'" +// IS_APPLICABLE_2: false +fun foo(list: List, out: MutableList){ + list.filterIndexedTo(out) { i, any -> any is String && i % 2 == 0 } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt b/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt new file mode 100644 index 00000000000..d01e29cbe82 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'" +fun foo(list: List){ + for (l in list) { + if (l != null && l.startsWith("IMG:")) + println(l) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt.after b/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt.after new file mode 100644 index 00000000000..a1815e2b048 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt.after @@ -0,0 +1,8 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'" +fun foo(list: List){ + list + .filter { it != null && it.startsWith("IMG:") } + .forEach { println(it) } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt.after2 b/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt.after2 new file mode 100644 index 00000000000..34477a27b10 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt.after2 @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'" +fun foo(list: List){ + list + .asSequence() + .filter { it != null && it.startsWith("IMG:") } + .forEach { println(it) } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt b/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt index bc6201f1e65..8fa2389b374 100644 --- a/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt +++ b/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt @@ -1,6 +1,6 @@ // WITH_RUNTIME -// INTENTION_TEXT: "Replace with '...flatMap{}.filterNot{}.mapTo(){}'" -// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filterNot{}.mapTo(){}'" +// INTENTION_TEXT: "Replace with '...flatMap{}.filter{}.mapTo(){}'" +// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filter{}.mapTo(){}'" fun foo(list: List, target: MutableCollection) { var i = 0 for (s in list) { diff --git a/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt.after b/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt.after index 241ac49f320..1922e73963c 100644 --- a/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt.after +++ b/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt.after @@ -1,10 +1,10 @@ // WITH_RUNTIME -// INTENTION_TEXT: "Replace with '...flatMap{}.filterNot{}.mapTo(){}'" -// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filterNot{}.mapTo(){}'" +// INTENTION_TEXT: "Replace with '...flatMap{}.filter{}.mapTo(){}'" +// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filter{}.mapTo(){}'" fun foo(list: List, target: MutableCollection) { list .filterIndexed { i, s -> i % 10 != 0 } .flatMap { it.indices } - .filterNot { it == 10 } + .filter { it != 10 } .mapTo(target) { it.toString() } } \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt.after2 b/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt.after2 index 3be1aec7ecc..aee8fbcf71c 100644 --- a/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt.after2 +++ b/idea/testData/intentions/loopToCallChain/filter/filterIndexedAndFlatMapWithContinue.kt.after2 @@ -1,11 +1,11 @@ // WITH_RUNTIME -// INTENTION_TEXT: "Replace with '...flatMap{}.filterNot{}.mapTo(){}'" -// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filterNot{}.mapTo(){}'" +// INTENTION_TEXT: "Replace with '...flatMap{}.filter{}.mapTo(){}'" +// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filter{}.mapTo(){}'" fun foo(list: List, target: MutableCollection) { list .asSequence() .filterIndexed { i, s -> i % 10 != 0 } .flatMap { it.indices.asSequence() } - .filterNot { it == 10 } + .filter { it != 10 } .mapTo(target) { it.toString() } } \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/filterNotTo.kt b/idea/testData/intentions/loopToCallChain/filter/filterNotTo.kt index 2c7991dd994..2aad2cc3a56 100644 --- a/idea/testData/intentions/loopToCallChain/filter/filterNotTo.kt +++ b/idea/testData/intentions/loopToCallChain/filter/filterNotTo.kt @@ -3,7 +3,9 @@ // IS_APPLICABLE_2: false fun foo(list: List, target: MutableList) { for (s in list) { - if (s.length == 0) continue + if (bar(s)) continue target.add(s) } -} \ No newline at end of file +} + +fun bar(string: String): Boolean = TODO() \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/filterNotTo.kt.after b/idea/testData/intentions/loopToCallChain/filter/filterNotTo.kt.after index 8d8dec42f72..f4681358c7b 100644 --- a/idea/testData/intentions/loopToCallChain/filter/filterNotTo.kt.after +++ b/idea/testData/intentions/loopToCallChain/filter/filterNotTo.kt.after @@ -2,5 +2,7 @@ // INTENTION_TEXT: "Replace with 'filterNotTo(){}'" // IS_APPLICABLE_2: false fun foo(list: List, target: MutableList) { - list.filterNotTo(target) { it.length == 0 } -} \ No newline at end of file + list.filterNotTo(target) { bar(it) } +} + +fun bar(string: String): Boolean = TODO() \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt b/idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt new file mode 100644 index 00000000000..1e73d212b91 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'" +fun foo(list: List){ + for (l in list) { + if (l == null) continue + if (l.startsWith("IMG:")) + println(l) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt.after b/idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt.after new file mode 100644 index 00000000000..a1815e2b048 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt.after @@ -0,0 +1,8 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'" +fun foo(list: List){ + list + .filter { it != null && it.startsWith("IMG:") } + .forEach { println(it) } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt.after2 b/idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt.after2 new file mode 100644 index 00000000000..34477a27b10 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt.after2 @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'" +fun foo(list: List){ + list + .asSequence() + .filter { it != null && it.startsWith("IMG:") } + .forEach { println(it) } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt new file mode 100644 index 00000000000..06ab8da0dcf --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterIsInstance<>().filterTo(){}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterIsInstance<>().filterTo(){}'" +fun foo(list: List, out: MutableList){ + for (any in list) { + if (any is String && any.length > 0) + out.add(any) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt.after b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt.after new file mode 100644 index 00000000000..08c8735e029 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt.after @@ -0,0 +1,8 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterIsInstance<>().filterTo(){}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterIsInstance<>().filterTo(){}'" +fun foo(list: List, out: MutableList){ + list + .filterIsInstance() + .filterTo(out) { it.length > 0 } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt.after2 b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt.after2 new file mode 100644 index 00000000000..b6450e4f0a7 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt.after2 @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterIsInstance<>().filterTo(){}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterIsInstance<>().filterTo(){}'" +fun foo(list: List, out: MutableList){ + list + .asSequence() + .filterIsInstance() + .filterTo(out) { it.length > 0 } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt new file mode 100644 index 00000000000..de6b208effb --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterNotNull().filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().filter{}.forEach{}'" +fun foo(list: List){ + for (l in list) { + if (l != null && l.startsWith("IMG:")) + println(l.hashCode()) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt.after b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt.after new file mode 100644 index 00000000000..e4917366821 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt.after @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterNotNull().filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().filter{}.forEach{}'" +fun foo(list: List){ + list + .filterNotNull() + .filter { it.startsWith("IMG:") } + .forEach { println(it.hashCode()) } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt.after2 b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt.after2 new file mode 100644 index 00000000000..0038389c1c2 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt.after2 @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterNotNull().filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().filter{}.forEach{}'" +fun foo(list: List){ + list + .asSequence() + .filterNotNull() + .filter { it.startsWith("IMG:") } + .forEach { println(it.hashCode()) } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt new file mode 100644 index 00000000000..fc7384b054f --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterNotNull().filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().filter{}.forEach{}'" +fun foo(list: List){ + for (l in list) { + if (l == null || !l.startsWith("IMG:")) continue + println(l.hashCode()) + } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt.after b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt.after new file mode 100644 index 00000000000..e4917366821 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt.after @@ -0,0 +1,9 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterNotNull().filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().filter{}.forEach{}'" +fun foo(list: List){ + list + .filterNotNull() + .filter { it.startsWith("IMG:") } + .forEach { println(it.hashCode()) } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt.after2 b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt.after2 new file mode 100644 index 00000000000..0038389c1c2 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt.after2 @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'filterNotNull().filter{}.forEach{}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().filter{}.forEach{}'" +fun foo(list: List){ + list + .asSequence() + .filterNotNull() + .filter { it.startsWith("IMG:") } + .forEach { println(it.hashCode()) } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt b/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt index 6fec8279dbd..cba04e5a66a 100644 --- a/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt +++ b/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt @@ -1,6 +1,6 @@ // WITH_RUNTIME -// INTENTION_TEXT: "Replace with 'filterNotNull().firstOrNull{}'" -// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().firstOrNull{}'" +// INTENTION_TEXT: "Replace with 'firstOrNull{}'" +// IS_APPLICABLE_2: false fun getFirstValue() = "value" diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt.after b/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt.after index cfb76214fcf..b8d1e37114c 100644 --- a/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt.after +++ b/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt.after @@ -1,13 +1,11 @@ // WITH_RUNTIME -// INTENTION_TEXT: "Replace with 'filterNotNull().firstOrNull{}'" -// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().firstOrNull{}'" +// INTENTION_TEXT: "Replace with 'firstOrNull{}'" +// IS_APPLICABLE_2: false fun getFirstValue() = "value" fun foo(list: List): String? { val value = getFirstValue() - val found: String? = list - .filterNotNull() - .firstOrNull { !it.startsWith("IMG:") && it.contains(value) } + val found: String? = list.firstOrNull { it != null && !it.startsWith("IMG:") && it.contains(value) } return found } \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt.after2 b/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt.after2 deleted file mode 100644 index 6d7b044b169..00000000000 --- a/idea/testData/intentions/loopToCallChain/firstOrNull/KT14292.kt.after2 +++ /dev/null @@ -1,14 +0,0 @@ -// WITH_RUNTIME -// INTENTION_TEXT: "Replace with 'filterNotNull().firstOrNull{}'" -// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().firstOrNull{}'" - -fun getFirstValue() = "value" - -fun foo(list: List): String? { - val value = getFirstValue() - val found: String? = list - .asSequence() - .filterNotNull() - .firstOrNull { !it.startsWith("IMG:") && it.contains(value) } - return found -} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/indexOf/KT14303.kt b/idea/testData/intentions/loopToCallChain/indexOf/KT14303.kt new file mode 100644 index 00000000000..572028462af --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/indexOf/KT14303.kt @@ -0,0 +1,11 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'indexOfFirst{}'" +// IS_APPLICABLE_2: false +fun f8_indexOfFirst_complex(value: String, list: List):Int{ + for ((i, any) in list.withIndex()) + if (any != null) + if (any is String) + if (any == value) + return i + return -1 +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/indexOf/KT14303.kt.after b/idea/testData/intentions/loopToCallChain/indexOf/KT14303.kt.after new file mode 100644 index 00000000000..a3f87667c5b --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/indexOf/KT14303.kt.after @@ -0,0 +1,6 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with 'indexOfFirst{}'" +// IS_APPLICABLE_2: false +fun f8_indexOfFirst_complex(value: String, list: List):Int{ + return list.indexOfFirst { it != null && it is String && it == value } +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt b/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt index 45cc13b7072..88ab6b6abf6 100644 --- a/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt +++ b/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt @@ -1,6 +1,6 @@ // WITH_RUNTIME -// INTENTION_TEXT: "Replace with 'flatMap{}.filterNot{}.mapIndexedTo(){}'" -// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filterNot{}.mapIndexedTo(){}'" +// INTENTION_TEXT: "Replace with 'flatMap{}.filter{}.mapIndexedTo(){}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filter{}.mapIndexedTo(){}'" fun foo(list: List, target: MutableCollection) { var i = 0 for (s in list) { diff --git a/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt.after b/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt.after index 315c2150f5c..1579e35e665 100644 --- a/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt.after +++ b/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt.after @@ -1,9 +1,9 @@ // WITH_RUNTIME -// INTENTION_TEXT: "Replace with 'flatMap{}.filterNot{}.mapIndexedTo(){}'" -// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filterNot{}.mapIndexedTo(){}'" +// INTENTION_TEXT: "Replace with 'flatMap{}.filter{}.mapIndexedTo(){}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filter{}.mapIndexedTo(){}'" fun foo(list: List, target: MutableCollection) { list .flatMap { it.indices } - .filterNot { it == 10 } + .filter { it != 10 } .mapIndexedTo(target) { i, j -> i + j } } \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt.after2 b/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt.after2 index 8453c184f6e..1be56318c10 100644 --- a/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt.after2 +++ b/idea/testData/intentions/loopToCallChain/introduceIndex/indexWithNestedLoop.kt.after2 @@ -1,10 +1,10 @@ // WITH_RUNTIME -// INTENTION_TEXT: "Replace with 'flatMap{}.filterNot{}.mapIndexedTo(){}'" -// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filterNot{}.mapIndexedTo(){}'" +// INTENTION_TEXT: "Replace with 'flatMap{}.filter{}.mapIndexedTo(){}'" +// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filter{}.mapIndexedTo(){}'" fun foo(list: List, target: MutableCollection) { list .asSequence() .flatMap { it.indices.asSequence() } - .filterNot { it == 10 } + .filter { it != 10 } .mapIndexedTo(target) { i, j -> i + j } } \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/smartCasts/smartCastRequired2.kt b/idea/testData/intentions/loopToCallChain/smartCasts/smartCastRequired2.kt index 6ffa86e9218..5f4db970a1b 100644 --- a/idea/testData/intentions/loopToCallChain/smartCasts/smartCastRequired2.kt +++ b/idea/testData/intentions/loopToCallChain/smartCasts/smartCastRequired2.kt @@ -1,6 +1,6 @@ // WITH_RUNTIME -// IS_APPLICABLE: false -// IS_APPLICABLE_2: false +// INTENTION_TEXT: "Replace with '...filter{}.map{}.firstOrNull()'" +// INTENTION_TEXT_2: "Replace with 'asSequence()...filter{}.map{}.firstOrNull()'" fun foo(list: List, o: Any): Int? { for (s in list) { if (s is String && s.length > 0) { diff --git a/idea/testData/intentions/loopToCallChain/smartCasts/smartCastRequired2.kt.after b/idea/testData/intentions/loopToCallChain/smartCasts/smartCastRequired2.kt.after new file mode 100644 index 00000000000..d4599a154d1 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/smartCasts/smartCastRequired2.kt.after @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with '...filter{}.map{}.firstOrNull()'" +// INTENTION_TEXT_2: "Replace with 'asSequence()...filter{}.map{}.firstOrNull()'" +fun foo(list: List, o: Any): Int? { + return list + .filterIsInstance() + .filter { it.length > 0 } + .map { it.length * 2 } + .firstOrNull() +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/smartCasts/smartCastRequired2.kt.after2 b/idea/testData/intentions/loopToCallChain/smartCasts/smartCastRequired2.kt.after2 new file mode 100644 index 00000000000..e03b639d8f9 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/smartCasts/smartCastRequired2.kt.after2 @@ -0,0 +1,11 @@ +// WITH_RUNTIME +// INTENTION_TEXT: "Replace with '...filter{}.map{}.firstOrNull()'" +// INTENTION_TEXT_2: "Replace with 'asSequence()...filter{}.map{}.firstOrNull()'" +fun foo(list: List, o: Any): Int? { + return list + .asSequence() + .filterIsInstance() + .filter { it.length > 0 } + .map { it.length * 2 } + .firstOrNull() +} \ 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 c528f34d568..5698797d008 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTest2Generated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTest2Generated.java @@ -358,6 +358,18 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 { doTest(fileName); } + @TestMetadata("doNotSplitOutFilterIsInstance.kt") + public void testDoNotSplitOutFilterIsInstance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterIsInstance.kt"); + doTest(fileName); + } + + @TestMetadata("doNotSplitOutFilterNotNull.kt") + public void testDoNotSplitOutFilterNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt"); + doTest(fileName); + } + @TestMetadata("filterIndexed.kt") public void testFilterIndexed() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/filterIndexed.kt"); @@ -514,6 +526,12 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 { doTest(fileName); } + @TestMetadata("glueTogetherFilterNotNull.kt") + public void testGlueTogetherFilterNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt"); + doTest(fileName); + } + @TestMetadata("ifContinue.kt") public void testIfContinue() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/ifContinue.kt"); @@ -555,6 +573,24 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/mergeMultiple.kt"); doTest(fileName); } + + @TestMetadata("splitOutFilterIsInstance.kt") + public void testSplitOutFilterIsInstance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt"); + doTest(fileName); + } + + @TestMetadata("splitOutFilterNotNull.kt") + public void testSplitOutFilterNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt"); + doTest(fileName); + } + + @TestMetadata("splitOutFilterNotNull2.kt") + public void testSplitOutFilterNotNull2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt"); + doTest(fileName); + } } @TestMetadata("idea/testData/intentions/loopToCallChain/firstOrNull") @@ -844,6 +880,12 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 { doTest(fileName); } + @TestMetadata("KT14303.kt") + public void testKT14303() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/indexOf/KT14303.kt"); + doTest(fileName); + } + @TestMetadata("lastIndexOf.kt") public void testLastIndexOf() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/indexOf/lastIndexOf.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 2efeb0a3008..8a42705ce52 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -8266,6 +8266,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("doNotSplitOutFilterIsInstance.kt") + public void testDoNotSplitOutFilterIsInstance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterIsInstance.kt"); + doTest(fileName); + } + + @TestMetadata("doNotSplitOutFilterNotNull.kt") + public void testDoNotSplitOutFilterNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/doNotSplitOutFilterNotNull.kt"); + doTest(fileName); + } + @TestMetadata("filterIndexed.kt") public void testFilterIndexed() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/filterIndexed.kt"); @@ -8422,6 +8434,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("glueTogetherFilterNotNull.kt") + public void testGlueTogetherFilterNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/glueTogetherFilterNotNull.kt"); + doTest(fileName); + } + @TestMetadata("ifContinue.kt") public void testIfContinue() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/ifContinue.kt"); @@ -8463,6 +8481,24 @@ public class IntentionTestGenerated extends AbstractIntentionTest { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/mergeMultiple.kt"); doTest(fileName); } + + @TestMetadata("splitOutFilterIsInstance.kt") + public void testSplitOutFilterIsInstance() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/splitOutFilterIsInstance.kt"); + doTest(fileName); + } + + @TestMetadata("splitOutFilterNotNull.kt") + public void testSplitOutFilterNotNull() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull.kt"); + doTest(fileName); + } + + @TestMetadata("splitOutFilterNotNull2.kt") + public void testSplitOutFilterNotNull2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/splitOutFilterNotNull2.kt"); + doTest(fileName); + } } @TestMetadata("idea/testData/intentions/loopToCallChain/firstOrNull") @@ -8752,6 +8788,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("KT14303.kt") + public void testKT14303() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/indexOf/KT14303.kt"); + doTest(fileName); + } + @TestMetadata("lastIndexOf.kt") public void testLastIndexOf() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/indexOf/lastIndexOf.kt");