Uast: fixes and tests for descriptors leak (KT-27663)

This commit is contained in:
Nicolay Mitropolsky
2018-10-25 17:51:23 +03:00
committed by Nicolay Mitropolsky
parent e48fbe4aa3
commit bf42bd1226
7 changed files with 108 additions and 76 deletions
@@ -29,8 +29,6 @@ import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
@@ -226,14 +224,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
is KtAnnotationEntry -> el<UAnnotation>(build(::KotlinUAnnotation))
is KtCallExpression ->
if (requiredType != null && UAnnotation::class.java.isAssignableFrom(requiredType)) {
el<UAnnotation> {
val classDescriptor =
(original.getResolvedCall(original.analyze())?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass
if (classDescriptor?.kind == ClassKind.ANNOTATION_CLASS)
KotlinUNestedAnnotation(original, givenParent, classDescriptor)
else
null
}
el<UAnnotation> { KotlinUNestedAnnotation.tryCreate(original, givenParent) }
} else null
is KtLightAnnotationForSourceEntry -> convertElement(original.kotlinOrigin, givenParent, requiredType)
else -> null
@@ -29,8 +29,6 @@ import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.KotlinLanguage
@@ -226,14 +224,7 @@ class KotlinUastLanguagePlugin : UastLanguagePlugin {
is KtAnnotationEntry -> el<UAnnotation>(build(::KotlinUAnnotation))
is KtCallExpression ->
if (requiredType != null && UAnnotation::class.java.isAssignableFrom(requiredType)) {
el<UAnnotation> {
val classDescriptor =
(original.getResolvedCall(original.analyze())?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass
if (classDescriptor?.kind == ClassKind.ANNOTATION_CLASS)
KotlinUNestedAnnotation(original, givenParent, classDescriptor)
else
null
}
el<UAnnotation> { KotlinUNestedAnnotation.tryCreate(original, givenParent) }
} else null
is KtLightAnnotationForSourceEntry -> convertElement(original.kotlinOrigin, givenParent, requiredType)
else -> null
@@ -2,9 +2,11 @@ package org.jetbrains.uast.kotlin
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.asJava.toLightAnnotation
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.*
@@ -21,25 +23,26 @@ import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.declarations.KotlinUIdentifier
import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
abstract class KotlinUAnnotationBase(
final override val psi: KtElement,
abstract class KotlinUAnnotationBase<T : KtCallElement>(
final override val sourcePsi: T,
givenParent: UElement?
) : KotlinAbstractUElement(givenParent), UAnnotationEx, UAnchorOwner {
abstract override val javaPsi: PsiAnnotation?
final override val sourcePsi = psi
final override val psi: PsiElement = sourcePsi
protected abstract fun annotationUseSiteTarget(): AnnotationUseSiteTarget?
private val resolvedCall: ResolvedCall<*>? by lz { psi.getResolvedCall(psi.analyze()) }
private val resolvedCall: ResolvedCall<*>? get () = sourcePsi.getResolvedCall(sourcePsi.analyze())
override val qualifiedName: String?
get() = annotationClassDescriptor.takeUnless(ErrorUtils::isError)
override val qualifiedName: String? by lz {
computeClassDescriptor().takeUnless(ErrorUtils::isError)
?.fqNameUnsafe
?.takeIf(FqNameUnsafe::isSafe)
?.toSafe()
?.toString()
}
override val attributeValues: List<UNamedExpression> by lz {
resolvedCall?.valueArguments?.entries?.mapNotNull {
@@ -55,12 +58,9 @@ abstract class KotlinUAnnotationBase(
} ?: emptyList()
}
protected abstract val annotationClassDescriptor: ClassDescriptor?
protected abstract fun computeClassDescriptor(): ClassDescriptor?
override fun resolve(): PsiClass? {
val descriptor = annotationClassDescriptor ?: return null
return descriptor.toSource()?.getMaybeLightElement(this) as? PsiClass
}
override fun resolve(): PsiClass? = computeClassDescriptor()?.toSource()?.getMaybeLightElement(this) as? PsiClass
override fun findAttributeValue(name: String?): UExpression? =
findDeclaredAttributeValue(name) ?: findAttributeDefaultValue(name ?: "value")
@@ -82,7 +82,7 @@ abstract class KotlinUAnnotationBase(
}
private fun findAttributeDefaultValue(name: String): UExpression? {
val parameter = annotationClassDescriptor
val parameter = computeClassDescriptor()
?.unsubstitutedPrimaryConstructor
?.valueParameters
?.find { it.name.asString() == name } ?: return null
@@ -103,18 +103,16 @@ abstract class KotlinUAnnotationBase(
}
class KotlinUAnnotation(
val annotationEntry: KtAnnotationEntry,
annotationEntry: KtAnnotationEntry,
givenParent: UElement?
) : KotlinUAnnotationBase(annotationEntry, givenParent), UAnnotation {
) : KotlinUAnnotationBase<KtAnnotationEntry>(annotationEntry, givenParent), UAnnotation {
override val javaPsi = annotationEntry.toLightAnnotation()
private val resolvedAnnotation: AnnotationDescriptor? by lz { annotationEntry.analyze()[BindingContext.ANNOTATION, annotationEntry] }
override fun computeClassDescriptor(): ClassDescriptor? =
sourcePsi.analyze()[BindingContext.ANNOTATION, sourcePsi]?.annotationClass
override val annotationClassDescriptor: ClassDescriptor?
get() = resolvedAnnotation?.annotationClass
override fun annotationUseSiteTarget() = annotationEntry.useSiteTarget?.getAnnotationUseSiteTarget()
override fun annotationUseSiteTarget() = sourcePsi.useSiteTarget?.getAnnotationUseSiteTarget()
override val uastAnchor by lazy {
KotlinUIdentifier(
@@ -128,14 +126,13 @@ class KotlinUAnnotation(
}
class KotlinUNestedAnnotation(
private val original: KtCallExpression,
givenParent: UElement?,
private val classDescriptor: ClassDescriptor?
) : KotlinUAnnotationBase(original, givenParent) {
class KotlinUNestedAnnotation private constructor(
original: KtCallExpression,
givenParent: UElement?
) : KotlinUAnnotationBase<KtCallExpression>(original, givenParent) {
override val javaPsi: PsiAnnotation? by lazy { original.toLightAnnotation() }
override val annotationClassDescriptor: ClassDescriptor?
get() = classDescriptor
override fun computeClassDescriptor(): ClassDescriptor? = classDescriptor(sourcePsi)
override fun annotationUseSiteTarget(): AnnotationUseSiteTarget? = null
@@ -147,6 +144,18 @@ class KotlinUNestedAnnotation(
)
}
companion object {
fun tryCreate(original: KtCallExpression, givenParent: UElement?): KotlinUNestedAnnotation? {
if (classDescriptor(original)?.kind == ClassKind.ANNOTATION_CLASS)
return KotlinUNestedAnnotation(original, givenParent)
else
return null
}
private fun classDescriptor(original: KtCallExpression) =
(original.getResolvedCall(original.analyze())?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass
}
}
@@ -2,9 +2,11 @@ package org.jetbrains.uast.kotlin
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.asJava.toLightAnnotation
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.name.FqNameUnsafe
import org.jetbrains.kotlin.psi.*
@@ -20,25 +22,26 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
import org.jetbrains.uast.*
import org.jetbrains.uast.kotlin.declarations.KotlinUMethod
abstract class KotlinUAnnotationBase(
final override val psi: KtElement,
abstract class KotlinUAnnotationBase<T : KtCallElement>(
final override val sourcePsi: T,
givenParent: UElement?
) : KotlinAbstractUElement(givenParent), UAnnotation {
abstract override val javaPsi: PsiAnnotation?
final override val sourcePsi = psi
final override val psi: PsiElement = sourcePsi
protected abstract fun annotationUseSiteTarget(): AnnotationUseSiteTarget?
private val resolvedCall: ResolvedCall<*>? by lz { psi.getResolvedCall(psi.analyze()) }
private val resolvedCall: ResolvedCall<*>? get () = sourcePsi.getResolvedCall(sourcePsi.analyze())
override val qualifiedName: String?
get() = annotationClassDescriptor.takeUnless(ErrorUtils::isError)
override val qualifiedName: String? by lz {
computeClassDescriptor().takeUnless(ErrorUtils::isError)
?.fqNameUnsafe
?.takeIf(FqNameUnsafe::isSafe)
?.toSafe()
?.toString()
}
override val attributeValues: List<UNamedExpression> by lz {
resolvedCall?.valueArguments?.entries?.mapNotNull {
@@ -54,12 +57,9 @@ abstract class KotlinUAnnotationBase(
} ?: emptyList()
}
protected abstract val annotationClassDescriptor: ClassDescriptor?
protected abstract fun computeClassDescriptor(): ClassDescriptor?
override fun resolve(): PsiClass? {
val descriptor = annotationClassDescriptor ?: return null
return descriptor.toSource()?.getMaybeLightElement(this) as? PsiClass
}
override fun resolve(): PsiClass? = computeClassDescriptor()?.toSource()?.getMaybeLightElement(this) as? PsiClass
override fun findAttributeValue(name: String?): UExpression? =
findDeclaredAttributeValue(name) ?: findAttributeDefaultValue(name ?: "value")
@@ -81,7 +81,7 @@ abstract class KotlinUAnnotationBase(
}
private fun findAttributeDefaultValue(name: String): UExpression? {
val parameter = annotationClassDescriptor
val parameter = computeClassDescriptor()
?.unsubstitutedPrimaryConstructor
?.valueParameters
?.find { it.name.asString() == name } ?: return null
@@ -102,32 +102,41 @@ abstract class KotlinUAnnotationBase(
}
class KotlinUAnnotation(
val annotationEntry: KtAnnotationEntry,
annotationEntry: KtAnnotationEntry,
givenParent: UElement?
) : KotlinUAnnotationBase(annotationEntry, givenParent), UAnnotation {
) : KotlinUAnnotationBase<KtAnnotationEntry>(annotationEntry, givenParent), UAnnotation {
override val javaPsi = annotationEntry.toLightAnnotation()
private val resolvedAnnotation: AnnotationDescriptor? by lz { annotationEntry.analyze()[BindingContext.ANNOTATION, annotationEntry] }
override fun computeClassDescriptor(): ClassDescriptor? =
sourcePsi.analyze()[BindingContext.ANNOTATION, sourcePsi]?.annotationClass
override val annotationClassDescriptor: ClassDescriptor?
get() = resolvedAnnotation?.annotationClass
override fun annotationUseSiteTarget() = annotationEntry.useSiteTarget?.getAnnotationUseSiteTarget()
override fun annotationUseSiteTarget() = sourcePsi.useSiteTarget?.getAnnotationUseSiteTarget()
}
class KotlinUNestedAnnotation(
class KotlinUNestedAnnotation private constructor(
original: KtCallExpression,
givenParent: UElement?,
private val classDescriptor: ClassDescriptor?
) : KotlinUAnnotationBase(original, givenParent) {
givenParent: UElement?
) : KotlinUAnnotationBase<KtCallExpression>(original, givenParent) {
override val javaPsi: PsiAnnotation? by lazy { original.toLightAnnotation() }
override val annotationClassDescriptor: ClassDescriptor?
get() = classDescriptor
override fun computeClassDescriptor(): ClassDescriptor? = classDescriptor(sourcePsi)
override fun annotationUseSiteTarget(): AnnotationUseSiteTarget? = null
companion object {
fun tryCreate(original: KtCallExpression, givenParent: UElement?): KotlinUNestedAnnotation? {
if (classDescriptor(original)?.kind == ClassKind.ANNOTATION_CLASS)
return KotlinUNestedAnnotation(original, givenParent)
else
return null
}
private fun classDescriptor(original: KtCallExpression) =
(original.getResolvedCall(original.analyze())?.resultingDescriptor as? ClassConstructorDescriptor)?.constructedClass
}
}
@@ -43,9 +43,8 @@ class KotlinUFunctionCallExpression(
constructor(psi: KtCallElement, uastParent: UElement?) : this(psi, uastParent, null)
private val resolvedCall by lz {
_resolvedCall ?: psi.getResolvedCall(psi.analyze())
}
private val resolvedCall
get() = _resolvedCall ?: psi.getResolvedCall(psi.analyze())
override val receiverType by lz {
val resolvedCall = this.resolvedCall ?: return@lz null
@@ -1,9 +1,15 @@
package org.jetbrains.uast.test.kotlin
import com.intellij.openapi.util.Conditions
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiRecursiveElementVisitor
import com.intellij.psi.impl.source.tree.LeafPsiElement
import com.intellij.util.PairProcessor
import com.intellij.util.ref.DebugReflectionUtil
import junit.framework.TestCase
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.utils.addToStdlib.assertedCast
@@ -37,6 +43,7 @@ abstract class AbstractKotlinRenderLogTest : AbstractKotlinUastTest(), RenderLog
file.checkContainingFileForAllElements()
file.checkJvmDeclarationsImplementations()
file.checkDescriptorsLeak()
}
private fun checkParentConsistency(file: UFile) {
@@ -106,6 +113,15 @@ abstract class AbstractKotlinRenderLogTest : AbstractKotlinUastTest(), RenderLog
})
}
private fun UFile.checkDescriptorsLeak() {
accept(object : UastVisitor {
override fun visitElement(node: UElement): Boolean {
checkDescriptorsLeak(node)
return false
}
})
}
private fun UFile.checkJvmDeclarationsImplementations() {
accept(object : UastVisitor {
override fun visitElement(node: UElement): Boolean {
@@ -132,6 +148,22 @@ abstract class AbstractKotlinRenderLogTest : AbstractKotlinUastTest(), RenderLog
}
}
private val descriptorsClasses = listOf(AnnotationDescriptor::class, DeclarationDescriptor::class)
fun checkDescriptorsLeak(node: UElement) {
DebugReflectionUtil.walkObjects(
10,
mapOf(node to node.javaClass.name),
Any::class.java,
Conditions.alwaysTrue(),
PairProcessor { value, backLink ->
descriptorsClasses.find { it.isInstance(value) }?.let {
TestCase.fail("""Leaked descriptor ${it.qualifiedName} in ${node.javaClass.name}\n$backLink""")
false
} ?: true
})
}
private fun PsiFile.clearUastCaches() {
accept(object : PsiRecursiveElementVisitor() {
override fun visitElement(element: PsiElement) {
@@ -306,6 +306,7 @@ class KotlinUastApiTest : AbstractKotlinUastTest() {
.toUElementOfType<UAnnotation>()
?: throw AssertionError("haven't got annotation from $referenceExpression(${referenceExpression?.javaClass})")
checkDescriptorsLeak(convertedUAnnotation)
assertEquals("Annotation", convertedUAnnotation.qualifiedName)
val lightAnnotation = convertedUAnnotation.getAsJavaPsiElement(PsiAnnotation::class.java)
?: throw AssertionError("can't get lightAnnotation from $convertedUAnnotation")