diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml
index cd4328259e0..4470e47ac4f 100644
--- a/idea/resources/META-INF/plugin-common.xml
+++ b/idea/resources/META-INF/plugin-common.xml
@@ -3577,6 +3577,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/resources/META-INF/plugin-common.xml.191 b/idea/resources/META-INF/plugin-common.xml.191
index 117f26f7f53..5d0061af27b 100644
--- a/idea/resources/META-INF/plugin-common.xml.191
+++ b/idea/resources/META-INF/plugin-common.xml.191
@@ -3578,6 +3578,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/resources/META-INF/plugin-common.xml.192 b/idea/resources/META-INF/plugin-common.xml.192
index 117f26f7f53..5d0061af27b 100644
--- a/idea/resources/META-INF/plugin-common.xml.192
+++ b/idea/resources/META-INF/plugin-common.xml.192
@@ -3578,6 +3578,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/resources/inspectionDescriptions/RedundantElvisReturnNull.html b/idea/resources/inspectionDescriptions/RedundantElvisReturnNull.html
new file mode 100644
index 00000000000..bbdee967e37
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/RedundantElvisReturnNull.html
@@ -0,0 +1,15 @@
+
+
+This inspection reports redundant '?: return null':
+
+
+fun foo(): Int? {
+ ...
+}
+
+fun test() : Int? {
+ return foo() ?: return null
+}
+
+
+
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantElvisReturnNullInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantElvisReturnNullInspection.kt
new file mode 100644
index 00000000000..86f26c2bae2
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantElvisReturnNullInspection.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.inspections
+
+import com.intellij.codeInspection.LocalQuickFix
+import com.intellij.codeInspection.ProblemDescriptor
+import com.intellij.codeInspection.ProblemHighlightType
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElementVisitor
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+class RedundantElvisReturnNullInspection : AbstractKotlinInspection() {
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
+ return returnExpressionVisitor(fun(returnExpression: KtReturnExpression) {
+ if ((returnExpression.returnedExpression?.deparenthesize() as? KtConstantExpression)?.text != KtTokens.NULL_KEYWORD.value) return
+
+ val binaryExpression = returnExpression.getStrictParentOfType()?.takeIf {
+ it == it.getStrictParentOfType()?.returnedExpression?.deparenthesize()
+ } ?: return
+ val right = binaryExpression.right?.deparenthesize()?.takeIf { it == returnExpression } ?: return
+ if (binaryExpression.operationToken == KtTokens.ELSE_KEYWORD) return
+ if (binaryExpression.left?.resolveToCall()?.resultingDescriptor?.returnType?.isMarkedNullable != true) return
+
+ holder.registerProblem(
+ binaryExpression,
+ "Redundant '?: return null'",
+ ProblemHighlightType.LIKE_UNUSED_SYMBOL,
+ TextRange(binaryExpression.operationReference.startOffset, right.endOffset).shiftLeft(binaryExpression.startOffset),
+ RemoveRedundantElvisReturnNull()
+ )
+ })
+ }
+
+ private fun KtExpression.deparenthesize() = KtPsiUtil.deparenthesize(this)
+
+ private class RemoveRedundantElvisReturnNull : LocalQuickFix {
+ override fun getName() = "Remove redundant '?: return null'"
+
+ override fun getFamilyName() = name
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val binaryExpression = descriptor.psiElement as? KtBinaryExpression ?: return
+ val left = binaryExpression.left ?: return
+ binaryExpression.replace(left)
+ }
+ }
+}
diff --git a/idea/testData/inspectionsLocal/redundantElvisReturnNull/.inspection b/idea/testData/inspectionsLocal/redundantElvisReturnNull/.inspection
new file mode 100644
index 00000000000..4d7799343fd
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantElvisReturnNull/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.RedundantElvisReturnNullInspection
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantElvisReturnNull/basic.kt b/idea/testData/inspectionsLocal/redundantElvisReturnNull/basic.kt
new file mode 100644
index 00000000000..9243f7ada54
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantElvisReturnNull/basic.kt
@@ -0,0 +1,5 @@
+fun foo(): Int? = null
+
+fun test() : Int? {
+ return foo() ?: return null
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantElvisReturnNull/basic.kt.after b/idea/testData/inspectionsLocal/redundantElvisReturnNull/basic.kt.after
new file mode 100644
index 00000000000..c7e497d726d
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantElvisReturnNull/basic.kt.after
@@ -0,0 +1,5 @@
+fun foo(): Int? = null
+
+fun test() : Int? {
+ return foo()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantElvisReturnNull/notInReturn.kt b/idea/testData/inspectionsLocal/redundantElvisReturnNull/notInReturn.kt
new file mode 100644
index 00000000000..e2db13b2b96
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantElvisReturnNull/notInReturn.kt
@@ -0,0 +1,7 @@
+// PROBLEM: none
+fun foo(): Int? = null
+
+fun test() : Int? {
+ val i = foo() ?: return null
+ return 0
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantElvisReturnNull/notReturnNull.kt b/idea/testData/inspectionsLocal/redundantElvisReturnNull/notReturnNull.kt
new file mode 100644
index 00000000000..c0a886c1d2f
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantElvisReturnNull/notReturnNull.kt
@@ -0,0 +1,6 @@
+// PROBLEM: none
+fun foo(): Int? = null
+
+fun test() : Int? {
+ return foo() ?: return 1
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantElvisReturnNull/uselessElvis.kt b/idea/testData/inspectionsLocal/redundantElvisReturnNull/uselessElvis.kt
new file mode 100644
index 00000000000..088d355bfc7
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantElvisReturnNull/uselessElvis.kt
@@ -0,0 +1,6 @@
+// PROBLEM: none
+fun bar(): Int = 1
+
+fun test() : Int? {
+ return bar() ?: return null // USELESS_ELVIS
+}
\ 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 6e50cc2804d..5a4a9754de4 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -7144,6 +7144,39 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
+ @TestMetadata("idea/testData/inspectionsLocal/redundantElvisReturnNull")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class RedundantElvisReturnNull extends AbstractLocalInspectionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInRedundantElvisReturnNull() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/redundantElvisReturnNull"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), true);
+ }
+
+ @TestMetadata("basic.kt")
+ public void testBasic() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantElvisReturnNull/basic.kt");
+ }
+
+ @TestMetadata("notInReturn.kt")
+ public void testNotInReturn() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantElvisReturnNull/notInReturn.kt");
+ }
+
+ @TestMetadata("notReturnNull.kt")
+ public void testNotReturnNull() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantElvisReturnNull/notReturnNull.kt");
+ }
+
+ @TestMetadata("uselessElvis.kt")
+ public void testUselessElvis() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantElvisReturnNull/uselessElvis.kt");
+ }
+ }
+
@TestMetadata("idea/testData/inspectionsLocal/redundantEmptyInitializerBlock")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)