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
|
// returns false when interrupted by handler
|
||||||
fun traverseFollowingInstructions(
|
fun traverseFollowingInstructions(
|
||||||
rootInstruction: Instruction,
|
rootInstruction: Instruction,
|
||||||
visited: MutableSet<Instruction>,
|
visited: MutableSet<Instruction> = HashSet(),
|
||||||
order: TraversalOrder,
|
order: TraversalOrder = FORWARD,
|
||||||
// true to continue traversal
|
// true to continue traversal
|
||||||
handler: ((Instruction) -> TraverseInstructionResult)?
|
handler: ((Instruction) -> TraverseInstructionResult)?
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.cfg.pseudocode
|
package org.jetbrains.kotlin.cfg.pseudocode
|
||||||
|
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
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.LocalFunctionDeclarationInstruction
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction
|
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.SubroutineExitInstruction
|
||||||
@@ -51,4 +52,6 @@ interface Pseudocode {
|
|||||||
fun isSideEffectFree(instruction: Instruction): Boolean
|
fun isSideEffectFree(instruction: Instruction): Boolean
|
||||||
|
|
||||||
fun copy(): Pseudocode
|
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.SubroutineEnterInstruction
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction
|
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.BACKWARD
|
||||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD
|
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 {
|
class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode {
|
||||||
|
|
||||||
inner class PseudocodeLabel internal constructor(private val name: String, private val comment: String?) : Label {
|
inner class PseudocodeLabel internal constructor(private val name: String, private val comment: String?) : Label {
|
||||||
var targetInstructionIndex: Int? = null
|
var targetInstructionIndex: Int? = null
|
||||||
private set
|
private set
|
||||||
@@ -89,8 +86,8 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
|||||||
override val localDeclarations: Set<LocalFunctionDeclarationInstruction> by lazy {
|
override val localDeclarations: Set<LocalFunctionDeclarationInstruction> by lazy {
|
||||||
getLocalDeclarations(this)
|
getLocalDeclarations(this)
|
||||||
}
|
}
|
||||||
//todo getters
|
|
||||||
private val representativeInstructions = HashMap<KtElement, Instruction>()
|
private val representativeInstructions = HashMap<KtElement, KtElementInstruction>()
|
||||||
|
|
||||||
private val labels = ArrayList<PseudocodeLabel>()
|
private val labels = ArrayList<PseudocodeLabel>()
|
||||||
|
|
||||||
@@ -191,7 +188,9 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
|||||||
instruction.owner = this
|
instruction.owner = this
|
||||||
|
|
||||||
if (instruction is KtElementInstruction) {
|
if (instruction is KtElementInstruction) {
|
||||||
representativeInstructions.put(instruction.element, instruction)
|
if (!representativeInstructions.containsKey(instruction.element)) {
|
||||||
|
representativeInstructions.put(instruction.element, instruction)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instruction is MergeInstruction) {
|
if (instruction is MergeInstruction) {
|
||||||
@@ -379,6 +378,10 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun instructionForElement(element: KtElement): KtElementInstruction? {
|
||||||
|
return representativeInstructions[element]
|
||||||
|
}
|
||||||
|
|
||||||
private fun repeatWhole(originalPseudocode: PseudocodeImpl) {
|
private fun repeatWhole(originalPseudocode: PseudocodeImpl) {
|
||||||
repeatInternal(originalPseudocode, null, null, 0)
|
repeatInternal(originalPseudocode, null, null, 0)
|
||||||
parent = originalPseudocode.parent
|
parent = originalPseudocode.parent
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.intentions.loopToCallChain
|
|||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiWhiteSpace
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtForExpression
|
import org.jetbrains.kotlin.psi.KtForExpression
|
||||||
@@ -102,7 +103,8 @@ data class MatchingState(
|
|||||||
*/
|
*/
|
||||||
val indexVariable: KtCallableDeclaration?,
|
val indexVariable: KtCallableDeclaration?,
|
||||||
val initializationStatementsToDelete: Collection<KtExpression> = emptyList(),
|
val initializationStatementsToDelete: Collection<KtExpression> = emptyList(),
|
||||||
val previousTransformations: List<SequenceTransformation>
|
val previousTransformations: List<SequenceTransformation>,
|
||||||
|
val pseudocodeProvider: () -> Pseudocode
|
||||||
)
|
)
|
||||||
|
|
||||||
interface TransformationMatcher {
|
interface TransformationMatcher {
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ package org.jetbrains.kotlin.idea.intentions.loopToCallChain
|
|||||||
|
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
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.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
@@ -71,13 +74,25 @@ fun match(loop: KtForExpression): MatchResult? {
|
|||||||
val loopContainsEmbeddedBreakOrContinue = loop.containsEmbeddedBreakOrContinue()
|
val loopContainsEmbeddedBreakOrContinue = loop.containsEmbeddedBreakOrContinue()
|
||||||
|
|
||||||
val sequenceTransformations = ArrayList<SequenceTransformation>()
|
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(
|
var state = MatchingState(
|
||||||
outerLoop = loop,
|
outerLoop = loop,
|
||||||
innerLoop = loop,
|
innerLoop = loop,
|
||||||
statements = listOf(loop.body ?: return null),
|
statements = listOf(loop.body ?: return null),
|
||||||
inputVariable = inputVariable,
|
inputVariable = inputVariable,
|
||||||
indexVariable = indexVariable,
|
indexVariable = indexVariable,
|
||||||
previousTransformations = sequenceTransformations
|
previousTransformations = sequenceTransformations,
|
||||||
|
pseudocodeProvider = pseudocodeProvider
|
||||||
)
|
)
|
||||||
|
|
||||||
MatchLoop@
|
MatchLoop@
|
||||||
|
|||||||
+63
-12
@@ -16,28 +16,37 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence
|
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.idea.intentions.loopToCallChain.*
|
||||||
import org.jetbrains.kotlin.psi.KtConstantExpression
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtContinueExpression
|
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
object IntroduceIndexMatcher : TransformationMatcher {
|
object IntroduceIndexMatcher : TransformationMatcher {
|
||||||
override val indexVariableAllowed: Boolean
|
override val indexVariableAllowed: Boolean
|
||||||
get() = false // old index variable is still needed - cannot introduce another one
|
get() = false // old index variable is still needed - cannot introduce another one
|
||||||
|
|
||||||
override fun match(state: MatchingState): TransformationMatch.Sequence? {
|
override fun match(state: MatchingState): TransformationMatch.Sequence? {
|
||||||
if (state.statements.size < 2) return null
|
for (statement in state.statements) {
|
||||||
val operand = state.statements.last().isPlusPlusOf() ?: return null
|
val unaryExpressions = statement.collectDescendantsOfType<KtUnaryExpression>(
|
||||||
val restStatements = state.statements.dropLast(1)
|
canGoInside = { it !is KtBlockExpression && it !is KtFunction }
|
||||||
|
)
|
||||||
fun isContinueOfThisLoopOrOuter(continueExpression: KtContinueExpression): Boolean {
|
for (unaryExpression in unaryExpressions) {
|
||||||
val targetLoop = continueExpression.targetLoop() ?: return true
|
checkIndexCandidate(unaryExpression, state)?.let { return it }
|
||||||
return targetLoop.isAncestor(state.innerLoop, strict = false)
|
}
|
||||||
}
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
// there should be no continuation of the loop in statements before index increment
|
private fun checkIndexCandidate(incrementExpression: KtUnaryExpression, state: MatchingState): TransformationMatch.Sequence? {
|
||||||
if (restStatements.any { statement -> statement.anyDescendantOfType<KtContinueExpression>(::isContinueOfThisLoopOrOuter) }) return null
|
val operand = incrementExpression.isPlusPlusOf() ?: return null
|
||||||
|
|
||||||
val variableInitialization = operand.isVariableInitializedBeforeLoop(state.outerLoop, checkNoOtherUsagesInLoop = false)
|
val variableInitialization = operand.isVariableInitializedBeforeLoop(state.outerLoop, checkNoOtherUsagesInLoop = false)
|
||||||
?: return null
|
?: 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
|
//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
|
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,
|
val newState = state.copy(statements = restStatements,
|
||||||
indexVariable = variable,
|
indexVariable = variable,
|
||||||
initializationStatementsToDelete = state.initializationStatementsToDelete + variableInitialization.initializationStatement)
|
initializationStatementsToDelete = state.initializationStatementsToDelete + variableInitialization.initializationStatement)
|
||||||
return TransformationMatch.Sequence(emptyList(), newState)
|
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
|
if (isItUsedInside) return lambdaExpression
|
||||||
|
|
||||||
val resolutionScope = inputVariable.getResolutionScope(inputVariable.analyze(BodyResolveMode.FULL), inputVariable.getResolutionFacade())
|
val usages = lambdaExpression.findParameterUsages(lambdaExpression.valueParameters.single(), inputVariable)
|
||||||
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 itExpr = psiFactory.createSimpleName("it")
|
val itExpr = psiFactory.createSimpleName("it")
|
||||||
for (usage in usages) {
|
for (usage in usages) {
|
||||||
@@ -72,6 +66,34 @@ fun generateLambda(inputVariable: KtCallableDeclaration, expression: KtExpressio
|
|||||||
return psiFactory.createExpressionByPattern("{ $0 }", lambdaExpression.bodyExpression!!) as KtLambdaExpression
|
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 {
|
fun generateLambda(expression: KtExpression, vararg inputVariables: KtCallableDeclaration): KtLambdaExpression {
|
||||||
return KtPsiFactory(expression).buildExpression {
|
return KtPsiFactory(expression).buildExpression {
|
||||||
appendFixedText("{")
|
appendFixedText("{")
|
||||||
@@ -91,6 +113,19 @@ fun generateLambda(expression: KtExpression, vararg inputVariables: KtCallableDe
|
|||||||
} as KtLambdaExpression
|
} 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(
|
data class VariableInitialization(
|
||||||
val variable: KtProperty,
|
val variable: KtProperty,
|
||||||
val initializationStatement: KtExpression,
|
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.KotlinNameSuggester
|
||||||
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
|
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
|
||||||
import org.jetbrains.kotlin.idea.core.compareDescriptors
|
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.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.ErrorMessage
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.Status
|
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.Status
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.*
|
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.*
|
||||||
@@ -106,7 +106,7 @@ private fun List<Instruction>.getVarDescriptorsAccessedAfterwards(bindingContext
|
|||||||
val visitedInstructions = HashSet<Instruction>()
|
val visitedInstructions = HashSet<Instruction>()
|
||||||
|
|
||||||
fun doTraversal(instruction: Instruction) {
|
fun doTraversal(instruction: Instruction) {
|
||||||
traverseFollowingInstructions(instruction, visitedInstructions, TraversalOrder.FORWARD) {
|
traverseFollowingInstructions(instruction, visitedInstructions) {
|
||||||
when {
|
when {
|
||||||
it is AccessValueInstruction && it !in this ->
|
it is AccessValueInstruction && it !in this ->
|
||||||
PseudocodeUtil.extractVariableDescriptorIfAny(it, false, bindingContext)?.let { accessedAfterwards.add(it) }
|
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) {
|
<caret>for (s in list) {
|
||||||
if (s.isBlank()) continue
|
if (s.isBlank()) continue
|
||||||
val x = s.length * index
|
val x = s.length * index
|
||||||
if (x > 0) return x
|
|
||||||
index++
|
index++
|
||||||
|
if (x > 0) return x
|
||||||
}
|
}
|
||||||
return null
|
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