191: Uast: support convertToAlternatives for KtProperty

This commit is contained in:
Nicolay Mitropolsky
2019-01-29 12:35:34 +03:00
parent 114c3be632
commit f4c51dc6d0
3 changed files with 100 additions and 18 deletions
@@ -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<UParameter> {
@@ -279,7 +279,9 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
return convertElement(element, null, requiredTypes)
}
override fun <T : UElement> convertToAlternatives(element: PsiElement, requiredTypes: Array<out Class<out T>>) = when (element) {
@Suppress("UNCHECKED_CAST")
override fun <T : UElement> convertToAlternatives(element: PsiElement, requiredTypes: Array<out Class<out T>>): Sequence<T> = when {
(element is KtProperty && !element.isLocal) -> convertNonLocalProperty(element, null, requiredTypes) as Sequence<T>
else -> sequenceOf(convertElementWithParent(element, requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)) as? T).filterNotNull()
}
}
@@ -298,18 +300,24 @@ internal inline fun <reified ActualT : UElement> Array<out Class<out UElement>>.
internal fun Array<out Class<out UElement>>.isAssignableFrom(cls: Class<*>) = any { it.isAssignableFrom(cls) }
private fun convertNonLocalProperty(property: KtProperty,
givenParent: UElement?,
requiredType: Array<out Class<out UElement>>
): UElement? {
private fun convertNonLocalProperty(
property: KtProperty,
givenParent: UElement?,
requiredType: Array<out Class<out UElement>>
): Sequence<UElement> {
val methods = LightClassUtil.getLightClassPropertyMethods(property)
return methods.backingField?.let { backingField ->
with(requiredType) {
el<UField> { 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<UCatchClause>(build(::KotlinUCatchClause))
is KtVariableDeclaration ->
if (element is KtProperty && !element.isLocal) {
el<UField> {
LightClassUtil.getLightClassBackingField(element)?.let {
KotlinUField(it, element, givenParent)
}
}
convertNonLocalProperty(element, givenParent, this).firstOrNull()
}
else {
el<UVariable> {
@@ -608,4 +612,16 @@ private fun expressionTypes(requiredType: Class<out UElement>?) = requiredType?.
private fun elementTypes(requiredType: Class<out UElement>?) = requiredType?.let { arrayOf(it) } ?: DEFAULT_TYPES_LIST
private fun <T : UElement> Array<out Class<out T>>.nonEmptyOr(default: Array<out Class<out UElement>>) = takeIf { it.isNotEmpty() }
?: default
?: default
private fun <U : UElement> Array<out Class<out UElement>>.accommodate(vararg makers: UElementAlternative<out U>): Sequence<UElement> {
val makersSeq = makers.asSequence()
return this.asSequence()
.flatMap { requiredType -> makersSeq.filter { requiredType.isAssignableFrom(it.uType) } }
.distinct()
.mapNotNull { it.make.invoke() }
}
private inline fun <reified U : UElement> alternative(noinline make: () -> U?) = UElementAlternative(U::class.java, make)
private class UElementAlternative<U : UElement>(val uType: Class<U>, val make: () -> U?)
+5
View File
@@ -0,0 +1,5 @@
class ClassA {
var writebleProp: Int = 0
}
@@ -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<UElement>(ktProperty, arrayOf(UField::class.java)).let {
assertEquals(
"@org.jetbrains.annotations.NotNull private var writebleProp: int = 0",
it.joinToString(transform = UElement::asRenderString)
)
}
plugin.convertToAlternatives<UElement>(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<UElement>(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<UElement>(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)
)
}
}
}
}