Uast: property resolve arrayOf in nested annotations (KT-29434)

This commit is contained in:
Nicolay Mitropolsky
2019-01-23 21:25:40 +03:00
committed by xiexed
parent 4ffeff5e6c
commit a3ea7bcd3b
7 changed files with 129 additions and 14 deletions
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -204,8 +205,12 @@ class KotlinUFunctionCallExpression(
private fun isAnnotationArgumentArrayInitializer(): Boolean { private fun isAnnotationArgumentArrayInitializer(): Boolean {
val resolvedCall = resolvedCall ?: return false val resolvedCall = resolvedCall ?: return false
// KtAnnotationEntry -> KtValueArgumentList -> KtValueArgument -> arrayOf call // KtAnnotationEntry (or KtCallExpression when annotation is nested) -> KtValueArgumentList -> KtValueArgument -> arrayOf call
return psi.parents.elementAtOrNull(2) is KtAnnotationEntry && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall) return when (val elementAt2 = psi.parents.elementAtOrNull(2)) {
is KtAnnotationEntry -> true
is KtCallExpression -> elementAt2.getParentOfType<KtAnnotationEntry>(true, KtDeclaration::class.java) != null
else -> false
} && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall)
} }
override fun convertParent(): UElement? = super.convertParent().let { result -> override fun convertParent(): UElement? = super.convertParent().let { result ->
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -148,8 +149,12 @@ class KotlinUFunctionCallExpression(
private fun isAnnotationArgumentArrayInitializer(): Boolean { private fun isAnnotationArgumentArrayInitializer(): Boolean {
val resolvedCall = resolvedCall ?: return false val resolvedCall = resolvedCall ?: return false
// KtAnnotationEntry -> KtValueArgumentList -> KtValueArgument -> arrayOf call // KtAnnotationEntry (or KtCallExpression when annotation is nested) -> KtValueArgumentList -> KtValueArgument -> arrayOf call
return psi.parents.elementAtOrNull(2) is KtAnnotationEntry && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall) return when (val elementAt2 = psi.parents.elementAtOrNull(2)) {
is KtAnnotationEntry -> true
is KtCallExpression -> elementAt2.getParentOfType<KtAnnotationEntry>(true, KtDeclaration::class.java) != null
else -> false
} && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall)
} }
override fun convertParent(): UElement? = super.convertParent().let { result -> override fun convertParent(): UElement? = super.convertParent().let { result ->
+7 -4
View File
@@ -1,4 +1,4 @@
annotation class Annotation annotation class Annotation(vararg val strings: String)
@Annotation @Annotation
class A class A
@@ -8,10 +8,13 @@ annotation class AnnotationInner(val value: Annotation)
@AnnotationArray(Annotation()) @AnnotationArray(Annotation())
class B1 class B1
@AnnotationArray(value = Annotation()) @AnnotationArray(value = Annotation("sv1", "sv2"))
class B2 class B2
annotation class AnnotationArray(vararg val value: Annotation) annotation class AnnotationArray(vararg val value: Annotation)
@AnnotationArray(Annotation()) @AnnotationArray(Annotation(strings = arrayOf("sar1", "sar2")))
class C class C
@AnnotationArray(Annotation(strings = ["[sar]1", "[sar]2"]))
class C2
+21 -2
View File
@@ -1,5 +1,6 @@
UFile (package = ) UFile (package = )
UClass (name = Annotation) UClass (name = Annotation)
UAnnotationMethod (name = strings)
UClass (name = A) UClass (name = A)
UAnnotation (fqName = Annotation) UAnnotation (fqName = Annotation)
UAnnotationMethod (name = A) UAnnotationMethod (name = A)
@@ -15,16 +16,34 @@ UFile (package = )
UClass (name = B2) UClass (name = B2)
UAnnotation (fqName = AnnotationArray) UAnnotation (fqName = AnnotationArray)
UNamedExpression (name = value) UNamedExpression (name = value)
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 2))
UIdentifier (Identifier (Annotation)) UIdentifier (Identifier (Annotation))
USimpleNameReferenceExpression (identifier = <init>) USimpleNameReferenceExpression (identifier = <init>)
ULiteralExpression (value = "sv1")
ULiteralExpression (value = "sv2")
UAnnotationMethod (name = B2) UAnnotationMethod (name = B2)
UClass (name = AnnotationArray) UClass (name = AnnotationArray)
UAnnotationMethod (name = value) UAnnotationMethod (name = value)
UClass (name = C) UClass (name = C)
UAnnotation (fqName = AnnotationArray) UAnnotation (fqName = AnnotationArray)
UNamedExpression (name = value) UNamedExpression (name = value)
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1))
UIdentifier (Identifier (Annotation)) UIdentifier (Identifier (Annotation))
USimpleNameReferenceExpression (identifier = <init>) USimpleNameReferenceExpression (identifier = <init>)
UCallExpression (kind = UastCallKind(name='array_initializer'), argCount = 2))
UIdentifier (Identifier (arrayOf))
USimpleNameReferenceExpression (identifier = arrayOf)
ULiteralExpression (value = "sar1")
ULiteralExpression (value = "sar2")
UAnnotationMethod (name = C) UAnnotationMethod (name = C)
UClass (name = C2)
UAnnotation (fqName = AnnotationArray)
UNamedExpression (name = value)
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1))
UIdentifier (Identifier (Annotation))
USimpleNameReferenceExpression (identifier = <init>)
UCallExpression (kind = UastCallKind(name='array_initializer'), argCount = 2))
UIdentifier (Identifier ([))
ULiteralExpression (value = "[sar]1")
ULiteralExpression (value = "[sar]2")
UAnnotationMethod (name = C2)
@@ -1,4 +1,5 @@
public abstract annotation Annotation { public abstract annotation Annotation {
public abstract fun strings() : java.lang.String[] = UastEmptyExpression
} }
public final class A { public final class A {
@@ -24,3 +25,7 @@ public abstract annotation AnnotationArray {
public final class C { public final class C {
public fun C() = UastEmptyExpression public fun C() = UastEmptyExpression
} }
public final class C2 {
public fun C2() = UastEmptyExpression
}
+41 -2
View File
@@ -17,6 +17,7 @@ import org.jetbrains.uast.test.env.kotlin.findElementByTextFromPsi
import org.jetbrains.uast.visitor.AbstractUastVisitor import org.jetbrains.uast.visitor.AbstractUastVisitor
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import kotlin.test.fail as kfail
class KotlinUastApiTest : AbstractKotlinUastTest() { class KotlinUastApiTest : AbstractKotlinUastTest() {
@@ -297,8 +298,8 @@ class KotlinUastApiTest : AbstractKotlinUastTest() {
@Test @Test
fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file -> fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file ->
file.findElementByTextFromPsi<UElement>("@AnnotationArray(value = Annotation())") file.findElementByTextFromPsi<UElement>("@AnnotationArray(value = Annotation(\"sv1\", \"sv2\"))")
.findElementByTextFromPsi<UElement>("Annotation()") .findElementByTextFromPsi<UElement>("Annotation(\"sv1\", \"sv2\")")
.sourcePsiElement .sourcePsiElement
.let { referenceExpression -> .let { referenceExpression ->
val convertedUAnnotation = referenceExpression val convertedUAnnotation = referenceExpression
@@ -315,6 +316,44 @@ class KotlinUastApiTest : AbstractKotlinUastTest() {
} }
} }
@Test
fun testNestedAnnotationParameters() = doTest("AnnotationComplex") { _, file ->
fun UFile.annotationAndParam(refText: String, check: (PsiAnnotation, String?) -> Unit) {
findElementByTextFromPsi<UElement>(refText)
.let { expression ->
val (annotation: PsiAnnotation, paramname: String?) =
getContainingAnnotationEntry(expression) ?: kfail("annotation not found for '$refText' ($expression)")
check(annotation, paramname)
}
}
file.annotationAndParam("sv1") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals(null, paramname)
}
file.annotationAndParam("sv2") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals(null, paramname)
}
file.annotationAndParam("sar1") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals("strings", paramname)
}
file.annotationAndParam("sar2") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals("strings", paramname)
}
file.annotationAndParam("[sar]1") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals("strings", paramname)
}
file.annotationAndParam("[sar]2") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals("strings", paramname)
}
}
@Test @Test
fun testParametersDisorder() = doTest("ParametersDisorder") { _, file -> fun testParametersDisorder() = doTest("ParametersDisorder") { _, file ->
@@ -17,6 +17,7 @@ import org.jetbrains.uast.test.env.kotlin.findElementByTextFromPsi
import org.jetbrains.uast.visitor.AbstractUastVisitor import org.jetbrains.uast.visitor.AbstractUastVisitor
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import kotlin.test.fail as kfail
class KotlinUastApiTest : AbstractKotlinUastTest() { class KotlinUastApiTest : AbstractKotlinUastTest() {
@@ -297,8 +298,8 @@ class KotlinUastApiTest : AbstractKotlinUastTest() {
@Test @Test
fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file -> fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file ->
file.findElementByTextFromPsi<UElement>("@AnnotationArray(value = Annotation())") file.findElementByTextFromPsi<UElement>("@AnnotationArray(value = Annotation(\"sv1\", \"sv2\"))")
.findElementByTextFromPsi<UElement>("Annotation()") .findElementByTextFromPsi<UElement>("Annotation(\"sv1\", \"sv2\")")
.sourcePsiElement .sourcePsiElement
.let { referenceExpression -> .let { referenceExpression ->
val convertedUAnnotation = referenceExpression val convertedUAnnotation = referenceExpression
@@ -315,6 +316,44 @@ class KotlinUastApiTest : AbstractKotlinUastTest() {
} }
} }
@Test
fun testNestedAnnotationParameters() = doTest("AnnotationComplex") { _, file ->
fun UFile.annotationAndParam(refText: String, check: (PsiAnnotation, String?) -> Unit) {
findElementByTextFromPsi<UElement>(refText)
.let { expression ->
val (annotation: PsiAnnotation, paramname: String?) =
getContainingAnnotationEntry(expression) ?: kfail("annotation not found for '$refText' ($expression)")
check(annotation, paramname)
}
}
file.annotationAndParam("sv1") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals(null, paramname)
}
file.annotationAndParam("sv2") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals(null, paramname)
}
file.annotationAndParam("sar1") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals("strings", paramname)
}
file.annotationAndParam("sar2") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals("strings", paramname)
}
file.annotationAndParam("[sar]1") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals("strings", paramname)
}
file.annotationAndParam("[sar]2") { annotation, paramname ->
assertEquals("Annotation", annotation.qualifiedName)
assertEquals("strings", paramname)
}
}
@Test @Test
fun testParametersDisorder() = doTest("ParametersDisorder") { _, file -> fun testParametersDisorder() = doTest("ParametersDisorder") { _, file ->