More cases supported for "any"

This commit is contained in:
Valentin Kipyatkov
2016-04-06 12:00:35 +03:00
parent c0f3ab59ab
commit fba9b123dd
6 changed files with 74 additions and 26 deletions
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
class FindAndAssignTransformation(
private val loop: KtForExpression,
override val inputVariable: KtCallableDeclaration,
private val stdlibFunName: String,
private val generator: (chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?) -> KtExpression,
private val initialDeclaration: KtProperty,
private val filter: KtExpression? = null
) : ResultTransformation {
@@ -35,20 +35,14 @@ class FindAndAssignTransformation(
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): ResultTransformation? {
if (previousTransformation !is FilterTransformation) return null
assert(filter == null) { "Should not happen because no 2 consecutive FilterTransformation's possible"}
return FindAndAssignTransformation(loop, previousTransformation.inputVariable, stdlibFunName, initialDeclaration, previousTransformation.buildRealCondition())
return FindAndAssignTransformation(loop, previousTransformation.inputVariable, generator, initialDeclaration, previousTransformation.buildRealCondition())
}
override val commentSavingRange = PsiChildRange(initialDeclaration, loop.unwrapIfLabeled())
override val commentRestoringRange = commentSavingRange.withoutLastStatement()
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
return if (filter == null) {
chainedCallGenerator.generate("$stdlibFunName()")
}
else {
val lambda = generateLambda(inputVariable, filter)
chainedCallGenerator.generate("$stdlibFunName $0:'{}'", lambda)
}
return generator(chainedCallGenerator, filter)
}
override fun convertLoop(resultCallChain: KtExpression): KtExpression {
@@ -107,9 +101,9 @@ class FindAndAssignTransformation(
val usageCountInLoop = ReferencesSearch.search(declarationBeforeLoop, LocalSearchScope(state.outerLoop)).count()
if (usageCountInLoop != 1) return null // this should be the only usage of this variable inside the loop
val stdlibFunName = stdlibFunNameForFind(right, initializer, state.workingVariable, findFirst) ?: return null
val generator = buildFindOperationGenerator(right, initializer, state.workingVariable, findFirst) ?: return null
val transformation = FindAndAssignTransformation(state.outerLoop, state.workingVariable, stdlibFunName, declarationBeforeLoop)
val transformation = FindAndAssignTransformation(state.outerLoop, state.workingVariable, generator, declarationBeforeLoop)
return ResultTransformationMatch(transformation)
}
}
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
class FindAndReturnTransformation(
private val loop: KtForExpression,
override val inputVariable: KtCallableDeclaration,
private val stdlibFunName: String,
private val generator: (chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?) -> KtExpression,
private val endReturn: KtReturnExpression,
private val filter: KtExpression? = null
) : ResultTransformation {
@@ -35,7 +35,7 @@ class FindAndReturnTransformation(
override fun mergeWithPrevious(previousTransformation: SequenceTransformation): ResultTransformation? {
if (previousTransformation !is FilterTransformation) return null
assert(filter == null) { "Should not happen because no 2 consecutive FilterTransformation's possible"}
return FindAndReturnTransformation(loop, previousTransformation.inputVariable, stdlibFunName, endReturn, previousTransformation.buildRealCondition())
return FindAndReturnTransformation(loop, previousTransformation.inputVariable, generator, endReturn, previousTransformation.buildRealCondition())
}
override val commentSavingRange = PsiChildRange(loop.unwrapIfLabeled(), endReturn)
@@ -43,13 +43,7 @@ class FindAndReturnTransformation(
override val commentRestoringRange = commentSavingRange.withoutFirstStatement()
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
return if (filter == null) {
chainedCallGenerator.generate("$stdlibFunName()")
}
else {
val lambda = generateLambda(inputVariable, filter)
chainedCallGenerator.generate("$stdlibFunName $0:'{}'", lambda)
}
return generator(chainedCallGenerator, filter)
}
override fun convertLoop(resultCallChain: KtExpression): KtExpression {
@@ -78,9 +72,9 @@ class FindAndReturnTransformation(
val returnValueInLoop = returnInLoop.returnedExpression ?: return null
val returnValueAfterLoop = returnAfterLoop.returnedExpression ?: return null
val stdlibFunName = stdlibFunNameForFind(returnValueInLoop, returnValueAfterLoop, state.workingVariable, findFirst = true) ?: return null
val generator = buildFindOperationGenerator(returnValueInLoop, returnValueAfterLoop, state.workingVariable, findFirst = true) ?: return null
val transformation = FindAndReturnTransformation(state.outerLoop, state.workingVariable, stdlibFunName, returnAfterLoop)
val transformation = FindAndReturnTransformation(state.outerLoop, state.workingVariable, generator, returnAfterLoop)
return ResultTransformationMatch(transformation)
}
}
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
@@ -95,16 +96,48 @@ fun KtProperty.hasWriteUsages(): Boolean {
}
}
fun stdlibFunNameForFind(valueIfFound: KtExpression, valueIfNotFound: KtExpression, workingVariable: KtCallableDeclaration, findFirst: Boolean): String? {
return when {
valueIfNotFound.isNullExpression() && valueIfFound.isVariableReference(workingVariable) -> if (findFirst) "firstOrNull" else "lastOrNull"
fun buildFindOperationGenerator(
valueIfFound: KtExpression,
valueIfNotFound: KtExpression,
workingVariable: KtCallableDeclaration,
findFirst: Boolean
): ((chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?) -> KtExpression)? {
assert(valueIfFound.isPhysical)
assert(valueIfNotFound.isPhysical)
fun generateChainedCall(stdlibFunName: String, chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?): KtExpression {
return if (filter == null) {
chainedCallGenerator.generate("$stdlibFunName()")
}
else {
val lambda = generateLambda(workingVariable, filter)
chainedCallGenerator.generate("$stdlibFunName $0:'{}'", lambda)
}
}
val stdlibFunName = when {
valueIfNotFound.isNullExpression() && valueIfFound.isVariableReference(workingVariable) -> if (findFirst) "firstOrNull" else "lastOrNull" //TODO: ?: if not null
valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> "any"
valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> "none"
else -> /*TODO: allow other constants*/ null
workingVariable.hasUsages(listOf(valueIfFound)) -> /*TODO*/ return null
// initial value is compile-time constant
ConstantExpressionEvaluator.getConstant(valueIfNotFound, valueIfNotFound.analyze(BodyResolveMode.PARTIAL)) != null -> {
return { chainedCallGenerator, filter ->
val chainedCall = generateChainedCall("any", chainedCallGenerator, filter)
KtPsiFactory(chainedCall).createExpressionByPattern("if ($0) $1 else $2", chainedCall, valueIfFound, valueIfNotFound)
}
}
else -> return null
}
return { chainedCallGenerator, filter -> generateChainedCall(stdlibFunName, chainedCallGenerator, filter) }
}
fun KtExpressionWithLabel.isBreakOrContinueOfLoop(loop: KtLoopExpression): Boolean {
@@ -0,0 +1,10 @@
// WITH_RUNTIME
fun foo(list: List<String>) {
var result = 0
<caret>for (s in list) {
if (s.length > 0) {
result = 1
break
}
}
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun foo(list: List<String>) {
<caret>val result = if (list.any { it.length > 0 }) 1 else 0
}
@@ -0,0 +1,13 @@
// WITH_RUNTIME
// IS_APPLICABLE: false
fun foo(list: List<String>) {
var result = takeInt()
<caret>for (s in list) {
if (s.length > 0) {
result = 1
break
}
}
}
fun takeInt(): Int = 0