diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt index e9a02496a74..ba1479ff727 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightField.kt @@ -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) } } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMembersCreator.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMembersCreator.kt index ff8d8b4e24f..8952b5394ac 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMembersCreator.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMembersCreator.kt @@ -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) } } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethods.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethods.kt index fc38a83c5d2..30f4e3b54d4 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethods.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightMethods.kt @@ -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) } } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightParameters.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightParameters.kt index eacbfa87b3c..5ee20edd346 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightParameters.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightParameters.kt @@ -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 } } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt index a6c32653a13..e6b0e8fc134 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/classes/ultraLightUtils.kt @@ -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> = + 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) } diff --git a/compiler/testData/asJava/lightClasses/StubOrderForOverloads.java b/compiler/testData/asJava/lightClasses/StubOrderForOverloads.java index 02951cc22a7..5bfd4a61f6b 100644 --- a/compiler/testData/asJava/lightClasses/StubOrderForOverloads.java +++ b/compiler/testData/asJava/lightClasses/StubOrderForOverloads.java @@ -1,7 +1,7 @@ public final class A /* A*/ { public A();// .ctor() - public final void foo(@org.jetbrains.annotations.NotNull() kotlin.jvm.functions.Function4,? extends java.lang.Object>);// foo(kotlin.jvm.functions.Function4,? extends java.lang.Object>) + public final void foo(@org.jetbrains.annotations.NotNull() kotlin.jvm.functions.Function4, ? extends java.lang.Object>);// foo(kotlin.jvm.functions.Function4, ? extends java.lang.Object>) public final void foo(@org.jetbrains.annotations.Nullable() P, @org.jetbrains.annotations.Nullable() P);// foo(P, P) diff --git a/compiler/testData/asJava/lightClasses/TypePararametersInClass.java b/compiler/testData/asJava/lightClasses/TypePararametersInClass.java index 1abf72c55d0..937092c9a85 100644 --- a/compiler/testData/asJava/lightClasses/TypePararametersInClass.java +++ b/compiler/testData/asJava/lightClasses/TypePararametersInClass.java @@ -6,7 +6,7 @@ public abstract class A /* A*/> extends B extends B> implements C { public Inner();// .ctor() -}public final class Inner2 /* A.Inner2*/ extends A.Inner implements C { +}public final class Inner2 /* A.Inner2*/ extends A.Inner implements C { public Inner2();// .ctor() }} \ No newline at end of file diff --git a/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.java b/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.java index 348538fba5d..9eb86337fbf 100644 --- a/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.java +++ b/compiler/testData/asJava/lightClasses/ideRegression/ImplementingMap.java @@ -1,4 +1,4 @@ -public final class TypeHierarchyMap /* p1.TypeHierarchyMap*/ implements java.util.Map,TValue>, kotlin.collections.Map,TValue>, kotlin.jvm.internal.markers.KMappedMarker { +public final class TypeHierarchyMap /* p1.TypeHierarchyMap*/ implements java.util.Map, TValue>, kotlin.collections.Map, TValue>, kotlin.jvm.internal.markers.KMappedMarker { public TypeHierarchyMap();// .ctor() public boolean containsKey(@org.jetbrains.annotations.NotNull() java.lang.Class);// containsKey(java.lang.Class) diff --git a/compiler/testData/asJava/ultraLightClasses/typeAnnotations.java b/compiler/testData/asJava/ultraLightClasses/typeAnnotations.java new file mode 100644 index 00000000000..d10d935f4f7 --- /dev/null +++ b/compiler/testData/asJava/ultraLightClasses/typeAnnotations.java @@ -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*/ { +} + +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() + +} \ No newline at end of file diff --git a/compiler/testData/asJava/ultraLightClasses/typeAnnotations.kt b/compiler/testData/asJava/ultraLightClasses/typeAnnotations.kt new file mode 100644 index 00000000000..412b1832292 --- /dev/null +++ b/compiler/testData/asJava/ultraLightClasses/typeAnnotations.kt @@ -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 + +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 "" + } +} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/asJava/PsiClassRenderer.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/asJava/PsiClassRenderer.kt index dc053132870..4c24f54b13f 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/asJava/PsiClassRenderer.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/asJava/PsiClassRenderer.kt @@ -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 "" diff --git a/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightClassLoadingTest.kt b/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightClassLoadingTest.kt index c4277a4a995..4501f19162a 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightClassLoadingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/asJava/classes/AbstractUltraLightClassLoadingTest.kt @@ -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) } } diff --git a/idea/tests/org/jetbrains/kotlin/asJava/classes/UltraLightClassLoadingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/asJava/classes/UltraLightClassLoadingTestGenerated.java index eca278af474..c5ada24b6db 100644 --- a/idea/tests/org/jetbrains/kotlin/asJava/classes/UltraLightClassLoadingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/asJava/classes/UltraLightClassLoadingTestGenerated.java @@ -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");