diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 index 970a182b3ae..463ef5f500d 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/KotlinUastLanguagePlugin.kt.191 @@ -42,6 +42,8 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.uast.* import org.jetbrains.uast.expressions.UInjectionHost +import org.jetbrains.uast.kotlin.KotlinConverter.convertDeclaration +import org.jetbrains.uast.kotlin.KotlinConverter.convertDeclarationOrElement import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier import org.jetbrains.uast.kotlin.declarations.KotlinUMethod import org.jetbrains.uast.kotlin.expressions.* @@ -87,29 +89,6 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { return convertDeclarationOrElement(element, null, elementTypes(requiredType)) } - private fun convertDeclarationOrElement( - element: PsiElement, - givenParent: UElement?, - requiredType: Array> - ): UElement? { - if (element is UElement) return element - - if (element.isValid) { - element.getUserData(KOTLIN_CACHED_UELEMENT_KEY)?.get()?.let { cachedUElement -> - return if (requiredType.isAssignableFrom(cachedUElement.javaClass)) cachedUElement else null - } - } - - val uElement = convertDeclaration(element, givenParent, requiredType) - ?: KotlinConverter.convertPsiElement(element, givenParent, requiredType) - /* - if (uElement != null) { - element.putUserData(KOTLIN_CACHED_UELEMENT_KEY, WeakReference(uElement)) - } - */ - return uElement - } - override fun getMethodCallExpression( element: PsiElement, containingClassFqName: String?, @@ -150,101 +129,6 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { return UastLanguagePlugin.ResolvedConstructor(uExpression, method, containingClass) } - internal fun convertDeclaration(element: PsiElement, - givenParent: UElement?, - expectedTypes: Array> - ): UElement? { - fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? = { ctor(element as P, givenParent) } - - fun

buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? = - { ctor(element as P, ktElement, givenParent) } - - fun

buildKtOpt(ktElement: K?, ctor: (P, K?, UElement?) -> UElement): () -> UElement? = - { ctor(element as P, ktElement, givenParent) } - - val original = element.originalElement - return with(expectedTypes) { - when (original) { - is KtLightMethod -> el(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934 - is KtLightClass -> when (original.kotlinOrigin) { - is KtEnumEntry -> el { - convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent) - } - else -> el { KotlinUClass.create(original, givenParent) } - } - is KtLightFieldImpl.KtLightEnumConstant -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant)) - is KtLightField -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUField)) - is KtLightParameter -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter)) - is UastKotlinPsiParameter -> el(buildKt(original.ktParameter, ::KotlinUParameter)) - is UastKotlinPsiVariable -> el(buildKt(original.ktElement, ::KotlinUVariable)) - - 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) ?: return null - convertDeclaration(lightMethod, givenParent, expectedTypes) - } - } - - is KtPropertyAccessor -> el { - val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null - convertDeclaration(lightMethod, givenParent, expectedTypes) - } - - is KtProperty -> - if (original.isLocal) { - KotlinConverter.convertPsiElement(element, givenParent, expectedTypes) - } - else { - convertNonLocalProperty(original, givenParent, expectedTypes).firstOrNull() - } - - is KtParameter -> convertParameter(original, givenParent, this).firstOrNull() - - is KtFile -> el { KotlinUFile(original, this@KotlinUastLanguagePlugin) } - is FakeFileForLightClass -> el { KotlinUFile(original.navigationElement, this@KotlinUastLanguagePlugin) } - 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 -> convertElement(original.kotlinOrigin, givenParent, expectedTypes) - else -> null - } - } - } - - - private fun convertEnumEntry(original: KtEnumEntry, givenParent: UElement?): UElement? { - return LightClassUtil.getLightClassBackingField(original)?.let { psiField -> - if (psiField is KtLightFieldImpl.KtLightEnumConstant) { - KotlinUEnumConstant(psiField, psiField.kotlinOrigin, givenParent) - } - else { - null - } - } - } - override fun isExpressionValueUsed(element: UExpression): Boolean { return when (element) { is KotlinUSimpleReferenceExpression.KotlinAccessorCallExpression -> element.setterValue != null @@ -269,9 +153,9 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin { @Suppress("UNCHECKED_CAST") override fun convertToAlternatives(element: PsiElement, requiredTypes: Array>): Sequence = when { - (element is KtProperty && !element.isLocal) -> convertNonLocalProperty(element, null, requiredTypes) as Sequence - element is KtParameter -> convertParameter(element, null, requiredTypes) as Sequence - element is KtClassOrObject -> convertClassOrObject(element, null, requiredTypes) as Sequence + (element is KtProperty && !element.isLocal) -> KotlinConverter.convertNonLocalProperty(element, null, requiredTypes) as Sequence + element is KtParameter -> KotlinConverter.convertParameter(element, null, requiredTypes) as Sequence + element is KtClassOrObject -> KotlinConverter.convertClassOrObject(element, null, requiredTypes) as Sequence else -> sequenceOf(convertElementWithParent(element, requiredTypes.nonEmptyOr(DEFAULT_TYPES_LIST)) as? T).filterNotNull() } } @@ -291,62 +175,6 @@ internal inline fun Array>. internal fun Array>.isAssignableFrom(cls: Class<*>) = any { it.isAssignableFrom(cls) } -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 { - // TODO: make it possible to use `convertDeclaration` without `KotlinUastLanguagePlugin` creation - methods.getter?.let { KotlinUastLanguagePlugin().convertDeclaration(it, givenParent, arrayOf(UMethod::class.java)) as? UMethod } - }, - alternative { - methods.setter?.let { KotlinUastLanguagePlugin().convertDeclaration(it, givenParent, arrayOf(UMethod::class.java)) as? UMethod } - } -) else emptyArray() - -private fun convertNonLocalProperty( - property: KtProperty, - givenParent: UElement?, - requiredType: Array> -): Sequence = - requiredType.accommodate(*convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(property), givenParent)) - - -private fun convertParameter( - element: KtParameter, - givenParent: UElement?, - requiredTypes: Array> -): Sequence = requiredTypes.accommodate( - alternative uParam@{ - val ownerFunction = element.ownerFunction as? KtFunction ?: return@uParam null - val lightMethod = LightClassUtil.getLightClassMethod(ownerFunction) ?: return@uParam null - val lightParameter = lightMethod.parameterList.parameters.find { it.name == element.name } ?: return@uParam null - KotlinUParameter(lightParameter, element, givenParent) - }, - *convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(element), givenParent) -) - -private fun convertClassOrObject( - element: KtClassOrObject, - givenParent: UElement?, - requiredTypes: Array> -): Sequence { - val ktLightClass = element.toLightClass() ?: return emptySequence() - val uClass = KotlinUClass.create(ktLightClass, givenParent) - return requiredTypes.accommodate( - alternative { uClass }, - alternative primaryConstructor@{ - val primaryConstructor = element.primaryConstructor ?: return@primaryConstructor null - uClass.methods.asSequence() - .filter { it.sourcePsi == primaryConstructor } - .firstOrNull() - } - ) -} - internal object KotlinConverter { internal tailrec fun unwrapElements(element: PsiElement?): PsiElement? = when (element) { @@ -598,6 +426,171 @@ internal object KotlinConverter { } } + private fun convertEnumEntry(original: KtEnumEntry, givenParent: UElement?): UElement? { + return LightClassUtil.getLightClassBackingField(original)?.let { psiField -> + if (psiField is KtLightFieldImpl.KtLightEnumConstant) { + KotlinUEnumConstant(psiField, psiField.kotlinOrigin, givenParent) + } else { + null + } + } + } + + + internal fun convertDeclaration( + element: PsiElement, + givenParent: UElement?, + expectedTypes: Array> + ): UElement? { + fun

build(ctor: (P, UElement?) -> UElement): () -> UElement? = { ctor(element as P, givenParent) } + + fun

buildKt(ktElement: K, ctor: (P, K, UElement?) -> UElement): () -> UElement? = + { ctor(element as P, ktElement, givenParent) } + + fun

buildKtOpt(ktElement: K?, ctor: (P, K?, UElement?) -> UElement): () -> UElement? = + { ctor(element as P, ktElement, givenParent) } + + val original = element.originalElement + return with(expectedTypes) { + when (original) { + is KtLightMethod -> el(build(KotlinUMethod.Companion::create)) // .Companion is needed because of KT-13934 + is KtLightClass -> when (original.kotlinOrigin) { + is KtEnumEntry -> el { + convertEnumEntry(original.kotlinOrigin as KtEnumEntry, givenParent) + } + else -> el { KotlinUClass.create(original, givenParent) } + } + is KtLightFieldImpl.KtLightEnumConstant -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUEnumConstant)) + is KtLightField -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUField)) + is KtLightParameter -> el(buildKtOpt(original.kotlinOrigin, ::KotlinUParameter)) + is UastKotlinPsiParameter -> el(buildKt(original.ktParameter, ::KotlinUParameter)) + is UastKotlinPsiVariable -> el(buildKt(original.ktElement, ::KotlinUVariable)) + + 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) ?: return null + convertDeclaration(lightMethod, givenParent, expectedTypes) + } + } + + is KtPropertyAccessor -> el { + val lightMethod = LightClassUtil.getLightClassAccessorMethod(original) ?: return null + convertDeclaration(lightMethod, givenParent, expectedTypes) + } + + is KtProperty -> + if (original.isLocal) { + KotlinConverter.convertPsiElement(element, givenParent, expectedTypes) + } else { + convertNonLocalProperty(original, givenParent, expectedTypes).firstOrNull() + } + + is KtParameter -> convertParameter(original, givenParent, this).firstOrNull() + + is KtFile -> el { KotlinUFile(original) } + is FakeFileForLightClass -> el { KotlinUFile(original.navigationElement) } + 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) + else -> null + } + } + } + + + 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 ownerFunction = element.ownerFunction as? KtFunction ?: return@uParam null + val lightMethod = LightClassUtil.getLightClassMethod(ownerFunction) ?: return@uParam null + val lightParameter = lightMethod.parameterList.parameters.find { it.name == element.name } ?: return@uParam null + KotlinUParameter(lightParameter, element, givenParent) + }, + *convertToPropertyAlternatives(LightClassUtil.getLightClassPropertyMethods(element), givenParent) + ) + + fun convertClassOrObject( + element: KtClassOrObject, + givenParent: UElement?, + expectedTypes: Array> + ): 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() + } + ) + } + internal fun convertOrEmpty(expression: KtExpression?, parent: UElement?): UExpression { return expression?.let { convertExpression(it, parent, DEFAULT_EXPRESSION_TYPES_LIST) } ?: UastEmptyExpression } diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUFile.kt.191 b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUFile.kt.191 new file mode 100644 index 00000000000..7fccefef117 --- /dev/null +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUFile.kt.191 @@ -0,0 +1,66 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.uast.kotlin + +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiComment +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiRecursiveElementWalkingVisitor +import org.jetbrains.kotlin.asJava.findFacadeClass +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.uast.* +import java.util.* + +class KotlinUFile( + override val psi: KtFile, + override val languagePlugin: UastLanguagePlugin = + UastLanguagePlugin.byLanguage(KotlinLanguage.INSTANCE) ?: KotlinUastLanguagePlugin() +) : UFile, JvmDeclarationUElementPlaceholder { + override val packageName: String + get() = psi.packageFqName.asString() + + override val annotations: List + get() = psi.annotationEntries.map { KotlinUAnnotation(it, this) } + + override val javaPsi: PsiElement? = null + + override val sourcePsi: PsiElement? = psi + + override val allCommentsInFile by lz { + val comments = ArrayList(0) + psi.accept(object : PsiRecursiveElementWalkingVisitor() { + override fun visitComment(comment: PsiComment) { + comments += UComment(comment, this@KotlinUFile) + } + }) + comments + } + + override val imports by lz { psi.importDirectives.map { KotlinUImportStatement(it, this) } } + + override val classes by lz { + val facadeOrScriptClass = if (psi.isScript()) psi.script?.toLightClass() else psi.findFacadeClass() + val classes = psi.declarations.mapNotNull { (it as? KtClassOrObject)?.toLightClass()?.toUClass() } + + (facadeOrScriptClass?.toUClass()?.let { listOf(it) } ?: emptyList()) + classes + } + + private fun PsiClass.toUClass() = languagePlugin.convertOpt(this, this@KotlinUFile) +} \ No newline at end of file