From 8b93727f64298909d6d2acdd5c83bb1e78ffb417 Mon Sep 17 00:00:00 2001 From: Jinseong Jeon Date: Thu, 20 May 2021 23:39:38 -0700 Subject: [PATCH] UAST: split KotlinConverter into a separate file --- .../jetbrains/uast/kotlin/KotlinConverter.kt | 601 ++++++++++++++++++ .../uast/kotlin/KotlinUastLanguagePlugin.kt | 574 +---------------- 2 files changed, 602 insertions(+), 573 deletions(-) create mode 100644 plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinConverter.kt diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinConverter.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinConverter.kt new file mode 100644 index 00000000000..23f21ea3343 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinConverter.kt @@ -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> + ): UElement? { + fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? { + return { + @Suppress("UNCHECKED_CAST") + ctor(element as P, givenParent) + } + } + + return with (expectedTypes) { when (element) { + is KtParameterList -> el { + 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(build(KotlinUExpressionList.Companion::createClassBody)) + is KtCatchClause -> el(build(::KotlinUCatchClause)) + is KtVariableDeclaration -> + if (element is KtProperty && !element.isLocal) { + convertNonLocalProperty(element, givenParent, this).firstOrNull() + } + else { + el { + 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 { UastEmptyExpression(givenParent) } + } + } + is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> + el(build(::KotlinStringULiteralExpression)) + is KtStringTemplateEntry -> + element.expression?.let { convertExpression(it, givenParent, expectedTypes) } + ?: expr { UastEmptyExpression(givenParent) } + is KtWhenEntry -> el(build(::KotlinUSwitchEntry)) + is KtWhenCondition -> convertWhenCondition(element, givenParent, expectedTypes) + is KtTypeReference -> + expectedTypes.accommodate( + alternative { LazyKotlinUTypeReferenceExpression(element, givenParent) }, + alternative { convertReceiverParameter(element) } + ).firstOrNull() + is KtConstructorDelegationCall -> + el { KotlinUFunctionCallExpression(element, givenParent) } + is KtSuperTypeCallEntry -> + el { + (element.getParentOfType(true)?.parent as? KtObjectLiteralExpression) + ?.toUElementOfType() + ?: KotlinUFunctionCallExpression(element, givenParent) + } + is KtImportDirective -> el(build(::KotlinUImportStatement)) + else -> { + if (element is LeafPsiElement) { + if (element.elementType in identifiersTokens) + if (element.elementType != KtTokens.OBJECT_KEYWORD || + element.getParentOfType(false)?.nameIdentifier == null + ) + el(build(::KotlinUIdentifier)) + else null + else if (element.elementType in KtTokens.OPERATIONS && element.parent is KtOperationReferenceExpression) + el(build(::KotlinUIdentifier)) + else if (element.elementType == KtTokens.LBRACKET && element.parent is KtCollectionLiteralExpression) + el { + 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()?.uastParameters?.firstOrNull() + } + + internal fun convertEntry( + entry: KtStringTemplateEntry, + givenParent: UElement?, + requiredType: Array> + ): UExpression? { + return with(requiredType) { + if (entry is KtStringTemplateEntryWithExpression) { + expr { + convertOrEmpty(entry.expression, givenParent) + } + } + else { + expr { + 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> + ): UExpression? { + fun

build(ctor: (P, UElement?) -> UExpression): () -> UExpression? { + return { + @Suppress("UNCHECKED_CAST") + ctor(expression as P, givenParent) + } + } + + return with (requiredType) { when (expression) { + is KtVariableDeclaration -> expr(build(::convertVariablesDeclaration)) + + is KtStringTemplateExpression -> { + when { + forceUInjectionHost || requiredType.contains(UInjectionHost::class.java) -> + expr { KotlinStringTemplateUPolyadicExpression(expression, givenParent) } + expression.entries.isEmpty() -> { + expr { KotlinStringULiteralExpression(expression, givenParent, "") } + } + + expression.entries.size == 1 -> convertEntry(expression.entries[0], givenParent, requiredType) + + else -> + expr { KotlinStringTemplateUPolyadicExpression(expression, givenParent) } + } + } + is KtDestructuringDeclaration -> expr { + 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(build(::KotlinULabeledExpression)) + is KtClassLiteralExpression -> expr(build(::KotlinUClassLiteralExpression)) + is KtObjectLiteralExpression -> expr(build(::KotlinUObjectLiteralExpression)) + is KtDotQualifiedExpression -> expr(build(::KotlinUQualifiedReferenceExpression)) + is KtSafeQualifiedExpression -> expr(build(::KotlinUSafeQualifiedExpression)) + is KtSimpleNameExpression -> expr(build(::KotlinUSimpleReferenceExpression)) + is KtCallExpression -> expr(build(::KotlinUFunctionCallExpression)) + is KtCollectionLiteralExpression -> expr(build(::KotlinUCollectionLiteralExpression)) + is KtBinaryExpression -> { + if (expression.operationToken == KtTokens.ELVIS) { + expr(build(::createElvisExpression)) + } + else expr(build(::KotlinUBinaryExpression)) + } + is KtParenthesizedExpression -> expr(build(::KotlinUParenthesizedExpression)) + is KtPrefixExpression -> expr(build(::KotlinUPrefixExpression)) + is KtPostfixExpression -> expr(build(::KotlinUPostfixExpression)) + is KtThisExpression -> expr(build(::KotlinUThisExpression)) + is KtSuperExpression -> expr(build(::KotlinUSuperExpression)) + is KtCallableReferenceExpression -> expr(build(::KotlinUCallableReferenceExpression)) + is KtIsExpression -> expr(build(::KotlinUTypeCheckExpression)) + is KtIfExpression -> expr(build(::KotlinUIfExpression)) + is KtWhileExpression -> expr(build(::KotlinUWhileExpression)) + is KtDoWhileExpression -> expr(build(::KotlinUDoWhileExpression)) + is KtForExpression -> expr(build(::KotlinUForEachExpression)) + is KtWhenExpression -> expr(build(::KotlinUSwitchExpression)) + is KtBreakExpression -> expr(build(::KotlinUBreakExpression)) + is KtContinueExpression -> expr(build(::KotlinUContinueExpression)) + is KtReturnExpression -> expr(build(::KotlinUReturnExpression)) + is KtThrowExpression -> expr(build(::KotlinUThrowExpression)) + is KtBlockExpression -> expr { + 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(build(::KotlinULiteralExpression)) + is KtTryExpression -> expr(build(::KotlinUTryExpression)) + is KtArrayAccessExpression -> expr(build(::KotlinUArrayAccessExpression)) + is KtLambdaExpression -> expr(build(::KotlinULambdaExpression)) + is KtBinaryExpressionWithTypeRHS -> expr(build(::KotlinUBinaryExpressionWithType)) + is KtClassOrObject -> expr { + expression.toLightClass()?.let { lightClass -> + KotlinUDeclarationsExpression(givenParent).apply { + declarations = listOf(KotlinUClass.create(lightClass, this)) + } + } ?: UastEmptyExpression(givenParent) + } + is KtFunction -> if (expression.name.isNullOrEmpty()) { + expr(build(::createLocalFunctionLambdaExpression)) + } + else { + expr(build(::createLocalFunctionDeclaration)) + } + is KtAnnotatedExpression -> { + expression.baseExpression + ?.let { convertExpression(it, givenParent, requiredType) } + ?: expr(build(::UnknownKotlinExpression)) + } + + else -> expr(build(::UnknownKotlinExpression)) + }} + } + + internal fun convertWhenCondition( + condition: KtWhenCondition, + givenParent: UElement?, + requiredType: Array> + ): UExpression? { + return with(requiredType) { + when (condition) { + is KtWhenConditionInRange -> expr { + 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 { + 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 { 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> + ): UElement? { + val original = element.originalElement + + fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? = { + @Suppress("UNCHECKED_CAST") + ctor(original as P, givenParent) + } + + fun

buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? = { + @Suppress("UNCHECKED_CAST") + ctor(original as P, ktElement, givenParent) + } + + fun

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(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934 + is UastFakeLightMethod -> el { + 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 { + convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent) + } + else -> el { KotlinUClass.create(original, givenParent) } + } + is KtLightField -> + if (original is PsiEnumConstant) + el(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant)) + else + el(buildKtOpt(original.kotlinOrigin, ::KotlinUField)) + is KtLightParameter -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter)) + is UastKotlinPsiParameter -> el(buildKt(original.ktParameter, ::KotlinUParameter)) + is UastKotlinPsiParameterBase<*> -> el { + original.ktOrigin.safeAs()?.let { convertReceiverParameter(it) } + } + is UastKotlinPsiVariable -> el(buildKt(original.ktElement, ::KotlinULocalVariable)) + + is KtEnumEntry -> el { + convertEnumEntry(original, givenParent) + } + is KtClassOrObject -> convertClassOrObject(original, givenParent, this).firstOrNull() + is KtFunction -> + if (original.isLocal) { + el { + 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 { + 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 { + 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 { KotlinUFile(original.navigationElement, kotlinUastPlugin) } + is KtAnnotationEntry -> el(build(::KotlinUAnnotation)) + is KtCallExpression -> + if (expectedTypes.isAssignableFrom(KotlinUNestedAnnotation::class.java) && + !expectedTypes.isAssignableFrom(UCallExpression::class.java) + ) { + el { KotlinUNestedAnnotation.tryCreate(original, givenParent) } + } else null + is KtLightAnnotationForSourceEntry -> convertDeclarationOrElement(original.kotlinOrigin, givenParent, expectedTypes) + is KtDelegatedSuperTypeEntry -> el { + KotlinSupertypeDelegationUExpression(original, givenParent) + } + else -> null + } + } + } + + internal fun convertFakeLightConstructorAlternatives( + original: UastFakeLightPrimaryConstructor, + givenParent: UElement?, + expectedTypes: Array> + ): Sequence { + 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> + ): 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> = 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> + ): Sequence = + expectedTypes.accommodate(*convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(property), givenParent)) + + + fun convertParameter( + element: KtParameter, + givenParent: UElement?, + expectedTypes: Array> + ): Sequence = 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()?.toUElementOfType() ?: return@catch null + uCatchClause.parameters.firstOrNull { it.sourcePsi == element } + }, + *convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(element), givenParent) + ) + + fun convertClassOrObject( + element: KtClassOrObject, + givenParent: UElement?, + expectedTypes: Array> + ): Sequence { + 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> + ): Sequence = 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 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() 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) } + } +} diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt index 4773d56f145..8d2a8e205a0 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt @@ -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> - ): UElement? { - fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? { - return { - @Suppress("UNCHECKED_CAST") - ctor(element as P, givenParent) - } - } - - return with (expectedTypes) { when (element) { - is KtParameterList -> el { - 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(build(KotlinUExpressionList.Companion::createClassBody)) - is KtCatchClause -> el(build(::KotlinUCatchClause)) - is KtVariableDeclaration -> - if (element is KtProperty && !element.isLocal) { - convertNonLocalProperty(element, givenParent, this).firstOrNull() - } - else { - el { - 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 { UastEmptyExpression(givenParent) } - } - } - is KtLiteralStringTemplateEntry, is KtEscapeStringTemplateEntry -> el(build(::KotlinStringULiteralExpression)) - is KtStringTemplateEntry -> element.expression?.let { convertExpression(it, givenParent, expectedTypes) } ?: expr { UastEmptyExpression(givenParent) } - is KtWhenEntry -> el(build(::KotlinUSwitchEntry)) - is KtWhenCondition -> convertWhenCondition(element, givenParent, expectedTypes) - is KtTypeReference -> - expectedTypes.accommodate( - alternative { LazyKotlinUTypeReferenceExpression(element, givenParent) }, - alternative { convertReceiverParameter(element) } - ).firstOrNull() - is KtConstructorDelegationCall -> - el { KotlinUFunctionCallExpression(element, givenParent) } - is KtSuperTypeCallEntry -> - el { - (element.getParentOfType(true)?.parent as? KtObjectLiteralExpression) - ?.toUElementOfType() - ?: KotlinUFunctionCallExpression(element, givenParent) - } - is KtImportDirective -> el(build(::KotlinUImportStatement)) - else -> { - if (element is LeafPsiElement) { - if (element.elementType in identifiersTokens) - if (element.elementType != KtTokens.OBJECT_KEYWORD || element.getParentOfType(false)?.nameIdentifier == null) - el(build(::KotlinUIdentifier)) - else null - else if (element.elementType in KtTokens.OPERATIONS && element.parent is KtOperationReferenceExpression) - el(build(::KotlinUIdentifier)) - else if (element.elementType == KtTokens.LBRACKET && element.parent is KtCollectionLiteralExpression) - el { - 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()?.uastParameters?.firstOrNull() - } - - internal fun convertEntry(entry: KtStringTemplateEntry, - givenParent: UElement?, - requiredType: Array> - ): UExpression? { - return with(requiredType) { - if (entry is KtStringTemplateEntryWithExpression) { - expr { - KotlinConverter.convertOrEmpty(entry.expression, givenParent) - } - } - else { - expr { - 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> - ): UExpression? { - fun

build(ctor: (P, UElement?) -> UExpression): () -> UExpression? { - return { - @Suppress("UNCHECKED_CAST") - ctor(expression as P, givenParent) - } - } - - return with (requiredType) { when (expression) { - is KtVariableDeclaration -> expr(build(::convertVariablesDeclaration)) - - is KtStringTemplateExpression -> { - when { - forceUInjectionHost || requiredType.contains(UInjectionHost::class.java) -> - expr { KotlinStringTemplateUPolyadicExpression(expression, givenParent) } - expression.entries.isEmpty() -> { - expr { KotlinStringULiteralExpression(expression, givenParent, "") } - } - - expression.entries.size == 1 -> convertEntry(expression.entries[0], givenParent, requiredType) - - else -> - expr { KotlinStringTemplateUPolyadicExpression(expression, givenParent) } - } - } - is KtDestructuringDeclaration -> expr { - 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(build(::KotlinULabeledExpression)) - is KtClassLiteralExpression -> expr(build(::KotlinUClassLiteralExpression)) - is KtObjectLiteralExpression -> expr(build(::KotlinUObjectLiteralExpression)) - is KtDotQualifiedExpression -> expr(build(::KotlinUQualifiedReferenceExpression)) - is KtSafeQualifiedExpression -> expr(build(::KotlinUSafeQualifiedExpression)) - is KtSimpleNameExpression -> expr(build(::KotlinUSimpleReferenceExpression)) - is KtCallExpression -> expr(build(::KotlinUFunctionCallExpression)) - is KtCollectionLiteralExpression -> expr(build(::KotlinUCollectionLiteralExpression)) - is KtBinaryExpression -> { - if (expression.operationToken == KtTokens.ELVIS) { - expr(build(::createElvisExpression)) - } - else expr(build(::KotlinUBinaryExpression)) - } - is KtParenthesizedExpression -> expr(build(::KotlinUParenthesizedExpression)) - is KtPrefixExpression -> expr(build(::KotlinUPrefixExpression)) - is KtPostfixExpression -> expr(build(::KotlinUPostfixExpression)) - is KtThisExpression -> expr(build(::KotlinUThisExpression)) - is KtSuperExpression -> expr(build(::KotlinUSuperExpression)) - is KtCallableReferenceExpression -> expr(build(::KotlinUCallableReferenceExpression)) - is KtIsExpression -> expr(build(::KotlinUTypeCheckExpression)) - is KtIfExpression -> expr(build(::KotlinUIfExpression)) - is KtWhileExpression -> expr(build(::KotlinUWhileExpression)) - is KtDoWhileExpression -> expr(build(::KotlinUDoWhileExpression)) - is KtForExpression -> expr(build(::KotlinUForEachExpression)) - is KtWhenExpression -> expr(build(::KotlinUSwitchExpression)) - is KtBreakExpression -> expr(build(::KotlinUBreakExpression)) - is KtContinueExpression -> expr(build(::KotlinUContinueExpression)) - is KtReturnExpression -> expr(build(::KotlinUReturnExpression)) - is KtThrowExpression -> expr(build(::KotlinUThrowExpression)) - is KtBlockExpression -> expr { - 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(build(::KotlinULiteralExpression)) - is KtTryExpression -> expr(build(::KotlinUTryExpression)) - is KtArrayAccessExpression -> expr(build(::KotlinUArrayAccessExpression)) - is KtLambdaExpression -> expr(build(::KotlinULambdaExpression)) - is KtBinaryExpressionWithTypeRHS -> expr(build(::KotlinUBinaryExpressionWithType)) - is KtClassOrObject -> expr { - expression.toLightClass()?.let { lightClass -> - KotlinUDeclarationsExpression(givenParent).apply { - declarations = listOf(KotlinUClass.create(lightClass, this)) - } - } ?: UastEmptyExpression(givenParent) - } - is KtFunction -> if (expression.name.isNullOrEmpty()) { - expr(build(::createLocalFunctionLambdaExpression)) - } - else { - expr(build(::createLocalFunctionDeclaration)) - } - is KtAnnotatedExpression -> { - expression.baseExpression - ?.let { convertExpression(it, givenParent, requiredType) } - ?: expr(build(::UnknownKotlinExpression)) - } - - else -> expr(build(::UnknownKotlinExpression)) - }} - } - - internal fun convertWhenCondition(condition: KtWhenCondition, - givenParent: UElement?, - requiredType: Array> - ): UExpression? { - return with(requiredType) { - when (condition) { - is KtWhenConditionInRange -> expr { - 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 { - 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 { 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> - ): UElement? { - val original = element.originalElement - - fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? = { - @Suppress("UNCHECKED_CAST") - ctor(original as P, givenParent) - } - - fun

buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? = { - @Suppress("UNCHECKED_CAST") - ctor(original as P, ktElement, givenParent) - } - - fun

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(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934 - is UastFakeLightMethod -> el { - 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 { - convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent) - } - else -> el { KotlinUClass.create(original, givenParent) } - } - is KtLightField -> - if (original is PsiEnumConstant) - el(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant)) - else - el(buildKtOpt(original.kotlinOrigin, ::KotlinUField)) - is KtLightParameter -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter)) - is UastKotlinPsiParameter -> el(buildKt(original.ktParameter, ::KotlinUParameter)) - is UastKotlinPsiParameterBase<*> -> el { - original.ktOrigin.safeAs()?.let { convertReceiverParameter(it) } - } - is UastKotlinPsiVariable -> el(buildKt(original.ktElement, ::KotlinULocalVariable)) - - is KtEnumEntry -> el { - convertEnumEntry(original, givenParent) - } - is KtClassOrObject -> convertClassOrObject(original, givenParent, this).firstOrNull() - is KtFunction -> - if (original.isLocal) { - el { - 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 { - 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 { - 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 { KotlinUFile(original.navigationElement, kotlinUastPlugin) } - is KtAnnotationEntry -> el(build(::KotlinUAnnotation)) - is KtCallExpression -> - if (expectedTypes.isAssignableFrom(KotlinUNestedAnnotation::class.java) && !expectedTypes.isAssignableFrom(UCallExpression::class.java)) { - el { KotlinUNestedAnnotation.tryCreate(original, givenParent) } - } else null - is KtLightAnnotationForSourceEntry -> convertDeclarationOrElement(original.kotlinOrigin, givenParent, expectedTypes) - is KtDelegatedSuperTypeEntry -> el { - KotlinSupertypeDelegationUExpression(original, givenParent) - } - else -> null - } - } - } - - internal fun convertFakeLightConstructorAlternatives( - original: UastFakeLightPrimaryConstructor, - givenParent: UElement?, - expectedTypes: Array> - ): Sequence { - 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> - ): 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> = 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> - ): Sequence = - expectedTypes.accommodate(*convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(property), givenParent)) - - - fun convertParameter( - element: KtParameter, - givenParent: UElement?, - expectedTypes: Array> - ): Sequence = 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()?.toUElementOfType() ?: return@catch null - uCatchClause.parameters.firstOrNull { it.sourcePsi == element } - }, - *convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(element), givenParent) - ) - - fun convertClassOrObject( - element: KtClassOrObject, - givenParent: UElement?, - expectedTypes: Array> - ): Sequence { - 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> - ): Sequence = 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 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() 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()