Supported index++ pattern as non-last statement and even inside expressions
This commit is contained in:
@@ -181,8 +181,8 @@ enum class TraverseInstructionResult {
|
||||
// returns false when interrupted by handler
|
||||
fun traverseFollowingInstructions(
|
||||
rootInstruction: Instruction,
|
||||
visited: MutableSet<Instruction>,
|
||||
order: TraversalOrder,
|
||||
visited: MutableSet<Instruction> = HashSet(),
|
||||
order: TraversalOrder = FORWARD,
|
||||
// true to continue traversal
|
||||
handler: ((Instruction) -> TraverseInstructionResult)?
|
||||
): Boolean {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.cfg.pseudocode
|
||||
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.KtElementInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction
|
||||
@@ -51,4 +52,6 @@ interface Pseudocode {
|
||||
fun isSideEffectFree(instruction: Instruction): Boolean
|
||||
|
||||
fun copy(): Pseudocode
|
||||
|
||||
fun instructionForElement(element: KtElement): KtElementInstruction?
|
||||
}
|
||||
|
||||
@@ -30,17 +30,14 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDec
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.*
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraverseInstructionResult
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
import java.util.*
|
||||
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraverseInstructionResult
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverseFollowingInstructions
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import java.util.*
|
||||
|
||||
class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode {
|
||||
|
||||
inner class PseudocodeLabel internal constructor(private val name: String, private val comment: String?) : Label {
|
||||
var targetInstructionIndex: Int? = null
|
||||
private set
|
||||
@@ -89,8 +86,8 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
override val localDeclarations: Set<LocalFunctionDeclarationInstruction> by lazy {
|
||||
getLocalDeclarations(this)
|
||||
}
|
||||
//todo getters
|
||||
private val representativeInstructions = HashMap<KtElement, Instruction>()
|
||||
|
||||
private val representativeInstructions = HashMap<KtElement, KtElementInstruction>()
|
||||
|
||||
private val labels = ArrayList<PseudocodeLabel>()
|
||||
|
||||
@@ -191,7 +188,9 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
instruction.owner = this
|
||||
|
||||
if (instruction is KtElementInstruction) {
|
||||
representativeInstructions.put(instruction.element, instruction)
|
||||
if (!representativeInstructions.containsKey(instruction.element)) {
|
||||
representativeInstructions.put(instruction.element, instruction)
|
||||
}
|
||||
}
|
||||
|
||||
if (instruction is MergeInstruction) {
|
||||
@@ -379,6 +378,10 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
return result
|
||||
}
|
||||
|
||||
override fun instructionForElement(element: KtElement): KtElementInstruction? {
|
||||
return representativeInstructions[element]
|
||||
}
|
||||
|
||||
private fun repeatWhole(originalPseudocode: PseudocodeImpl) {
|
||||
repeatInternal(originalPseudocode, null, null, 0)
|
||||
parent = originalPseudocode.parent
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.intentions.loopToCallChain
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
@@ -102,7 +103,8 @@ data class MatchingState(
|
||||
*/
|
||||
val indexVariable: KtCallableDeclaration?,
|
||||
val initializationStatementsToDelete: Collection<KtExpression> = emptyList(),
|
||||
val previousTransformations: List<SequenceTransformation>
|
||||
val previousTransformations: List<SequenceTransformation>,
|
||||
val pseudocodeProvider: () -> Pseudocode
|
||||
)
|
||||
|
||||
interface TransformationMatcher {
|
||||
|
||||
@@ -18,6 +18,9 @@ package org.jetbrains.kotlin.idea.intentions.loopToCallChain
|
||||
|
||||
import com.intellij.openapi.util.Key
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
@@ -71,13 +74,25 @@ fun match(loop: KtForExpression): MatchResult? {
|
||||
val loopContainsEmbeddedBreakOrContinue = loop.containsEmbeddedBreakOrContinue()
|
||||
|
||||
val sequenceTransformations = ArrayList<SequenceTransformation>()
|
||||
|
||||
val pseudocodeProvider: () -> Pseudocode = object : () -> Pseudocode {
|
||||
val pseudocode: Pseudocode by lazy {
|
||||
val declaration = loop.containingDeclarationForPseudocode!!
|
||||
val bindingContext = loop.analyze(BodyResolveMode.FULL)
|
||||
PseudocodeUtil.generatePseudocode(declaration, bindingContext)
|
||||
}
|
||||
|
||||
override fun invoke() = pseudocode
|
||||
}
|
||||
|
||||
var state = MatchingState(
|
||||
outerLoop = loop,
|
||||
innerLoop = loop,
|
||||
statements = listOf(loop.body ?: return null),
|
||||
inputVariable = inputVariable,
|
||||
indexVariable = indexVariable,
|
||||
previousTransformations = sequenceTransformations
|
||||
previousTransformations = sequenceTransformations,
|
||||
pseudocodeProvider = pseudocodeProvider
|
||||
)
|
||||
|
||||
MatchLoop@
|
||||
|
||||
+63
-12
@@ -16,28 +16,37 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence
|
||||
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.ReadValueInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraverseInstructionResult
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverseFollowingInstructions
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||
import org.jetbrains.kotlin.psi.KtConstantExpression
|
||||
import org.jetbrains.kotlin.psi.KtContinueExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
import java.util.*
|
||||
|
||||
object IntroduceIndexMatcher : TransformationMatcher {
|
||||
override val indexVariableAllowed: Boolean
|
||||
get() = false // old index variable is still needed - cannot introduce another one
|
||||
|
||||
override fun match(state: MatchingState): TransformationMatch.Sequence? {
|
||||
if (state.statements.size < 2) return null
|
||||
val operand = state.statements.last().isPlusPlusOf() ?: return null
|
||||
val restStatements = state.statements.dropLast(1)
|
||||
|
||||
fun isContinueOfThisLoopOrOuter(continueExpression: KtContinueExpression): Boolean {
|
||||
val targetLoop = continueExpression.targetLoop() ?: return true
|
||||
return targetLoop.isAncestor(state.innerLoop, strict = false)
|
||||
for (statement in state.statements) {
|
||||
val unaryExpressions = statement.collectDescendantsOfType<KtUnaryExpression>(
|
||||
canGoInside = { it !is KtBlockExpression && it !is KtFunction }
|
||||
)
|
||||
for (unaryExpression in unaryExpressions) {
|
||||
checkIndexCandidate(unaryExpression, state)?.let { return it }
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// there should be no continuation of the loop in statements before index increment
|
||||
if (restStatements.any { statement -> statement.anyDescendantOfType<KtContinueExpression>(::isContinueOfThisLoopOrOuter) }) return null
|
||||
private fun checkIndexCandidate(incrementExpression: KtUnaryExpression, state: MatchingState): TransformationMatch.Sequence? {
|
||||
val operand = incrementExpression.isPlusPlusOf() ?: return null
|
||||
|
||||
val variableInitialization = operand.isVariableInitializedBeforeLoop(state.outerLoop, checkNoOtherUsagesInLoop = false)
|
||||
?: return null
|
||||
@@ -51,9 +60,51 @@ object IntroduceIndexMatcher : TransformationMatcher {
|
||||
//TODO: preform more precise analysis when variable can be used earlier or used later but value overwritten before that
|
||||
if (variable.countUsages() != variable.countUsages(state.statements + variableInitialization.initializationStatement)) return null
|
||||
|
||||
val pseudocode = state.pseudocodeProvider()
|
||||
val firstStatement = state.statements.first()
|
||||
val firstInstruction = pseudocode.instructionForElement(firstStatement)!!
|
||||
val incrementInstruction = pseudocode.instructionForElement(incrementExpression)!!
|
||||
if (!isAlwaysReachedOrExitedLoop(firstInstruction, incrementInstruction, state.outerLoop, state.innerLoop)) return null
|
||||
|
||||
val variableDescriptor = variable.resolveToDescriptor() as VariableDescriptor
|
||||
if (isAccessedAfter(variableDescriptor, incrementInstruction, state.innerLoop)) return null // index accessed inside loop after increment
|
||||
|
||||
val restStatements = state.statements - incrementExpression // if it is among statements then drop it, otherwise "index++" will be replaced with "index" by generateLambda()
|
||||
val newState = state.copy(statements = restStatements,
|
||||
indexVariable = variable,
|
||||
initializationStatementsToDelete = state.initializationStatementsToDelete + variableInitialization.initializationStatement)
|
||||
return TransformationMatch.Sequence(emptyList(), newState)
|
||||
}
|
||||
|
||||
private fun isAlwaysReachedOrExitedLoop(
|
||||
from: Instruction,
|
||||
to: Instruction,
|
||||
outerLoop: KtForExpression,
|
||||
innerLoop: KtForExpression
|
||||
): Boolean {
|
||||
val visited = HashSet<Instruction>()
|
||||
return traverseFollowingInstructions(from, visited) { instruction ->
|
||||
val nextInstructionScope = instruction.blockScope.block
|
||||
when {
|
||||
instruction == to -> TraverseInstructionResult.SKIP
|
||||
!outerLoop.isAncestor(nextInstructionScope, strict = false) -> TraverseInstructionResult.SKIP // we are out of the outer loop - it's ok
|
||||
!innerLoop.isAncestor(nextInstructionScope, strict = true) -> TraverseInstructionResult.HALT // we exited or continued inner loop
|
||||
else -> TraverseInstructionResult.CONTINUE
|
||||
}
|
||||
} && visited.contains(to)
|
||||
}
|
||||
|
||||
private fun isAccessedAfter(variableDescriptor: VariableDescriptor, instruction: Instruction, loop: KtForExpression): Boolean {
|
||||
return !traverseFollowingInstructions(instruction) { instruction ->
|
||||
when {
|
||||
!loop.isAncestor(instruction.blockScope.block, strict = true) -> TraverseInstructionResult.SKIP // we are outside the loop or going to the next iteration
|
||||
instruction.isReadOfVariable(variableDescriptor) -> TraverseInstructionResult.HALT
|
||||
else -> TraverseInstructionResult.CONTINUE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Instruction.isReadOfVariable(descriptor: VariableDescriptor): Boolean {
|
||||
return ((this as? ReadValueInstruction)?.target as? AccessTarget.Call)?.resolvedCall?.resultingDescriptor == descriptor
|
||||
}
|
||||
}
|
||||
@@ -53,13 +53,7 @@ fun generateLambda(inputVariable: KtCallableDeclaration, expression: KtExpressio
|
||||
|
||||
if (isItUsedInside) return lambdaExpression
|
||||
|
||||
val resolutionScope = inputVariable.getResolutionScope(inputVariable.analyze(BodyResolveMode.FULL), inputVariable.getResolutionFacade())
|
||||
val bindingContext = lambdaExpression.analyzeInContext(resolutionScope, contextExpression = inputVariable)
|
||||
val lambdaParam = lambdaExpression.valueParameters.single()
|
||||
val lambdaParamDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, lambdaParam]
|
||||
val usages = lambdaExpression.collectDescendantsOfType<KtNameReferenceExpression> {
|
||||
it.mainReference.resolveToDescriptors(bindingContext).singleOrNull() == lambdaParamDescriptor
|
||||
}
|
||||
val usages = lambdaExpression.findParameterUsages(lambdaExpression.valueParameters.single(), inputVariable)
|
||||
|
||||
val itExpr = psiFactory.createSimpleName("it")
|
||||
for (usage in usages) {
|
||||
@@ -72,6 +66,34 @@ fun generateLambda(inputVariable: KtCallableDeclaration, expression: KtExpressio
|
||||
return psiFactory.createExpressionByPattern("{ $0 }", lambdaExpression.bodyExpression!!) as KtLambdaExpression
|
||||
}
|
||||
|
||||
fun generateLambda(expression: KtExpression, indexVariable: KtCallableDeclaration, inputVariable: KtCallableDeclaration): KtLambdaExpression {
|
||||
val lambdaExpression = generateLambda(expression, *arrayOf(indexVariable, inputVariable))
|
||||
|
||||
// replace "index++" with "index" or "index + 1" (see IntroduceIndexMatcher)
|
||||
val indexPlusPlus = lambdaExpression.findDescendantOfType<KtUnaryExpression> { unaryExpression ->
|
||||
val operand = unaryExpression.isPlusPlusOf() as? KtNameReferenceExpression
|
||||
if (operand != null && operand.getReferencedName() == indexVariable.name) {
|
||||
val bindingContext = lambdaExpression.analyzeInContext(inputVariable)
|
||||
val parameter = lambdaExpression.valueParameters[0]
|
||||
val parameterDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, parameter]
|
||||
operand.mainReference.resolveToDescriptors(bindingContext).singleOrNull() == parameterDescriptor
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
if (indexPlusPlus != null) {
|
||||
val operand = indexPlusPlus.baseExpression!!
|
||||
val replacement = if (indexPlusPlus is KtPostfixExpression) // index++
|
||||
operand
|
||||
else // ++index
|
||||
KtPsiFactory(operand).createExpressionByPattern("$0 + 1", operand)
|
||||
indexPlusPlus.replace(replacement)
|
||||
}
|
||||
|
||||
return lambdaExpression
|
||||
}
|
||||
|
||||
fun generateLambda(expression: KtExpression, vararg inputVariables: KtCallableDeclaration): KtLambdaExpression {
|
||||
return KtPsiFactory(expression).buildExpression {
|
||||
appendFixedText("{")
|
||||
@@ -91,6 +113,19 @@ fun generateLambda(expression: KtExpression, vararg inputVariables: KtCallableDe
|
||||
} as KtLambdaExpression
|
||||
}
|
||||
|
||||
private fun KtLambdaExpression.findParameterUsages(lambdaParam: KtParameter, context: KtExpression): Collection<KtNameReferenceExpression> {
|
||||
val bindingContext = analyzeInContext(context)
|
||||
val lambdaParamDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, lambdaParam]
|
||||
return collectDescendantsOfType<KtNameReferenceExpression> {
|
||||
it.mainReference.resolveToDescriptors(bindingContext).singleOrNull() == lambdaParamDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtLambdaExpression.analyzeInContext(context: KtExpression): BindingContext {
|
||||
val resolutionScope = context.getResolutionScope(context.analyze(BodyResolveMode.FULL), context.getResolutionFacade())
|
||||
return analyzeInContext(resolutionScope, contextExpression = context)
|
||||
}
|
||||
|
||||
data class VariableInitialization(
|
||||
val variable: KtProperty,
|
||||
val initializationStatement: KtExpression,
|
||||
|
||||
+2
-2
@@ -46,8 +46,8 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
|
||||
import org.jetbrains.kotlin.idea.core.compareDescriptors
|
||||
import org.jetbrains.kotlin.idea.refactoring.createTempCopy
|
||||
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle
|
||||
import org.jetbrains.kotlin.idea.refactoring.createTempCopy
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.ErrorMessage
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.Status
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.*
|
||||
@@ -106,7 +106,7 @@ private fun List<Instruction>.getVarDescriptorsAccessedAfterwards(bindingContext
|
||||
val visitedInstructions = HashSet<Instruction>()
|
||||
|
||||
fun doTraversal(instruction: Instruction) {
|
||||
traverseFollowingInstructions(instruction, visitedInstructions, TraversalOrder.FORWARD) {
|
||||
traverseFollowingInstructions(instruction, visitedInstructions) {
|
||||
when {
|
||||
it is AccessValueInstruction && it !in this ->
|
||||
PseudocodeUtil.extractVariableDescriptorIfAny(it, false, bindingContext)?.let { accessedAfterwards.add(it) }
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'filterNot{}.mapIndexed{}.firstOrNull{}'"
|
||||
fun foo(list: List<String>): Int? {
|
||||
var index = 0
|
||||
<caret>for (s in list) {
|
||||
if (s.isBlank()) continue
|
||||
val x = s.length * index++
|
||||
if (x > 0) return x
|
||||
}
|
||||
return null
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'filterNot{}.mapIndexed{}.firstOrNull{}'"
|
||||
fun foo(list: List<String>): Int? {
|
||||
<caret>return list
|
||||
.filterNot { it.isBlank() }
|
||||
.mapIndexed { index, s -> s.length * index }
|
||||
.firstOrNull { it > 0 }
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
//TODO: should not be available without "asSequence()"!
|
||||
fun foo(list: List<String>): Int? {
|
||||
var index = 0
|
||||
<caret>for (s in list) {
|
||||
if (s.isBlank()) continue
|
||||
val x = s.length * index++
|
||||
if (x * 100 > index * index) return x
|
||||
}
|
||||
return null
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
//TODO: should not be available without "asSequence()"!
|
||||
fun foo(list: List<String>): Int? {
|
||||
var index = 0
|
||||
return list
|
||||
.filterNot { it.isBlank() }
|
||||
.map { it.length * index++ }
|
||||
.firstOrNull { it * 100 > index * index }
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with '...mapIndexed{}.mapIndexed{}.firstOrNull{}'"
|
||||
fun foo(list: List<String>): Int? {
|
||||
var index = 0
|
||||
<caret>for (s in list) {
|
||||
if (s.isBlank()) continue
|
||||
val x = s.length * index
|
||||
val y = x * index++
|
||||
if (y > 1000) return y
|
||||
}
|
||||
return null
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with '...mapIndexed{}.mapIndexed{}.firstOrNull{}'"
|
||||
fun foo(list: List<String>): Int? {
|
||||
<caret>return list
|
||||
.filterNot { it.isBlank() }
|
||||
.mapIndexed { index, s -> s.length * index }
|
||||
.mapIndexed { index, x -> x * index }
|
||||
.firstOrNull { it > 1000 }
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'mapIndexed{}.mapIndexed{}.firstOrNull{}'"
|
||||
fun foo(list: List<String>): Int? {
|
||||
var index = 0
|
||||
<caret>for (s in list) {
|
||||
val x = s.length * index
|
||||
val y = x * index++
|
||||
if (y > 1000) continue
|
||||
return y
|
||||
}
|
||||
return null
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'mapIndexed{}.mapIndexed{}.firstOrNull{}'"
|
||||
fun foo(list: List<String>): Int? {
|
||||
return list
|
||||
.mapIndexed { index, s -> s.length * index }
|
||||
.mapIndexed { index, x -> x * index }
|
||||
.firstOrNull { it <= 1000 }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'filterNot{}.mapIndexed{}.firstOrNull{}'"
|
||||
fun foo(list: List<String>): Int? {
|
||||
var index = 0
|
||||
<caret>for (s in list) {
|
||||
if (s.isBlank()) continue
|
||||
val x = s.length * index
|
||||
index++
|
||||
if (x > 0) return x
|
||||
}
|
||||
return null
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'filterNot{}.mapIndexed{}.firstOrNull{}'"
|
||||
fun foo(list: List<String>): Int? {
|
||||
<caret>return list
|
||||
.filterNot { it.isBlank() }
|
||||
.mapIndexed { index, s -> s.length * index }
|
||||
.firstOrNull { it > 0 }
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
// IS_APPLICABLE: false
|
||||
fun foo(list: List<String>): Int? {
|
||||
var index = 0
|
||||
<caret>for (s in list) {
|
||||
if (s.isBlank()) continue
|
||||
val x = s.length * index
|
||||
index++
|
||||
if ((x + index) % 3 == 0) return x
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -5,8 +5,8 @@ fun foo(list: List<String>): Int? {
|
||||
<caret>for (s in list) {
|
||||
if (s.isBlank()) continue
|
||||
val x = s.length * index
|
||||
if (x > 0) return x
|
||||
index++
|
||||
if (x > 0) return x
|
||||
}
|
||||
return null
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'filterNot{}.mapIndexed{}.firstOrNull{}'"
|
||||
fun foo(list: List<String>): Int? {
|
||||
var index = 0
|
||||
<caret>for (s in list) {
|
||||
if (s.isBlank()) continue
|
||||
val x = s.length * ++index
|
||||
if (x > 0) return x
|
||||
}
|
||||
return null
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// WITH_RUNTIME
|
||||
// INTENTION_TEXT: "Replace with 'filterNot{}.mapIndexed{}.firstOrNull{}'"
|
||||
fun foo(list: List<String>): Int? {
|
||||
<caret>return list
|
||||
.filterNot { it.isBlank() }
|
||||
.mapIndexed { index, s -> s.length * (index + 1) }
|
||||
.firstOrNull { it > 0 }
|
||||
}
|
||||
Reference in New Issue
Block a user