Kapt: Parse generic signatures with inner class types correctly
This commit is contained in:
committed by
Yan Zhulanow
parent
5f3c1dfc41
commit
142496c00f
@@ -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<SignatureNode> = SmartList<SignatureNode>()
|
||||
}
|
||||
|
||||
@@ -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<SignatureNode>()
|
||||
val innerClasses = mutableListOf<SignatureNode>()
|
||||
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<SignatureNode>): 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<SignatureNode>(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()
|
||||
|
||||
+6
@@ -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");
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
class Test {
|
||||
private var a = FilterValueDelegate<Float>()
|
||||
private inner class FilterValueDelegate<T>
|
||||
}
|
||||
|
||||
class Test2 {
|
||||
inner class FilterValueDelegate<T> {
|
||||
private var a = Filter2<String>()
|
||||
inner class Filter2<X>
|
||||
}
|
||||
}
|
||||
|
||||
class Test3 {
|
||||
private var a = FilterValueDelegate<Float>()
|
||||
private class FilterValueDelegate<T>
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
public final class Test {
|
||||
private Test.FilterValueDelegate<java.lang.Float> a;
|
||||
|
||||
public Test() {
|
||||
super();
|
||||
}
|
||||
|
||||
private final class FilterValueDelegate<T extends java.lang.Object> {
|
||||
|
||||
public FilterValueDelegate() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public final class Test2 {
|
||||
|
||||
public Test2() {
|
||||
super();
|
||||
}
|
||||
|
||||
public final class FilterValueDelegate<T extends java.lang.Object> {
|
||||
private Test2.FilterValueDelegate<T>.Filter2<java.lang.String> a;
|
||||
|
||||
public FilterValueDelegate() {
|
||||
super();
|
||||
}
|
||||
|
||||
public final class Filter2<X extends java.lang.Object> {
|
||||
|
||||
public Filter2() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
|
||||
|
||||
public final class Test3 {
|
||||
private Test3.FilterValueDelegate<java.lang.Float> a;
|
||||
|
||||
public Test3() {
|
||||
super();
|
||||
}
|
||||
|
||||
private static final class FilterValueDelegate<T extends java.lang.Object> {
|
||||
|
||||
public FilterValueDelegate() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user