diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml
index 2120f34ecf7..ca1191cd119 100644
--- a/idea/resources/META-INF/plugin-common.xml
+++ b/idea/resources/META-INF/plugin-common.xml
@@ -3279,6 +3279,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/resources/inspectionDescriptions/KotlinThrowableNotThrown.html b/idea/resources/inspectionDescriptions/KotlinThrowableNotThrown.html
new file mode 100644
index 00000000000..b749da5a4b1
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/KotlinThrowableNotThrown.html
@@ -0,0 +1,6 @@
+
+
+This inspection reports creating of Throwable and subclasses, where the created Throwable is never actually thrown.
+Also reports function calls which return Throwable instances, where the result of the function call is not thrown.
+
+
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinThrowableNotThrownInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinThrowableNotThrownInspection.kt
new file mode 100644
index 00000000000..fa55274019f
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinThrowableNotThrownInspection.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.ProblemHighlightType
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.psi.search.searches.ReferencesSearch
+import com.siyeh.ig.psiutils.TestUtils
+import org.jetbrains.kotlin.builtins.DefaultBuiltIns
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.analyze
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypes
+import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
+import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
+import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
+import org.jetbrains.kotlin.types.isNullable
+import org.jetbrains.kotlin.types.typeUtil.isNothing
+
+class KotlinThrowableNotThrownInspection : AbstractKotlinInspection() {
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = callExpressionVisitor(fun(callExpression) {
+ val calleeExpression = callExpression.calleeExpression ?: return
+ if (!calleeExpression.text.let { it.contains("Exception") || it.contains("Error") }) return
+ if (TestUtils.isInTestSourceContent(callExpression)) return
+ val resultingDescriptor = callExpression.resolveToCall()?.resultingDescriptor ?: return
+ val type = resultingDescriptor.returnType ?: return
+ if (type.isNothing() || type.isNullable()) return
+ val classDescriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return
+ if (!classDescriptor.isSubclassOf(DefaultBuiltIns.Instance.throwable)) return
+ if (callExpression.isUsed()) return
+
+ val description = if (resultingDescriptor is ConstructorDescriptor) {
+ "Throwable instance '${calleeExpression.text}' is not thrown"
+ } else {
+ "Result of '${calleeExpression.text}' call is not thrown"
+ }
+ holder.registerProblem(calleeExpression, description, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
+ })
+
+ private fun KtExpression.isUsed(): Boolean {
+ if (!isUsedAsExpression(analyze(BodyResolveMode.PARTIAL_WITH_CFA))) return false
+ val property = getParentOfTypes(
+ true,
+ KtThrowExpression::class.java,
+ KtReturnExpression::class.java,
+ KtProperty::class.java
+ ) as? KtProperty ?: return true
+ return !property.isLocal || ReferencesSearch.search(property).any()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/.inspection b/idea/testData/inspectionsLocal/throwableNotThrown/.inspection
new file mode 100644
index 00000000000..b16a94bf1ce
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.KotlinThrowableNotThrownInspection
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/basic.kt b/idea/testData/inspectionsLocal/throwableNotThrown/basic.kt
new file mode 100644
index 00000000000..a378363cdb4
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/basic.kt
@@ -0,0 +1,7 @@
+// PROBLEM: Throwable instance 'FooException' is not thrown
+// FIX: none
+class FooException : RuntimeException()
+
+fun test() {
+ FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/basic2.kt b/idea/testData/inspectionsLocal/throwableNotThrown/basic2.kt
new file mode 100644
index 00000000000..8948eedc86f
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/basic2.kt
@@ -0,0 +1,9 @@
+// PROBLEM: Result of 'createError' call is not thrown
+// FIX: none
+class FooException : RuntimeException()
+
+fun createError() = FooException()
+
+fun test() {
+ createError()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/basic3.kt b/idea/testData/inspectionsLocal/throwableNotThrown/basic3.kt
new file mode 100644
index 00000000000..8fcd6821ed1
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/basic3.kt
@@ -0,0 +1,8 @@
+// PROBLEM: none
+fun foo() {
+ throw RuntimeException()
+}
+
+fun test() {
+ foo()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/field.kt b/idea/testData/inspectionsLocal/throwableNotThrown/field.kt
new file mode 100644
index 00000000000..11b88d07b28
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/field.kt
@@ -0,0 +1,6 @@
+// PROBLEM: none
+class FooException : Exception()
+
+class Test {
+ val e = FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/if.kt b/idea/testData/inspectionsLocal/throwableNotThrown/if.kt
new file mode 100644
index 00000000000..75dc43fa37d
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/if.kt
@@ -0,0 +1,13 @@
+// PROBLEM: Throwable instance 'FooException' is not thrown
+// FIX: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test(i: Int) {
+ if (i == 1) {
+ FooException()
+ } else {
+ createException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/if2.kt b/idea/testData/inspectionsLocal/throwableNotThrown/if2.kt
new file mode 100644
index 00000000000..1d982c8a4c3
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/if2.kt
@@ -0,0 +1,12 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test(i: Int) {
+ throw if (i == 1) {
+ FooException()
+ } else {
+ createException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/if3.kt b/idea/testData/inspectionsLocal/throwableNotThrown/if3.kt
new file mode 100644
index 00000000000..e198d608734
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/if3.kt
@@ -0,0 +1,13 @@
+// PROBLEM: Throwable instance 'FooException' is not thrown
+// FIX: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test(i: Int) {
+ val e = if (i == 1) {
+ FooException()
+ } else {
+ createException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/if4.kt b/idea/testData/inspectionsLocal/throwableNotThrown/if4.kt
new file mode 100644
index 00000000000..c5255672c5c
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/if4.kt
@@ -0,0 +1,13 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test(i: Int) {
+ val e = if (i == 1) {
+ FooException()
+ } else {
+ createException()
+ }
+ throw e
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/if5.kt b/idea/testData/inspectionsLocal/throwableNotThrown/if5.kt
new file mode 100644
index 00000000000..7a556a011e5
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/if5.kt
@@ -0,0 +1,10 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+fun test(i: Int) {
+ val x = if (i == 1) {
+ throw FooException()
+ } else {
+ 0
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/nullable.kt b/idea/testData/inspectionsLocal/throwableNotThrown/nullable.kt
new file mode 100644
index 00000000000..2d272c44e1e
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/nullable.kt
@@ -0,0 +1,8 @@
+// PROBLEM: none
+fun foo(): RuntimeException? {
+ return RuntimeException()
+}
+
+fun test() {
+ foo()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/property.kt b/idea/testData/inspectionsLocal/throwableNotThrown/property.kt
new file mode 100644
index 00000000000..d2fe004cc3b
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/property.kt
@@ -0,0 +1,7 @@
+// PROBLEM: Throwable instance 'FooException' is not thrown
+// FIX: none
+class FooException : Exception()
+
+fun test() {
+ val e = FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/property2.kt b/idea/testData/inspectionsLocal/throwableNotThrown/property2.kt
new file mode 100644
index 00000000000..a5d29f78f41
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/property2.kt
@@ -0,0 +1,7 @@
+// PROBLEM: none
+class FooException : Exception()
+
+fun test() {
+ val e = FooException()
+ throw e
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/property3.kt b/idea/testData/inspectionsLocal/throwableNotThrown/property3.kt
new file mode 100644
index 00000000000..b78f739ebe7
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/property3.kt
@@ -0,0 +1,7 @@
+// PROBLEM: none
+class FooException : Exception()
+
+fun test() {
+ val e = FooException()
+ val e2 = e
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/property4.kt b/idea/testData/inspectionsLocal/throwableNotThrown/property4.kt
new file mode 100644
index 00000000000..c678bbfc9c8
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/property4.kt
@@ -0,0 +1,6 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+fun test(i: Int?) {
+ val x = i ?: throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/property5.kt b/idea/testData/inspectionsLocal/throwableNotThrown/property5.kt
new file mode 100644
index 00000000000..65af6cc4eca
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/property5.kt
@@ -0,0 +1,9 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+class BarException : RuntimeException()
+
+fun test(i: Int?): RuntimeException {
+ val x = i ?: return FooException()
+ return BarException()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/return.kt b/idea/testData/inspectionsLocal/throwableNotThrown/return.kt
new file mode 100644
index 00000000000..ad3ac8e06d6
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/return.kt
@@ -0,0 +1,8 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test(): FooException {
+ return createException()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/return2.kt b/idea/testData/inspectionsLocal/throwableNotThrown/return2.kt
new file mode 100644
index 00000000000..84536ec78d4
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/return2.kt
@@ -0,0 +1,6 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test() = createException()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/throw.kt b/idea/testData/inspectionsLocal/throwableNotThrown/throw.kt
new file mode 100644
index 00000000000..44e9b5848f6
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/throw.kt
@@ -0,0 +1,6 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/when.kt b/idea/testData/inspectionsLocal/throwableNotThrown/when.kt
new file mode 100644
index 00000000000..b62a9f2c669
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/when.kt
@@ -0,0 +1,12 @@
+// PROBLEM: Result of 'createException' call is not thrown
+// FIX: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test(i: Int) {
+ when (i) {
+ 1 -> FooException()
+ else -> createException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/when2.kt b/idea/testData/inspectionsLocal/throwableNotThrown/when2.kt
new file mode 100644
index 00000000000..883ad7cf7c4
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/when2.kt
@@ -0,0 +1,11 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test(i: Int) {
+ throw when (i) {
+ 1 -> FooException()
+ else -> createException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/when3.kt b/idea/testData/inspectionsLocal/throwableNotThrown/when3.kt
new file mode 100644
index 00000000000..69114d9b040
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/when3.kt
@@ -0,0 +1,12 @@
+// PROBLEM: Result of 'createException' call is not thrown
+// FIX: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test(i: Int) {
+ val e = when (i) {
+ 1 -> FooException()
+ else -> createException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/when4.kt b/idea/testData/inspectionsLocal/throwableNotThrown/when4.kt
new file mode 100644
index 00000000000..4383ee38627
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/when4.kt
@@ -0,0 +1,12 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test(i: Int) {
+ val e = when (i) {
+ 1 -> FooException()
+ else -> createException()
+ }
+ throw e
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/throwableNotThrown/when5.kt b/idea/testData/inspectionsLocal/throwableNotThrown/when5.kt
new file mode 100644
index 00000000000..6e677a2413d
--- /dev/null
+++ b/idea/testData/inspectionsLocal/throwableNotThrown/when5.kt
@@ -0,0 +1,11 @@
+// PROBLEM: none
+class FooException : RuntimeException()
+
+fun createException() = FooException()
+
+fun test(i: Int) {
+ val x = when (i) {
+ 1 -> 10
+ else -> throw createException()
+ }
+}
\ 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 65fec55437a..e0c2452223e 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -7763,6 +7763,134 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
+ @TestMetadata("idea/testData/inspectionsLocal/throwableNotThrown")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class ThrowableNotThrown extends AbstractLocalInspectionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInThrowableNotThrown() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/throwableNotThrown"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("basic.kt")
+ public void testBasic() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/basic.kt");
+ }
+
+ @TestMetadata("basic2.kt")
+ public void testBasic2() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/basic2.kt");
+ }
+
+ @TestMetadata("basic3.kt")
+ public void testBasic3() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/basic3.kt");
+ }
+
+ @TestMetadata("field.kt")
+ public void testField() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/field.kt");
+ }
+
+ @TestMetadata("if.kt")
+ public void testIf() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/if.kt");
+ }
+
+ @TestMetadata("if2.kt")
+ public void testIf2() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/if2.kt");
+ }
+
+ @TestMetadata("if3.kt")
+ public void testIf3() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/if3.kt");
+ }
+
+ @TestMetadata("if4.kt")
+ public void testIf4() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/if4.kt");
+ }
+
+ @TestMetadata("if5.kt")
+ public void testIf5() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/if5.kt");
+ }
+
+ @TestMetadata("nullable.kt")
+ public void testNullable() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/nullable.kt");
+ }
+
+ @TestMetadata("property.kt")
+ public void testProperty() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/property.kt");
+ }
+
+ @TestMetadata("property2.kt")
+ public void testProperty2() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/property2.kt");
+ }
+
+ @TestMetadata("property3.kt")
+ public void testProperty3() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/property3.kt");
+ }
+
+ @TestMetadata("property4.kt")
+ public void testProperty4() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/property4.kt");
+ }
+
+ @TestMetadata("property5.kt")
+ public void testProperty5() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/property5.kt");
+ }
+
+ @TestMetadata("return.kt")
+ public void testReturn() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/return.kt");
+ }
+
+ @TestMetadata("return2.kt")
+ public void testReturn2() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/return2.kt");
+ }
+
+ @TestMetadata("throw.kt")
+ public void testThrow() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/throw.kt");
+ }
+
+ @TestMetadata("when.kt")
+ public void testWhen() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/when.kt");
+ }
+
+ @TestMetadata("when2.kt")
+ public void testWhen2() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/when2.kt");
+ }
+
+ @TestMetadata("when3.kt")
+ public void testWhen3() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/when3.kt");
+ }
+
+ @TestMetadata("when4.kt")
+ public void testWhen4() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/when4.kt");
+ }
+
+ @TestMetadata("when5.kt")
+ public void testWhen5() throws Exception {
+ runTest("idea/testData/inspectionsLocal/throwableNotThrown/when5.kt");
+ }
+ }
+
@TestMetadata("idea/testData/inspectionsLocal/unlabeledReturnInsideLambda")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)