[ULC] Add annotations for PsiTypes in UltraLight classes

WIP on KT-41148, KT-41406, KT-41420
This commit is contained in:
Igor Yakovlev
2020-09-04 11:58:32 +03:00
parent b82d8cd4f4
commit e08763f3dd
13 changed files with 224 additions and 24 deletions

View File

@@ -145,7 +145,7 @@ internal open class KtUltraLightFieldImpl protected constructor(
val kotlinType = declaration.getKotlinType() ?: return@lazyPub PsiType.NULL
val descriptor = variableDescriptor ?: return@lazyPub PsiType.NULL
support.mapType(this) { typeMapper, sw ->
support.mapType(kotlinType, this) { typeMapper, sw ->
typeMapper.writeFieldSignature(kotlinType, descriptor, sw)
}
}

View File

@@ -243,7 +243,7 @@ internal class UltraLightMembersCreator(
ktDeclaration.resolve()?.getterIfProperty() as? CallableDescriptor
?: return PsiType.NULL
return support.mapType(wrapper) { typeMapper, signatureWriter ->
return support.mapType(desc.returnType, wrapper) { typeMapper, signatureWriter ->
typeMapper.mapReturnType(desc, signatureWriter)
}
}

View File

@@ -258,7 +258,7 @@ internal class KtUltraLightMethodForDescriptor(
delegate.isConstructor = true
PsiType.VOID
} else {
support.mapType(this) { typeMapper, signatureWriter ->
support.mapType(methodDescriptor.returnType, this) { typeMapper, signatureWriter ->
typeMapper.mapReturnType(methodDescriptor, signatureWriter)
}
}

View File

@@ -9,6 +9,8 @@ import com.intellij.navigation.ItemPresentation
import com.intellij.navigation.ItemPresentationProviders
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.impl.compiled.ClsTypeElementImpl
import com.intellij.psi.impl.source.PsiClassReferenceType
import com.intellij.psi.search.LocalSearchScope
import com.intellij.psi.search.SearchScope
import com.intellij.psi.util.TypeConversionUtil
@@ -108,7 +110,6 @@ internal abstract class KtUltraLightParameter(
override val psiTypeForNullabilityAnnotation: PsiType?
get() = type
protected fun computeParameterType(kotlinType: KotlinType?, containingDeclaration: CallableDescriptor?): PsiType {
kotlinType ?: return PsiType.NULL
@@ -116,9 +117,10 @@ internal abstract class KtUltraLightParameter(
return kotlinType.asPsiType(support, TypeMappingMode.DEFAULT, this)
} else {
val containingDescriptor = containingDeclaration ?: return PsiType.NULL
val mappedType = support.mapType(this) { typeMapper, sw ->
val mappedType = support.mapType(kotlinType, this) { typeMapper, sw ->
typeMapper.writeParameterType(sw, kotlinType, containingDescriptor)
}
return if (ultraLightMethod.checkNeedToErasureParametersTypes) TypeConversionUtil.erasure(mappedType) else mappedType
}
}

View File

@@ -14,8 +14,10 @@ import com.intellij.psi.impl.cache.ModifierFlags
import com.intellij.psi.impl.cache.TypeInfo
import com.intellij.psi.impl.compiled.ClsTypeElementImpl
import com.intellij.psi.impl.compiled.SignatureParsing
import com.intellij.psi.impl.compiled.StubBuildingVisitor
import com.intellij.psi.impl.light.*
import com.intellij.psi.impl.compiled.StubBuildingVisitor.GUESSING_MAPPER
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.util.TypeConversionUtil
import com.intellij.util.BitUtil.isSet
import com.intellij.util.IncorrectOperationException
@@ -23,9 +25,7 @@ import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.UltraLightClassModifierExtension
import org.jetbrains.kotlin.asJava.builder.LightMemberOriginForDeclaration
import org.jetbrains.kotlin.asJava.elements.KotlinLightTypeParameterListBuilder
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotationForSourceEntry
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.elements.*
import org.jetbrains.kotlin.asJava.elements.psiType
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.StandardNames
@@ -39,8 +39,8 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.psi.*
@@ -163,31 +163,95 @@ internal fun KotlinType.asPsiType(
support: KtUltraLightSupport,
mode: TypeMappingMode,
psiContext: PsiElement,
): PsiType = support.mapType(psiContext) { typeMapper, signatureWriter ->
): PsiType = support.mapType(this, psiContext) { typeMapper, signatureWriter ->
typeMapper.mapType(this, signatureWriter, mode)
}
internal fun KtUltraLightSupport.mapType(
// There is no other known way found to make PSI types annotated for now.
// It seems we need for platform changes to do it more convenient way (KTIJ-141).
private val setPsiTypeAnnotationProvider: (PsiType, TypeAnnotationProvider) -> Unit by lazyPub {
val klass = PsiType::class.java
val providerField = try {
klass.getDeclaredField("myAnnotationProvider")
.also { it.isAccessible = true }
} catch (e: NoSuchFieldException) {
if (ApplicationManager.getApplication().isInternal) throw e
null
} catch (e: SecurityException) {
if (ApplicationManager.getApplication().isInternal) throw e
null
}
{ psiType, provider ->
providerField?.set(psiType, provider)
}
}
private fun annotateByKotlinType(
psiType: PsiType,
kotlinType: KotlinType,
psiContext: PsiElement,
mapTypeToSignatureWriter: (KotlinTypeMapper, JvmSignatureWriter) -> Unit,
ultraLightSupport: KtUltraLightSupport
) {
fun KotlinType.getAnnotationsSequence(): Sequence<List<PsiAnnotation>> =
sequence {
yield(annotations.mapNotNull { it.toLightAnnotation(ultraLightSupport, psiContext) })
for (argument in arguments) {
yieldAll(argument.type.getAnnotationsSequence())
}
}
val annotationsIterator = kotlinType.getAnnotationsSequence().iterator()
fun recursiveAnnotator(psiType: PsiType) {
if (!annotationsIterator.hasNext()) return
val typeAnnotations = annotationsIterator.next()
if (psiType is PsiClassType) {
for (parameterType in psiType.parameters) {
recursiveAnnotator(parameterType)
}
} else if (psiType is PsiArrayType) {
recursiveAnnotator(psiType.componentType)
}
if (typeAnnotations.isEmpty()) return
val provider = TypeAnnotationProvider.Static.create(typeAnnotations.toTypedArray())
setPsiTypeAnnotationProvider(psiType, provider)
}
recursiveAnnotator(psiType)
}
internal fun KtUltraLightSupport.mapType(
kotlinType: KotlinType?,
psiContext: PsiElement,
mapTypeToSignatureWriter: (KotlinTypeMapper, JvmSignatureWriter) -> Unit
): PsiType {
val signatureWriter = BothSignatureWriter(BothSignatureWriter.Mode.SKIP_CHECKS)
mapTypeToSignatureWriter(typeMapper, signatureWriter)
val canonicalSignature = signatureWriter.toString()
return createTypeFromCanonicalText(canonicalSignature, psiContext)
return createTypeFromCanonicalText(kotlinType, canonicalSignature, psiContext)
}
fun createTypeFromCanonicalText(
private fun KtUltraLightSupport.createTypeFromCanonicalText(
kotlinType: KotlinType?,
canonicalSignature: String,
psiContext: PsiElement,
): PsiType {
val signature = StringCharacterIterator(canonicalSignature)
val javaType = SignatureParsing.parseTypeString(signature, StubBuildingVisitor.GUESSING_MAPPER)
val javaType = SignatureParsing.parseTypeString(signature, GUESSING_MAPPER)
val typeInfo = TypeInfo.fromString(javaType, false)
val typeText = TypeInfo.createTypeText(typeInfo) ?: return PsiType.NULL
val type = ClsTypeElementImpl(psiContext, typeText, '\u0000').type
val typeElement = ClsTypeElementImpl(psiContext, typeText, '\u0000')
val type = typeElement.type
if (kotlinType != null) {
annotateByKotlinType(type, kotlinType, typeElement, this)
}
if (type is PsiArrayType && psiContext is KtUltraLightParameter && psiContext.isVarArgs) {
return PsiEllipsisType(type.componentType, type.annotationProvider)
}

View File

@@ -1,7 +1,7 @@
public final class A /* A*/ {
public A();// .ctor()
public final void foo(@org.jetbrains.annotations.NotNull() kotlin.jvm.functions.Function4<? super RS,? super P,? super P,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object>);// foo(kotlin.jvm.functions.Function4<? super RS,? super P,? super P,? super kotlin.coroutines.Continuation<? super kotlin.Unit>,? extends java.lang.Object>)
public final void foo(@org.jetbrains.annotations.NotNull() kotlin.jvm.functions.Function4<? super RS, ? super P, ? super P, ? super kotlin.coroutines.Continuation<? super kotlin.Unit>, ? extends java.lang.Object>);// foo(kotlin.jvm.functions.Function4<? super RS, ? super P, ? super P, ? super kotlin.coroutines.Continuation<? super kotlin.Unit>, ? extends java.lang.Object>)
public final void foo(@org.jetbrains.annotations.Nullable() P, @org.jetbrains.annotations.Nullable() P);// foo(P, P)

View File

@@ -6,7 +6,7 @@ public abstract class A /* A*/<T extends A<T>> extends B<java.util.Collection<?
public class Inner /* A.Inner*/<D> extends B<java.util.Collection<? extends T>> implements C<D> {
public Inner();// .ctor()
}public final class Inner2 /* A.Inner2*/<X> extends A<T>.Inner<X> implements C<X> {
}public final class Inner2 /* A.Inner2*/<X> extends A.Inner<X> implements C<X> {
public Inner2();// .ctor()
}}

View File

@@ -1,4 +1,4 @@
public final class TypeHierarchyMap /* p1.TypeHierarchyMap*/<TValue> implements java.util.Map<java.lang.Class<?>,TValue>, kotlin.collections.Map<java.lang.Class<?>,TValue>, kotlin.jvm.internal.markers.KMappedMarker {
public final class TypeHierarchyMap /* p1.TypeHierarchyMap*/<TValue> implements java.util.Map<java.lang.Class<?>, TValue>, kotlin.collections.Map<java.lang.Class<?>, TValue>, kotlin.jvm.internal.markers.KMappedMarker {
public TypeHierarchyMap();// .ctor()
public boolean containsKey(@org.jetbrains.annotations.NotNull() java.lang.Class<?>);// containsKey(java.lang.Class<?>)

View File

@@ -0,0 +1,62 @@
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PARAMETER})
@kotlin.annotation.Target(allowedTargets = {kotlin.annotation.AnnotationTarget.VALUE_PARAMETER, kotlin.annotation.AnnotationTarget.TYPE})
public abstract @interface A0 /* A0*/ {
}
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PARAMETER})
@kotlin.annotation.Target(allowedTargets = {kotlin.annotation.AnnotationTarget.VALUE_PARAMETER, kotlin.annotation.AnnotationTarget.TYPE})
public abstract @interface A1 /* A1*/ {
}
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PARAMETER})
@kotlin.annotation.Target(allowedTargets = {kotlin.annotation.AnnotationTarget.VALUE_PARAMETER, kotlin.annotation.AnnotationTarget.TYPE})
public abstract @interface A2 /* A2*/ {
}
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PARAMETER})
@kotlin.annotation.Target(allowedTargets = {kotlin.annotation.AnnotationTarget.VALUE_PARAMETER, kotlin.annotation.AnnotationTarget.TYPE})
public abstract @interface A3 /* A3*/ {
}
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PARAMETER})
@kotlin.annotation.Target(allowedTargets = {kotlin.annotation.AnnotationTarget.VALUE_PARAMETER, kotlin.annotation.AnnotationTarget.TYPE})
public abstract @interface A4 /* A4*/ {
}
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PARAMETER})
@kotlin.annotation.Target(allowedTargets = {kotlin.annotation.AnnotationTarget.VALUE_PARAMETER, kotlin.annotation.AnnotationTarget.TYPE})
public abstract @interface A5 /* A5*/ {
}
@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(value = {java.lang.annotation.ElementType.PARAMETER})
@kotlin.annotation.Target(allowedTargets = {kotlin.annotation.AnnotationTarget.VALUE_PARAMETER, kotlin.annotation.AnnotationTarget.TYPE})
public abstract @interface A6 /* A6*/ {
}
public abstract interface P /* P*/<T, K> {
}
public final class X /* X*/ {
public X();// .ctor()
}
public final class Y /* Y*/ {
public Y();// .ctor()
}
public final class klass /* klass*/ {
@org.jetbrains.annotations.NotNull()
public final @A6() X annotatedMethod(@org.jetbrains.annotations.NotNull() @A0() P<@A1() X, P<@A2() @A3() X, @A4() Y>>, @org.jetbrains.annotations.NotNull() @A5() Y[]);// annotatedMethod(@A0() P<@A1() X, P<@A2() @A3() X, @A4() Y>>, @A5() Y[])
public klass();// .ctor()
}

