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.lang.Language
|
||||||
import com.intellij.openapi.components.ServiceManager
|
import com.intellij.openapi.components.ServiceManager
|
||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.registry.Registry
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiEnumConstant
|
|
||||||
import com.intellij.psi.PsiFile
|
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.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.ConstructorDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
|
||||||
import org.jetbrains.uast.*
|
import org.jetbrains.uast.*
|
||||||
import org.jetbrains.uast.expressions.UInjectionHost
|
|
||||||
import org.jetbrains.uast.kotlin.KotlinConverter.convertDeclaration
|
import org.jetbrains.uast.kotlin.KotlinConverter.convertDeclaration
|
||||||
import org.jetbrains.uast.kotlin.KotlinConverter.convertDeclarationOrElement
|
import org.jetbrains.uast.kotlin.KotlinConverter.convertDeclarationOrElement
|
||||||
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
|
import org.jetbrains.uast.kotlin.psi.UastFakeLightPrimaryConstructor
|
||||||
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.*
|
|
||||||
|
|
||||||
var PsiElement.destructuringDeclarationInitializer: Boolean? by UserDataProperty(Key.create("kotlin.uast.destructuringDeclarationInitializer"))
|
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 {
|
val kotlinUastPlugin: UastLanguagePlugin by lz {
|
||||||
UastLanguagePlugin.getInstances().find { it.language == KotlinLanguage.INSTANCE }
|
UastLanguagePlugin.getInstances().find { it.language == KotlinLanguage.INSTANCE }
|
||||||
?: KotlinUastLanguagePlugin()
|
?: KotlinUastLanguagePlugin()
|
||||||
|
|||||||
Reference in New Issue
Block a user