mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
[ULC] Add annotations for PsiTypes in UltraLight classes
WIP on KT-41148, KT-41406, KT-41420
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
}}
|
||||
@@ -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<?>)
|
||||
|
||||
62
compiler/testData/asJava/ultraLightClasses/typeAnnotations.java
vendored
Normal file
62
compiler/testData/asJava/ultraLightClasses/typeAnnotations.java
vendored
Normal 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()
|
||||
|
||||
}
|
||||
25
compiler/testData/asJava/ultraLightClasses/typeAnnotations.kt
vendored
Normal file
25
compiler/testData/asJava/ultraLightClasses/typeAnnotations.kt
vendored
Normal 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 ""
|
||||
}
|
||||
}
|
||||
@@ -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 ""
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user