191: Uast: support convertToAlternatives for KtParameter

This commit is contained in:
Nicolay Mitropolsky
2019-01-29 12:35:35 +03:00
parent f4c51dc6d0
commit 9e2358e8ff
4 changed files with 118 additions and 26 deletions
@@ -222,16 +222,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
convertNonLocalProperty(original, givenParent, expectedTypes).firstOrNull()
}
is KtParameter -> el<UParameter> {
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<UField> {
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<UFile> { KotlinUFile(original, this@KotlinUastLanguagePlugin) }
is FakeFileForLightClass -> el<UFile> { 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 <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>
element is KtParameter -> convertParameter(element, null, requiredTypes) as Sequence<T>
else -> sequenceOf(convertElementWithParent(element, requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)) as? T).filterNotNull()
}
}
@@ -300,25 +293,44 @@ 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 convertToPropertyAlternatives(
methods: LightClassUtil.PropertyAccessorsPsiMethods?,
givenParent: UElement?
): Array<UElementAlternative<*>> = 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<out Class<out UElement>>
): Sequence<UElement> {
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<UElement> =
requiredType.accommodate(*convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(property), givenParent))
private fun convertParameter(
element: KtParameter,
givenParent: UElement?,
requiredTypes: Array<out Class<out UElement>>
): Sequence<UElement> = 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) {
@@ -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)
+1 -1
View File
@@ -1,4 +1,4 @@
class ClassA {
class ClassA(justParam: Int, val paramAndProp: String) {
var writebleProp: Int = 0
@@ -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<UElement>(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<UElement>(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<UElement>(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<UElement>(ktProperty, arrayOf(UField::class.java)).let {
assertEquals("", it.joinToString(transform = UElement::asRenderString))
}
plugin.convertToAlternatives<UElement>(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<UElement>(ktProperty, arrayOf(UElement::class.java)).let {
assertEquals(
"@org.jetbrains.annotations.NotNull var justParam: int",
it.joinToString(transform = UElement::asRenderString)
)
}
}
}
}