From c517b85de1ec42c57fc30b6e9b4652b6e508c86c Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Tue, 4 Sep 2018 22:30:45 +0300 Subject: [PATCH] Kapt: Support also erroneous super interface names --- .../stubs/ClassFileToSourceStubConverter.kt | 96 ++++++- ...ileToSourceStubConverterTestGenerated.java | 10 + .../kapt3/test/KotlinKapt3IntegrationTests.kt | 14 - .../converter/errorLocationMapping.kt | 18 +- .../converter/errorLocationMapping.txt | 4 +- .../testData/converter/errorSuperclass.kt | 7 + .../testData/converter/errorSuperclass.txt | 38 +++ .../errorSuperclassCorrectErrorTypes.kt | 45 ++++ .../errorSuperclassCorrectErrorTypes.txt | 253 ++++++++++++++++++ .../testData/kotlinRunner/Superclass.it.txt | 30 --- .../testData/kotlinRunner/Superclass.kt | 7 - 11 files changed, 449 insertions(+), 73 deletions(-) create mode 100644 plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclass.kt create mode 100644 plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclass.txt create mode 100644 plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclassCorrectErrorTypes.kt create mode 100644 plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclassCorrectErrorTypes.txt delete mode 100644 plugins/kapt3/kapt3-compiler/testData/kotlinRunner/Superclass.it.txt delete mode 100644 plugins/kapt3/kapt3-compiler/testData/kotlinRunner/Superclass.kt diff --git a/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt b/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt index d3fb0d4ec1c..a137161d268 100644 --- a/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt +++ b/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/stubs/ClassFileToSourceStubConverter.kt @@ -52,7 +52,9 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.resolve.source.PsiSourceElement +import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.isError import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.tree.* @@ -322,7 +324,6 @@ class ClassFileToSourceStubConverter( val superClass = treeMaker.FqName(treeMaker.getQualifiedName(clazz.superName)) - val hasSuperClass = clazz.superName != "java/lang/Object" && !isEnum val genericType = signatureParser.parseClassSignature(clazz.signature, superClass, interfaces) class EnumValueData(val field: FieldNode, val innerClass: InnerClassNode?, val correspondingClass: ClassNode?) @@ -386,28 +387,99 @@ class ClassFileToSourceStubConverter( lineMappings.registerClass(clazz) + val superTypes = calculateSuperTypes(clazz, genericType) + return treeMaker.ClassDef( modifiers, treeMaker.name(simpleName), genericType.typeParameters, - if (hasSuperClass) genericType.superClass else getSuperType(descriptor as? ClassDescriptor), - genericType.interfaces, + superTypes.superClass, + superTypes.interfaces, enumValues + fields + methods + nestedClasses).keepKdocComments(clazz) } - private inline fun getSuperType(classDescriptor: ClassDescriptor?): JCExpression? { + private class ClassSupertypes(val superClass: JCExpression?, val interfaces: JavacList) + + private fun calculateSuperTypes(clazz: ClassNode, genericType: SignatureParser.ClassGenericSignature): ClassSupertypes { + val hasSuperClass = clazz.superName != "java/lang/Object" && !clazz.isEnum() + + val defaultSuperTypes = ClassSupertypes( + if (hasSuperClass) genericType.superClass else null, + genericType.interfaces + ) + if (!correctErrorTypes) { + return defaultSuperTypes + } + + val declaration = kaptContext.origins[clazz]?.element as? KtClassOrObject ?: return defaultSuperTypes + + val (superClass, superInterfaces) = partitionSuperTypes(declaration) ?: return defaultSuperTypes + + val sameSuperClassCount = (superClass == null) == (defaultSuperTypes.superClass == null) + val sameSuperInterfaceCount = superInterfaces.size == defaultSuperTypes.interfaces.size + + if (sameSuperClassCount && sameSuperInterfaceCount) { + return defaultSuperTypes + } + + class SuperTypeCalculationFailure : RuntimeException() + + fun nonErrorType(ref: () -> KtTypeReference?): JCExpression { + assert(correctErrorTypes) + + return getNonErrorType( + ErrorUtils.createErrorType("Error super class"), + ErrorTypeCorrector.TypeKind.SUPER_TYPE, + ref + ) { throw SuperTypeCalculationFailure() } + } + + return try { + ClassSupertypes( + superClass?.let { nonErrorType { it } }, + mapJList(superInterfaces) { nonErrorType { it } } + ) + } catch (e: SuperTypeCalculationFailure) { + defaultSuperTypes + } + } + + private fun partitionSuperTypes(declaration: KtClassOrObject): Pair>? { + val superTypeEntries = declaration.superTypeListEntries + .takeIf { it.isNotEmpty() } + ?: return Pair(null, emptyList()) + + val resolvedSuperTypes = superTypeEntries + .map { it to kaptContext.bindingContext[BindingContext.TYPE, it.typeReference] } + + val (resolvedAsClasses, otherSuperTypes) = resolvedSuperTypes + .partition { + it.second?.isError == false + && (it.second?.constructor?.declarationDescriptor as? ClassDescriptor)?.kind == ClassKind.CLASS + } + + if (resolvedAsClasses.size > 1) { + // Error in user code, several entries were resolved to classes return null + } else if (resolvedAsClasses.size == 1) { + return Pair(resolvedAsClasses.single().first.typeReference, otherSuperTypes.mapNotNull { it.first.typeReference }) } - val ktClass = (classDescriptor?.source as? PsiSourceElement)?.psi as? KtClass - val superTypeCallEntry = - ktClass?.getSuperTypeList()?.entries?.mapNotNull { it as? KtSuperTypeCallEntry }?.first() - val typeFromSource = superTypeCallEntry?.typeReference?.typeElement - val ktFile = typeFromSource?.containingKtFile - if (ktFile != null) { - return ErrorTypeCorrector(this, SUPER_TYPE, ktFile).convert(typeFromSource, emptyMap()) + + val (withParenthesis, withoutParenthesis) = superTypeEntries.partition { it is KtSuperTypeCallEntry } + + if (withParenthesis.size > 1) { + // Error in user code, several entries with super constructor call + return null + } else if (withParenthesis.size == 1) { + return Pair(withParenthesis.single().typeReference, withoutParenthesis.mapNotNull { it.typeReference }) } - return null + + if (declaration is KtClass && declaration.primaryConstructor == null && declaration.secondaryConstructors.isNotEmpty()) { + return Pair(superTypeEntries.first().typeReference, superTypeEntries.drop(1).mapNotNull { it.typeReference }) + } + + return Pair(null, superTypeEntries.mapNotNull { it.typeReference }) } private tailrec fun checkIfValidTypeName(containingClass: ClassNode, type: Type): Boolean { diff --git a/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java b/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java index 5457d3059fb..2a144c5253f 100644 --- a/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java +++ b/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/ClassFileToSourceStubConverterTestGenerated.java @@ -89,6 +89,16 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi runTest("plugins/kapt3/kapt3-compiler/testData/converter/errorLocationMapping.kt"); } + @TestMetadata("errorSuperclass.kt") + public void testErrorSuperclass() throws Exception { + runTest("plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclass.kt"); + } + + @TestMetadata("errorSuperclassCorrectErrorTypes.kt") + public void testErrorSuperclassCorrectErrorTypes() throws Exception { + runTest("plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclassCorrectErrorTypes.kt"); + } + @TestMetadata("fileFacadeJvmName.kt") public void testFileFacadeJvmName() throws Exception { runTest("plugins/kapt3/kapt3-compiler/testData/converter/fileFacadeJvmName.kt"); diff --git a/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/KotlinKapt3IntegrationTests.kt b/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/KotlinKapt3IntegrationTests.kt index d7ab0123ca8..93d8ef88ff9 100644 --- a/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/KotlinKapt3IntegrationTests.kt +++ b/plugins/kapt3/kapt3-compiler/test/org/jetbrains/kotlin/kapt3/test/KotlinKapt3IntegrationTests.kt @@ -50,20 +50,6 @@ class KotlinKapt3IntegrationTests : AbstractKotlinKapt3IntegrationTest(), Java9T assertEquals("myMethod", annotatedElements.single().simpleName.toString()) } - @Test - fun testSuperclass() = test("Superclass", "test.MyAnnotation") { set, roundEnv, env -> - if (set.size == 1) { - val annotatedElements = roundEnv.getElementsAnnotatedWith(set.single()) - assertEquals(1, annotatedElements.size) - val typeElement = annotatedElements.single() as TypeElement - assertEquals("FooBar", typeElement.superclass.toString()) - env.filer.createSourceFile("test.FooBar").openWriter().use { writer -> - writer.write("package test;") - writer.write("public class FooBar {}") - } - } - } - @Test fun testComments() = test("Simple", "test.MyAnnotation") { set, roundEnv, env -> fun commentOf(className: String) = env.elementUtils.getDocComment(env.elementUtils.getTypeElement(className)) diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/errorLocationMapping.kt b/plugins/kapt3/kapt3-compiler/testData/converter/errorLocationMapping.kt index 8ef0ab2e08d..68ee7fed0af 100644 --- a/plugins/kapt3/kapt3-compiler/testData/converter/errorLocationMapping.kt +++ b/plugins/kapt3/kapt3-compiler/testData/converter/errorLocationMapping.kt @@ -1,14 +1,16 @@ // CORRECT_ERROR_TYPES -// EXPECTED_ERROR(kotlin:16:1) cannot find symbol -// EXPECTED_ERROR(kotlin:19:34) cannot find symbol -// EXPECTED_ERROR(kotlin:19:50) cannot find symbol -// EXPECTED_ERROR(kotlin:19:62) cannot find symbol -// EXPECTED_ERROR(kotlin:26:5) cannot find symbol -// EXPECTED_ERROR(kotlin:27:5) cannot find symbol -// EXPECTED_ERROR(kotlin:30:5) cannot find symbol +// EXPECTED_ERROR(kotlin:18:1) cannot find symbol +// EXPECTED_ERROR(kotlin:21:34) cannot find symbol +// EXPECTED_ERROR(kotlin:21:50) cannot find symbol +// EXPECTED_ERROR(kotlin:21:62) cannot find symbol +// EXPECTED_ERROR(kotlin:23:1) cannot find symbol +// EXPECTED_ERROR(kotlin:24:1) cannot find symbol +// EXPECTED_ERROR(kotlin:28:5) cannot find symbol +// EXPECTED_ERROR(kotlin:29:5) cannot find symbol // EXPECTED_ERROR(kotlin:32:5) cannot find symbol -// EXPECTED_ERROR(kotlin:35:5) cannot find symbol +// EXPECTED_ERROR(kotlin:34:5) cannot find symbol +// EXPECTED_ERROR(kotlin:37:5) cannot find symbol @file:Suppress("UNRESOLVED_REFERENCE", "ANNOTATION_ARGUMENT_MUST_BE_CONST", "NON_CONST_VAL_USED_IN_CONSTANT_EXPRESSION") import kotlin.reflect.KClass diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/errorLocationMapping.txt b/plugins/kapt3/kapt3-compiler/testData/converter/errorLocationMapping.txt index 8a43634c117..8bded353588 100644 --- a/plugins/kapt3/kapt3-compiler/testData/converter/errorLocationMapping.txt +++ b/plugins/kapt3/kapt3-compiler/testData/converter/errorLocationMapping.txt @@ -134,7 +134,7 @@ public final class ErrorInDeclarations { import kotlin.reflect.KClass; @kotlin.Metadata() -public final class ErrorInSupertype { +public final class ErrorInSupertype implements ABC { public ErrorInSupertype() { super(); @@ -147,7 +147,7 @@ public final class ErrorInSupertype { import kotlin.reflect.KClass; @kotlin.Metadata() -public final class ErrorInSupertype2 { +public final class ErrorInSupertype2 extends ABC { public ErrorInSupertype2() { super(); diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclass.kt b/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclass.kt new file mode 100644 index 00000000000..d2774008f43 --- /dev/null +++ b/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclass.kt @@ -0,0 +1,7 @@ +package test + +internal annotation class Anno + +@Anno +@Suppress("UNRESOLVED_REFERENCE") +internal class ClassWithParent: Foo(), Bar, Baz, CharSequence \ No newline at end of file diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclass.txt b/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclass.txt new file mode 100644 index 00000000000..0fdad8ea0d2 --- /dev/null +++ b/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclass.txt @@ -0,0 +1,38 @@ +package test; + +import java.lang.System; + +@kotlin.Metadata() +@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) +public abstract @interface Anno { +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Suppress(names = {"UNRESOLVED_REFERENCE"}) +@kotlin.Metadata() +@Anno() +public final class ClassWithParent implements java.lang.CharSequence { + + public ClassWithParent() { + super(); + } + + @java.lang.Override() + public final int length() { + return 0; + } + + public abstract int getLength(); + + @java.lang.Override() + public final char charAt(int p0) { + return '\u0000'; + } + + public abstract char get(int p0); +} diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclassCorrectErrorTypes.kt b/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclassCorrectErrorTypes.kt new file mode 100644 index 00000000000..6bedc2181d0 --- /dev/null +++ b/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclassCorrectErrorTypes.kt @@ -0,0 +1,45 @@ +// CORRECT_ERROR_TYPES +// NO_VALIDATION + +@file:Suppress("UNRESOLVED_REFERENCE", "DELEGATION_NOT_TO_INTERFACE", "SUPERTYPE_NOT_INITIALIZED") +package test + +interface Intf + +open class Cl + +class TFooBarBaz: Foo(), Bar, Baz + +// Error, two () +class TFooBarBaz2: Foo(), Bar(), Baz, Intf + +class TFooBarBaz3 : Foo, Bar, Baz + +class TFooBarBaz4() : Foo, Bar, Baz + +class TFooBarBaz5() : Foo, Bar, Baz { + constructor(s: String) {} +} + +class TFooBarBaz6 : Foo, Bar, Baz { + constructor(s: String) : super(s) +} + +class TClBarBaz : Cl, Bar, Baz + +class TBarBazCl : Bar, Baz, Cl + +class TFooBar(val a: X) : Foo(), Bar by a, Intf + +class TFooBar2(val a: X): Foo by a, Bar by a + +class TxFooxBarxBaz : x.Foo(), x.Bar, x.Baz, Intf + +// Error, two () +class TxFooxBarxBaz2 : x.Foo(), x.Bar, x.Baz() + +class Generics1 : Foo() + +class Generics2 : Foo + +class Generics3 : Foo>, String> \ No newline at end of file diff --git a/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclassCorrectErrorTypes.txt b/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclassCorrectErrorTypes.txt new file mode 100644 index 00000000000..b8e96b020c3 --- /dev/null +++ b/plugins/kapt3/kapt3-compiler/testData/converter/errorSuperclassCorrectErrorTypes.txt @@ -0,0 +1,253 @@ +package test; + +import java.lang.System; + +@kotlin.Metadata() +public class Cl { + + public Cl() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class Generics1 extends Foo { + + public Generics1() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class Generics2 implements Foo { + + public Generics2() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class Generics3 implements Foo>, java.lang.String> { + + public Generics3() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public abstract interface Intf { +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TBarBazCl extends test.Cl implements Bar, Baz { + + public TBarBazCl() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TClBarBaz extends test.Cl implements Bar, Baz { + + public TClBarBaz() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TFooBar extends Foo implements Bar, test.Intf { + @org.jetbrains.annotations.NotNull() + private final X a = null; + + @org.jetbrains.annotations.NotNull() + public final X getA() { + return null; + } + + public TFooBar(@org.jetbrains.annotations.NotNull() + X a) { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TFooBar2 implements Foo, Bar { + @org.jetbrains.annotations.NotNull() + private final X a = null; + + @org.jetbrains.annotations.NotNull() + public final X getA() { + return null; + } + + public TFooBar2(@org.jetbrains.annotations.NotNull() + X a) { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TFooBarBaz extends Foo implements Bar, Baz { + + public TFooBarBaz() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TFooBarBaz2 implements test.Intf { + + public TFooBarBaz2() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TFooBarBaz3 implements Foo, Bar, Baz { + + public TFooBarBaz3() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TFooBarBaz4 implements Foo, Bar, Baz { + + public TFooBarBaz4() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TFooBarBaz5 implements Foo, Bar, Baz { + + public TFooBarBaz5() { + super(); + } + + public TFooBarBaz5(@org.jetbrains.annotations.NotNull() + java.lang.String s) { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TFooBarBaz6 extends Foo implements Bar, Baz { + + public TFooBarBaz6(@org.jetbrains.annotations.NotNull() + java.lang.String s) { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TxFooxBarxBaz extends x.Foo implements x.Bar, x.Baz, test.Intf { + + public TxFooxBarxBaz() { + super(); + } +} + +//////////////////// + +package test; + +import java.lang.System; + +@kotlin.Metadata() +public final class TxFooxBarxBaz2 { + + public TxFooxBarxBaz2() { + super(); + } +} diff --git a/plugins/kapt3/kapt3-compiler/testData/kotlinRunner/Superclass.it.txt b/plugins/kapt3/kapt3-compiler/testData/kotlinRunner/Superclass.it.txt deleted file mode 100644 index cd61087cea6..00000000000 --- a/plugins/kapt3/kapt3-compiler/testData/kotlinRunner/Superclass.it.txt +++ /dev/null @@ -1,30 +0,0 @@ -package error; - -public final class NonExistentClass { -} - -//////////////////// - -package test; - -import java.lang.System; - -@kotlin.Metadata() -@MyAnnotation() -public final class ClassWithParent extends FooBar { - - public ClassWithParent() { - super(); - } -} - -//////////////////// - -package test; - -import java.lang.System; - -@kotlin.Metadata() -@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME) -public abstract @interface MyAnnotation { -} diff --git a/plugins/kapt3/kapt3-compiler/testData/kotlinRunner/Superclass.kt b/plugins/kapt3/kapt3-compiler/testData/kotlinRunner/Superclass.kt deleted file mode 100644 index 5e39ff4c1e4..00000000000 --- a/plugins/kapt3/kapt3-compiler/testData/kotlinRunner/Superclass.kt +++ /dev/null @@ -1,7 +0,0 @@ -package test - -internal annotation class MyAnnotation - -@MyAnnotation -internal class ClassWithParent: FooBar() { -}