Split or glue together checks for not null and is instance + more intelligent use of filter vs filterTo
#KT-14284 Fixed #KT-14286 Fixed #KT-14287 Fixed #KT-14303 Fixed
This commit is contained in:
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
|||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
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.core.ShortenReferences
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
@@ -164,8 +163,8 @@ private fun KtExpression.specialNegation(): KtExpression? {
|
|||||||
if (operationReference.getReferencedName() == "!") {
|
if (operationReference.getReferencedName() == "!") {
|
||||||
val baseExpression = baseExpression
|
val baseExpression = baseExpression
|
||||||
if (baseExpression != null) {
|
if (baseExpression != null) {
|
||||||
val context = baseExpression.analyzeAndGetResult().bindingContext
|
val bindingContext = baseExpression.analyze(BodyResolveMode.PARTIAL)
|
||||||
val type = context.getType(baseExpression)
|
val type = bindingContext.getType(baseExpression)
|
||||||
if (type != null && KotlinBuiltIns.isBoolean(type)) {
|
if (type != null && KotlinBuiltIns.isBoolean(type)) {
|
||||||
return KtPsiUtil.safeDeparenthesize(baseExpression)
|
return KtPsiUtil.safeDeparenthesize(baseExpression)
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-16
@@ -22,11 +22,7 @@ import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
|||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FilterNotNullTransformation
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.*
|
||||||
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.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -41,7 +37,9 @@ class AddToCollectionTransformation(
|
|||||||
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): ResultTransformation? {
|
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): ResultTransformation? {
|
||||||
return when (previousTransformation) {
|
return when (previousTransformation) {
|
||||||
is FilterTransformation -> {
|
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 -> {
|
is FilterNotNullTransformation -> {
|
||||||
@@ -201,15 +199,19 @@ class FilterToTransformation private constructor(
|
|||||||
private val inputVariable: KtCallableDeclaration,
|
private val inputVariable: KtCallableDeclaration,
|
||||||
private val indexVariable: KtCallableDeclaration?,
|
private val indexVariable: KtCallableDeclaration?,
|
||||||
private val targetCollection: KtExpression,
|
private val targetCollection: KtExpression,
|
||||||
private val condition: KtExpression,
|
private val effectiveCondition: Condition,
|
||||||
private val isInverse: Boolean
|
private val isFilterNot: Boolean
|
||||||
) : ReplaceLoopResultTransformation(loop) {
|
) : ReplaceLoopResultTransformation(loop) {
|
||||||
|
|
||||||
private fun effectiveCondition() = if (isInverse) condition.negate() else condition
|
init {
|
||||||
|
if (isFilterNot) {
|
||||||
|
assert(indexVariable == null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val functionName = when {
|
private val functionName = when {
|
||||||
indexVariable != null -> "filterIndexedTo"
|
indexVariable != null -> "filterIndexedTo"
|
||||||
isInverse -> "filterNotTo"
|
isFilterNot -> "filterNotTo"
|
||||||
else -> "filterTo"
|
else -> "filterTo"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,9 +220,9 @@ class FilterToTransformation private constructor(
|
|||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val lambda = if (indexVariable != null)
|
val lambda = if (indexVariable != null)
|
||||||
generateLambda(inputVariable, indexVariable, effectiveCondition())
|
generateLambda(inputVariable, indexVariable, effectiveCondition.asExpression())
|
||||||
else
|
else
|
||||||
generateLambda(inputVariable, condition)
|
generateLambda(inputVariable, if (isFilterNot) effectiveCondition.asNegatedExpression() else effectiveCondition.asExpression())
|
||||||
return chainedCallGenerator.generate("$functionName($0) $1:'{}'", targetCollection, lambda)
|
return chainedCallGenerator.generate("$functionName($0) $1:'{}'", targetCollection, lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,16 +232,16 @@ class FilterToTransformation private constructor(
|
|||||||
inputVariable: KtCallableDeclaration,
|
inputVariable: KtCallableDeclaration,
|
||||||
indexVariable: KtCallableDeclaration?,
|
indexVariable: KtCallableDeclaration?,
|
||||||
targetCollection: KtExpression,
|
targetCollection: KtExpression,
|
||||||
condition: KtExpression,
|
condition: Condition,
|
||||||
isInverse: Boolean
|
isFilterNot: Boolean
|
||||||
): ResultTransformation {
|
): ResultTransformation {
|
||||||
val initialization = targetCollection.findVariableInitializationBeforeLoop(loop, checkNoOtherUsagesInLoop = true)
|
val initialization = targetCollection.findVariableInitializationBeforeLoop(loop, checkNoOtherUsagesInLoop = true)
|
||||||
if (initialization != null && initialization.initializer.hasNoSideEffect()) {
|
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)
|
return AssignToVariableResultTransformation.createDelegated(transformation, initialization)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return FilterToTransformation(loop, inputVariable, indexVariable, targetCollection, condition, isInverse)
|
return FilterToTransformation(loop, inputVariable, indexVariable, targetCollection, condition, isFilterNot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -34,9 +34,9 @@ class CountTransformation(
|
|||||||
if (previousTransformation !is FilterTransformationBase) return null
|
if (previousTransformation !is FilterTransformationBase) return null
|
||||||
if (previousTransformation.indexVariable != null) return null
|
if (previousTransformation.indexVariable != null) return null
|
||||||
val newFilter = if (filter == null)
|
val newFilter = if (filter == null)
|
||||||
previousTransformation.effectiveCondition
|
previousTransformation.effectiveCondition.asExpression()
|
||||||
else
|
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)
|
return CountTransformation(loop, previousTransformation.inputVariable, initialization, newFilter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -225,7 +225,7 @@ object FindTransformationMatcher : TransformationMatcher {
|
|||||||
assert(valueIfFound.isPhysical)
|
assert(valueIfFound.isPhysical)
|
||||||
assert(valueIfNotFound.isPhysical)
|
assert(valueIfNotFound.isPhysical)
|
||||||
|
|
||||||
val filter = filterTransformation?.effectiveCondition
|
val filter = filterTransformation?.effectiveCondition?.asExpression()
|
||||||
|
|
||||||
if (indexVariable != null) {
|
if (indexVariable != null) {
|
||||||
if (filterTransformation == null) return null // makes no sense, indexVariable must be always null
|
if (filterTransformation == null) return null // makes no sense, indexVariable must be always null
|
||||||
|
|||||||
@@ -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<AtomicCondition>
|
||||||
|
|
||||||
|
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<AtomicCondition>) : 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<AtomicCondition>): Condition {
|
||||||
|
return conditions.singleOrNull() ?: CompositeCondition(conditions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Condition.hasUsagesOf(variable: KtCallableDeclaration): Boolean {
|
||||||
|
return toAtomicConditions().any { variable.hasUsages(it.expression) }
|
||||||
|
}
|
||||||
|
|
||||||
+121
-61
@@ -16,17 +16,25 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence
|
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.branchedTransformations.isNullExpression
|
||||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.result.FindTransformationMatcher
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.result.FindTransformationMatcher
|
||||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.result.MaxOrMinTransformation
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.result.MaxOrMinTransformation
|
||||||
import org.jetbrains.kotlin.idea.intentions.negate
|
import org.jetbrains.kotlin.idea.intentions.negate
|
||||||
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.blockExpressionsOrSingle
|
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 class FilterTransformationBase : SequenceTransformation {
|
||||||
abstract val effectiveCondition: KtExpression
|
abstract val effectiveCondition: Condition
|
||||||
|
|
||||||
abstract val inputVariable: KtCallableDeclaration
|
abstract val inputVariable: KtCallableDeclaration
|
||||||
abstract val indexVariable: KtCallableDeclaration?
|
abstract val indexVariable: KtCallableDeclaration?
|
||||||
|
|
||||||
@@ -66,31 +74,75 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
var (transformation, currentState) = matchOneTransformation(state) ?: return null
|
var (transformation, currentState) = matchOneTransformation(state) ?: return null
|
||||||
assert(currentState.indexVariable == state.indexVariable) // indexVariable should not change
|
assert(currentState.indexVariable == state.indexVariable) // indexVariable should not change
|
||||||
|
|
||||||
if (transformation is FilterTransformation) {
|
if (transformation !is FilterTransformationBase) {
|
||||||
while (true) {
|
return TransformationMatch.Sequence(transformation, currentState)
|
||||||
currentState = currentState.unwrapBlock()
|
}
|
||||||
|
|
||||||
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 (MaxOrMinTransformation.Matcher.match(currentState) != null) break // do not take 'if' which is required for min/max matcher
|
||||||
if (nextTransformation !is FilterTransformation) break
|
|
||||||
assert(nextState.indexVariable == currentState.indexVariable) // indexVariable should not change
|
|
||||||
|
|
||||||
val indexVariable = transformation.indexVariable ?: nextTransformation.indexVariable
|
val (nextTransformation, nextState) = matchOneTransformation(currentState) ?: break
|
||||||
val mergedCondition = KtPsiFactory(state.outerLoop).createExpressionByPattern(
|
if (nextTransformation !is FilterTransformationBase) break
|
||||||
"$0 && $1", transformation.effectiveCondition, nextTransformation.effectiveCondition)
|
assert(nextState.indexVariable == currentState.indexVariable) // indexVariable should not change
|
||||||
transformation = FilterTransformation(state.outerLoop, transformation.inputVariable, indexVariable, mergedCondition, isInverse = false) //TODO: build filterNot in some cases?
|
|
||||||
currentState = nextState
|
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<AtomicCondition>,
|
||||||
|
restStatements: List<KtExpression>
|
||||||
|
): List<FilterTransformationBase> {
|
||||||
|
//TODO: indexVariable case
|
||||||
|
|
||||||
|
if (conditions.size == 1) {
|
||||||
|
return createFilterTransformation(loop, inputVariable, indexVariable, conditions.single()).singletonList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val transformations = ArrayList<FilterTransformationBase>()
|
||||||
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
return transformations
|
||||||
if (transformation is FilterTransformationBase) {
|
|
||||||
FindTransformationMatcher.matchWithFilterBefore(currentState, transformation)
|
|
||||||
?.let { return it }
|
|
||||||
}
|
|
||||||
|
|
||||||
return TransformationMatch.Sequence(transformation, currentState)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun matchOneTransformation(state: MatchingState): Pair<SequenceTransformation, MatchingState>? {
|
private fun matchOneTransformation(state: MatchingState): Pair<SequenceTransformation, MatchingState>? {
|
||||||
@@ -104,7 +156,7 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
if (!state.inputVariable.hasUsages(condition) && (state.indexVariable == null || !state.indexVariable.hasUsages(condition))) return null
|
if (!state.inputVariable.hasUsages(condition) && (state.indexVariable == null || !state.indexVariable.hasUsages(condition))) return null
|
||||||
|
|
||||||
if (state.statements.size == 1) {
|
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))
|
val newState = state.copy(statements = listOf(then))
|
||||||
return transformation to newState
|
return transformation to newState
|
||||||
}
|
}
|
||||||
@@ -113,7 +165,7 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
when (statement) {
|
when (statement) {
|
||||||
is KtContinueExpression -> {
|
is KtContinueExpression -> {
|
||||||
if (statement.targetLoop() != state.innerLoop) return null
|
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))
|
val newState = state.copy(statements = state.statements.drop(1))
|
||||||
return transformation to newState
|
return transformation to newState
|
||||||
}
|
}
|
||||||
@@ -130,40 +182,52 @@ abstract class FilterTransformationBase : SequenceTransformation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: choose filter or filterNot depending on condition
|
|
||||||
private fun createFilterTransformation(
|
private fun createFilterTransformation(
|
||||||
loop: KtForExpression,
|
loop: KtForExpression,
|
||||||
inputVariable: KtCallableDeclaration,
|
inputVariable: KtCallableDeclaration,
|
||||||
indexVariable: KtCallableDeclaration?,
|
indexVariable: KtCallableDeclaration?,
|
||||||
condition: KtExpression,
|
condition: Condition,
|
||||||
isInverse: Boolean
|
onlyFilterOrFilterNot: Boolean = false
|
||||||
): FilterTransformationBase {
|
): FilterTransformationBase {
|
||||||
|
|
||||||
val effectiveCondition = if (isInverse) condition.negate() else condition
|
if (indexVariable != null && condition.hasUsagesOf(indexVariable)) {
|
||||||
|
return FilterTransformation(loop, inputVariable, indexVariable, condition, isFilterNot = false)
|
||||||
if (indexVariable != null && indexVariable.hasUsages(condition)) {
|
|
||||||
return FilterTransformation(loop, inputVariable, indexVariable, effectiveCondition, isInverse = false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (effectiveCondition is KtIsExpression
|
val conditionAsExpression = condition.asExpression()
|
||||||
&& !effectiveCondition.isNegated
|
if (!onlyFilterOrFilterNot) {
|
||||||
&& effectiveCondition.leftHandSide.isSimpleName(inputVariable.nameAsSafeName) // we cannot use isVariableReference here because expression can be non-physical
|
if (conditionAsExpression is KtIsExpression
|
||||||
) {
|
&& !conditionAsExpression.isNegated
|
||||||
val typeRef = effectiveCondition.typeReference
|
&& conditionAsExpression.leftHandSide.isSimpleName(inputVariable.nameAsSafeName) // we cannot use isVariableReference here because expression can be non-physical
|
||||||
if (typeRef != null) {
|
) {
|
||||||
return FilterIsInstanceTransformation(loop, inputVariable, typeRef)
|
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
|
if (conditionAsExpression is KtPrefixExpression && conditionAsExpression.operationToken == KtTokens.EXCL) {
|
||||||
&& effectiveCondition.operationToken == KtTokens.EXCLEQ
|
return FilterTransformation(loop, inputVariable, null, condition, isFilterNot = true)
|
||||||
&& effectiveCondition.right.isNullExpression()
|
|
||||||
&& effectiveCondition.left.isSimpleName(inputVariable.nameAsSafeName)
|
|
||||||
) {
|
|
||||||
return FilterNotNullTransformation(loop, inputVariable)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FilterTransformation(loop, inputVariable, null, condition, isInverse)
|
return FilterTransformation(loop, inputVariable, null, condition, isFilterNot = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSmartCastUsed(inputVariable: KtCallableDeclaration, statements: List<KtExpression>): Boolean {
|
||||||
|
return statements.any { statement ->
|
||||||
|
statement.anyDescendantOfType<KtNameReferenceExpression> {
|
||||||
|
it.mainReference.resolve() == inputVariable && it.analyze(BodyResolveMode.PARTIAL)[BindingContext.SMARTCAST, it] != null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,17 +236,19 @@ class FilterTransformation(
|
|||||||
override val loop: KtForExpression,
|
override val loop: KtForExpression,
|
||||||
override val inputVariable: KtCallableDeclaration,
|
override val inputVariable: KtCallableDeclaration,
|
||||||
override val indexVariable: KtCallableDeclaration?,
|
override val indexVariable: KtCallableDeclaration?,
|
||||||
val condition: KtExpression,
|
override val effectiveCondition: Condition,
|
||||||
val isInverse: Boolean
|
val isFilterNot: Boolean
|
||||||
) : FilterTransformationBase() {
|
) : FilterTransformationBase() {
|
||||||
|
|
||||||
override val effectiveCondition: KtExpression by lazy {
|
init {
|
||||||
if (isInverse) condition.negate() else condition
|
if (isFilterNot) {
|
||||||
|
assert(indexVariable == null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val functionName = when {
|
private val functionName = when {
|
||||||
indexVariable != null -> "filterIndexed"
|
indexVariable != null -> "filterIndexed"
|
||||||
isInverse -> "filterNot"
|
isFilterNot -> "filterNot"
|
||||||
else -> "filter"
|
else -> "filter"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,9 +257,9 @@ class FilterTransformation(
|
|||||||
|
|
||||||
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
val lambda = if (indexVariable != null)
|
val lambda = if (indexVariable != null)
|
||||||
generateLambda(inputVariable, indexVariable, effectiveCondition)
|
generateLambda(inputVariable, indexVariable, effectiveCondition.asExpression())
|
||||||
else
|
else
|
||||||
generateLambda(inputVariable, condition)
|
generateLambda(inputVariable, if (isFilterNot) effectiveCondition.asNegatedExpression() else effectiveCondition.asExpression())
|
||||||
return chainedCallGenerator.generate("$0$1:'{}'", functionName, lambda)
|
return chainedCallGenerator.generate("$0$1:'{}'", functionName, lambda)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,13 +267,10 @@ class FilterTransformation(
|
|||||||
class FilterIsInstanceTransformation(
|
class FilterIsInstanceTransformation(
|
||||||
override val loop: KtForExpression,
|
override val loop: KtForExpression,
|
||||||
override val inputVariable: KtCallableDeclaration,
|
override val inputVariable: KtCallableDeclaration,
|
||||||
private val type: KtTypeReference
|
private val type: KtTypeReference,
|
||||||
|
override val effectiveCondition: Condition
|
||||||
) : FilterTransformationBase() {
|
) : FilterTransformationBase() {
|
||||||
|
|
||||||
override val effectiveCondition by lazy {
|
|
||||||
KtPsiFactory(loop).createExpressionByPattern("$0 is $1", inputVariable.nameAsSafeName, type)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val indexVariable: KtCallableDeclaration? get() = null
|
override val indexVariable: KtCallableDeclaration? get() = null
|
||||||
|
|
||||||
override val presentation: String
|
override val presentation: String
|
||||||
@@ -220,13 +283,10 @@ class FilterIsInstanceTransformation(
|
|||||||
|
|
||||||
class FilterNotNullTransformation(
|
class FilterNotNullTransformation(
|
||||||
override val loop: KtForExpression,
|
override val loop: KtForExpression,
|
||||||
override val inputVariable: KtCallableDeclaration
|
override val inputVariable: KtCallableDeclaration,
|
||||||
|
override val effectiveCondition: Condition
|
||||||
) : FilterTransformationBase() {
|
) : FilterTransformationBase() {
|
||||||
|
|
||||||
override val effectiveCondition: KtExpression by lazy {
|
|
||||||
KtPsiFactory(loop).createExpressionByPattern("$0 != null", inputVariable.nameAsSafeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val indexVariable: KtCallableDeclaration? get() = null
|
override val indexVariable: KtCallableDeclaration? get() = null
|
||||||
|
|
||||||
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): SequenceTransformation? {
|
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): SequenceTransformation? {
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filterIndexedTo(){}'"
|
||||||
|
// IS_APPLICABLE_2: false
|
||||||
|
fun foo(list: List<Any>, out: MutableList<Any>){
|
||||||
|
<caret>for ((i, any) in list.withIndex()) {
|
||||||
|
if (any is String && i % 2 == 0)
|
||||||
|
out.add(any)
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filterIndexedTo(){}'"
|
||||||
|
// IS_APPLICABLE_2: false
|
||||||
|
fun foo(list: List<Any>, out: MutableList<Any>){
|
||||||
|
list.filterIndexedTo(out) { i, any -> any is String && i % 2 == 0 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'"
|
||||||
|
fun foo(list: List<String?>){
|
||||||
|
<caret>for (l in list) {
|
||||||
|
if (l != null && l.startsWith("IMG:"))
|
||||||
|
println(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'"
|
||||||
|
fun foo(list: List<String?>){
|
||||||
|
list
|
||||||
|
.filter { it != null && it.startsWith("IMG:") }
|
||||||
|
.forEach { println(it) }
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'"
|
||||||
|
fun foo(list: List<String?>){
|
||||||
|
list
|
||||||
|
.asSequence()
|
||||||
|
.filter { it != null && it.startsWith("IMG:") }
|
||||||
|
.forEach { println(it) }
|
||||||
|
}
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// INTENTION_TEXT: "Replace with '...flatMap{}.filterNot{}.mapTo(){}'"
|
// INTENTION_TEXT: "Replace with '...flatMap{}.filter{}.mapTo(){}'"
|
||||||
// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filterNot{}.mapTo(){}'"
|
// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filter{}.mapTo(){}'"
|
||||||
fun foo(list: List<String>, target: MutableCollection<String>) {
|
fun foo(list: List<String>, target: MutableCollection<String>) {
|
||||||
var i = 0
|
var i = 0
|
||||||
<caret>for (s in list) {
|
<caret>for (s in list) {
|
||||||
|
|||||||
Vendored
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// INTENTION_TEXT: "Replace with '...flatMap{}.filterNot{}.mapTo(){}'"
|
// INTENTION_TEXT: "Replace with '...flatMap{}.filter{}.mapTo(){}'"
|
||||||
// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filterNot{}.mapTo(){}'"
|
// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filter{}.mapTo(){}'"
|
||||||
fun foo(list: List<String>, target: MutableCollection<String>) {
|
fun foo(list: List<String>, target: MutableCollection<String>) {
|
||||||
<caret>list
|
<caret>list
|
||||||
.filterIndexed { i, s -> i % 10 != 0 }
|
.filterIndexed { i, s -> i % 10 != 0 }
|
||||||
.flatMap { it.indices }
|
.flatMap { it.indices }
|
||||||
.filterNot { it == 10 }
|
.filter { it != 10 }
|
||||||
.mapTo(target) { it.toString() }
|
.mapTo(target) { it.toString() }
|
||||||
}
|
}
|
||||||
Vendored
+3
-3
@@ -1,11 +1,11 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// INTENTION_TEXT: "Replace with '...flatMap{}.filterNot{}.mapTo(){}'"
|
// INTENTION_TEXT: "Replace with '...flatMap{}.filter{}.mapTo(){}'"
|
||||||
// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filterNot{}.mapTo(){}'"
|
// INTENTION_TEXT_2: "Replace with 'asSequence()...flatMap{}.filter{}.mapTo(){}'"
|
||||||
fun foo(list: List<String>, target: MutableCollection<String>) {
|
fun foo(list: List<String>, target: MutableCollection<String>) {
|
||||||
<caret>list
|
<caret>list
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.filterIndexed { i, s -> i % 10 != 0 }
|
.filterIndexed { i, s -> i % 10 != 0 }
|
||||||
.flatMap { it.indices.asSequence() }
|
.flatMap { it.indices.asSequence() }
|
||||||
.filterNot { it == 10 }
|
.filter { it != 10 }
|
||||||
.mapTo(target) { it.toString() }
|
.mapTo(target) { it.toString() }
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
// IS_APPLICABLE_2: false
|
// IS_APPLICABLE_2: false
|
||||||
fun foo(list: List<String>, target: MutableList<String>) {
|
fun foo(list: List<String>, target: MutableList<String>) {
|
||||||
<caret>for (s in list) {
|
<caret>for (s in list) {
|
||||||
if (s.length == 0) continue
|
if (bar(s)) continue
|
||||||
target.add(s)
|
target.add(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun bar(string: String): Boolean = TODO()
|
||||||
@@ -2,5 +2,7 @@
|
|||||||
// INTENTION_TEXT: "Replace with 'filterNotTo(){}'"
|
// INTENTION_TEXT: "Replace with 'filterNotTo(){}'"
|
||||||
// IS_APPLICABLE_2: false
|
// IS_APPLICABLE_2: false
|
||||||
fun foo(list: List<String>, target: MutableList<String>) {
|
fun foo(list: List<String>, target: MutableList<String>) {
|
||||||
<caret>list.filterNotTo(target) { it.length == 0 }
|
list.filterNotTo(target) { bar(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun bar(string: String): Boolean = TODO()
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'"
|
||||||
|
fun foo(list: List<String?>){
|
||||||
|
<caret>for (l in list) {
|
||||||
|
if (l == null) continue
|
||||||
|
if (l.startsWith("IMG:"))
|
||||||
|
println(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'"
|
||||||
|
fun foo(list: List<String?>){
|
||||||
|
list
|
||||||
|
.filter { it != null && it.startsWith("IMG:") }
|
||||||
|
.forEach { println(it) }
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filter{}.forEach{}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().filter{}.forEach{}'"
|
||||||
|
fun foo(list: List<String?>){
|
||||||
|
list
|
||||||
|
.asSequence()
|
||||||
|
.filter { it != null && it.startsWith("IMG:") }
|
||||||
|
.forEach { println(it) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filterIsInstance<>().filterTo(){}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().filterIsInstance<>().filterTo(){}'"
|
||||||
|
fun foo(list: List<Any>, out: MutableList<String>){
|
||||||
|
<caret>for (any in list) {
|
||||||
|
if (any is String && any.length > 0)
|
||||||
|
out.add(any)
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filterIsInstance<>().filterTo(){}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().filterIsInstance<>().filterTo(){}'"
|
||||||
|
fun foo(list: List<Any>, out: MutableList<String>){
|
||||||
|
list
|
||||||
|
.filterIsInstance<String>()
|
||||||
|
.filterTo(out) { it.length > 0 }
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'filterIsInstance<>().filterTo(){}'"
|
||||||
|
// INTENTION_TEXT_2: "Replace with 'asSequence().filterIsInstance<>().filterTo(){}'"
|
||||||
|
fun foo(list: List<Any>, out: MutableList<String>){
|
||||||
|
list
|
||||||
|
.asSequence()
|
||||||
|
.filterIsInstance<String>()
|
||||||
|
.filterTo(out) { it.length > 0 }
|
||||||
|
}
|
||||||
@@ -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<String?>){
|
||||||
|
<caret>for (l in list) {
|
||||||
|
if (l != null && l.startsWith("IMG:"))
|
||||||
|
println(l.hashCode())
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -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<String?>){
|
||||||
|
list
|
||||||
|
.filterNotNull()
|
||||||
|
.filter { it.startsWith("IMG:") }
|
||||||
|
.forEach { println(it.hashCode()) }
|
||||||
|
}
|
||||||
+10
@@ -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<String?>){
|
||||||
|
list
|
||||||
|
.asSequence()
|
||||||
|
.filterNotNull()
|
||||||
|
.filter { it.startsWith("IMG:") }
|
||||||
|
.forEach { println(it.hashCode()) }
|
||||||
|
}
|
||||||
@@ -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<String?>){
|
||||||
|
<caret>for (l in list) {
|
||||||
|
if (l == null || !l.startsWith("IMG:")) continue
|
||||||
|
println(l.hashCode())
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -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<String?>){
|
||||||
|
list
|
||||||
|
.filterNotNull()
|
||||||
|
.filter { it.startsWith("IMG:") }
|
||||||
|
.forEach { println(it.hashCode()) }
|
||||||
|
}
|
||||||
+10
@@ -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<String?>){
|
||||||
|
list
|
||||||
|
.asSequence()
|
||||||
|
.filterNotNull()
|
||||||
|
.filter { it.startsWith("IMG:") }
|
||||||
|
.forEach { println(it.hashCode()) }
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// INTENTION_TEXT: "Replace with 'filterNotNull().firstOrNull{}'"
|
// INTENTION_TEXT: "Replace with 'firstOrNull{}'"
|
||||||
// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().firstOrNull{}'"
|
// IS_APPLICABLE_2: false
|
||||||
|
|
||||||
fun getFirstValue() = "value"
|
fun getFirstValue() = "value"
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// INTENTION_TEXT: "Replace with 'filterNotNull().firstOrNull{}'"
|
// INTENTION_TEXT: "Replace with 'firstOrNull{}'"
|
||||||
// INTENTION_TEXT_2: "Replace with 'asSequence().filterNotNull().firstOrNull{}'"
|
// IS_APPLICABLE_2: false
|
||||||
|
|
||||||
fun getFirstValue() = "value"
|
fun getFirstValue() = "value"
|
||||||
|
|
||||||
fun foo(list: List<String?>): String? {
|
fun foo(list: List<String?>): String? {
|
||||||
val value = getFirstValue()
|
val value = getFirstValue()
|
||||||
val <caret>found: String? = list
|
val found: String? = list.firstOrNull { it != null && !it.startsWith("IMG:") && it.contains(value) }
|
||||||
.filterNotNull()
|
|
||||||
.firstOrNull { !it.startsWith("IMG:") && it.contains(value) }
|
|
||||||
return found
|
return found
|
||||||
}
|
}
|
||||||
@@ -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?>): String? {
|
|
||||||
val value = getFirstValue()
|
|
||||||
val <caret>found: String? = list
|
|
||||||
.asSequence()
|
|
||||||
.filterNotNull()
|
|
||||||
.firstOrNull { !it.startsWith("IMG:") && it.contains(value) }
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'indexOfFirst{}'"
|
||||||
|
// IS_APPLICABLE_2: false
|
||||||
|
fun f8_indexOfFirst_complex(value: String, list: List<Any?>):Int{
|
||||||
|
<caret>for ((i, any) in list.withIndex())
|
||||||
|
if (any != null)
|
||||||
|
if (any is String)
|
||||||
|
if (any == value)
|
||||||
|
return i
|
||||||
|
return -1
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'indexOfFirst{}'"
|
||||||
|
// IS_APPLICABLE_2: false
|
||||||
|
fun f8_indexOfFirst_complex(value: String, list: List<Any?>):Int{
|
||||||
|
return list.indexOfFirst { it != null && it is String && it == value }
|
||||||
|
}
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// INTENTION_TEXT: "Replace with 'flatMap{}.filterNot{}.mapIndexedTo(){}'"
|
// INTENTION_TEXT: "Replace with 'flatMap{}.filter{}.mapIndexedTo(){}'"
|
||||||
// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filterNot{}.mapIndexedTo(){}'"
|
// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filter{}.mapIndexedTo(){}'"
|
||||||
fun foo(list: List<String>, target: MutableCollection<Int>) {
|
fun foo(list: List<String>, target: MutableCollection<Int>) {
|
||||||
var i = 0
|
var i = 0
|
||||||
<caret>for (s in list) {
|
<caret>for (s in list) {
|
||||||
|
|||||||
+3
-3
@@ -1,9 +1,9 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// INTENTION_TEXT: "Replace with 'flatMap{}.filterNot{}.mapIndexedTo(){}'"
|
// INTENTION_TEXT: "Replace with 'flatMap{}.filter{}.mapIndexedTo(){}'"
|
||||||
// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filterNot{}.mapIndexedTo(){}'"
|
// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filter{}.mapIndexedTo(){}'"
|
||||||
fun foo(list: List<String>, target: MutableCollection<Int>) {
|
fun foo(list: List<String>, target: MutableCollection<Int>) {
|
||||||
<caret>list
|
<caret>list
|
||||||
.flatMap { it.indices }
|
.flatMap { it.indices }
|
||||||
.filterNot { it == 10 }
|
.filter { it != 10 }
|
||||||
.mapIndexedTo(target) { i, j -> i + j }
|
.mapIndexedTo(target) { i, j -> i + j }
|
||||||
}
|
}
|
||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// INTENTION_TEXT: "Replace with 'flatMap{}.filterNot{}.mapIndexedTo(){}'"
|
// INTENTION_TEXT: "Replace with 'flatMap{}.filter{}.mapIndexedTo(){}'"
|
||||||
// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filterNot{}.mapIndexedTo(){}'"
|
// INTENTION_TEXT_2: "Replace with 'asSequence().flatMap{}.filter{}.mapIndexedTo(){}'"
|
||||||
fun foo(list: List<String>, target: MutableCollection<Int>) {
|
fun foo(list: List<String>, target: MutableCollection<Int>) {
|
||||||
<caret>list
|
<caret>list
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.flatMap { it.indices.asSequence() }
|
.flatMap { it.indices.asSequence() }
|
||||||
.filterNot { it == 10 }
|
.filter { it != 10 }
|
||||||
.mapIndexedTo(target) { i, j -> i + j }
|
.mapIndexedTo(target) { i, j -> i + j }
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// IS_APPLICABLE: false
|
// INTENTION_TEXT: "Replace with '...filter{}.map{}.firstOrNull()'"
|
||||||
// IS_APPLICABLE_2: false
|
// INTENTION_TEXT_2: "Replace with 'asSequence()...filter{}.map{}.firstOrNull()'"
|
||||||
fun foo(list: List<Any>, o: Any): Int? {
|
fun foo(list: List<Any>, o: Any): Int? {
|
||||||
<caret>for (s in list) {
|
<caret>for (s in list) {
|
||||||
if (s is String && s.length > 0) {
|
if (s is String && s.length > 0) {
|
||||||
|
|||||||
+10
@@ -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<Any>, o: Any): Int? {
|
||||||
|
return list
|
||||||
|
.filterIsInstance<String>()
|
||||||
|
.filter { it.length > 0 }
|
||||||
|
.map { it.length * 2 }
|
||||||
|
.firstOrNull()
|
||||||
|
}
|
||||||
+11
@@ -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<Any>, o: Any): Int? {
|
||||||
|
return list
|
||||||
|
.asSequence()
|
||||||
|
.filterIsInstance<String>()
|
||||||
|
.filter { it.length > 0 }
|
||||||
|
.map { it.length * 2 }
|
||||||
|
.firstOrNull()
|
||||||
|
}
|
||||||
@@ -358,6 +358,18 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("filterIndexed.kt")
|
||||||
public void testFilterIndexed() throws Exception {
|
public void testFilterIndexed() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/filterIndexed.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/filterIndexed.kt");
|
||||||
@@ -514,6 +526,12 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("ifContinue.kt")
|
||||||
public void testIfContinue() throws Exception {
|
public void testIfContinue() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/ifContinue.kt");
|
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");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/mergeMultiple.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("idea/testData/intentions/loopToCallChain/firstOrNull")
|
||||||
@@ -844,6 +880,12 @@ public class IntentionTest2Generated extends AbstractIntentionTest2 {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("lastIndexOf.kt")
|
||||||
public void testLastIndexOf() throws Exception {
|
public void testLastIndexOf() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/indexOf/lastIndexOf.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/indexOf/lastIndexOf.kt");
|
||||||
|
|||||||
@@ -8266,6 +8266,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("filterIndexed.kt")
|
||||||
public void testFilterIndexed() throws Exception {
|
public void testFilterIndexed() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/filterIndexed.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/filterIndexed.kt");
|
||||||
@@ -8422,6 +8434,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("ifContinue.kt")
|
||||||
public void testIfContinue() throws Exception {
|
public void testIfContinue() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/ifContinue.kt");
|
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");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/filter/mergeMultiple.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("idea/testData/intentions/loopToCallChain/firstOrNull")
|
||||||
@@ -8752,6 +8788,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("lastIndexOf.kt")
|
||||||
public void testLastIndexOf() throws Exception {
|
public void testLastIndexOf() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/indexOf/lastIndexOf.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/loopToCallChain/indexOf/lastIndexOf.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user