Support nested header/impl classes

This commit is contained in:
Alexander Udalov
2016-12-15 16:28:02 +03:00
parent a57455ce62
commit 56d4ff0cad
17 changed files with 393 additions and 23 deletions
@@ -31,7 +31,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Compatible
import org.jetbrains.kotlin.resolve.checkers.HeaderImplDeclarationChecker.Compatibility.Incompatible
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
@@ -101,7 +101,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
private fun checkImplementationHasHeaderDeclaration(
reportOn: KtDeclaration, descriptor: MemberDescriptor, diagnosticHolder: DiagnosticSink
) {
fun ClassifierDescriptor.findDeclarationForClass(): ClassDescriptor? =
fun ClassifierDescriptorWithTypeParameters.findDeclarationForClass(): ClassDescriptor? =
findClassifiersFromTheSameModule().firstOrNull { declaration ->
this != declaration &&
declaration is ClassDescriptor && declaration.isHeader &&
@@ -122,7 +122,7 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
areCompatibleCallables(declaration, descriptor, checkImpl = false) == Compatible
}
}
is ClassifierDescriptor -> descriptor.findDeclarationForClass() != null
is ClassifierDescriptorWithTypeParameters -> descriptor.findDeclarationForClass() != null
else -> false
}
@@ -146,12 +146,26 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
}
}
fun ClassifierDescriptor.findClassifiersFromTheSameModule(): Collection<ClassifierDescriptor> {
fun ClassifierDescriptorWithTypeParameters.findClassifiersFromTheSameModule(): Collection<ClassifierDescriptorWithTypeParameters> {
val myModule = moduleToCheck ?: module
// TODO: support nested classes
return myModule.getPackage(fqNameSafe.parent()).memberScope
.getDescriptorsFiltered(DescriptorKindFilter.CLASSIFIERS) { it == name }
.filterIsInstance<ClassifierDescriptor>()
val classId = classId ?: return emptyList()
fun MemberScope.getAllClassifiers(name: Name): Collection<ClassifierDescriptorWithTypeParameters> =
getDescriptorsFiltered(DescriptorKindFilter.CLASSIFIERS) { it == name }
.filterIsInstance<ClassifierDescriptorWithTypeParameters>()
val segments = classId.relativeClassName.pathSegments()
var classifiers = myModule.getPackage(classId.packageFqName).memberScope.getAllClassifiers(segments.first())
for (name in segments.subList(1, segments.size)) {
classifiers = classifiers.mapNotNull { classifier ->
(classifier as? ClassDescriptor)?.unsubstitutedInnerClassesScope?.getContributedClassifier(
name, NoLookupLocation.FOR_ALREADY_TRACKED
) as? ClassifierDescriptorWithTypeParameters
}
}
return classifiers
}
sealed class Compatibility {
@@ -190,6 +204,8 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
object ClassKind : Incompatible("class kinds are different (class, interface, object, enum, annotation)")
object ClassModifiers : Incompatible("modifiers are different (companion, inner)")
object Supertypes : Incompatible("some supertypes are missing in the implementation")
class ClassScopes(
@@ -318,6 +334,8 @@ class HeaderImplDeclarationChecker(val moduleToCheck: ModuleDescriptor? = null)
if (a.kind != b.kind) return Incompatible.ClassKind
if (!equalBy(a, b) { listOf(it.isCompanionObject, it.isInner) }) return Incompatible.ClassModifiers
val aTypeParams = a.declaredTypeParameters
val bTypeParams = b.declaredTypeParameters
if (aTypeParams.size != bTypeParams.size) return Incompatible.TypeParameterCount
@@ -336,6 +336,8 @@ open class LazyClassMemberScope(
val hasPrimaryConstructor = classOrObject.hasExplicitPrimaryConstructor()
if (DescriptorUtils.isInterface(thisDescriptor) && !hasPrimaryConstructor) return null
if (thisDescriptor.isHeader && thisDescriptor.kind == ClassKind.OBJECT) return null
if (DescriptorUtils.canHaveDeclaredConstructors(thisDescriptor) || hasPrimaryConstructor) {
val constructor = c.functionDescriptorResolver.resolvePrimaryConstructorDescriptor(
thisDescriptor.scopeForConstructorHeaderResolution, thisDescriptor, classOrObject, trace)
@@ -343,11 +345,10 @@ open class LazyClassMemberScope(
setDeferredReturnType(constructor)
return constructor
}
else {
val constructor = DescriptorResolver.createAndRecordPrimaryConstructorForObject(classOrObject, thisDescriptor, trace)
setDeferredReturnType(constructor)
return constructor
}
val constructor = DescriptorResolver.createAndRecordPrimaryConstructorForObject(classOrObject, thisDescriptor, trace)
setDeferredReturnType(constructor)
return constructor
}
private fun resolveSecondaryConstructors(): Collection<ClassConstructorDescriptor> {
@@ -40,7 +40,6 @@ public header interface Interface {
}
public header object Object {
private constructor Object()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@@ -0,0 +1,46 @@
// !LANGUAGE: +MultiPlatformProjects
// MODULE: m1-common
// FILE: common.kt
header class OuterClass {
header class NestedClass {
header class DeepNested {
header class Another
}
}
header inner class InnerClass
header companion object
}
header class OuterClassWithNamedCompanion {
header companion object Factory
}
header object OuterObject {
header object NestedObject
}
// MODULE: m2-jvm(m1-common)
// FILE: jvm.kt
impl class OuterClass {
impl class NestedClass {
impl class DeepNested {
impl class Another
}
}
impl inner class InnerClass
impl companion object
}
impl class OuterClassWithNamedCompanion {
impl companion object Factory
}
impl object OuterObject {
impl object NestedObject
}
@@ -0,0 +1,142 @@
// -- Module: <m1-common> --
package
public final header class OuterClass {
public constructor OuterClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public header companion object Companion {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final header inner class InnerClass {
public constructor InnerClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final header class NestedClass {
public constructor NestedClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final header class DeepNested {
public constructor DeepNested()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final header class Another {
public constructor Another()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
}
}
public final header class OuterClassWithNamedCompanion {
public constructor OuterClassWithNamedCompanion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public header companion object Factory {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
public header object OuterObject {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public header object NestedObject {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
// -- Module: <m2-jvm> --
package
public final impl class OuterClass {
public constructor OuterClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public impl companion object Companion {
private constructor Companion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final impl inner class InnerClass {
public constructor InnerClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final impl class NestedClass {
public constructor NestedClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final impl class DeepNested {
public constructor DeepNested()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public final impl class Another {
public constructor Another()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
}
}
public final impl class OuterClassWithNamedCompanion {
public constructor OuterClassWithNamedCompanion()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public impl companion object Factory {
private constructor Factory()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
public impl object OuterObject {
private constructor OuterObject()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
public impl object NestedObject {
private constructor NestedObject()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
}
@@ -5,7 +5,7 @@ Output:
-- JVM --
Exit code: OK
Output:
compiler/testData/multiplatform/implementClassWithTypeAlias/jvm.kt:3:13: warning: parameter 'a' is never used
compiler/testData/multiplatform/implTypeAlias/generic/jvm.kt:3:13: warning: parameter 'a' is never used
fun foo(a: A): List<A>? = null
^
@@ -0,0 +1,3 @@
header class ByTypeAlias {
header interface Nested
}
@@ -0,0 +1,5 @@
class ByTypeAliasImpl {
interface Nested
}
impl typealias ByTypeAlias = ByTypeAliasImpl
@@ -0,0 +1,10 @@
-- Common --
Exit code: OK
Output:
-- JVM --
Exit code: COMPILATION_ERROR
Output:
compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/common.kt:2:22: error: header declaration 'Nested' has no implementation in module
header interface Nested
^
@@ -0,0 +1,19 @@
header class O1 {
header class N1
header interface N2
header object N3
}
header class O2 {
header class N2
header inner class I2
}
header class O3 {
header object Companion
header companion object Factory
}
header class O4 {
header companion object
}
@@ -0,0 +1,19 @@
impl class O1 {
impl interface N1
impl object N2
impl class N3
}
impl class O2 {
impl inner class N2
impl class I2
}
impl class O3 {
impl companion object {}
impl object Factory
}
impl class O4 {
impl object Companion
}
@@ -0,0 +1,80 @@
-- Common --
Exit code: OK
Output:
-- JVM --
Exit code: COMPILATION_ERROR
Output:
compiler/testData/multiplatform/incompatibleNestedClasses/common.kt:2:18: error: header declaration 'N1' has no implementation in module
The following declaration is incompatible because class kinds are different (class, interface, object, enum, annotation):
public impl interface N1
header class N1
^
compiler/testData/multiplatform/incompatibleNestedClasses/common.kt:3:22: error: header declaration 'N2' has no implementation in module
The following declaration is incompatible because class kinds are different (class, interface, object, enum, annotation):
public impl object N2
header interface N2
^
compiler/testData/multiplatform/incompatibleNestedClasses/common.kt:4:12: error: header declaration 'N3' has no implementation in module
The following declaration is incompatible because class kinds are different (class, interface, object, enum, annotation):
public final impl class N3
header object N3
^
compiler/testData/multiplatform/incompatibleNestedClasses/common.kt:8:18: error: header declaration 'N2' has no implementation in module
The following declaration is incompatible because modifiers are different (companion, inner):
public final impl inner class N2
header class N2
^
compiler/testData/multiplatform/incompatibleNestedClasses/common.kt:9:24: error: header declaration 'I2' has no implementation in module
The following declaration is incompatible because modifiers are different (companion, inner):
public final impl class I2
header inner class I2
^
compiler/testData/multiplatform/incompatibleNestedClasses/common.kt:13:12: error: header declaration 'Companion' has no implementation in module
The following declaration is incompatible because modifiers are different (companion, inner):
public impl companion object
header object Companion
^
compiler/testData/multiplatform/incompatibleNestedClasses/common.kt:14:22: error: header declaration 'Factory' has no implementation in module
The following declaration is incompatible because modifiers are different (companion, inner):
public impl object Factory
header companion object Factory
^
compiler/testData/multiplatform/incompatibleNestedClasses/common.kt:18:22: error: header declaration 'Companion' has no implementation in module
The following declaration is incompatible because modifiers are different (companion, inner):
public impl object Companion
header companion object
^
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:2:5: error: modifier 'impl' is only applicable to members that are initially declared in platform-independent code
impl interface N1
^
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:3:5: error: modifier 'impl' is only applicable to members that are initially declared in platform-independent code
impl object N2
^
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:4:5: error: modifier 'impl' is only applicable to members that are initially declared in platform-independent code
impl class N3
^
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:8:5: error: modifier 'impl' is only applicable to members that are initially declared in platform-independent code
impl inner class N2
^
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:9:5: error: modifier 'impl' is only applicable to members that are initially declared in platform-independent code
impl class I2
^
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:13:5: error: modifier 'impl' is only applicable to members that are initially declared in platform-independent code
impl companion object {}
^
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:14:5: error: modifier 'impl' is only applicable to members that are initially declared in platform-independent code
impl object Factory
^
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:18:5: error: modifier 'impl' is only applicable to members that are initially declared in platform-independent code
impl object Companion
^
@@ -13104,6 +13104,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("nestedClasses.kt")
public void testNestedClasses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.kt");
doTest(fileName);
}
@TestMetadata("simpleHeaderClass.kt")
public void testSimpleHeaderClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/simpleHeaderClass.kt");
@@ -42,12 +42,6 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform
doTest(fileName);
}
@TestMetadata("implementClassWithTypeAlias")
public void testImplementClassWithTypeAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/implementClassWithTypeAlias/");
doTest(fileName);
}
@TestMetadata("incompatibleCallables")
public void testIncompatibleCallables() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/incompatibleCallables/");
@@ -66,6 +60,12 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform
doTest(fileName);
}
@TestMetadata("incompatibleNestedClasses")
public void testIncompatibleNestedClasses() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/incompatibleNestedClasses/");
doTest(fileName);
}
@TestMetadata("incompatibleProperties")
public void testIncompatibleProperties() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/incompatibleProperties/");
@@ -130,4 +130,26 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform
}
@TestMetadata("compiler/testData/multiplatform/implTypeAlias")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ImplTypeAlias extends AbstractMultiPlatformIntegrationTest {
public void testAllFilesPresentInImplTypeAlias() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/multiplatform/implTypeAlias"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true);
}
@TestMetadata("generic")
public void testGeneric() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/implTypeAlias/generic/");
doTest(fileName);
}
@TestMetadata("nestedClassesViaTypeAlias")
public void testNestedClassesViaTypeAlias() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/implTypeAlias/nestedClassesViaTypeAlias/");
doTest(fileName);
}
}
}
@@ -74,11 +74,11 @@ val ClassifierDescriptorWithTypeParameters.denotedClassDescriptor: ClassDescript
else -> throw UnsupportedOperationException("Unexpected descriptor kind: $this")
}
val ClassDescriptor.classId: ClassId?
val ClassifierDescriptorWithTypeParameters.classId: ClassId?
get() = containingDeclaration.let { owner ->
when (owner) {
is PackageFragmentDescriptor -> ClassId(owner.fqName, name)
is ClassDescriptor -> owner.classId?.createNestedClassId(name)
is ClassifierDescriptorWithTypeParameters -> owner.classId?.createNestedClassId(name)
else -> null
}
}