diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt index b963290b9df..33caffbbf16 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationTargetFix.kt @@ -9,7 +9,9 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.searches.ReferencesSearch +import org.jetbrains.kotlin.asJava.LightClassUtil 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 @@ -85,11 +87,24 @@ private fun KtAnnotationEntry.getActualTargetList(): List { val targetList = AnnotationChecker.getActualTargetList(annotatedElement, null, BindingTraceContext()) - if (useSiteTarget == null) { - return targetList.defaultTargets + val useSiteTarget = this.useSiteTarget ?: return targetList.defaultTargets + val annotationUseSiteTarget = useSiteTarget.getAnnotationUseSiteTarget() + val target = KotlinTarget.USE_SITE_MAPPING[annotationUseSiteTarget] ?: return emptyList() + + if (annotationUseSiteTarget == AnnotationUseSiteTarget.FIELD) { + if (KotlinTarget.MEMBER_PROPERTY !in targetList.defaultTargets && KotlinTarget.TOP_LEVEL_PROPERTY !in targetList.defaultTargets) { + return emptyList() + } + val property = annotatedElement as? KtProperty + if (property != null && (LightClassUtil.getLightClassPropertyMethods(property).backingField == null || property.hasDelegate())) { + return emptyList() + } + } else { + if (target !in with(targetList) { defaultTargets + canBeSubstituted + onlyWithUseSiteTarget }) { + return emptyList() + } } - val target = KotlinTarget.USE_SITE_MAPPING[useSiteTarget?.getAnnotationUseSiteTarget()] ?: return emptyList() - if (target !in with(targetList) { defaultTargets + canBeSubstituted + onlyWithUseSiteTarget }) return emptyList() + return listOf(target) } diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_member.kt b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member.kt new file mode 100644 index 00000000000..9271f910ded --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member.kt @@ -0,0 +1,9 @@ +// "Add annotation target" "true" + +@Target +annotation class Ann + +class Test { + @field:Ann + var foo = "" +} \ No newline at end of file diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_member.kt.after b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member.kt.after new file mode 100644 index 00000000000..2d66ffcf9cb --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member.kt.after @@ -0,0 +1,9 @@ +// "Add annotation target" "true" + +@Target(AnnotationTarget.FIELD) +annotation class Ann + +class Test { + @field:Ann + var foo = "" +} \ No newline at end of file diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_with_delegate.kt b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_with_delegate.kt new file mode 100644 index 00000000000..a6e1bfce633 --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_with_delegate.kt @@ -0,0 +1,15 @@ +// "Add annotation target" "false" +// WITH_RUNTIME +// ACTION: Make internal +// ACTION: Make private +// ACTION: Make protected +// ERROR: '@field:' annotations could be applied only to properties with backing fields +// ERROR: This annotation is not applicable to target 'member property with delegate' and use site target '@field' + +@Target +annotation class Ann + +class Test { + @field:Ann + val baz: String by lazy { "" } +} \ No newline at end of file diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_without_backing.kt b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_without_backing.kt new file mode 100644 index 00000000000..295ccf0d11c --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_member_without_backing.kt @@ -0,0 +1,16 @@ +// "Add annotation target" "false" +// ACTION: Make internal +// ACTION: Make private +// ACTION: Make protected +// ACTION: Specify type explicitly +// ERROR: This annotation is not applicable to target 'member property without backing field or delegate' and use site target '@field' + +@Target +annotation class Ann + +class Test { + @field:Ann + var bar + get() = "" + set(p) {} +} \ No newline at end of file diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel.kt b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel.kt new file mode 100644 index 00000000000..9fe6992bc3f --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel.kt @@ -0,0 +1,7 @@ +// "Add annotation target" "true" + +@Target +annotation class Ann + +@field:Ann +var foo = "" \ No newline at end of file diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel.kt.after b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel.kt.after new file mode 100644 index 00000000000..ad4b845bab4 --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel.kt.after @@ -0,0 +1,7 @@ +// "Add annotation target" "true" + +@Target(AnnotationTarget.FIELD) +annotation class Ann + +@field:Ann +var foo = "" \ No newline at end of file diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_with_delegate.kt b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_with_delegate.kt new file mode 100644 index 00000000000..0cc354d89c6 --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_with_delegate.kt @@ -0,0 +1,12 @@ +// "Add annotation target" "false" +// WITH_RUNTIME +// ACTION: Make internal +// ACTION: Make private +// ERROR: '@field:' annotations could be applied only to properties with backing fields +// ERROR: This annotation is not applicable to target 'top level property with delegate' and use site target '@field' + +@Target +annotation class Ann + +@field:Ann +val baz: String by lazy { "" } \ No newline at end of file diff --git a/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_without_backing.kt b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_without_backing.kt new file mode 100644 index 00000000000..a46018c4d73 --- /dev/null +++ b/idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_without_backing.kt @@ -0,0 +1,13 @@ +// "Add annotation target" "false" +// ACTION: Make internal +// ACTION: Make private +// ACTION: Specify type explicitly +// ERROR: This annotation is not applicable to target 'top level property without backing field or delegate' and use site target '@field' + +@Target +annotation class Ann + +@field:Ann +var bar + get() = "" + set(p) {} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 58920cf93c6..0430dd24dd4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -306,6 +306,42 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("use-site_field_member.kt") + public void testUse_site_field_member() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addAnnotationTarget/use-site_field_member.kt"); + doTest(fileName); + } + + @TestMetadata("use-site_field_member_with_delegate.kt") + public void testUse_site_field_member_with_delegate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addAnnotationTarget/use-site_field_member_with_delegate.kt"); + doTest(fileName); + } + + @TestMetadata("use-site_field_member_without_backing.kt") + public void testUse_site_field_member_without_backing() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addAnnotationTarget/use-site_field_member_without_backing.kt"); + doTest(fileName); + } + + @TestMetadata("use-site_field_toplevel.kt") + public void testUse_site_field_toplevel() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel.kt"); + doTest(fileName); + } + + @TestMetadata("use-site_field_toplevel_with_delegate.kt") + public void testUse_site_field_toplevel_with_delegate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_with_delegate.kt"); + doTest(fileName); + } + + @TestMetadata("use-site_field_toplevel_without_backing.kt") + public void testUse_site_field_toplevel_without_backing() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addAnnotationTarget/use-site_field_toplevel_without_backing.kt"); + doTest(fileName); + } + @TestMetadata("use-site_file.kt") public void testUse_site_file() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addAnnotationTarget/use-site_file.kt");