Take Java source file into account in "Add annotation target"

So #KT-22860 Fixed
This commit is contained in:
Felix Guo
2018-03-09 20:37:46 +03:00
committed by Mikhail Glukhikh
parent 5559a6edd3
commit 9539212180
6 changed files with 94 additions and 6 deletions
@@ -250,7 +250,7 @@ class AnnotationChecker(
}
}
private object TargetLists {
object TargetLists {
val T_CLASSIFIER = targetList(CLASS)
val T_TYPEALIAS = targetList(TYPEALIAS)
@@ -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<KtAnnotationEntry>()?.takeIf { it != this }?.getActualTargetList()
if (reference.element is KtNameReferenceExpression) {
// Kotlin annotation
reference.element.getNonStrictParentOfType<KtAnnotationEntry>()?.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<KotlinTarget> {
val annotated = parent.parent as? PsiTarget ?: return emptyList()
return getActualTargetList(annotated).defaultTargets
}
private fun KtAnnotationEntry.getActualTargetList(): List<KotlinTarget> {
val annotatedElement = getStrictParentOfType<KtModifierList>()?.owner as? KtElement
?: getStrictParentOfType<KtAnnotatedExpression>()?.baseExpression
@@ -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)
}
@@ -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
<caret>@AnnTarget v++
println(v)
}
@@ -0,0 +1,11 @@
package test;
@AnnTarget
public class UsageJava {
@AnnTarget
public UsageJava () {
@AnnTarget int i = 10;
}
@AnnTarget
public void apply(@AnnTarget String in) {}
}
@@ -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)