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() convertNonLocalProperty(original, givenParent, expectedTypes).firstOrNull()
} }
is KtParameter -> el<UParameter> { is KtParameter -> convertParameter(original, givenParent, this).firstOrNull()
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 KtFile -> el<UFile> { KotlinUFile(original, this@KotlinUastLanguagePlugin) } is KtFile -> el<UFile> { KotlinUFile(original, this@KotlinUastLanguagePlugin) }
is FakeFileForLightClass -> el<UFile> { KotlinUFile(original.navigationElement, 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? { private fun convertEnumEntry(original: KtEnumEntry, givenParent: UElement?): UElement? {
return LightClassUtil.getLightClassBackingField(original)?.let { psiField -> return LightClassUtil.getLightClassBackingField(original)?.let { psiField ->
if (psiField is KtLightFieldImpl.KtLightEnumConstant) { if (psiField is KtLightFieldImpl.KtLightEnumConstant) {
@@ -282,6 +274,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T : UElement> convertToAlternatives(element: PsiElement, requiredTypes: Array<out Class<out T>>): Sequence<T> = when { 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 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() 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) } 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( private fun convertNonLocalProperty(
property: KtProperty, property: KtProperty,
givenParent: UElement?, givenParent: UElement?,
requiredType: Array<out Class<out UElement>> requiredType: Array<out Class<out UElement>>
): Sequence<UElement> { ): Sequence<UElement> =
val methods = LightClassUtil.getLightClassPropertyMethods(property) requiredType.accommodate(*convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(property), givenParent))
return requiredType.accommodate(
alternative {
methods.backingField?.let { KotlinUField(it, (it as? KtLightElement<*, *>)?.kotlinOrigin, givenParent) } private fun convertParameter(
}, element: KtParameter,
alternative { givenParent: UElement?,
// TODO: make it possible to use `convertDeclaration` without `KotlinUastLanguagePlugin` creation requiredTypes: Array<out Class<out UElement>>
methods.getter?.let { KotlinUastLanguagePlugin().convertDeclaration(it, givenParent, arrayOf(UMethod::class.java)) as? UMethod } ): Sequence<UElement> = requiredTypes.accommodate(
}, alternative uParam@{
alternative { val ownerFunction = element.ownerFunction as? KtFunction ?: return@uParam null
methods.setter?.let { KotlinUastLanguagePlugin().convertDeclaration(it, givenParent, arrayOf(UMethod::class.java)) as? UMethod } 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 object KotlinConverter {
internal tailrec fun unwrapElements(element: PsiElement?): PsiElement? = when (element) { 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 var writebleProp: Int = 0
@@ -6,7 +6,9 @@
package org.jetbrains.uast.test.kotlin package org.jetbrains.uast.test.kotlin
import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase.assertInstanceOf
import org.jetbrains.uast.* import org.jetbrains.uast.*
import org.junit.Test 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)
)
}
}
}
} }