172: Revert "Uast: uastParent made final in KotlinAbstractUElement"

This reverts commit 9b006787581cca8167714d07ad36aea195ff9c02.
This commit is contained in:
Nikolay Krasko
2018-01-11 20:11:43 +03:00
parent affa1e232f
commit d402412208
4 changed files with 555 additions and 6 deletions
@@ -0,0 +1,211 @@
/*
* 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.PsiElement
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClassForLocalDeclaration
import org.jetbrains.kotlin.asJava.toLightGetter
import org.jetbrains.kotlin.asJava.toLightSetter
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isPropertyParameter
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.expressions.KotlinUElvisExpression
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable
abstract class KotlinAbstractUElement(private val givenParent: UElement?) : UElement, JvmDeclarationUElement {
override val uastParent: UElement? by lz {
givenParent ?: convertParent()
}
protected open fun convertParent(): UElement? {
val psi = psi
var parent = psi?.parent ?: psi?.containingFile
if (psi is KtLightClassForLocalDeclaration) {
val originParent = psi.kotlinOrigin.parent
parent = when (originParent) {
is KtClassBody -> originParent.parent
else -> originParent
}
}
if (psi is KtAnnotationEntry) {
val parentUnwrapped = KotlinConverter.unwrapElements(parent) ?: return null
val target = psi.useSiteTarget?.getAnnotationUseSiteTarget()
when (target) {
AnnotationUseSiteTarget.PROPERTY_GETTER ->
parent = (parentUnwrapped as? KtProperty)?.getter
?: (parentUnwrapped as? KtParameter)?.toLightGetter()
?: parent
AnnotationUseSiteTarget.PROPERTY_SETTER ->
parent = (parentUnwrapped as? KtProperty)?.setter
?: (parentUnwrapped as? KtParameter)?.toLightSetter()
?: parent
AnnotationUseSiteTarget.FIELD ->
parent = (parentUnwrapped as? KtProperty)
?: (parentUnwrapped as? KtParameter)
?.takeIf { it.isPropertyParameter() }
?.let(LightClassUtil::getLightClassBackingField)
?: parent
AnnotationUseSiteTarget.SETTER_PARAMETER ->
parent = (parentUnwrapped as? KtParameter)
?.toLightSetter()?.parameterList?.parameters?.firstOrNull() ?: parent
}
}
if (psi is UastKotlinPsiVariable && parent != null) {
parent = parent.parent
}
while (parent is KtStringTemplateEntryWithExpression ||
parent is KtStringTemplateExpression && parent.entries.size == 1) {
parent = parent.parent
}
if (parent is KtWhenConditionWithExpression) {
parent = parent.parent
}
if (parent is KtImportList) {
parent = parent.parent
}
if (psi is KtFunctionLiteral && parent is KtLambdaExpression) {
parent = parent.parent
}
if (parent is KtLambdaArgument) {
parent = parent.parent
}
if (psi is KtSuperTypeCallEntry) {
parent = parent?.parent
}
val result = doConvertParent(this, parent)
if (result == this) {
throw IllegalStateException("Loop in parent structure when converting a $psi of type ${psi?.javaClass} with parent $parent of type ${parent?.javaClass} text: [${parent?.text}]")
}
return result
}
override fun equals(other: Any?): Boolean {
if (other !is UElement) {
return false
}
return this.psi == other.psi
}
override fun hashCode() = psi?.hashCode() ?: 0
}
fun doConvertParent(element: UElement, parent: PsiElement?): UElement? {
val parentUnwrapped = KotlinConverter.unwrapElements(parent) ?: return null
if (parent is KtValueArgument && parentUnwrapped is KtAnnotationEntry) {
return (KotlinUastLanguagePlugin().convertElementWithParent(parentUnwrapped, null) as? KotlinUAnnotation)
?.findAttributeValueExpression(parent)
}
if (parent is KtParameter) {
val annotationClass = findAnnotationClassFromConstructorParameter(parent)
if (annotationClass != null) {
return annotationClass.methods.find { it.name == parent.name }
}
}
if (parent is KtClassInitializer) {
val containingClass = parent.containingClassOrObject
if (containingClass != null) {
val containingUClass = KotlinUastLanguagePlugin().convertElementWithParent(containingClass, null) as? KotlinUClass
containingUClass?.methods?.filterIsInstance<KotlinConstructorUMethod>()?.firstOrNull { it.isPrimary }?.let {
return it.uastBody
}
}
}
val result = KotlinUastLanguagePlugin().convertElementWithParent(parentUnwrapped, null)
if (result is UEnumConstant && element is UDeclaration) {
return result.initializingClass
}
if (result is USwitchClauseExpressionWithBody && !isInConditionBranch(element, result)) {
return result.body
}
if (result is KotlinUDestructuringDeclarationExpression &&
element.psi == (parent as KtDestructuringDeclaration).initializer) {
return result.tempVarAssignment
}
if (result is KotlinUElvisExpression && parent is KtBinaryExpression) {
when (element.psi) {
parent.left -> return result.lhsDeclaration
parent.right -> return result.rhsIfExpression
}
}
if (result is UMethod
&& result !is KotlinConstructorUMethod // no sense to wrap super calls with `return`
&& element is UExpression
&& element !is UBlockExpression
&& element !is UTypeReferenceExpression // when element is a type in extension methods
) {
return KotlinUBlockExpression.KotlinLazyUBlockExpression(result, { block ->
listOf(KotlinUImplicitReturnExpression(block).apply { returnExpression = element })
}).expressions.single()
}
return result
}
private fun isInConditionBranch(element: UElement, result: USwitchClauseExpressionWithBody) =
element.psi?.parentsWithSelf?.takeWhile { it !== result.psi }?.any { it is KtWhenCondition } ?: false
private fun findAnnotationClassFromConstructorParameter(parameter: KtParameter): UClass? {
val primaryConstructor = parameter.getStrictParentOfType<KtPrimaryConstructor>() ?: return null
val containingClass = primaryConstructor.getContainingClassOrObject()
if (containingClass.isAnnotation()) {
return KotlinUastLanguagePlugin().convertElementWithParent(containingClass, null) as? UClass
}
return null
}
abstract class KotlinAbstractUExpression(givenParent: UElement?)
: KotlinAbstractUElement(givenParent), UExpression, JvmDeclarationUElement {
override val javaPsi: PsiElement? = null
override val sourcePsi
get() = psi
override val annotations: List<UAnnotation>
get() {
val annotatedExpression = psi?.parent as? KtAnnotatedExpression ?: return emptyList()
return annotatedExpression.annotationEntries.map { KotlinUAnnotation(it, this) }
}
}
@@ -0,0 +1,292 @@
/*
* 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.*
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForScript
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
import org.jetbrains.uast.kotlin.declarations.UastLightIdentifier
abstract class AbstractKotlinUClass(givenParent: UElement?) : KotlinAbstractUElement(givenParent), UClass, JvmDeclarationUElement {
override val uastDeclarations by lz {
mutableListOf<UDeclaration>().apply {
addAll(fields)
addAll(initializers)
addAll(methods)
addAll(innerClasses)
}
}
override val uastSuperTypes: List<UTypeReferenceExpression>
get() {
val ktClass = (psi as? KtLightClass)?.kotlinOrigin ?: return emptyList()
return ktClass.superTypeListEntries.mapNotNull { it.typeReference }.map {
LazyKotlinUTypeReferenceExpression(it, this)
}
}
override val uastAnchor: UElement?
get() = UIdentifier(psi.nameIdentifier, this)
override val annotations: List<UAnnotation> by lz {
(sourcePsi as? KtModifierListOwner)?.annotationEntries.orEmpty().map { KotlinUAnnotation(it, this) }
}
override fun equals(other: Any?) = other is AbstractKotlinUClass && psi == other.psi
override fun hashCode() = psi.hashCode()
}
open class KotlinUClass private constructor(
psi: KtLightClass,
givenParent: UElement?
) : AbstractKotlinUClass(givenParent), PsiClass by psi {
val ktClass = psi.kotlinOrigin
override val javaPsi: KtLightClass = psi
override val sourcePsi: KtClassOrObject? = ktClass
override val psi = unwrap<UClass, PsiClass>(psi)
override fun getSourceElement() = sourcePsi ?: this
override fun getOriginalElement(): PsiElement? = super.getOriginalElement()
override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, ktClass)
override fun getContainingFile(): PsiFile? = unwrapFakeFileForLightClass(psi.containingFile)
override val uastAnchor: UElement
get() = UIdentifier(nameIdentifier, this)
override fun getInnerClasses(): Array<UClass> {
// filter DefaultImpls to avoid processing same methods from original interface multiple times
// filter Enum entry classes to avoid duplication with PsiEnumConstant initializer class
return psi.innerClasses.filter {
it.name != JvmAbi.DEFAULT_IMPLS_CLASS_NAME && !it.isEnumEntryLightClass()
}.mapNotNull {
getLanguagePlugin().convertOpt<UClass>(it, this)
}.toTypedArray()
}
override fun getSuperClass(): UClass? = super.getSuperClass()
override fun getFields(): Array<UField> = super.getFields()
override fun getInitializers(): Array<UClassInitializer> = super.getInitializers()
override fun getMethods(): Array<UMethod> {
val hasPrimaryConstructor = ktClass?.hasPrimaryConstructor() ?: false
var secondaryConstructorsCount = 0
fun createUMethod(psiMethod: PsiMethod): UMethod {
return if (psiMethod is KtLightMethod &&
psiMethod.isConstructor) {
if (!hasPrimaryConstructor && secondaryConstructorsCount++ == 0)
KotlinSecondaryConstructorWithInitializersUMethod(ktClass, psiMethod, this)
else
KotlinConstructorUMethod(ktClass, psiMethod, this)
} else {
getLanguagePlugin().convertOpt(psiMethod, this) ?: reportConvertFailure(psiMethod)
}
}
fun isDelegatedMethod(psiMethod: PsiMethod) = psiMethod is KtLightMethod && psiMethod.isDelegated
return psi.methods.asSequence()
.filterNot(::isDelegatedMethod)
.map(::createUMethod)
.toList()
.toTypedArray()
}
private fun PsiClass.isEnumEntryLightClass() = (this as? KtLightClass)?.kotlinOrigin is KtEnumEntry
companion object {
fun create(psi: KtLightClass, containingElement: UElement?): UClass = when (psi) {
is PsiAnonymousClass -> KotlinUAnonymousClass(psi, containingElement)
is KtLightClassForScript -> KotlinScriptUClass(psi, containingElement)
else -> KotlinUClass(psi, containingElement)
}
}
}
open class KotlinConstructorUMethod(
private val ktClass: KtClassOrObject?,
override val psi: KtLightMethod,
givenParent: UElement?
) : KotlinUMethod(psi, givenParent) {
val isPrimary: Boolean
get() = psi.kotlinOrigin.let { it is KtPrimaryConstructor || it is KtClassOrObject }
override val uastBody: UExpression? by lz {
val delegationCall: KtCallElement? = psi.kotlinOrigin.let {
when {
isPrimary -> ktClass?.superTypeListEntries?.firstIsInstanceOrNull<KtSuperTypeCallEntry>()
it is KtSecondaryConstructor -> it.getDelegationCall()
else -> null
}
}
val bodyExpressions = getBodyExpressions()
if (delegationCall == null && bodyExpressions.isEmpty()) return@lz null
KotlinUBlockExpression.KotlinLazyUBlockExpression(this) { uastParent ->
SmartList<UExpression>().apply {
delegationCall?.let {
add(KotlinUFunctionCallExpression(it, uastParent))
}
bodyExpressions.forEach {
add(KotlinConverter.convertOrEmpty(it, uastParent))
}
}
}
}
override val javaPsi = psi
override val sourcePsi = psi.kotlinOrigin
open protected fun getBodyExpressions(): List<KtExpression> {
if (isPrimary) return getInitializers()
val bodyExpression = (psi.kotlinOrigin as? KtFunction)?.bodyExpression ?: return emptyList()
if (bodyExpression is KtBlockExpression) return bodyExpression.statements
return listOf(bodyExpression)
}
protected fun getInitializers() = ktClass?.getAnonymousInitializers()?.mapNotNull { it.body } ?: emptyList()
}
// This class was created as a workaround for KT-21617 to be the only constructor which includes `init` block
// when there is no primary constructors in the class.
// It is expected to have only one constructor of this type in a UClass.
class KotlinSecondaryConstructorWithInitializersUMethod(
ktClass: KtClassOrObject?,
psi: KtLightMethod,
givenParent: UElement?
) : KotlinConstructorUMethod(ktClass, psi, givenParent) {
override fun getBodyExpressions(): List<KtExpression> = getInitializers() + super.getBodyExpressions()
}
class KotlinUAnonymousClass(
psi: PsiAnonymousClass,
givenParent: UElement?
) : AbstractKotlinUClass(givenParent), UAnonymousClass, PsiAnonymousClass by psi {
override val psi: PsiAnonymousClass = unwrap<UAnonymousClass, PsiAnonymousClass>(psi)
override val javaPsi: PsiAnonymousClass = psi
override val sourcePsi: KtClassOrObject? = (psi as? KtLightClass)?.kotlinOrigin
override fun getOriginalElement(): PsiElement? = super<AbstractKotlinUClass>.getOriginalElement()
override fun getSuperClass(): UClass? = super<AbstractKotlinUClass>.getSuperClass()
override fun getFields(): Array<UField> = super<AbstractKotlinUClass>.getFields()
override fun getMethods(): Array<UMethod> = super<AbstractKotlinUClass>.getMethods()
override fun getInitializers(): Array<UClassInitializer> = super<AbstractKotlinUClass>.getInitializers()
override fun getInnerClasses(): Array<UClass> = super<AbstractKotlinUClass>.getInnerClasses()
override fun getContainingFile(): PsiFile = unwrapFakeFileForLightClass(psi.containingFile)
override val uastAnchor: UElement?
get() {
val ktClassOrObject = (psi.originalElement as? KtLightClass)?.kotlinOrigin as? KtObjectDeclaration ?: return null
return UIdentifier(ktClassOrObject.getObjectKeyword(), this)
}
}
class KotlinScriptUClass(
psi: KtLightClassForScript,
givenParent: UElement?
) : AbstractKotlinUClass(givenParent), PsiClass by psi {
override fun getContainingFile(): PsiFile = unwrapFakeFileForLightClass(psi.containingFile)
override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, psi.kotlinOrigin)
override val uastAnchor: UElement
get() = UIdentifier(nameIdentifier, this)
override val javaPsi: PsiClass = psi
override val sourcePsi: KtClassOrObject? = psi.kotlinOrigin
override val psi = unwrap<UClass, KtLightClassForScript>(psi)
override fun getSuperClass(): UClass? = super.getSuperClass()
override fun getFields(): Array<UField> = super.getFields()
override fun getInitializers(): Array<UClassInitializer> = super.getInitializers()
override fun getInnerClasses(): Array<UClass> =
psi.innerClasses.mapNotNull { getLanguagePlugin().convertOpt<UClass>(it, this) }.toTypedArray()
override fun getMethods(): Array<UMethod> = psi.methods.map(this::createUMethod).toTypedArray()
private fun createUMethod(method: PsiMethod): UMethod {
return if (method.isConstructor) {
KotlinScriptConstructorUMethod(psi.script, method as KtLightMethod, this)
}
else {
getLanguagePlugin().convertOpt(method, this) ?: reportConvertFailure(method)
}
}
override fun getOriginalElement(): PsiElement? = psi.originalElement
class KotlinScriptConstructorUMethod(
script: KtScript,
override val psi: KtLightMethod,
override val uastParent: UElement?
) : KotlinUMethod(psi, uastParent) {
override val uastBody: UExpression? by lz {
val initializers = script.declarations.filterIsInstance<KtScriptInitializer>()
KotlinUBlockExpression.create(initializers, this)
}
override val javaPsi = psi
override val sourcePsi = psi.kotlinOrigin
}
}
private fun reportConvertFailure(psiMethod: PsiMethod): Nothing {
val isValid = psiMethod.isValid
val report = KotlinExceptionWithAttachments(
"cant convert $psiMethod of ${psiMethod.javaClass} to UMethod"
+ if (!isValid) " (method is not valid)" else ""
)
if (isValid) {
report.withAttachment("method", psiMethod.text)
psiMethod.containingFile?.let {
report.withAttachment("file", it.text)
}
}
throw report
}
@@ -338,7 +338,7 @@ open class KotlinUAnnotatedLocalVariable(
override val annotations: List<UAnnotation> by lz { computeAnnotations(this) }
}
class KotlinUEnumConstant(
open class KotlinUEnumConstant(
psi: PsiEnumConstant,
override val sourcePsi: KtElement?,
givenParent: UElement?
@@ -350,10 +350,6 @@ class KotlinUEnumConstant(
}
}
override fun getInitializer(): PsiExpression? = super<AbstractKotlinUVariable>.getInitializer()
override fun getOriginalElement(): PsiElement? = super<AbstractKotlinUVariable>.getOriginalElement()
override val javaPsi = unwrap<UEnumConstant, PsiEnumConstant>(psi)
override val psi = javaPsi
@@ -409,11 +405,15 @@ class KotlinUEnumConstant(
private class KotlinEnumConstantClassReference(
override val psi: PsiEnumConstant,
override val sourcePsi: KtElement?,
givenParent: UElement?
private val givenParent: UElement?
) : KotlinAbstractUExpression(givenParent), USimpleNameReferenceExpression {
override val javaPsi: PsiElement?
get() = psi
override val uastParent: UElement? by lz {
givenParent ?: KotlinUastLanguagePlugin().convertElementWithParent(psi.parent ?: psi.containingFile, null)
}
override fun resolve() = psi.containingClass
override val resolvedName: String?
get() = psi.containingClass?.name
@@ -0,0 +1,46 @@
/*
* 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
import com.intellij.psi.PsiElement
import org.jetbrains.uast.kotlin.KotlinAbstractUExpression
import org.jetbrains.uast.kotlin.doConvertParent
open class KotlinUDeclarationsExpression(
override val psi: PsiElement?,
givenParent: UElement?,
val psiAnchor: PsiElement? = null
) : KotlinAbstractUExpression(givenParent), UDeclarationsExpression {
override val sourcePsi: PsiElement?
get() = psiAnchor
override val uastParent: UElement?
get() = if (psiAnchor != null) doConvertParent(this, psiAnchor.parent) else super.uastParent
constructor(uastParent: UElement?) : this(null, uastParent)
override lateinit var declarations: List<UDeclaration>
internal set
}
class KotlinUDestructuringDeclarationExpression(
givenParent: UElement?,
psiAnchor: PsiElement
) : KotlinUDeclarationsExpression(null, givenParent, psiAnchor) {
val tempVarAssignment get() = declarations.first()
}