Uast: Uast produces reified methods on his own because light-classes doesn't (KT-34316)

This commit is contained in:
Nicolay Mitropolsky
2019-11-21 20:22:05 +03:00
parent 9c18df61ca
commit 2772659ed2
13 changed files with 175 additions and 12 deletions
@@ -17,8 +17,10 @@
package org.jetbrains.uast.kotlin
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClassForLocalDeclaration
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.toLightGetter
import org.jetbrains.kotlin.asJava.toLightSetter
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
@@ -45,6 +47,20 @@ abstract class KotlinAbstractUElement(private val givenParent: UElement?) : Kotl
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 KtLightClassForLocalDeclaration) {
val originParent = psi.kotlinOrigin.parent
parent = when (originParent) {
@@ -17,8 +17,10 @@
package org.jetbrains.uast.kotlin
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClassForLocalDeclaration
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.toLightGetter
import org.jetbrains.kotlin.asJava.toLightSetter
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
@@ -45,6 +47,20 @@ abstract class KotlinAbstractUElement(private val givenParent: UElement?) : Kotl
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 KtLightClassForLocalDeclaration) {
val originParent = psi.kotlinOrigin.parent
parent = when (originParent) {
@@ -22,6 +22,9 @@ import com.intellij.openapi.util.Key
import com.intellij.openapi.util.registry.Registry
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.light.LightMethodBuilder
import com.intellij.psi.impl.light.LightModifierList
import com.intellij.psi.impl.light.LightParameterListBuilder
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.asJava.LightClassUtil
@@ -513,8 +516,17 @@ internal object KotlinConverter {
}
} else {
el<UMethod> {
val lightMethod = LightClassUtil.getLightClassMethod(original) ?: return null
convertDeclaration(lightMethod, givenParent, expectedTypes)
val lightMethod = LightClassUtil.getLightClassMethod(original)
if (lightMethod != null)
convertDeclaration(lightMethod, givenParent, expectedTypes)
else {
val lightFake = LightMethodBuilder(
original.manager, original.language, original.name,
LightParameterListBuilder(original.manager, original.language),
LightModifierList(original.manager)
)
KotlinUMethod(lightFake, original, givenParent)
}
}
}
@@ -21,6 +21,9 @@ import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.impl.light.LightMethodBuilder
import com.intellij.psi.impl.light.LightModifierList
import com.intellij.psi.impl.light.LightParameterListBuilder
import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClass
@@ -478,8 +481,17 @@ internal object KotlinConverter {
}
else {
el<UMethod> {
val lightMethod = LightClassUtil.getLightClassMethod(original) ?: return null
convertDeclaration(lightMethod, givenParent, requiredType)
val lightMethod = LightClassUtil.getLightClassMethod(original)
if (lightMethod != null)
convertDeclaration(lightMethod, givenParent, expectedTypes)
else {
val lightFake = LightMethodBuilder(
original.manager, original.language, original.name,
LightParameterListBuilder(original.manager, original.language),
LightModifierList(original.manager)
)
KotlinUMethod(lightFake, original, givenParent)
}
}
}
@@ -19,12 +19,14 @@ 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.KotlinExceptionWithAttachments
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.uast.*
import org.jetbrains.uast.internal.acceptList
@@ -156,11 +158,29 @@ open class KotlinUClass private constructor(
fun isDelegatedMethod(psiMethod: PsiMethod) = psiMethod is KtLightMethod && psiMethod.isDelegated
return psi.methods.asSequence()
.filterNot(::isDelegatedMethod)
.map(::createUMethod)
.toList()
.toTypedArray()
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)
handledKtDeclarations.addIfNotNull(uMethod.sourcePsi)
}
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) { KotlinConverter.convertDeclaration(it, this, arrayOf(UElement::class.java)) as? UMethod }
return result.toTypedArray()
}
private fun PsiClass.isEnumEntryLightClass() = (this as? KtLightClass)?.kotlinOrigin is KtEnumEntry
@@ -50,7 +50,10 @@ open class KotlinUMethod(
private val kotlinOrigin = (psi.originalElement as? KtLightElement<*, *>)?.kotlinOrigin ?: sourcePsi
override fun getContainingFile(): PsiFile? = unwrapFakeFileForLightClass(psi.containingFile)
override fun getContainingFile(): PsiFile? {
kotlinOrigin?.containingFile?.let { return it }
return unwrapFakeFileForLightClass(psi.containingFile)
}
override fun getNameIdentifier() = UastLightIdentifier(psi, kotlinOrigin as KtNamedDeclaration?)
@@ -48,7 +48,10 @@ open class KotlinUMethod(
private val kotlinOrigin = (psi.originalElement as? KtLightElement<*, *>)?.kotlinOrigin ?: sourcePsi
override fun getContainingFile(): PsiFile? = unwrapFakeFileForLightClass(psi.containingFile)
override fun getContainingFile(): PsiFile? {
kotlinOrigin?.containingFile?.let { return it }
return unwrapFakeFileForLightClass(psi.containingFile)
}
override fun getNameIdentifier() = UastLightIdentifier(psi, kotlinOrigin as KtNamedDeclaration?)
@@ -48,7 +48,10 @@ open class KotlinUMethod(
private val kotlinOrigin = (psi.originalElement as? KtLightElement<*, *>)?.kotlinOrigin ?: sourcePsi
override fun getContainingFile(): PsiFile? = unwrapFakeFileForLightClass(psi.containingFile)
override fun getContainingFile(): PsiFile? {
kotlinOrigin?.containingFile?.let { return it }
return unwrapFakeFileForLightClass(psi.containingFile)
}
override fun getNameIdentifier() = UastLightIdentifier(psi, kotlinOrigin as KtNamedDeclaration?)
+10
View File
@@ -0,0 +1,10 @@
package test.pkg
class Context {
inline fun <reified T> ownSystemService1() = getSystemService(T::class.java)
inline fun ownSystemService2() = getSystemService(String::class.java)
}
inline fun <reified T> Context.systemService1() = getSystemService(T::class.java)
inline fun Context.systemService2() = getSystemService(String::class.java)
+42
View File
@@ -0,0 +1,42 @@
UFile (package = test.pkg)
UClass (name = ReifiedKt)
UMethod (name = systemService2)
UParameter (name = $this$systemService2)
UAnnotation (fqName = org.jetbrains.annotations.NotNull)
UBlockExpression
UReturnExpression
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
UIdentifier (Identifier (getSystemService))
USimpleNameReferenceExpression (identifier = <anonymous class>, resolvesTo = null)
UQualifiedReferenceExpression
UClassLiteralExpression
USimpleNameReferenceExpression (identifier = java)
UMethod (name = systemService1)
UBlockExpression
UReturnExpression
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
UIdentifier (Identifier (getSystemService))
USimpleNameReferenceExpression (identifier = <anonymous class>, resolvesTo = null)
UQualifiedReferenceExpression
UClassLiteralExpression
USimpleNameReferenceExpression (identifier = java)
UClass (name = Context)
UMethod (name = ownSystemService2)
UBlockExpression
UReturnExpression
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
UIdentifier (Identifier (getSystemService))
USimpleNameReferenceExpression (identifier = <anonymous class>, resolvesTo = null)
UQualifiedReferenceExpression
UClassLiteralExpression
USimpleNameReferenceExpression (identifier = java)
UMethod (name = Context)
UMethod (name = ownSystemService1)
UBlockExpression
UReturnExpression
UCallExpression (kind = UastCallKind(name='method_call'), argCount = 1))
UIdentifier (Identifier (getSystemService))
USimpleNameReferenceExpression (identifier = <anonymous class>, resolvesTo = null)
UQualifiedReferenceExpression
UClassLiteralExpression
USimpleNameReferenceExpression (identifier = java)
+20
View File
@@ -0,0 +1,20 @@
package test.pkg
public final class ReifiedKt {
public static final fun systemService2(@org.jetbrains.annotations.NotNull $this$systemService2: test.pkg.Context) : error.NonExistentClass {
return <anonymous class>(java.lang.String.java)
}
fun systemService1() {
return <anonymous class>(java.lang.Object.java)
}
}
public final class Context {
public final fun ownSystemService2() : error.NonExistentClass {
return <anonymous class>(java.lang.String.java)
}
public fun Context() = UastEmptyExpression
fun ownSystemService1() {
return <anonymous class>(java.lang.Object.java)
}
}
@@ -102,6 +102,9 @@ class SimpleKotlinRenderLogTest : AbstractKotlinRenderLogTest() {
@Test
fun testLambdaReturn() = doTest("LambdaReturn")
@Test
fun testReified() = doTest("Reified")
}
fun withForceUInjectionHostValue(call: () -> Unit) {
@@ -96,4 +96,7 @@ class SimpleKotlinRenderLogTest : AbstractKotlinRenderLogTest() {
@Test
fun testLambdaReturn() = doTest("LambdaReturn")
@Test
fun testReified() = doTest("Reified")
}