FIR/UAST: commonize constructor declarations

This commit is contained in:
Jinseong Jeon
2021-06-18 22:58:58 -07:00
committed by Ilya Kirillov
parent cbaa645dbe
commit 5ec5b7f041
10 changed files with 275 additions and 178 deletions
@@ -0,0 +1,70 @@
/*
* 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.psi.PsiMethod
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UIdentifier
abstract class BaseKotlinConstructorUMethod(
private val ktClass: KtClassOrObject?,
override val psi: PsiMethod,
kotlinOrigin: KtDeclaration?,
givenParent: UElement?
) : BaseKotlinUMethod(psi, kotlinOrigin, givenParent) {
override val javaPsi = psi
val isPrimary: Boolean
get() = sourcePsi is KtPrimaryConstructor || sourcePsi is KtClassOrObject
override val uastBody: UExpression? by lz {
val delegationCall: KtCallElement? = sourcePsi.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
KotlinLazyUBlockExpression(this) { uastParent ->
SmartList<UExpression>().apply {
delegationCall?.let {
add(buildDelegationCall(it, uastParent))
}
bodyExpressions.forEach {
add(baseResolveProviderService.baseKotlinConverter.convertOrEmpty(it, uastParent))
}
}
}
}
abstract fun buildDelegationCall(delegationCall: KtCallElement, uastParent: UElement): UExpression
override val uastAnchor: UIdentifier? by lz {
KotlinUIdentifier(
javaPsi.nameIdentifier,
if (isPrimary) ktClass?.nameIdentifier else (sourcePsi as? KtSecondaryConstructor)?.getConstructorKeyword(),
this
)
}
protected open fun getBodyExpressions(): List<KtExpression> {
if (isPrimary) return getInitializers()
val bodyExpression = (sourcePsi as? KtFunction)?.bodyExpression ?: return emptyList()
if (bodyExpression is KtBlockExpression) return bodyExpression.statements
return listOf(bodyExpression)
}
protected fun getInitializers(): List<KtExpression> {
return ktClass?.getAnonymousInitializers()?.mapNotNull { it.body } ?: emptyList()
}
}
@@ -0,0 +1,25 @@
/*
* 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 org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.uast.UElement
// 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.
abstract class BaseKotlinSecondaryConstructorWithInitializersUMethod(
ktClass: KtClassOrObject?,
psi: KtLightMethod,
givenParent: UElement?
) : BaseKotlinConstructorUMethod(ktClass, psi, psi.kotlinOrigin, givenParent) {
override fun getBodyExpressions(): List<KtExpression> {
return getInitializers() + super.getBodyExpressions()
}
}
@@ -0,0 +1,35 @@
/*
* 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.psi.PsiMethod
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.psi.KtCallElement
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UParameter
import org.jetbrains.uast.UastEmptyExpression
class FirKotlinConstructorUMethod(
ktClass: KtClassOrObject?,
psi: PsiMethod,
kotlinOrigin: KtDeclaration?,
givenParent: UElement?
) : BaseKotlinConstructorUMethod(ktClass, psi, kotlinOrigin, givenParent), FirKotlinUMethodParametersProducer {
constructor(
ktClass: KtClassOrObject?,
psi: KtLightMethod,
givenParent: UElement?
) : this(ktClass, psi, psi.kotlinOrigin, givenParent)
override val uastParameters: List<UParameter> by lz { produceUastParameters(this, receiverTypeReference) }
override fun buildDelegationCall(delegationCall: KtCallElement, uastParent: UElement): UExpression {
return UastEmptyExpression(uastParent)
}
}
@@ -0,0 +1,26 @@
/*
* 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 org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.psi.KtCallElement
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UParameter
import org.jetbrains.uast.UastEmptyExpression
class FirKotlinSecondaryConstructorWithInitializersUMethod(
ktClass: KtClassOrObject?,
psi: KtLightMethod,
givenParent: UElement?
) : BaseKotlinSecondaryConstructorWithInitializersUMethod(ktClass, psi, givenParent), FirKotlinUMethodParametersProducer {
override val uastParameters: List<UParameter> by lz { produceUastParameters(this, receiverTypeReference) }
override fun buildDelegationCall(delegationCall: KtCallElement, uastParent: UElement): UExpression {
return UastEmptyExpression(uastParent)
}
}
@@ -10,35 +10,25 @@ import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.uast.*
open class FirKotlinUMethod(
class FirKotlinUMethod(
psi: PsiMethod,
sourcePsi: KtDeclaration?,
givenParent: UElement?
) : BaseKotlinUMethod(psi, sourcePsi, givenParent) {
) : BaseKotlinUMethod(psi, sourcePsi, givenParent), FirKotlinUMethodParametersProducer {
constructor(
psi: KtLightMethod,
givenParent: UElement?
) : this(psi, getKotlinMemberOrigin(psi), givenParent)
override val uastParameters: List<UParameter> by lz {
val lightParams = psi.parameterList.parameters
val receiverTypeReference =
receiverTypeReference ?: return@lz lightParams.map { FirKotlinUParameter(it, getKotlinMemberOrigin(it), this) }
val lightReceiver = lightParams.firstOrNull() ?: return@lz emptyList<UParameter>()
val uParameters = SmartList<UParameter>(FirKotlinReceiverUParameter(lightReceiver, receiverTypeReference, this))
lightParams.drop(1).mapTo(uParameters) { FirKotlinUParameter(it, getKotlinMemberOrigin(it), this) }
uParameters
}
override val uastParameters: List<UParameter> by lz { produceUastParameters(this, receiverTypeReference) }
companion object {
fun create(
psi: KtLightMethod,
givenParent: UElement?
): FirKotlinUMethod {
): UMethod {
return when (psi) {
is KtConstructor<*> ->
FirKotlinConstructorUMethod(psi.containingClassOrObject, psi, givenParent)
@@ -51,7 +41,7 @@ open class FirKotlinUMethod(
fun create(
sourcePsi: KtDeclaration?,
givenParent: UElement?
): FirKotlinUMethod? {
): UMethod? {
val javaPsi = when (sourcePsi) {
is KtPropertyAccessor ->
LightClassUtil.getLightClassAccessorMethod(sourcePsi)
@@ -69,76 +59,3 @@ open class FirKotlinUMethod(
}
}
}
// TODO: can be commonized if *KotlinUMethod is commonized
open class FirKotlinConstructorUMethod(
private val ktClass: KtClassOrObject?,
override val psi: PsiMethod,
kotlinOrigin: KtDeclaration?,
givenParent: UElement?
) : FirKotlinUMethod(psi, kotlinOrigin, givenParent) {
constructor(
ktClass: KtClassOrObject?,
psi: KtLightMethod,
givenParent: UElement?
) : this(ktClass, psi, psi.kotlinOrigin, givenParent)
override val javaPsi = psi
val isPrimary: Boolean
get() = sourcePsi is KtPrimaryConstructor || sourcePsi is KtClassOrObject
override val uastBody: UExpression? by lz {
val delegationCall: KtCallElement? = sourcePsi.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
KotlinLazyUBlockExpression(this) { uastParent ->
SmartList<UExpression>().apply {
delegationCall?.let {
// TODO: function call for delegationCall
add(UastEmptyExpression(uastParent))
}
bodyExpressions.forEach {
add(baseResolveProviderService.baseKotlinConverter.convertOrEmpty(it, uastParent))
}
}
}
}
override val uastAnchor: UIdentifier? by lz {
KotlinUIdentifier(
javaPsi.nameIdentifier,
if (isPrimary) ktClass?.nameIdentifier else (sourcePsi as? KtSecondaryConstructor)?.getConstructorKeyword(),
this
)
}
protected open fun getBodyExpressions(): List<KtExpression> {
if (isPrimary) return getInitializers()
val bodyExpression = (sourcePsi as? KtFunction)?.bodyExpression ?: return emptyList()
if (bodyExpression is KtBlockExpression) return bodyExpression.statements
return listOf(bodyExpression)
}
protected fun getInitializers(): List<KtExpression> {
return ktClass?.getAnonymousInitializers()?.mapNotNull { it.body } ?: emptyList()
}
}
// TODO: can be commonized if *KotlinUMethod is commonized
// also reuse the comments there (about KT-21617)
class FirKotlinSecondaryConstructorWithInitializersUMethod(
ktClass: KtClassOrObject?,
psi: KtLightMethod,
givenParent: UElement?
) : FirKotlinConstructorUMethod(ktClass, psi, givenParent) {
override fun getBodyExpressions(): List<KtExpression> {
return getInitializers() + super.getBodyExpressions()
}
}
@@ -0,0 +1,21 @@
/*
* 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 org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.uast.UParameter
internal interface FirKotlinUMethodParametersProducer {
fun produceUastParameters(uMethod: BaseKotlinUMethod, receiverTypeReference: KtTypeReference?): List<UParameter> {
val lightParams = uMethod.psi.parameterList.parameters
val receiver = receiverTypeReference ?: return lightParams.map { FirKotlinUParameter(it, getKotlinMemberOrigin(it), uMethod) }
val lightReceiver = lightParams.firstOrNull() ?: return emptyList()
val uParameters = SmartList<UParameter>(FirKotlinReceiverUParameter(lightReceiver, receiver, uMethod))
lightParams.drop(1).mapTo(uParameters) { FirKotlinUParameter(it, getKotlinMemberOrigin(it), uMethod) }
return uParameters
}
}
@@ -0,0 +1,33 @@
/*
* 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.psi.PsiMethod
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.psi.KtCallElement
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
class KotlinConstructorUMethod(
ktClass: KtClassOrObject?,
psi: PsiMethod,
kotlinOrigin: KtDeclaration?,
givenParent: UElement?
) : BaseKotlinConstructorUMethod(ktClass, psi, kotlinOrigin, givenParent), KotlinUMethodParametersProducer {
constructor(
ktClass: KtClassOrObject?,
psi: KtLightMethod,
givenParent: UElement?
) : this(ktClass, psi, psi.kotlinOrigin, givenParent)
override val uastParameters by lz { produceUastParameters(this, receiverTypeReference) }
override fun buildDelegationCall(delegationCall: KtCallElement, uastParent: UElement): UExpression {
return KotlinUFunctionCallExpression(delegationCall, uastParent)
}
}
@@ -0,0 +1,25 @@
/*
* 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 org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.psi.KtCallElement
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
class KotlinSecondaryConstructorWithInitializersUMethod(
ktClass: KtClassOrObject?,
psi: KtLightMethod,
givenParent: UElement?
) : BaseKotlinSecondaryConstructorWithInitializersUMethod(ktClass, psi, givenParent), KotlinUMethodParametersProducer {
override val uastParameters by lz { produceUastParameters(this, receiverTypeReference) }
override fun buildDelegationCall(delegationCall: KtCallElement, uastParent: UElement): UExpression {
return KotlinUFunctionCallExpression(delegationCall, uastParent)
}
}
@@ -21,41 +21,24 @@ import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.*
import org.jetbrains.uast.kotlin.psi.UastFakeLightMethod
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter
open class KotlinUMethod(
psi: PsiMethod,
sourcePsi: KtDeclaration?,
givenParent: UElement?
) : BaseKotlinUMethod(psi, sourcePsi, givenParent) {
) : BaseKotlinUMethod(psi, sourcePsi, givenParent), KotlinUMethodParametersProducer {
constructor(
psi: KtLightMethod,
givenParent: UElement?
) : this(psi, getKotlinMemberOrigin(psi), givenParent)
override val uastParameters by lz {
fun parameterOrigin(psiParameter: PsiParameter?): KtElement? = when (psiParameter) {
is KtLightElement<*, *> -> psiParameter.kotlinOrigin
is UastKotlinPsiParameter -> psiParameter.ktParameter
else -> null
}
val lightParams = psi.parameterList.parameters
val receiver = receiverTypeReference ?: return@lz lightParams.map { KotlinUParameter(it, parameterOrigin(it), this) }
val receiverLight = lightParams.firstOrNull() ?: return@lz emptyList<UParameter>()
val uParameters = SmartList<UParameter>(KotlinReceiverUParameter(receiverLight, receiver, this))
lightParams.drop(1).mapTo(uParameters) { KotlinUParameter(it, parameterOrigin(it), this) }
uParameters
}
override val uastParameters by lz { produceUastParameters(this, receiverTypeReference) }
companion object {
fun create(psi: KtLightMethod, containingElement: UElement?): KotlinUMethod {
fun create(psi: KtLightMethod, containingElement: UElement?): UMethod {
val kotlinOrigin = psi.kotlinOrigin
return if (kotlinOrigin is KtConstructor<*>) {
KotlinConstructorUMethod(
@@ -71,76 +54,6 @@ open class KotlinUMethod(
}
}
open class KotlinConstructorUMethod(
private val ktClass: KtClassOrObject?,
override val psi: PsiMethod,
kotlinOrigin: KtDeclaration?,
givenParent: UElement?
) : KotlinUMethod(psi, kotlinOrigin, givenParent) {
constructor(
ktClass: KtClassOrObject?,
psi: KtLightMethod,
givenParent: UElement?
) : this(ktClass, psi, psi.kotlinOrigin, givenParent)
val isPrimary: Boolean
get() = sourcePsi.let { it is KtPrimaryConstructor || it is KtClassOrObject }
override val uastBody: UExpression? by lz {
val delegationCall: KtCallElement? = sourcePsi.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
KotlinLazyUBlockExpression(this) { uastParent ->
SmartList<UExpression>().apply {
delegationCall?.let {
add(KotlinUFunctionCallExpression(it, uastParent))
}
bodyExpressions.forEach {
add(KotlinConverter.convertOrEmpty(it, uastParent))
}
}
}
}
override val uastAnchor: KotlinUIdentifier by lz {
KotlinUIdentifier(
psi.nameIdentifier,
if (isPrimary) ktClass?.nameIdentifier else (sourcePsi as? KtSecondaryConstructor)?.getConstructorKeyword(),
this
)
}
override val javaPsi = psi
protected open fun getBodyExpressions(): List<KtExpression> {
if (isPrimary) return getInitializers()
val bodyExpression = (sourcePsi 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()
}
open class KotlinUAnnotationMethod(
psi: KtLightMethod,
givenParent: UElement?
@@ -0,0 +1,32 @@
/*
* 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.psi.PsiParameter
import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.uast.UParameter
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter
internal interface KotlinUMethodParametersProducer {
fun produceUastParameters(uMethod: BaseKotlinUMethod, receiverTypeReference: KtTypeReference?): List<UParameter> {
fun parameterOrigin(psiParameter: PsiParameter?): KtElement? = when (psiParameter) {
is KtLightElement<*, *> -> psiParameter.kotlinOrigin
is UastKotlinPsiParameter -> psiParameter.ktParameter
else -> null
}
val lightParams = uMethod.psi.parameterList.parameters
val receiver = receiverTypeReference ?: return lightParams.map { KotlinUParameter(it, parameterOrigin(it), uMethod) }
val receiverLight = lightParams.firstOrNull() ?: return emptyList()
val uParameters = SmartList<UParameter>(KotlinReceiverUParameter(receiverLight, receiver, uMethod))
lightParams.drop(1).mapTo(uParameters) { KotlinUParameter(it, parameterOrigin(it), uMethod) }
return uParameters
}
}