FIR/UAST: commonize abstract UElement and UExpression

This commit is contained in:
Jinseong Jeon
2021-05-13 14:01:00 -07:00
committed by Ilya Kirillov
parent a54a807dc9
commit 3c3b5aa4ac
20 changed files with 188 additions and 161 deletions
+1
View File
@@ -78,6 +78,7 @@ dependencies {
compile(project(":idea:idea-jps-common"))
compile(project(":idea:kotlin-gradle-tooling"))
compile(project(":idea:line-indent-provider"))
compile(project(":plugins:uast-kotlin-base"))
compile(project(":plugins:uast-kotlin"))
compile(project(":plugins:uast-kotlin-idea"))
compile(project(":plugins:uast-kotlin-idea-base"))
+2
View File
@@ -128,6 +128,8 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
<projectService serviceInterface="org.jetbrains.kotlin.psi.KtFileClassProvider"
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.KtFileClassProviderImpl"/>
<projectService serviceInterface="org.jetbrains.uast.kotlin.BaseKotlinUastResolveProviderService"
serviceImplementation="org.jetbrains.uast.kotlin.internal.FirIdeaKotlinUastResolveProviderService"/>
<projectService serviceInterface="org.jetbrains.uast.kotlin.FirKotlinUastResolveProviderService"
serviceImplementation="org.jetbrains.uast.kotlin.internal.FirIdeaKotlinUastResolveProviderService"/>
+2
View File
@@ -137,6 +137,8 @@
displayName="Incompatible API usage"
implementationClass="org.jetbrains.kotlin.idea.inspections.api.IncompatibleAPIInspection"/>
<projectService serviceInterface="org.jetbrains.uast.kotlin.BaseKotlinUastResolveProviderService"
serviceImplementation="org.jetbrains.uast.kotlin.internal.IdeaKotlinUastResolveProviderService"/>
<projectService serviceInterface="org.jetbrains.uast.kotlin.KotlinUastResolveProviderService"
serviceImplementation="org.jetbrains.uast.kotlin.internal.IdeaKotlinUastResolveProviderService"/>
@@ -0,0 +1,16 @@
/*
* 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.PsiElement
import org.jetbrains.uast.UElement
interface BaseKotlinUastResolveProviderService {
fun isJvmElement(psiElement: PsiElement): Boolean
fun convertParent(uElement: UElement): UElement?
}
@@ -5,21 +5,30 @@
package org.jetbrains.uast.kotlin
import com.intellij.psi.PsiElement
import com.intellij.openapi.components.ServiceManager
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UastFacade
import org.jetbrains.uast.UastLanguagePlugin
import org.jetbrains.uast.kotlin.internal.KotlinUElementWithComments
abstract class FirKotlinAbstractUElement(
private val givenParent: UElement?
abstract class KotlinAbstractUElement(
givenParent: UElement?
) : KotlinUElementWithComments {
protected val languagePlugin: UastLanguagePlugin? by lz {
psi?.let { UastFacade.findPlugin(it) }
}
protected val baseResolveProviderService: BaseKotlinUastResolveProviderService? by lz {
psi?.project?.let { ServiceManager.getService(it, BaseKotlinUastResolveProviderService::class.java) }
}
final override val uastParent: UElement? by lz {
givenParent ?: convertParent()
}
protected open fun convertParent(): UElement? {
TODO("Not yet implemented")
return baseResolveProviderService?.convertParent(this)
}
override fun equals(other: Any?): Boolean {
@@ -27,21 +36,10 @@ abstract class FirKotlinAbstractUElement(
return false
}
return this.sourcePsi == other.sourcePsi
return this.psi == other.psi
}
override fun hashCode(): Int {
return sourcePsi?.hashCode() ?: 0
return psi?.hashCode() ?: 0
}
}
abstract class FirKotlinAbstractUExpression(
givenParent: UElement?
) : FirKotlinAbstractUElement(givenParent), UExpression {
override val javaPsi: PsiElement? = null
override val psi: PsiElement?
get() = sourcePsi
// TODO: annotations
}
@@ -0,0 +1,29 @@
/*
* 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.PsiElement
import org.jetbrains.kotlin.psi.KtAnnotatedExpression
import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.convertOpt
abstract class KotlinAbstractUExpression(
givenParent: UElement?
) : KotlinAbstractUElement(givenParent), UExpression {
override val javaPsi: PsiElement? = null
override val psi
get() = sourcePsi
override val uAnnotations: List<UAnnotation>
get() {
val annotatedExpression = sourcePsi?.parent as? KtAnnotatedExpression ?: return emptyList()
return annotatedExpression.annotationEntries.mapNotNull { languagePlugin?.convertOpt(it, this) }
}
}
@@ -6,6 +6,7 @@
package org.jetbrains.uast.kotlin
import com.intellij.lang.Language
import com.intellij.openapi.components.ServiceManager
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.kotlin.psi.KtClassOrObject
@@ -18,8 +19,10 @@ import org.jetbrains.uast.UExpression
import org.jetbrains.uast.UastLanguagePlugin
import org.jetbrains.uast.kotlin.FirKotlinConverter.convertDeclarationOrElement
interface FirKotlinUastResolveProviderService {
fun isJvmElement(psiElement: PsiElement): Boolean
interface FirKotlinUastResolveProviderService : BaseKotlinUastResolveProviderService {
override fun convertParent(uElement: UElement): UElement? {
TODO("Not yet implemented")
}
}
class FirKotlinUastLanguagePlugin : UastLanguagePlugin {
@@ -33,7 +36,10 @@ class FirKotlinUastLanguagePlugin : UastLanguagePlugin {
}
private val PsiElement.isJvmElement: Boolean
get() = service.isJvmElement(this)
get() {
val resolveProvider = ServiceManager.getService(project, FirKotlinUastResolveProviderService::class.java)
return resolveProvider.isJvmElement(this)
}
override fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class<out UElement>?): UElement? {
if (!element.isJvmElement) return null
@@ -18,7 +18,7 @@ import org.jetbrains.uast.visitor.UastVisitor
sealed class AbstractFirKotlinUClass(
givenParent: UElement?
) : FirKotlinAbstractUElement(givenParent), UClass, UAnchorOwner {
) : KotlinAbstractUElement(givenParent), UClass, UAnchorOwner {
override val uAnnotations: List<UAnnotation>
get() {
// TODO: Not yet implemented
@@ -19,7 +19,7 @@ import org.jetbrains.uast.USimpleNameReferenceExpression
class FirKotlinUImportStatement(
override val psi: KtImportDirective,
givenParent: UElement?
): FirKotlinAbstractUElement(givenParent), UImportStatement {
) : KotlinAbstractUElement(givenParent), UImportStatement {
override val javaPsi: PsiElement? = null
override val sourcePsi: KtImportDirective = psi
@@ -41,7 +41,7 @@ class FirKotlinUImportStatement(
override val identifier: String,
givenParent: UElement?,
private val importDirective: KtImportDirective
) : FirKotlinAbstractUExpression(givenParent), USimpleNameReferenceExpression {
) : KotlinAbstractUExpression(givenParent), USimpleNameReferenceExpression {
override val sourcePsi: KtExpression = psi
override val resolvedName: String = identifier
@@ -18,7 +18,7 @@ open class FirKotlinUMethod(
psi: PsiMethod,
final override val sourcePsi: KtDeclaration?,
givenParent: UElement?
) : FirKotlinAbstractUElement(givenParent), UMethod, UAnchorOwner, PsiMethod by psi {
) : KotlinAbstractUElement(givenParent), UMethod, UAnchorOwner, PsiMethod by psi {
constructor(
psi: KtLightMethod,
givenParent: UElement?
@@ -14,7 +14,7 @@ import org.jetbrains.uast.visitor.UastVisitor
sealed class AbstractFirKotlinUVariable(
givenParent: UElement?
) : FirKotlinAbstractUElement(givenParent), PsiVariable, UVariableEx, UAnchorOwner {
) : KotlinAbstractUElement(givenParent), PsiVariable, UVariableEx, UAnchorOwner {
override val uAnnotations: List<UAnnotation>
get() {
// TODO: Not yet implemented
@@ -15,7 +15,7 @@ class FirKotlinUTypeReferenceExpression(
override val sourcePsi: KtTypeReference,
givenParent: UElement?,
private val typeSupplier: (() -> PsiType)? = null,
) : FirKotlinAbstractUExpression(givenParent), UTypeReferenceExpression {
) : KotlinAbstractUExpression(givenParent), UTypeReferenceExpression {
override val type: PsiType by lz {
typeSupplier?.invoke() ?: sourcePsi.toPsiType(uastParent ?: this)
}
@@ -8,11 +8,10 @@ package org.jetbrains.uast.kotlin
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UExpression
import org.jetbrains.uast.kotlin.FirKotlinAbstractUExpression
class FirUnknownKotlinExpression(
override val sourcePsi: KtExpression,
givenParent: UElement?
) : FirKotlinAbstractUExpression(givenParent), UExpression {
) : KotlinAbstractUExpression(givenParent), UExpression {
override fun asLogString() = "[!] FirUnknownKotlinExpression ($sourcePsi)"
}
@@ -5,8 +5,6 @@
package org.jetbrains.uast.kotlin
import com.intellij.openapi.components.ServiceManager
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.KotlinLanguage
import org.jetbrains.uast.UastLanguagePlugin
@@ -14,8 +12,3 @@ val firKotlinUastPlugin: UastLanguagePlugin by lz {
UastLanguagePlugin.getInstances().find { it.language == KotlinLanguage.INSTANCE }
?: FirKotlinUastLanguagePlugin()
}
internal val PsiElement.service: FirKotlinUastResolveProviderService
get() {
return ServiceManager.getService(project, FirKotlinUastResolveProviderService::class.java)
}
@@ -19,6 +19,7 @@ import org.jetbrains.uast.UFile
import org.jetbrains.uast.UastFacade
import org.jetbrains.uast.UastLanguagePlugin
import org.jetbrains.uast.common.kotlin.FirUastPluginSelection
import org.jetbrains.uast.kotlin.BaseKotlinUastResolveProviderService
import org.jetbrains.uast.kotlin.FirKotlinUastResolveProviderService
import org.jetbrains.uast.kotlin.firKotlinUastPlugin
import org.jetbrains.uast.kotlin.internal.FirCliKotlinUastResolveProviderService
@@ -45,9 +46,14 @@ abstract class AbstractFirUastTest : KotlinLightCodeInsightFixtureTestCase(), Fi
)
area.getExtensionPoint(UastLanguagePlugin.extensionPointName)
.registerExtension(firKotlinUastPlugin, testRootDisposable)
val service = FirCliKotlinUastResolveProviderService()
project.registerServiceInstance(
BaseKotlinUastResolveProviderService::class.java,
service
)
project.registerServiceInstance(
FirKotlinUastResolveProviderService::class.java,
FirCliKotlinUastResolveProviderService()
service
)
}
@@ -11,8 +11,9 @@ dependencies {
compile(project(":compiler:frontend.java"))
compile(project(":idea:ide-common"))
compile(project(":idea:idea-core"))
compile(project(":plugins:uast-kotlin-idea-base"))
compile(project(":plugins:uast-kotlin-base"))
compile(project(":plugins:uast-kotlin-fir"))
compile(project(":plugins:uast-kotlin-idea-base"))
compileOnly(intellijDep())
compileOnly(intellijPluginDep("java"))
}
@@ -11,6 +11,7 @@ dependencies {
compile(project(":compiler:frontend.java"))
compile(project(":idea:ide-common"))
compile(project(":idea:idea-core"))
compile(project(":plugins:uast-kotlin-base"))
compile(project(":plugins:uast-kotlin"))
compile(project(":plugins:uast-kotlin-idea-base"))
compileOnly(intellijDep())
@@ -53,12 +53,15 @@ import org.jetbrains.uast.kotlin.declarations.KotlinUMethodWithFakeLightDelegate
import org.jetbrains.uast.kotlin.expressions.*
import org.jetbrains.uast.kotlin.psi.*
interface KotlinUastResolveProviderService {
interface KotlinUastResolveProviderService : BaseKotlinUastResolveProviderService {
fun getBindingContext(element: KtElement): BindingContext
fun getTypeMapper(element: KtElement): KotlinTypeMapper?
fun getLanguageVersionSettings(element: KtElement): LanguageVersionSettings
fun isJvmElement(psiElement: PsiElement): Boolean
fun getReferenceVariants(ktElement: KtElement, nameHint: String): Sequence<DeclarationDescriptor>
override fun convertParent(uElement: UElement): UElement? {
return convertParentImpl(uElement)
}
}
var PsiElement.destructuringDeclarationInitializer: Boolean? by UserDataProperty(Key.create("kotlin.uast.destructuringDeclarationInitializer"))
@@ -33,122 +33,104 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.expressions.KotlinLocalFunctionULambdaExpression
import org.jetbrains.uast.kotlin.expressions.KotlinUElvisExpression
import org.jetbrains.uast.kotlin.internal.KotlinUElementWithComments
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiParameter
import org.jetbrains.uast.kotlin.psi.UastKotlinPsiVariable
abstract class KotlinAbstractUElement(private val givenParent: UElement?) : KotlinUElementWithComments {
fun convertParentImpl(uElement: UElement): UElement? {
@Suppress("DEPRECATION")
val psi = uElement.psi //TODO: `psi` is deprecated but it seems that it couldn't be simply replaced for this case
var parent = psi?.parent ?: uElement.sourcePsi?.parent ?: psi?.containingFile
if (psi is PsiMethod && psi !is KtLightMethod) { // handling of synthetic things not represented in lightclasses directly
when (parent) {
is KtClassBody -> {
val grandParent = parent.parent
doConvertParent(uElement, grandParent)?.let { return it }
parent = grandParent
}
is KtFile -> {
parent.toUElementOfType<UClass>()?.let { return it } // mutlifile facade class
}
}
final override val uastParent: UElement? by lz {
givenParent ?: convertParent()
}
protected open fun convertParent(): UElement? {
@Suppress("DEPRECATION")
val psi = psi //TODO: `psi` is deprecated but it seems that it couldn't be simply replaced for this case
var parent = psi?.parent ?: sourcePsi?.parent ?: psi?.containingFile
if (psi is PsiMethod && psi !is KtLightMethod) { // handling of synthetic things not represented in lightclasses directly
when (parent) {
is KtClassBody -> {
val grandParent = parent.parent
doConvertParent(this, grandParent)?.let { return it }
parent = grandParent
}
is KtFile -> {
parent.toUElementOfType<UClass>()?.let { return it } // mutlifile facade class
}
}
if (psi is KtLightElement<*, *> && uElement.sourcePsi.safeAs<KtClassOrObject>()?.isLocal == true) {
val originParent = psi.kotlinOrigin?.parent
parent = when (originParent) {
null -> parent
is KtClassBody -> originParent.parent
else -> originParent
}
if (psi is KtLightElement<*, *> && sourcePsi.safeAs<KtClassOrObject>()?.isLocal == true) {
val originParent = psi.kotlinOrigin?.parent
parent = when (originParent) {
null -> parent
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 || psi is UastKotlinPsiParameter) && parent != null) {
parent = parent.parent
}
if (KotlinConverter.forceUInjectionHost) {
if (parent is KtBlockStringTemplateEntry) {
parent = parent.parent
}
} else
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
}
if (parent is KtPropertyDelegate) {
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}], result = $result")
}
return result
}
override fun equals(other: Any?): Boolean {
if (other !is UElement) {
return false
}
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
return this.psi == other.psi
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 || psi is UastKotlinPsiParameter) && parent != null) {
parent = parent.parent
}
override fun hashCode() = psi?.hashCode() ?: 0
if (KotlinConverter.forceUInjectionHost) {
if (parent is KtBlockStringTemplateEntry) {
parent = parent.parent
}
} else
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
}
if (parent is KtPropertyDelegate) {
parent = parent.parent
}
val result = doConvertParent(uElement, parent)
if (result == uElement) {
throw IllegalStateException("Loop in parent structure when converting a $psi of type ${psi?.javaClass} with parent $parent of type ${parent?.javaClass} text: [${parent?.text}], result = $result")
}
return result
}
fun doConvertParent(element: UElement, parent: PsiElement?): UElement? {
@@ -247,20 +229,3 @@ private fun findAnnotationClassFromConstructorParameter(parameter: KtParameter):
}
return null
}
abstract class KotlinAbstractUExpression(givenParent: UElement?) :
KotlinAbstractUElement(givenParent),
UExpression {
override val javaPsi: PsiElement? = null
override val psi
get() = sourcePsi
override val annotations: List<UAnnotation>
get() {
val annotatedExpression = sourcePsi?.parent as? KtAnnotatedExpression ?: return emptyList()
return annotatedExpression.annotationEntries.map { KotlinUAnnotation(it, this) }
}
}
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.test.TestJdkKind
import org.jetbrains.kotlin.test.testFramework.resetApplicationToNull
import org.jetbrains.uast.UastLanguagePlugin
import org.jetbrains.uast.evaluation.UEvaluatorExtension
import org.jetbrains.uast.kotlin.BaseKotlinUastResolveProviderService
import org.jetbrains.uast.kotlin.KotlinUastLanguagePlugin
import org.jetbrains.uast.kotlin.KotlinUastResolveProviderService
import org.jetbrains.uast.kotlin.evaluation.KotlinEvaluatorExtension
@@ -94,6 +95,10 @@ abstract class AbstractKotlinUastTest : AbstractUastTest() {
area.getExtensionPoint(UEvaluatorExtension.EXTENSION_POINT_NAME)
.registerExtension(KotlinEvaluatorExtension())
project.registerService(
BaseKotlinUastResolveProviderService::class.java,
CliKotlinUastResolveProviderService::class.java
)
project.registerService(
KotlinUastResolveProviderService::class.java,
CliKotlinUastResolveProviderService::class.java