UAST: split KotlinConverter into a separate file

This commit is contained in:
Jinseong Jeon
2021-05-20 23:39:38 -07:00
committed by Ilya Kirillov
parent 0395294933
commit 8b93727f64
2 changed files with 602 additions and 573 deletions
@@ -0,0 +1,601 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.kotlin
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiEnumConstant
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.asJava.findFacadeClass
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.uast.*
import org.jetbrains.uast.expressions.UInjectionHost
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
import org.jetbrains.uast.kotlin.declarations.KotlinUMethodWithFakeLightDelegate
import org.jetbrains.uast.kotlin.expressions.*
import org.jetbrains.uast.kotlin.psi.*
internal object KotlinConverter {
internal tailrec fun unwrapElements(element: PsiElement?): PsiElement? = when (element) {
is KtValueArgumentList -> unwrapElements(element.parent)
is KtValueArgument -> unwrapElements(element.parent)
is KtDeclarationModifierList -> unwrapElements(element.parent)
is KtContainerNode -> unwrapElements(element.parent)
is KtSimpleNameStringTemplateEntry -> unwrapElements(element.parent)
is KtLightParameterList -> unwrapElements(element.parent)
is KtTypeElement -> unwrapElements(element.parent)
is KtSuperTypeList -> unwrapElements(element.parent)
is KtFinallySection -> unwrapElements(element.parent)
is KtAnnotatedExpression -> unwrapElements(element.parent)
is KtWhenConditionWithExpression -> unwrapElements(element.parent)
else -> element
}
private val identifiersTokens = setOf(
KtTokens.IDENTIFIER, KtTokens.CONSTRUCTOR_KEYWORD, KtTokens.OBJECT_KEYWORD,
KtTokens.THIS_KEYWORD, KtTokens.SUPER_KEYWORD,
KtTokens.GET_KEYWORD, KtTokens.SET_KEYWORD
)
internal fun convertPsiElement(
element: PsiElement?,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): UElement? {
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? {
return {
@Suppress("UNCHECKED_CAST")
ctor(element as P, givenParent)
}
}
return with (expectedTypes) { when (element) {
is KtParameterList -> el<UDeclarationsExpression> {
val resolveProviderService = ServiceManager.getService(element.project, KotlinUastResolveProviderService::class.java)
val declarationsExpression = KotlinUDeclarationsExpression(null, givenParent, null) { resolveProviderService }
declarationsExpression.apply {
declarations = element.parameters.mapIndexed { i, p ->
KotlinUParameter(UastKotlinPsiParameter.create(p, element, declarationsExpression, i), p, this)
}
}
}
is KtClassBody -> el<UExpressionList>(build(KotlinUExpressionList.Companion::createClassBody))
is KtCatchClause -> el<UCatchClause>(build(::KotlinUCatchClause))
is KtVariableDeclaration ->
if (element is KtProperty && !element.isLocal) {
convertNonLocalProperty(element, givenParent, this).firstOrNull()
}
else {
el<UVariable> {
convertVariablesDeclaration(element, givenParent).declarations.singleOrNull()
}
}
is KtExpression -> convertExpression(element, givenParent, expectedTypes)
is KtLambdaArgument -> element.getLambdaExpression()?.let { convertExpression(it, givenParent, expectedTypes) }
is KtLightElementBase -> {
val expression = element.kotlinOrigin
when (expression) {
is KtExpression -> convertExpression(expression, givenParent, expectedTypes)
else -> el<UExpression> { UastEmptyExpression(givenParent) }
}
}
is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry ->
el<ULiteralExpression>(build(::KotlinStringULiteralExpression))
is KtStringTemplateEntry ->
element.expression?.let { convertExpression(it, givenParent, expectedTypes) }
?: expr<UExpression> { UastEmptyExpression(givenParent) }
is KtWhenEntry -> el<USwitchClauseExpressionWithBody>(build(::KotlinUSwitchEntry))
is KtWhenCondition -> convertWhenCondition(element, givenParent, expectedTypes)
is KtTypeReference ->
expectedTypes.accommodate(
alternative { LazyKotlinUTypeReferenceExpression(element, givenParent) },
alternative { convertReceiverParameter(element) }
).firstOrNull()
is KtConstructorDelegationCall ->
el<UCallExpression> { KotlinUFunctionCallExpression(element, givenParent) }
is KtSuperTypeCallEntry ->
el<UExpression> {
(element.getParentOfType<KtClassOrObject>(true)?.parent as? KtObjectLiteralExpression)
?.toUElementOfType<UExpression>()
?: KotlinUFunctionCallExpression(element, givenParent)
}
is KtImportDirective -> el<UImportStatement>(build(::KotlinUImportStatement))
else -> {
if (element is LeafPsiElement) {
if (element.elementType in identifiersTokens)
if (element.elementType != KtTokens.OBJECT_KEYWORD ||
element.getParentOfType<KtObjectDeclaration>(false)?.nameIdentifier == null
)
el<UIdentifier>(build(::KotlinUIdentifier))
else null
else if (element.elementType in KtTokens.OPERATIONS && element.parent is KtOperationReferenceExpression)
el<UIdentifier>(build(::KotlinUIdentifier))
else if (element.elementType == KtTokens.LBRACKET && element.parent is KtCollectionLiteralExpression)
el<UIdentifier> {
UIdentifier(
element,
KotlinUCollectionLiteralExpression(
element.parent as KtCollectionLiteralExpression,
null
)
)
}
else null
} else null
}
}}
}
internal fun convertReceiverParameter(receiver: KtTypeReference): UParameter? {
val call = (receiver.parent as? KtCallableDeclaration) ?: return null
if (call.receiverTypeReference != receiver) return null
return call.toUElementOfType<UMethod>()?.uastParameters?.firstOrNull()
}
internal fun convertEntry(
entry: KtStringTemplateEntry,
givenParent: UElement?,
requiredType: Array<out Class<out UElement>>
): UExpression? {
return with(requiredType) {
if (entry is KtStringTemplateEntryWithExpression) {
expr<UExpression> {
convertOrEmpty(entry.expression, givenParent)
}
}
else {
expr<ULiteralExpression> {
if (entry is KtEscapeStringTemplateEntry)
KotlinStringULiteralExpression(entry, givenParent, entry.unescapedValue)
else
KotlinStringULiteralExpression(entry, givenParent)
}
}
}
}
var forceUInjectionHost = Registry.`is`("kotlin.uast.force.uinjectionhost", false)
@TestOnly
set(value) {
field = value
}
internal fun convertExpression(
expression: KtExpression,
givenParent: UElement?,
requiredType: Array<out Class<out UElement>>
): UExpression? {
fun <P : PsiElement> build(ctor: (P, UElement?) -> UExpression): () -> UExpression? {
return {
@Suppress("UNCHECKED_CAST")
ctor(expression as P, givenParent)
}
}
return with (requiredType) { when (expression) {
is KtVariableDeclaration -> expr<UDeclarationsExpression>(build(::convertVariablesDeclaration))
is KtStringTemplateExpression -> {
when {
forceUInjectionHost || requiredType.contains(UInjectionHost::class.java) ->
expr<UInjectionHost> { KotlinStringTemplateUPolyadicExpression(expression, givenParent) }
expression.entries.isEmpty() -> {
expr<ULiteralExpression> { KotlinStringULiteralExpression(expression, givenParent, "") }
}
expression.entries.size == 1 -> convertEntry(expression.entries[0], givenParent, requiredType)
else ->
expr<KotlinStringTemplateUPolyadicExpression> { KotlinStringTemplateUPolyadicExpression(expression, givenParent) }
}
}
is KtDestructuringDeclaration -> expr<UDeclarationsExpression> {
val declarationsExpression = KotlinUDestructuringDeclarationExpression(givenParent, expression)
declarationsExpression.apply {
val tempAssignment = KotlinULocalVariable(
UastKotlinPsiVariable.create(expression, declarationsExpression),
expression,
declarationsExpression
)
val destructuringAssignments = expression.entries.mapIndexed { i, entry ->
val psiFactory = KtPsiFactory(expression.project)
val initializer = psiFactory.createAnalyzableExpression("${tempAssignment.name}.component${i + 1}()",
expression.containingFile)
initializer.destructuringDeclarationInitializer = true
KotlinULocalVariable(
UastKotlinPsiVariable.create(entry, tempAssignment.javaPsi, declarationsExpression, initializer),
entry,
declarationsExpression
)
}
declarations = listOf(tempAssignment) + destructuringAssignments
}
}
is KtLabeledExpression -> expr<ULabeledExpression>(build(::KotlinULabeledExpression))
is KtClassLiteralExpression -> expr<UClassLiteralExpression>(build(::KotlinUClassLiteralExpression))
is KtObjectLiteralExpression -> expr<UObjectLiteralExpression>(build(::KotlinUObjectLiteralExpression))
is KtDotQualifiedExpression -> expr<UQualifiedReferenceExpression>(build(::KotlinUQualifiedReferenceExpression))
is KtSafeQualifiedExpression -> expr<UQualifiedReferenceExpression>(build(::KotlinUSafeQualifiedExpression))
is KtSimpleNameExpression -> expr<USimpleNameReferenceExpression>(build(::KotlinUSimpleReferenceExpression))
is KtCallExpression -> expr<UCallExpression>(build(::KotlinUFunctionCallExpression))
is KtCollectionLiteralExpression -> expr<UCallExpression>(build(::KotlinUCollectionLiteralExpression))
is KtBinaryExpression -> {
if (expression.operationToken == KtTokens.ELVIS) {
expr<UExpressionList>(build(::createElvisExpression))
}
else expr<UBinaryExpression>(build(::KotlinUBinaryExpression))
}
is KtParenthesizedExpression -> expr<UParenthesizedExpression>(build(::KotlinUParenthesizedExpression))
is KtPrefixExpression -> expr<UPrefixExpression>(build(::KotlinUPrefixExpression))
is KtPostfixExpression -> expr<UPostfixExpression>(build(::KotlinUPostfixExpression))
is KtThisExpression -> expr<UThisExpression>(build(::KotlinUThisExpression))
is KtSuperExpression -> expr<USuperExpression>(build(::KotlinUSuperExpression))
is KtCallableReferenceExpression -> expr<UCallableReferenceExpression>(build(::KotlinUCallableReferenceExpression))
is KtIsExpression -> expr<UBinaryExpressionWithType>(build(::KotlinUTypeCheckExpression))
is KtIfExpression -> expr<UIfExpression>(build(::KotlinUIfExpression))
is KtWhileExpression -> expr<UWhileExpression>(build(::KotlinUWhileExpression))
is KtDoWhileExpression -> expr<UDoWhileExpression>(build(::KotlinUDoWhileExpression))
is KtForExpression -> expr<UForEachExpression>(build(::KotlinUForEachExpression))
is KtWhenExpression -> expr<USwitchExpression>(build(::KotlinUSwitchExpression))
is KtBreakExpression -> expr<UBreakExpression>(build(::KotlinUBreakExpression))
is KtContinueExpression -> expr<UContinueExpression>(build(::KotlinUContinueExpression))
is KtReturnExpression -> expr<UReturnExpression>(build(::KotlinUReturnExpression))
is KtThrowExpression -> expr<UThrowExpression>(build(::KotlinUThrowExpression))
is KtBlockExpression -> expr<UBlockExpression> {
if (expression.parent is KtFunctionLiteral
&& expression.parent.parent is KtLambdaExpression
&& givenParent !is KotlinULambdaExpression
) {
KotlinULambdaExpression(expression.parent.parent as KtLambdaExpression, givenParent).body
} else
KotlinUBlockExpression(expression, givenParent)
}
is KtConstantExpression -> expr<ULiteralExpression>(build(::KotlinULiteralExpression))
is KtTryExpression -> expr<UTryExpression>(build(::KotlinUTryExpression))
is KtArrayAccessExpression -> expr<UArrayAccessExpression>(build(::KotlinUArrayAccessExpression))
is KtLambdaExpression -> expr<ULambdaExpression>(build(::KotlinULambdaExpression))
is KtBinaryExpressionWithTypeRHS -> expr<UBinaryExpressionWithType>(build(::KotlinUBinaryExpressionWithType))
is KtClassOrObject -> expr<UDeclarationsExpression> {
expression.toLightClass()?.let { lightClass ->
KotlinUDeclarationsExpression(givenParent).apply {
declarations = listOf(KotlinUClass.create(lightClass, this))
}
} ?: UastEmptyExpression(givenParent)
}
is KtFunction -> if (expression.name.isNullOrEmpty()) {
expr<ULambdaExpression>(build(::createLocalFunctionLambdaExpression))
}
else {
expr<UDeclarationsExpression>(build(::createLocalFunctionDeclaration))
}
is KtAnnotatedExpression -> {
expression.baseExpression
?.let { convertExpression(it, givenParent, requiredType) }
?: expr<UExpression>(build(::UnknownKotlinExpression))
}
else -> expr<UExpression>(build(::UnknownKotlinExpression))
}}
}
internal fun convertWhenCondition(
condition: KtWhenCondition,
givenParent: UElement?,
requiredType: Array<out Class<out UElement>>
): UExpression? {
return with(requiredType) {
when (condition) {
is KtWhenConditionInRange -> expr<UBinaryExpression> {
KotlinCustomUBinaryExpression(condition, givenParent).apply {
leftOperand = KotlinStringUSimpleReferenceExpression("it", this)
operator = when {
condition.isNegated -> KotlinBinaryOperators.NOT_IN
else -> KotlinBinaryOperators.IN
}
rightOperand = convertOrEmpty(condition.rangeExpression, this)
}
}
is KtWhenConditionIsPattern -> expr<UBinaryExpression> {
KotlinCustomUBinaryExpressionWithType(condition, givenParent).apply {
operand = KotlinStringUSimpleReferenceExpression("it", this)
operationKind = when {
condition.isNegated -> KotlinBinaryExpressionWithTypeKinds.NEGATED_INSTANCE_CHECK
else -> UastBinaryExpressionWithTypeKind.INSTANCE_CHECK
}
val typeRef = condition.typeReference
typeReference = typeRef?.let {
LazyKotlinUTypeReferenceExpression(it, this) { typeRef.toPsiType(this, boxed = true) }
}
}
}
is KtWhenConditionWithExpression ->
condition.expression?.let { convertExpression(it, givenParent, requiredType) }
else -> expr<UExpression> { UastEmptyExpression(givenParent) }
}
}
}
private fun convertEnumEntry(original: KtEnumEntry, givenParent: UElement?): UElement? {
return LightClassUtil.getLightClassBackingField(original)?.let { psiField ->
if (psiField is KtLightField && psiField is PsiEnumConstant) {
KotlinUEnumConstant(psiField, psiField.kotlinOrigin, givenParent)
} else {
null
}
}
}
internal fun convertDeclaration(
element: PsiElement,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): UElement? {
val original = element.originalElement
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? = {
@Suppress("UNCHECKED_CAST")
ctor(original as P, givenParent)
}
fun <P : PsiElement, K : KtElement> buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? = {
@Suppress("UNCHECKED_CAST")
ctor(original as P, ktElement, givenParent)
}
fun <P : PsiElement, K : KtElement> buildKtOpt(ktElement: K?, ctor: (P, K?, UElement?) -> UElement): () -> UElement? = {
@Suppress("UNCHECKED_CAST")
ctor(original as P, ktElement, givenParent)
}
return with(expectedTypes) {
when (original) {
is KtLightMethod -> el<UMethod>(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934
is UastFakeLightMethod -> el<UMethod> {
val ktFunction = original.original
if (ktFunction.isLocal)
convertDeclaration(ktFunction, givenParent, expectedTypes)
else
KotlinUMethodWithFakeLightDelegate(ktFunction, original, givenParent)
}
is UastFakeLightPrimaryConstructor ->
convertFakeLightConstructorAlternatives(original, givenParent, expectedTypes).firstOrNull()
is KtLightClass -> when (original.kotlinOrigin) {
is KtEnumEntry -> el<UEnumConstant> {
convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent)
}
else -> el<UClass> { KotlinUClass.create(original, givenParent) }
}
is KtLightField ->
if (original is PsiEnumConstant)
el<UEnumConstant>(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant))
else
el<UField>(buildKtOpt(original.kotlinOrigin, ::KotlinUField))
is KtLightParameter -> el<UParameter>(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter))
is UastKotlinPsiParameter -> el<UParameter>(buildKt(original.ktParameter, ::KotlinUParameter))
is UastKotlinPsiParameterBase<*> -> el<UParameter> {
original.ktOrigin.safeAs<KtTypeReference>()?.let { convertReceiverParameter(it) }
}
is UastKotlinPsiVariable -> el<ULocalVariable>(buildKt(original.ktElement, ::KotlinULocalVariable))
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)
if (lightMethod != null)
convertDeclaration(lightMethod, givenParent, expectedTypes)
else {
val ktLightClass = getLightClassForFakeMethod(original) ?: return null
KotlinUMethodWithFakeLightDelegate(original, ktLightClass, givenParent)
}
}
}
is KtPropertyAccessor -> el<UMethod> {
val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null
convertDeclaration(lightMethod, givenParent, expectedTypes)
}
is KtProperty ->
if (original.isLocal) {
convertPsiElement(original, givenParent, expectedTypes)
} else {
convertNonLocalProperty(original, givenParent, expectedTypes).firstOrNull()
}
is KtParameter -> convertParameter(original, givenParent, this).firstOrNull()
is KtFile -> convertKtFile(original, givenParent, this).firstOrNull()
is FakeFileForLightClass -> el<UFile> { KotlinUFile(original.navigationElement, kotlinUastPlugin) }
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)
is KtDelegatedSuperTypeEntry -> el<KotlinSupertypeDelegationUExpression> {
KotlinSupertypeDelegationUExpression(original, givenParent)
}
else -> null
}
}
}
internal fun convertFakeLightConstructorAlternatives(
original: UastFakeLightPrimaryConstructor,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): Sequence<UElement> {
return expectedTypes.accommodate(
alternative { convertDeclaration(original.original, givenParent, expectedTypes) as? UClass },
alternative { KotlinConstructorUMethod(original.original, original, original.original, givenParent) }
)
}
private fun getLightClassForFakeMethod(original: KtFunction): KtLightClass? {
if (original.isLocal) return null
return getContainingLightClass(original)
}
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)
?: 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 lightMethod = when (val ownerFunction = element.ownerFunction) {
is KtFunction -> LightClassUtil.getLightClassMethod(ownerFunction)
?: getLightClassForFakeMethod(ownerFunction)
?.takeIf { !it.isAnnotationType }
?.let { UastFakeLightMethod(ownerFunction, it) }
is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethod(ownerFunction)
else -> null
} ?: return@uParam null
val lightParameter = lightMethod.parameterList.parameters.find { it.name == element.name } ?: return@uParam null
KotlinUParameter(lightParameter, element, givenParent)
},
alternative catch@{
val uCatchClause = element.parent?.parent?.safeAs<KtCatchClause>()?.toUElementOfType<UCatchClause>() ?: return@catch null
uCatchClause.parameters.firstOrNull { it.sourcePsi == element }
},
*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()
}
)
}
fun convertKtFile(
element: KtFile,
givenParent: UElement?,
requiredTypes: Array<out Class<out UElement>>
): Sequence<UElement> = requiredTypes.accommodate(
alternative { KotlinUFile(element, kotlinUastPlugin) },
alternative { element.findFacadeClass()?.let { KotlinUClass.create(it, givenParent) } }
)
internal fun convertOrEmpty(expression: KtExpression?, parent: UElement?): UExpression {
return expression?.let { convertExpression(it, parent, DEFAULT_EXPRESSION_TYPES_LIST) } ?: UastEmptyExpression(parent)
}
internal fun convertOrNull(expression: KtExpression?, parent: UElement?): UExpression? {
return if (expression != null) convertExpression(expression, parent, DEFAULT_EXPRESSION_TYPES_LIST) else null
}
internal fun KtPsiFactory.createAnalyzableExpression(text: String, context: PsiElement): KtExpression =
createAnalyzableProperty("val x = $text", context).initializer ?: error("Failed to create expression from text: '$text'")
internal fun KtPsiFactory.createAnalyzableProperty(text: String, context: PsiElement): KtProperty =
createAnalyzableDeclaration(text, context)
internal fun <TDeclaration : KtDeclaration> KtPsiFactory.createAnalyzableDeclaration(text: String, context: PsiElement): TDeclaration {
val file = createAnalyzableFile("dummy.kt", text, context)
val declarations = file.declarations
assert(declarations.size == 1) { "${declarations.size} declarations in $text" }
@Suppress("UNCHECKED_CAST")
return declarations.first() as TDeclaration
}
private fun convertVariablesDeclaration(
psi: KtVariableDeclaration,
parent: UElement?
): UDeclarationsExpression {
val declarationsExpression = parent as? KotlinUDeclarationsExpression
?: psi.parent.toUElementOfType<UDeclarationsExpression>() as? KotlinUDeclarationsExpression
?: KotlinUDeclarationsExpression(null, parent, psi)
val parentPsiElement = parent?.javaPsi //TODO: looks weird. mb look for the first non-null `javaPsi` in `parents` ?
val variable = KotlinUAnnotatedLocalVariable(
UastKotlinPsiVariable.create(psi, parentPsiElement, declarationsExpression), psi, declarationsExpression) { annotationParent ->
psi.annotationEntries.map { KotlinUAnnotation(it, annotationParent) }
}
return declarationsExpression.apply { declarations = listOf(variable) }
}
}
@@ -19,36 +19,19 @@ package org.jetbrains.uast.kotlin
import com.intellij.lang.Language
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiEnumConstant
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.asJava.findFacadeClass
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
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.declarations.KotlinUMethodWithFakeLightDelegate
import org.jetbrains.uast.kotlin.expressions.*
import org.jetbrains.uast.kotlin.psi.*
import org.jetbrains.uast.kotlin.psi.UastFakeLightPrimaryConstructor
var PsiElement.destructuringDeclarationInitializer: Boolean? by UserDataProperty(Key.create("kotlin.uast.destructuringDeclarationInitializer"))
@@ -158,561 +141,6 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
}
}
internal object KotlinConverter {
internal tailrec fun unwrapElements(element: PsiElement?): PsiElement? = when (element) {
is KtValueArgumentList -> unwrapElements(element.parent)
is KtValueArgument -> unwrapElements(element.parent)
is KtDeclarationModifierList -> unwrapElements(element.parent)
is KtContainerNode -> unwrapElements(element.parent)
is KtSimpleNameStringTemplateEntry -> unwrapElements(element.parent)
is KtLightParameterList -> unwrapElements(element.parent)
is KtTypeElement -> unwrapElements(element.parent)
is KtSuperTypeList -> unwrapElements(element.parent)
is KtFinallySection -> unwrapElements(element.parent)
is KtAnnotatedExpression -> unwrapElements(element.parent)
is KtWhenConditionWithExpression -> unwrapElements(element.parent)
else -> element
}
private val identifiersTokens = setOf(
KtTokens.IDENTIFIER, KtTokens.CONSTRUCTOR_KEYWORD, KtTokens.OBJECT_KEYWORD,
KtTokens.THIS_KEYWORD, KtTokens.SUPER_KEYWORD,
KtTokens.GET_KEYWORD, KtTokens.SET_KEYWORD
)
internal fun convertPsiElement(element: PsiElement?,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): UElement? {
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? {
return {
@Suppress("UNCHECKED_CAST")
ctor(element as P, givenParent)
}
}
return with (expectedTypes) { when (element) {
is KtParameterList -> el<UDeclarationsExpression> {
val resolveProviderService = ServiceManager.getService(element.project, KotlinUastResolveProviderService::class.java)
val declarationsExpression = KotlinUDeclarationsExpression(null, givenParent, null) { resolveProviderService }
declarationsExpression.apply {
declarations = element.parameters.mapIndexed { i, p ->
KotlinUParameter(UastKotlinPsiParameter.create(p, element, declarationsExpression, i), p, this)
}
}
}
is KtClassBody -> el<UExpressionList>(build(KotlinUExpressionList.Companion::createClassBody))
is KtCatchClause -> el<UCatchClause>(build(::KotlinUCatchClause))
is KtVariableDeclaration ->
if (element is KtProperty && !element.isLocal) {
convertNonLocalProperty(element, givenParent, this).firstOrNull()
}
else {
el<UVariable> {
convertVariablesDeclaration(element, givenParent).declarations.singleOrNull()
}
}
is KtExpression -> KotlinConverter.convertExpression(element, givenParent, expectedTypes)
is KtLambdaArgument -> element.getLambdaExpression()?.let { KotlinConverter.convertExpression(it, givenParent, expectedTypes) }
is KtLightElementBase -> {
val expression = element.kotlinOrigin
when (expression) {
is KtExpression -> KotlinConverter.convertExpression(expression, givenParent, expectedTypes)
else -> el<UExpression> { UastEmptyExpression(givenParent) }
}
}
is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> el<ULiteralExpression>(build(::KotlinStringULiteralExpression))
is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, expectedTypes) } ?: expr<UExpression> { UastEmptyExpression(givenParent) }
is KtWhenEntry -> el<USwitchClauseExpressionWithBody>(build(::KotlinUSwitchEntry))
is KtWhenCondition -> convertWhenCondition(element, givenParent, expectedTypes)
is KtTypeReference ->
expectedTypes.accommodate(
alternative { LazyKotlinUTypeReferenceExpression(element, givenParent) },
alternative { convertReceiverParameter(element) }
).firstOrNull()
is KtConstructorDelegationCall ->
el<UCallExpression> { KotlinUFunctionCallExpression(element, givenParent) }
is KtSuperTypeCallEntry ->
el<UExpression> {
(element.getParentOfType<KtClassOrObject>(true)?.parent as? KtObjectLiteralExpression)
?.toUElementOfType<UExpression>()
?: KotlinUFunctionCallExpression(element, givenParent)
}
is KtImportDirective -> el<UImportStatement>(build(::KotlinUImportStatement))
else -> {
if (element is LeafPsiElement) {
if (element.elementType in identifiersTokens)
if (element.elementType != KtTokens.OBJECT_KEYWORD || element.getParentOfType<KtObjectDeclaration>(false)?.nameIdentifier == null)
el<UIdentifier>(build(::KotlinUIdentifier))
else null
else if (element.elementType in KtTokens.OPERATIONS && element.parent is KtOperationReferenceExpression)
el<UIdentifier>(build(::KotlinUIdentifier))
else if (element.elementType == KtTokens.LBRACKET && element.parent is KtCollectionLiteralExpression)
el<UIdentifier> {
UIdentifier(
element,
KotlinUCollectionLiteralExpression(
element.parent as KtCollectionLiteralExpression,
null
)
)
}
else null
} else null
}
}}
}
internal fun convertReceiverParameter(receiver: KtTypeReference): UParameter? {
val call = (receiver.parent as? KtCallableDeclaration) ?: return null
if (call.receiverTypeReference != receiver) return null
return call.toUElementOfType<UMethod>()?.uastParameters?.firstOrNull()
}
internal fun convertEntry(entry: KtStringTemplateEntry,
givenParent: UElement?,
requiredType: Array<out Class<out UElement>>
): UExpression? {
return with(requiredType) {
if (entry is KtStringTemplateEntryWithExpression) {
expr<UExpression> {
KotlinConverter.convertOrEmpty(entry.expression, givenParent)
}
}
else {
expr<ULiteralExpression> {
if (entry is KtEscapeStringTemplateEntry)
KotlinStringULiteralExpression(entry, givenParent, entry.unescapedValue)
else
KotlinStringULiteralExpression(entry, givenParent)
}
}
}
}
var forceUInjectionHost = Registry.`is`("kotlin.uast.force.uinjectionhost", false)
@TestOnly
set(value) {
field = value
}
internal fun convertExpression(expression: KtExpression,
givenParent: UElement?,
requiredType: Array<out Class<out UElement>>
): UExpression? {
fun <P : PsiElement> build(ctor: (P, UElement?) -> UExpression): () -> UExpression? {
return {
@Suppress("UNCHECKED_CAST")
ctor(expression as P, givenParent)
}
}
return with (requiredType) { when (expression) {
is KtVariableDeclaration -> expr<UDeclarationsExpression>(build(::convertVariablesDeclaration))
is KtStringTemplateExpression -> {
when {
forceUInjectionHost || requiredType.contains(UInjectionHost::class.java) ->
expr<UInjectionHost> { KotlinStringTemplateUPolyadicExpression(expression, givenParent) }
expression.entries.isEmpty() -> {
expr<ULiteralExpression> { KotlinStringULiteralExpression(expression, givenParent, "") }
}
expression.entries.size == 1 -> convertEntry(expression.entries[0], givenParent, requiredType)
else ->
expr<KotlinStringTemplateUPolyadicExpression> { KotlinStringTemplateUPolyadicExpression(expression, givenParent) }
}
}
is KtDestructuringDeclaration -> expr<UDeclarationsExpression> {
val declarationsExpression = KotlinUDestructuringDeclarationExpression(givenParent, expression)
declarationsExpression.apply {
val tempAssignment = KotlinULocalVariable(UastKotlinPsiVariable.create(expression, declarationsExpression), expression, declarationsExpression)
val destructuringAssignments = expression.entries.mapIndexed { i, entry ->
val psiFactory = KtPsiFactory(expression.project)
val initializer = psiFactory.createAnalyzableExpression("${tempAssignment.name}.component${i + 1}()",
expression.containingFile)
initializer.destructuringDeclarationInitializer = true
KotlinULocalVariable(UastKotlinPsiVariable.create(entry, tempAssignment.javaPsi, declarationsExpression, initializer), entry, declarationsExpression)
}
declarations = listOf(tempAssignment) + destructuringAssignments
}
}
is KtLabeledExpression -> expr<ULabeledExpression>(build(::KotlinULabeledExpression))
is KtClassLiteralExpression -> expr<UClassLiteralExpression>(build(::KotlinUClassLiteralExpression))
is KtObjectLiteralExpression -> expr<UObjectLiteralExpression>(build(::KotlinUObjectLiteralExpression))
is KtDotQualifiedExpression -> expr<UQualifiedReferenceExpression>(build(::KotlinUQualifiedReferenceExpression))
is KtSafeQualifiedExpression -> expr<UQualifiedReferenceExpression>(build(::KotlinUSafeQualifiedExpression))
is KtSimpleNameExpression -> expr<USimpleNameReferenceExpression>(build(::KotlinUSimpleReferenceExpression))
is KtCallExpression -> expr<UCallExpression>(build(::KotlinUFunctionCallExpression))
is KtCollectionLiteralExpression -> expr<UCallExpression>(build(::KotlinUCollectionLiteralExpression))
is KtBinaryExpression -> {
if (expression.operationToken == KtTokens.ELVIS) {
expr<UExpressionList>(build(::createElvisExpression))
}
else expr<UBinaryExpression>(build(::KotlinUBinaryExpression))
}
is KtParenthesizedExpression -> expr<UParenthesizedExpression>(build(::KotlinUParenthesizedExpression))
is KtPrefixExpression -> expr<UPrefixExpression>(build(::KotlinUPrefixExpression))
is KtPostfixExpression -> expr<UPostfixExpression>(build(::KotlinUPostfixExpression))
is KtThisExpression -> expr<UThisExpression>(build(::KotlinUThisExpression))
is KtSuperExpression -> expr<USuperExpression>(build(::KotlinUSuperExpression))
is KtCallableReferenceExpression -> expr<UCallableReferenceExpression>(build(::KotlinUCallableReferenceExpression))
is KtIsExpression -> expr<UBinaryExpressionWithType>(build(::KotlinUTypeCheckExpression))
is KtIfExpression -> expr<UIfExpression>(build(::KotlinUIfExpression))
is KtWhileExpression -> expr<UWhileExpression>(build(::KotlinUWhileExpression))
is KtDoWhileExpression -> expr<UDoWhileExpression>(build(::KotlinUDoWhileExpression))
is KtForExpression -> expr<UForEachExpression>(build(::KotlinUForEachExpression))
is KtWhenExpression -> expr<USwitchExpression>(build(::KotlinUSwitchExpression))
is KtBreakExpression -> expr<UBreakExpression>(build(::KotlinUBreakExpression))
is KtContinueExpression -> expr<UContinueExpression>(build(::KotlinUContinueExpression))
is KtReturnExpression -> expr<UReturnExpression>(build(::KotlinUReturnExpression))
is KtThrowExpression -> expr<UThrowExpression>(build(::KotlinUThrowExpression))
is KtBlockExpression -> expr<UBlockExpression> {
if (expression.parent is KtFunctionLiteral
&& expression.parent.parent is KtLambdaExpression
&& givenParent !is KotlinULambdaExpression
) {
KotlinULambdaExpression(expression.parent.parent as KtLambdaExpression, givenParent).body
} else
KotlinUBlockExpression(expression, givenParent)
}
is KtConstantExpression -> expr<ULiteralExpression>(build(::KotlinULiteralExpression))
is KtTryExpression -> expr<UTryExpression>(build(::KotlinUTryExpression))
is KtArrayAccessExpression -> expr<UArrayAccessExpression>(build(::KotlinUArrayAccessExpression))
is KtLambdaExpression -> expr<ULambdaExpression>(build(::KotlinULambdaExpression))
is KtBinaryExpressionWithTypeRHS -> expr<UBinaryExpressionWithType>(build(::KotlinUBinaryExpressionWithType))
is KtClassOrObject -> expr<UDeclarationsExpression> {
expression.toLightClass()?.let { lightClass ->
KotlinUDeclarationsExpression(givenParent).apply {
declarations = listOf(KotlinUClass.create(lightClass, this))
}
} ?: UastEmptyExpression(givenParent)
}
is KtFunction -> if (expression.name.isNullOrEmpty()) {
expr<ULambdaExpression>(build(::createLocalFunctionLambdaExpression))
}
else {
expr<UDeclarationsExpression>(build(::createLocalFunctionDeclaration))
}
is KtAnnotatedExpression -> {
expression.baseExpression
?.let { convertExpression(it, givenParent, requiredType) }
?: expr<UExpression>(build(::UnknownKotlinExpression))
}
else -> expr<UExpression>(build(::UnknownKotlinExpression))
}}
}
internal fun convertWhenCondition(condition: KtWhenCondition,
givenParent: UElement?,
requiredType: Array<out Class<out UElement>>
): UExpression? {
return with(requiredType) {
when (condition) {
is KtWhenConditionInRange -> expr<UBinaryExpression> {
KotlinCustomUBinaryExpression(condition, givenParent).apply {
leftOperand = KotlinStringUSimpleReferenceExpression("it", this)
operator = when {
condition.isNegated -> KotlinBinaryOperators.NOT_IN
else -> KotlinBinaryOperators.IN
}
rightOperand = KotlinConverter.convertOrEmpty(condition.rangeExpression, this)
}
}
is KtWhenConditionIsPattern -> expr<UBinaryExpression> {
KotlinCustomUBinaryExpressionWithType(condition, givenParent).apply {
operand = KotlinStringUSimpleReferenceExpression("it", this)
operationKind = when {
condition.isNegated -> KotlinBinaryExpressionWithTypeKinds.NEGATED_INSTANCE_CHECK
else -> UastBinaryExpressionWithTypeKind.INSTANCE_CHECK
}
val typeRef = condition.typeReference
typeReference = typeRef?.let {
LazyKotlinUTypeReferenceExpression(it, this) { typeRef.toPsiType(this, boxed = true) }
}
}
}
is KtWhenConditionWithExpression ->
condition.expression?.let { KotlinConverter.convertExpression(it, givenParent, requiredType) }
else -> expr<UExpression> { UastEmptyExpression(givenParent) }
}
}
}
private fun convertEnumEntry(original: KtEnumEntry, givenParent: UElement?): UElement? {
return LightClassUtil.getLightClassBackingField(original)?.let { psiField ->
if (psiField is KtLightField && psiField is PsiEnumConstant) {
KotlinUEnumConstant(psiField, psiField.kotlinOrigin, givenParent)
} else {
null
}
}
}
internal fun convertDeclaration(
element: PsiElement,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): UElement? {
val original = element.originalElement
fun <P : PsiElement> build(ctor: (P, UElement?) -> UElement): () -> UElement? = {
@Suppress("UNCHECKED_CAST")
ctor(original as P, givenParent)
}
fun <P : PsiElement, K : KtElement> buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? = {
@Suppress("UNCHECKED_CAST")
ctor(original as P, ktElement, givenParent)
}
fun <P : PsiElement, K : KtElement> buildKtOpt(ktElement: K?, ctor: (P, K?, UElement?) -> UElement): () -> UElement? = {
@Suppress("UNCHECKED_CAST")
ctor(original as P, ktElement, givenParent)
}
return with(expectedTypes) {
when (original) {
is KtLightMethod -> el<UMethod>(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934
is UastFakeLightMethod -> el<UMethod> {
val ktFunction = original.original
if (ktFunction.isLocal)
convertDeclaration(ktFunction, givenParent, expectedTypes)
else
KotlinUMethodWithFakeLightDelegate(ktFunction, original, givenParent)
}
is UastFakeLightPrimaryConstructor ->
convertFakeLightConstructorAlternatives(original, givenParent, expectedTypes).firstOrNull()
is KtLightClass -> when (original.kotlinOrigin) {
is KtEnumEntry -> el<UEnumConstant> {
convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent)
}
else -> el<UClass> { KotlinUClass.create(original, givenParent) }
}
is KtLightField ->
if (original is PsiEnumConstant)
el<UEnumConstant>(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant))
else
el<UField>(buildKtOpt(original.kotlinOrigin, ::KotlinUField))
is KtLightParameter -> el<UParameter>(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter))
is UastKotlinPsiParameter -> el<UParameter>(buildKt(original.ktParameter, ::KotlinUParameter))
is UastKotlinPsiParameterBase<*> -> el<UParameter> {
original.ktOrigin.safeAs<KtTypeReference>()?.let { convertReceiverParameter(it) }
}
is UastKotlinPsiVariable -> el<ULocalVariable>(buildKt(original.ktElement, ::KotlinULocalVariable))
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)
if (lightMethod != null)
convertDeclaration(lightMethod, givenParent, expectedTypes)
else {
val ktLightClass = getLightClassForFakeMethod(original) ?: return null
KotlinUMethodWithFakeLightDelegate(original, ktLightClass, givenParent)
}
}
}
is KtPropertyAccessor -> el<UMethod> {
val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null
convertDeclaration(lightMethod, givenParent, expectedTypes)
}
is KtProperty ->
if (original.isLocal) {
KotlinConverter.convertPsiElement(original, givenParent, expectedTypes)
} else {
convertNonLocalProperty(original, givenParent, expectedTypes).firstOrNull()
}
is KtParameter -> convertParameter(original, givenParent, this).firstOrNull()
is KtFile -> convertKtFile(original, givenParent, this).firstOrNull()
is FakeFileForLightClass -> el<UFile> { KotlinUFile(original.navigationElement, kotlinUastPlugin) }
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)
is KtDelegatedSuperTypeEntry -> el<KotlinSupertypeDelegationUExpression> {
KotlinSupertypeDelegationUExpression(original, givenParent)
}
else -> null
}
}
}
internal fun convertFakeLightConstructorAlternatives(
original: UastFakeLightPrimaryConstructor,
givenParent: UElement?,
expectedTypes: Array<out Class<out UElement>>
): Sequence<UElement> {
return expectedTypes.accommodate(
alternative { convertDeclaration(original.original, givenParent, expectedTypes) as? UClass },
alternative { KotlinConstructorUMethod(original.original, original, original.original, givenParent) }
)
}
private fun getLightClassForFakeMethod(original: KtFunction): KtLightClass? {
if (original.isLocal) return null
return getContainingLightClass(original)
}
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 lightMethod = when (val ownerFunction = element.ownerFunction) {
is KtFunction -> LightClassUtil.getLightClassMethod(ownerFunction)
?: getLightClassForFakeMethod(ownerFunction)
?.takeIf { !it.isAnnotationType }
?.let { UastFakeLightMethod(ownerFunction, it) }
is KtPropertyAccessor -> LightClassUtil.getLightClassAccessorMethod(ownerFunction)
else -> null
} ?: return@uParam null
val lightParameter = lightMethod.parameterList.parameters.find { it.name == element.name } ?: return@uParam null
KotlinUParameter(lightParameter, element, givenParent)
},
alternative catch@{
val uCatchClause = element.parent?.parent?.safeAs<KtCatchClause>()?.toUElementOfType<UCatchClause>() ?: return@catch null
uCatchClause.parameters.firstOrNull { it.sourcePsi == element }
},
*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()
}
)
}
fun convertKtFile(
element: KtFile,
givenParent: UElement?,
requiredTypes: Array<out Class<out UElement>>
): Sequence<UElement> = requiredTypes.accommodate(
alternative { KotlinUFile(element, kotlinUastPlugin) },
alternative { element.findFacadeClass()?.let { KotlinUClass.create(it, givenParent) } }
)
internal fun convertOrEmpty(expression: KtExpression?, parent: UElement?): UExpression {
return expression?.let { convertExpression(it, parent, DEFAULT_EXPRESSION_TYPES_LIST) } ?: UastEmptyExpression(parent)
}
internal fun convertOrNull(expression: KtExpression?, parent: UElement?): UExpression? {
return if (expression != null) convertExpression(expression, parent, DEFAULT_EXPRESSION_TYPES_LIST) else null
}
internal fun KtPsiFactory.createAnalyzableExpression(text: String, context: PsiElement): KtExpression =
createAnalyzableProperty("val x = $text", context).initializer ?: error("Failed to create expression from text: '$text'")
internal fun KtPsiFactory.createAnalyzableProperty(text: String, context: PsiElement): KtProperty =
createAnalyzableDeclaration(text, context)
internal fun <TDeclaration : KtDeclaration> KtPsiFactory.createAnalyzableDeclaration(text: String, context: PsiElement): TDeclaration {
val file = createAnalyzableFile("dummy.kt", text, context)
val declarations = file.declarations
assert(declarations.size == 1) { "${declarations.size} declarations in $text" }
@Suppress("UNCHECKED_CAST")
return declarations.first() as TDeclaration
}
}
private fun convertVariablesDeclaration(
psi: KtVariableDeclaration,
parent: UElement?
): UDeclarationsExpression {
val declarationsExpression = parent as? KotlinUDeclarationsExpression
?: psi.parent.toUElementOfType<UDeclarationsExpression>() as? KotlinUDeclarationsExpression
?: KotlinUDeclarationsExpression(null, parent, psi)
val parentPsiElement = parent?.javaPsi //TODO: looks weird. mb look for the first non-null `javaPsi` in `parents` ?
val variable = KotlinUAnnotatedLocalVariable(
UastKotlinPsiVariable.create(psi, parentPsiElement, declarationsExpression), psi, declarationsExpression) { annotationParent ->
psi.annotationEntries.map { KotlinUAnnotation(it, annotationParent) }
}
return declarationsExpression.apply { declarations = listOf(variable) }
}
val kotlinUastPlugin: UastLanguagePlugin by lz {
UastLanguagePlugin.getInstances().find { it.language == KotlinLanguage.INSTANCE }
?: KotlinUastLanguagePlugin()