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 dd448aba77c..0baf4d14b70 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/loopToCallChain/utils.kt @@ -123,13 +123,32 @@ fun buildFindOperationGenerator( } } + val workingVariableCanHoldNull = (workingVariable.resolveToDescriptor() as VariableDescriptor).type.nullability() != TypeNullability.NOT_NULL - val stdlibFunName = when { - valueIfNotFound.isNullExpression() && valueIfFound.isVariableReference(workingVariable) -> if (findFirst) "firstOrNull" else "lastOrNull" //TODO: ?: if not null + when { + valueIfFound.isVariableReference(workingVariable) -> { + val stdlibFunName = if (findFirst) "firstOrNull" else "lastOrNull" + val nullIfNotFound = valueIfNotFound.isNullExpression() - valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> "any" + // we cannot use ?: if found value can be null + if (!nullIfNotFound && workingVariableCanHoldNull) return null - valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> "none" + return { chainedCallGenerator, filter -> + val chainedCall = generateChainedCall(stdlibFunName, chainedCallGenerator, filter) + if (nullIfNotFound) + chainedCall + else + KtPsiFactory(chainedCall).createExpressionByPattern("$0 ?: $1", chainedCall, valueIfNotFound) + } + } + + valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> { + return { chainedCallGenerator, filter -> generateChainedCall("any", chainedCallGenerator, filter) } + } + + valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> { + return { chainedCallGenerator, filter -> generateChainedCall("none", chainedCallGenerator, filter) } + } workingVariable.hasUsages(valueIfFound) -> { if (!valueIfNotFound.isNullExpression()) return null //TODO @@ -149,7 +168,7 @@ fun buildFindOperationGenerator( } // in case of nullable working variable we cannot distinguish by the result of "firstOrNull" whether nothing was found or 'null' was found - if ((workingVariable.resolveToDescriptor() as VariableDescriptor).type.nullability() != TypeNullability.NOT_NULL) return null + if (workingVariableCanHoldNull) return null return { chainedCallGenerator, filter -> val findFirstCall = generateChainedCall("firstOrNull", chainedCallGenerator, filter) @@ -163,11 +182,8 @@ fun buildFindOperationGenerator( val chainedCall = generateChainedCall("any", chainedCallGenerator, filter) KtPsiFactory(chainedCall).createExpressionByPattern("if ($0) $1 else $2", chainedCall, valueIfFound, valueIfNotFound) } - } } - - return { chainedCallGenerator, filter -> generateChainedCall(stdlibFunName, chainedCallGenerator, filter) } } fun KtExpressionWithLabel.isBreakOrContinueOfLoop(loop: KtLoopExpression): Boolean { @@ -184,13 +200,13 @@ fun KtExpressionWithLabel.isBreakOrContinueOfLoop(loop: KtLoopExpression): Boole } fun KtExpression.previousStatement(): KtExpression? { - var statement = unwrapIfLabeled() + val statement = unwrapIfLabeled() if (statement.parent !is KtBlockExpression) return null return statement.siblings(forward = false, withItself = false).firstIsInstanceOrNull() } fun KtExpression.nextStatement(): KtExpression? { - var statement = unwrapIfLabeled() + val statement = unwrapIfLabeled() if (statement.parent !is KtBlockExpression) return null return statement.siblings(forward = true, withItself = false).firstIsInstanceOrNull() } diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull_returnNotNullIfNone.kt b/idea/testData/intentions/loopToCallChain/firstOrNull_returnNotNullIfNone.kt index a2ff283ea35..1e2abf7f26e 100644 --- a/idea/testData/intentions/loopToCallChain/firstOrNull_returnNotNullIfNone.kt +++ b/idea/testData/intentions/loopToCallChain/firstOrNull_returnNotNullIfNone.kt @@ -1,5 +1,4 @@ // WITH_RUNTIME -// IS_APPLICABLE: false fun foo(list: List): String { for (s in list) { if (s.isNotEmpty()) { diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull_returnNotNullIfNone.kt.after b/idea/testData/intentions/loopToCallChain/firstOrNull_returnNotNullIfNone.kt.after new file mode 100644 index 00000000000..39bae0eee04 --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/firstOrNull_returnNotNullIfNone.kt.after @@ -0,0 +1,4 @@ +// WITH_RUNTIME +fun foo(list: List): String { + return list.firstOrNull { it.isNotEmpty() } ?: "" +} \ No newline at end of file diff --git a/idea/testData/intentions/loopToCallChain/firstOrNull_returnNotNullIfNone2.kt b/idea/testData/intentions/loopToCallChain/firstOrNull_returnNotNullIfNone2.kt new file mode 100644 index 00000000000..ad0e7e72f0d --- /dev/null +++ b/idea/testData/intentions/loopToCallChain/firstOrNull_returnNotNullIfNone2.kt @@ -0,0 +1,10 @@ +// WITH_RUNTIME +// IS_APPLICABLE: false +fun foo(list: List): String? { + for (s in list) { + if (s == null || s.isNotEmpty()) { + return s + } + } + return "" +} \ No newline at end of file