diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/FoldInitializerAndIfToElvisInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/FoldInitializerAndIfToElvisInspection.kt index 69e0ea30760..433bc1ef231 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/FoldInitializerAndIfToElvisInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/FoldInitializerAndIfToElvisInspection.kt @@ -29,14 +29,19 @@ import org.jetbrains.kotlin.idea.util.hasComments import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange +import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.siblings +import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.isError +import org.jetbrains.kotlin.types.isNullable import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.kotlin.utils.addToStdlib.safeAs class FoldInitializerAndIfToElvisInspection : AbstractApplicabilityBasedInspection(KtIfExpression::class.java) { override fun inspectionText(element: KtIfExpression): String = KotlinBundle.message("if.null.return.break.foldable.to") @@ -137,6 +142,15 @@ class FoldInitializerAndIfToElvisInspection : AbstractApplicabilityBasedInspecti val checkedType = ifExpression.analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, typeReference] val variableType = (prevStatement.resolveToDescriptorIfAny() as? VariableDescriptor)?.type if (checkedType != null && variableType != null && !checkedType.isSubtypeOf(variableType)) return null + } else if (prevStatement.isVar && operationExpression is KtBinaryExpression) { + val ifEndOffset = ifExpression.endOffset + val context = ifExpression.analyze() + val isUsedAsNotNullable = ReferencesSearch.search(prevStatement, LocalSearchScope(prevStatement.parent)).any { + if (it.element.startOffset <= ifEndOffset) return@any false + val type = it.element.safeAs()?.getType(context) ?: return@any false + !type.isNullable() + } + if (isUsedAsNotNullable) return null } val statement = if (then is KtBlockExpression) then.statements.singleOrNull() else then diff --git a/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNotNullable.kt b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNotNullable.kt new file mode 100644 index 00000000000..f3d46bcd50b --- /dev/null +++ b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNotNullable.kt @@ -0,0 +1,8 @@ +// PROBLEM: none +fun test(foo: Int?, bar: Int): Int { + var i = foo + if (i == null) { + return bar + } + return i +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNotNullable2.kt b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNotNullable2.kt new file mode 100644 index 00000000000..ffaa8d2f4b0 --- /dev/null +++ b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNotNullable2.kt @@ -0,0 +1,12 @@ +// PROBLEM: none +fun test(foo: Int?, bar: Int): Int { + var i = foo + if (i == null) { + return bar + } + baz(i) + val j = i + 1 + return j +} + +fun baz(i: Int?) {} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable.kt b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable.kt new file mode 100644 index 00000000000..e715affdf8c --- /dev/null +++ b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable.kt @@ -0,0 +1,7 @@ +fun test(foo: Int?, bar: Int): Int? { + var i = foo + if (i == null) { + return bar + } + return i +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable.kt.after b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable.kt.after new file mode 100644 index 00000000000..6ce67277e2f --- /dev/null +++ b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable.kt.after @@ -0,0 +1,4 @@ +fun test(foo: Int?, bar: Int): Int? { + var i: Int? = foo ?: return bar + return i +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable2.kt b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable2.kt new file mode 100644 index 00000000000..8e247c3e79f --- /dev/null +++ b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable2.kt @@ -0,0 +1,9 @@ +fun test(foo: Int?, bar: Int): Int { + var i = foo + if (i == null) { + return bar + } + return baz(i) +} + +fun baz(i: Int?) = 1 \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable2.kt.after b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable2.kt.after new file mode 100644 index 00000000000..63a3b275e72 --- /dev/null +++ b/idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable2.kt.after @@ -0,0 +1,6 @@ +fun test(foo: Int?, bar: Int): Int { + var i: Int? = foo ?: return bar + return baz(i) +} + +fun baz(i: Int?) = 1 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index c3331f8c3c7..bfeed2be4a9 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -4923,6 +4923,26 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { public void testVar() throws Exception { runTest("idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/Var.kt"); } + + @TestMetadata("VarUsedAsNotNullable.kt") + public void testVarUsedAsNotNullable() throws Exception { + runTest("idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNotNullable.kt"); + } + + @TestMetadata("VarUsedAsNotNullable2.kt") + public void testVarUsedAsNotNullable2() throws Exception { + runTest("idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNotNullable2.kt"); + } + + @TestMetadata("VarUsedAsNullable.kt") + public void testVarUsedAsNullable() throws Exception { + runTest("idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable.kt"); + } + + @TestMetadata("VarUsedAsNullable2.kt") + public void testVarUsedAsNullable2() throws Exception { + runTest("idea/testData/inspectionsLocal/foldInitializerAndIfToElvis/VarUsedAsNullable2.kt"); + } } @TestMetadata("idea/testData/inspectionsLocal/forEachParameterNotUsed")