UAST: split KotlinConverter into a separate file
This commit is contained in:
committed by
Ilya Kirillov
parent
0395294933
commit
8b93727f64
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user