From f4c51dc6d0ccdd46fc3351abdf3f956b1397a233 Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Tue, 29 Jan 2019 12:35:34 +0300 Subject: [PATCH] 191: Uast: support `convertToAlternatives` for `KtProperty` --- .../kotlin/KotlinUastLanguagePlugin.kt.191 | 52 ++++++++++------ .../testData/ManyAlternatives.kt.191 | 5 ++ .../tests/KotlinUastAlternativesTest.kt.191 | 61 +++++++++++++++++++ 3 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 plugins/uast-kotlin/testData/ManyAlternatives.kt.191 create mode 100644 plugins/uast-kotlin/tests/KotlinUastAlternativesTest.kt.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 ceebd9f608c..c98aceebc6a 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 @@ -219,7 +219,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { KotlinConverter.convertPsiElement(element, givenParent, expectedTypes) } else { - convertNonLocalProperty(original, givenParent, expectedTypes) + convertNonLocalProperty(original, givenParent, expectedTypes).firstOrNull() } is KtParameter -> el { @@ -279,7 +279,9 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { return convertElement(element, null, requiredTypes) } - override fun convertToAlternatives(element: PsiElement, requiredTypes: Array>) = when (element) { + @Suppress("UNCHECKED_CAST") + override fun convertToAlternatives(element: PsiElement, requiredTypes: Array>): Sequence = when { + (element is KtProperty && !element.isLocal) -> convertNonLocalProperty(element, null, requiredTypes) as Sequence else -> sequenceOf(convertElementWithParent(element, requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)) as? T).filterNotNull() } } @@ -298,18 +300,24 @@ internal inline fun Array>. internal fun Array>.isAssignableFrom(cls: Class<*>) = any { it.isAssignableFrom(cls) } -private fun convertNonLocalProperty(property: KtProperty, - givenParent: UElement?, - requiredType: Array> -): UElement? { +private fun convertNonLocalProperty( + property: KtProperty, + givenParent: UElement?, + requiredType: Array> +): Sequence { val methods = LightClassUtil.getLightClassPropertyMethods(property) - return methods.backingField?.let { backingField -> - with(requiredType) { - el { KotlinUField(backingField, (backingField as? KtLightElement<*,*>)?.kotlinOrigin, givenParent) } + 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 } } - } ?: methods.getter?.let { getter -> - KotlinUastLanguagePlugin().convertDeclaration(getter, givenParent, requiredType) - } + ) } internal object KotlinConverter { @@ -348,11 +356,7 @@ internal object KotlinConverter { is KtCatchClause -> el(build(::KotlinUCatchClause)) is KtVariableDeclaration -> if (element is KtProperty && !element.isLocal) { - el { - LightClassUtil.getLightClassBackingField(element)?.let { - KotlinUField(it, element, givenParent) - } - } + convertNonLocalProperty(element, givenParent, this).firstOrNull() } else { el { @@ -608,4 +612,16 @@ private fun expressionTypes(requiredType: Class?) = requiredType?. private fun elementTypes(requiredType: Class?) = requiredType?.let { arrayOf(it) } ?: DEFAULT_TYPES_LIST private fun Array>.nonEmptyOr(default: Array>) = takeIf { it.isNotEmpty() } - ?: default \ No newline at end of file + ?: default + +private fun Array>.accommodate(vararg makers: UElementAlternative): Sequence { + val makersSeq = makers.asSequence() + return this.asSequence() + .flatMap { requiredType -> makersSeq.filter { requiredType.isAssignableFrom(it.uType) } } + .distinct() + .mapNotNull { it.make.invoke() } +} + +private inline fun alternative(noinline make: () -> U?) = UElementAlternative(U::class.java, make) + +private class UElementAlternative(val uType: Class, val make: () -> U?) \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/ManyAlternatives.kt.191 b/plugins/uast-kotlin/testData/ManyAlternatives.kt.191 new file mode 100644 index 00000000000..5e354d313f9 --- /dev/null +++ b/plugins/uast-kotlin/testData/ManyAlternatives.kt.191 @@ -0,0 +1,5 @@ +class ClassA { + + var writebleProp: Int = 0 + +} \ No newline at end of file diff --git a/plugins/uast-kotlin/tests/KotlinUastAlternativesTest.kt.191 b/plugins/uast-kotlin/tests/KotlinUastAlternativesTest.kt.191 new file mode 100644 index 00000000000..377d13f461c --- /dev/null +++ b/plugins/uast-kotlin/tests/KotlinUastAlternativesTest.kt.191 @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.uast.test.kotlin + +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.uast.* +import org.junit.Test + +class KotlinUastAlternativesTest : AbstractKotlinUastTest() { + override fun check(testName: String, file: UFile) { + } + + @Test + fun testPropertyAlternatives() { + doTest("ManyAlternatives") { name, file -> + val index = file.psi.text.indexOf("writebleProp") + val ktProperty = PsiTreeUtil.getParentOfType(file.psi.findElementAt(index), KtProperty::class.java)!! + val plugin = UastLanguagePlugin.byLanguage(ktProperty.language)!! + + plugin.convertToAlternatives(ktProperty, arrayOf(UField::class.java)).let { + assertEquals( + "@org.jetbrains.annotations.NotNull private var writebleProp: int = 0", + it.joinToString(transform = UElement::asRenderString) + ) + } + + plugin.convertToAlternatives(ktProperty, arrayOf(UMethod::class.java, UField::class.java)).let { + assertEquals( + "public final fun getWritebleProp() : int = UastEmptyExpression, " + + "public final fun setWritebleProp(@null p: int) : void = UastEmptyExpression, " + + "@org.jetbrains.annotations.NotNull private var writebleProp: int = 0", + it.joinToString(transform = UElement::asRenderString) + ) + } + + plugin.convertToAlternatives(ktProperty, arrayOf(UField::class.java, UMethod::class.java)).let { + assertEquals( + "@org.jetbrains.annotations.NotNull private var writebleProp: int = 0, " + + "public final fun getWritebleProp() : int = UastEmptyExpression, " + + "public final fun setWritebleProp(@null p: int) : void = UastEmptyExpression", + it.joinToString(transform = UElement::asRenderString) + ) + } + + plugin.convertToAlternatives(ktProperty, arrayOf(UDeclaration::class.java)).let { + assertEquals( + "@org.jetbrains.annotations.NotNull private var writebleProp: int = 0, " + + "public final fun getWritebleProp() : int = UastEmptyExpression, " + + "public final fun setWritebleProp(@null p: int) : void = UastEmptyExpression", + it.joinToString(transform = UElement::asRenderString) + ) + } + + } + } + +} \ No newline at end of file