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 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 { when {
valueIfFound.isVariableReference(workingVariable) -> { valueIfFound.isVariableReference(workingVariable) -> {
val stdlibFunName = if (findFirst) "firstOrNull" else "lastOrNull" val stdlibFunName = if (findFirst) "firstOrNull" else "lastOrNull"
val nullIfNotFound = valueIfNotFound.isNullExpression() val generator = { chainedCallGenerator: ChainedCallGenerator, filter: KtExpression? ->
generateChainedCall(stdlibFunName, chainedCallGenerator, filter)
// 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)
} }
return generator.useElvisOperatorIfNeeded()
} }
valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> { valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> {
@@ -151,7 +155,6 @@ fun buildFindOperationGenerator(
} }
workingVariable.hasUsages(valueIfFound) -> { workingVariable.hasUsages(valueIfFound) -> {
if (!valueIfNotFound.isNullExpression()) return null //TODO
if (!findFirst) return null // too dangerous because of side effects 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>" // 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 receiver = qualifiedExpression.receiverExpression
val selector = qualifiedExpression.selectorExpression val selector = qualifiedExpression.selectorExpression
if (receiver.isVariableReference(workingVariable) && selector != null && !workingVariable.hasUsages(selector)) { if (receiver.isVariableReference(workingVariable) && selector != null && !workingVariable.hasUsages(selector)) {
return { chainedCallGenerator, filter -> return { chainedCallGenerator: ChainedCallGenerator, filter: KtExpression? ->
val findFirstCall = generateChainedCall("firstOrNull", chainedCallGenerator, filter) val findFirstCall = generateChainedCall("firstOrNull", chainedCallGenerator, filter)
KtPsiFactory(findFirstCall).createExpressionByPattern("$0?.$1", findFirstCall, selector) 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 // 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 if (workingVariableCanHoldNull) return null
return { chainedCallGenerator, filter -> return { chainedCallGenerator: ChainedCallGenerator, filter: KtExpression? ->
val findFirstCall = generateChainedCall("firstOrNull", chainedCallGenerator, filter) val findFirstCall = generateChainedCall("firstOrNull", chainedCallGenerator, filter)
val letBody = generateLambda(workingVariable, valueIfFound) val letBody = generateLambda(workingVariable, valueIfFound)
KtPsiFactory(findFirstCall).createExpressionByPattern("$0?.let $1:'{}'", findFirstCall, letBody) KtPsiFactory(findFirstCall).createExpressionByPattern("$0?.let $1:'{}'", findFirstCall, letBody)
} }.useElvisOperatorIfNeeded()
} }
else -> { 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
}