diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml
index b52f5a8201a..83bdc03a458 100644
--- a/idea/resources/META-INF/plugin-common.xml
+++ b/idea/resources/META-INF/plugin-common.xml
@@ -3248,6 +3248,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/resources/inspectionDescriptions/RedundantRequireNotNullCall.html b/idea/resources/inspectionDescriptions/RedundantRequireNotNullCall.html
new file mode 100644
index 00000000000..989194a2afc
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/RedundantRequireNotNullCall.html
@@ -0,0 +1,11 @@
+
+
This inspection reports redundant requireNotNull or checkNotNull call:
+
+
+fun foo(i: Int) {
+ requireNotNull(i) // This 'i' is always not null, so this 'requireNotNull' call is redundant.
+}
+
+
+
+
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantRequireNotNullCallInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantRequireNotNullCallInspection.kt
new file mode 100644
index 00000000000..cacf8ae28da
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantRequireNotNullCallInspection.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. 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 org.jetbrains.kotlin.descriptors.CallableDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.analyze
+import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
+import org.jetbrains.kotlin.idea.inspections.collections.isCalling
+import org.jetbrains.kotlin.idea.project.languageVersionSettings
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.KtReferenceExpression
+import org.jetbrains.kotlin.psi.callExpressionVisitor
+import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
+import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
+import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
+import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
+import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
+import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
+import org.jetbrains.kotlin.types.KotlinType
+import org.jetbrains.kotlin.types.isNullable
+
+class RedundantRequireNotNullCallInspection : AbstractKotlinInspection() {
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = callExpressionVisitor(fun(callExpression) {
+ val callee = callExpression.calleeExpression ?: return
+ val context = callExpression.analyze(BodyResolveMode.PARTIAL)
+ if (!callExpression.isCalling(FqName("kotlin.requireNotNull"), context)
+ && !callExpression.isCalling(FqName("kotlin.checkNotNull"), context)
+ ) return
+
+ val argument = callExpression.valueArguments.firstOrNull()?.getArgumentExpression()?.referenceExpression() ?: return
+ val descriptor = argument.getResolvedCall(context)?.resultingDescriptor ?: return
+ val type = descriptor.returnType ?: return
+ if (argument.isNullable(descriptor, type, context)) return
+
+ val functionName = callee.text
+ holder.registerProblem(
+ callee,
+ "Redundant '$functionName' call",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ RemoveRequireNotNullCallFix(functionName)
+ )
+ })
+
+ private fun KtReferenceExpression.isNullable(descriptor: CallableDescriptor, type: KotlinType, context: BindingContext): Boolean {
+ if (!type.isNullable()) return false
+ val dataFlowValueFactory = this.getResolutionFacade().getFrontendService(DataFlowValueFactory::class.java)
+ val dataFlow = dataFlowValueFactory.createDataFlowValue(this, type, context, descriptor)
+ val stableTypes = context.getDataFlowInfoBefore(this).getStableTypes(dataFlow, this.languageVersionSettings)
+ return stableTypes.none { !it.isNullable() }
+ }
+}
+
+private class RemoveRequireNotNullCallFix(private val functionName: String) : LocalQuickFix {
+ override fun getName() = "Remove '$functionName' call"
+
+ override fun getFamilyName() = name
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val callExpression = descriptor.psiElement.getStrictParentOfType() ?: return
+ val qualifiedExpression = callExpression.getQualifiedExpressionForSelector()
+ (qualifiedExpression ?: callExpression).delete()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/.inspection b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/.inspection
new file mode 100644
index 00000000000..dbb0ce71354
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.RedundantRequireNotNullCallInspection
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/checkNotNull.kt b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/checkNotNull.kt
new file mode 100644
index 00000000000..0b21f827230
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/checkNotNull.kt
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+fun test(s: String) {
+ println(1)
+ kotlin.checkNotNull(s)
+ println(2)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/checkNotNull.kt.after b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/checkNotNull.kt.after
new file mode 100644
index 00000000000..9e26962c0ce
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/checkNotNull.kt.after
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+fun test(s: String) {
+ println(1)
+ println(2)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull.kt b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull.kt
new file mode 100644
index 00000000000..0aeb1d72ae9
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull.kt
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+fun test(s: String) {
+ println(1)
+ requireNotNull(s)
+ println(2)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull.kt.after b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull.kt.after
new file mode 100644
index 00000000000..9e26962c0ce
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull.kt.after
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+fun test(s: String) {
+ println(1)
+ println(2)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull2.kt b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull2.kt
new file mode 100644
index 00000000000..1f608d7ff21
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull2.kt
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+fun test(s: String?) {
+ if (s != null) {
+ println(1)
+ requireNotNull(s) { "" }
+ println(2)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull2.kt.after b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull2.kt.after
new file mode 100644
index 00000000000..05ecbff68ff
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull2.kt.after
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+fun test(s: String?) {
+ if (s != null) {
+ println(1)
+ println(2)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull3.kt b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull3.kt
new file mode 100644
index 00000000000..7a73ac5aaa1
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull3.kt
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+fun test(s: String?) {
+ requireNotNull(s)
+ requireNotNull(s)
+ println(1)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull3.kt.after b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull3.kt.after
new file mode 100644
index 00000000000..4b9f7246171
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull3.kt.after
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+fun test(s: String?) {
+ requireNotNull(s)
+ println(1)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/nullable.kt b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/nullable.kt
new file mode 100644
index 00000000000..3a14d124cd7
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/nullable.kt
@@ -0,0 +1,5 @@
+// PROBLEM: none
+// WITH_RUNTIME
+fun test(s: String?) {
+ requireNotNull(s)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/nullable2.kt b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/nullable2.kt
new file mode 100644
index 00000000000..8f8ee26ada0
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/nullable2.kt
@@ -0,0 +1,11 @@
+// PROBLEM: none
+// WITH_RUNTIME
+class Test {
+ var s: String? = null
+
+ fun test() {
+ if (s != null) {
+ requireNotNull(s)
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/redundantRequireNotNullCall/require.kt b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/require.kt
new file mode 100644
index 00000000000..12cce5ff2ab
--- /dev/null
+++ b/idea/testData/inspectionsLocal/redundantRequireNotNullCall/require.kt
@@ -0,0 +1,5 @@
+// PROBLEM: none
+// WITH_RUNTIME
+fun test(b: Boolean) {
+ require(b)
+}
\ 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 2a1de9df46c..b146948826e 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -5206,6 +5206,54 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
+ @TestMetadata("idea/testData/inspectionsLocal/redundantRequireNotNullCall")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class RedundantRequireNotNullCall extends AbstractLocalInspectionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInRedundantRequireNotNullCall() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/redundantRequireNotNullCall"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("checkNotNull.kt")
+ public void testCheckNotNull() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantRequireNotNullCall/checkNotNull.kt");
+ }
+
+ @TestMetadata("notNull.kt")
+ public void testNotNull() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull.kt");
+ }
+
+ @TestMetadata("notNull2.kt")
+ public void testNotNull2() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull2.kt");
+ }
+
+ @TestMetadata("notNull3.kt")
+ public void testNotNull3() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantRequireNotNullCall/notNull3.kt");
+ }
+
+ @TestMetadata("nullable.kt")
+ public void testNullable() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantRequireNotNullCall/nullable.kt");
+ }
+
+ @TestMetadata("nullable2.kt")
+ public void testNullable2() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantRequireNotNullCall/nullable2.kt");
+ }
+
+ @TestMetadata("require.kt")
+ public void testRequire() throws Exception {
+ runTest("idea/testData/inspectionsLocal/redundantRequireNotNullCall/require.kt");
+ }
+ }
+
@TestMetadata("idea/testData/inspectionsLocal/redundantReturnLabel")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)