View File

@@ -0,0 +1,25 @@
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
annotation class A0
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
annotation class A1
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
annotation class A2
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
annotation class A3
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
annotation class A4
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
annotation class A5
@Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
annotation class A6
interface P<T, K>
class X
class Y
class klass {
fun annotatedMethod(x: @A0 P<@A1 X, P<@A2 @A3 X, @A4 Y>>, y: Array<@A5 Y>): @A6 X {
return ""
}
}

View File

@@ -14,8 +14,43 @@ import org.jetbrains.kotlin.load.kotlin.NON_EXISTENT_CLASS_NAME
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
object PsiClassRenderer {
private fun PsiType.renderType() = getCanonicalText(true)
var extendedTypeRenderer = false
private fun PsiType.renderType() = StringBuffer().also { renderType(it) }.toString()
private fun PsiType.renderType(sb: StringBuffer) {
if (extendedTypeRenderer && annotations.isNotEmpty()) {
sb.append(annotations.joinToString(" ", postfix = " ") { it.renderAnnotation() })
}
when (this) {
is PsiClassType -> {
sb.append(PsiNameHelper.getQualifiedClassName(canonicalText, false))
if (parameterCount > 0) {
sb.append("<")
parameters.forEachIndexed { index, type ->
type.renderType(sb)
if (index < parameterCount - 1) sb.append(", ")
}
sb.append(">")
}
}
is PsiEllipsisType -> {
componentType.renderType(sb)
sb.append("...")
}
is PsiArrayType -> {
componentType.renderType(sb)
sb.append("[]")
}
else -> {
sb.append(canonicalText)
}
}
}
private fun PsiReferenceList?.renderRefList(keyword: String, sortReferences: Boolean = true): String {
if (this == null) return ""

View File

@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.asJava.classes
import com.intellij.testFramework.LightProjectDescriptor
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
import org.jetbrains.kotlin.asJava.PsiClassRenderer
import org.jetbrains.kotlin.asJava.PsiClassRenderer.renderClass
import org.jetbrains.kotlin.idea.perf.UltraLightChecker
import org.jetbrains.kotlin.idea.perf.UltraLightChecker.checkDescriptorsLeak
@@ -32,7 +33,13 @@ abstract class AbstractUltraLightClassLoadingTest : KotlinLightCodeInsightFixtur
LightClassGenerationSupport.getInstance(ktClass.project).createUltraLightClass(ktClass)?.let { it to ktClass }
}.joinToString("\n\n") { (ultraLightClass, ktClass) ->
with(UltraLightChecker) {
ultraLightClass.renderClass().also {
val extendedTypeRendererOld = PsiClassRenderer.extendedTypeRenderer
try {
PsiClassRenderer.extendedTypeRenderer = file.name == "typeAnnotations.kt"
ultraLightClass.renderClass()
} finally {
PsiClassRenderer.extendedTypeRenderer = extendedTypeRendererOld
}.also {
checkDescriptorsLeak(ultraLightClass)
}
}

View File

@@ -183,6 +183,11 @@ public class UltraLightClassLoadingTestGenerated extends AbstractUltraLightClass
runTest("compiler/testData/asJava/ultraLightClasses/typeAliases.kt");
}
@TestMetadata("typeAnnotations.kt")
public void testTypeAnnotations() throws Exception {
runTest("compiler/testData/asJava/ultraLightClasses/typeAnnotations.kt");
}
@TestMetadata("wildcardOptimization.kt")
public void testWildcardOptimization() throws Exception {
runTest("compiler/testData/asJava/ultraLightClasses/wildcardOptimization.kt");