191: Uast: refactoring: all convert* methods were moved to KotlinConverter

This commit is contained in:
Nicolay Mitropolsky
2019-01-29 12:35:37 +03:00
parent 28e19ed8c7
commit 4df72b4f79
2 changed files with 236 additions and 177 deletions
@@ -42,6 +42,8 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.uast.*
import org.jetbrains.uast.expressions.UInjectionHost
import org.jetbrains.uast.kotlin.KotlinConverter.convertDeclaration
import org.jetbrains.uast.kotlin.KotlinConverter.convertDeclarationOrElement
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
import org.jetbrains.uast.kotlin.expressions.*
@@ -87,29 +89,6 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
return convertDeclarationOrElement(element, null, elementTypes(requiredType))
}
private fun convertDeclarationOrElement(
element: PsiElement,
givenParent: UElement?,
requiredType: Array<out Class<out UElement>>
): UElement? {
if (element is UElement) return element
if (element.isValid) {
element.getUserData(KOTLIN_CACHED_UELEMENT_KEY)?.get()?.let { cachedUElement ->
return if (requiredType.isAssignableFrom(cachedUElement.javaClass)) cachedUElement else null
}
}
val uElement = convertDeclaration(element, givenParent, requiredType)
?: KotlinConverter.convertPsiElement(element, givenParent, requiredType)
/*
if (uElement != null) {
element.putUserData(KOTLIN_CACHED_UELEMENT_KEY, WeakReference(uElement))
}
*/
return uElement
}
override fun getMethodCallExpression(
element: PsiElement,
containingClassFqName: String?,
@@ -150,101 +129,6 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
return UastLanguagePlugin.ResolvedConstructor(uExpression, method, containingClass)
}
internal fun convertDeclaration(element: PsiElement,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): UElement? {
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? = { ctor(element as P, givenParent) }
fun <P : PsiElement, K : KtElement> buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? =
{ ctor(element as P, ktElement, givenParent) }
fun <P : PsiElement, K : KtElement> buildKtOpt(ktElement: K?, ctor: (P, K?, UElement?) -> UElement): () -> UElement? =
{ ctor(element as P, ktElement, givenParent) }
val original = element.originalElement
return with(expectedTypes) {
when (original) {
is KtLightMethod -> el<UMethod>(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934
is KtLightClass -> when (original.kotlinOrigin) {
is KtEnumEntry -> el<UEnumConstant> {
convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent)
}
else -> el<UClass> { KotlinUClass.create(original, givenParent) }
}
is KtLightFieldImpl.KtLightEnumConstant -> el<UEnumConstant>(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant))
is KtLightField -> el<UField>(buildKtOpt(original.kotlinOrigin, ::KotlinUField))
is KtLightParameter -> el<UParameter>(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter))
is UastKotlinPsiParameter -> el<UParameter>(buildKt(original.ktParameter, ::KotlinUParameter))
is UastKotlinPsiVariable -> el<UVariable>(buildKt(original.ktElement, ::KotlinUVariable))
is KtEnumEntry -> el<UEnumConstant> {
convertEnumEntry(original, givenParent)
}
is KtClassOrObject -> convertClassOrObject(original, givenParent, this).firstOrNull()
is KtFunction ->
if (original.isLocal) {
el<ULambdaExpression> {
val parent = original.parent
if (parent is KtLambdaExpression) {
KotlinULambdaExpression(parent, givenParent) // your parent is the ULambdaExpression
} else if (original.name.isNullOrEmpty()) {
createLocalFunctionLambdaExpression(original, givenParent)
}
else {
val uDeclarationsExpression = createLocalFunctionDeclaration(original, givenParent)
val localFunctionVar = uDeclarationsExpression.declarations.single() as KotlinLocalFunctionUVariable
localFunctionVar.uastInitializer
}
}
}
else {
el<UMethod> {
val lightMethod = LightClassUtil.getLightClassMethod(original) ?: return null
convertDeclaration(lightMethod, givenParent, expectedTypes)
}
}
is KtPropertyAccessor -> el<UMethod> {
val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null
convertDeclaration(lightMethod, givenParent, expectedTypes)
}
is KtProperty ->
if (original.isLocal) {
KotlinConverter.convertPsiElement(element, givenParent, expectedTypes)
}
else {
convertNonLocalProperty(original, givenParent, expectedTypes).firstOrNull()
}
is KtParameter -> convertParameter(original, givenParent, this).firstOrNull()
is KtFile -> el<UFile> { KotlinUFile(original, this@KotlinUastLanguagePlugin) }
is FakeFileForLightClass -> el<UFile> { KotlinUFile(original.navigationElement, this@KotlinUastLanguagePlugin) }
is KtAnnotationEntry -> el<UAnnotation>(build(::KotlinUAnnotation))
is KtCallExpression ->
if (expectedTypes.isAssignableFrom(KotlinUNestedAnnotation::class.java) && !expectedTypes.isAssignableFrom(UCallExpression::class.java)) {
el<UAnnotation> { KotlinUNestedAnnotation.tryCreate(original, givenParent) }
} else null
is KtLightAnnotationForSourceEntry -> convertElement(original.kotlinOrigin, givenParent, expectedTypes)
else -> null
}
}
}
private fun convertEnumEntry(original: KtEnumEntry, givenParent: UElement?): UElement? {
return LightClassUtil.getLightClassBackingField(original)?.let { psiField ->
if (psiField is KtLightFieldImpl.KtLightEnumConstant) {
KotlinUEnumConstant(psiField, psiField.kotlinOrigin, givenParent)
}
else {
null
}
}
}
override fun isExpressionValueUsed(element: UExpression): Boolean {
return when (element) {
is KotlinUSimpleReferenceExpression.KotlinAccessorCallExpression -> element.setterValue != null
@@ -269,9 +153,9 @@ 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>
element is KtClassOrObject -> convertClassOrObject(element, null, requiredTypes) as Sequence<T>
(element is KtProperty && !element.isLocal) -> KotlinConverter.convertNonLocalProperty(element, null, requiredTypes) as Sequence<T>
element is KtParameter -> KotlinConverter.convertParameter(element, null, requiredTypes) as Sequence<T>
element is KtClassOrObject -> KotlinConverter.convertClassOrObject(element, null, requiredTypes) as Sequence<T>
else -> sequenceOf(convertElementWithParent(element, requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)) as? T).filterNotNull()
}
}
@@ -291,62 +175,6 @@ 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> =
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)
)
private fun convertClassOrObject(
element: KtClassOrObject,
givenParent: UElement?,
requiredTypes: Array<out Class<out UElement>>
): Sequence<UElement> {
val ktLightClass = element.toLightClass() ?: return emptySequence()
val uClass = KotlinUClass.create(ktLightClass, givenParent)
return requiredTypes.accommodate(
alternative { uClass },
alternative primaryConstructor@{
val primaryConstructor = element.primaryConstructor ?: return@primaryConstructor null
uClass.methods.asSequence()
.filter { it.sourcePsi == primaryConstructor }
.firstOrNull()
}
)
}
internal object KotlinConverter {
internal tailrec fun unwrapElements(element: PsiElement?): PsiElement? = when (element) {
@@ -598,6 +426,171 @@ internal object KotlinConverter {
}
}
private fun convertEnumEntry(original: KtEnumEntry, givenParent: UElement?): UElement? {
return LightClassUtil.getLightClassBackingField(original)?.let { psiField ->
if (psiField is KtLightFieldImpl.KtLightEnumConstant) {
KotlinUEnumConstant(psiField, psiField.kotlinOrigin, givenParent)
} else {
null
}
}
}
internal fun convertDeclaration(
element: PsiElement,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): UElement? {
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? = { ctor(element as P, givenParent) }
fun <P : PsiElement, K : KtElement> buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? =
{ ctor(element as P, ktElement, givenParent) }
fun <P : PsiElement, K : KtElement> buildKtOpt(ktElement: K?, ctor: (P, K?, UElement?) -> UElement): () -> UElement? =
{ ctor(element as P, ktElement, givenParent) }
val original = element.originalElement
return with(expectedTypes) {
when (original) {
is KtLightMethod -> el<UMethod>(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934
is KtLightClass -> when (original.kotlinOrigin) {
is KtEnumEntry -> el<UEnumConstant> {
convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent)
}
else -> el<UClass> { KotlinUClass.create(original, givenParent) }
}
is KtLightFieldImpl.KtLightEnumConstant -> el<UEnumConstant>(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant))
is KtLightField -> el<UField>(buildKtOpt(original.kotlinOrigin, ::KotlinUField))
is KtLightParameter -> el<UParameter>(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter))
is UastKotlinPsiParameter -> el<UParameter>(buildKt(original.ktParameter, ::KotlinUParameter))
is UastKotlinPsiVariable -> el<UVariable>(buildKt(original.ktElement, ::KotlinUVariable))
is KtEnumEntry -> el<UEnumConstant> {
convertEnumEntry(original, givenParent)
}
is KtClassOrObject -> convertClassOrObject(original, givenParent, this).firstOrNull()
is KtFunction ->
if (original.isLocal) {
el<ULambdaExpression> {
val parent = original.parent
if (parent is KtLambdaExpression) {
KotlinULambdaExpression(parent, givenParent) // your parent is the ULambdaExpression
} else if (original.name.isNullOrEmpty()) {
createLocalFunctionLambdaExpression(original, givenParent)
} else {
val uDeclarationsExpression = createLocalFunctionDeclaration(original, givenParent)
val localFunctionVar = uDeclarationsExpression.declarations.single() as KotlinLocalFunctionUVariable
localFunctionVar.uastInitializer
}
}
} else {
el<UMethod> {
val lightMethod = LightClassUtil.getLightClassMethod(original) ?: return null
convertDeclaration(lightMethod, givenParent, expectedTypes)
}
}
is KtPropertyAccessor -> el<UMethod> {
val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null
convertDeclaration(lightMethod, givenParent, expectedTypes)
}
is KtProperty ->
if (original.isLocal) {
KotlinConverter.convertPsiElement(element, givenParent, expectedTypes)
} else {
convertNonLocalProperty(original, givenParent, expectedTypes).firstOrNull()
}
is KtParameter -> convertParameter(original, givenParent, this).firstOrNull()
is KtFile -> el<UFile> { KotlinUFile(original) }
is FakeFileForLightClass -> el<UFile> { KotlinUFile(original.navigationElement) }
is KtAnnotationEntry -> el<UAnnotation>(build(::KotlinUAnnotation))
is KtCallExpression ->
if (expectedTypes.isAssignableFrom(KotlinUNestedAnnotation::class.java) && !expectedTypes.isAssignableFrom(UCallExpression::class.java)) {
el<UAnnotation> { KotlinUNestedAnnotation.tryCreate(original, givenParent) }
} else null
is KtLightAnnotationForSourceEntry -> convertDeclarationOrElement(original.kotlinOrigin, givenParent, expectedTypes)
else -> null
}
}
}
fun convertDeclarationOrElement(
element: PsiElement,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): UElement? {
if (element is UElement) return element
if (element.isValid) {
element.getUserData(KOTLIN_CACHED_UELEMENT_KEY)?.get()?.let { cachedUElement ->
return if (expectedTypes.isAssignableFrom(cachedUElement.javaClass)) cachedUElement else null
}
}
val uElement = convertDeclaration(element, givenParent, expectedTypes)
?: KotlinConverter.convertPsiElement(element, givenParent, expectedTypes)
/*
if (uElement != null) {
element.putUserData(KOTLIN_CACHED_UELEMENT_KEY, WeakReference(uElement))
}
*/
return uElement
}
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 { methods.getter?.let { convertDeclaration(it, givenParent, arrayOf(UMethod::class.java)) as? UMethod } },
alternative { methods.setter?.let { convertDeclaration(it, givenParent, arrayOf(UMethod::class.java)) as? UMethod } }
) else emptyArray()
fun convertNonLocalProperty(
property: KtProperty,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): Sequence<UElement> =
expectedTypes.accommodate(*convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(property), givenParent))
fun convertParameter(
element: KtParameter,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): Sequence<UElement> = expectedTypes.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)
)
fun convertClassOrObject(
element: KtClassOrObject,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): Sequence<UElement> {
val ktLightClass = element.toLightClass() ?: return emptySequence()
val uClass = KotlinUClass.create(ktLightClass, givenParent)
return expectedTypes.accommodate(
alternative { uClass },
alternative primaryConstructor@{
val primaryConstructor = element.primaryConstructor ?: return@primaryConstructor null
uClass.methods.asSequence()
.filter { it.sourcePsi == primaryConstructor }
.firstOrNull()
}
)
}
internal fun convertOrEmpty(expression: KtExpression?, parent: UElement?): UExpression {
return expression?.let { convertExpression(it, parent, DEFAULT_EXPRESSION_TYPES_LIST) } ?: UastEmptyExpression
}
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.uast.kotlin
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementWalkingVisitor
import org.jetbrains.kotlin.asJava.findFacadeClass
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.uast.*
import java.util.*
class KotlinUFile(
override val psi: KtFile,
override val languagePlugin: UastLanguagePlugin =
UastLanguagePlugin.byLanguage(KotlinLanguage.INSTANCE) ?: KotlinUastLanguagePlugin()
) : UFile, JvmDeclarationUElementPlaceholder {
override val packageName: String
get() = psi.packageFqName.asString()
override val annotations: List<UAnnotation>
get() = psi.annotationEntries.map { KotlinUAnnotation(it, this) }
override val javaPsi: PsiElement? = null
override val sourcePsi: PsiElement? = psi
override val allCommentsInFile by lz {
val comments = ArrayList<UComment>(0)
psi.accept(object : PsiRecursiveElementWalkingVisitor() {
override fun visitComment(comment: PsiComment) {
comments += UComment(comment, this@KotlinUFile)
}
})
comments
}
override val imports by lz { psi.importDirectives.map { KotlinUImportStatement(it, this) } }
override val classes by lz {
val facadeOrScriptClass = if (psi.isScript()) psi.script?.toLightClass() else psi.findFacadeClass()
val classes = psi.declarations.mapNotNull { (it as? KtClassOrObject)?.toLightClass()?.toUClass() }
(facadeOrScriptClass?.toUClass()?.let { listOf(it) } ?: emptyList()) + classes
}
private fun PsiClass.toUClass() = languagePlugin.convertOpt<UClass>(this, this@KotlinUFile)
}