Elvis operator supported for all cases of find

This commit is contained in:
Valentin Kipyatkov
2016-04-06 13:07:45 +03:00
parent 79800492a9
commit bd6b38962d
5 changed files with 50 additions and 16 deletions
@@ -125,21 +125,25 @@ fun buildFindOperationGenerator(
val workingVariableCanHoldNull = (workingVariable.resolveToDescriptor() as VariableDescriptor).type.nullability() != TypeNullability.NOT_NULL
fun ((chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?) -> KtExpression).useElvisOperatorIfNeeded(): ((chainedCallGenerator: ChainedCallGenerator, filter: KtExpression?) -> KtExpression)? {
if (valueIfNotFound.isNullExpression()) return this
// we cannot use ?: if found value can be null
if (workingVariableCanHoldNull) return null
return { chainedCallGenerator, filter ->
val generated = this(chainedCallGenerator, filter)
KtPsiFactory(generated).createExpressionByPattern("$0 ?: $1", generated, valueIfNotFound)
}
}
when {
valueIfFound.isVariableReference(workingVariable) -> {
val stdlibFunName = if (findFirst) "firstOrNull" else "lastOrNull"
val nullIfNotFound = valueIfNotFound.isNullExpression()
// we cannot use ?: if found value can be null
if (!nullIfNotFound && workingVariableCanHoldNull) return null
return { chainedCallGenerator, filter ->
val chainedCall = generateChainedCall(stdlibFunName, chainedCallGenerator, filter)
if (nullIfNotFound)
chainedCall
else
KtPsiFactory(chainedCall).createExpressionByPattern("$0 ?: $1", chainedCall, valueIfNotFound)
val generator = { chainedCallGenerator: ChainedCallGenerator, filter: KtExpression? ->
generateChainedCall(stdlibFunName, chainedCallGenerator, filter)
}
return generator.useElvisOperatorIfNeeded()
}
valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> {
@@ -151,7 +155,6 @@ fun buildFindOperationGenerator(
}
workingVariable.hasUsages(valueIfFound) -> {
if (!valueIfNotFound.isNullExpression()) return null //TODO
if (!findFirst) return null // too dangerous because of side effects
// specially handle the case when the result expression is "<working variable>.<some call>" or "<working variable>?.<some call>"
@@ -160,21 +163,21 @@ fun buildFindOperationGenerator(
val receiver = qualifiedExpression.receiverExpression
val selector = qualifiedExpression.selectorExpression
if (receiver.isVariableReference(workingVariable) && selector != null && !workingVariable.hasUsages(selector)) {
return { chainedCallGenerator, filter ->
return { chainedCallGenerator: ChainedCallGenerator, filter: KtExpression? ->
val findFirstCall = generateChainedCall("firstOrNull", chainedCallGenerator, filter)
KtPsiFactory(findFirstCall).createExpressionByPattern("$0?.$1", findFirstCall, selector)
}
}.useElvisOperatorIfNeeded()
}
}
// in case of nullable working variable we cannot distinguish by the result of "firstOrNull" whether nothing was found or 'null' was found
if (workingVariableCanHoldNull) return null
return { chainedCallGenerator, filter ->
return { chainedCallGenerator: ChainedCallGenerator, filter: KtExpression? ->
val findFirstCall = generateChainedCall("firstOrNull", chainedCallGenerator, filter)
val letBody = generateLambda(workingVariable, valueIfFound)
KtPsiFactory(findFirstCall).createExpressionByPattern("$0?.let $1:'{}'", findFirstCall, letBody)
}
}.useElvisOperatorIfNeeded()
}
else -> {
@@ -0,0 +1,12 @@
// WITH_RUNTIME
fun foo(list: List<String>) {
var result = ""
<caret>for (s in list) {
if (s.length > 0) {
result = bar(s)
break
}
}
}
fun bar(s: String): String = s
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun foo(list: List<String>) {
<caret>val result = list.firstOrNull { it.length > 0 }?.let { bar(it) } ?: ""
}
fun bar(s: String): String = s
@@ -0,0 +1,9 @@
// WITH_RUNTIME
fun foo(list: List<String>): Int {
<caret>for (s in list) {
if (s.isNotEmpty()) {
return s.length
}
}
return -1
}
@@ -0,0 +1,4 @@
// WITH_RUNTIME
fun foo(list: List<String>): Int {
<caret>return list.firstOrNull { it.isNotEmpty() }?.length ?: -1
}