From 209ba89a49ddc90b87879558e5cf6d576d203f9c Mon Sep 17 00:00:00 2001 From: Nicolay Mitropolsky Date: Tue, 5 Dec 2017 12:22:36 +0300 Subject: [PATCH] Uast: `KotlinSecondaryConstructorWithInitializersUMethod` introduced as workaround for KT-21617 to be the only constructor which includes `init` block when there is no primary constructors in the class --- .../uast/kotlin/declarations/KotlinUClass.kt | 24 +++++++++++++++---- .../expressions/KotlinUBlockExpression.kt | 12 ++++++++++ plugins/uast-kotlin/testData/Constructors.kt | 2 -- .../uast-kotlin/testData/Constructors.log.txt | 4 ++++ .../testData/Constructors.render.txt | 3 +++ 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt index d671fe73ae9..b0796e97167 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt @@ -81,11 +81,16 @@ open class KotlinUClass private constructor( override fun getInitializers(): Array = super.getInitializers() override fun getMethods(): Array { + val hasPrimaryConstructor = ktClass?.hasPrimaryConstructor() ?: false + var secondaryConstructorsCount = 0 fun createUMethod(psiMethod: PsiMethod): UMethod { return if (psiMethod is KtLightMethod && psiMethod.isConstructor) { - KotlinConstructorUMethod(ktClass, psiMethod, this) + if (!hasPrimaryConstructor && secondaryConstructorsCount++ == 0) + KotlinSecondaryConstructorWithInitializersUMethod(ktClass, psiMethod, this) + else + KotlinConstructorUMethod(ktClass, psiMethod, this) } else { getLanguagePlugin().convert(psiMethod, this) } @@ -111,7 +116,7 @@ open class KotlinUClass private constructor( } } -class KotlinConstructorUMethod( +open class KotlinConstructorUMethod( private val ktClass: KtClassOrObject?, override val psi: KtLightMethod, givenParent: UElement? @@ -143,17 +148,28 @@ class KotlinConstructorUMethod( } } - private fun getBodyExpressions(): List { + open protected fun getBodyExpressions(): List { if (isPrimary) return getInitializers() val bodyExpression = (psi.kotlinOrigin as? KtFunction)?.bodyExpression ?: return emptyList() if (bodyExpression is KtBlockExpression) return bodyExpression.statements return listOf(bodyExpression) } - private fun getInitializers() = ktClass?.getAnonymousInitializers()?.mapNotNull { it.body } ?: emptyList() + 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 = getInitializers() + super.getBodyExpressions() +} + class KotlinUAnonymousClass( psi: PsiAnonymousClass, givenParent: UElement? diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBlockExpression.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBlockExpression.kt index 480f0d73ae9..244e6d7aa04 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBlockExpression.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/expressions/KotlinUBlockExpression.kt @@ -44,4 +44,16 @@ class KotlinUBlockExpression( } } } + + override fun convertParent(): UElement? { + val directParent = super.convertParent() + if (directParent is UnknownKotlinExpression && directParent.psi is KtAnonymousInitializer) { + val containingUClass = directParent.getContainingUClass() ?: return directParent + containingUClass.methods + .find { it is KotlinConstructorUMethod && it.isPrimary || it is KotlinSecondaryConstructorWithInitializersUMethod }?.let { + return it.uastBody + } + } + return directParent + } } \ No newline at end of file diff --git a/plugins/uast-kotlin/testData/Constructors.kt b/plugins/uast-kotlin/testData/Constructors.kt index dccd7e134bd..c60ddf4022e 100644 --- a/plugins/uast-kotlin/testData/Constructors.kt +++ b/plugins/uast-kotlin/testData/Constructors.kt @@ -58,8 +58,6 @@ class AWithSecondaryInit { lateinit var a: String init { - // This body will not be picked by UAST because no lightMethod contains it. - // Of course it is not a desired behaviour, so fix it if you know how println() } diff --git a/plugins/uast-kotlin/testData/Constructors.log.txt b/plugins/uast-kotlin/testData/Constructors.log.txt index 7a162e6ac93..6d951f6b1f5 100644 --- a/plugins/uast-kotlin/testData/Constructors.log.txt +++ b/plugins/uast-kotlin/testData/Constructors.log.txt @@ -130,6 +130,10 @@ UFile (package = ) UCallExpression (kind = UastCallKind(name='constructor_call'), argCount = 0)) UIdentifier (Identifier ()) USimpleNameReferenceExpression (identifier = ) + UBlockExpression + UCallExpression (kind = UastCallKind(name='method_call'), argCount = 0)) + UIdentifier (Identifier (println)) + USimpleNameReferenceExpression (identifier = println) UBinaryExpression (operator = =) USimpleNameReferenceExpression (identifier = a) UQualifiedReferenceExpression diff --git a/plugins/uast-kotlin/testData/Constructors.render.txt b/plugins/uast-kotlin/testData/Constructors.render.txt index 8a3db9fa922..c551ca105a4 100644 --- a/plugins/uast-kotlin/testData/Constructors.render.txt +++ b/plugins/uast-kotlin/testData/Constructors.render.txt @@ -67,6 +67,9 @@ public final class AWithSecondaryInit { public final fun setA(p: java.lang.String) : void = UastEmptyExpression public fun AWithSecondaryInit(i: int) { () + { + println() + } a = i.toString() } public fun AWithSecondaryInit(s: java.lang.String) {