From a621171d9baf7a8735c287ff487c598607a38bff Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Mon, 5 Nov 2018 20:25:00 +0900 Subject: [PATCH] KT-24515 Intention to add an exception under the cursor to @Throws annotations --- .../after.kt.template | 6 + .../before.kt.template | 5 + .../description.html | 5 + idea/src/META-INF/plugin-common.xml | 5 + .../AddThrowsAnnotationIntention.kt | 138 ++++++++++++++++++ .../intentions/addThrowsAnnotation/.intention | 1 + .../intentions/addThrowsAnnotation/fqName.kt | 5 + .../addThrowsAnnotation/fqName.kt.after | 8 + .../intentions/addThrowsAnnotation/fqName2.kt | 8 + .../addThrowsAnnotation/fqName2.kt.after | 10 ++ .../addThrowsAnnotation/functionCall.kt | 8 + .../addThrowsAnnotation/functionCall.kt.after | 9 ++ .../hasThrowsWithDifferentClassArgument.kt | 9 ++ ...sThrowsWithDifferentClassArgument.kt.after | 9 ++ .../hasThrowsWithDifferentClassArgument2.kt | 10 ++ ...ThrowsWithDifferentClassArgument2.kt.after | 10 ++ .../hasThrowsWithDifferentClassArgument3.kt | 9 ++ ...ThrowsWithDifferentClassArgument3.kt.after | 9 ++ .../hasThrowsWithDifferentClassArgument4.kt | 9 ++ ...ThrowsWithDifferentClassArgument4.kt.after | 9 ++ .../hasThrowsWithDifferentClassArgument5.kt | 12 ++ .../hasThrowsWithEmptyArgument.kt | 7 + .../hasThrowsWithEmptyArgument.kt.after | 7 + .../hasThrowsWithNoArgument.kt | 7 + .../hasThrowsWithNoArgument.kt.after | 7 + .../hasThrowsWithSameClassArgument.kt | 9 ++ .../hasThrowsWithSameClassArgument2.kt | 10 ++ .../hasThrowsWithSameClassArgument3.kt | 9 ++ .../hasThrowsWithSameClassArgument4.kt | 9 ++ .../intentions/addThrowsAnnotation/if.kt | 5 + .../addThrowsAnnotation/if.kt.after | 6 + .../addThrowsAnnotation/inConstructor.kt | 9 ++ .../inConstructor.kt.after | 10 ++ .../addThrowsAnnotation/inFunction.kt | 8 + .../addThrowsAnnotation/inFunction.kt.after | 9 ++ .../addThrowsAnnotation/inGetter.kt | 8 + .../addThrowsAnnotation/inGetter.kt.after | 9 ++ .../addThrowsAnnotation/inGetter2.kt | 5 + .../addThrowsAnnotation/inGetter2.kt.after | 5 + .../intentions/addThrowsAnnotation/inInit.kt | 8 + .../addThrowsAnnotation/inLambda.kt | 4 + .../addThrowsAnnotation/inSetter.kt | 8 + .../addThrowsAnnotation/inSetter.kt.after | 9 ++ .../addThrowsAnnotation/inSetter2.kt | 5 + .../addThrowsAnnotation/inSetter2.kt.after | 5 + .../intentions/addThrowsAnnotation/js.kt | 6 + .../addThrowsAnnotation/localException.kt | 7 + .../intentions/IntentionTestGenerated.java | 138 ++++++++++++++++++ 48 files changed, 623 insertions(+) create mode 100644 idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/AddThrowsAnnotationIntention/description.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/AddThrowsAnnotationIntention.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/.intention create mode 100644 idea/testData/intentions/addThrowsAnnotation/fqName.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/fqName.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/fqName2.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/fqName2.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/functionCall.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/functionCall.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument2.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument2.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument3.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument3.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument4.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument4.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithDifferentClassArgument5.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithEmptyArgument.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithEmptyArgument.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithNoArgument.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithNoArgument.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument2.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument3.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/hasThrowsWithSameClassArgument4.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/if.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/if.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/inConstructor.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/inConstructor.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/inFunction.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/inFunction.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/inGetter.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/inGetter.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/inGetter2.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/inGetter2.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/inInit.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/inLambda.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/inSetter.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/inSetter.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/inSetter2.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/inSetter2.kt.after create mode 100644 idea/testData/intentions/addThrowsAnnotation/js.kt create mode 100644 idea/testData/intentions/addThrowsAnnotation/localException.kt 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)