From 142496c00f55731a14afb6bfb63815d9f56570bf Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Tue, 20 Dec 2016 18:02:04 +0300 Subject: [PATCH] Kapt: Parse generic signatures with inner class types correctly --- .../kapt3/stubs/SignatureParserVisitor.kt | 75 +++++++++++++------ ...ileToSourceStubConverterTestGenerated.java | 6 ++ .../innerClassesWithTypeParameters.kt | 16 ++++ .../innerClassesWithTypeParameters.txt | 57 ++++++++++++++ 4 files changed, 133 insertions(+), 21 deletions(-) create mode 100644 plugins/kapt3/testData/converter/innerClassesWithTypeParameters.kt create mode 100644 plugins/kapt3/testData/converter/innerClassesWithTypeParameters.txt diff --git a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/SignatureParserVisitor.kt b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/SignatureParserVisitor.kt index 912d1dbfc05..56abea20721 100644 --- a/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/SignatureParserVisitor.kt +++ b/plugins/kapt3/src/org/jetbrains/kotlin/kapt3/stubs/SignatureParserVisitor.kt @@ -74,6 +74,10 @@ import com.sun.tools.javac.util.List as JavacList ClassType < * * TypeArgument + * InnerClass + + InnerClass < ClassType + ! TypeArgument TypeArgument < ClassType + ClassType @@ -81,10 +85,10 @@ import com.sun.tools.javac.util.List as JavacList internal enum class ElementKind { Root, TypeParameter, ClassBound, InterfaceBound, SuperClass, Interface, TypeArgument, ParameterType, ReturnType, ExceptionType, - ClassType, TypeVariable, PrimitiveType, ArrayType + ClassType, InnerClass, TypeVariable, PrimitiveType, ArrayType } -private class SignatureNode(val kind: ElementKind, val name: String? = null) { +private class SignatureNode(val kind: ElementKind, var name: String? = null) { val children: MutableList = SmartList() } @@ -188,23 +192,20 @@ class SignatureParser(val treeMaker: KaptTreeMaker) { val kind = node.kind return when (kind) { ClassType -> { - val classFqName = node.name!!.replace('/', '.') - val args = node.children - val fqNameExpression = treeMaker.FqName(classFqName) - if (args.isEmpty()) return fqNameExpression + val typeArgs = mutableListOf() + val innerClasses = mutableListOf() + node.split(typeArgs, TypeArgument, innerClasses, InnerClass) - treeMaker.TypeApply(fqNameExpression, mapJList(args) { arg -> - assert(arg.kind == TypeArgument) { "Unexpected kind ${arg.kind}, $TypeArgument expected" } - val variance = arg.name ?: return@mapJList treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.UNBOUND), null) + var expression = makeExpressionForClassTypeWithArguments(treeMaker.FqName(node.name!!.replace('/', '.')), typeArgs) + if (innerClasses.isEmpty()) return expression - val argType = parseType(arg.children.single()) - when (variance.single()) { - '=' -> argType - '+' -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.EXTENDS), argType) - '-' -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.SUPER), argType) - else -> error("Unknown variance, '=', '+' or '-' expected") - } - }) + for (innerClass in innerClasses) { + expression = makeExpressionForClassTypeWithArguments( + treeMaker.Select(expression, treeMaker.name(innerClass.name!!)), + innerClass.children) + } + + expression } TypeVariable -> treeMaker.SimpleName(node.name!!) ArrayType -> treeMaker.TypeArray(parseType(node.children.single())) @@ -227,6 +228,23 @@ class SignatureParser(val treeMaker: KaptTreeMaker) { } } + private fun makeExpressionForClassTypeWithArguments(fqNameExpression: JCExpression, args: List): JCExpression { + if (args.isEmpty()) return fqNameExpression + + return treeMaker.TypeApply(fqNameExpression, mapJList(args) { arg -> + assert(arg.kind == TypeArgument) { "Unexpected kind ${arg.kind}, $TypeArgument expected" } + val variance = arg.name ?: return@mapJList treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.UNBOUND), null) + + val argType = parseType(arg.children.single()) + when (variance.single()) { + '=' -> argType + '+' -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.EXTENDS), argType) + '-' -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.SUPER), argType) + else -> error("Unknown variance, '=', '+' or '-' expected") + } + }) + } + private fun parse(signature: String): SignatureNode { val parser = SignatureParserVisitor() SignatureReader(signature).accept(parser) @@ -292,12 +310,21 @@ private class SignatureParserVisitor : SignatureVisitor(Opcodes.ASM5) { val root = SignatureNode(Root) private val stack = ArrayDeque(5).apply { add(root) } - private fun push(kind: ElementKind, parent: ElementKind? = null, name: String? = null) { - if (parent != null) { - while (stack.peek().kind != parent) { + private fun popUntil(kind: ElementKind?) { + if (kind != null) { + while (stack.peek().kind != kind) { stack.pop() } } + } + private fun popUntil(vararg kinds: ElementKind) { + while (stack.peek().kind !in kinds) { + stack.pop() + } + } + + private fun push(kind: ElementKind, parent: ElementKind? = null, name: String? = null) { + popUntil(parent) val newNode = SignatureNode(kind, name) stack.peek().children += newNode @@ -329,14 +356,20 @@ private class SignatureParserVisitor : SignatureVisitor(Opcodes.ASM5) { } override fun visitTypeArgument() { + popUntil(ClassType, InnerClass) push(TypeArgument, parent = ClassType) } override fun visitTypeArgument(variance: Char): SignatureVisitor { - push(TypeArgument, parent = ClassType, name = variance.toString()) + popUntil(ClassType, InnerClass) + push(TypeArgument, name = variance.toString()) return super.visitTypeArgument(variance) } + override fun visitInnerClassType(name: String) { + push(InnerClass, name = name, parent = ClassType) + } + override fun visitParameterType(): SignatureVisitor { push(ParameterType, parent = Root) return super.visitParameterType() diff --git a/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java index afe5bd1e6de..443dc1717ef 100644 --- a/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java +++ b/plugins/kapt3/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java @@ -102,6 +102,12 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi doTest(fileName); } + @TestMetadata("innerClassesWithTypeParameters.kt") + public void testInnerClassesWithTypeParameters() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/innerClassesWithTypeParameters.kt"); + doTest(fileName); + } + @TestMetadata("jvmOverloads.kt") public void testJvmOverloads() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("plugins/kapt3/testData/converter/jvmOverloads.kt"); diff --git a/plugins/kapt3/testData/converter/innerClassesWithTypeParameters.kt b/plugins/kapt3/testData/converter/innerClassesWithTypeParameters.kt new file mode 100644 index 00000000000..02aeabef710 --- /dev/null +++ b/plugins/kapt3/testData/converter/innerClassesWithTypeParameters.kt @@ -0,0 +1,16 @@ +class Test { + private var a = FilterValueDelegate() + private inner class FilterValueDelegate +} + +class Test2 { + inner class FilterValueDelegate { + private var a = Filter2() + inner class Filter2 + } +} + +class Test3 { + private var a = FilterValueDelegate() + private class FilterValueDelegate +} \ No newline at end of file diff --git a/plugins/kapt3/testData/converter/innerClassesWithTypeParameters.txt b/plugins/kapt3/testData/converter/innerClassesWithTypeParameters.txt new file mode 100644 index 00000000000..a0f62381b8a --- /dev/null +++ b/plugins/kapt3/testData/converter/innerClassesWithTypeParameters.txt @@ -0,0 +1,57 @@ +public final class Test { + private Test.FilterValueDelegate a; + + public Test() { + super(); + } + + private final class FilterValueDelegate { + + public FilterValueDelegate() { + super(); + } + } +} + +//////////////////// + + +public final class Test2 { + + public Test2() { + super(); + } + + public final class FilterValueDelegate { + private Test2.FilterValueDelegate.Filter2 a; + + public FilterValueDelegate() { + super(); + } + + public final class Filter2 { + + public Filter2() { + super(); + } + } + } +} + +//////////////////// + + +public final class Test3 { + private Test3.FilterValueDelegate a; + + public Test3() { + super(); + } + + private static final class FilterValueDelegate { + + public FilterValueDelegate() { + super(); + } + } +}