From fd4f4ef85365ac8fdf0efc6bddbb67b903a1bed4 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Tue, 27 Feb 2018 15:11:38 +0300 Subject: [PATCH] KotlinLanguageInjector: support for nested annotations (#KT-21753) --- .../idea/injection/KotlinLanguageInjector.kt | 18 +++++++--- .../kotlin/psi/KotlinInjectionTest.kt | 33 +++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt index dcea6bca1f6..4101b02f8f9 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/injection/KotlinLanguageInjector.kt @@ -276,9 +276,14 @@ class KotlinLanguageInjector( private fun injectInAnnotationCall(host: KtElement): InjectionInfo? { if (!annotationInjectionsEnabled) return null val argument = host.parent as? KtValueArgument ?: return null - val annotationEntry = argument.parent.parent as? KtAnnotationEntry ?: return null + val annotationEntry = argument.parent.parent as? KtCallElement ?: return null if (!fastCheckInjectionsExists(annotationEntry)) return null - val calleeReference = annotationEntry.calleeExpression?.constructorReferenceExpression?.mainReference + val calleeExpression = annotationEntry.calleeExpression ?: return null + val calleeReference = when (calleeExpression) { + is KtConstructorCalleeExpression -> calleeExpression.constructorReferenceExpression?.mainReference // for top annotations + is KtNameReferenceExpression -> calleeExpression.mainReference // for nested annotations + else -> return null + } val callee = calleeReference?.resolve() when (callee) { is KtFunction -> return injectionForKotlinCall(argument, callee, calleeReference) @@ -378,8 +383,13 @@ class KotlinLanguageInjector( }, configuration) }, false) - private fun fastCheckInjectionsExists(annotationEntry: KtAnnotationEntry): Boolean { - val referencedName = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referencedName ?: return false + private fun fastCheckInjectionsExists(annotationEntry: KtCallElement): Boolean { + val referencedName = when (annotationEntry) { + is KtAnnotationEntry -> // for top annotations + (annotationEntry.typeReference?.typeElement as? KtUserType)?.referencedName ?: return false + else -> // for nested annotations + (annotationEntry.calleeExpression as? KtNameReferenceExpression)?.getReferencedName() ?: return false + } val annotationShortName = annotationEntry.containingKtFile.aliasImportMap()[referencedName].singleOrNull() ?: referencedName return annotationShortName in injectableTargetClassShortNames.value } diff --git a/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt b/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt index 4ccb608e10c..026afb8559f 100644 --- a/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/psi/KotlinInjectionTest.kt @@ -519,6 +519,39 @@ class KotlinInjectionTest : AbstractInjectionTest() { """) } + fun testInjectionInJavaNestedAnnotation() { + myFixture.addClass( + """ + package myinjection; + + public @interface InHtml { + String html(); + } + """ + ) + myFixture.addClass( + """ + package myinjection; + + public @interface InHtmls { + InHtml[] htmls(); + } + """ + ) + doAnnotationInjectionTest( + injectedLanguage = HTMLLanguage.INSTANCE.id, + pattern = """psiMethod().withName("html").withParameters().definedInClass("myinjection.InHtml")""", + kotlinCode = """ + import myinjection.InHtml + import myinjection.InHtmls + + @InHtmls(htmls = [InHtml(html = "l>")]) + fun foo() { + } + """ + ) + } + fun testInjectionInAliasedJavaAnnotation() { myFixture.addClass(""" @interface InHtml {