Kapt: Fix "no interface expected here" in class implementing interface with secondary constructor (KT-27506)

This commit is contained in:
Yan Zhulanow
2019-02-05 21:51:51 +03:00
parent a9eaf1c345
commit 87cad2af02
5 changed files with 94 additions and 24 deletions
@@ -467,36 +467,49 @@ class ClassFileToSourceStubConverter(val kaptContext: KaptContextForStubGenerati
.takeIf { it.isNotEmpty() }
?: return Pair(null, emptyList())
val resolvedSuperTypes = superTypeEntries
.map { it to kaptContext.bindingContext[BindingContext.TYPE, it.typeReference] }
val classEntries = mutableListOf<KtSuperTypeListEntry>()
val interfaceEntries = mutableListOf<KtSuperTypeListEntry>()
val otherEntries = mutableListOf<KtSuperTypeListEntry>()
val (resolvedAsClasses, otherSuperTypes) = resolvedSuperTypes
.partition {
it.second?.isError == false
&& (it.second?.constructor?.declarationDescriptor as? ClassDescriptor)?.kind == ClassKind.CLASS
for (entry in superTypeEntries) {
val type = kaptContext.bindingContext[BindingContext.TYPE, entry.typeReference]
val classDescriptor = type?.constructor?.declarationDescriptor as? ClassDescriptor
if (type != null && !type.isError && classDescriptor != null) {
val container = if (classDescriptor.kind == ClassKind.INTERFACE) interfaceEntries else classEntries
container += entry
continue
}
if (resolvedAsClasses.size > 1) {
if (entry is KtSuperTypeCallEntry) {
classEntries += entry
continue
}
otherEntries += entry
}
for (entry in otherEntries) {
if (classEntries.isEmpty()) {
if (declaration is KtClass && !declaration.isInterface() && declaration.hasOnlySecondaryConstructors()) {
classEntries += entry
continue
}
}
interfaceEntries += entry
}
if (classEntries.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 (withParenthesis, withoutParenthesis) = superTypeEntries.partition { it is KtSuperTypeCallEntry }
return Pair(classEntries.firstOrNull()?.typeReference, interfaceEntries.mapNotNull { it.typeReference })
}
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 })
}
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 fun KtClass.hasOnlySecondaryConstructors(): Boolean {
return primaryConstructor == null && secondaryConstructors.isNotEmpty()
}
private tailrec fun checkIfValidTypeName(containingClass: ClassNode, type: Type): Boolean {
@@ -369,6 +369,11 @@ public class ClassFileToSourceStubConverterTestGenerated extends AbstractClassFi
runTest("plugins/kapt3/kapt3-compiler/testData/converter/repeatableAnnotations.kt");
}
@TestMetadata("secondaryConstructor.kt")
public void testSecondaryConstructor() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/secondaryConstructor.kt");
}
@TestMetadata("severalPackageParts.kt")
public void testSeveralPackageParts() throws Exception {
runTest("plugins/kapt3/kapt3-compiler/testData/converter/severalPackageParts.kt");
@@ -176,7 +176,7 @@ package test;
import java.lang.System;
@kotlin.Metadata()
public final class TFooBar extends Foo implements Bar, test.Intf {
public final class TFooBar extends Foo implements test.Intf, Bar {
@org.jetbrains.annotations.NotNull()
private final X a = null;
@@ -310,7 +310,7 @@ package test;
import java.lang.System;
@kotlin.Metadata()
public final class TxFooxBarxBaz extends x.Foo implements x.Bar, x.Baz, test.Intf {
public final class TxFooxBarxBaz extends x.Foo implements test.Intf, x.Bar, x.Baz {
public TxFooxBarxBaz() {
super();
@@ -0,0 +1,15 @@
// CORRECT_ERROR_TYPES
package secondary
interface Named {
val name: String?
}
class Product2 : Named {
override var name: String? = null
constructor(otherName: String) {
this.name = otherName
}
}
@@ -0,0 +1,37 @@
package secondary;
import java.lang.System;
@kotlin.Metadata()
public abstract interface Named {
@org.jetbrains.annotations.Nullable()
public abstract java.lang.String getName();
}
////////////////////
package secondary;
import java.lang.System;
@kotlin.Metadata()
public final class Product2 implements secondary.Named {
@org.jetbrains.annotations.Nullable()
private java.lang.String name;
@org.jetbrains.annotations.Nullable()
@java.lang.Override()
public java.lang.String getName() {
return null;
}
public void setName(@org.jetbrains.annotations.Nullable()
java.lang.String p0) {
}
public Product2(@org.jetbrains.annotations.NotNull()
java.lang.String otherName) {
super();
}
}