From 95392121808d5ab22dd56e56a25adbf8f1c1f944 Mon Sep 17 00:00:00 2001 From: Felix Guo Date: Fri, 9 Mar 2018 20:37:46 +0300 Subject: [PATCH] Take Java source file into account in "Add annotation target" So #KT-22860 Fixed --- .../kotlin/resolve/AnnotationChecker.kt | 2 +- .../idea/quickfix/AddAnnotationTargetFix.kt | 43 ++++++++++++++++--- .../addAnnotationTarget/withJava.after.kt | 15 +++++++ .../withJava.before.Main.kt | 14 ++++++ .../withJava.before.UsageJava.java | 11 +++++ .../QuickFixMultiFileTestGenerated.java | 15 +++++++ 6 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 idea/testData/quickfix/addAnnotationTarget/withJava.after.kt create mode 100644 idea/testData/quickfix/addAnnotationTarget/withJava.before.Main.kt create mode 100644 idea/testData/quickfix/addAnnotationTarget/withJava.before.UsageJava.java diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt index d442865996f..8c068cb091d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt @@ -250,7 +250,7 @@ class AnnotationChecker( } } - private object TargetLists { + object TargetLists { val T_CLASSIFIER = targetList(CLASS) val T_TYPEALIAS = targetList(TYPEALIAS) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt index 4da46c1e9e5..1fc6d5d7924 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.quickfix import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project +import com.intellij.psi.* import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.searches.ReferencesSearch import org.jetbrains.kotlin.asJava.LightClassUtil @@ -14,15 +15,21 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.diagnostics.Diagnostic -import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors.WRONG_ANNOTATION_TARGET import org.jetbrains.kotlin.diagnostics.Errors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.idea.search.restrictToKotlinSources import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.AnnotationChecker +import org.jetbrains.kotlin.resolve.AnnotationChecker.Companion.TargetLists.EMPTY +import org.jetbrains.kotlin.resolve.AnnotationChecker.Companion.TargetLists.T_CLASSIFIER +import org.jetbrains.kotlin.resolve.AnnotationChecker.Companion.TargetLists.T_CONSTRUCTOR +import org.jetbrains.kotlin.resolve.AnnotationChecker.Companion.TargetLists.T_EXPRESSION +import org.jetbrains.kotlin.resolve.AnnotationChecker.Companion.TargetLists.T_LOCAL_VARIABLE +import org.jetbrains.kotlin.resolve.AnnotationChecker.Companion.TargetLists.T_MEMBER_FUNCTION +import org.jetbrains.kotlin.resolve.AnnotationChecker.Companion.TargetLists.T_MEMBER_PROPERTY +import org.jetbrains.kotlin.resolve.AnnotationChecker.Companion.TargetLists.T_VALUE_PARAMETER_WITHOUT_VAL import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTraceContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils @@ -74,15 +81,41 @@ private fun KtAnnotationEntry.getRequiredAnnotationTargets(annotationClass: KtCl val requiredTargets = getActualTargetList() if (requiredTargets.isEmpty()) return emptyList() - val searchScope = GlobalSearchScope.allScope(project).restrictToKotlinSources() + val searchScope = GlobalSearchScope.allScope(project) val otherReferenceRequiredTargets = ReferencesSearch.search(annotationClass, searchScope).mapNotNull { reference -> - reference.element.getNonStrictParentOfType()?.takeIf { it != this }?.getActualTargetList() + if (reference.element is KtNameReferenceExpression) { + // Kotlin annotation + reference.element.getNonStrictParentOfType()?.takeIf { it != this }?.getActualTargetList() + } else { + // Java annotation + (reference.element.parent as? PsiAnnotation)?.getActualTargetList() + } }.flatten().toSet() - val annotationTargetValueNames = AnnotationTarget.values().map { it.name } return (requiredTargets + otherReferenceRequiredTargets).distinct().filter { it.name in annotationTargetValueNames } } +private fun getActualTargetList(annotated: PsiTarget): AnnotationChecker.Companion.TargetList { + return when (annotated) { + is PsiClass -> T_CLASSIFIER + is PsiMethod -> + when { + annotated.isConstructor -> T_CONSTRUCTOR + else -> T_MEMBER_FUNCTION + } + is PsiExpression -> T_EXPRESSION + is PsiField -> T_MEMBER_PROPERTY(true, false) + is PsiLocalVariable -> T_LOCAL_VARIABLE + is PsiParameter -> T_VALUE_PARAMETER_WITHOUT_VAL + else -> EMPTY + } +} + +private fun PsiAnnotation.getActualTargetList(): List { + val annotated = parent.parent as? PsiTarget ?: return emptyList() + return getActualTargetList(annotated).defaultTargets +} + private fun KtAnnotationEntry.getActualTargetList(): List { val annotatedElement = getStrictParentOfType()?.owner as? KtElement ?: getStrictParentOfType()?.baseExpression diff --git a/idea/testData/quickfix/addAnnotationTarget/withJava.after.kt b/idea/testData/quickfix/addAnnotationTarget/withJava.after.kt new file mode 100644 index 00000000000..403b7abfa90 --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/withJava.after.kt @@ -0,0 +1,15 @@ +// "Add annotation target" "true" +// ERROR: This annotation is not applicable to target 'expression' + +package test + +@Target(AnnotationTarget.EXPRESSION, AnnotationTarget.CLASS, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.FUNCTION, AnnotationTarget.VALUE_PARAMETER) +annotation class AnnTarget + +fun println(v: Int) {} + +fun apply() { + var v = 0 + @AnnTarget v++ + println(v) +} \ No newline at end of file diff --git a/idea/testData/quickfix/addAnnotationTarget/withJava.before.Main.kt b/idea/testData/quickfix/addAnnotationTarget/withJava.before.Main.kt new file mode 100644 index 00000000000..f118d5a4274 --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/withJava.before.Main.kt @@ -0,0 +1,14 @@ +// "Add annotation target" "true" +// ERROR: This annotation is not applicable to target 'expression' + +package test + +annotation class AnnTarget + +fun println(v: Int) {} + +fun apply() { + var v = 0 + @AnnTarget v++ + println(v) +} \ No newline at end of file diff --git a/idea/testData/quickfix/addAnnotationTarget/withJava.before.UsageJava.java b/idea/testData/quickfix/addAnnotationTarget/withJava.before.UsageJava.java new file mode 100644 index 00000000000..ede5caf830e --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/withJava.before.UsageJava.java @@ -0,0 +1,11 @@ +package test; + +@AnnTarget +public class UsageJava { + @AnnTarget + public UsageJava () { + @AnnTarget int i = 10; + } + @AnnTarget + public void apply(@AnnTarget String in) {} +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 516c764cefe..b82894017f8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -25,6 +25,21 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); } + @TestMetadata("idea/testData/quickfix/addAnnotationTarget") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddAnnotationTarget extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInAddAnnotationTarget() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addAnnotationTarget"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("withJava.before.Main.kt") + public void testWithJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addAnnotationTarget/withJava.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } + @TestMetadata("idea/testData/quickfix/addJvmDefault") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)