diff --git a/idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/after.kt.template b/idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/after.kt.template
new file mode 100644
index 00000000000..82506c676c5
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/after.kt.template
@@ -0,0 +1,6 @@
+class MyException : Exception()
+
+@Throws(MyException::class)
+fun test() {
+ throw MyException()
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/before.kt.template b/idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/before.kt.template
new file mode 100644
index 00000000000..965a9996740
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/before.kt.template
@@ -0,0 +1,5 @@
+class MyException : Exception()
+
+fun test() {
+ throw MyException()
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/description.html b/idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/description.html
new file mode 100644
index 00000000000..06997c3a381
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/description.html
@@ -0,0 +1,5 @@
+
+
+This intention adds a @Throws annotation for an exception under the caret.
+
+
\ No newline at end of file
diff --git a/idea/src/META-INF/plugin-common.xml b/idea/src/META-INF/plugin-common.xml
index 3b7af635336..9fb3b068048 100644
--- a/idea/src/META-INF/plugin-common.xml
+++ b/idea/src/META-INF/plugin-common.xml
@@ -1665,6 +1665,11 @@
Kotlin
+
+ org.jetbrains.kotlin.idea.intentions.AddThrowsAnnotationIntention
+ Kotlin
+
+
(
+ KtThrowExpression::class.java, "Add '@Throws' annotation"
+) {
+
+ override fun isApplicableTo(element: KtThrowExpression, caretOffset: Int): Boolean {
+ if (element.platform != JvmPlatform) return false
+ val containingDeclaration = element.getContainingDeclaration() ?: return false
+
+ val type = element.thrownExpression?.resolveToCall()?.resultingDescriptor?.returnType ?: return false
+ if ((type.constructor.declarationDescriptor as? DeclarationDescriptorWithVisibility)?.visibility == Visibilities.LOCAL) return false
+
+ val context = element.analyze(BodyResolveMode.PARTIAL)
+
+ val annotationEntry = containingDeclaration.findThrowsAnnotation(context) ?: return true
+ val valueArguments = annotationEntry.valueArguments
+ if (valueArguments.isEmpty()) return true
+
+ val argumentExpression = valueArguments.firstOrNull()?.getArgumentExpression()
+ if (argumentExpression is KtCallExpression
+ && argumentExpression.calleeExpression?.getCallableDescriptor()?.fqNameSafe != FqName("kotlin.arrayOf")
+ ) return false
+
+ return valueArguments.none { it.hasType(type, context) }
+ }
+
+ override fun applyTo(element: KtThrowExpression, editor: Editor?) {
+ val containingDeclaration = element.getContainingDeclaration() ?: return
+ val type = element.thrownExpression?.resolveToCall()?.resultingDescriptor?.returnType ?: return
+
+ val annotationArgumentText = if (type.getAbbreviatedType() != null)
+ "$type::class"
+ else
+ type.constructor.declarationDescriptor?.fqNameSafe?.let { "$it::class" } ?: return
+
+ val context = element.analyze(BodyResolveMode.PARTIAL)
+ val annotationEntry = containingDeclaration.findThrowsAnnotation(context)
+ if (annotationEntry == null || annotationEntry.valueArguments.isEmpty()) {
+ annotationEntry?.delete()
+ val whiteSpaceText = if (containingDeclaration is KtPropertyAccessor) " " else "\n"
+ containingDeclaration.addAnnotation(throwsAnnotationFqName, annotationArgumentText, whiteSpaceText)
+ } else {
+ val factory = KtPsiFactory(element)
+ val argument = annotationEntry.valueArguments.firstOrNull()
+ val expression = argument?.getArgumentExpression()
+ val added = when {
+ argument?.getArgumentName() == null ->
+ annotationEntry.valueArgumentList?.addArgument(factory.createArgument(annotationArgumentText))
+ expression is KtCallExpression ->
+ expression.valueArgumentList?.addArgument(factory.createArgument(annotationArgumentText))
+ expression is KtClassLiteralExpression -> {
+ expression.replaced(
+ factory.createCollectionLiteral(listOf(expression), annotationArgumentText)
+ ).getInnerExpressions().lastOrNull()
+ }
+ expression is KtCollectionLiteralExpression -> {
+ expression.replaced(
+ factory.createCollectionLiteral(expression.getInnerExpressions(), annotationArgumentText)
+ ).getInnerExpressions().lastOrNull()
+ }
+ else -> null
+ }
+ if (added != null) ShortenReferences.DEFAULT.process(added)
+ }
+ }
+}
+
+private val throwsAnnotationFqName = FqName("kotlin.jvm.Throws")
+
+private fun KtThrowExpression.getContainingDeclaration(): KtDeclaration? {
+ val parent = getParentOfTypesAndPredicate(
+ true,
+ KtNamedFunction::class.java,
+ KtSecondaryConstructor::class.java,
+ KtPropertyAccessor::class.java,
+ KtClassInitializer::class.java,
+ KtLambdaExpression::class.java
+ ) { true }
+ if (parent is KtClassInitializer || parent is KtLambdaExpression) return null
+ return parent as? KtDeclaration
+}
+
+private fun KtDeclaration.findThrowsAnnotation(context: BindingContext): KtAnnotationEntry? {
+ val annotationEntries = this.annotationEntries + (parent as? KtProperty)?.annotationEntries.orEmpty()
+ return annotationEntries.find {
+ val typeReference = it.typeReference ?: return@find false
+ context[BindingContext.TYPE, typeReference]?.constructor?.declarationDescriptor?.fqNameSafe == throwsAnnotationFqName
+ }
+}
+
+private fun ValueArgument.hasType(type: KotlinType, context: BindingContext): Boolean {
+ val argumentExpression = getArgumentExpression()
+ val expressions = when (argumentExpression) {
+ is KtClassLiteralExpression -> listOf(argumentExpression)
+ is KtCollectionLiteralExpression -> argumentExpression.getInnerExpressions().filterIsInstance(KtClassLiteralExpression::class.java)
+ is KtCallExpression -> argumentExpression.valueArguments.mapNotNull { it.getArgumentExpression() as? KtClassLiteralExpression }
+ else -> emptyList()
+ }
+ return expressions.any { it.getType(context)?.arguments?.firstOrNull()?.type == type }
+}
+
+private fun KtPsiFactory.createCollectionLiteral(expressions: List, lastExpression: String): KtCollectionLiteralExpression {
+ return buildExpression {
+ appendFixedText("[")
+ expressions.forEach {
+ appendExpression(it)
+ appendFixedText(", ")
+ }
+ appendFixedText(lastExpression)
+ appendFixedText("]")
+ } as KtCollectionLiteralExpression
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/.intention b/idea/testData/intentions/addThrowsAnnotation/.intention
new file mode 100644
index 00000000000..85a69a1b098
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/.intention
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.intentions.AddThrowsAnnotationIntention
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/fqName.kt b/idea/testData/intentions/addThrowsAnnotation/fqName.kt
new file mode 100644
index 00000000000..e3f1f16b65a
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/fqName.kt
@@ -0,0 +1,5 @@
+fun test() {
+ throw java.io.IOException()
+}
+
+// RUNTIME_WITH_FULL_JDK
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/fqName.kt.after b/idea/testData/intentions/addThrowsAnnotation/fqName.kt.after
new file mode 100644
index 00000000000..f079b91a3b2
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/fqName.kt.after
@@ -0,0 +1,8 @@
+import java.io.IOException
+
+@Throws(IOException::class)
+fun test() {
+ throw java.io.IOException()
+}
+
+// RUNTIME_WITH_FULL_JDK
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/fqName2.kt b/idea/testData/intentions/addThrowsAnnotation/fqName2.kt
new file mode 100644
index 00000000000..8333f00420f
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/fqName2.kt
@@ -0,0 +1,8 @@
+class FooException : Exception()
+
+@Throws(FooException::class)
+fun test() {
+ throw java.io.IOException()
+}
+
+// RUNTIME_WITH_FULL_JDK
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/fqName2.kt.after b/idea/testData/intentions/addThrowsAnnotation/fqName2.kt.after
new file mode 100644
index 00000000000..b5397c4ea73
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/fqName2.kt.after
@@ -0,0 +1,10 @@
+import java.io.IOException
+
+class FooException : Exception()
+
+@Throws(FooException::class, IOException::class)
+fun test() {
+ throw java.io.IOException()
+}
+
+// RUNTIME_WITH_FULL_JDK
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/functionCall.kt b/idea/testData/intentions/addThrowsAnnotation/functionCall.kt
new file mode 100644
index 00000000000..05df0295e3f
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/functionCall.kt
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+
+class Test {
+ fun a() {
+ throw myError()
+ }
+}
+fun myError() = RuntimeException()
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/functionCall.kt.after b/idea/testData/intentions/addThrowsAnnotation/functionCall.kt.after
new file mode 100644
index 00000000000..57935b5724a
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/functionCall.kt.after
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+
+class Test {
+ @Throws(RuntimeException::class)
+ fun a() {
+ throw myError()
+ }
+}
+fun myError() = RuntimeException()
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument.kt
new file mode 100644
index 00000000000..e082e17448d
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument.kt
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+class FooException : Exception()
+
+class BarException : Exception()
+
+@Throws(BarException::class)
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument.kt.after b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument.kt.after
new file mode 100644
index 00000000000..a2d033b49c2
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument.kt.after
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+class FooException : Exception()
+
+class BarException : Exception()
+
+@Throws(BarException::class, FooException::class)
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument2.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument2.kt
new file mode 100644
index 00000000000..df4e5d68468
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument2.kt
@@ -0,0 +1,10 @@
+// WITH_RUNTIME
+// DISABLE-ERRORS
+class FooException : Exception()
+
+class BarException : Exception()
+
+@Throws(exceptionClasses = BarException::class)
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument2.kt.after b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument2.kt.after
new file mode 100644
index 00000000000..a4b4a8405e4
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument2.kt.after
@@ -0,0 +1,10 @@
+// WITH_RUNTIME
+// DISABLE-ERRORS
+class FooException : Exception()
+
+class BarException : Exception()
+
+@Throws(exceptionClasses = [BarException::class, FooException::class])
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument3.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument3.kt
new file mode 100644
index 00000000000..9a955f9b65b
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument3.kt
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+class FooException : Exception()
+
+class BarException : Exception()
+
+@Throws(exceptionClasses = [BarException::class])
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument3.kt.after b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument3.kt.after
new file mode 100644
index 00000000000..905c02e364b
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument3.kt.after
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+class FooException : Exception()
+
+class BarException : Exception()
+
+@Throws(exceptionClasses = [BarException::class, FooException::class])
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument4.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument4.kt
new file mode 100644
index 00000000000..b3c51787dfb
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument4.kt
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+class FooException : Exception()
+
+class BarException : Exception()
+
+@Throws(exceptionClasses = arrayOf(BarException::class))
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument4.kt.after b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument4.kt.after
new file mode 100644
index 00000000000..afb7eebce3d
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument4.kt.after
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+class FooException : Exception()
+
+class BarException : Exception()
+
+@Throws(exceptionClasses = arrayOf(BarException::class, FooException::class))
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument5.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument5.kt
new file mode 100644
index 00000000000..6b7f9f55bab
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument5.kt
@@ -0,0 +1,12 @@
+// DISABLE-ERRORS
+// WITH_RUNTIME
+// IS_APPLICABLE: false
+
+class FooException : Exception()
+
+class BarException : Exception()
+
+@Throws(exceptionClasses = listOf(BarException::class))
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithEmptyArgument.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithEmptyArgument.kt
new file mode 100644
index 00000000000..8e9560ad6c9
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithEmptyArgument.kt
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+class FooException : Exception()
+
+@Throws()
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithEmptyArgument.kt.after b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithEmptyArgument.kt.after
new file mode 100644
index 00000000000..1652c634c31
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithEmptyArgument.kt.after
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+class FooException : Exception()
+
+@Throws(FooException::class)
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithNoArgument.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithNoArgument.kt
new file mode 100644
index 00000000000..6f13b843a46
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithNoArgument.kt
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+class FooException : Exception()
+
+@Throws
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithNoArgument.kt.after b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithNoArgument.kt.after
new file mode 100644
index 00000000000..1652c634c31
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithNoArgument.kt.after
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+class FooException : Exception()
+
+@Throws(FooException::class)
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument.kt
new file mode 100644
index 00000000000..c7aac41324c
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument.kt
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+// IS_APPLICABLE: false
+
+class FooException : Exception()
+
+@Throws(FooException::class)
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument2.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument2.kt
new file mode 100644
index 00000000000..449d73396a6
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument2.kt
@@ -0,0 +1,10 @@
+// WITH_RUNTIME
+// DISABLE-ERRORS
+// IS_APPLICABLE: false
+
+class FooException : Exception()
+
+@Throws(exceptionClasses = FooException::class)
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument3.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument3.kt
new file mode 100644
index 00000000000..7ae9eb138d6
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument3.kt
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+// IS_APPLICABLE: false
+
+class FooException : Exception()
+
+@Throws(exceptionClasses = [FooException::class])
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument4.kt b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument4.kt
new file mode 100644
index 00000000000..137b3110385
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument4.kt
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+// IS_APPLICABLE: false
+
+class FooException : Exception()
+
+@Throws(exceptionClasses = arrayOf(FooException::class))
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/if.kt b/idea/testData/intentions/addThrowsAnnotation/if.kt
new file mode 100644
index 00000000000..71de6c98ee0
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/if.kt
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+
+fun a(b: Boolean) {
+ throw if (b) RuntimeException() else Exception()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/if.kt.after b/idea/testData/intentions/addThrowsAnnotation/if.kt.after
new file mode 100644
index 00000000000..b031061a9cd
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/if.kt.after
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+
+@Throws(Throwable::class)
+fun a(b: Boolean) {
+ throw if (b) RuntimeException() else Exception()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inConstructor.kt b/idea/testData/intentions/addThrowsAnnotation/inConstructor.kt
new file mode 100644
index 00000000000..aeab38a18c5
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inConstructor.kt
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+
+class FooException : Exception()
+
+class Test {
+ constructor() {
+ throw FooException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inConstructor.kt.after b/idea/testData/intentions/addThrowsAnnotation/inConstructor.kt.after
new file mode 100644
index 00000000000..e59953bf870
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inConstructor.kt.after
@@ -0,0 +1,10 @@
+// WITH_RUNTIME
+
+class FooException : Exception()
+
+class Test {
+ @Throws(FooException::class)
+ constructor() {
+ throw FooException()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inFunction.kt b/idea/testData/intentions/addThrowsAnnotation/inFunction.kt
new file mode 100644
index 00000000000..d8f64ab191e
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inFunction.kt
@@ -0,0 +1,8 @@
+// INTENTION_TEXT: "Add '@Throws' annotation"
+// WITH_RUNTIME
+
+class FooException : Exception()
+
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inFunction.kt.after b/idea/testData/intentions/addThrowsAnnotation/inFunction.kt.after
new file mode 100644
index 00000000000..01d03661c94
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inFunction.kt.after
@@ -0,0 +1,9 @@
+// INTENTION_TEXT: "Add '@Throws' annotation"
+// WITH_RUNTIME
+
+class FooException : Exception()
+
+@Throws(FooException::class)
+fun test() {
+ throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inGetter.kt b/idea/testData/intentions/addThrowsAnnotation/inGetter.kt
new file mode 100644
index 00000000000..0c09eb48079
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inGetter.kt
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+
+class FooException : Exception()
+
+class Test {
+ val getter: String
+ get() = throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inGetter.kt.after b/idea/testData/intentions/addThrowsAnnotation/inGetter.kt.after
new file mode 100644
index 00000000000..b252c914e3e
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inGetter.kt.after
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+
+class FooException : Exception()
+
+class Test {
+ val getter: String
+ @Throws(FooException::class)
+ get() = throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inGetter2.kt b/idea/testData/intentions/addThrowsAnnotation/inGetter2.kt
new file mode 100644
index 00000000000..69b9d846982
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inGetter2.kt
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+
+@get:Throws(RuntimeException::class)
+val a: String
+ get() = throw Exception()
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inGetter2.kt.after b/idea/testData/intentions/addThrowsAnnotation/inGetter2.kt.after
new file mode 100644
index 00000000000..75cba6c9c5d
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inGetter2.kt.after
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+
+@get:Throws(RuntimeException::class, Exception::class)
+val a: String
+ get() = throw Exception()
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inInit.kt b/idea/testData/intentions/addThrowsAnnotation/inInit.kt
new file mode 100644
index 00000000000..1e0ec6db38d
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inInit.kt
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+// IS_APPLICABLE: false
+
+class X {
+ init {
+ throw RuntimeException()
+ }
+}
diff --git a/idea/testData/intentions/addThrowsAnnotation/inLambda.kt b/idea/testData/intentions/addThrowsAnnotation/inLambda.kt
new file mode 100644
index 00000000000..8397778d6e4
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inLambda.kt
@@ -0,0 +1,4 @@
+// WITH_RUNTIME
+// IS_APPLICABLE: false
+
+val f = { throw RuntimeException() }
diff --git a/idea/testData/intentions/addThrowsAnnotation/inSetter.kt b/idea/testData/intentions/addThrowsAnnotation/inSetter.kt
new file mode 100644
index 00000000000..6b4a6602a99
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inSetter.kt
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+
+class FooException : Exception()
+
+class Test {
+ var setter: String = ""
+ set(value) = throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inSetter.kt.after b/idea/testData/intentions/addThrowsAnnotation/inSetter.kt.after
new file mode 100644
index 00000000000..fe0afe2bce1
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inSetter.kt.after
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+
+class FooException : Exception()
+
+class Test {
+ var setter: String = ""
+ @Throws(FooException::class)
+ set(value) = throw FooException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inSetter2.kt b/idea/testData/intentions/addThrowsAnnotation/inSetter2.kt
new file mode 100644
index 00000000000..4d3accd1956
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inSetter2.kt
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+
+@set:Throws(RuntimeException::class)
+var setter: String = ""
+ set(value) = throw Exception()
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/inSetter2.kt.after b/idea/testData/intentions/addThrowsAnnotation/inSetter2.kt.after
new file mode 100644
index 00000000000..986ba3acc8b
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/inSetter2.kt.after
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+
+@set:Throws(RuntimeException::class, Exception::class)
+var setter: String = ""
+ set(value) = throw Exception()
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/js.kt b/idea/testData/intentions/addThrowsAnnotation/js.kt
new file mode 100644
index 00000000000..af7c1c89317
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/js.kt
@@ -0,0 +1,6 @@
+// JS
+// IS_APPLICABLE: false
+
+fun a() {
+ throw RuntimeException()
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addThrowsAnnotation/localException.kt b/idea/testData/intentions/addThrowsAnnotation/localException.kt
new file mode 100644
index 00000000000..61574b97f65
--- /dev/null
+++ b/idea/testData/intentions/addThrowsAnnotation/localException.kt
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+// IS_APPLICABLE: false
+
+fun a() {
+ class Ex : RuntimeException()
+ throw Ex()
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
index d12e1001838..5af9feff561 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
@@ -1698,6 +1698,144 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
+ @TestMetadata("idea/testData/intentions/addThrowsAnnotation")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class AddThrowsAnnotation extends AbstractIntentionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInAddThrowsAnnotation() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/addThrowsAnnotation"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("fqName.kt")
+ public void testFqName() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/fqName.kt");
+ }
+
+ @TestMetadata("fqName2.kt")
+ public void testFqName2() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/fqName2.kt");
+ }
+
+ @TestMetadata("functionCall.kt")
+ public void testFunctionCall() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/functionCall.kt");
+ }
+
+ @TestMetadata("hasThrowsWithDifferentClassArgument.kt")
+ public void testHasThrowsWithDifferentClassArgument() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument.kt");
+ }
+
+ @TestMetadata("hasThrowsWithDifferentClassArgument2.kt")
+ public void testHasThrowsWithDifferentClassArgument2() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument2.kt");
+ }
+
+ @TestMetadata("hasThrowsWithDifferentClassArgument3.kt")
+ public void testHasThrowsWithDifferentClassArgument3() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument3.kt");
+ }
+
+ @TestMetadata("hasThrowsWithDifferentClassArgument4.kt")
+ public void testHasThrowsWithDifferentClassArgument4() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument4.kt");
+ }
+
+ @TestMetadata("hasThrowsWithDifferentClassArgument5.kt")
+ public void testHasThrowsWithDifferentClassArgument5() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument5.kt");
+ }
+
+ @TestMetadata("hasThrowsWithEmptyArgument.kt")
+ public void testHasThrowsWithEmptyArgument() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithEmptyArgument.kt");
+ }
+
+ @TestMetadata("hasThrowsWithNoArgument.kt")
+ public void testHasThrowsWithNoArgument() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithNoArgument.kt");
+ }
+
+ @TestMetadata("hasThrowsWithSameClassArgument.kt")
+ public void testHasThrowsWithSameClassArgument() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument.kt");
+ }
+
+ @TestMetadata("hasThrowsWithSameClassArgument2.kt")
+ public void testHasThrowsWithSameClassArgument2() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument2.kt");
+ }
+
+ @TestMetadata("hasThrowsWithSameClassArgument3.kt")
+ public void testHasThrowsWithSameClassArgument3() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument3.kt");
+ }
+
+ @TestMetadata("hasThrowsWithSameClassArgument4.kt")
+ public void testHasThrowsWithSameClassArgument4() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument4.kt");
+ }
+
+ @TestMetadata("if.kt")
+ public void testIf() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/if.kt");
+ }
+
+ @TestMetadata("inConstructor.kt")
+ public void testInConstructor() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/inConstructor.kt");
+ }
+
+ @TestMetadata("inFunction.kt")
+ public void testInFunction() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/inFunction.kt");
+ }
+
+ @TestMetadata("inGetter.kt")
+ public void testInGetter() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/inGetter.kt");
+ }
+
+ @TestMetadata("inGetter2.kt")
+ public void testInGetter2() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/inGetter2.kt");
+ }
+
+ @TestMetadata("inInit.kt")
+ public void testInInit() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/inInit.kt");
+ }
+
+ @TestMetadata("inLambda.kt")
+ public void testInLambda() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/inLambda.kt");
+ }
+
+ @TestMetadata("inSetter.kt")
+ public void testInSetter() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/inSetter.kt");
+ }
+
+ @TestMetadata("inSetter2.kt")
+ public void testInSetter2() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/inSetter2.kt");
+ }
+
+ @TestMetadata("js.kt")
+ public void testJs() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/js.kt");
+ }
+
+ @TestMetadata("localException.kt")
+ public void testLocalException() throws Exception {
+ runTest("idea/testData/intentions/addThrowsAnnotation/localException.kt");
+ }
+ }
+
@TestMetadata("idea/testData/intentions/addValOrVar")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)