From bd6b38962d1a8891512c3faa0fd93f6cde2473f7 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Wed, 6 Apr 2016 13:07:45 +0300 Subject: [PATCH] Elvis operator supported for all cases of find --- .../idea/intentions/loopToCallChain/utils.kt | 35 ++++++++++--------- .../firstOrNull_letOrNotNull.kt | 12 +++++++ .../firstOrNull_letOrNotNull.kt.after | 6 ++++ .../firstOrNull_returnExpressionOrNotNull.kt | 9 +++++ ...tOrNull_returnExpressionOrNotNull.kt.after | 4 +++ 5 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 idea/testData/intentions/loopToCallChain/firstOrNull_letOrNotNull.kt create mode 100644 idea/testData/intentions/loopToCallChain/firstOrNull_letOrNotNull.kt.after create mode 100644 idea/testData/intentions/loopToCallChain/firstOrNull_returnExpressionOrNotNull.kt create mode 100644 idea/testData/intentions/loopToCallChain/firstOrNull_returnExpressionOrNotNull.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/utils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/utils.kt index 0baf4d14b70..462baa60b3f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/utils.kt @@ -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 "." or "?." @@ -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 -> { diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull_letOrNotNull.kt b/idea/testData/intentions/loopToCallChain/firstOrNull_letOrNotNull.kt new file mode 100644 index 00000000000..de30a83dc59 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/firstOrNull_letOrNotNull.kt @@ -0,0 +1,12 @@ +// WITH_RUNTIME +fun foo(list: List) { + var result = "" + for (s in list) { + if (s.length > 0) { + result = bar(s) + break + } + } +} + +fun bar(s: String): String = s diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull_letOrNotNull.kt.after b/idea/testData/intentions/loopToCallChain/firstOrNull_letOrNotNull.kt.after new file mode 100644 index 00000000000..024e429a018 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/firstOrNull_letOrNotNull.kt.after @@ -0,0 +1,6 @@ +// WITH_RUNTIME +fun foo(list: List) { + val result = list.firstOrNull { it.length > 0 }?.let { bar(it) } ?: "" +} + +fun bar(s: String): String = s diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull_returnExpressionOrNotNull.kt b/idea/testData/intentions/loopToCallChain/firstOrNull_returnExpressionOrNotNull.kt new file mode 100644 index 00000000000..b992bdc7995 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/firstOrNull_returnExpressionOrNotNull.kt @@ -0,0 +1,9 @@ +// WITH_RUNTIME +fun foo(list: List): Int { + for (s in list) { + if (s.isNotEmpty()) { + return s.length + } + } + return -1 +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull_returnExpressionOrNotNull.kt.after b/idea/testData/intentions/loopToCallChain/firstOrNull_returnExpressionOrNotNull.kt.after new file mode 100644 index 00000000000..d51bb1d9876 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/firstOrNull_returnExpressionOrNotNull.kt.after @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun foo(list: List): Int { + return list.firstOrNull { it.isNotEmpty() }?.length ?: -1 +} \ No newline at end of file