Render decompiled annotations on enum entries

This commit is contained in:
Alexander Udalov
2015-12-10 23:30:36 +03:00
parent 6f347f351a
commit 65c5c99c68
13 changed files with 133 additions and 29 deletions
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.psi.stubs.KotlinFileStub;
import java.io.IOException; import java.io.IOException;
public class KtFileElementType extends IStubFileElementType<KotlinFileStub> { public class KtFileElementType extends IStubFileElementType<KotlinFileStub> {
public static final int STUB_VERSION = 64; public static final int STUB_VERSION = 65;
private static final String NAME = "kotlin.FILE"; private static final String NAME = "kotlin.FILE";
@@ -52,10 +52,8 @@ public class AnnotationLoaderForStubBuilderImpl(
} }
} }
override fun loadEnumEntryAnnotations(container: ProtoContainer, proto: ProtoBuf.EnumEntry): List<ClassId> { override fun loadEnumEntryAnnotations(container: ProtoContainer, proto: ProtoBuf.EnumEntry): List<ClassId> =
// TODO proto.getExtension(protocol.enumEntryAnnotation).orEmpty().map { container.nameResolver.getClassId(it.id) }
return listOf()
}
override fun loadValueParameterAnnotations( override fun loadValueParameterAnnotations(
container: ProtoContainer, container: ProtoContainer,
@@ -64,9 +62,7 @@ public class AnnotationLoaderForStubBuilderImpl(
parameterIndex: Int, parameterIndex: Int,
proto: ProtoBuf.ValueParameter proto: ProtoBuf.ValueParameter
): List<ClassId> = ): List<ClassId> =
proto.getExtension(protocol.parameterAnnotation).orEmpty().map { proto.getExtension(protocol.parameterAnnotation).orEmpty().map { container.nameResolver.getClassId(it.id) }
container.nameResolver.getClassId(it.id)
}
override fun loadExtensionReceiverParameterAnnotations( override fun loadExtensionReceiverParameterAnnotations(
container: ProtoContainer, container: ProtoContainer,
@@ -172,9 +172,22 @@ private class ClassClsStubBuilder(
} }
private fun createEnumEntryStubs(classBody: KotlinPlaceHolderStubImpl<KtClassBody>) { private fun createEnumEntryStubs(classBody: KotlinPlaceHolderStubImpl<KtClassBody>) {
classProto.getEnumEntryNameList().forEach { id -> if (classKind != ProtoBuf.Class.Kind.ENUM_CLASS) return
val name = c.nameResolver.getName(id)
KotlinClassStubImpl( val container = ProtoContainer(classProto, null, c.nameResolver, c.typeTable)
val enumEntries: List<Pair<Int, List<ClassId>>> =
if (classProto.enumEntryList.isNotEmpty())
classProto.enumEntryList.map { enumEntryProto ->
enumEntryProto.name to c.components.annotationLoader.loadEnumEntryAnnotations(container, enumEntryProto)
}
else classProto.enumEntryNameList.map { enumEntryName ->
enumEntryName to listOf<ClassId>()
}
enumEntries.forEach { entry ->
val name = c.nameResolver.getName(entry.first)
val annotations = entry.second
val enumEntryStub = KotlinClassStubImpl(
KtStubElementTypes.ENUM_ENTRY, KtStubElementTypes.ENUM_ENTRY,
classBody, classBody,
qualifiedName = c.containerFqName.child(name).ref(), qualifiedName = c.containerFqName.child(name).ref(),
@@ -185,6 +198,9 @@ private class ClassClsStubBuilder(
isLocal = false, isLocal = false,
isTopLevel = false isTopLevel = false
) )
if (annotations.isNotEmpty()) {
createAnnotationStubs(annotations, createEmptyModifierListStub(enumEntryStub))
}
} }
} }
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.decompiler.stubBuilder
import com.google.protobuf.MessageLite import com.google.protobuf.MessageLite
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.stubs.StubElement import com.intellij.psi.stubs.StubElement
import com.intellij.util.SmartList
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
@@ -175,21 +174,13 @@ class TypeClsStubBuilder(private val c: ClsStubBuilderContext) {
container, callableProto, callableProto.annotatedCallableKind, index, valueParameterProto container, callableProto, callableProto.annotatedCallableKind, index, valueParameterProto
) )
if (parameterAnnotations.isNotEmpty()) { if (parameterAnnotations.isNotEmpty()) {
createAnnotationStubs(parameterAnnotations, modifierList ?: createEmptyModifierList(parameterStub)) createAnnotationStubs(parameterAnnotations, modifierList ?: createEmptyModifierListStub(parameterStub))
} }
createTypeReferenceStub(parameterStub, typeProto) createTypeReferenceStub(parameterStub, typeProto)
} }
} }
private fun createEmptyModifierList(parameterStub: KotlinParameterStubImpl): KotlinModifierListStubImpl {
return KotlinModifierListStubImpl(
parameterStub,
ModifierMaskUtils.computeMask { false },
KtStubElementTypes.MODIFIER_LIST
)
}
fun createTypeParameterListStub( fun createTypeParameterListStub(
parent: StubElement<out PsiElement>, parent: StubElement<out PsiElement>,
typeParameterProtoList: List<ProtoBuf.TypeParameter> typeParameterProtoList: List<ProtoBuf.TypeParameter>
@@ -262,6 +262,14 @@ fun createModifierListStub(
) )
} }
fun createEmptyModifierListStub(parent: KotlinStubBaseImpl<*>): KotlinModifierListStubImpl {
return KotlinModifierListStubImpl(
parent,
ModifierMaskUtils.computeMask { false },
KtStubElementTypes.MODIFIER_LIST
)
}
fun createAnnotationStubs(annotationIds: List<ClassId>, parent: KotlinStubBaseImpl<*>) { fun createAnnotationStubs(annotationIds: List<ClassId>, parent: KotlinStubBaseImpl<*>) {
return createTargetedAnnotationStubs(annotationIds.map { ClassIdWithTarget(it, null) }, parent) return createTargetedAnnotationStubs(annotationIds.map { ClassIdWithTarget(it, null) }, parent)
} }
@@ -77,13 +77,19 @@ public fun buildDecompiledText(
if (descriptor is MissingDependencyErrorClass) { if (descriptor is MissingDependencyErrorClass) {
throw IllegalStateException("${descriptor.javaClass.getSimpleName()} cannot be rendered. FqName: ${descriptor.fullFqName}") throw IllegalStateException("${descriptor.javaClass.getSimpleName()} cannot be rendered. FqName: ${descriptor.fullFqName}")
} }
val startOffset = builder.length() val startOffset = builder.length
val header = if (isEnumEntry(descriptor)) if (isEnumEntry(descriptor)) {
descriptor.name.asString() + if (lastEnumEntry!!) ";" else "," for (annotation in descriptor.annotations) {
else builder.append(descriptorRenderer.renderAnnotation(annotation))
descriptorRenderer.render(descriptor).replace("= ...", DECOMPILED_COMMENT_FOR_PARAMETER) builder.append(" ")
builder.append(header) }
var endOffset = builder.length() builder.append(descriptor.name.asString())
builder.append(if (lastEnumEntry!!) ";" else ",")
}
else {
builder.append(descriptorRenderer.render(descriptor).replace("= ...", DECOMPILED_COMMENT_FOR_PARAMETER))
}
var endOffset = builder.length
if (descriptor is CallableDescriptor) { if (descriptor is CallableDescriptor) {
//NOTE: assuming that only return types can be flexible //NOTE: assuming that only return types can be flexible
@@ -0,0 +1,12 @@
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package test
public final enum class AnnotatedEnumEntry private constructor() : kotlin.Enum<test.AnnotatedEnumEntry> {
Entry1,
@dependency.A @dependency.B Entry2,
@dependency.A Entry3;
}
@@ -0,0 +1,9 @@
package test
import dependency.*
enum class AnnotatedEnumEntry {
Entry1,
@A("2") @B(2) Entry2,
@A("3") Entry3
}
@@ -0,0 +1,7 @@
package dependency
@Target(AnnotationTarget.FIELD)
annotation class A(val s: String)
@Target(AnnotationTarget.FIELD)
annotation class B(val i: Int)
@@ -22,6 +22,8 @@
class Nested @a private @b(E.E1) @b(E.E2) constructor() class Nested @a private @b(E.E1) @b(E.E2) constructor()
enum class En { Entry1, @a @b(E.E2) Entry2, @a @c Entry3 }
fun types(param: @a @b(E.E1) LongRange): @a @b(E.E2) Unit {} fun types(param: @a @b(E.E1) LongRange): @a @b(E.E2) Unit {}
} }
@@ -36,4 +38,9 @@ annotation class a
@Repeatable @Repeatable
annotation class b(val e: E) annotation class b(val e: E)
enum class E { E1, E2 } @Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.FUNCTION,
AnnotationTarget.CONSTRUCTOR, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.TYPE, AnnotationTarget.CLASS)
annotation class c
enum class E { E1, E2 }
@@ -172,6 +172,46 @@ PsiJetFileStubImpl[package=]
USER_TYPE:[isAbsoluteInRootPackage=false] USER_TYPE:[isAbsoluteInRootPackage=false]
REFERENCE_EXPRESSION:[referencedName=kotlin] REFERENCE_EXPRESSION:[referencedName=kotlin]
REFERENCE_EXPRESSION:[referencedName=Unit] REFERENCE_EXPRESSION:[referencedName=Unit]
CLASS:[fqName=Annotations.En, isEnumEntry=false, isInterface=false, isLocal=false, isTopLevel=false, name=En, superNames=[Enum]]
MODIFIER_LIST:[enum public final]
PRIMARY_CONSTRUCTOR:
MODIFIER_LIST:[private]
VALUE_PARAMETER_LIST:
SUPER_TYPE_LIST:
SUPER_TYPE_ENTRY:
TYPE_REFERENCE:
USER_TYPE:[isAbsoluteInRootPackage=false]
USER_TYPE:[isAbsoluteInRootPackage=false]
REFERENCE_EXPRESSION:[referencedName=kotlin]
REFERENCE_EXPRESSION:[referencedName=Enum]
TYPE_ARGUMENT_LIST:
TYPE_PROJECTION:[projectionKind=NONE]
TYPE_REFERENCE:
USER_TYPE:[isAbsoluteInRootPackage=false]
USER_TYPE:[isAbsoluteInRootPackage=false]
REFERENCE_EXPRESSION:[referencedName=Annotations]
REFERENCE_EXPRESSION:[referencedName=En]
CLASS_BODY:
ENUM_ENTRY:[fqName=Annotations.En.Entry1, isEnumEntry=true, isInterface=false, isLocal=false, isTopLevel=false, name=Entry1, superNames=[]]
ENUM_ENTRY:[fqName=Annotations.En.Entry2, isEnumEntry=true, isInterface=false, isLocal=false, isTopLevel=false, name=Entry2, superNames=[]]
MODIFIER_LIST:[]
ANNOTATION_ENTRY:[hasValueArguments=false, shortName=a]
CONSTRUCTOR_CALLEE:
TYPE_REFERENCE:
USER_TYPE:[isAbsoluteInRootPackage=false]
REFERENCE_EXPRESSION:[referencedName=a]
ENUM_ENTRY:[fqName=Annotations.En.Entry3, isEnumEntry=true, isInterface=false, isLocal=false, isTopLevel=false, name=Entry3, superNames=[]]
MODIFIER_LIST:[]
ANNOTATION_ENTRY:[hasValueArguments=false, shortName=a]
CONSTRUCTOR_CALLEE:
TYPE_REFERENCE:
USER_TYPE:[isAbsoluteInRootPackage=false]
REFERENCE_EXPRESSION:[referencedName=a]
ANNOTATION_ENTRY:[hasValueArguments=false, shortName=c]
CONSTRUCTOR_CALLEE:
TYPE_REFERENCE:
USER_TYPE:[isAbsoluteInRootPackage=false]
REFERENCE_EXPRESSION:[referencedName=c]
CLASS:[fqName=Annotations.Nested, isEnumEntry=false, isInterface=false, isLocal=false, isTopLevel=false, name=Nested, superNames=[]] CLASS:[fqName=Annotations.Nested, isEnumEntry=false, isInterface=false, isLocal=false, isTopLevel=false, name=Nested, superNames=[]]
MODIFIER_LIST:[public final] MODIFIER_LIST:[public final]
PRIMARY_CONSTRUCTOR: PRIMARY_CONSTRUCTOR:
@@ -65,6 +65,12 @@ public class CommonDecompiledTextFromJsMetadataTestGenerated extends AbstractCom
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/decompiler/decompiledText"), Pattern.compile("^([^\\.]+)$"), true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/decompiler/decompiledText"), Pattern.compile("^([^\\.]+)$"), true);
} }
@TestMetadata("AnnotatedEnumEntry")
public void testAnnotatedEnumEntry() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/decompiledText/AnnotatedEnumEntry/");
doTest(fileName);
}
@TestMetadata("Annotations") @TestMetadata("Annotations")
public void testAnnotations() throws Exception { public void testAnnotations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/decompiledText/Annotations/"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/decompiledText/Annotations/");
@@ -35,6 +35,12 @@ public class CommonDecompiledTextTestGenerated extends AbstractCommonDecompiledT
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/decompiler/decompiledText"), Pattern.compile("^([^\\.]+)$"), true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/decompiler/decompiledText"), Pattern.compile("^([^\\.]+)$"), true);
} }
@TestMetadata("AnnotatedEnumEntry")
public void testAnnotatedEnumEntry() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/decompiledText/AnnotatedEnumEntry/");
doTest(fileName);
}
@TestMetadata("Annotations") @TestMetadata("Annotations")
public void testAnnotations() throws Exception { public void testAnnotations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/decompiledText/Annotations/"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/decompiler/decompiledText/Annotations/");