FIR/UAST: commonize base UClass (with bogus annotation abstraction)

This commit is contained in:
Jinseong Jeon
2021-06-18 01:25:13 -07:00
committed by Ilya Kirillov
parent 06c20bb10e
commit 1749c90083
11 changed files with 205 additions and 188 deletions
@@ -0,0 +1,111 @@
/*
* 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.*
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.uast.*
abstract class BaseKotlinUClass(
psi: KtLightClass,
givenParent: UElement?
) : AbstractKotlinUClass(givenParent), PsiClass by psi {
final override 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 by lazy { getIdentifierSourcePsi()?.let { KotlinUIdentifier(nameIdentifier, it, this) } }
private fun getIdentifierSourcePsi(): PsiElement? {
ktClass?.nameIdentifier?.let { return it }
(ktClass as? KtObjectDeclaration)?.getObjectKeyword()?.let { return it }
return null
}
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 {
languagePlugin?.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)
buildSecondaryConstructorUMethod(ktClass, psiMethod, this)
else
buildPrimaryConstructorUMethod(ktClass, psiMethod, this)
} else {
languagePlugin?.convertOpt(psiMethod, this) ?: reportConvertFailure(psiMethod)
}
}
fun isDelegatedMethod(psiMethod: PsiMethod) = psiMethod is KtLightMethod && psiMethod.isDelegated
val result = ArrayList<UMethod>(javaPsi.methods.size)
val handledKtDeclarations = mutableSetOf<PsiElement>()
for (lightMethod in javaPsi.methods) {
if (isDelegatedMethod(lightMethod)) continue
val uMethod = createUMethod(lightMethod)
result.add(uMethod)
// Ensure we pick the main Kotlin origin, not the auxiliary one
val kotlinOrigin = (lightMethod as? KtLightMethod)?.kotlinOrigin ?: uMethod.sourcePsi
handledKtDeclarations.addIfNotNull(kotlinOrigin)
}
val ktDeclarations: List<KtDeclaration> = run ktDeclarations@{
ktClass?.let { return@ktDeclarations it.declarations }
(javaPsi as? KtLightClassForFacade)?.let { facade ->
return@ktDeclarations facade.files.flatMap { file -> file.declarations }
}
emptyList()
}
ktDeclarations.asSequence()
.filterNot { handledKtDeclarations.contains(it) }
.mapNotNullTo(result) {
baseResolveProviderService.baseKotlinConverter.convertDeclaration(it, this, arrayOf(UElement::class.java)) as? UMethod
}
return result.toTypedArray()
}
abstract fun buildPrimaryConstructorUMethod(ktClass: KtClassOrObject?, psi: KtLightMethod, givenParent: UElement?) : UMethod
abstract fun buildSecondaryConstructorUMethod(ktClass: KtClassOrObject?, psi: KtLightMethod, givenParent: UElement?) : UMethod
}
@@ -19,10 +19,12 @@ 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.FirKotlinUAnnotation
internal object FirKotlinConverter : BaseKotlinConverter {
override fun convertAnnotation(annotationEntry: KtAnnotationEntry, givenParent: UElement?): UAnnotation {
TODO("Not yet implemented")
// TODO: need to polish/implement annotations more
return FirKotlinUAnnotation(annotationEntry, givenParent)
}
internal fun convertDeclarationOrElement(
@@ -0,0 +1,49 @@
/*
* 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.declarations
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.ResolveResult
import org.jetbrains.kotlin.asJava.toLightAnnotation
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.KotlinAbstractUElement
class FirKotlinUAnnotation(
annotationEntry: KtAnnotationEntry,
givenParent: UElement?
) : KotlinAbstractUElement(givenParent), UAnnotation, UAnchorOwner, UMultiResolvable {
override val javaPsi = annotationEntry.toLightAnnotation()
override val psi: PsiElement = annotationEntry
override val attributeValues: List<UNamedExpression>
get() = listOf() // TODO("Not yet implemented")
override val qualifiedName: String?
get() = "not-implemented-annotation"
override val uastAnchor: UIdentifier?
get() = TODO("Not yet implemented")
override fun findAttributeValue(name: String?): UExpression? {
// TODO("Not yet implemented")
return null
}
override fun findDeclaredAttributeValue(name: String?): UExpression? {
TODO("Not yet implemented")
}
override fun resolve(): PsiClass? {
TODO("Not yet implemented")
}
override fun multiResolve(): Iterable<ResolveResult> {
TODO("Not yet implemented")
}
}
@@ -5,114 +5,21 @@
package org.jetbrains.uast.kotlin
import com.intellij.psi.*
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.uast.*
// TODO: can be commonized once *KotlinUClass is commonized
class FirKotlinUClass(
override val javaPsi: KtLightClass,
psi: KtLightClass,
givenParent: UElement?,
) : AbstractKotlinUClass(givenParent), PsiClass by javaPsi {
override val ktClass: KtClassOrObject? = javaPsi.kotlinOrigin
override val psi = unwrap<UClass, PsiClass>(javaPsi)
override fun getSourceElement() = sourcePsi
override fun getOriginalElement(): PsiElement? = sourcePsi?.originalElement
override fun getNameIdentifier(): PsiIdentifier = UastLightIdentifier(psi, ktClass)
override fun getContainingFile(): PsiFile = unwrapFakeFileForLightClass(psi.containingFile)
override val uastAnchor: UIdentifier? by lz {
getIdentifierSourcePsi()?.let {
KotlinUIdentifier(nameIdentifier, it, this)
}
) : BaseKotlinUClass(psi, givenParent) {
override fun buildPrimaryConstructorUMethod(ktClass: KtClassOrObject?, psi: KtLightMethod, givenParent: UElement?): UMethod {
return FirKotlinConstructorUMethod(ktClass, psi, givenParent)
}
private fun getIdentifierSourcePsi(): PsiElement? {
ktClass?.nameIdentifier?.let { return it }
(ktClass as? KtObjectDeclaration)?.getObjectKeyword()?.let { return it }
return null
}
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 {
languagePlugin?.convertOpt<UClass>(it, this)
}.toTypedArray()
}
override fun getSuperClass(): UClass? {
return super.getSuperClass()
}
override fun getFields(): Array<UField> {
return super.getFields()
}
override fun getInitializers(): Array<UClassInitializer> {
// TODO: why not just emptyList()? Kotlin class won't have <clinit>?
return 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)
FirKotlinSecondaryConstructorWithInitializersUMethod(ktClass, psiMethod, this)
else
FirKotlinConstructorUMethod(ktClass, psiMethod, this)
} else {
languagePlugin?.convertOpt(psiMethod, this) ?: reportConvertFailure(psiMethod)
}
}
fun isDelegatedMethod(psiMethod: PsiMethod) = psiMethod is KtLightMethod && psiMethod.isDelegated
val result = ArrayList<UMethod>(javaPsi.methods.size)
val handledKtDeclarations = mutableSetOf<PsiElement>()
for (lightMethod in javaPsi.methods) {
if (isDelegatedMethod(lightMethod)) continue
val uMethod = createUMethod(lightMethod)
result.add(uMethod)
// Ensure we pick the main Kotlin origin, not the auxiliary one
val kotlinOrigin = (lightMethod as? KtLightMethod)?.kotlinOrigin ?: uMethod.sourcePsi
handledKtDeclarations.addIfNotNull(kotlinOrigin)
}
val ktDeclarations: List<KtDeclaration> = run ktDeclarations@{
ktClass?.let { return@ktDeclarations it.declarations }
(javaPsi as? KtLightClassForFacade)?.let { facade ->
return@ktDeclarations facade.files.flatMap { file -> file.declarations }
}
emptyList()
}
ktDeclarations.asSequence()
.filterNot { handledKtDeclarations.contains(it) }
.mapNotNullTo(result) {
baseResolveProviderService.baseKotlinConverter.convertDeclaration(it, this, arrayOf(UElement::class.java)) as? UMethod
}
return result.toTypedArray()
override fun buildSecondaryConstructorUMethod(ktClass: KtClassOrObject?, psi: KtLightMethod, givenParent: UElement?): UMethod {
return FirKotlinSecondaryConstructorWithInitializersUMethod(ktClass, psi, givenParent)
}
companion object {
@@ -2,16 +2,21 @@ UFile (package = )
UClass (name = Annotation)
UMethod (name = strings)
UClass (name = A)
UAnnotation (fqName = not-implemented-annotation)
UMethod (name = A)
UClass (name = AnnotationInner)
UMethod (name = value)
UClass (name = B1)
UAnnotation (fqName = not-implemented-annotation)
UMethod (name = B1)
UClass (name = B2)
UAnnotation (fqName = not-implemented-annotation)
UMethod (name = B2)
UClass (name = AnnotationArray)
UMethod (name = value)
UClass (name = C)
UAnnotation (fqName = not-implemented-annotation)
UMethod (name = C)
UClass (name = C2)
UAnnotation (fqName = not-implemented-annotation)
UMethod (name = C2)
@@ -1,15 +1,20 @@
UFile (package = )
UClass (name = A)
UAnnotation (fqName = not-implemented-annotation)
UMethod (name = A)
UClass (name = MyAnnotation)
UMethod (name = text)
UClass (name = B)
UAnnotation (fqName = not-implemented-annotation)
UField (name = Companion)
UMethod (name = B)
UClass (name = InB)
UAnnotation (fqName = not-implemented-annotation)
UMethod (name = InB)
UClass (name = Companion)
UAnnotation (fqName = not-implemented-annotation)
UMethod (name = Companion)
UClass (name = Obj)
UAnnotation (fqName = not-implemented-annotation)
UField (name = INSTANCE)
UMethod (name = Obj)
@@ -2,16 +2,21 @@ UFile (package = ) [public abstract annotation Annotation {...]
UClass (name = Annotation) [public abstract annotation Annotation {...}]
UMethod (name = strings) [public abstract fun strings() : java.lang.String[] = UastEmptyExpression]
UClass (name = A) [public final class A {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = A) [public fun A() = UastEmptyExpression]
UClass (name = AnnotationInner) [public abstract annotation AnnotationInner {...}]
UMethod (name = value) [public abstract fun value() : Annotation = UastEmptyExpression]
UClass (name = B1) [public final class B1 {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = B1) [public fun B1() = UastEmptyExpression]
UClass (name = B2) [public final class B2 {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = B2) [public fun B2() = UastEmptyExpression]
UClass (name = AnnotationArray) [public abstract annotation AnnotationArray {...}]
UMethod (name = value) [public abstract fun value() : Annotation[] = UastEmptyExpression]
UClass (name = C) [public final class C {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = C) [public fun C() = UastEmptyExpression]
UClass (name = C2) [public final class C2 {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = C2) [public fun C2() = UastEmptyExpression]
@@ -1,15 +1,20 @@
UFile (package = ) [public final class A {...]
UClass (name = A) [public final class A {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = A) [public fun A() = UastEmptyExpression]
UClass (name = MyAnnotation) [public abstract annotation MyAnnotation {...}]
UMethod (name = text) [public abstract fun text() : java.lang.String = UastEmptyExpression]
UClass (name = B) [public final class B {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UField (name = Companion) [public static final var Companion: B.Companion]
UMethod (name = B) [public fun B() = UastEmptyExpression]
UClass (name = InB) [public static final class InB {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = InB) [public fun InB() = UastEmptyExpression]
UClass (name = Companion) [public static final class Companion {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = Companion) [private fun Companion() = UastEmptyExpression]
UClass (name = Obj) [public final class Obj {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UField (name = INSTANCE) [public static final var INSTANCE: Obj]
UMethod (name = Obj) [private fun Obj() = UastEmptyExpression]
@@ -2,16 +2,21 @@ UFile (package = ) [public abstract annotation Annotation {...]
UClass (name = Annotation) [public abstract annotation Annotation {...}]
UMethod (name = strings) [public abstract fun strings() : java.lang.String[] = UastEmptyExpression]
UClass (name = A) [public final class A {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = A) [public fun A() = UastEmptyExpression]
UClass (name = AnnotationInner) [public abstract annotation AnnotationInner {...}]
UMethod (name = value) [public abstract fun value() : Annotation = UastEmptyExpression]
UClass (name = B1) [public final class B1 {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = B1) [public fun B1() = UastEmptyExpression]
UClass (name = B2) [public final class B2 {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = B2) [public fun B2() = UastEmptyExpression]
UClass (name = AnnotationArray) [public abstract annotation AnnotationArray {...}]
UMethod (name = value) [public abstract fun value() : Annotation[] = UastEmptyExpression]
UClass (name = C) [public final class C {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = C) [public fun C() = UastEmptyExpression]
UClass (name = C2) [public final class C2 {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = C2) [public fun C2() = UastEmptyExpression]
@@ -1,15 +1,20 @@
UFile (package = ) [public final class A {...]
UClass (name = A) [public final class A {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = A) [public fun A() = UastEmptyExpression]
UClass (name = MyAnnotation) [public abstract annotation MyAnnotation {...}]
UMethod (name = text) [public abstract fun text() : java.lang.String = UastEmptyExpression]
UClass (name = B) [public final class B {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UField (name = Companion) [public static final var Companion: B.Companion]
UMethod (name = B) [public fun B() = UastEmptyExpression]
UClass (name = InB) [public static final class InB {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = InB) [public fun InB() = UastEmptyExpression]
UClass (name = Companion) [public static final class Companion {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UMethod (name = Companion) [private fun Companion() = UastEmptyExpression]
UClass (name = Obj) [public final class Obj {...}]
UAnnotation (fqName = not-implemented-annotation) [@not-implemented-annotation]
UField (name = INSTANCE) [public static final var INSTANCE: Obj]
UMethod (name = Obj) [private fun Obj() = UastEmptyExpression]
@@ -19,103 +19,21 @@ package org.jetbrains.uast.kotlin
import com.intellij.psi.*
import com.intellij.psi.impl.light.LightPsiClassBuilder
import org.jetbrains.kotlin.asJava.classes.KtLightClass
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
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.addIfNotNull
import org.jetbrains.uast.*
open class KotlinUClass(
class KotlinUClass(
psi: KtLightClass,
givenParent: UElement?
) : AbstractKotlinUClass(givenParent), PsiClass by psi {
final override 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 by lazy { getIdentifierSourcePsi()?.let { KotlinUIdentifier(nameIdentifier, it, this) } }
private fun getIdentifierSourcePsi(): PsiElement? {
ktClass?.nameIdentifier?.let { return it }
(ktClass as? KtObjectDeclaration)?.getObjectKeyword()?.let { return it }
return null
) : BaseKotlinUClass(psi, givenParent) {
override fun buildPrimaryConstructorUMethod(ktClass: KtClassOrObject?, psi: KtLightMethod, givenParent: UElement?): UMethod {
return KotlinConstructorUMethod(ktClass, psi, givenParent)
}
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 {
languagePlugin?.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 {
languagePlugin?.convertOpt(psiMethod, this) ?: reportConvertFailure(psiMethod)
}
}
fun isDelegatedMethod(psiMethod: PsiMethod) = psiMethod is KtLightMethod && psiMethod.isDelegated
val result = ArrayList<UMethod>(javaPsi.methods.size)
val handledKtDeclarations = mutableSetOf<PsiElement>()
for (lightMethod in javaPsi.methods) {
if (isDelegatedMethod(lightMethod)) continue
val uMethod = createUMethod(lightMethod)
result.add(uMethod)
// Ensure we pick the main Kotlin origin, not the auxiliary one
val kotlinOrigin = (lightMethod as? KtLightMethod)?.kotlinOrigin ?: uMethod.sourcePsi
handledKtDeclarations.addIfNotNull(kotlinOrigin)
}
val ktDeclarations: List<KtDeclaration> = run ktDeclarations@{
ktClass?.let { return@ktDeclarations it.declarations }
(javaPsi as? KtLightClassForFacade)?.let { facade ->
return@ktDeclarations facade.files.flatMap { file -> file.declarations }
}
emptyList()
}
ktDeclarations.asSequence()
.filterNot { handledKtDeclarations.contains(it) }
.mapNotNullTo(result) {
baseResolveProviderService.baseKotlinConverter.convertDeclaration(it, this, arrayOf(UElement::class.java)) as? UMethod
}
return result.toTypedArray()
override fun buildSecondaryConstructorUMethod(ktClass: KtClassOrObject?, psi: KtLightMethod, givenParent: UElement?): UMethod {
return KotlinSecondaryConstructorWithInitializersUMethod(ktClass, psi, givenParent)
}
companion object {