From 9e2358e8ff0364f72164280f524119a35d05a988 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Tue, 29 Jan 2019 12:35:35 +0300 Subject: [PATCH] 191: Uast: support `convertToAlternatives` for `KtParameter` --- .../kotlin/KotlinUastLanguagePlugin.kt.191 | 62 ++++++++++------- .../ClassAnnotation.identifiers.txt.191 | 13 ++++ .../testData/ManyAlternatives.kt.191 | 2 +- .../tests/KotlinUastAlternativesTest.kt.191 | 67 +++++++++++++++++++ 4 files changed, 118 insertions(+), 26 deletions(-) create mode 100644 plugins/uast-kotlin/testData/ClassAnnotation.identifiers.txt.191 diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 index c98aceebc6a..5ce867eb3c5 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 @@ -222,16 +222,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { convertNonLocalProperty(original, givenParent, expectedTypes).firstOrNull() } - is KtParameter -> el { - val ownerFunction = original.ownerFunction as? KtFunction ?: return@el null - val lightMethod = LightClassUtil.getLightClassMethod(ownerFunction) ?: return@el null - val lightParameter = lightMethod.parameterList.parameters.find { it.name == original.name } ?: return@el null - KotlinUParameter(lightParameter, original, givenParent) - } ?: el { - if (!original.hasValOrVar()) return@el null - val lightField = LightClassUtil.getLightClassBackingField(original) ?: return@el null - KotlinUField(lightField, original, givenParent) - } + is KtParameter -> convertParameter(original, givenParent, this).firstOrNull() is KtFile -> el { KotlinUFile(original, this@KotlinUastLanguagePlugin) } is FakeFileForLightClass -> el { KotlinUFile(original.navigationElement, this@KotlinUastLanguagePlugin) } @@ -246,6 +237,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { } } + private fun convertEnumEntry(original: KtEnumEntry, givenParent: UElement?): UElement? { return LightClassUtil.getLightClassBackingField(original)?.let { psiField -> if (psiField is KtLightFieldImpl.KtLightEnumConstant) { @@ -282,6 +274,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { @Suppress("UNCHECKED_CAST") override fun convertToAlternatives(element: PsiElement, requiredTypes: Array>): Sequence = when { (element is KtProperty && !element.isLocal) -> convertNonLocalProperty(element, null, requiredTypes) as Sequence + element is KtParameter -> convertParameter(element, null, requiredTypes) as Sequence else -> sequenceOf(convertElementWithParent(element, requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)) as? T).filterNotNull() } } @@ -300,25 +293,44 @@ internal inline fun Array>. internal fun Array>.isAssignableFrom(cls: Class<*>) = any { it.isAssignableFrom(cls) } + +private fun convertToPropertyAlternatives( + methods: LightClassUtil.PropertyAccessorsPsiMethods?, + givenParent: UElement? +): Array> = if (methods != null) arrayOf( + alternative { + methods.backingField?.let { KotlinUField(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, givenParent) } + }, + alternative { + // TODO: make it possible to use `convertDeclaration` without `KotlinUastLanguagePlugin` creation + methods.getter?.let { KotlinUastLanguagePlugin().convertDeclaration(it, givenParent, arrayOf(UMethod::class.java)) as? UMethod } + }, + alternative { + methods.setter?.let { KotlinUastLanguagePlugin().convertDeclaration(it, givenParent, arrayOf(UMethod::class.java)) as? UMethod } + } +) else emptyArray() + private fun convertNonLocalProperty( property: KtProperty, givenParent: UElement?, requiredType: Array> -): Sequence { - val methods = LightClassUtil.getLightClassPropertyMethods(property) - return requiredType.accommodate( - alternative { - methods.backingField?.let { KotlinUField(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, givenParent) } - }, - alternative { - // TODO: make it possible to use `convertDeclaration` without `KotlinUastLanguagePlugin` creation - methods.getter?.let { KotlinUastLanguagePlugin().convertDeclaration(it, givenParent, arrayOf(UMethod::class.java)) as? UMethod } - }, - alternative { - methods.setter?.let { KotlinUastLanguagePlugin().convertDeclaration(it, givenParent, arrayOf(UMethod::class.java)) as? UMethod } - } - ) -} +): Sequence = + requiredType.accommodate(*convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(property), givenParent)) + + +private fun convertParameter( + element: KtParameter, + givenParent: UElement?, + requiredTypes: Array> +): Sequence = requiredTypes.accommodate( + alternative uParam@{ + val ownerFunction = element.ownerFunction as? KtFunction ?: return@uParam null + val lightMethod = LightClassUtil.getLightClassMethod(ownerFunction) ?: return@uParam null + val lightParameter = lightMethod.parameterList.parameters.find { it.name == element.name } ?: return@uParam null + KotlinUParameter(lightParameter, element, givenParent) + }, + *convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(element), givenParent) +) internal object KotlinConverter { internal tailrec fun unwrapElements(element: PsiElement?): PsiElement? = when (element) { diff --git a/plugins/uast-kotlin/testData/ClassAnnotation.identifiers.txt.191 b/plugins/uast-kotlin/testData/ClassAnnotation.identifiers.txt.191 new file mode 100644 index 00000000000..991b620ac1d --- /dev/null +++ b/plugins/uast-kotlin/testData/ClassAnnotation.identifiers.txt.191 @@ -0,0 +1,13 @@ +Test -> UAnnotation (fqName = null) +A -> UClass (name = A) +MyAnnotation -> UClass (name = MyAnnotation) +text -> UAnnotationMethod (name = text) +String -> USimpleNameReferenceExpression (identifier = String) +MyAnnotation -> UAnnotation (fqName = MyAnnotation) +B -> UClass (name = B) +MyAnnotation -> UAnnotation (fqName = MyAnnotation) +InB -> UClass (name = InB) +MyAnnotation -> UAnnotation (fqName = MyAnnotation) +object -> UClass (name = Companion) +MyAnnotation -> UAnnotation (fqName = MyAnnotation) +Obj -> UClass (name = Obj) diff --git a/plugins/uast-kotlin/testData/ManyAlternatives.kt.191 b/plugins/uast-kotlin/testData/ManyAlternatives.kt.191 index 5e354d313f9..2dcecd56d4e 100644 --- a/plugins/uast-kotlin/testData/ManyAlternatives.kt.191 +++ b/plugins/uast-kotlin/testData/ManyAlternatives.kt.191 @@ -1,4 +1,4 @@ -class ClassA { +class ClassA(justParam: Int, val paramAndProp: String) { var writebleProp: Int = 0 diff --git a/plugins/uast-kotlin/tests/KotlinUastAlternativesTest.kt.191 b/plugins/uast-kotlin/tests/KotlinUastAlternativesTest.kt.191 index 377d13f461c..ed08683d41b 100644 --- a/plugins/uast-kotlin/tests/KotlinUastAlternativesTest.kt.191 +++ b/plugins/uast-kotlin/tests/KotlinUastAlternativesTest.kt.191 @@ -6,7 +6,9 @@ package org.jetbrains.uast.test.kotlin import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase.assertInstanceOf import org.jetbrains.uast.* import org.junit.Test @@ -58,4 +60,69 @@ class KotlinUastAlternativesTest : AbstractKotlinUastTest() { } } + + @Test + fun testParamAndPropertylternatives() { + doTest("ManyAlternatives") { name, file -> + val index = file.psi.text.indexOf("paramAndProp") + val ktProperty = PsiTreeUtil.getParentOfType(file.psi.findElementAt(index), KtParameter::class.java)!! + val plugin = UastLanguagePlugin.byLanguage(ktProperty.language)!! + + plugin.convertToAlternatives(ktProperty, arrayOf(UField::class.java)).let { + assertInstanceOf(it.single(), UField::class.java) + assertEquals( + "@org.jetbrains.annotations.NotNull private final var paramAndProp: java.lang.String", + it.joinToString(transform = UElement::asRenderString) + ) + } + + plugin.convertToAlternatives(ktProperty, arrayOf(UParameter::class.java)).let { + assertInstanceOf(it.single(), UParameter::class.java) + assertEquals( + "@org.jetbrains.annotations.NotNull var paramAndProp: java.lang.String", + it.joinToString(transform = UElement::asRenderString) + ) + } + + plugin.convertToAlternatives(ktProperty, arrayOf(UElement::class.java)).let { + assertEquals( + "@org.jetbrains.annotations.NotNull var paramAndProp: java.lang.String, " + + "@org.jetbrains.annotations.NotNull private final var paramAndProp: java.lang.String, " + + "public final fun getParamAndProp() : java.lang.String = UastEmptyExpression", + it.joinToString(transform = UElement::asRenderString) + ) + } + + } + } + + @Test + fun testJustParamAlternatives() { + doTest("ManyAlternatives") { name, file -> + val index = file.psi.text.indexOf("justParam") + val ktProperty = PsiTreeUtil.getParentOfType(file.psi.findElementAt(index), KtParameter::class.java)!! + val plugin = UastLanguagePlugin.byLanguage(ktProperty.language)!! + + plugin.convertToAlternatives(ktProperty, arrayOf(UField::class.java)).let { + assertEquals("", it.joinToString(transform = UElement::asRenderString)) + } + + plugin.convertToAlternatives(ktProperty, arrayOf(UParameter::class.java)).let { + assertInstanceOf(it.single(), UParameter::class.java) + assertEquals( + "@org.jetbrains.annotations.NotNull var justParam: int", + it.joinToString(transform = UElement::asRenderString) + ) + } + + plugin.convertToAlternatives(ktProperty, arrayOf(UElement::class.java)).let { + assertEquals( + "@org.jetbrains.annotations.NotNull var justParam: int", + it.joinToString(transform = UElement::asRenderString) + ) + } + + } + } + } \ No newline at end of file