diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.172 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.172 index f76719b3c00..40b684464ea 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.172 +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.172 @@ -27,8 +27,6 @@ import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade import org.jetbrains.kotlin.asJava.elements.* import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper -import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor -import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.KotlinLanguage @@ -80,7 +78,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { if (!element.isJvmElement) return null return convertDeclarationOrElement(element, parent, requiredType) } - + override fun convertElementWithParent(element: PsiElement, requiredType: Class?): UElement? { if (!element.isJvmElement) return null if (element is PsiFile) return convertDeclaration(element, null, requiredType) @@ -117,7 +115,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { val resolvedCall = element.getResolvedCall(element.analyze()) ?: return null val resultingDescriptor = resolvedCall.resultingDescriptor if (resultingDescriptor !is FunctionDescriptor || resultingDescriptor.name.asString() != methodName) return null - + val parent = element.parent val parentUElement = convertElementWithParent(parent, null) ?: return null @@ -134,7 +132,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { if (element !is KtCallExpression) return null val resolvedCall = element.getResolvedCall(element.analyze()) ?: return null val resultingDescriptor = resolvedCall.resultingDescriptor - if (resultingDescriptor !is ConstructorDescriptor + if (resultingDescriptor !is ConstructorDescriptor || resultingDescriptor.returnType.constructor.declarationDescriptor?.name?.asString() != fqName) { return null } @@ -221,17 +219,6 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { is KtFile -> el { KotlinUFile(original, this@KotlinUastLanguagePlugin) } is FakeFileForLightClass -> el { KotlinUFile(original.navigationElement, this@KotlinUastLanguagePlugin) } is KtAnnotationEntry -> el(build(::KotlinUAnnotation)) - is KtCallExpression -> - if (requiredType != null && UAnnotation::class.java.isAssignableFrom(requiredType)) { - el { - val classDescriptor = - (original.getResolvedCall(original.analyze())?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass - if (classDescriptor?.kind == ClassKind.ANNOTATION_CLASS) - KotlinUNestedAnnotation(original, givenParent, classDescriptor) - else - null - } - } else null is KtLightAnnotationForSourceEntry -> convertElement(original.kotlinOrigin, givenParent, requiredType) else -> null } diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt.172 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt.172 new file mode 100644 index 00000000000..8e467ef6dea --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUAnnotation.kt.172 @@ -0,0 +1,76 @@ +package org.jetbrains.uast.kotlin + +import com.intellij.psi.PsiClass +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.ValueArgument +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.uast.* + +class KotlinUAnnotation( + override val psi: KtAnnotationEntry, + givenParent: UElement? +) : KotlinAbstractUElement(givenParent), UAnnotation { + private val resolvedAnnotation: AnnotationDescriptor? by lz { psi.analyze()[BindingContext.ANNOTATION, psi] } + + private val resolvedCall: ResolvedCall<*>? by lz { psi.getResolvedCall(psi.analyze()) } + + override val qualifiedName: String? + get() = resolvedAnnotation?.fqName?.asString() + + override val attributeValues: List by lz { + resolvedCall?.valueArguments?.entries?.mapNotNull { + val arguments = it.value.arguments + val name = it.key.name.asString() + when { + arguments.size == 1 -> + KotlinUNamedExpression.create(name, arguments.first(), this) + arguments.size > 1 -> + KotlinUNamedExpression.create(name, arguments, this) + else -> null + } + } ?: emptyList() + } + + override fun resolve(): PsiClass? { + val descriptor = resolvedAnnotation?.annotationClass ?: return null + return descriptor.toSource()?.getMaybeLightElement(this) as? PsiClass + } + + override fun findAttributeValue(name: String?): UExpression? = + findDeclaredAttributeValue(name) ?: findAttributeDefaultValue(name ?: "value") + + fun findAttributeValueExpression(arg: ValueArgument): UExpression? { + val mapping = resolvedCall?.getArgumentMapping(arg) + return (mapping as? ArgumentMatch)?.let { match -> + val namedExpression = attributeValues.find { it.name == match.valueParameter.name.asString() } + namedExpression?.expression as? KotlinUVarargExpression ?: namedExpression + } + } + + override fun findDeclaredAttributeValue(name: String?): UExpression? { + return attributeValues.find { + it.name == name || + (name == null && it.name == "value") || + (name == "value" && it.name == null) + }?.expression + } + + private fun findAttributeDefaultValue(name: String): UExpression? { + val parameter = resolvedAnnotation + ?.annotationClass + ?.unsubstitutedPrimaryConstructor + ?.valueParameters + ?.find { it.name.asString() == name } ?: return null + + val defaultValue = (parameter.source.getPsi() as? KtParameter)?.defaultValue ?: return null + return getLanguagePlugin().convertWithParent(defaultValue) + } +} + diff --git a/plugins/uast-kotlin/testData/AnnotationComplex.kt.172 b/plugins/uast-kotlin/testData/AnnotationComplex.kt.172 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/uast-kotlin/testData/AnnotationComplex.log.txt.172 b/plugins/uast-kotlin/testData/AnnotationComplex.log.txt.172 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/uast-kotlin/testData/AnnotationComplex.render.txt.172 b/plugins/uast-kotlin/testData/AnnotationComplex.render.txt.172 new file mode 100644 index 00000000000..e69de29bb2d diff --git a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.172 b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.172 index e4a528099a0..11ab3106f14 100644 --- a/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.172 +++ b/plugins/uast-kotlin/tests/KotlinUastApiTest.kt.172 @@ -1,12 +1,10 @@ package org.jetbrains.uast.test.kotlin -import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiModifier import com.intellij.testFramework.UsefulTestCase import org.jetbrains.kotlin.asJava.toLightAnnotation import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType -import org.jetbrains.kotlin.psi.psiUtil.getValueParameterList import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.uast.* @@ -109,14 +107,6 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { } } - @Test fun testConvertTypeInAnnotation() { - doTest("TypeInAnnotation") { _, file -> - val index = file.psi.text.indexOf("Test") - val element = file.psi.findElementAt(index)!!.getParentOfType(false)!! - assertNotNull(element.getUastParentOfType(UAnnotation::class.java)) - } - } - @Test fun testElvisType() { doTest("ElvisType") { _, file -> val elvisExpression = file.findElementByText("text ?: return") @@ -195,68 +185,6 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { } } - @Test - fun testSimpleAnnotated() { - doTest("SimpleAnnotated") { _, file -> - file.findElementByTextFromPsi("@SinceKotlin(\"1.0\")\n val property: String = \"Mary\"").let { field -> - val annotation = field.annotations.assertedFind("kotlin.SinceKotlin") { it.qualifiedName } - Assert.assertEquals(annotation.findDeclaredAttributeValue("version")?.evaluateString(), "1.0") - } - } - } - - - fun UFile.checkUastSuperTypes(refText: String, superTypes: List) { - findElementByTextFromPsi(refText, false).let { - assertEquals("base classes", superTypes, it.uastSuperTypes.map { it.getQualifiedName() }) - } - } - - - @Test - fun testSuperTypes() { - doTest("SuperCalls") { _, file -> - file.checkUastSuperTypes("B", listOf("A")) - file.checkUastSuperTypes("O", listOf("A")) - file.checkUastSuperTypes("innerObject ", listOf("A")) - file.checkUastSuperTypes("InnerClass", listOf("A")) - file.checkUastSuperTypes("object : A(\"textForAnon\")", listOf("A")) - } - } - - @Test - fun testAnonymousSuperTypes() { - doTest("Anonymous") { _, file -> - file.checkUastSuperTypes("object : Runnable { override fun run() {} }", listOf("java.lang.Runnable")) - file.checkUastSuperTypes( - "object : Runnable, Closeable { override fun close() {} override fun run() {} }", - listOf("java.lang.Runnable", "java.io.Closeable") - ) - file.checkUastSuperTypes( - "object : InputStream(), Runnable { override fun read(): Int = 0; override fun run() {} }", - listOf("java.io.InputStream", "java.lang.Runnable") - ) - } - } - - @Test - fun testLiteralArraysTypes() { - doTest("AnnotationParameters") { _, file -> - file.findElementByTextFromPsi("intArrayOf(1, 2, 3)").let { field -> - Assert.assertEquals("PsiType:int[]", field.returnType.toString()) - } - file.findElementByTextFromPsi("[1, 2, 3]").let { field -> - Assert.assertEquals("PsiType:int[]", field.returnType.toString()) - Assert.assertEquals("PsiType:int", field.typeArguments.single().toString()) - } - file.findElementByTextFromPsi("[\"a\", \"b\", \"c\"]").let { field -> - Assert.assertEquals("PsiType:String[]", field.returnType.toString()) - Assert.assertEquals("PsiType:String", field.typeArguments.single().toString()) - } - - } - } - @Test fun testTypeAliases() { doTest("TypeAliases") { _, file -> @@ -266,26 +194,4 @@ class KotlinUastApiTest : AbstractKotlinUastTest() { Assert.assertTrue((originalTypeParameters.declarations.single() as UParameter).type.isValid) } } - - @Test - fun testNestedAnnotation() = doTest("AnnotationComplex") { _, file -> - file.findElementByTextFromPsi("@AnnotationArray(value = Annotation())") - .findElementByTextFromPsi("Annotation()") - .sourcePsiElement - .let { referenceExpression -> - val convertedUAnnotation = referenceExpression - .cast() - .toUElementOfType() - ?: throw AssertionError("haven't got annotation from $referenceExpression(${referenceExpression?.javaClass})") - - assertEquals("Annotation", convertedUAnnotation.qualifiedName) - val lightAnnotation = convertedUAnnotation.getAsJavaPsiElement(PsiAnnotation::class.java) - ?: throw AssertionError("can't get lightAnnotation from $convertedUAnnotation") - assertEquals("Annotation", lightAnnotation.qualifiedName) - } - } - - } - -fun Iterable.assertedFind(value: R, transform: (T) -> R): T = find { transform(it) == value } ?: throw AssertionError("'$value' not found, only ${this.joinToString { transform(it).toString() }}") diff --git a/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt.172 b/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt.172 index 2542f9a6c14..d86400a434a 100644 --- a/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt.172 +++ b/plugins/uast-kotlin/tests/SimpleKotlinRenderLogTest.kt.172 @@ -47,11 +47,9 @@ class SimpleKotlinRenderLogTest : AbstractKotlinRenderLogTest() { @Test fun testParametersWithDefaultValues() = doTest("ParametersWithDefaultValues") - @Test - fun testUnexpectedContainer() = doTest("UnexpectedContainerException") + @Test fun testUnexpectedContainer() = doTest("UnexpectedContainerException") { testName, file -> check(testName, file, false) } - @Test - fun testWhenStringLiteral() = doTest("WhenStringLiteral") + @Test fun testWhenStringLiteral() = doTest("WhenStringLiteral") { testName, file -> check(testName, file, false) } @Test fun testWhenAndDestructing() = doTest("WhenAndDestructing") { testName, file -> check(testName, file, false) } @@ -61,16 +59,4 @@ class SimpleKotlinRenderLogTest : AbstractKotlinRenderLogTest() { @Test fun testConstructors() = doTest("Constructors") - - @Test - fun testClassAnnotation() = doTest("ClassAnnotation") - - @Test - fun testReceiverFun() = doTest("ReceiverFun") - - @Test - fun testAnonymous() = doTest("Anonymous") - - @Test - fun testAnnotationComplex() = doTest("AnnotationComplex") }