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.Visibilities
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -204,8 +205,12 @@ class KotlinUFunctionCallExpression(
private fun isAnnotationArgumentArrayInitializer(): Boolean {
val resolvedCall = resolvedCall ?: return false
// KtAnnotationEntry -> KtValueArgumentList -> KtValueArgument -> arrayOf call
return psi.parents.elementAtOrNull(2) is KtAnnotationEntry && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall)
// KtAnnotationEntry (or KtCallExpression when annotation is nested) -> KtValueArgumentList -> KtValueArgument -> arrayOf call
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 ->
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
@@ -148,8 +149,12 @@ class KotlinUFunctionCallExpression(
private fun isAnnotationArgumentArrayInitializer(): Boolean {
val resolvedCall = resolvedCall ?: return false
// KtAnnotationEntry -> KtValueArgumentList -> KtValueArgument -> arrayOf call
return psi.parents.elementAtOrNull(2) is KtAnnotationEntry && CompileTimeConstantUtils.isArrayFunctionCall(resolvedCall)
// KtAnnotationEntry (or KtCallExpression when annotation is nested) -> KtValueArgumentList -> KtValueArgument -> arrayOf call
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 ->
+7 -4
View File
@@ -1,4 +1,4 @@
annotation class Annotation
annotation class Annotation(vararg val strings: String)
@Annotation
class A
@@ -8,10 +8,13 @@ annotation class AnnotationInner(val value: Annotation)
@AnnotationArray(Annotation())
class B1
@AnnotationArray(value = Annotation())
@AnnotationArray(value = Annotation("sv1", "sv2"))
class B2
annotation class AnnotationArray(vararg val value: Annotation)
@AnnotationArray(Annotation())
class C
@AnnotationArray(Annotation(strings = arrayOf("sar1", "sar2")))
class C
@AnnotationArray(Annotation(strings = ["[sar]1", "[sar]2"]))
class C2
+21 -2
View File
@@ -1,5 +1,6 @@
UFile (package = )
UClass (name = Annotation)
UAnnotationMethod (name = strings)
UClass (name = A)
UAnnotation (fqName = Annotation)
UAnnotationMethod (name = A)
@@ -15,16 +16,34 @@ UFile (package = )
UClass (name = B2)
UAnnotation (fqName = AnnotationArray)
UNamedExpression (name = value)
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0))
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 2))
UIdentifier (Identifier (Annotation))
USimpleNameReferenceExpression (identifier = <init>)
ULiteralExpression (value = "sv1")
ULiteralExpression (value = "sv2")
UAnnotationMethod (name = B2)
UClass (name = AnnotationArray)
UAnnotationMethod (name = value)
UClass (name = C)
UAnnotation (fqName = AnnotationArray)
UNamedExpression (name = value)
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0))
UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 1))
UIdentifier (Identifier (Annotation))
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)
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 fun strings() : java.lang.String[] = UastEmptyExpression
}
public final class A {
@@ -24,3 +25,7 @@ public abstract annotation AnnotationArray {
public final class C {
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.junit.Assert
import org.junit.Test
import kotlin.test.fail as kfail
class KotlinUastApiTest : AbstractKotlinUastTest() {
@@ -297,8 +298,8 @@ class KotlinUastApiTest : AbstractKotlinUastTest() {
@Test
fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file ->
file.findElementByTextFromPsi<UElement>("@AnnotationArray(value = Annotation())")
.findElementByTextFromPsi<UElement>("Annotation()")
file.findElementByTextFromPsi<UElement>("@AnnotationArray(value = Annotation(\"sv1\", \"sv2\"))")
.findElementByTextFromPsi<UElement>("Annotation(\"sv1\", \"sv2\")")
.sourcePsiElement
.let { 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
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.junit.Assert
import org.junit.Test
import kotlin.test.fail as kfail
class KotlinUastApiTest : AbstractKotlinUastTest() {
@@ -297,8 +298,8 @@ class KotlinUastApiTest : AbstractKotlinUastTest() {
@Test
fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file ->
file.findElementByTextFromPsi<UElement>("@AnnotationArray(value = Annotation())")
.findElementByTextFromPsi<UElement>("Annotation()")
file.findElementByTextFromPsi<UElement>("@AnnotationArray(value = Annotation(\"sv1\", \"sv2\"))")
.findElementByTextFromPsi<UElement>("Annotation(\"sv1\", \"sv2\")")
.sourcePsiElement
.let { 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
fun testParametersDisorder() = doTest("ParametersDisorder") { _, file ->