diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/AddThrowsAnnotationIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/AddThrowsAnnotationIntention.kt index f1112bb7d84..b210b71f4a3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/AddThrowsAnnotationIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/AddThrowsAnnotationIntention.kt @@ -6,6 +6,8 @@ package org.jetbrains.kotlin.idea.intentions import com.intellij.openapi.editor.Editor +import com.intellij.openapi.module.Module +import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility import org.jetbrains.kotlin.descriptors.Visibilities @@ -17,8 +19,11 @@ import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.project.platform import org.jetbrains.kotlin.idea.refactoring.fqName.fqName +import org.jetbrains.kotlin.idea.stubindex.KotlinFullClassNameIndex import org.jetbrains.kotlin.idea.util.addAnnotation +import org.jetbrains.kotlin.idea.util.module import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.platform.js.isJs import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate @@ -35,14 +40,18 @@ class AddThrowsAnnotationIntention : SelfTargetingIntention( KtThrowExpression::class.java, KotlinBundle.lazyMessage("add.throws.annotation") ) { override fun isApplicableTo(element: KtThrowExpression, caretOffset: Int): Boolean { - if (!element.platform.isJvm()) return false + if (element.platform.isJs()) 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 module = element.module ?: return false + if (!KOTLIN_THROWS_ANNOTATION_FQ_NAME.fqNameIsExists(module) && + !(element.platform.isJvm() && JVM_THROWS_ANNOTATION_FQ_NAME.fqNameIsExists(module)) + ) return false + val context = element.analyze(BodyResolveMode.PARTIAL) val annotationEntry = containingDeclaration.findThrowsAnnotation(context) ?: return true val valueArguments = annotationEntry.valueArguments if (valueArguments.isEmpty()) return true @@ -140,3 +149,9 @@ private fun KtPsiFactory.createCollectionLiteral(expressions: List appendFixedText(lastExpression) appendFixedText("]") } as KtCollectionLiteralExpression + +private fun FqName.fqNameIsExists(module: Module): Boolean = KotlinFullClassNameIndex.getInstance()[ + asString(), + module.project, + GlobalSearchScope.moduleWithLibrariesScope(module) +].isNotEmpty() \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/addThrowAnnotation/common/common_dep(stdlib)/common.kt b/idea/testData/multiModuleQuickFix/addThrowAnnotation/common/common_dep(stdlib)/common.kt new file mode 100644 index 00000000000..ad832554773 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/addThrowAnnotation/common/common_dep(stdlib)/common.kt @@ -0,0 +1,7 @@ +// "Add '@Throws' annotation" "true" + +class FooException : Exception() + +fun test() { + throw FooException() +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/addThrowAnnotation/common/common_dep(stdlib)/common.kt.after b/idea/testData/multiModuleQuickFix/addThrowAnnotation/common/common_dep(stdlib)/common.kt.after new file mode 100644 index 00000000000..3cae6f01d40 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/addThrowAnnotation/common/common_dep(stdlib)/common.kt.after @@ -0,0 +1,8 @@ +// "Add '@Throws' annotation" "true" + +class FooException : Exception() + +@Throws(FooException::class) +fun test() { + throw FooException() +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/addThrowAnnotation/js/js_dep(stdlib)/js.kt b/idea/testData/multiModuleQuickFix/addThrowAnnotation/js/js_dep(stdlib)/js.kt new file mode 100644 index 00000000000..0519fe8764a --- /dev/null +++ b/idea/testData/multiModuleQuickFix/addThrowAnnotation/js/js_dep(stdlib)/js.kt @@ -0,0 +1,5 @@ +// "Add '@Throws' annotation" "false" + +fun test() { + throw Throwable() +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/addThrowAnnotation/jvm/jvm_dep(stdlib)/jvm.kt b/idea/testData/multiModuleQuickFix/addThrowAnnotation/jvm/jvm_dep(stdlib)/jvm.kt new file mode 100644 index 00000000000..486246de773 --- /dev/null +++ b/idea/testData/multiModuleQuickFix/addThrowAnnotation/jvm/jvm_dep(stdlib)/jvm.kt @@ -0,0 +1,5 @@ +// "Add '@Throws' annotation" "true" + +fun test() { + throw Throwable() +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/addThrowAnnotation/jvm/jvm_dep(stdlib)/jvm.kt.after b/idea/testData/multiModuleQuickFix/addThrowAnnotation/jvm/jvm_dep(stdlib)/jvm.kt.after new file mode 100644 index 00000000000..ce71cd29dac --- /dev/null +++ b/idea/testData/multiModuleQuickFix/addThrowAnnotation/jvm/jvm_dep(stdlib)/jvm.kt.after @@ -0,0 +1,6 @@ +// "Add '@Throws' annotation" "true" + +@Throws(Throwable::class) +fun test() { + throw Throwable() +} \ No newline at end of file diff --git a/idea/testData/multiModuleQuickFix/addThrowAnnotation/jvmWithoutStdlib/jvm/jvm.kt b/idea/testData/multiModuleQuickFix/addThrowAnnotation/jvmWithoutStdlib/jvm/jvm.kt new file mode 100644 index 00000000000..0519fe8764a --- /dev/null +++ b/idea/testData/multiModuleQuickFix/addThrowAnnotation/jvmWithoutStdlib/jvm/jvm.kt @@ -0,0 +1,5 @@ +// "Add '@Throws' annotation" "false" + +fun test() { + throw Throwable() +} \ No newline at end of file diff --git a/idea/testData/quickfix/addReturnToUnusedLastExpressionInFunction/nothing.kt b/idea/testData/quickfix/addReturnToUnusedLastExpressionInFunction/nothing.kt index 051d4090b4c..83da2c88d01 100644 --- a/idea/testData/quickfix/addReturnToUnusedLastExpressionInFunction/nothing.kt +++ b/idea/testData/quickfix/addReturnToUnusedLastExpressionInFunction/nothing.kt @@ -1,5 +1,4 @@ // "Add 'return' before the expression" "false" -// ACTION: Add '@Throws' annotation fun test(): Nothing { throw Throwable("Error") diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java index cd7727345ff..69ad067d138 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTestGenerated.java @@ -209,6 +209,39 @@ public class QuickFixMultiModuleTestGenerated extends AbstractQuickFixMultiModul } } + @TestMetadata("idea/testData/multiModuleQuickFix/addThrowAnnotation") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddThrowAnnotation extends AbstractQuickFixMultiModuleTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + public void testAllFilesPresentInAddThrowAnnotation() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/multiModuleQuickFix/addThrowAnnotation"), Pattern.compile("^([^\\.]+)$"), null, true); + } + + @TestMetadata("common") + public void testCommon() throws Exception { + runTest("idea/testData/multiModuleQuickFix/addThrowAnnotation/common/"); + } + + @TestMetadata("js") + public void testJs() throws Exception { + runTest("idea/testData/multiModuleQuickFix/addThrowAnnotation/js/"); + } + + @TestMetadata("jvm") + public void testJvm() throws Exception { + runTest("idea/testData/multiModuleQuickFix/addThrowAnnotation/jvm/"); + } + + @TestMetadata("jvmWithoutStdlib") + public void testJvmWithoutStdlib() throws Exception { + runTest("idea/testData/multiModuleQuickFix/addThrowAnnotation/jvmWithoutStdlib/"); + } + } + @TestMetadata("idea/testData/multiModuleQuickFix/changeModifier") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)